文章目录
类模板案例-实现一个通用的数组类
1. 要求
1.可以对内置数据类型以及自定义数据类型的数据进行存储
2.将数组中的数据存储到堆区
3.构造函数中可以传入数组的容量
4.提供对应的拷贝构造函数以及operator=防止浅拷贝问题
5.提供尾插法和尾删法对数组中的数据进行增加和删除
6.可以通过下标的方式访问数组中的元素
7.可以获取数组中当前元素个数和数组的容量
2. 需求分析
3. 数组类模板要求1-4的步骤及注意点:
- 分文见编写.hpp文件:把类模板分文见编写,类模板的声明和实现写在一起;
- 类模板MyArray: 包括私有成员属性和对外提供接口的公共成员函数,其中成员函数包括有参构造函数、析构函数、拷贝构造、operator=;
- 有参构造函数MyArray(): 私有属性,数组的容量capacity,数组的大小siza,开辟在堆区的数组pAddress,其中数组类型及其数据都是通用数据类型T,即模板化。根据传入的数组容量来在堆区开辟该容量的数组,数组大小初始值为0;
- 析构函数~MyArray(): 有参构造函数中在堆区开辟了数据,手动开辟需要手动释放,因此需要在此判断堆区数组是否释放干净。如果没有,再次释放;
- 拷贝构造: 数组容量和大小都可以浅拷贝,但是数组是堆区的数据,浅拷贝会引发析构的重复释放。采用深拷贝解决,在堆区再次开辟一个数组,并将原数组的所有数据都拷贝到新数组中;
- operator=: 也是为了防止浅拷贝的问题,与拷贝构造不同的是,首先要判断在堆区的原数组释放释放干净,如果没有需要再次释放。然后,再在堆区开辟新数组,并拷贝原数组的所有数据。
- 测试以上六点是否成功实现
4. 数组类模板要求1-4的代码与测试:
.hpp文件
#pragma once
#include <iostream>
using namespace std;
//自己的通用数组类声明
template<class T>
class MyArray
{
public:
//有参构造 参数:容量
MyArray(int capacity)
{
cout << "MyArray 有参构造 调用" << endl;//测试代码
this->m_Capacity = capacity;//根据传入的数组容量来初始化
this->m_Size = 0;//初始数组大小为0
this->pAddress = new T[this->m_Capacity];//根据容量在堆区开辟T类型的数组,每一个数据都是T类型
}
//拷贝构造 浅拷贝 深拷贝
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];
//将arr所有数据都拷贝
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
//operator= 防止浅拷贝
MyArray& operator=(const MyArray& arr)
{
cout << "MyArray operator= 调用" << endl;//测试代码
//先判断原来堆区是否有数据,如果有,先释放
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
this->m_Size = 0;
this->m_Capacity = 0;
}
//深拷贝
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity];
//拷贝arr所有数据
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
//析构函数 堆区数据手动开辟手动释放
~MyArray()
{
cout << "MyArray 析构函数 调用" << endl;//测试代码
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
}
private:
T* pAddress;//指针指向堆区开辟的真实数组
int m_Capacity;//数组容量
int m_Size;//数组大小
};
测试文件
#include <iostream>
using namespace std;
#include "MyArray.hpp"
void test()
{
cout << "arr1" << endl;
MyArray<int> arr1(5);//指定MyArray中的T是int类型
cout << string(30, '-') << endl;
cout << "arr2" << endl;
MyArray<int> arr2(arr1);//测试拷贝构造 深浅拷贝
cout << string(30, '-') << endl;
cout << "arr3" << endl;
MyArray<int> arr3(10);
arr3 = arr1;//测试operator=
cout << string(30, '-') << endl;
}
int main()
{
test();
cout << endl;
system("pause");
return 0;
}
- 创建arr1时,使用有参构造;
- 创建arr2则是使用拷贝构造;
- 创建arr3首先使用有参构造,再把arr1通过赋值=拷贝给arr3
5. 数组类模板要求5-7的步骤及注意点:
- 尾插法:首先判断数组是否满了,如果没有满,再插入新数据。函数的形参通过引用的方式值为val类型为T的常量,还要注意数组的大小要更新;
- 尾删法: 首先判断数组是否空,不为空再做逻辑删除,也就是让数组访问不到最后一个数据;
- 通过下标方式访问数组元素: 需要明确几点,如下
- 通常访问一个数组arr,只需要通过一个 整型下标 和 [] 访问,但这只能访问内置的数据类型。对于自定的数据类型的数组MyArray, [] 是无法访问的,所以需要重载 [] ;
- 通过下标访问数组,这个下标只能是整数,因此形参类型是int
- [] 访问数组时,应该返回该数组的数据,而数据的类型是T,因此重载函数的返回值类型也是T
- [] 访问数组时,如果想做赋值操作,MyArray[index] = 100,也就是这个函数做为左值存在,因此需要返回数据本身,因此返回值需要使用引用的方式,T&;
- 返回数组的容量: 返回自身属性capacity即可;
- 返回数组的大小: 返回自身属性size即可;
- 测试以上五点是否成功实现
6. 数组类模板要求5-7的代码:
.hpp文件加入
//尾插法
void Push_Back(const T& val)//引用的方式传入值为val类型为T的常量
{
//首先判断数组是否满了
if (this->m_Capacity == this->m_Size)
{
cout << "数组已满,无法插入新数据!" << endl;
return;
}
this->pAddress[this->m_Size] = val;//在尾部插入新数据
this->m_Size++;//数组大小要更新
}
//尾删法
void Pop_Back()
{
//判断数组是否为空数组
if (this->m_Size == 0)
{
cout << "数组为空,无法删除!" << endl;
return;
}
//delete this->pAddress[this->m_Size];
this->m_Size--;//逻辑删除,访问不到最后一个数据
}
//通过下标方式访问数组元素
T& operator[](int index)
{
return this->pAddress[index];
}
//返回数组的容量
int getCapacity()
{
return this->m_Capacity;//返回自身属性
}
//返回数组的大小
int getSize()
{
return this->m_Size;//返回自身属性
}
7. 数组类模板要求5-7-内置数据类型测试:
测试文件
//打印数组 内置数据类型
void printIntArr(MyArray<int>& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << arr[i] << '\t';//operator[]
}
cout << endl;
}
//数组类5-7 内置数据类型测试
void test2()
{
int s = 60;
cout << "内置数据类型测试 int" << endl;
cout << string(s, '-') << endl;
cout << "arr1" << endl;
MyArray<int> arr1(5);
for(int i = 0; i < 5; i++)//获取数组大小
{
arr1.Push_Back(i+1);//尾插法
}
cout << "arr1 打印:\t";
printIntArr(arr1);
cout << "arr1 数组容量:" << arr1.getCapacity() << endl;//获取数组容量
cout << "arr1 数组大小:" << arr1.getSize() << endl;
cout << string(s, '-') << endl;
cout << "arr2" << endl;
MyArray<int> arr2(arr1);//拷贝构造
cout << "arr2 尾删前打印:\t";
printIntArr(arr2);
cout << "arr2 尾删前数组容量:" << arr2.getCapacity() << endl;//获取数组容量
cout << "arr2 尾删前数组大小:" << arr2.getSize() << endl;
cout << string(s, '-') << endl;
arr2.Pop_Back();
cout << "arr2 尾删后打印:\t";
printIntArr(arr2);
cout << "arr2 尾删后数组容量:" << arr2.getCapacity() << endl;//获取数组容量
cout << "arr2 尾删后数组大小:" << arr2.getSize() << endl;
cout << string(s, '-') << endl;
cout << "自定义数据类型测试 Person" << endl;
cout << "arr3" << endl;
MyArray<int> arr3(10);
cout << string(s, '-') << endl;
}
8. 数组类模板要求5-7-自定义数据类型测试:
测试文件
class Person
{
public:
Person() {};//默认构造 空实现
Person(string name, int age):m_name(name), m_age(age) { }
string m_name;
int m_age;
};
//打印数组 自定义数据类型
void printPersonArr(MyArray<Person>& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "姓名:" << arr[i].m_name << '\t'
<< "年龄:" << arr[i].m_age << endl;//operator[]
}
}
//数组类5-7 自定义数据类型测试
void test3()
{
int s = 40;
cout << "自定义数据类型测试 Person" << endl;
cout << string(s, '-') << endl;
cout << "arr1" << endl;
MyArray<Person> arr1(10);
Person p1("刘备", 23);
Person p2("关羽", 22);
Person p3("张飞", 29);
Person p4("貂蝉", 24);
Person p5("西施", 25);
arr1.Push_Back(p5);
arr1.Push_Back(p2);
arr1.Push_Back(p3);
arr1.Push_Back(p4);
arr1.Push_Back(p1);
cout << "arr1 打印" << endl;
printPersonArr(arr1);
cout << "arr1 数组容量:" << arr1.getCapacity() << endl;//获取数组容量
cout << "arr1 数组大小:" << arr1.getSize() << endl;
cout << string(s, '-') << endl;
cout << "arr2" << endl;
MyArray<Person> arr2(arr1);//拷贝构造
cout << "arr2 尾删前打印:" << endl;
printPersonArr(arr2);
cout << "arr2 尾删前数组容量:" << arr2.getCapacity() << endl;//获取数组容量
cout << "arr2 尾删前数组大小:" << arr2.getSize() << endl;
cout << string(s, '-') << endl;
arr2.Pop_Back();
cout << "arr2 尾删后打印:" << endl;
printPersonArr(arr2);
cout << "arr2 尾删后数组容量:" << arr2.getCapacity() << endl;//获取数组容量
cout << "arr2 尾删后数组大小:" << arr2.getSize() << endl;
cout << string(s, '-') << endl;
}