当前位置: 首页 > article >正文

类模板案例:实现一个通用的数组类

3.9 类模板案例

案例描述:

实现一个通用的数组类

要求如下:

(1)、可以对内置数据类型以及自定义数据类型的数据进行存储;

(2)、将数组中的数据存储到堆区;

(3)、构造函数中可以传入数组的容量;

(4)、提供对应的拷贝构造函数以及operator=防止浅拷贝问题;

(5)、提供尾插法和尾删法对数组中的数据进行增加和删除;

(6)、可以通过下标的方式访问数组中的元素;

(7)、可以获取数组中当前元素个数和数组的容量。

myArray.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;
		pAddress = new T[this->m_Capacity];  // 堆区数据,按照容量m_Capacity开辟堆区数组空间;在堆区创建一个对象pAddress
	}

	// 拷贝构造
	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[this->m_Capacity];

		// 将arr中的数据都拷贝过来
		for (int i = 0; i < this->m_Size; i++)
		{
			// 如果T为对象,而且还包含指针,必须需要重载 = 操作符,因为这个等号不是 构造 而是赋值,
			// 普通类型可以直接= 但是指针类型需要深拷贝
			this->pAddress[i] = arr.pAddress[i];
		}
	}

	// 重载= 操作符  防止浅拷贝问题;等号做赋值传递时,他要返回自身的引用,然后可以做连等操作:arr1 = arr2 = arr3
	MyArray& operator=(const MyArray& myarray) {  // 返回自身,使用引用&,MyArray&
		cout << "调用MyArray的重载operator= -----------------------------" << 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;  // 将自身做一个返回
	}

	// 重载[] 操作符  arr[0] = 100;MyArray的对象是自己写的,编译器并不知道对象是啥,所以需要重载
	T& operator [](int index)  // 返回T,每个元素都是T类型;作为左值存在:arr[0] = 100,需返回引用&
	{
		//cout << "调用MyArray的重载operator[]" << endl;
		return this->pAddress[index];  // 不考虑越界,用户自己去处理
	}

	// 尾插法
	void Push_back(const T& val)  //防修改;传入T类型的一个数val,使用引用&
	{
		// 判断容量是否等于大小;容量满时,不可再插入
		if (this->m_Capacity == this->m_Size)
		{
			return;
		}
		// 尾部,就是m_Size,数组最后一个;在数组末尾插入数据
		this->pAddress[this->m_Size] = val;

		// 更新数组大小
		this->m_Size++;
	}

	// 尾删法
	void Pop_back()
	{
		// 让用户访问不到最后一个元素,即为尾删,逻辑删除
		if (this->m_Size == 0)  // 如果数组个数为0,直接返回,不需要删除
		{
			return;
		}
		this->m_Size--;  // 数组下标减1,即数组最后一位访问不到;之前最大下标为m_Size,现在为m_Size-1
	}

	// 获取数组容量
	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;  // 防止野指针
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
	}

private:

	T* pAddress;  // 指向一个堆空间,这个空间存储真正的数据;指针指向堆区开辟的真实数组
	int m_Capacity;  // 容量
	int m_Size;  // 大小
};

main.cpp中代码:

#include "myArray.hpp"  // 包含myArray.hpp文件
#include <string>

void printIntArray(MyArray<int>& arr) {  // 直接传入对象的类型MyArray<int>
	for (int i = 0; i < arr.getSize(); i++) {  // arr.getSize()获取数组大小
		cout << arr[i] << " ";  // arr[],[]进行了重载,所以不会报错
	}
	cout << endl;
}

// 测试内置数据类型
void test01()
{
	MyArray<int> array1(10);  // 模板

	for (int i = 0; i < 10; i++)
	{
		array1.Push_back(i);  // 调用尾插法函数,向数组中插入数据
	}
	cout << "array1打印输出:" << endl;

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

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

	MyArray<int> array2(array1);
	array2.Pop_back();
	cout << "array2打印输出:" << endl;
	printIntArray(array2);
	cout << "array2尾删后的大小:" << array2.getSize() << endl;
	cout << "array2尾删后的容量:" << array2.getCapacity() << endl;
}

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

void printPersonArray(MyArray<Person>& personArr)
{
	for (int i = 0; i < personArr.getSize(); i++) {
		cout << "姓名:" << personArr[i].m_Name << " 年龄: " << personArr[i].m_Age << endl;
	}
}

void test02()
{
	// 创建数组
	MyArray<Person> pArray(10);
	Person p1("孙悟空", 30);
	Person p2("韩信", 20);
	Person p3("妲己", 18);
	Person p4("王昭君", 15);
	Person p5("赵云", 24);

	// 插入数据
	pArray.Push_back(p1);
	pArray.Push_back(p2);
	pArray.Push_back(p3);
	pArray.Push_back(p4);
	pArray.Push_back(p5);

	printPersonArray(pArray);

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

int main() {

	test01();

	//test02();

	system("pause");

	return 0;
}

运行:test01()
在这里插入图片描述
运行:test02()

在这里插入图片描述


http://www.kler.cn/a/5945.html

相关文章:

  • HBuilderX打包ios保姆式教程
  • 基于html5实现音乐录音播放动画源码
  • Mac中配置vscode(第一期:python开发)
  • 单片机实现模式转换
  • UI自动化测试保姆级教程--pytest详解(精简易懂)
  • Git撤销指定commit并更新远端仓库
  • SuperMap GIS基础产品组件GIS FAQ集锦(1)
  • 《数学建模实战攻略:常用数学理论与方法Matlab》
  • 小驰私房菜_04_Camera2 - Image中YUV格式理解
  • 【jmeter+Ant+Jenkins】开展接口自动化测试集成
  • 数据结构——哈希表
  • android 源码framework中加入自定义服务
  • 【全网独家】华为OD机试Golang解题 - 机智的外卖员
  • 我能“C”——详解操作符(下)
  • FFmpeg 入门学习 07--创建音视频解码管理类
  • 图片怎么转PDF文件?三种免费转换方法集合!
  • 3、AI的道德性测试
  • linux宝塔面板安装composer的方法[全网详解]
  • @Transactional和synchronized同时使用时的一些问题以及解决
  • YOLO算法改进指南【算法解读篇】:2.如何训练自己的数据集
  • 13、操作系统——posix信号量(无名信号量)
  • python开启局域网传输
  • 【C++笔试强训】第五天
  • 不相交的集合数据结构
  • PerfEnforce Demonstration: Data Analytics with Performance Guarantees
  • 涨点技巧:Yolov5/Yolov7 引入Yolo-Z---ResneXtBottleneckCSP和DenseBlock,提升小目标检测能力