c++知识点总结--函数模板

模板函数—使用泛型来定义函数

//example:
template<typename T>
void Swap(T &a,T &b) // 声明或定义
{
	T temp;
	temp = a; 
	a = b;
	b = temp;
}
int main()
{
	int a = 12;
	int b = 13;
	Swap(a,b);
	cout<<a<<"  "<< b<<endl;
	Swap<int>(a,b);
	cout<<a<<"  "<< b<<endl;

}

第一行指出建立一个模板类,并将类型命名为T,关键字templatetypename是必须的,除非使用关键字class代替typename(两个关键字是等价的)
模板函数使用: ------必须确定T的数据类型,才可以使用。
1自动类型推导: Swap(a,b);-----推导出必须·一致的数据类型
2显示指定类型:Swap<int>(a,b);
普通函数和函数模板的区别:
1普通函数调用可以发生隐式类型转换
2对于函数模板,在自动类型推导下,不可以发生隐式类型转换,但在显示指定类型下可以发生隐式类型转换。
普通函数和模板函数调用规则:
1如果函数模板和普通函数都可以调用,优先调用普通函数
2可以通过空模板参数列表,强制调用函数模板 Swap<>(a,b);
3函数模板可以发生重载
4如果函数模板可以产生更好的匹配,优先调用函数模板
总结:在实际中,提供了模板,就不要在提供普通函数。
模板不是万能的,对于特殊数据类型,需要具体化相应的数据类型的实现。
函数模板显式具体化: template<> void Swap(job &a,job &b); job是类类型,需要给出具体的函数实现
函数模板显式实例化:template void Swap(char &a,char &b);就这一句就可以,编译时就提前生成char类型的函数。
上述两种只能择其一。

//多参数模板
template<typename T1,class T2>
void Print(T1 &a,T2 &b) // 声明或定义
{
	cout<<a<<"  "<<b<<endl;
}
int main()
{
	int a = 12;
	double b = 13;
	Print(a,b);
	Print<int,double>(a,b);
}
struct Node{
	int x;
	int y;
};

template<typename T>
void Print(T &a) // 声明或定义
{
	cout<<a<<"  "<<endl;
}
template void Print(int& a);
template<> void Print<Node>(Node &n){//显示具体化
	cout<<n.x<<"   "<<n.y<<endl;
}
int main()
{
	Node node;
	node.x =12;
	node.y = 2;
	Print<Node>(node);
	int a = 12;
	Print<int>(a);
}

类模板:
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。
1基础类模板定义:

//template <class nametype, class agetype>//多个
template <class Type>
class className{
private:
	Type items[1];
	int top;
	......;
public:
	className();
	bool isempty();
	..........;
};
template <class Type>
className<Type>::className(){...}
template <class Type>
bool className<Type>::isempty(){...}

上面是类实现,类模板一般将实现和声明放一起。且每一个函数前都要使用template
如果一定要类外但同一个文件中实现则需要1声明模板,2声明作用域classname<Type>如Person类类外实现构造函数: template<class T> Person<T>::Person(T name){...};
如果分文件编写,则在需要该类时,包含头文件为“xxxx.cpp”直接包含源文件,在源文件中,实现成员函数,同上
对类模板而言,并没有自动类型推导;类模板在模板参数列表可以有默认参数 ----- template <class Type = int>;则,使用时Person p(2)不会报错;
对类模板而言,成员函数是在调用时创建的,而普通类中的成员函数是一开始创建的。

模板类的继承
在模板类的继承中,需要注意以下几点:
• 如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化(普通的也一样)。
• 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间

class ChildOne:public Parent<int>{
....
};

• 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类

template <typename T>
class ChildTwo:public Parent<T>{
....
};

3类模板与友元
全局函数类内声明+类外实例化—直接在类内声明友元即可;在类中:friend void print(Person<T> p){....}; ----重要

template<class T>
class Node
{
	private:
		T x;
		T y;
		static int all;
	public:
		Node(T a,T b);
		friend void print();//print是所有模板的友元函数,由于没有对象,故只能访问静态变量
		friend void print1(Node<T> &);//这里的形参是Node<T>,而不能是Node,因为不存在Node这种类型,只能模板具体化
};
template<class T>
int Node<T>::all = 0;

template<class T>
Node<T>::Node(T a,T b){
	all++;
	x=a;
	y=b;
}
void print(){
	cout<<Node<int>::all<<endl;//统计的是int型的个数
}
void print1(Node<int> &n)//模板具体化
{
	cout<<n.all<<"  "<<n.x<<"  "<<n.y<<endl;
}
void print1(Node<double> &n)//模板具体化
{
	cout<<n.all<<"  "<<n.x<<"  "<<n.y<<endl;
}
int main()
{
	Node<int> n1(1,1);
	print();//1
	print1(n1);//1 1 1
	Node<int> n2(2,2);
	print();//2
	print1(n2);//2 2 2
	Node<double> n3(1.2,1.2);
	print();//2  
	print1(n3);//1 1.2 1.2
}

全局函数类内声明+类外模板化实现1

template<class T>
void print();
template<class T>
void print1(T &);

template<class T>
class Node
{
private:
	T x;
	T y;
	static int all;
public:
	Node(T a,T b);
	friend void print<T>();//print是所有模板的友元函数,由于没有对象,故只能访问静态变量
	friend void print1<>(Node<T> &);
};
template<class T>
int Node<T>::all = 0;

template<class T>
Node<T>::Node(T a,T b){
	all++;
	x=a;
	y=b;
}
template<class T>
void print(){
	cout<<Node<int>::all<<endl;
}
template<class T>
void print1(T &n)
{
	cout<<n.all<<"  "<<n.x<<"  "<<n.y<<endl;
}

int main()
{
	Node<int> n1(1,1);
	print<int>();
	print1(n1);
	Node<int> n2(2,2);
	print<int>();;
	print1(n2);
	Node<double> n3(1.2,1.2);
	print<int>();
	print1(n3);
}

全局函数类内声明+类外模板化实现方法2—需要提前让编译器知道全局函数的存在; ---------尽量别用了
1 template class Person;,在最上方
2类外定义 template void printPerson(Person p){…} ,在1下面,在3上方
3在类中声明:friend void printPerson<>(Person p),
类外定义 template void printPerson(Person p){…}

另外一个常见问题就是在模板函数实现时,有时候可能不能确定某个参数的类型。就用到关键字decltype

template<typename T1,class T2,class T>
//解决1 因为返回值类型无法确定,解决方法1;其实由用户在具体传参时,自己推到该类型,若写的不对,可能会降低精度,或者发生错误。
T add1(T1 a, T2 b) {
	decltype(a+b) c =  a + b;
	return c;
}
//解决2 更好的写法,是返回值类型后置
auto add2(T1 a,T2,b) ->decltype(a+b){
	return a+b
}

int main() {
	//1
	double a = add<double,int,double>(3.4,4);
	//2
	auto b =add<double,int>(3.3,5);
	return 0;
}
long func(int x);
int main(){
	int a =10;
	const int i = 10;
	//1
	decltype(a) b;//int
	decltype(i) i1;//const int
	//2
	decltype(func(4)) n;//long 即函数返回值类型,并不会真的执行,只有去查看该函数的返回值类型即可。
	//3
	decltype((a)) c = b;//int&
	//4
	decltype(i + 20.3) i2;//double
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值