(读书笔记:全部摘抄自cPP标注库)
C++ 11 中,标准库提供了两大类smart pointer:
1. Class shared_ptr 实现共享式拥有(shared ownership)概念。多个 只能指针 可以指向相同的对象,改对象和其相关资源会在 “最后一个reference被销毁” 时释放。为了在结构较为复杂的情境中执行上述工作,标准库提供了weak_ptr ,bad_weak_ptr 和 enable_shared_from_this等辅助类。
2,Class_unique_ptr 实现独占式拥有(exclusive ownership) 或 严格拥有(strict ownership)概念。保证同一时间内只有一个smart pointer可以指向该对象。你可以移交拥有权。他对于 避免资源泄露特别有用。
c++98只让c++标准库提供一个smart pointer class : auto_ptr<>,其设计是为了执行现今的unique_ptr所提供的服务。然而由于当时缺乏语言特性如“针对构造和赋值”的move语义,以及其他瑕疵,这个class不易被理解且容易出错。因此在TR1引入clss shared_ptr,c++11引入class unique_ptr之后,auto_ptr成为c++11中被证实反对的成分,除非老旧代码需要编译,否则你不应使用它。
所有smart pointer class 都被定义于头文件<memory>内。
shard_ptr 的目标是没有蛀牙(在其所指向的对象不再被需要之后,自动释放与对象相关的资源)。
使用shard_ptr
#include<iostream> #include<string> #include<vector> #include<memory> using namespace std; int main(){ shared_ptr<string> pNico(new string("nico")); shared_ptr<string> pJutta(new string("jutta")); (*pNico)[0] = 'N'; pJutta->replace(0,1,"J"); vector<shared_ptr<string>> whoMadeCoffee; whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pNico); whoMadeCoffee.push_back(pNico); for(auto ptr : whoMadeCoffee) { cout << *ptr << " "; } cout << endl; *pNico = " Nicolai"; for(auto ptr : whoMadeCoffee) { cout << *ptr << " "; } cout << endl; cout << "use_cout " << whoMadeCoffee[0].use_count() << endl; }
需要注意的是,由于“接受单一pointer作为唯一实参" 的构造函数是explicit,所以这里不能使用赋值符,因为那样的话会被视为需要一个隐式转换,然而新式的语法是被接受的:
shared_ptr<string> pNico = new string("nico"); //ERROR shared_ptr<string> pNico{new string("nico")}; //OK
也可以使用便捷的 make_shared() :
shared_ptr<string> pNico = make_shared<string>("nico");
这种方式比较快,也比较安全,因为它使用一次而非二次分配:一次针对对象,另一次针对"shared pointer 用以控制对象" 的shared data。
另一种写法是,先声明shard pointer ,然后对它赋值一个new pointer,然后不可以使用assignment操作符,必须改用reset():
pNico3 = new string("nico"); //ERROR :no assignment for ordinary pointers pNico3.reset(new string("nico")); //Ok
定义一个Deleter
定义一个deleter,例如让它在“删除被指向对象”之前先打印一条信息:
shared_ptr<string> pNico(new string("nico"), [](string * p){ cout << "delete " << *p << endl; delete p; }); pNico = nullptr; //pNico does not refer to string any longer whoMadeCoffee.resize(2); // all copies of string in pNico are destoryed
其中函数对象 D del参数是一个lambda表达式。
关于shared_ptr 的构造函数,参考链接
对付Array
shared_ptr提供的default deleter 调用的是delete,不是delete[] ,这意味着局限性,只有当shared_ptr拥有“由new建立起来的单一对象”,default deleter才试用。然而很不幸,为array建立一个shared_ptr是可能的,却是错误的:
std::shared_ptr<int> p(new int[10]); //ERROR,but compiles
故,如果你试用new[]建立一个array of object ,必须自己定义自己deleter。你可以传递一个函数,或者函数对象,或者lambda,让它们针对传入的寻常指针调用delete[]。例如:
shared_ptr<int> p(new int[10], [](int *p) { delete[] p; });
也可以试用为unique_ptr而提供的辅助函数作为deleter,其内调用deleter[]:
std::shared_ptr<int> p(new int[10], std::default_delete<int[]> ());
需要注意的是:shared_ptr 和unique_ptr 以稍稍不同的方式处理deleter。例如unique_ptr允许只传递对应的元素类型作为template实参,但这对shared_ptr就不行:
std::unique_ptr<int[]> p(new int[10]); //OK std::shared_ptr<int[]> p(new int[10]); //ERROR:does not compile
此外,对于 unique_ptr,你必须明确给予第二个template实参,指出自己的deleter:
std::unique_ptr<int,void(*)(int *)> p(new int[10], [](int *p) { delete[] p; });
还需注意,shared_ptr不提供operator[]。至于unique_ptr,它有一个针对array的偏特化版本,该版本提供operator[] 取代 operator* 和 operator-> 。之所以有此差异是因为,unique_ptr在效能和弹性上进行了优化。