【C++第三阶段】模板&类模板&通用数组实现案例

以下内容仅为当前认识,可能有不足之处,欢迎讨论!



本教程针对泛型编程和STL做技术讲解,探讨更深层的使用。

模板

什么是模板,模板的目的是什么?怎么用模板,优缺点,能用在哪里。

C++提供两种模板机制,函数模板和类模板。

什么是模板?

建立通用的模具,提高复用性。

模板的目的是什么?

提高代码的复用性,同时解决函数重载的问题:不能根据情况指定不同的返回值类型。以及其他函数重载的问题(2024年3月26日20点44分暂时想不到了)

黑马:建立通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型代表。

怎么使用模板

首先,明确模板的语法如下:

template<typename T>
//后写方法

其中

template 是创建模板的声明,说明下面接着的方法用到了模板。

typename是传入不同类型的参数,这个类型是typename。

黑马:typename-表明其后符号是一种数据类型,可以用class类型。

T是通用数据类型,名称可以替换,通常为大写字母。

接着是模板使用具体案例:

#include<iostream>
#include<string>
using namespace std;

//以下是模板的简单举例
//说明返回值是temp。
template <typename T>
T MySwap(T& param1, T& param2) {
	T temp = param1;
	param1 = param2;
	param2 = temp;
	return temp;
}

void test0326a() {
	int a = 10;
	int b = 20;
	cout << "now , a = " << a << " , b = " << b << " . " << endl;
	//隐式调用模板函数
	MySwap(a, b);
	cout << "After implicitly utilizing swap func , a= " << a << " , b = " << b << " . " << endl;

	//显式调用模板函数
	MySwap<int>(a, b);
	cout << "After explicitly utilizing swap func , a = " << a << " , b = " << b << " . " << endl;
}

int main() {

	test0326a();

	system("pause");
	return 0;
}

可以看到,有两种使用模板方式,隐式及显式。隐式直接传入参数即可,由编译器自行判断参数是何数据类型。显式需要在调用时在函数名与参数列表中用<>说明是何数据类型。

运行结果:

image-20240326231120001

函数模板注意事项

隐式:自动类型推导,必须推导出一致的数据类型T,才可以使用。

显式:模板必须要确定T的数据类型,才可以使用。

注意事项一:必须推导出一致的数据类型T才可以使用。比如下面的将C作为参数传入,识别出两个数据类型不一致。

image-20240326214133982

注意事项二:既然用了函数模板,就需要确定T的数据类型。

像下面的函数,如果隐式地让编译器自动确定数据类型,编译器不知道要确定哪个数据类型。就会报错。所以需要显式地指定数据类型。

image-20240326214501054

image-20240326214454022

所以,解决办法就是显式地指定数据类型。

void test0326c() {
	print<float>();
}

对应代码:

#include<iostream>
#include<string>
using namespace std;

//以下是模板的简单举例
//说明返回值是temp。
template <typename T>
T MySwap(T& param1, T& param2) {
	T temp = param1;
	param1 = param2;
	param2 = temp;
	return temp;
}

template <typename T>
void print() {
	cout << "print 函数运行." << endl;
}

void test0326b() {
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << "原来的a = " << a << " , b = " << b << " . " << endl;
	//MySwap(a, c);
}

void test0326c() {
	print<float>();
}

int main() {

	test0326c();

	system("pause");
	return 0;
}

运行结果:

image-20240326231048933

一个通用模板排序数组的案例代码:

#include<iostream>
#include<string>
using namespace std;

template<typename T>
void MySwap(T &param1 , T &param2){
	T temp = param1;
	param1 = param2;
	param2 = temp;
}

template<typename T>
void print(T& array, int number) {
	//cout << "当前数组为:";
	for (int i = 0; i < number; i++) {
		if (array[i] != '\0') {

			cout << array[i] << "\t";
		}
	}
	cout << endl;
}

template <typename T>
void sort(T& array) {
	int member_number = sizeof(array) / sizeof(array[0]);
	print(array, member_number);
	for (int i = 0; i < member_number; i++) {
		for (int j = i; j < member_number-1; j++) {
			if (array[i] > array[j + 1]) {
				MySwap(array[i], array[j + 1]);
			}
		}
	}
	print(array, member_number);
}



int main() {
	int int_array[] = { 2,9 ,3, 5 , 1 };
	float float_array[] = { 3.1 , 9.1 , 3.6 , 5.2 };
	char char_array[] = "fezcda";
	sort(char_array);
	sort(int_array);
	sort(float_array);
	system("pause");
	return 0;
	}

运行结果:

image-20240326230951333

普通函数与函数模板的区别

区别:是否会发生隐式类型转换。

A—普通函数传入参数时,会发生隐式类型转换。

B—a函数模板传入参数,如果是隐式地自动类型推导,不会发生隐式类型转换,会发生报错。

B—b函数模板传入参数,如果是显式地指定类型方式,会发生隐式类型转换。

根据以上三个编号,分别设计代码验证。

#include<iostream>
#include<string>
using namespace std;

//设计简单的加法函数与加法重载
int MyAdd(int a, int b) {
	//如果是引用方式就不可以
	return a + b;
}

template<typename T>
T MyTempAdd(T a, T b) {//如果是引用方式就不可以
	return a + b;
}


int main() {
	int int_array[] = { 2,9 ,3, 5 , 1 };
	float float_array[] = { 3.1 , 9.1 , 3.6 , 5.2 };
	char char_array[] = "fezcda";

	int a = 10;
	int b = 13;
	char c = 'c'; //ASCII = 99
	cout <<"myadd a+b = " << MyAdd(a, b) << endl;
	cout << "myadd a+c = " << MyAdd(a, c) << endl;
	cout << "MyTempAdd a+b = " << MyTempAdd(a, b) << endl;
	cout << "MyTempAdd a+c = " << MyTempAdd<int>(a, c) << endl;//必须显式指定数据类型,否则会报错。
	system("pause");
	return 0;
}

image-20240326230719245

运行结果:

image-20240326230823336

问题:这里就不能再传入引用,如果引用,必须是对应的数据类型,无论是函数还是模板。

普通函数与函数模板调用规则

1.如果函数模板和普通函数都可以实现,优先调用普通函数。

2.可以通过空模板参数列表来强制调用函数模板。

3.函数模板也可以发生重载。

4.如果函数模板可以产生更好的匹配,优先调用函数模板。

1.优先调用普通函数

#include<iostream>
#include<string>
using namespace std;

void print( int a) {
	cout << "调用普通函数" << endl;
}

template<typename T>
void print(T a) {
	cout << "调用函数模板" << endl;
}

void test0326e() {
	int a = 1;
	print(a);
}

int main() {
	test0326e();

	system("pause");
	return 0;
}

通过结果可以看出,优先调用了普通函数。

image-20240327000647436

2.可以通过空模板参数列表强制调用函数模板。

在前面加上空的英文书名号就可以。

更改test0326e()函数中代码为:

void test0326e() {
	int a = 1;
	print<>(a);
}

即可优先调用函数模板

image-20240327000801084

3.函数模板也可以发生重载。

参数个数不同,参数类型不同。

#include<iostream>
#include<string>
using namespace std;


template<typename T>
void print(T a) {
	cout << "调用函数模板" << endl;
}

template<typename T>
void print(T a, T b) {
	cout << "调用重载的函数模板" << endl;
}

void test0326e() {
	int a = 1;
	int b = 2;
	print<>(a ,b);
}

int main() {
	test0326e();

	system("pause");
	return 0;
}

可以看到,调用了重载的函数模板。

image-20240327000930124

4.如果函数模板可以产生更好的匹配,优先调用函数模板。

怎么理解?比如下面代码,传入的是字符,在普通函数中,可以隐式转换数据类型到int,但是函数模板可以不用隐式转换直接到char。所以编译器会执行进函数重载中。

#include<iostream>
#include<string>
using namespace std;

void print( int a) {
	cout << "调用普通函数" << endl;
}

template<typename T>
void print(T a) {
	cout << "调用函数模板" << endl;
}


void test0326e() {
	int a = 1;
	int b = 2;
	char c = 'c';
	print<>(c);
}

int main() {
	test0326e();

	system("pause");
	return 0;
}

运行结果:

image-20240327001147736

函数模板限制

普通的数据类型,当然可以运算。

#include<iostream>
#include<string>
using namespace std;

template<typename T>
bool MyCompare(T a, T b) {
	if (a == b) {
		return true;
	}
	else {
		return false;
	}
}

void test0326b() {
	int a = 10;
	int b = 10;
	bool res = MyCompare(a, b);
	if (res) {
		cout << "a == b" << endl;
	}
	else {
		cout << "a != b" << endl;
	}
}

int main() {
	test0326b();


	system("pause");
	return 0;
}

运行结果:

image-20240326235833087

如果是自定义的数据类型,也会出现无法运算的时候。

如果是自定义数据类型,就如果想要走特殊的通道到达对应的函数模板,则需要特殊操作。

#include<iostream>
#include<string>
using namespace std;

class Person {
public:
	Person(int age, string name) {
		this->p_age = age;
		this->p_name = name;
	}

public:
	int p_age;
	string p_name;
};


template<typename T>
bool MyCompare(T a, T b) {
	if (a == b) {
		return true;
	}
	else {
		return false;
	}
}

template<> bool MyCompare(Person p1, Person p2) {
	if (p1.p_age == p2.p_age && p1.p_name == p2.p_name) {
		return true;
	}
	else {
		return false;
	}
}

void test0326b() {
	int a = 10;
	int b = 10;
	bool res = MyCompare(a, b);
	if (res) {
		cout << "a == b" << endl;
	}
	else {
		cout << "a != b" << endl;
	}
}

void test0326d() {
	Person tom(12, "Cooper");
	Person Jack(12, "Cooper");
	bool res = MyCompare(tom, Jack);

	if (res) {
		cout << "a == b" << endl;
	}
	else {
		cout << "a != b" << endl;
	}
}

int main() {
	test0326d();
	//test0326b();


	system("pause");
	return 0;
}

运行结果:

image-20240327000237165

类模板

类模板语法

目的/作用:

建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型来代表。

我理解:望名思义,以为是通用的类,类名也是通用的。但其实是类里面的属性或者方法,可以通用。

语法:

template <typename T1 ,typename T2 , typename ,Tn>
class{}

template 声明创建模板

typename 表明后面的符号是一种数据类型,具体是什么数据类型可以自己指定。

T 通用的数据类型,名称可以替换,通常为大写字母。

案例代码:

#include<iostream>
#include<string>
using namespace std;

template<typename name_type , typename age_type>
class Person {
public:
	Person(name_type name, age_type age) :p_name(name), p_age(age) {}

public:
	name_type p_name;
	age_type p_age;

public:
	void print() {
		cout << "name = " << p_name << " , age = " << p_age << " . " << endl;
	}
};

void test0327a() {
	Person <string, int>p("Json", 23);
	p.print();
}

int main() {
	//test0326e();
	test0327a();

	system("pause");
	return 0;
}

运行结果:

image-20240327194736825

类模板与函数模板区别

区别①类模板不能自动推导类型。

函数模板可以通过传入参数,由编译器判断谁是什么数据类型,而类模板没有这个功能。

区别②类模板可以对类型赋默认值。

如果类中成员属性≥1,可以对成员属性的类型赋默认值。

以下是两点具体代码体现。

void test0327b() {
	Person p("docker", 99);
	p.print();
}

区别①会报错:

image-20240327200148738

区别②:

#include<iostream>
#include<string>
using namespace std;

template<typename name_type , typename age_type = int>//体现区别2,可以对类型赋默认值。
class Person {
public:
	Person(name_type name, age_type age) :p_name(name), p_age(age) {}

public:
	name_type p_name;
	age_type p_age;

public:
	void print() {
		cout << "name = " << p_name << " , age = " << p_age << " . " << endl;
	}
};
}

void test0327b() {
	//Person p("docker", 99);
	//p.print();
}

void test0327c() {
	Person <string>p("Coco", 33);//体现2,可以对类型赋默认值,使得string数据类型是参数1,默认数据类型是参数2
	p.print();
}

int main() {
	test0327c();

	system("pause");
	return 0;
}

运行结果:

image-20240327200302791

类模板中成员函数创建时机

类模板成员函数和普通类中成员函数创建时机有区别。

  • 普通类成员函数一开始创建。
  • 类模板成员函数调用时创建。

问题:什么叫调用时才创建?

因为具体的类有明确的数据类型,而类模板没有指定的数据类型,所以一开始时不会创建,在实际调用时才会创建。

代码示例:

#include<iostream>
#include<string>
using namespace std;

class Per {
public:
	void print() {
		cout << "Per类调用print函数" << endl;
	}
};

class Son {
public:
	void show() {
		cout << "Son类调用show函数" << endl;
	}
};

template<class T>
class Person {
public:
	T obj;//这一点在自己编写时忘记了
public:
	void p_print(){
		obj.print();
	}
	void p_show() {
		obj.show();
	}
};

void test0327d() {
	Person <Per> per;
	per.p_print();
	//per.p_show();
	Person <Son> son;
	//son.p_print();
	son.p_show();
}
int main() {
	test0327d();

	system("pause");
	return 0;
}

如果不注释per.p_print();son.p_print();,就会报错:image-20240327202523442

注释掉之后,能有运行结果:

image-20240327202555263

类模板对象做函数参数

掌握:类模板实例化出的对象,向函数传参的方式

什么意思?就是将对象作为参数传入函数中。但此时的对象是类模板对象,所以又有模板数据类型不确定的问题在里面。就会有对应三种类模板对象做函数参数的方法。

三种传入方式:

①指定传入类型——直接显示对象的数据类型。

②参数模板化——将对象中的参数变为模板进行传递。

③整个类模板化——将这个对象类型模板化进行传递。

第一种方法,指定传入类型,就是在将类模板对象作为参数传入普通函数时,直截了当的在函数定义时就说明类模板对象的模板数据类型是什么。

第二种,参数模板化——将对象中的参数作为模板传递。

此时函数不再是普通函数,而是模板函数。类模板对象中的模板数据类型需要与模板函数中的模板数据类型保持一致。

第三种,整个类模板化——将这个对象类型作为模板进行传递。

示例代码:

#include<iostream>
#include<string>
using namespace std;

template<class T1 , class T2>
class Person {
public:
	Person(T1 name, T2 age):p_name(name),p_age(age) {}

public:
	T1 p_name;
	T2 p_age;

public:
	void print() {
		cout << "name = " << p_name << " , age = " << p_age << " . " << endl;
	}
};

//方式1:指定类模板对象模板数据类型,把类模板对象作为参数传入函数中。
void CommonPrintPerson(Person<string , int> &p) {
	//现在这是个普通函数
	p.print();
}

//方式2:参数模板化,使用模板函数,把类模板对象中的成员属性类型作为模板数据类型传入。
template<typename T1 , typename T2>
void TemplatePrintPerson(Person <T1, T2>& p) {
	p.print();
}

//方式3:对象模板化,将类模板对象也作为模板数据类型传入函数中
template <class T3>
void TemplateClassPrintPerson(T3 p) {
	p.print();
}


void test0327e() {
	Person<string, int>p("Jack", 22);
	Person<string, int>e("Eoa", 32);
	Person<string, int>r("Ross", 32);
	//普通函数调用时,指定模板数据类型将类模板对象作为参数传入函数中。
	CommonPrintPerson(p);
	TemplatePrintPerson<string, int>(e);
	TemplateClassPrintPerson(r);
}

int main() {

	test0327e();

	system("pause");
	return 0;
}


运行结果:

image-20240327210559275

打印类别

补充的一点是可以通过typeid(要查看的数据类型).name()获得是什么类别。

比如

类模板与继承

当类模板碰到继承时,需要注意以下几点:

如果子类继承的父类是一个类模板时,子类在声明时,需要指定父类中T的类型。如果不指定,编译器无法给子类分配内存。

如果想灵活指定父类中T的类型,子类也需要变为类模板。

为什么不指定父类T中的类型,编译器无法给子类分配内存呢?因为不确定父类有什么数据类型。

所以,对于第一点显式指定父类数据类型,指定的模板数据类型需要写在父类的右边。

image-20240327213010633

如果想要灵活指定父类中的模板数据类型,子类也需要变为类模板。

代码示例如下:

#include<iostream>
#include<string>
using namespace std;

template <class T>
class Base {
public:
	T member;
};

//显式指定父类模板数据类型
class Under :public Base<int> {
public:
	Under() {
		cout << "父类继承子类,显式指定类模板数据类型" << endl;
		cout << "类模板数据类型为:" << typeid(member).name() << "."<<endl;
	}

};

//灵活指定父类模板,需要子类也是类模板
template <class T1 , class T2>
class Bottom :public Base<T2> {
public:
	Bottom() {
		cout << "灵活指定父类模板,需要子类也是类模板" << endl;
		//cout << "父类类模板数据类型为:" << typeid(member).name() << "." << endl;
		//需要注释掉才能运行,说明子类先构造,父类才构造。而显式指定的已经指定了。
		cout << "子类类模板数据类型为:" << typeid(B_member).name() << "." << endl;
	}
public:
	T2 B_member;
};

void test0327f() {
	Under u;
	Bottom<int, char>bo;
}

int main() {
	test0327f();
	system("pause");
	return 0;
}

运行结果:

image-20240327213707604

类模板成员函数类外实现

学习目标:掌握类模板外的成员函数类外实现

成员函数类外实现,需要加上类模板的说明,同时加上类模板参数。

代码说明:

#include<iostream>
#include<string>
using namespace std;

template<class T1 , class T2 >
class Person {
public:
	Person(T1 name , T2 age);
public:
	T1 p_name;
	T2 p_age;
public:
	void show();
};

template<class T1 , class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->p_name = name;
	this->p_age = age;
}

template<class T1 , class T2>
void Person<T1 , T2>::show() {
	cout << "name = " << this->p_name << " , age = " << this->p_age << " . " << endl;
}

void test0327g() {
	Person <string, int>p("Ross", 33);
	p.show();
}

int main() {
	test0327g();


	system("pause");
	return 0;
}

运行结果:

image-20240327221140056

类模板分文件编写

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。

解决:

①直接包含cpp源文件。

②将声明和实现写入同一个文件中,并改后缀名为.hpp,约定名称,不强制。

类模板中的成员函数创建时机是在调用阶段,调用才会创建,一开始不创建。所以要么包含源码—cpp文件,要么写入同一个文件中,后缀名是’hpp’。

防止头文件重复包含

类模板与友元

学习目标:掌握类模板配合友元函数的类内和类外实现

全局函数类内实现👉直接在类内声明友元即可。

全局函数类外实现👉需要提前让编译器知道全局函数的存在。

我的理解:加了友元标识符之后,类内的函数就变成了全局函数。所以可以在类外直接调用。

弹幕:printPerson函数不加friend时是一个私有的成员函数,但加了friend之后就变成了一个全局函数,因为自身的成员函数默认就可以调用自身的成员属性不需要友元,反之需要友元的都不是成员函数。

全局函数类内实现就一步到位。

代码示例:

#include<iostream>
#include<string>
using namespace std;

template<class T1 , class T2>
class Person {
	//类内全局函数
	friend void printPerson(Person<T1,T2> p) {
		cout << "全局函数类内实现" << endl;
		cout << "name = " << p.p_name << " , age = " << p.p_age << " . " << endl;
	}
public:
	Person(T1 name, T2 age) :p_name(name), p_age(age) {}
private:
	T1 p_name;
	T2 p_age;
};

void test0327h() {
	Person <string, int> p("张三", 34);
	printPerson(p);
}

int main() {
	test0327h();


	system("pause");
	return 0;
}

运行结果:

image-20240327231909161

全局函数类外实现,由于模板函数的特殊,若将类外模板函数的实现写于声明后,编译器不认识。并且,类内的函数声明是普通的函数声明,需要将其转换成模板函数的声明,

image-20240327232647811

如图,是全局函数类外实现写在了声明后,编译器不认识。

如果写在声明前,由于传入了Person类,编译器也不认识Person类,还需要把Person类的声明也写在前面。

image-20240327232355135

接下来,还是不行,因为这样相当于全局函数是普通函数,需要加上空模板参数列表让编译器认为它是函数模板。

最后,终于成功生成。

image-20240327232714203

完整代码:

#include<iostream>
#include<string>
using namespace std;

template<class T1, class T2>
class Person;

template<class T1, class T2>
void showPerson(Person<T1, T2> p) {
	cout << "全局函数类外实现" << endl;
	cout << "name = " << p.p_name << " , age = " << p.p_age << " . " << endl;
}

template<class T1 , class T2>
class Person {
	//类内全局函数
	friend void printPerson(Person<T1,T2> p) {
		cout << "全局函数类内实现" << endl;
		cout << "name = " << p.p_name << " , age = " << p.p_age << " . " << endl;
	}

	friend void showPerson<>(Person <T1, T2> p);
public:
	Person(T1 name, T2 age) :p_name(name), p_age(age) {}
private:
	T1 p_name;
	T2 p_age;
};


void test0327h() {
	Person <string, int> p("张三", 34);
	//printPerson(p);
	showPerson(p);
}

int main() {
	test0327h();


	system("pause");
	return 0;
}

运行结果:

image-20240327232732006

看弹幕:友元不是类的成员,也不接受它所在区域访问控制级别的约束。

所以,一般都是全局函数类内实现。当然,也可以直接在类里面写上函数模板。

template<class T1 , class T2>
friend void showPerson(Person <T1, T2> p);

一样可以运行。

类模板案例-通用数组实现

制作一个通用的数组类,可以满足如下要求:

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

自己的分析:

①,可以用类模板存储元素。

②,在每个数据存储时,也就是构造函数,需要new一个数据到堆区。同时析构函数需要释放内存。

③,怎么理解?——传入数组容量,才能确定开辟多少内存空间。

④,这个拷贝构造函数,说的是类的拷贝。重写=,就是在类中重写=号运算符。

⑤,尾插法,尾删法。提供两种方法,参数是什么?对于插入,参数就是新的数据元素;对于删除,可以没有参数。内存空间的扩充怎么做?参考职工管理系统。内存空间的删除,就是直接将最后一个元素内存地址释放,并将数组个数-1。

⑥传入对应的数字,获取其东西。

CommonArray.hpp

#include<iostream>
#include<string>
using namespace std;

当前元素个数和数组容量。
*/
template <class T>
class CommonArray;




template<class T>
class CommonArray {
public:
	CommonArray(int num);
	CommonArray(CommonArray& ca);
	~CommonArray();
public:
	
	//这个代表什么意思?为什么要是创建一个T类型的东西?这个东西再指向一个数组?
	T* array;
private:
	int ca_num;
	int ca_size;
public:
	void tail_insert(const T &element);
	void tail_delete();
	int get_array_size();
	int get_element_number();
	int get_common_array_cost();
	CommonArray& operator=(const CommonArray& ca);
	T& operator[](int index);
};

template<class T>
CommonArray<T>::CommonArray(int size) {

	//cout << "有参构造函数调用" << endl;
	//容量:数组内一共可以放ca_num个元素。
	this->ca_size = size;
	//新建一个T类型的数组指针,并把地址设为null
	//this->array = NULL;
	//开辟这么大一个空间的数组。
	this->array = new T[this->ca_size];
	this->ca_num = 0;
};

template<class T>
CommonArray<T>::CommonArray(CommonArray& ca) {

	//cout << "拷贝构造函数调用" << endl;
	this->ca_size = ca.ca_size;
	this->ca_num = ca.ca_num;
	this->array = new T[ca.ca_size];
	for (int i = 0; i < ca.ca_size; i++) {
		this->array[i] = ca.array[i];
	}
}

//拷贝构造函数和重写=运算符防止编译器提供的两个函数造成浅拷贝现象。
//如果浅拷贝,删除时会导致堆区数据重复释放。

template<class T>//返回值左值存在,应该要返回引用。
CommonArray<T>& CommonArray<T>::operator=(const CommonArray<T>& ca) {

	//cout << "operator=函数调用" << endl;
	this->ca_size = ca.ca_size;
	this->ca_num = ca.ca_num;
	this->array = new T[ca.ca_size];
	for (int i = 0; i < ca.ca_size; i++) {
		this->array[i] = ca.array[i];
	}
	return *this;
}

template<class T>
CommonArray<T>::~CommonArray() {
	if (this->array) {

		delete[] this->array;
		this->array = 0;
		this->ca_size = 0;
		this->ca_num = 0;
	}
}

//为了防止T被修改,一般用引用的方式传入,使用const
template<class T>
void CommonArray<T>::tail_insert(const T &element) {
	if (this->ca_num != this->ca_size) {
		this->array[this->ca_num] = element;
		this->ca_num += 1;
	}
	else {
		cout << "数组已满,无法新增。" << endl;
		system("pause");
		return;
	}
}

template<class T>
void CommonArray<T>::tail_delete() {
	this->ca_num--;
}

template<class T>
int CommonArray<T>::get_array_size() {
	return this->ca_size;
}


template<class T>
int CommonArray<T>::get_element_number() {
	return this->ca_num;
}

template<class T>
int CommonArray<T>::get_common_array_cost() {
	int cost;
	cost = sizeof(T) * this->ca_size;
	return cost;
}

template<class T>
//返回值是T是因为要作为返回值,又因为想让它作为左值存在,比如int i = 0 ,就需要让他放回一个引用。。
T& CommonArray<T>::operator[](int index) {
	return this->array[index];
}

测试文件.cpp

#include<iostream>
#include<string>
using namespace std;

#include"CommonArray.hpp"

class Person {
public:
	Person() {}
	Person(string name, int age) :p_name(name),p_age(age) {}
public:
	string p_name;
	int p_age;
};

void Print_Array(CommonArray<int>& common_array) {
	for (int i =0 ; i< common_array.get_element_number(); i++){
		cout << common_array.array[i] << " ";
	}
}

void Print_Person_Array(CommonArray<Person>& common_array_persons) {
	for (int i = 0; i < common_array_persons.get_element_number(); i++) {
		cout << "数组成员" << i + 1 << ": 姓名:" << common_array_persons.array[i].p_name<<"\t,";
		cout << "\t年龄:" << common_array_persons.array[i].p_age << " \t. " << endl;
	}

}

void test0328() {
	CommonArray<int> ca(5);
	for (int i = 0; i < ca.get_array_size(); i++) {
		ca.tail_insert(i);
	}
	cout << "ca原先为:" ;
	Print_Array(ca);

	ca.tail_delete();
	cout << "现在的ca容量大小为:" << ca.get_array_size() << endl;
	cout << "现在的ca 元素个数为:" << ca.get_element_number() << endl;
	cout << "ca现在为:";
	Print_Array(ca);
	cout << endl;
	CommonArray<Person> persons(5);
	Person p1("孙悟空", 999);
	Person p2("韩信", 19);
	Person p3("爪云", 39);
	Person p4("张飞", 29);
	Person p5("安其拉", 18);

	persons.tail_insert(p1);
	persons.tail_insert(p2);
	persons.tail_insert(p3);
	persons.tail_insert(p4);
	persons.tail_insert(p5);
	
	Print_Person_Array(persons);

}

int main() {
	test0328();


	system("pause");
	return 0;
}

image-20240402203442273


以上是我的学习笔记,希望对你有所帮助!
如有不当之处欢迎指出!谢谢!

学吧,学无止境,太深了

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++ 中的类模板通用指针数组是一种可以存储任意类型对象的数组,它使用模板技术实现通用性。该数组可以存储任意类型的指针,包括基本数据类型、自定义类类型等。通用指针数组实现数据结构和算法时非常有用。 使用类模板通用指针数组时,需要定义一个模板类,该类包含一个指针数组和相关的操作函数,如添加元素、删除元素、获取元素等。在使用时,需要指定数组元素类型,即实例化该模板类。例如,可以定义一个通用指针数组类模板如下: ``` template<typename T> class PointerArray { private: T** array; int size; public: PointerArray(int size) { this->size = size; array = new T*[size]; } ~PointerArray() { for(int i = 0; i < size; i++) { delete array[i]; } delete[] array; } void add(T* value) { for(int i = 0; i < size; i++) { if(array[i] == NULL) { array[i] = value; break; } } } void remove(T* value) { for(int i = 0; i < size; i++) { if(array[i] == value) { array[i] = NULL; break; } } } T* get(int index) { if(index < 0 || index >= size) { return NULL; } return array[index]; } }; ``` 在实例化该模板类时,可以指定数组元素类型,例如: ``` PointerArray<int> intArray(10); PointerArray<string> stringArray(20); ``` 这样就可以创建一个可以存储 int 类型和 string 类型对象的通用指针数组。在使用时,可以调用该类的添加、删除、获取元素等函数。例如,可以添加一个 int 类型的指针到 intArray 中: ``` int* p = new int(5); intArray.add(p); ``` 也可以获取 intArray 中的某个元素: ``` int* p = intArray.get(0); ``` 总之,类模板通用指针数组是一种非常灵活的数据结构,可以存储任意类型的对象,具有广泛的应用价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HelpFireCode

随缘惜缘不攀缘。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值