“”

这里是目录

一、泛型编程二、函数模板1.函数模板的实例化

三、类模板1.类模板的定义格式

四、非类型模板参数五、模板的特化1.函数模板的特化2.类模板的特化全特化偏特化

六、模板分离编译(重点)1.什么是分离编译 ?2.模板的声明和定义3.模板的分离编译原理

为什么分离就链接不上?

一、泛型编程

概念: 编写与类型无关的通用代码,达成代码复用,模板是泛型编程的基础。

模板就是把工作交给编译器去做。让编译器去生成多个函数,省的我们再去写函数模板。比如Add加法函数。

平时经常用的是函数模板和类模板

二、函数模板

函数模板格式:

template

这里需要有个感性的认知:

1.一个模板参数只能定义一个函数。模板参数可以有缺省参数。

2.模板参数是类型。函数参数是对象。 模板参数传递的是类型,函数参数传递的是对象值。

3.普通函数是有地址的,而模板函数没有地址。 但是模板会推算,会通过实参传递给形参,推算他的实际类型。

template

void Swap(T& left, T& right)

{

T temp = left;

left = right;

right = temp;

}

1.函数模板的实例化

实例化:用不同类型的参数使用函数模板时,称为函数模板的实例化 但是有时候也有例外。需要显式实例化。 代码如下

template

T* func(int n)

{

return new T[n];

}

int main()

{

int* p = func(10);

return 0;

}

typename是用来定义模板参数的关键字。也可以用class.但是不能用struct。

三、类模板

1.类模板和函数模板不同。函数模板一般可以显式传参推出实际类型。

2.而类模板不能传参推断类型,所以类模板需要在类名的 后面加尖括号<>里面加类型,这叫做类模板的显式实例化

例如:

vector v1;//int类型

vector v2;//元素是double类型

1.类模板的定义格式

注意: cao不是具体的一个类,而是编译器根据被实例化的类型生成具体类的模具。

template

class 类模板名

{

//类内成员定义

};

//类模板

template

class cao

{

};

//类模板的实例化

cao c;

//cao类名,cao才是类型

四、非类型模板参数

概念:非类型形参。就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用

1.非类型的模板参数无法修改 2.浮点数,类对象以及字符串无法做非类型模板参数 (非类型模板参数一般是整形) 3.非类型的模板参数必须在编译期间就能确认结果。

N就是非类型形参。

template

五、模板的特化

使用模板可以实现与类型无关的代码。但有时候还需要做一些特殊处理。

1.函数模板的特化

特化步骤 1.必须要先有一个基础的函数模板 2.关键字template后面接一对空的尖括号<> 3.函数名后跟一对尖括号,尖括号中指定需要特化的类型。 4.函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪错误

struct Date

{

int _year = 1;

int _month = 1;

int _day = 1;

};

//基础的函数模板

template

bool IsEqual(T left, T right)

{

return left == right;

}

//关键字template后面接一对空的尖括号<>

template<>

//函数名后跟一对尖括号,尖括号中指定需要特化的类型

//函数形参表:必须要和模板函数的基础参数类型完全相同

bool IsEqual(Date* left, Date* right)

{

return left->_year == right->_year

&& left->_month == right->_month

&& left->_day == right->_day;

}

int main()

{

cout << IsEqual(1, 2) << endl;

Date* p1 = new Date;

Date* p2 = new Date;

cout << IsEqual(p1, p2) << endl;

return 0;

}

2.类模板的特化

全特化

全特化就指的是模板参数列表中所有的参数都确定化。

//全特化

template

class Data

{

public:

Data()

{

cout << "Data" << endl;

}

private:

T1 _d1;

T2 _d2;

};

//跟一对尖括号<>

template<>

class Data

{

public:

Data()

{

cout << "Data" << endl;

}

private:

int _d1;

char _d2;

};

int main()

{

Data d1;

//模板的全特化

Data d2;

}

偏特化

偏特化是针对模板参数进一步进行条件限制设计的特化版本。

//类的基础模板

template

class Data

{

public:

Data()

{

cout << "Data" << endl;

}

private:

T1 _d1;

T2 _d2;

};

//1.部分特化

//将模板参数列表中的一部分参数特化。

template

class Data

{

public:

Data()

{

cout << "Data" << endl;

}

private:

T1 _d1;

int _d2;

};

//两个参数偏特化为引用类型

template

class Data

{

public:

Data() { cout << "Data" << endl; }

private:

T1 _d1;

T2 _d2;

};

//两个参数偏特化为引用类型

template

class Data

{

public:

Data(const T1& d1, const T2& d2)

:_d1(d1)

, _d2(d2)

{

cout << "Data" << endl;

}

private:

const T1& _d1;

const T2& _d2;

};

int main()

{

Data d1;

Data d2;

Data d3;

Data d4(1,2);

}

六、模板分离编译(重点)

1.什么是分离编译 ?

一个程序由若干个源文件组成。而每个源文件单独编译成目标文件,然后目标文件链接起来形成可执行文件的过程称为分离编译。

模板一般不支持分离编译。但普通函数是可以的。

C/C++程序要运行,基本步骤. 预处理 -> 编译->汇编->链接

2.模板的声明和定义

函数模板的声明和定义也有一些讲就。 声明:

template

void Swap(T& left, T& right);

定义:

template

void Swap(T& left, T& right)

{

T temp = left;

left = right;

right = temp;

}

注意:类模板的定义还需要加上类域。

3.模板的分离编译原理

a.cpp a.h main.cpp

a.h头文件不参与编译。 a.cpp中不会生成模板函数的实例化, main.cpp中调用函数链接时找地址。main.cpp包含的头文件只有a.h的。但是a.cpp中没有实例化所以没有地址。

解决方法: 1.将声明和定义放到一个文件.hpp的文件中。 2.模板定义的位置显式实例化。(不推荐使用).

模板的优点:

1.模板复用了代码,节省了资源。STL标准模板库因此而产生。 2.增强了代码的灵活性。

缺点: 1.模板会导致代码膨胀,也会导致编译时间变长。 2.出现模板编译错误时,错误信息非常凌乱,不易定位错误。

为什么分离就链接不上?

符号表找不到。 和实例化有关系。

a.cpp 从预处理到 a.i经过了头文件的展开。 a.i经编译到a.s再经过汇编到a.o什么都没干,因为模板的类型没有确定,所以没法实例化。a.s和a.o都是空的,空壳子。

解决办法: 1.显示实例化,太矬了,几乎不用这个办法。 2.不分离到两个文件中,放到同一个文件中。这样为什么就可以了呢? 为什么就不存在链接错误了?**原因是因为在main.cpp中头文件展开后,有了函数模板的声明和定义。**在链接的时候就不用找他的地址了。

查看原文