案例实现:实现一个通用的数组类,要求如下:
- 可以对内置数据类型以及自定义数据类型的数据进行存储
- 将数组中的数据存储到堆区
- 构造函数中可以传入数组的容量
- 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
- 提供尾插法和尾删法对数组中的数据进行增加和删除
- 可以通过下标的方式访问数组中的元素
- 可以获取数组中当前元素个数和数组的容量

因为我们并不知道里面有什么数据类型,因此我们需要将这些数据进行模版化。
首先我们在MyArray.hpp文件里面写入以下的代码
cpp
//自己的通用的数组类
#include <iostream>
using namespace std;
template<typename T>
class MyArray{
public:
//有参构造函数 参数 容量
MyArray(int capacity)
{
cout<<"MyArray有参构造调用"<<endl;
this->m_Capacity=capacity;
//数组初始化的大小为0
this->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=c
MyArray &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这个文件里面写入下面的代码
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
cpp
//自己的通用的数组类
#include <iostream>
using namespace std;
template<typename T>
class MyArray{
public:
//有参构造函数 参数 容量
MyArray(int capacity)
{
this->m_Capacity=capacity;
//数组初始化的大小为0
this->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=c
MyArray &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;//数组的元素个数(数组的大小)
};
数组类封装函数那里写
cpp
#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表示程序正常结束
}