C++系列-类模板案例


苏轼《饮湖上初晴后雨二首·其二》
水光潋滟晴方好,山色空蒙雨亦奇。
欲把西湖比西子,淡妆浓抹总相宜


案例要求

实现一个通用的数组类,此类主要管理数组,有如下要求:

  1. 可以对自定义的数据类型或者内置数据进行存储
  2. 数组中的数据存储在堆区
  3. 构造函数中传入数组的容量(给数据开辟了多大的空间)
  4. 实现拷贝构造函数,实现=的重载,实现数组的赋值
  5. 实现尾插法和尾删法对数组中的数据增加或删除
  6. 可以通过下标的方式访问数组元素
  7. 可以获取数组中元素的个数(元素不一定要占据所有空间)和数组的容量

案例实现分析

需要由类模板实现,类模板实现分析如下(对应以上各条):
1,类中主要的成员属性是该数组,数组的容量,数组元素的个数,m_p_array, m_capacity, m_size
其中m_p_array是该数组的首地址,指针类型。m:member,p:pointer
2,存储在堆区,需要在构造函数中使用new方法来开辟空间,同时,析构函数也要自己实现,用于
释放堆区空间。
3,有参构造。
4,拷贝构造应用于使用已有对象来实例化一个新的对象。=重载应用于数组的直接赋值
5,通过写成员函数实现,删除元素时,将m_size减小。增加元素时,m_size增加,并给增加的位置
赋值。
6,数组名[]是可以直接访问数组元素的,但是通过对象名[]访问其中的一个属性(数组)中的元素需
进行运算符重载。
7,成员函数实现。
8,再增加拼接数组的代码。

案例实现

总代码

#include <iostream>    
using namespace std;

class Person
{
public:
	string m_name;
	int m_age;
public:
	Person() 
	{
		//cout << "Person无参构造" << endl;
	}
	Person(string name, int age)
	{
		//cout << "Person有参构造" << endl;
		m_name = name;
		m_age = age;
	}
};

// 创建类模板
template<class T>
class MyArray
{
	// 函数模板作为类模板的友元,可以直接把函数的定义部分拷贝进来
	template<class T1>
	friend void print_array(T1 ref_array);
	friend void print_array(MyArray<Person>& ref_array);
private:
	int m_capacity;	// 为数组开辟的内存大小,有些空间不一定有元素
	int m_size;		// 数组中元素的个数
	T *m_p_array;	// 数组的首地址

public:
	MyArray(int capacity)		// 有参构造
	{
		//cout << "有参构造调用" << endl;
		m_capacity = capacity;
		m_size = 0;
		m_p_array = new T[m_capacity];
	}
	
	MyArray(const MyArray& ref_array)		// 拷贝构造
	{
		//cout << "拷贝构造调用" << endl;
		m_capacity = ref_array.m_capacity;
		m_size = ref_array.m_size;
		m_p_array = new T[ref_array.m_capacity];	// 开辟好内存空间
		// 数组中的元素也进行拷贝
		for (int i_loop = 0; i_loop < ref_array.m_size; i_loop++)
		{
			m_p_array[i_loop] = ref_array.m_p_array[i_loop];
		}
	}

	MyArray& operator=(const MyArray& ref_array)	// 实现连等,返回必须是MyArray& 
	{
		//cout << "=运算符重载" << endl;
		// 如果赋值目标对象中原来有数据,先都删除掉,包括堆区内存也要释放,然后再赋值
		if (m_p_array != NULL)
		{
			delete[] m_p_array;
			m_p_array = NULL;
			m_capacity = 0;
			m_size = 0;
		}
		m_capacity = ref_array.m_capacity;
		m_size = ref_array.m_size;
		m_p_array = new T[ref_array.m_capacity];	// 开辟好内存空间
		for (int i_loop = 0; i_loop < m_size; i_loop++)
		{
			m_p_array[i_loop] = ref_array.m_p_array[i_loop];
		}
		return *this;
	}

	T& operator[](int idx)		// 如果要作为左值,返回引用,如a[5]=3;
	{
		//cout << "[]运算符重载" << endl;
		return m_p_array[idx];
	}
	
	void append(T append_data)
	{
		if (m_size == m_capacity)
		{
			cout << "没有空间可以添加元素了" << endl;
			return;
		}
		else
		{
			m_p_array[m_size] = append_data;
			m_size += 1;
		}
	}

	void pop()
	{
		if (m_size == 0)
		{
			cout << "没有元素可以删除了" << endl;
			return;
		}
		else
		{
			m_size --;
		}
	}
	
	void extend(T* normal_array, int array_len) // 函数重载,添加普通的数组元素,不用创建类对象
	{
		cout << "extend(T* normal_array, int array_len)" << endl;
		int i_loop = 0;
		if (m_size == m_capacity)
		{
			cout << "数组容量已满,不能再拼接任何数组" << endl;
			return;
		}
		else if ((m_size + array_len) > m_capacity)
		{
			cout << "添加所有会导致容量超标,只能添加部分元素" << endl;
		}
		for (i_loop = 0; i_loop < array_len; i_loop++)
		{
			if (m_size + i_loop == m_capacity)
			{
				break;
			}
			m_p_array[m_size+ i_loop] = normal_array[i_loop];
		}
		m_size += i_loop;
	}
	
	void extend(MyArray& ref_array)		// 函数重载,添加类对象中的数组元素
	{
		cout << "void extend(MyArray& ref_array)" << endl;
		int i_loop = 0;
		if (m_size == m_capacity)
		{
			cout << "数组容量已满,不能再拼接任何数组" << endl;
			return;
		}
		else if ((m_size + ref_array.m_size) > m_capacity)
		{
			cout << "添加所有会导致容量超标,只能添加部分" << endl;
		}

		for (i_loop = 0; i_loop < ref_array.m_size; i_loop++)
		{
			if (m_size + i_loop == m_capacity)
			{
				break;
			}
			m_p_array[m_size + i_loop] = ref_array.m_p_array[i_loop];
		}
		m_size += i_loop;
	}

	~MyArray()
	{
		//cout << "析构函数调用" << endl;
		if (m_p_array != NULL)
		{
			delete[] m_p_array;
			m_p_array = NULL;
		}
		m_capacity = 0;
		m_size = 0;
	}

	int get_size()
	{
		//cout << "数组的size: " << m_size << endl;
		return m_size;
	}
	int get_capacity()
	{
		//cout << "数组的capacity: " << m_capacity << endl;
		return m_capacity;
	}
};
template<class T>
void print_array(T ref_array)
{
	cout << "capacity: " << ref_array.m_capacity << ", size: " << ref_array.m_size << endl;
	for (int i_loop = 0; i_loop < ref_array.m_size; i_loop++)
	{
		cout << ref_array[i_loop];
		if (i_loop < ref_array.m_size - 1)
		{
			cout << ", ";
		};
	}
	cout << endl;
}

// 已经声明为友元,可以直接访问MyArray对象的私有成员变量
void print_array(MyArray<Person> &ref_array)
{
	cout << "capacity: " << ref_array.m_capacity << ", size: " << ref_array.m_size << endl;
	for (int i_loop = 0; i_loop < ref_array.m_size; i_loop++)
	{
		cout << "name: " << ref_array[i_loop].m_name << ", age: " << ref_array[i_loop].m_age << endl;
	}
	cout << endl;
}

void test01()
{
	cout << "\n\ntest01开始\n" << endl;
	MyArray<int> p1(10);
	cout << "\n---------- 末尾添加元素 ----------" << endl;
	print_array(p1);
	p1.append(1);
	p1.append(2);
	print_array(p1);

	cout << "\n---------- 末尾添加数组 ----------" << endl;
	print_array(p1);
	int normal_array[4] = { 44, 55, 66, 77 };
	p1.extend(normal_array, sizeof(normal_array)/ sizeof(normal_array[0]));
	print_array(p1);

	cout << "\n---------- 末尾删除元素 ----------" << endl;
	print_array(p1);
	p1.pop();
	print_array(p1);
}

void test02()
{
	cout << "\n\ntest02开始\n" << endl;
	MyArray<int> p1(6);
	int normal_array[4] = {11, 22, 33, 44};
	p1.extend(normal_array, sizeof(normal_array) / sizeof(normal_array[0])); // extend函数重载
	print_array(p1);
	MyArray<int> p2(p1);	// 拷贝构造函数
	print_array(p2);
	MyArray<int>p3(6);
	p3 = p1;				// = 赋值重载
	print_array(p3);
	p3.extend(p1);			// extend函数重载
	p3[1] = 666;
	print_array(p3);
}

void test03()
{
	cout << "\n\ntest03开始\n" << endl;
	MyArray<Person> p1(3);		// 会用到无参构造,当自己写有参构造后,系统默认的无参构造不再提供
	Person stu1("张三", 10);
	Person stu2("李四", 8);
	Person stu3("王五", 12);
	p1.append(stu1);
	p1.append(stu2);
	p1.append(stu3);
	print_array(p1);
}

int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

每段代码详细分析

  • 定义一个Person类,用于测试当数组元素为自定义类型Person时,类模板的各部分是否正常。
  • 之所以要写无参构造函数,是因为后面测试中MyArray <Person> p1(3); 会用到无参构造。当重写了有参构造后,默认的无参构造系统不再提供,所以要自己实现。
class Person
{
public:
	string m_name;
	int m_age;
public:
	Person() 
	{
		//cout << "Person无参构造" << endl;
	}
	Person(string name, int age)
	{
		//cout << "Person有参构造" << endl;
		m_name = name;
		m_age = age;
	}
};

  • 定义类模板,其中将要用到的数组的元素类型用模板参数T
  • template<class T1> friend void print_array(T1 ref_array); 声明友元函数,这个友元是一个函数模板。
  • friend void print_array(MyArray& ref_array); 普通友元函数的声明。
template<class T>
class MyArray
{
	// 函数模板作为类模板的友元,可以直接把函数的定义部分拷贝进来
	template<class T1>
	friend void print_array(T1 ref_array);
	friend void print_array(MyArray<Person>& ref_array);
private:
	int m_capacity;	// 为数组开辟的内存大小,有些空间不一定有元素
	int m_size;		// 数组中元素的个数
	T *m_p_array;	// 数组的首地址

  • 有参构造中有形参capacity,用于创建对象时,在堆区开辟空间确定空间大小。
  • 拷贝构造为了在使用已经存在的对象创建初始化新对象时使用,因为有空间开辟在堆区,不能使用默认的拷贝构造函数,否则会引起堆区内存重复释放的问题,所以要在拷贝构造函数中实现深拷贝,重新为新的对象开辟堆区空间。
  • 同时将已有对象中的元素进行拷贝。
public:
	MyArray(int capacity)		// 有参构造
	{
		//cout << "有参构造调用" << endl;
		m_capacity = capacity;
		m_size = 0;
		m_p_array = new T[m_capacity];
	}
	
	MyArray(const MyArray& ref_array)		// 拷贝构造
	{
		//cout << "拷贝构造调用" << endl;
		m_capacity = ref_array.m_capacity;
		m_size = ref_array.m_size;
		m_p_array = new T[ref_array.m_capacity];	// 开辟好内存空间
		// 数组中的元素也进行拷贝
		for (int i_loop = 0; i_loop < ref_array.m_size; i_loop++)
		{
			m_p_array[i_loop] = ref_array.m_p_array[i_loop];
		}
	}


  • 赋值运算符=重载,在对象之间互相赋值操作时,要用到=,为了实现连续赋值,返回值需要是对象本身的引用,*this的引用。
  • 在赋值前,如果赋值目标中原来有数据,先都删除,堆区内存释放,然后再在堆区开辟空间,进行数组中元素的赋值操作。
	MyArray& operator=(const MyArray& ref_array)	// 实现连等,返回必须是MyArray& 
	{
		//cout << "=运算符重载" << endl;
		// 如果赋值目标对象中原来有数据,先都删除掉,包括堆区内存也要释放,然后再赋值
		if (m_p_array != NULL)
		{
			delete[] m_p_array;
			m_p_array = NULL;
			m_capacity = 0;
			m_size = 0;
		}
		m_capacity = ref_array.m_capacity;
		m_size = ref_array.m_size;
		m_p_array = new T[ref_array.m_capacity];	// 开辟好内存空间
		for (int i_loop = 0; i_loop < m_size; i_loop++)
		{
			m_p_array[i_loop] = ref_array.m_p_array[i_loop];
		}
		return *this;
	}

  • 运算符[]重载,可以使用对象直接访问其属性中维护的指针指向的数组中的内容。
  • 返回引用,使得可以对a[]进行赋值操作,直接操作a[],而不是返回a[]的副本。
	T& operator[](int idx)		// 如果要作为左值,返回引用,如a[5]=3;
	{
		//cout << "[]运算符重载" << endl;
		return m_p_array[idx];
	}

  • append在末尾添加元素,当m_capacity已满,说明给数组开辟的内存已经占满了,不能再添加数据。
  • pop删除末尾元素,当m_size=0时,数组里已经没元素了,否则,将m_size-1。
void append(T append_data)
	{
		if (m_size == m_capacity)
		{
			cout << "没有空间可以添加元素了" << endl;
			return;
		}
		else
		{
			m_p_array[m_size] = append_data;
			m_size += 1;
		}
	}

	void pop()
	{
		if (m_size == 0)
		{
			cout << "没有元素可以删除了" << endl;
			return;
		}
		else
		{
			m_size --;
		}
	}

  • extend函数是在数组的末尾拼接一个数组。
  • 要么是拼接普通的数组,要么是拼接一个对象维护的数组。
  • 按照传参进行函数的重载。
void extend(T* normal_array, int array_len) // 函数重载,添加普通的数组元素,不用创建类对象
	{
		cout << "extend(T* normal_array, int array_len)" << endl;
		int i_loop = 0;
		if (m_size == m_capacity)
		{
			cout << "数组容量已满,不能再拼接任何数组" << endl;
			return;
		}
		else if ((m_size + array_len) > m_capacity)
		{
			cout << "添加所有会导致容量超标,只能添加部分元素" << endl;
		}
		for (i_loop = 0; i_loop < array_len; i_loop++)
		{
			if (m_size + i_loop == m_capacity)
			{
				break;
			}
			m_p_array[m_size+ i_loop] = normal_array[i_loop];
		}
		m_size += i_loop;
	}
	
	void extend(MyArray& ref_array)		// 函数重载,添加类对象中的数组元素
	{
		cout << "void extend(MyArray& ref_array)" << endl;
		int i_loop = 0;
		if (m_size == m_capacity)
		{
			cout << "数组容量已满,不能再拼接任何数组" << endl;
			return;
		}
		else if ((m_size + ref_array.m_size) > m_capacity)
		{
			cout << "添加所有会导致容量超标,只能添加部分" << endl;
		}

		for (i_loop = 0; i_loop < ref_array.m_size; i_loop++)
		{
			if (m_size + i_loop == m_capacity)
			{
				break;
			}
			m_p_array[m_size + i_loop] = ref_array.m_p_array[i_loop];
		}
		m_size += i_loop;
	}

  • 析构函数,注意删除数组的方法。
  • 同时将m_capacity =0,m_size=0。
	~MyArray()
	{
		//cout << "析构函数调用" << endl;
		if (m_p_array != NULL)
		{
			delete[] m_p_array;
			m_p_array = NULL;
		}
		m_capacity = 0;
		m_size = 0;
	}

  • 打印数组信息,这两个函数是全局函数,声明为友元,可以访问类中的私有成员。
  • 函数模板声明成友元,template这句也要在友元中,为了避免T的混乱,可以重新叫个名字。
template<class T>
void print_array(T ref_array)
{
	cout << "capacity: " << ref_array.m_capacity << ", size: " << ref_array.m_size << endl;
	for (int i_loop = 0; i_loop < ref_array.m_size; i_loop++)
	{
		cout << ref_array[i_loop];
		if (i_loop < ref_array.m_size - 1)
		{
			cout << ", ";
		};
	}
	cout << endl;
}

// 已经声明为友元,可以直接访问MyArray对象的私有成员变量
void print_array(MyArray<Person> &ref_array)
{
	cout << "capacity: " << ref_array.m_capacity << ", size: " << ref_array.m_size << endl;
	for (int i_loop = 0; i_loop < ref_array.m_size; i_loop++)
	{
		cout << "name: " << ref_array[i_loop].m_name << ", age: " << ref_array[i_loop].m_age << endl;
	}
	cout << endl;
}

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值