C++ 提高编程 第一章 模板

一、模板的概念

模板就是建立通用的模具,大大提高复用性
C++另一种编程思想称为泛型编程,主要利用的技术是模板
C++提高两种模板机制:函数模板和类模板

二、模板的特点

  1. 模板不可直接使用,它只是一个框架
  2. 模板的通用并不是万能的

三、函数模板

函数模板作用:建立一个通用函数,其返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表

1.语法

template<typename T>
函数声明或定义

template——声明创建模板
typename——表明其后面的符号是一种数据结构,可以用class代替
T —— 通用的数据类型,名称可以替换,通常为大写字母

示例代码

template<typename T> //声明一个模板,告诉编译器后面代码紧跟着的T不要报错,T是一个通用数据类型
void Myswap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 10, b = 50;
	cout << a << " " << b << endl;
	Myswap(a, b); //1、自动类型推导
	cout << a << " " << b << endl;
	Myswap<int>(a, b);//2、显示指定类型
}

2.注意事项

自动类型推导,必须推导出一致的数据类型T才可以使用
模板必须要确定出T的数据类型,才可以使用

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

  1. 普通函数调用时可以发生自动类型转换(隐式类型转换)
  2. 函数模板调用时,如果发生自动类型推导,不会发生隐式类型转换
  3. 如果利用显示指定类型的方式,可以发生隐式类型转换

4.普通函数与函数模板的调用规则

  1. 如果普通函数和函数模板都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
void Myswap(T& a, T& b)
{
	cout << "Template called!" << endl; 
	T temp = a;
	a = b;
	b = temp;
}
void Myswap(int& a, int& b)
{
	cout << "function called!" << endl;
	int temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 20, b = 10;
	cout << a << " " << b << endl;
	Myswap<>(a, b);//可以通过空模板参数列表来强制调用函数模板
	cout << a << " " << b << endl;
	return 0;
}
  1. 函数模板也可以发生重载
  2. 如果函数模板可以更好的匹配,则优先调用函数模板
template<class T>
void Myswap(T& a, T& b)
{
	cout << "Template called!" << endl; 
	T temp = a;
	a = b;
	b = temp;
}
void Myswap(char& a, char& b)
{
	cout << "function called!" << endl;
	int temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 20, b = 10;
	cout << a << " " << b << endl;
	Myswap(a, b);//函数模板能产生更好的匹配
	cout << a << " " << b << endl;
	return 0;
}

总结:既然提供了函数模板,最好不要提供普通函数,否则容易产生二义性

四、类模板

类模板作用:
建立一个通用的类,类中成员数据类型可以不具体制定,用一个虚拟的类型来代表

1.语法

template<typename T>
类

template——声明创建模板
typename——表明其后面的符号是一种数据结构,可以用class代替
T —— 通用的数据类型,名称可以替换,通常为大写字母

代码示例

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

int main()
{
	Person<string,int> p1("yxc", 18); //必须声明类型
}

2.类模板与函数模板区别

类模板与函数模板区别主要有两点:

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有参构造
template<class NameType,class AgeType = int> //可以在参数列表指定类型
class Person
{
public:
	NameType name;
	AgeType age;
	Person(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age; 
	}
	void ShowInfo()
	{
		cout << "Name: " << name << " Age:" << age << endl;
	}
};

int main()
{
	Person<string> p1("yxc", 18); //指定后无需在实参列表声明
	p1.ShowInfo();
	return 0;
}

五、模板的局限性

模板的通用性不是万能的

  • 利用具体化的模板,可以解决自定义类型的通用化
  • 学习模板不是为了写模板,而是在STL能够运用系统提供的模板

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

类模板中成员函数在调用时才去创建

七、类模板对象做函数参数

三种传入方式

  1. 指定传入的类型——直接显示对象的数据类型
  2. 参数实例化——将对象中的参数变为模板进行传递
  3. 整个类模板化——将这个对象类型 模板化进行传递
void ShowInfo1(Person<string,int>&p) //1.指定传入类型
{
	cout << "Name: " << p.name << " Age:" << p.age << endl;
}

template<class T1,class T2> //2.参数模板化
void ShowInfo2(Person<T1,T2>&p)
{
	cout << "Name: " << p.name << " Age:" << p.age << endl;
	cout << "T1的类型为" << typeid(T1).name() << " T2的类型为" << typeid(T2).name() << endl;
}

template<class T>
void ShowInfo3(T& p) //3.将整个对象模板化
{
	cout << "Name: " << p.name << " Age:" << p.age << endl;
	cout << "T的类型为" << typeid(p).name() << endl;
}

八、类模板与继承

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

  • 当子类继承的父类是一个类模板时,子类在声明时,要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需变成类模板
template<class T>
class Base
{
public:
	T m;
};
class Son :public Base<int> //必须明确T的类型,才能赋值给子类
{
public:
	Son()
	{
		cout << typeid(m).name() << endl;
	}
};

//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1,class T2>
class Son2 :public Base<T1>
{
public:
	T2 obj;
	Son2()
	{
		cout << typeid(this->m).name() << " " << typeid(obj).name() << endl;
	}
};
int main()
{
	Son s1;
	Son2<char,int> s;
	return 0;
}

九、类模板成员函数类外实现

类模板成员函数类外实现时,需要加上模板参数列表

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

template<class T1,class T2>
Person<T1,T2>::Person(T1 name, T2 age)//模板类的构造函数类外实现
{
	this->name = name;
	this->age = age;
}

template<class T1,class T2>
void Person<T1, T2>::showPerson() //模板类的成员函数类外实现
{
	cout << this->name << " " << this->age << endl;
}

十、类模板分文件编写

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决
直接包含.cpp源文件
将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,不是强制

第一种解决方式:直接包含cpp头文件

第二种解决方式:将.cpp和.h内容写在一起,将后缀名改为.hpp文件

十一、类模板和友元

全局函数类内实现——直接在类内声明友元并实现即可(使用较多)
全局函数类外实现——需要提前让编译器知道全局函数的存在

template<class T1,class T2> //提前让编译器指导Person类的存在
class Person;

template<class T1, class T2>//提前让编译器知道全局函数的存在
void showPerson(Person<T1, T2>& p)
{
	cout << p.name << " " << p.age << endl;
}

template<class T1, class T2>
class Person
{
	friend void showPerson<>(Person<T1,T2>& p); //全局函数类外实现,做类模板的友元,需要加空模板参数列表
private:
	T1 name;
	T2 age;
public:
	Person(T1 name, T2 age);
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)//模板类的构造函数类外实现
{
	this->name = name;
	this->age = age;
}

int main()
{
	Person<string,int> p1("yxc", 18);
	showPerson(p1);
	return 0;
}
posted @   安河桥北i  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示