c++ 智能指针基础详解
简介
在现代C++编程中,标准库包含了智能指针(Smartpointers)。
智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe)的。
智能指针的使用
智能指针定义在头文件memory里的命名空间std中。它对于资源获取即初始化(RAII,ResourceAcquisitionIsInitialization)编程理念至关重要。该理念的目的是保证对象初始化的时候也是资源获取的时候,从而使对象的所有资源在单行代码中创建。
实践中,RAII的主要原则就是把任何在堆上分配的资源(比如动态分配的内存或者系统对象的处理)的所有权提供给在栈上分配的对象(其析构函数包含释放资源及相关清理的代码)。
大多数时候,当你初始化一个原始指针或者资源句柄使其指向实际的资源时,立即将其传给智能指针。
在现代C++中,原始指针只用于包含在局部作用域,循环或者工具函数的小块代码中(对性能有要求,并且对资源的所有权也不容易混淆)。
原始指针和智能指针的声明比较如下:
voidUseRawPointer() { //Usingarawpointer--notrecommended. Song*pSong=newSong(L"NothingonYou",L"BrunoMars"); //UsepSong... //Don'tforgettodelete! deletepSong; } voidUseSmartPointer() { //Declareasmartpointeronstackandpassittherawpointer. unique_ptrsong2(newSong(L"NothingonYou",L"BrunoMars")); //Usesong2... wstrings=song2->duration_; //... }//song2isdeletedautomaticallyhere.
如上所示,智能指针是一个在栈上声明的类模板,并由指向分配在堆上的对象的原始指针初始化。当智能指针初始化后,它就拥有了原始指针的所有权。这意味着智能指针需要负责原始指针指向的内存释放。智能指针的析构函数包含了delete的调用,并且由于智能指针是在栈上声明的,其析构函数会在智能指针对象离开作用域时被调用,即使在栈中发生了异常。
通过使用指针运算符(->和*)访问被封装的指针,智能指针类重载了这些运算符以返回被封装的原始指针。
C++智能指针的理念类似于在C#语言中创建对象的过程:创建对象后让系统负责在正确的时间将其删除。不同之处在于,没有独立的垃圾回收器运行于后台;内存是按照标准C++规范对内存进行管理的,使运行时环境更加快速和高效。
[!重要]
总是在单独的行上创建智能指针,而不是在参数列表中,从而避免由于特定的参数列表分配规则出现一些轻微的内存泄漏
以下示例显示了C++标准库中的unique_ptr是如何封装指向大型对象的指针的。
classLargeObject { public: voidDoSomething(){} }; voidProcessLargeObject(constLargeObject&lo){} voidSmartPointerDemo() { //Createtheobjectandpassittoasmartpointer std::unique_ptrpLarge(newLargeObject()); //Callamethodontheobject pLarge->DoSomething(); //Passareferencetoamethod. ProcessLargeObject(*pLarge); }//pLargeisdeletedautomaticallywhenfunctionblockgoesoutofscope.
上述示例演示了使用智能指针的关键步骤:
- 将智能指针声明为局部变量(不要在智能指针上使用new或者malloc表达式)。
- 在类型参数上,指定被封装指针指向的对象类型。
- 将指向由new创建的对象的指针传给智能指针的构造函数。
- 使用重载的操作符->和*来访问对象。
- 让智能指针来delete对象。
智能指针在设计上兼顾了内存和性能的高效性。例如,unique_ptr唯一的数据成员是被封装的原始指针,这意味着unique_ptr具有原始指针同样地大小,4字节或者8字节。通过智能指针重载的操作符->和*来访问并不比直接使用原始指针来访问慢多少。
智能指针有其自己的成员函数,通过.来访问。例如,一些C++标准库的智能指针有用于重置的成员函数来释放对原始指针的所有权。这可以用于在智能指针超出作用域前释放智能指针管理的内存,看下面的示例:
voidSmartPointerDemo2() { //Createtheobjectandpassittoasmartpointer std::unique_ptrpLarge(newLargeObject()); //Callamethodontheobject pLarge->DoSomething(); //Freethememorybeforeweexitfunctionblock. pLarge.reset(); //Dosomeotherwork... }
智能指针通常提供了获取原始指针的方式。C++标准库中的智能指针包含了成员函数get来获取原始指针。CComPtr有公共的类成员p。通过获取原始指针,你能够使用智能指针来管理你自己代码涉及的内存并依然能够将原始指针传递给不支持智能指针的代码。
voidSmartPointerDemo4() { //Createtheobjectandpassittoasmartpointer std::unique_ptrpLarge(newLargeObject()); //Callamethodontheobject pLarge->DoSomething(); //PassrawpointertoalegacyAPI LegacyLargeObjectFunction(pLarge.get()); }
智能指针的种类
以下部分总结了在Windows环境下不同种类的智能指针,以及如何使用它们。
C++标准库中的智能指针
优先使用下列智能指针来封装原始指针指向的纯旧对象(plainoldC++objects,POCO):
unique_ptr
- 对封装的原始指针是独占的
- 默认用于POCO,除非你明确的知道你需要一个shared_ptr
- 可以移入新的所有者,但不能拷贝或者共享
- 替代auto_ptr,auto_ptr已作废
- 对比boost::scoped_ptr,unique_ptr更加小巧和高效
- 长度为一个指针的大小,并且支持右值引用来快速执行C++标准库容器的插入和遍历操作
shared_ptr
- 引用计数智能指针
- 当你需要将原始指针分派给多个所有者时使用,例如,当你从容器返回一个指针的拷贝并且想要保留它
- 原始指针不会被delete直到所有的shared_ptr超出作用域或者放弃所有权。
- 长度为两个指针的大小,一个用于对象,另一个用于包含引用计数的共享控制块
weak_ptr
- 结合shared_ptr使用的特殊智能指针。
- weak_ptr提供了对被一个或者多个shared_ptr所拥有的对象的访问,但不参与引用计数。
- 如果你想要监测某个对象,不要求其不被释放,可以使用weak_ptr
- 在某些情况下,用于解决shared_ptr实例间的循环引用。
扩展
- 用于COM组件的智能指针
- 用于POCO对象的ATL智能指针
引用
MicrosoftDocs(https://docs.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=msvc-160)
以上就是c++智能指针基础详解的详细内容,更多关于c++智能指针的资料请关注毛票票其它相关文章!