案例实现:实现一个通用的数组类,要求如下:
- 可以对内置数据类型以及自定义数据类型的数据进行存储
- 将数组中的数据存储到堆区
- 构造函数中可以传入数组的容量
- 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
- 提供尾插法和尾删法对数组中的数据进行增加和删除
- 可以通过下标的方式访问数组中的元素
- 可以获取数组中当前元素个数和数组的容量
因为我们并不知道里面有什么数据类型,因此我们需要将这些数据进行模版化。
首先我们在MyArray.hpp文件里面写入以下的代码
//自己的通用的数组类
#include <iostream>
using namespace std;template<typename T>
class MyArray{public://有参构造函数 参数 容量MyArray(int capacity){cout<<"MyArray有参构造调用"<<endl;this->m_Capacity=capacity;//数组初始化的大小为0this->m_Size=0;this->pAddress=new T[capacity]; //开辟堆区空间}//为了防止浅拷贝的问题,还必须写一个拷贝构造函数MyArray(const MyArray &arr){cout<<"MyArray拷贝构造函数调用"<<endl;this->m_Capacity=arr.m_Capacity;this->m_Size=arr.m_Size;//潜拷贝//this->pAddress=arr.pAddress; //将原数组的地址赋值给新数组//开辟新的堆区空间this->pAddress=new T[arr.m_Capacity];//将原数组的元素拷贝到新数组中for(int i=0;i<this->m_Capacity;i++){this->pAddress[i]=arr.pAddress[i];}}//operator= 也是为了防止浅拷贝问题. a=b=cMyArray &operator=(const MyArray &arr){cout<<"MyArray赋值运算符调用"<<endl;//先判断原来堆区是否有数据,如果有先释放if(this->pAddress!=nullptr){delete[] this->pAddress;this->pAddress=nullptr;//防止其为一个野指针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];//将原数组的元素拷贝到新数组中}return *this;//返回当前对象的引用}//之后再去做一个深拷贝//析构函数~MyArray(){if(this->pAddress!=nullptr){cout<<"MyArray析构函数调用"<<endl;delete[] this->pAddress;//防止其为一个野指针this->pAddress=nullptr;}}private:T* pAddress; //指针指向堆区开辟的真实的数组int m_Capacity;//数组的容量int m_Size;//数组的元素个数(数组的大小)
};
在数组类封装.cpp这个文件里面写入下面的代码
#include <iostream>
using namespace std;
#include "MyArray.hpp"void test01()
{MyArray<int> arr1(5); // 创建一个容量为5的数组MyArray<int> arr2(arr1); // 使用拷贝构造函数创建一个新数组MyArray<int> arr3(100); // 使用赋值运算符进行赋值arr3=arr1;// 使用赋值运算符进行赋值
}int main()
{test01(); // 测试函数return 0; // 返回0表示程序正常结束
}
之后运行,我们可以得到以下的内容
也就是说我们这几个进行了深拷贝,还有有参构造,之后通过析构函数释放了它们的内存。
MyArray.hpp
//自己的通用的数组类
#include <iostream>
using namespace std;template<typename T>
class MyArray{public://有参构造函数 参数 容量MyArray(int capacity){this->m_Capacity=capacity;//数组初始化的大小为0this->m_Size=0;this->pAddress=new T[capacity]; //开辟堆区空间}//为了防止浅拷贝的问题,还必须写一个拷贝构造函数MyArray(const MyArray &arr){this->m_Capacity=arr.m_Capacity;this->m_Size=arr.m_Size;//潜拷贝//this->pAddress=arr.pAddress; //将原数组的地址赋值给新数组//开辟新的堆区空间this->pAddress=new T[arr.m_Capacity];//将原数组的元素拷贝到新数组中for(int i=0;i<this->m_Capacity;i++){this->pAddress[i]=arr.pAddress[i];}}//operator= 也是为了防止浅拷贝问题. a=b=cMyArray &operator=(const MyArray &arr){//先判断原来堆区是否有数据,如果有先释放if(this->pAddress!=nullptr){delete[] this->pAddress;this->pAddress=nullptr;//防止其为一个野指针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];//将原数组的元素拷贝到新数组中}return *this;//返回当前对象的引用}//尾插法//一般为了防止T被修改,因此我们一般会写入一个const修饰符void Push_Back(const T &val){//判断数组是否已满if(this->m_Size>=this->m_Capacity) {cout<<"数组已满,无法插入元素"<<endl;return;}else{this->pAddress[this->m_Size]=val; //将元素插入到数组的末尾this->m_Size++;//元素个数加1}}//尾删法void Pop_Back(){if(this->m_Size<=0){cout<<"数组为空,无法删除元素"<<endl;return;}else{//让用户访问不到最后一个元素就可以了this->m_Size--;//元素个数减1//不需要删除最后一个元素,因为数组的大小已经减小了}}//通过下标的方式访问数组中的元素T& operator[](int index){return this->pAddress[index]; //返回数组中指定下标的元素}//返回数组的容量int GetCapacity() const{return this->m_Capacity;}//返回数组的大小int GetSize() const{return this->m_Size;}//析构函数~MyArray(){if(this->pAddress!=nullptr){delete[] this->pAddress;//防止其为一个野指针this->pAddress=nullptr;}}private:T* pAddress; //指针指向堆区开辟的真实的数组int m_Capacity;//数组的容量int m_Size;//数组的元素个数(数组的大小)
};
数组类封装函数那里写
#include <iostream>
using namespace std;
#include "MyArray.hpp"
#include <string>void printIntArray(MyArray<int> &arr)
{for(int i=0;i<arr.GetSize();i++){cout<<arr[i]<<" "<<endl;}
}
void test01()
{MyArray<int> arr1(5); // 创建一个容量为5的数组for(int i=0;i<5;i++){arr1.Push_Back(i); // 向数组中添加元素}cout<<"arr1的打印输出为:"<<endl;// MyArray<int> arr2(arr1); // 使用拷贝构造函数创建一个新数组// MyArray<int> arr3(100); // 使用赋值运算符进行赋值// arr3=arr1;printIntArray(arr1); // 打印数组内容cout<<"arr1的容量为:" << arr1.GetCapacity() << endl; // 打印数组容量 cout<<"arr1的大小为:" << arr1.GetSize() << endl; // 打印数组大小MyArray<int> arr2(arr1); // 使用拷贝构造函数创建一个新数组cout<<"arr2的打印输出为:"<<endl;arr2.Pop_Back(); // 删除数组的最后一个元素printIntArray(arr2); // 打印删除后的数组内容cout<<"删除一个元素后,arr2的大小为:" << arr2.GetSize() << endl; // 打印数组大小cout<<"删除一个元素后,arr2的容量为:" << arr2.GetCapacity() << endl; // 打印数组容量}//测试自定义的数据类型class Person{public:Person() {}Person(string name,int age): m_Name(name),m_Age(age){this->m_Name=name;this->m_Age=age;}string m_Name;int m_Age;};void printPersonArray(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> arr3(10);Person p1("孙悟空",500);Person p2("猪八戒",300);Person p3("沙和尚",400);Person p4("唐僧",1000);Person p5("白龙马",200);Person p6("小白龙",150);Person p7("小红龙",120);arr3.Push_Back(p1);arr3.Push_Back(p2);arr3.Push_Back(p3);arr3.Push_Back(p4);arr3.Push_Back(p5);arr3.Push_Back(p6);arr3.Push_Back(p7); //打印数组printPersonArray(arr3); // 这里需要重载printIntArray函数来打印Person类型的数组}int main()
{test01(); // 测试函数cout << "------------------------" << endl;cout << "测试自定义数据类型的数组" << endl;cout << "------------------------" << endl;cout << "测试自定义数据类型的数组" << endl;test02(); // 测试函数return 0; // 返回0表示程序正常结束
}