C++的模板机制

为何须要模板编程

所谓函数模板,其实是创建一个通用函数其函数返回类型、形参类型或函数中使用的类型不具体指定,用一个虚拟的类型来表明,这个通用函数就称为函数模板ios

所谓类模板,和函数模板差很少,创建一个通用类,其类中的成员类型和类中使用的类型不具体指定,用一个虚拟的类型表明编程

简单的说,无论是函数仍是类,除了数据的类型不一样,其余都同样的函数或者类,咱们经过用一个或多个虚拟类型来表明不一样的类型,创建一个通用函数(模板函数)或类(模板类)函数

再简单的讲,模板编程就是类型参数化,即参数模板this

函数模板

//template 关键字告诉C++编译器 我要开始泛型了.你不要随便报错  
//数据类型T 数据类型参数化
template <typename T> 
void swapT(T &a,T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

函数模板的调用方式编码

一、显示调用    如:swapT<int>(a,b)spa

二、自动数据类型推导    如:swapT(a,b)  编译器会根据实参的类型自动推导出模板的参数类型.net

自动推导需注意虚拟的数据类型T不在函数的形参中出现时,必须经过显示调用来告诉编译器应该把你的虚拟数据类型转化为具体的类型。code

函数模板与普通函数重载的调用规则对象

一、函数模板遵循严格的参数类型匹配,而普通函数能够经过隐式的参数类型转化来匹配调用函数blog

二、当调用的函数参数类型与普通函数参数类型彻底匹配(没有通过隐式的类型转化),就调用普通函数

三、当调用的函数参数类型经过隐式类型转化才与普通函数类型匹配,而调用的函数参数类型与模板的类型一致,就调用模板函数

简单的说:优先调用普通函数(其没有隐式类型转化),其次才是模板函数(普通函数参数隐式类型转化了)

类模板

 

注意:一、当将类模板的.h和.cpp分开后,须要在包含main函数的文件中#include<xxx.cpp>

            二、不要滥用友元函数,不然会形成严重错误。能够使用友元函数实现左移右移运算符的重载(在模板类中声明做左移友元函数时要加上<T>

eg:   friend ostream& operator<< <T>(ostream& out,Complex& comx);)

单个类模板 (调用必须显示实例化类)

代码例子

#include <iostream>
using namespace std;

/*定义模板类,类的类型参数化*/
template<class T>
class B 
{
public:
	B(T b)
	{
		this->b = b;
	}
	void printB()
	{
		cout<<"B b:"<<this->b<<endl;
	}
	protected:
	T b;
};

int main()
{
	B<int> bb(33);   //显示参数类型化,二次编译时告诉编译器要给类B分配的内存
	bb.printB();
    system("pause");
	return 0
}

模板类派生普通类(继承模板类时须要显示参数类型化

代码例子

#include <iostream>
using namespace std;

/*定义模板类,类的类型参数化*/
template<class T>  /*能够使用class,也能够使用typename*/
class B 
{
public:
	B(T b)
	{
		this->b = b;
	}

	void printB()
	{
		cout<<"B b:"<<this->b<<endl; 
    }
protected:
	T b;          /*将模板类的成员类型参数化*/
};

/*类模板派生出普通类*/
class C :public B<int>  //!!!派生普通类,基类的参数必须类型化,告诉编译器基类的占用内存
{
public:
	C(int b,int c):B(b)
	{
		this->c = c;
	}
	void printC()
	{
		cout<<"B b:"<<this->b<<"  C c:"<<this->c<<endl;
	}
private:
	int c;
};

int main()
{
    //调用派生的普通类
	C c(12,34);
	c.printC();
    system("pause");
	return 0;
}

模板类派生模板类(在实例化对象时,须要显示参数类型化)
代码例子

#include <iostream>
using namespace std;

/*定义模板类,类的类型参数化*/
template<class T> //能够是class,也能够是typename
class B 
{
public:
	B(T b)
	{
		this->b = b;
	}
	void printB()
	{
		cout<<"B b:"<<this->b<<endl;
	}
protected:
	T b;
};

/*模板类派生模板类*/
template<class T1,class T2>
class D : public B    //!!!派生模板类能够不具体参数类型
{
public:
	D(T1 b,T2 d):B(b)
	{
		this->d = d;
	}
	void printD()
	{
		cout<<"B b:"<<this->b<<"  D d:"<<this->d<<endl;
	}
private:
	T2 d;	
};

int main()
{
    /*实例化派生的模板类*/
	D<int,char> d(55,'h');	//必须使用显示参数类型化方式
	d.printD();
	system("pause");
	return 0;
}

模板函数和类编译本质

编译器并非把函数模板处理成可以处理任意类的函数
编译器从函数模板经过具体类型产生不一样的函数

模板函数和类编译实现

编译器会对函数模板进行两次编译

一、在声明的地方对模板代码自己进行编译;(对其进行词法、语法、句法的分析)
二、在调用的地方对参数替换后的代码进行编译。(生成具体的函数(或类))

练习

一、编码模板函数并调用,利用g++编译成汇编文件,观察两次编译实现

二、编码模板类并实例,利用g++编译成汇编文件,观察两次编译实现

三、模板类派生普通类和模板类并实例,利用g++编译成汇编文件,观察两次编译实现

编译命令及过程,参考此文http://my.oschina.net/u/1783725/blog/680722

但愿能是你们对模板机制有了深刻的了解,若有疑问,可留言共同探讨。哈哈

相关文章
相关标签/搜索