C++ 学习之类模板案例 - 实现一个通用的数组类(综合性较强)

C++ 学习之类模板案例 - 实现一个通用的数组类(综合性较强),第1张

C++ 学习之类模板案例 - 实现一个通用的数组类(综合性较强)

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

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

小总结:“拷贝构造” 和 "=赋值 *** 作 " 如果遇到有在堆区开辟的数据,都会有浅拷贝的问题,要写成深拷贝避免重复释放内存。
具体写法见以下代码:

1、第一版测试代码: myarray.hpp: 包含类模板的头文件写法
#ifndef _MYARRAY_HPP_//防止头文件重复包含
#define _MYARRAY_HPP_

#include
#include
using namespace std;

template
class MyArray
{
public:
	MyArray(int Capacity)//有参构造函数
	{
		cout << "有参构造函数调用" << endl;
		this->m_Capacity = Capacity;
		this->m_Size = 0;//初始化数组中元素个数为0
		this->pAddress = new T[this->m_Capacity];//开辟对应数据类型的空间	
	}


	MyArray(const MyArray& arr)//1、自己写拷贝构造函数 防止浅拷贝问题
	{
		cout << "拷贝构造函数调用" << endl;
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		//this->pAddress = arr.pAddress;//编译器提供的,指针简单赋值,最后会导致重复释放内存

		this->pAddress = new T[this->m_Capacity];//深拷贝

		for (int i = 0; i < m_Size; i++)//原来数组中可能已经有数据了,所以要把数据都拷贝过来
		{
			this->pAddress[i] = arr.pAddress[i];
		}
	}


	MyArray& operator=(const MyArray& myarray)//2、重载= 运算符 防止浅拷贝问题
	{
		cout << "=赋值函数调用" << endl;
		if (this->pAddress != NULL) //先判断原来堆区是否有数据,如果有先释放
		{
			delete[] this->pAddress;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		this->m_Capacity = myarray.m_Capacity;
		this->m_Size = myarray.m_Size;
		this->pAddress = new T[this->m_Capacity];

		for (int i = 0; i < this->m_Size; i++)
		{
			this->pAddress[i] = myarray.pAddress[i];
		}

		return *this;//返回对象本身,形成链式编程
	}


	int getCapacity()//获取数组的容量
	{
		return this->m_Capacity;
	}

	int getSize()//获取数组的大小
	{
		return this->m_Size;
	}


	~MyArray()//析构函数
	{
		cout << "析构函数调用" << endl;
		if (pAddress != NULL)
		{
			delete[] pAddress;
			pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
	}


public:
	T* pAddress;   //指向一个堆空间,这个空间存储真正的数据,传进来什么样的数据类型,就开辟对应类型的空间
	int m_Capacity;//数组容量,一共可以存储多少个数据
	int m_Size;    //数组中元素的个数,存完之后数组的元素个数

};

#endif // !_MYARRAY_HPP_

主cpp:
#include
#include
#include"myarray.hpp"//包含类模板头文件
using namespace std;

int main()
{
	

	MyArray arr1(10);//调用有参构造

	MyArray arr2(arr1);//调用拷贝构造

	MyArray arr3(10);//赋值 *** 作
	arr3 = arr1;

	return 0;
}


现象:

2、第二版实现功能代码: myarray.hpp :
#ifndef _MYARRAY_HPP_//防止头文件重复包含
#define _MYARRAY_HPP_

#include
#include
using namespace std;

template
class MyArray
{
public:
	MyArray(int Capacity)//有参构造函数
	{
		this->m_Capacity = Capacity;
		this->m_Size = 0;//初始化数组中元素个数为0
		this->pAddress = new T[this->m_Capacity];//开辟对应数据类型的空间	
	}


	MyArray(const MyArray& arr)//1、自己写拷贝构造函数 防止浅拷贝问题
	{
		if (this->pAddress != NULL) //先判断原来堆区是否有数据,如果有先释放
		{
			delete[] this->pAddress;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		//this->pAddress = arr.pAddress;//编译器提供的,指针简单赋值,最后会导致重复释放内存

		this->pAddress = new T[this->m_Capacity];//深拷贝

		for (int i = 0; i < m_Size; i++)//原来数组中可能已经有数据了,所以要把数据都拷贝过来
		{
			this->pAddress[i] = arr.pAddress[i];
		}
	}


	MyArray& operator=(const MyArray& myarray)//2、重载= 运算符 防止浅拷贝问题
	{
		if (this->pAddress != NULL) //先判断原来堆区是否有数据,如果有先释放
		{
			delete[] this->pAddress;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		this->m_Capacity = myarray.m_Capacity;
		this->m_Size = myarray.m_Size;
		this->pAddress = new T[this->m_Capacity];

		for (int i = 0; i < this->m_Size; i++)
		{
			this->pAddress[i] = myarray.pAddress[i];
		}

		return *this;//返回对象本身,形成链式编程
	}


	T& operator[](int index)//重载[]运算符,实现用下标方式访问数组  例如:arr[1]=100;
	{
		return this->pAddress[index];  //注意返回值的类型要为引用,即返回一个地址,它要作为左值存在,因为你可以修改他的值嘛
	}


	void Push_back(T& data)//尾插法 向数组中插入数据
	{
		//插入之前先判断一下数组是否还有空间,容量是否等于大小
		if (this->m_Capacity == this->m_Size)
		{
			cout << "数组没有空间了" << endl;
			return;
		}

		this->pAddress[this->m_Size] = data;//在数组末尾插入数据,即尾插
		this->m_Size++;//更新数组大小
	}


	void Pop_back()//尾删法  每调用一次,就从数组最后删除一个数据
	{
		//删除之前要判断一下,数组的状态,如果数组中已经没有数据了,那就没法删了
		if (this->m_Size == 0)
		{
			cout << "数组中已没有数据,无法删除" << endl;
			return;
		}
		this->m_Size--;//把数组的大小减一,往前移一个位置让用户访问不到就可以了,即为逻辑删除
	}



	int getCapacity()//获取数组的容量
	{
		return this->m_Capacity;
	}

	int getSize()//获取数组的大小
	{
		return this->m_Size;
	}


	~MyArray()//析构函数
	{
		if (pAddress != NULL)
		{
			delete[] pAddress;
			pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
	}


private:
	T* pAddress;   //指向一个堆空间,这个空间存储真正的数据,传进来什么样的数据类型,就开辟对应类型的空间
	int m_Capacity;//数组容量,一共可以存储多少个数据
	int m_Size;    //数组中元素的个数,存完之后数组的元素个数; 当前数组的大小

};

#endif // !_MYARRAY_HPP_
主cpp :
#include
#include
#include"myarray.hpp"//包含类模板头文件
using namespace std;


void PrintInt(MyArray& array)//将打印整型数组元素封装成一个函数
{
	for (int i = 0; i < array.getSize(); i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
}


void test01()
{
	

	MyArray arr1(10);//调用有参构造
	cout << arr1.getCapacity() << endl;
	cout << arr1.getSize() << endl;

	MyArray arr2(arr1);//调用拷贝构造
	cout << arr2.getCapacity() << endl;
	cout << arr2.getSize() << endl;

	MyArray arr3(10);//赋值 *** 作
	arr3 = arr1;
}


void test02()
{
	

	MyArray arr(10);//设置数组的容量大小

	for (int i = 0; i < 10; i++)//往数组中插入数据
	{
		arr.Push_back(i);
	}

	arr[0] = 100;//设置数组中第一个元素值为100

	arr.Pop_back();//删除一个元素

	PrintInt(arr);//打印数组中的数据

	cout << "----------------------------" << endl;

	MyArray arr2(arr);//调用拷贝构造复制arr的值

	arr2.Pop_back();

	cout << "数组容量:" << arr2.getCapacity() << " " << "数组大小:" << arr2.getSize() << endl;

	PrintInt(arr2);//打印数组中的数据

}




class Person
{
public:
	Person()
	{
		//cout << "默认构造调用" << endl;
	}

	Person(string name, int age)
	{
		//cout << "有参构造调用" << endl;
		m_name = name;
		m_age = age;
	}

	~Person()
	{
		//cout << "析构调用" << endl;
	}

public:
	string m_name;
	int m_age;
};


void PrintPerson(MyArray& p)//将打印Person类型数组封成一个函数
{
	for (int i = 0; i < p.getSize(); i++)
	{
		cout << p[i].m_name<<" "< parray(10);//创建Person类型的数组,并声明数组容量

	Person p1("张三", 20);
	Person p2("李四", 21);
	Person p3("王五", 22);
	Person p4("赵六", 23);

	parray.Push_back(p1);
	parray.Push_back(p2);

	cout << "容量:" << parray.getCapacity() << " " << "大小:" << parray.getSize() << endl;

	PrintPerson(parray);

}



int main()
{
	test01();//测试基本功能

	test02();//测试int类型数组

	test03();//测试自定义Perosn类型数组

	return 0;
}

总结:
这个案例综合性还是比较强的,通过做这个案例,把之前的运算符重载,拷贝构造,类模板知识都复习了一遍,这个案例以后还是经常多敲几遍比较好,巩固一下

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/3971391.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-10-21
下一篇 2022-10-21

发表评论

登录后才能评论

评论列表(0条)

保存