c++模板
概述
模板的作用
1.模板是一种参数化的多态工具
2.所谓参数化的多态性,是指将程序所处理的对象的类型参数化,使一段程序代码处理不同类型的对象。
3.采用模板编程,可以为各种逻辑功能相同而数据类型不同的程序提供一种代码共享机制
代码复用
函数模板 链接
template<模板形参表>
返回值类型 函数名(模板函数形参表){
函数定义体
}
模板被编译了两次;
模板的编译需要查看模板的定义;
template<类型形参表>
class <类名>
{
类说明体
}
类模板的使用
成员模板
实例化两个类,d和i,但是i却没法给d赋值。
成员模板的实现–链接
关键字-typename
内嵌依赖类型名
内嵌是指定义在类的定义中的
依赖是指依赖于一个模板参数typedef
类型名是指这里最终要指出的是个类型名
实例
链接
实例化
隐式实例化
函数模板隐式实例化指的是在发生函数调用的时候,如果没有发现相匹配的函数存在,编译器就会寻找同名函数模板,如果可以成功进行参数类型推演,就对函数模板进行实例化。
类模板隐式实例化指的是在使用模板类时才将模板实例化,相对于类模板显示实例化而言的
显示实例化
定义:显示实例化也称为外部实例化。在不发生函数调用的时候将函数模板实例化,或者在不使用类模板的时候将类模板实例化称之为模板显示实例化。
函数模板的显示实例化:template [函数返回类型] [函数模板名]<实际类型列表>(函数参数列表)例如:template void func(const int&);
类模板的显示实例化:template class [类模板名]<实际类型列表>
例如:template class theclass;
作用:减少隐式实例化实例多个函数和类的开销;最优化实例一次(不同编译器处理机制可能不同)
using给模板起别名
template using str_map_t = std::map<std::string, Val>; // … str_map_t map1;
模板全特化与偏特化(C++11)
类模板的全特化
特化模板成员函数而不是模板
//模板泛化 template <typename T1, typename T2> class A{ T1 data1; T2 da ta2; }; //模板全特化 template <> class A<int, double>{ int data1; double data2; }; 类模板的偏特化 //泛型化 template <typename T1,typename T2,typename T3> class B { public: void myfunc(T2 b) { cout << “泛化函数” << endl; } }; //偏特化:参数数量 template class B<int, T2, int> { public: void myfunc(T2 b) { cout << “偏特化函数” << endl; } }; 参数范围变化:int–const int;T–T*; T–T& //模板偏特化参数范围int–const int;T–T*; T–T& template class A { public: void myfunc() { cout << “const T偏特化函数” << endl; } };
函数模板全特化
注:函数模板不能偏特化
注:调用优先级:普通函数、重载函数、函数全特化
模板元编程
框架、库
Qt内部开发机制:模板元
c++内存管理
C语言内存管理
Linux虚拟内存空间分布
内存泄露——C语言没有更好的方法杜绝malloc的导致的内存泄漏
C++内存管理(1)
如何避免内存泄漏?
new/delete的使用
new/delete必须成对出现,new[]/delete []必须成对出现
人为的控制new/delete问题,无法杜绝内存泄漏
C++为什么没有提供GC机制?
1.没有共同基类:C++是从C发展而成,允许直接操作指针,允许将一个类型转换为另一个类型,对于一个指针无法知道它真正指向的类型;而Java或C#都有一个共同基类
2.系统开销:垃圾回收所带来的系统开销,违反了C++的设计哲学,“不为不必要的功能支付代价”,不符合C++高效的特性,使得不适合做底层工作
3.消耗内存:C++产生的年代内存很少,垃圾回收机制需要占用更多的内存
4.替代方法:C++有析构函数、智能指针、引用计数去管理资源的释放,对GC的需求不迫切
智能指针
作用:
帮助开发者对动态分配对象(new)的生命周期进行管理。能够有效防止内存泄漏
分类:
unique_ptr(C++11)–独占指针
shared_ptr(C++11)–共享指针
weak_ptr(C++11)–弱指针
shared_ptr
共享式指针:
多个指针可以同时指向同一个对象(共享所有权,协同工作),当最后一个指针被销毁或者指向其他对象时,这个对象会被释放;
工作原理:引用计数增加/减少(原子操作)
引用计数增加
用一个智能指针初始化另一个智能指针
函数传参:传递一个智能指针;
函数返回值:返回一个智能指针
引用计数减少
给智能指针赋予新值,指向一个新对象
局部的智能指针离开其作用域
定义:
shared_ptr<指向的类型> 智能指针变量名
shared_ptr是explicit,不可以进行隐式类型转换,只能直接初始化;
std::make_shared函数
auto p = std::make_shared(“hello world”);
功能:在 堆内存中可以动态分配对象,并返回一个shared_ptr;
推荐方法,因为此方法安全,高效;
shared_ptr常规操作
use_count();
功能:返回有多少个shared_ptr智能指针指向某对象;(引用计数的个数)
unique();
是否该智能指针独占某个对象,独占返回true,否则返回false;
reset();
reset()无参使用:若该智能指针是独占某个对象,则释放该对象,并将智能指针置nullptr;若不独占,引用计数减1,并将该指针置nullptr;
reset()代参使用:若该智能指针是独占某个对象,则释放该对象,并将该指针指向新对象;若不独占,则将该指针指向的对象引用计数减1,并将该指针指向新对象;
实例:p.reset();p.reset(new int());
*解应用
获得智能指针指向的对象,并对其操作;
get();
考虑到有些函数参数是裸指针并不是智能指针,所以需要将智能指针转化为裸指针;
获得智能指针中保存的指针(裸指针);
注:获得裸指针要小心使用,因为智能指针一旦释放,裸指针也就失效;
= nullptr;
将该智能指针引用计数减1,并置nullptr,若引用计数为零,将指向对象释放;
指定删除器
why:有些情况,默认删除器处理不了(shared_ptr管理动态数组),需要我们自己指定删除器;
实例–链接
移动语义:std::move();
weak_ptr
概述
1.weak_ptr弱指针,不会控制对象的生命周期(不会改变对象的引用计数),shared_ptr释放指向对象时,是不会考虑weak_ptr是否指向该对象
2.weak_ptr不是独立指针,不能单独操作所指向的资源;
作用
1.weak_ptr指针一般用来辅助shared_ptr的使用(监视shared_ptr指向对象的生命周期)
2.weak_ptr和shared_ptr之间可以相互转换,shared_ptr可以直接赋值给weak_pt,但是反过来是行不通的,需要使用lock函数。
weak_ptr的常规操作
lock();
调用lock函数来获得shared_ptr(如果对象已经被释放,则返回一个空的shared_ptr)
use_count();
功能:返回有多少个shared_ptr智能指针指向某对象;(引用计数的个数)
expired();
判断弱指针是否过期(所检测的对象是否被释放true/false)
reset();
将该弱指针设置为空,弱引用计数减1,强引用计数不变
shared_ptr & weak_ptr
尺寸
shared_ptr和weak_ptr一样大,是裸指针的两倍;
常见使用问题
shared_ptr多次引用同一数据,会导致两次释放同一内存
使用shared_ptr包装this指针导致局部对象会释放两次-解决办法: enable_shared_from_this模板类
shared_ptr循环引用导致内存泄露
unique_ptr
独占式指针(专属所有权)
同一时刻,只能有一个unique_ptr指向这个对象;当指针销毁,指向的对象也销毁;
初始化
手动初始化:unique_ptr p;或unique p(new int(5))
std::make_unique函数(C++14)
unique_ptr常规操作
不支持操作:该指针不支持拷贝和赋值操作;
移动语义std::move();
release();
放弃对指针的控制权,将该指针置nullptr,返回裸指针;
reset();
reset()无参使用:若该智能指针是独占某个对象,则释放该对象,并将智能指针置nullptr;
reset()代参使用:若该智能指针是独占某个对象,则释放该对象,并将该指针指向新对象;
实例:p.reset();p.reset(new int());
*解应用
获得智能指针指向的对象,并对其操作;
get();
获得智能指针中保存的指针(裸指针);
= nullptr;
将该智能指针引用计数减1,并置nullptr,若引用计数为零,将指向对象释放;
指定删除器
语法:unique_ptr<指向对象类型,删除器的类型> 智能指针变量名
自定义删除函数
void mydeleter(string *s) { delete s; } using MYFUNC = void (*)(string *);// tyepdef void (*MYFUNC)(string *); unique_ptr<string,MYFUNC> p(new string(“hello world”),mydeleter); lambda函数—decltype() auto mydeleter = [](string *s) { delete s; }; unique_ptr<string,decltype(mydeleter)> p(new string(“hello world”),mydeleter);
指定不同的删除器会导致不同的unique_ptr;
unique_ptr尺寸:
和裸指针一样大,但是指定自定义删除函数会影响尺寸大小;
C++内存管理(2)
内存池
起因:malloc:内存浪费,频繁分配小块内存,则浪费更加显得明显
作用:减少malloc的次数,减少malloc()调用次数就意味着减少对内存的浪费
原理:用malloc申请一大块内存,当要分配的时候,从这一大块内存中一点一点的分配,当一大块内存分配的差不多的时候,再用malloc再申请一大块内存,然后再一点一点的分配给你
内存池的实现v1.0–问题:next指针占用4个字节,每块内存都存在next指针,导致空间的浪费–链接
内存池的实现v2.0
嵌入式指针–链接
使用嵌入式指针改进内存池–链接
总结
C++内存管理的特点(智能指针)
智能指针的种类及使用
区分new operator(全局new)、operator new
重载各类版本的operator new
placement new的作用理解
内存池的实现