class="hide-preCode-box">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
其中支持typename
和class
来定义模板参数关键字,但是不能使用struct
来代替class
定义。
提出思考 :当我们编写了函数模板,两次函数调用是否为同一函数呢?
答 :调用不是同一个函数,虽然在调试中都执行到模板函数体中,但是这只是编译器为了方便观察进行的调整。对于不同类型所占用空间大小不是相同以及浮点数存储和释放都有自己的规定。 (Swap函数在库实现好了并且C++有模块的概念,可以直接调用库中Swap函数)
2.1 函数模板原理
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所用其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,**编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用,函数传的是对象,模板传的是类型。**比如:当用double类型使用函数模板事,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
2.2 函数模板实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例。
如图该语句不能通过编译器,由于编译期间,编译器进行实例化需要推演其实参类型。
报错理由 :通过实参a1将T推演为int,通过实参d1将T推演为double类型,但是模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int或者double类型而报错。 (在模板中编译器一般不会进行类型转换操作 ,因为一旦转化出问题,编译器就要背黑锅了。)
那么针对上面出现的问题,有三种解决方案
隐式实例化 :让编译器根据参数推演模板参数的实际类 型
显式实例化 :在函数名后的<>中指定模板参数的实际类型
第一种 :强制转化
int main ( )
{
int a1 = 10 , a2 = 20 ;
double d1 = 10.2 , d2 = 20.2 ;
Add ( a1, ( int ) d1) ;
Add ( ( double ) a1, d2) ;
return 0 ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
第二种 :显式实例化
int main ( )
{
int a1 = 10 , a2 = 20 ;
double d1 = 10.2 , d2 = 20.2 ;
Add < int > ( a1, d1) ;
Add < double > ( a1, d1) ;
return 0 ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
如果类型不匹配,编译器会尝试进行隐式类型转换 ,如果无法转换成功编译器将会报错。
第三种 :添加一个T2
(这里返回值的类型,需要用户选择T1 or T2
)
template < class T1 , class T2 >
T1 Add ( const T1& left, const T2& right)
{
cout << left + right<< endl;
return left + right;
}
int main ( )
{
int a1 = 10 , a2 = 20 ;
double d1 = 10.2 , d2 = 20.2 ;
Add ( a1, d1) ;
Add ( a1, d1) ;
return 0 ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
2.3 模板参数匹配原则
当同名函数模板与非模板函数同时存在,该函数模板可以实例化为非函数模板。
int Add ( int left, int right)
{
return left + right;
}
template < class T >
T Add ( T left, T right)
{
return left + right;
}
void Test ( )
{
Add ( 1 , 2 ) ;
Add < int > ( 1 , 2 ) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
如果同名函数模板与非函数模板,并且其他条件相同,在调用时会优先调用非函数模板(有现成的吃现成的菜 )而不会从该模板实例化一个,除非模板可以产生一个具有更好匹配的函数,在调用时优先选择模板(现成的不好吃,不如吃自己做的 )就像是想我委屈嫁给你,不如我找个有钱的大爷~
templace< class T1 , class T2 >
T1 Add ( T1 left, T2 right)
{
return left + right;
}
int main ( )
{
int ret = Add ( 1 , 2.0 ) ;
return 0 ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
模板函数不允许自动类型转换 ,但是普通函数可以进行自动类型转换
三、类模板
3.1 类模板定义格式
template < class T1 , class T2 , . . . . , class Tn >
class 类模板名
{
} ;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
template < class T >
class Vector
{
public :
Vector ( size_t capacity = 10 )
: _pData ( new T[ capacity] )
, _size ( 0 )
, _capacity ( capacity)
{ }
~ Vector ( ) ;
void PushBack ( const T& data) ;
void PopBack ( ) ;
size_t Size ( ) { return _size; }
T& operator [ ] ( size_t pos)
{
assert ( pos < _size) ;
return _pData[ pos] ;
}
private :
T* _pData;
size_t _size;
size_t _capacity;
} ;
template < class T >
Vector< T> :: ~ Vector ( )
{
if ( _pData)
delete [ ] _pData;
_size = _capacity = 0 ;
}
int main ( )
{
Vector< int > d1 ( 10 ) ;
vector< double > d2 ( 10.0 ) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
类模板可以处理需要很多类型的数据,如果按照C语言那一套TypeData typename
使用,当创建不同类型数据,需要修改名字连同实现逻辑也需要更换名字。不如使用模板,将我们需要重复做的事情交给编译器来做。
3.2 类模板的实现化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真的类
Vector< int > s1;
Vector< double > s2;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
四、简单了解STL
STL(standard tmplate libaray-标准库)
:是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
4.1 STL的版本
原始版本 :
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使 用。 HP 版本–所有STL实现版本的始祖。
P. J.版本 : 由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
RW 版本 : 由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本 : 由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习STL 要阅读部分源代码, 主要参考的就是这个版本
4.2 STL的六大组件
4.3 如何学习STL
4.4 STL的缺陷:
STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新 STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。 STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取 STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的 在不久的将来,将更加深入的学习模板的进阶知识。
以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/2302_79177254/article/details/141004143","extend1":"pc","ab":"new"}">>
评论记录:
回复评论: