c++学习之模板

#2023 博客之星评选已开启--成为城市领跑者#

目录

一,模板的概述

二,函数模板

1.函数模板的定义

2.函数模板的注意事项

3.函数模板的重载

4.函数模板的局限性

三,类模板

1.类模板的概念

2.类模板定义方式

3.类模板的成员函数在类外实现

4.函数模板作为类模板的友元

5.在写程序时模板的头文件与源文件分离时的问题。


一,模板的概述

c++提供了函数模板(function template),即建立一个通用的函数,不具体定义函数的参数类型以及函数类型,以他为模板。与该函数功能相同的函数我们定义时,直接利用模板,不必重新定义,大大提高了写代码的效率,其次根据模板定义不同的参数的类型以及函数的类型,实现函数不同的功能。总的来说,C++中的模板是一种支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。C++程序由类和函数组成,模板也分为类模板和函数模板。

这也是c++泛型编程思想:模板。

二,函数模板

1.函数模板的定义

关键字 template

 c++提供了关键字tmplate用来实现模板类型的虚拟化,在调用模板时,编译器会自动地将虚拟的类型具体化,对于函数类型,就是函数模板,对于类的抽象化类型,就是类模板。

在这里我们还会用到typename 也是一个关键字,专门用来表示函数模板中的类型

例如:

/利用template将T抽象成一个虚拟类型,即T认为是一个类型
//以交换函数来举例  
template<typename T> void myswap(T &x, T &y)//该类型抽象只作用该函数
{
	T tmp = x;
	x = y;
	y = tmp;
}
//再调用函数模板时,会自动将虚拟类型具体化
int main()
{
	int a = 10; int b = 20;
	myswap(a, b);//函数调用时会根据参数类型自动推导出T是哪一个类型
	cout << a << " " << b << endl;
   //20 10
	return 0;
}

可以看到函数模板在面对不同的参数类型都可以实现函数功能,展现了函数模板的多功能。

函数模板会被编译器编译两次:

第一次编译:对函数模板本身编译,即将函数源代码转化为机器码:预处理阶段。

第二次编译:函数调用处将T的类型具体化。

对于实例化过程:

C++ 函数模板中的类型具体化是在函数编译的过程中完成的,当使用函数模板创建一个具体类型的函数时,编译器会根据实际参数的类型来生成对应的函数代码,这个过程也被称为模板类或模板函数的实例化。在实例化时,编译器会将函数模板中的类型参数具体化为实际的类型,同时在编译器内部生成对应的代码。
需要注意的是,C++ 中的函数模板并不是一种动态类型或者泛型编程的方式,而是一种静态的模板化机制。因此,在编译阶段,编译器会将模板代码生成为具体的函数代码,以提高代码的效率和可靠性。

函数模板目标:模板是为了实现泛型,可以减轻编程的工作量,增强函数的重用性。

2.函数模板的注意事项

1.对于函数模板与普通函数,在函数调用时,函数时会优先选择普通函数。

如下图:函数在调用时优先选择普通函数

 2.普通函数和模板函数,像调用函数模板只能强制使用函数模板。

在调用时函数名与参数中间添加<>,表示强制调用函数模板。

当然我们也可以改变其中的类型<double>,强制调用模板类型为double。

 3.函数模板自动类型推导时,不能对函数的参数进行自动类型转换。

template<typename T>
void print(T a, T b)
{
	cout << a << b << endl;
	cout << "函数模板" << endl;
}
int main()
{
	int a = 10; char b = 'c';
	print(a, b);
}

类型不相同就不会调用,这里编译器会自动报错。

3.函数模板的重载

函数模板也可以同名,根据函数的参数以及雷响,在调用会自动调用对应的模板,也就是函数模板的重载。

template<typename T> void myprint(T a, T b)
{
	cout << a << b << endl;
	cout << "调用双参数模板" << endl;
}
template<typename T> void myprint(T a)
{
	cout << a  << endl;
	cout << "调用单参数模板" << endl;
}
int main()
{
	int a = 10;
	myprint(a);
	return 0;
}

4.函数模板的局限性

对于函数模板,这里的函数类型都是编译器认识的的,倘若遇到自定义类型如结构体或类时,该类型函数模板无法识别,函数无法实现其功能。解决办法如下:

一,一般是将运算符重载。

template<typename T> void myprint (T a)
{
	cout << a << endl;
}
class Data
{
	friend ostream& operator<<(ostream& out, Data x);
public:
	Data(int x)
	{
		data= x;
	}
	~Data(){}
private:
	int data;
};
ostream& operator<<(ostream& out, Data x)
{
	out << x.data;
	return out;
}

template<typename T> void myprint(T a)
{
	cout << a << endl;
}
int main()
{
	Data x(10);
	int a = 10;
	myprint(a);
	myprint(x);//需将函数里的所有运算符重载
	return 0;
}

二,具体化函数模板

再写函数模板,将之前的函数模板的类型删掉,重新具体实现函数模板的类型以及参数以及函数实现。

template<typename T> void myprint (T a)
{
	cout << a << endl;
}
class Data
{
	friend void myprint<Data>(Data a);
public:
	Data(int x)
	{
		data= x;
	}
	~Data(){}
private:
	int data;
};
//具体实例化模板
template<> void myprint<Data>(Data a)
{
	cout << a.data << endl;
}

int main()
{
	Data x(10);
	int a = 10;
	myprint(a);
	myprint(x);//需将函数里的运算符重载
	return 0;
}

三,类模板

1.类模板的概念

与函数模板的概念相类似,有时有两个类或多个类的功能是大体相同的,仅仅因为数据类型不一样,于是乎c++提供了函数模板近似的类模板,用于实现所需数据的类型参数化。

2.类模板定义方式

类模板定义方式如下:

//类模板
//这里的类型就不用typename,为了区分模板,这里就用class表示类
template<class T1, class T2> class Data
{
private:
T1 a;
T2 b;
public:
Data(){}
Data(T1 a, T2 b)
{
this->a = a;
this->b = b;
}

void showData()
{
cout<<a<<" "<<b<<endl;
}
};

对于类模板,在实例化对象时,不能自动推导类型:


int main()
{
	//不能这样定义
	//Data obj(10,20);
	//在实例化对象时,是无法自动推导出类中数据类型,需要标明
	Data<int,int >obj(10, 20);
	obj.showData();
	return 0;
}

3.类模板的成员函数在类外实现

若成员函数在类外实现,则需要注意其类模板的作用域是只在类中的,而成员函数在类外定义时就需要重新在成员函数前定义类模板。

//类模板
template<class T1, class T2> class Data
{
private:
T1 a;
T2 b;
public:
Data(){}
Data(T1 a, T2 b);
void showData();
};

template<class T1, class T2>Data<T1,T2>::Data(T1 a, T2 b)
{
this->a = a;
this->b = b;
}

template<class T1, class T2>void Data<T1,T2>::showData()
{
cout<<a<<" "<<b<<endl;
}

4.函数模板作为类模板的友元

函数可以作为类的友元函数,那么函数模板亦可作为类模板中友元函数模板

template<class T1, class T2>class Data
{
	template<typename T3, typename T4>friend void myPrintData(Data<T3, T4>& ob);//声明为友元
private:
	T1 a;
	T2 b;
public:
	Data() {}
	Data(T1 a, T2 b)
	{
		this->a = a;
		this->b = b;
	}
};
//函数模板
template<typename T3, typename T4> void myPrintData(Data<T3, T4>& ob)
{
	cout << ob.a << " " << ob.b << endl;
}
int main()
{
	Data<int, char> ob1(100, 'A');
	myPrintData(ob1);
}

在函数模板定义中我们可以看到其参数为类,而我们这里是类模板,故参数是需要实例化类的对象,其次需要引用代表对对象的操作。

其次还需要注意设置友元时,其中的template虚拟的类型也需要紧跟。

其中类中的数据类型在这里是所定义的类模板的类型。

5.在写程序时模板的头文件与源文件分离时的问题。

头文件定义类模板.

源文件中实现类中的成员函数(注意需加上数据类型)。如:

template<class T1,class T2> Data<T1,T2>::Data()
{
....
};

其次在主函数文件实例化对象时进行两次编译,第一次编译在预处理阶段,在头文件,编译类或函数本身,第二次编译在源文件中推导类型或指明类型,可是此时之前第一次编译类中的数据以及成员函数找不到了,第二次无法调用无法具体化并实现函数。

记得包含上源文件主函数才会通过。

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜菜求佬带

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值