(C++基础随笔) 05 C++模板

(C++基础随笔) 05 C++模板,第1张

模板

泛型编程

  • 函数模板
  • 类模板
函数模板 基本写法

这里的typename可以用class替换

template
T func(T t1) {

}
示例
//交换两个数据
template
void myswap(T &t1,T &t2) {
	T tmp = t1;
	t1 = t2;
	t2 = tmp;
}

void test01() {
	//int a = 10, b = 20;
	//myswap(a, b);

	double d1 = 1.0, d2 = 20.1;
	myswap(d1, d2);
	cout << "d1=  " << d1 << "  d2=  " << d2 << endl;
}
  • 函数模板会进行自动类型推导
  • 也可以显式指定类型
//在调用时显式指定
myswap(d1, d2);
注意事项
  • 自动类型推导时必须推导出一致的数据类型T才能使用
	double d1 = 1.0, d2 = 20.1;
	myswap(d1, d2);

	char c = 'c';
	myswap(d1, c);//这样写错
  • 模板必须要确定出T的数据类型,才可以使用
template
void func() {
	cout << "func调用" << endl;

}
void test01() {
	func();//错误
}
数组排序案例
//交换两个数据
template
void myswap(T& t1, T& t2) {
	T tmp = t1;
	t1 = t2;
	t2 = tmp;
}

//选择排序数组
template
void selectSort(T arr[],int lens) {
	for (int i = 0; i < lens; i++) {
		//认定最大值的下标
		int max = i;
		for (int j = i + 1; j < lens; j++) {
			if (arr[max] < arr[j]) {
				max = j;
			}
		}
		if (max != i) {
			myswap(arr[max], arr[i]);
		}
	}
}


void test01() {
	int intArr[] = { 5,2,3,1,4 };
	char charArr[] = "baedc";

	int len = sizeof(charArr) / sizeof(char);
	selectSort(charArr, len);

	for (int i = 0; i < len; i++) {
		cout << charArr[i] << " ";
	}

	/*int len = sizeof(intArr) / sizeof(int);
	selectSort(intArr,len);

	for (int i = 0; i < len; i++) {
		cout << intArr[i] << " ";
	}*/
}
普通函数和函数模板的区别
  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板在 使用自动类型推导时 不会发生隐式类型转换
  • 函数模板在 使用显式指定类型时 可以发生隐式类型转换
//普通函数的隐式类型转换
int func(int a,int b) {
	return a + b;
}

//如果是自动类型推导,不会发生隐式类型转换
template
T func2(T a,T b) {
	return a + b;
}

void test01() {
	int a = 10, b = 20;
	char c = 'a';
	int result=func(a, c);//107
	cout << result << endl;

	//int result2 = func2(a, c);//会报错,因为没有推导出一致的数据类型
	int result2 = func2(a, c);//显式指定类型时,可以发生隐式类型转换
	cout << result2 << endl;
}
普通函数和函数模板的调用规则
  • 普通函数和函数模板可以重载
  • 优先调用普通函数
  • 可以通过空模板参数列表强制调用函数模板
myPrint<>(a,b);
  • 函数模板也可以重载
template
void myPrint(T a,T b) {
	cout << "调用的模板" << endl;
}
template
void myPrint(T a, T b,T c) {
	cout << "调用重载的模板" << endl;
}
  • 如果函数模板可以更好匹配,调用函数模板
模板的局限性

模板也不能直接赋值两个数组,或直接比较两个对象

提出了模板重载
class Person {
public:
	Person(int id, string name) {
		this->pid = id;
		this->pname = name;
	}

	int pid;
	string pname;

};

//对比两个数据是否相等
template
bool myCompare(T t1, T t2) {
	if (t1 == t2) {
		return true;
	}
	else {
		return false;
	}
}

//针对Person类型的函数模板重载,优先调用
template<> bool myCompare(Person& p1, Person& p2) {
	if (p1.pid == p2.pid && p1.pname == p2.pname) {
		return true;
	}
	else {
		return false;
	}
}
/
void test01() {
	//int a = 10, b = 10;
	//bool ret=myCompare(a, b);
	Person p1(1, "alex");
	Person p2(1, "alex");
	bool ret=myCompare(p1, p2);//直接调用不指定类型T的函数模板时会报错
	cout << ret << endl;
}

类模板

要给类模板形参传值

template
class Person {
public:
	Person(NameType name,AgeType age) {
		this->pname = name;
		this->page = age;
	}

	void showPerson() {
		cout << this->pname << "  " << this->page << endl;
	}

	NameType pname;
	AgeType page;
};

void test01() {
	//通过类模板初始化对象,<>里面是模板的参数列表,指定了NameType和AgeType
	Person p1("alex",20);
	p1.showPerson();

}

类模板和函数模板区别
  • 类模板没有自动类型推导的使用方式
Person p1("alex",20);//就是不能写成这样
  • 类模板在模板参数列表中可以有默认参数
template//就是模板声明可以写成这样
类模板中成员函数的创建时机
  • 普通类的成员函数是一开始就创建
  • 类模板中的成员函数在被调用时创建(因为在被调用时才能确定数据类型)
class Person1 {
public:
	void showPerson1() {
		cout << "show person 1" << endl;
	}
};

class Person2 {
public:
	void showPerson2() {
		cout << "show person 2" << endl;
	}
};

template
class myClass {
public:
	T obj;
	void func1() {
		obj.showPerson1();
	}
	void func2() {
		obj.showPerson2();
	}
};

void test01() {
	myClassm;
	m.func1();
	m.func2();//这条代码会出错,因为被调用时,才会创建Person1类的obj对象,而showPerson2不是Person1的成员函数
}
类模板对象做函数参数 三种传入方式
  • 指定传入的类型(最常用) 直接显示对象的数据类型
//指定传入的类型
void printPerson1(Person& p) {
	p.showPerson();
}
  • 参数模板化 将对象中的参数变为模板进行传递
//参数模板化
template
void printPerson2(Person &p) {
	p.showPerson();
	//cout << "T1的类型:  " << typeid(T1).name() << endl;
}
  • 整个类模板化 将整个对象类型 模板化传递
//整个类都模板化
template
void printPerson3(T& p) {
	p.showPerson();
}
template
class Person {
public:
	Person(T1 name,T2 age) {
		this->m_name = name;
		this->m_age = age;
	}
	void showPerson() {
		cout << this->m_name << "   " << this->m_age << endl;
	}

	T1 m_name;
	T2 m_age;

};

//指定传入的类型
void printPerson1(Person& p) {
	p.showPerson();
}

//参数模板化
template
void printPerson2(Person &p) {
	p.showPerson();
	//cout << "T1的类型:  " << typeid(T1).name() << endl;
}

//整个类都模板化
template
void printPerson3(T& p) {
	p.showPerson();
}

void test01() {
	Personp("alex", 21);
	printPerson1(p);
}

void test02() {
	Personp("amy", 20);
	printPerson2(p);
}

void test03() {
	Personp("Peter", 25);
	printPerson3(p);
}
类模板和继承
  1. 当没有指定父类中的T的类型时,无法确定子类应该申请多少内存
  2. 如果想灵活的指定父类中的T类型,子类也要写成类模板
template
class Base {
public:
	T t;
};

class Son :public Base {
	//当没有指定父类中的T的类型时,无法确定子类应该申请多少内存
};

//如果想灵活的指定父类中的T类型,子类也要写成类模板
template
class Son2 :public Base {

};

void test01() {
	Son s;
	Son2 s2;
}
类模板成员函数的类外实现
  1. 需要类模板和函数模板相配合
  2. 需要模板参数列表
template
void Base::printT(){}
示例
template
class Base {
public:
	Base(T t) {
		this->m_t = t;
	}
	void printT();
	T m_t;
};

template
void Base::printT() {
	cout << this->m_t << endl;
}

void test01() {
	char a = 'a';
	Base b1(a);
	b1.printT();
}
类模板分文件编写 问题

类模板中的成员函数的创建时期是在运行时,所以分文件链接阶段会出错

解决方法
  • 直接包含.cpp源文件
#include"Person.cpp"
  • 将声明和实现写道同一个文件中,约定名为.hpp
#include"Person.hpp"
Person.hpp
#pragma once
#include 
using namespace std;

//原.h的内容
template
class Person {
public:
	Person(T1 name, T2 age);
	void printPerson();

	T1 m_name;
	T2 m_age;
};

//原.cpp的内容
template
Person::Person(T1 name, T2 age) {
	this->m_name = name;
	this->m_age = age;
}
template
void Person::printPerson() {
	cout << this->m_name << "  " << this->m_age << endl;
}

类模板与友元
  • 类内实现:直接在类内声明友元
  • 类外实现:要提前让编译器知道全局函数的存在
#pragma once
#include 
using namespace std;


//编译器要先知道有printPerson2()这样一个全局函数存在
template
class Person;

template
void printPerson2(Person p) {
	cout << p.m_name << "  " << p.m_age << endl;
}


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

	//全局函数的类外实现,要加上空模板参数列表!!
	friend void printPerson2<>(Person p);

	T1 m_name;
	T2 m_age;
};


测试
//全局函数类内实现测试
void test01() {
	Person p1("alex", 21);
	printPerson(p1);
}

//全局函数类外实现测试
void test02() {
	Person p1("jelly", 25);
	printPerson2(p1);
}
一个通用的数组模板类 需求分析

MyArray.hpp
#pragma once
#include 
using namespace std;

template
class MyArray {
public:
	MyArray(int cap) {
		cout << "有参构造调用" << endl;
		this->m_cap = cap;
		this->m_size = 0;
		this->pAddress = new T[this->m_cap];//按照m_cap把需要的堆区空间开辟出来
	}
	~MyArray() {
		cout << "析构调用" << endl;
		delete[] this->pAddress;//释放内存
		this->pAddress = NULL;//防止野指针
	}
	//拷贝构造
	MyArray(const MyArray& arr) {
		cout << "拷贝构造调用" << endl;
		this->m_cap = arr.m_cap;
		this->m_size = arr.m_size;

		//深拷贝
		this->pAddress = new T[arr.m_cap];

		//将arr中的数据都拷贝过来
		for (int i = 0; i < arr.m_size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
	}

	//operator=防止浅拷贝问题
	MyArray& operator=(const MyArray& arr) {
		cout << "operator=  调用" << endl;
		//先判断原来堆区是否有数据,有要先释放
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_cap = 0;
			this->m_size = 0;
		}

		//深拷贝
		this->m_cap = arr.m_cap;
		this->m_size = arr.m_size;
		this->pAddress = new T[arr.m_cap];
		for (int i = 0; i < arr.m_size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}

		return *this;
	}

	//尾插法
	void pushBack(const T& val) {
		//判断是不是满了
		if (this->m_size == this->m_cap) {
			return;
		}

		this->pAddress[this->m_size] = val;//在数组末尾插入数据
		this->m_size++;//更新size
	}

	//尾删法
	void popBack() {
		//判断是否为空
		if (this->m_size == 0) {
			return;
		}

		//让用户访问不到尾部元素
		this->m_size--;
	}

	//通过中括号加下标访问数组内的元素
	T& operator[] (int index) {
		return this->pAddress[index];
	}

	int getArrSize() {
		return this->m_size;
	}

	int getArrCap() {
		return this->m_cap;
	}


private:
	T* pAddress;//指针指向堆区开辟的真实数组
	int m_cap;//容量
	int m_size;//大小

};
测试
void test01() {
	//有参构造测试
	MyArray arr1(5);

	arr1.pushBack(5);
	arr1.pushBack(4);
	arr1.pushBack(3);
	arr1.pushBack(2);
	arr1.pushBack(1);
	arr1.popBack();//  5 4 3 2

	int lens = sizeof(arr1) / sizeof(int);
	for (int i = 0; i < lens; i++) {
		cout << arr1[i] << endl;
	}
}

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

原文地址: http://outofmemory.cn/langs/577797.html

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

发表评论

登录后才能评论

评论列表(0条)

保存