首页 > 代码库 > 现代C++学习笔记之一入门篇:智能指针(C++ 11)
现代C++学习笔记之一入门篇:智能指针(C++ 11)
原始指针:通过new建立的*指针
智能指针:通过智能指针关键字(unique_ptr, shared_ptr ,weak_ptr)建立的指针
在现代 C++ 编程中,标准库包含智能指针,该指针用于确保程序不存在内存和资源泄漏且是异常安全的。 在现代 C++ 中,原始指针仅用于范围有限的小代码块、循环或者性能至关重要且不会混淆所有权的 Helper 函数中。
1 void UseRawPointer() 2 { 3 // Using a raw pointer -- not recommended. 4 Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); 5 6 // Use pSong... 7 8 // Don‘t forget to delete! 9 delete pSong; 10 }11 12 13 void UseSmartPointer()14 {15 // Declare a smart pointer on stack and pass it the raw pointer.16 unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));17 18 // Use song2...19 wstring s = song2->duration_;20 //...21 22 } // song2 is deleted automatically here.
C++ 智能指针思路类似于在语言(如 C#)中创建对象的过程:创建对象后让系统负责在正确的时间将其删除。 不同之处在于,单独的垃圾回收器不在后台运行;按照标准 C++ 范围规则对内存进行管理,以使运行时环境更快速更有效。
Notice:请始终在单独的代码行上创建智能指针,而绝不在参数列表中创建智能指针,这样就不会由于某些参数列表分配规则而发生轻微泄露资源的情况。
智能指针的设计原则是在内存和性能上尽可能高效。 例如,unique_ptr 中的唯一数据成员是封装的指针。 这意味着,unique_ptr 与该指针的大小完全相同,不是四个字节就是八个字节。 使用重载了 * 和 -> 运算符的智能指针访问封装指针的速度不会明显慢于直接访问原始指针的速度。
智能指针具有通过使用“点”表示法访问的成员函数。 例如,一些 STL 智能指针具有释放指针所有权的重置成员函数。 如果你想要在智能指针超出范围之前释放其内存将很有用,这会很有用,如以下示例所示:
void SmartPointerDemo2(){ // Create the object and pass it to a smart pointer std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object pLarge->DoSomething(); // Free the memory before we exit function block. pLarge.reset(); // Do some other work...}
智能指针通常提供直接访问其原始指针的方法。 STL 智能指针拥有一个用于此目的的 get 成员函数,CComPtr 拥有一个公共的 p 类成员。 通过提供对基础指针的直接访问,你可以使用智能指针管理你自己的代码中的内存,还能将原始指针传递给不支持智能指针的代码。
void SmartPointerDemo4(){ // Create the object and pass it to a smart pointer std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object pLarge->DoSomething(); // Pass raw pointer to a legacy API LegacyLargeObjectFunction(pLarge.get()); }
C++ 标准库智能指针
使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项。
unique_ptr
只允许基础指针的一个所有者。 除非你确信需要 shared_ptr,否则请将该指针用作 POCO 的默认选项。 可以移到新所有者,但不会复制或共享。 替换已弃用的auto_ptr。 与 boost::scoped_ptr 比较。 unique_ptr 小巧高效;大小等同于一个指针且支持 rvalue 引用,从而可实现快速插入和对 STL 集合的检索。 头文件:<memory>。 有关更多信息,请参见如何:创建和使用 unique_ptr 实例和unique_ptr 类。shared_ptr
采用引用计数的智能指针。 如果你想要将一个原始指针分配给多个所有者(例如,从容器返回了指针副本又想保留原始指针时),请使用该指针。 直至所有shared_ptr 所有者超出了范围或放弃所有权,才会删除原始指针。 大小为两个指针;一个用于对象,另一个用于包含引用计数的共享控制块。 头文件:<memory>。 有关更多信息,请参见如何:创建和使用 shared_ptr 实例和shared_ptr 类。weak_ptr
结合 shared_ptr 使用的特例智能指针。 weak_ptr 提供对一个或多个 shared_ptr 实例拥有的对象的访问,但不参与引用计数。 如果你想要观察某个对象但不需要其保持活动状态,请使用该实例。 在某些情况下,需要断开 shared_ptr 实例间的循环引用。 头文件:<memory>。 有关更多信息,请参见如何:创建和使用共享 weak_ptr 实例和weak_ptr 类。
void SongVector(){ vector<unique_ptr<Song>> v; // Create a few new unique_ptr<Song> instances // and add them to vector using implicit move semantics. v.push_back(unique_ptr<Song>(new Song(L"B‘z", L"Juice"))); v.push_back(unique_ptr<Song>(new Song(L"Namie Amuro", L"Funky Town"))); v.push_back(unique_ptr<Song>(new Song(L"Kome Kome Club", L"Kimi ga Iru Dake de"))); v.push_back(unique_ptr<Song>(new Song(L"Ayumi Hamasaki", L"Poker Face"))); // Pass by reference to lambda body. for_each(v.begin(), v.end(), [] (const unique_ptr<Song>& p) { wcout << L"Artist: " << p->artist << L"Title: " << p->title << endl; }); }
class MyClass{private: // MyClass owns the unique_ptr. unique_ptr<ClassFactory> factory;public: // Initialize by invoking the unique_ptr move constructor. MyClass() : factory ( unique_ptr<ClassFactory>(new ClassFactory())) { } void MakeClass() { factory->DoSomething(); }};