目录

前言

1.函数模板

1.1使用

1.2实现逻辑 

1.3实例化

1.4匹配规则

2.类模板

2.1使用

实例化

前言

️照以前的想法,若我们想实现一个交换函数,需要这样写。

void swap(int& x, int& y)

{

int tmp = x;

x = y;

y = tmp;

}

int main()

{

int a = 5, b = 6;

swap(a, b);

return 0;

}

️若想写通用的交换函数呢?根本没完没了,换一个类型就要重新写一次,就算有了函数重载也不能减少多少工作量。

void swap(int& x, int& y)

{

int tmp = x;

x = y;

y = tmp;

}

void swap(char& x, char& y)

{

int tmp = x;

x = y;

y = tmp;

}

void swap(double& x, double& y)

{

int tmp = x;

x = y;

y = tmp;

}

......

️就像古代最早出现的印刷术那样,使用一个模板可以去除掉那些大量并重复的工作。落实到语言中则诞生了模板(泛型编程)。

1.函数模板

1.1使用

️想要将这个函数定义成模板只需要在函数上加上这句话:template 其中参数的个数取决于实际需要的个数。

️其中的 class 还可以使用 typename 替换,二者都是定义模板参数关键字,区别不大,但是不可以使用 struct 。

️这样子,我们便可以使用模板对上面交换函数进行修改。

template

void swap(T& x, T& y)

{

T tmp = x;

x = y;

y = tmp;

}

️我们可以将这个 T 想象成一个抽象的数据类型,他具体是什么我们不知道,但之后这个 T 会自己进行推演并转化成传入的类型。

1.2实现逻辑 

️让我们思考一下,以下两次 swap 调用的是同一个函数吗?

template

void swap(T& x, T& y)

{

T tmp = x;

x = y;

y = tmp;

}

int main()

{

int a = 5, b = 6;

double c = 5.5, d = 6.6;

swap(a, b);

swap(c, d);

return 0;

}

️通过查看汇编,我们可以发现,其中调用的函数地址并不相同,即调用了两个不同的函数。 

 ️我们曾经将类比作蓝图,对象比作楼房。其实模板也是一样的,它会根据传入参数的不同类型进行推演,之后再进行实例化,生成不同版本的代码。因此不同类型的参数调用的函数并不是同一份。

️本质上不同类型的函数还是要实现一份的,但使用模板就将实例化这个重复的工作交给了编译器,由编译器代我们实现。

1.3实例化

️用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

隐式实例化:让编译器根据实参推演模板参数的实际类型。显式实例化:在函数名后的<>中指定模板参数的实际类型。

️若模板中只有一个数据类型,用两个类型的参数进行调用,编译器不知道该推演成哪个类型,便会出现歧义。

️可以传参时强制类型转换,使其满足隐式实例化。也可以指定模板参数的实际类型,让参数发生隐式类型转换,使其满足显式类型转换。

1.4匹配规则

️一个非模板函数和一个同名的函数模板可以同时存在,那么在这种情况下会调用哪一个函数呢?

template

int add(const T& x,const T& y)

{

return x + y;

}

int add(int x, int y)

{

return x + y;

}

int main()

{

int a = 5, b = 6;

add(a, b);

return 0;

}

️为了节约程序开销,编译器会优先选择成本较低的,参数更加匹配的的函数进行调用。

2.类模板

2.1使用

️类模板的定义与函数模板类似,根据需要定义任意数量的模板参数,之后便可以使用模板参数作为抽象的数据类型到类中。

template

class A

{

public:

A(T a = 1)

:_a(a)

{}

private:

T _a;

};

int main()

{

A a1;

return 0;

}

️类模板中函数放在类外进行定义时,需要加模板参数列表。

template

class A

{

public:

A(T a = 1)

:_a(a)

{}

~A(); //类中声明函数

private:

T _a;

};

template //需加上函数参数列表

A::~A() //类外定义函数

{

_a = 0;

}

int main()

{

A a1;

return 0;

}

️在实例化之前,当前模板类并不能算作一个真正的类,只是编译器根据被实例化的类型生成具体类的模具 。

实例化

️与函数模板实例化不同,类模板实例化时需要在类模板名字后加上 <> ,<>中放的是实例化的类型。因为编译器无法推演出其中的类型应该是如何对应的,因此需要手动指定。

A是类名,A才是类型

️好了,今天模板基础内容的讲解到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: