Th4.4:成员函数模板,显式实例化,声明之详述

本文详细介绍了C++中的成员函数模板,包括普通类和模板类的成员函数模板的使用,强调了虚拟函数不能为模板的情况。同时,讨论了模板的显式实例化和声明,以优化编译时的开销,防止多次实例化相同模板导致的效率问题。通过实例展示了如何在不同源文件中使用显式实例化和声明。
摘要由CSDN通过智能技术生成

本小节回顾的知识点分别是成员函数模板,显式实例化,声明

今天总结的知识分为以下3个点:

(1)成员函数模板
(2)普通类and模板类的成员函数模板
(3)模板显式实例化,模板声明

(1)成员函数模板:

        不论是普通类,亦或是模板类,它的成员函数都可以成为一个函数模板。so,若一个类的成员函数是函数模板的话,那该函数模板就称之为成员函数模板

注意虚函数不可以成为模板!

class Person {
public:
	//...
	template<typename T>
	virtual void func(T t) {//错误!函数模板的声明中不允许使用virtual!
		cout << t << endl;
	}
	//...
};

运行结果:

(2)普通类and模板类的成员函数模板        

a)普通类的成员函数模板:

(一个普通类,其某个成员函数是函数模板)

请看以下例子codes:

class A {普通类
public:
	template<typename T>
	void mytfunc(T tmpt) {//成员函数模板 ==> 普通类的成员函数是一个函数模板
		cout << tmpt << endl;
	}
};
int main(void) {
	A a;
	a.mytfunc(3);//注意:只有当编译器遇到这句代码时,才会实例化出特殊版本的成员函数
	/*
	==> 此时编译器为我们生成了一个特殊版本的成员函数myfunc:
		void mytfunc(int tmpt){
			cout<< tmpt <<endl;
		}
		 (注意:这里是编译器自动推断我们传入的参数是一个int类型)
	*/
	//==> result: 3
	a.mytfunc<double>(1388.8);
	/*
	==> 此时编译器为我们生成了一个特殊版本的成员函数myfunc:
		void mytfunc(double tmpt){
			cout<< tmpt <<endl;
		}
	 (注意:这里是我们给编译器指定了参数是一个double类型)
	*/
	//==> result: 1388.8
	return 0;
}

a)模板类的成员函数模板:

(一个模板类, 其某个成员函数是函数模板)

        注意①:当need在模板类的定义之外部定义其成员函数模板时,应该这么写:

        格式

template<整个模板类的模板参数列表1>
template<某个成员函数模板的模板参数列表2>
retName className<列表1>::funcName(Params){
    //...
}

        注意②类模板成员函数(包括普通成员函数and成员函数模板只有被调用时(也即程序中出现了对该成员函数/函数模板的调用代码时),编译器才会帮我们把这些函数的具体实现代码进行实例化。若模板类中的某函数在程序中从未被调用过,那么编译器当然不会实例化(生成)该成员函数的具体代码了。

请看以下例子codes:

template<typename C>
class AA {
public:
	C m_ic;
public:
	//定义一个构造函数模板
	template<typename T1,typename T2>//注意:模板参数T1和T2与整个类的模板参数C是没有关系的!
	AA(T1 v1, T2 v2);
	template<typename T3, typename T4>
	void func(T3 v3, T4 v4);
    template<typename T5>
    void func2(T5 tmpt){ cout<<"this is func2!"<<endl; }
    void func3(){ cout<<"this is func3!"<<endl; }
    
};

template<typename C> //先写整个类模板的参数列表
template<typename T1, typename T2>//再写这个类内的成员函数模板的参数列表
AA<C>::AA(T1 v1, T2 v2) {
	cout << "this is constructor ~ yeah" << endl;
}
template<typename C> //先写整个类模板的参数列表
template<typename T3, typename T4> //再写这个类内的成员函数模板的参数列表
void AA<C>::func(T3 v3, T4 v4) {
	cout << "v3 = " << v3 << " v4 = " << v4 << endl;
}
int main(void){
	AA<float> aa(1,2);
	//由于构造函数是编译器自动为我们调用的(并且为我们自动推断出传入构造函数的参数是2个int型参数)
	//因此我们必然不能 给构造函数指定模板参数进而调用它
	aa.func(3, 4);
	aa.func<double,double>(33.3, 44.4);//但是对于一般的成员函数,我们是可以指定模板参数来调用它的!
    return 0;
}

        解释:上述代码中,模板类AA中的成员函数只有其构造还有func()函数给调用了,也即此时编译器为我们实例化了这2个成员函数的具体实现代码:

AA的构造函数中的代码:cout << "this is constructor ~ yeah" << endl;被编译器实例化出来了
func()中的代码:cout << "v3 = " << v3 << " v4 = " << v4 << endl;被编译器实例化出来了

,而func2()和func3()这2个函数并没有被编译器实例化:

func2()中的代码:{ cout<<"this is func3!"<<endl; }没有被编译器实例化!
func3()中的代码:{ cout<<"this is func2!"<<endl; }没有被编译器实例化!

(3)模板显式实例化,模板声明:(了解认识一下即可)

        为了防止在多个.cpp源文件中都实例化相同的 类模板(生成多个类模板实例的动作会使得build时的成本消耗极大),C++11中提出了一个deal方法,称之为“显式实例化”用以减少系统实例化模板类/函数的开销!

显式实例化的格式:

第一步(在其中一个.cpp源文件中进行"实例化定义")
"实例化定义":提前在一个.cpp源文件中实例化好你所要用到的模板类/函数的代码

template retName tfunc(realTemplateParams);//实例化定义模板函数
template tclassName<realTemplateParams>;//实例化定义模板类

第二步(在其他.cpp源文件中都进行"实例化声明")(extern告诉编译器本.cpp的外部是存在一个"实例化定义"的)
"实例化声明":告诉编译器本.cpp的外部存在了一个已经实例化好的模板类/函数的代码,因此我本.cpp源文件就不需要再实例化了,直接拿来用即可~(拿来吧你)

extern template retName tfunc(realTemplateParams);//实例化声明模板函数
extern template tclassName<realTemplateParams>;//实例化声明模板类

注意①当编译器一看到该显式实例化的代码时,就会为我们实例化出一个特定版本的模板类/模板函数

比如:

template void mytfunc(int tmpt1, int tmpt2);
//此时编译器一看到这条代码就会为我们生成一个void mytfunc(int tmpt1, int tmpt2)带有2个int形参版本模板的函数
template AA<float>;//此时编译器一看到这条代码就会为我们生成一个AA<float>类

注意②:显式实例化一带多的关系,即:每一个实例化定义都可以带有多个实例化声明

        解释:这样的实例化定义的代码只需要在其中一个.cpp源文件中用到即可了,其他要用到的.cpp源文件就用extern实例化声明即可。并不需要你在all的.cpp源文件中都写一条这样的代码。除非你想生成其他特定类型的模板类,那你大可再按照需求来写对应的显式实例化的代码。

        比如:

        在test_template.h中有这样一个类模板AA和模板函数mytfunc():

#ifndef __TEST_TEMPLATE_H__
#define __TEST_TEMPLATE_H__
#include<iostream>
using namespace std;

//模板类
template<typename C>
class AA {
public:
	C m_ic;
public:
	//定义一个构造函数模板
	template<typename T1, typename T2>//注意:模板参数T1和T2与整个类的模板参数C是没有关系的!
	AA(T1 v1, T2 v2);
	template<typename T3, typename T4>
	void func(T3 v3, T4 v4);
};

template<typename C> //先写整个类模板的参数列表
template<typename T1, typename T2>//再写这个类内的成员函数模板的参数列表
AA<C>::AA(T1 v1, T2 v2) {
	cout << "this is constructor ~ yeah" << endl;
}
template<typename C> //先写整个类模板的参数列表
template<typename T3, typename T4> //再写这个类内的成员函数模板的参数列表
void AA<C>::func(T3 v3, T4 v4) {
	cout << "v3 = " << v3 << " v4 = " << v4 << endl;
}


//模板函数
template<typename T1, typename T2>
void mytfunc(T1 tmpt1, T2 tmpt2) {
	cout <<"add "<< tmpt1 <<" of "<< tmpt2 <<" is "<< tmpt1+ tmpt2<< endl;
}
#endif //__TEST_TEMPLATE_H__

        在main1.cpp中有这样一个调用类模板生成特定对象的函数:

#include<iostream>
#include"test_template.h"
using namespace std;
void tfunc() {
	AA<float> a(1, 1);
}

        在main2.cpp中也有这样一个调用类模板生成特定对象的函数:

#include<iostream>
#include"test_template.h"
using namespace std;
int main(void) {
	AA<float> aa(1,2);
	aa.func(3, 4);
	aa.func<double,double>(33.3, 44.4);
	return 0;
}

        从而,这样就会在多个.cpp源文件中都实例化了同一个类模板!这样会导致编译开销变大!

so,请看以下显式实例化的代码:

        main1.cpp中添加这样一条显式"实例化定义"的代码:

#include<iostream>
#include"test_template.h"
using namespace std;

template void mytfunc(int tmpt1, int tmpt2);
template AA<float>;

void tfunc() {
	AA<float> a(1, 1);
}

        main2.cpp中添加这样一条extern显式"实例化声明"的代码(因为main函数要用到AA<float>这种特定版本的类!so要实例化声明一下,否则就不需要):

#include<iostream>
#include"test_template.h"
using namespace std;

extern template void mytfunc(int tmpt1, int tmpt2);
extern template AA<float>;

int main(void) {
	AA<float> aa(1,2);
	aa.func(3, 4);
	aa.func<double,double>(33.3, 44.4);
	return 0;
}

        这样,在用到AA<float>这种特殊版本的类之前,编译器就会实例化该特殊版本的类的代码,进而后面需要用到时就不会再重新实例化了,在一个.cpp源文件中实例化了AA<float>之后,另外的.cpp源文件也就不再需要实例化一次了,这样就提高了你代码的build效率!

        以后在读别人写的代码时,遇到extern template AA<float>;还有template AA<float>;类似的显式实例化的代码时千万不要发懵就行哈~

        以上就是我总结的关于成员函数模板,显式实例化,声明的笔记。希望你能读懂并且消化完,也希望自己能牢记这些小小的细节知识点,加油吧,我们都在coding的路上~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Fanfan21ya

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

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

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

打赏作者

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

抵扣说明:

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

余额充值