Day17 C++提高 之 类模板案例

类模板案例

实现一个通用的数组类,要求如下:

  • 可以对内置数据类型以及自定义数据类型的数据进行存储;
  • 将数组中的数据存储到堆区(new);
  • 构造函数中可以传入数组的容量(有参构造);
  • 提供对应的拷贝构造函数以及operator = 防止浅拷贝问题;
  • 提供尾插法和尾删法对数组中的数据进行增加和删除;
  • 可以通过下标的方式访问数组中的元素;
  • 可以获取数组中当前元素个数和数组的容量(私有化,设置接口可以访问,但不可修改)

构建思路:

创建一个数组的类,类中需要有数组、数组的容量、数组当前元素个数;这个数组可以存储用户输入的数据类型的数据(数据类型包含系统内置以及用户自定义的数据类型),所以数组中的每一个元素的数据类型都是通用数据类型 T,将数据开放到堆区(用new,但new出来的一般都是指针,所以用T* =new T[n])。所以在数组类的内部维护的数组应该是T* pAddress,这个指针维护的是堆区真实存放数组元素的指针;数组的属性可以写为private,其他的写public的接口,接口应该有有参构造、拷贝构造(深拷贝)、operator、析构函数;如果是编译器提供的拷贝构造(浅拷贝)以及编译器提供的operator=都会出现浅拷贝的问题,所以需要自己写深拷贝解决这个问题(当数据有在堆区的数据,一定要自己写拷贝构造函数和operator函数防止浅拷贝问题;利用数组的下标访问数组中的元素?提供对外接口(尾插法、尾删法、获取数组容量、获取数组大小)。

因为不知道用户传入的类型是什么?所以每个数据的类型是T,模板化;用户传入容量,就new T[n]即可(这里n是用户传入的容量),返回的是T*;

类模板做分文件编写的时候,成员函数不好实现,所以将.h文件变为.hpp文件,将声明和实现都写在其中。

myArray.hpp文件

c++ 复制代码
#pragma once
#include<iostream>
using namespace std;
#include<string>

//类模板作分文件编写,会报错,所以写在一个文件中,改后缀名为.hpp
template<class T>
class MyArray
{
	//对外的成员函数
public:
	//有参构造,让用户输入容量,所以参数是用户传入的容量
	MyArray(int capacity)//初始化属性值
	{
		//cout << "myArray有参构造函数的调用" << endl;
		this->m_Capacity = capacity;
		this->m_Size = 0;//最开始容量为0
		//pAddress指针指向在堆区开辟的空间
		this->pAddress = new T[this->m_Capacity];//对数组进行初始化,开辟出用户需要的空间
	}

	//拷贝构造:防止浅拷贝的问题,需要自己写拷贝构造函数
	//类对象作为参数传入对象时调用拷贝构造函数
	MyArray(const MyArray& arr)
	{
		//将arr数组中的值拷贝过来
		//cout << "myArray拷贝构造函数的调用" << endl;
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		//this->pAddress = arr.m_pAddress;浅拷贝的问题,指针不可以直接拷贝,会导致堆区的数据重复释放

		//深拷贝
		this->pAddress = new T[arr.m_Capacity];//开辟一样大小的空间

		//将arr中的数据都拷贝过来
		for (int i = 0; i < this->m_Size; i++)
		{
			this->pAddress[i] = arr.pAddress[i];//数组中的元素做一个传递
		}
	
	}

	//operator=防止浅拷贝问题,进行重载,返回对象是本身,防止出现连等的操作
	//两个对象直接=号时进行operator操作
    //重载=。让两个对象可以直接赋值
	MyArray& operator=(const MyArray &arr)
	{
		
		//cout << "myArray的operator=函数的调用" << endl;
		//先判断原来堆区是否有数据,如果有,先释放,再深拷贝
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

	    //深拷贝
	    this->m_Capacity = arr.m_Capacity;
	    this->m_Size - arr.m_Size;
	    this->pAddress = new T[arr.m_Capacity];//先开辟一样大小的空间
	    for (int i = 0; i < this->m_Size; i++)
	    {
		    this->pAddress[i] = arr.pAddress[i];//将arr中的数据全部拿过来
	    }
	    return *this;//返回自身
    }
	
	//尾插法
	void Push_Back(const T& value)//向数组中插入数据value
	{
		//先判断数组容量还够不够
		if (this->m_Size == this->m_Capacity)
		{
			cout << "数组容量已满!塞不下了!!" << endl;
			return;
		}
		this->pAddress[this->m_Size] = value;//将传入的值插入数组中下标尾size的地方,也就是当前数组的最后一维(尾插)
		this->m_Size++;//更改数组大小+1
	}

	//尾删法
	void Pop_Back()
	{
		//让用户访问不到最后一个元素,(逻辑尾删)
		//数组数量-1,指针前移一个
		//先判断有没有
		if (this->m_Size == 0)
		{
			cout << "当前数组为空!" << endl;
			return;
		}
		this->m_Size--;
	}

	//通过下标方式访问元素,
	//若想要函数返回可以作为一个左值存在A[1]=7,因为想要作为左值,就是要改变这个值(这里是将A[1]重新赋值为7),所以用&,改变内部系统的值
    //重载[],让用户可以直接通过下标访问元素
	T& operator[](int index)
	{
		return this->pAddress[index];//返回的类型是T
	}

	//返回数组的容量
	int getCapacity()
	{
		return this->m_Capacity;
	}
	
	//返回数组大小
	int getSize()
	{
		return this->m_Size;
	}
	

	//析构函数
	~MyArray()
	{
		//析构函数需要做的是释放空间并置空指针
		if (this->pAddress != NULL)
		{
			//cout << "myArray析构造函数的调用" << endl;
			delete[] this->pAddress;//释放空间,是一个数组,所以需要加[]
			this->pAddress = NULL;//将指针置空防止是一个野指针
		}
	}
	//需要维护的几个私有的属性
private:
	T* pAddress;//指针指向堆区开辟的真实数组

	int m_Capacity;//数组容量
	int m_Size;//数组元素数量

};

源文件

c++ 复制代码
#include<iostream>
using namespace std;
#include<string>
#include"myArray.hpp"

//打印数组函数
void myPrint(MyArray<int> &arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << arr[i] << endl;
	}
}

//创建自定义数据类型
class Person
{
public:
	Person() {};
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

void test01()
{
	//声明数组
	MyArray<int>arr1(5);//对象创建会先调用构造函数,随即调用析构函数,5是传入的容量,指定类型是int

	for (int i = 0; i < 5; i++)
	{
		//利用尾插法向数组中插入数据
		arr1.Push_Back(i);
	}
	cout << "arr1中的数据输出为:" << endl;
	myPrint(arr1);

	cout << "arr1的容量为:" << arr1.getCapacity()<<endl;
	cout << "arr1的大小为:" << arr1.getSize() << endl;

	//将arr1的值赋值给arr2,需要调用拷贝构造函数
	MyArray<int>arr2(arr1);//拷贝构造函数,深拷贝,将对象作为参数传入另一个对象
	cout << "arr2中的数据输出为:" << endl;
	myPrint(arr2);
	//尾删法
	arr2.Pop_Back();
	cout << "arr2的容量为:" << arr2.getCapacity() << endl;
	cout << "arr2的大小为:" << arr2.getSize() << endl;

	cout << "arr2中的数据输出为:" << endl;
	myPrint(arr2);

	MyArray<int>arr3(100);//创建arr3容量为100,调用有参构造函数
	arr3 = arr1;//对arr3重新赋值,会调用operator的函数,赋值操作,先将arr3中的内容清空,再进行赋值拷贝操作
}


void myPrintPerson(MyArray<Person>& arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << arr[i].m_Name << " "<< arr[i].m_Age << endl;
	}
}
//测试自定义类型
void test02()
{
	MyArray<Person>arr(5);
	Person p1("懒羊羊", 10);
	Person p2("喜羊羊", 13);
	Person p3("沸羊羊", 13);
	Person p4("美羊羊", 12);
	Person p5("暖羊羊", 14);

	//将数据插入到数组中
	arr.Push_Back(p1);
	arr.Push_Back(p2);
	arr.Push_Back(p3);
	arr.Push_Back(p4);
	arr.Push_Back(p5);

	//打印数组
	myPrintPerson(arr);

	cout << "arr的容量为:" << arr.getCapacity() << endl;
	cout << "arr的大小为:" << arr.getSize() << endl;

	//尾删法
	arr.Pop_Back();
	cout << "arr中的数据输出为:" << endl;
	myPrintPerson(arr);

	cout << "arr的容量为:" << arr.getCapacity() << endl;
	cout << "arr的大小为:" << arr.getSize() << endl;
}

int main()
{
	test01();
    test02();

	system("pause");
	return 0;
}
相关推荐
leo__5208 小时前
基于MATLAB实现的鲁棒性音频数字水印系统
开发语言·matlab·音视频
2301_789015628 小时前
C++:多态(面向对象的主要手段之一)
c语言·开发语言·c++·多态
小年糕是糕手8 小时前
【C++】string类(一)
linux·开发语言·数据结构·c++·算法·leetcode·改行学it
初願致夕霞8 小时前
LeetCode双指针题型总结
算法·leetcode·职场和发展
努力学算法的蒟蒻8 小时前
day36(12.17)——leetcode面试经典150
算法·leetcode·面试
sali-tec8 小时前
C# 基于halcon的视觉工作流-章70 深度学习-Deep OCR
开发语言·人工智能·深度学习·算法·计算机视觉·c#·ocr
晚霞的不甘8 小时前
C语言利用数组处理批量数据详解
android·c语言·开发语言
Howie Zphile8 小时前
在 WSL Ubuntu 上从零到数据迁移:通过 pgloader 将 SQL Server 转 PostgreSQL 实战笔记
笔记·ubuntu·postgresql
渡我白衣8 小时前
C++可变参数队列与压栈顺序:从模板语法到汇编调用约定的深度解析
c语言·汇编·c++·人工智能·windows·深度学习·硬件架构