不使用auto_ptr。
unique_ptr可以指向动态数组。因为unique_ptr有unique_ptr
class A{};
unique_ptr<A[]> unique_ap(new A[1]{A()});
使用unique_ptr,当需要共享对象所有权时,依然可以将其转化为shared_ptr,但反过来则不行。
class A{};
unique_ptr<A> a(new A("unique_ptr"));
shared_ptr<A> b = move(a);
//a = move(b); // 报错
//a.reset(b.get()); // 运行错误
cout<<a.get()<<endl;
使用shared_ptr需要消耗更多的资源,shared_ptr需要维护一个指向动态内存对象的线程安全的引用计数器以及背后的一个控制块,这使它比unique_ptr更加复杂。
共享对象所有权也许并非你的本意,但使用shared_ptr有可能造成其他程序员无意间通过赋值给另一个共享指针而修改了你共享出来的对象。
不要使用静态分配对象的指针初始化智能指针,否则,当智能指针本身被撤销时,它将试图删除指向非动态分配对象的指针,导致未定义的行为。
A a();//全局变量
int main() {
A b();//局部变量
//unique_ptr pa(&a); // 运行错误
//unique_ptr pa(&b); // 析构两次
}
unique_ptr可以作为函数返回值。
尽管unique_ptr无拷贝语义,但提供了移动语义,所以可作为函数返回值。
class A{};
unique_ptr<A> fun(){
unique_ptr<A> pa(new A());
return pa;
}
int main() {
auto pa = fun();
return 0;
}
谨慎使用智能指针的get与release方法
当使用get方法返回裸指针时,智能指针并没有释放指向对象的所有权,所以我们必须小心使用裸指针以避免程序崩溃。
但通过unique_ptr.release()方法返回的裸指针,需要我们自己delete删除对象,因为调用release方法后,该unique_ptr不再拥有对象的所有权。
当需要裸指针与智能指针搭配使用时,需要避免如下操作:
以上操作会导致程序再次尝试销毁已被销毁了的对象,进而造成程序崩溃。所以,当使用裸指针初始化智能指针后,应确保裸指针永远不应该被再次使用。
class A{};
A *pa = new A();
unique_ptr<A> unique_pa(pa);
//delete pa; // 运行错误
A *pb = new A();
unique_ptr<A> unique_pb1(pb);
//unique_ptr unique_pb2(pb); // 运行错误
在对象内部获取shared_ptr必须使用shared_from_this方法
在对象内部如果想要获取指向该对象的shared_ptr,不可以使用this指针进行构造(理由见第3点),而必须使用shared_from_this方法,以确保所有的shared_ptr指向同一个控制块。
未使用shared_from_this情况:
class A{
public:
string id;
A(string id):id(id){}
shared_ptr<A> get_shared_ptr(){return shared_ptr<A>(this);}
~A(){}
};
int main() {
shared_ptr<A> pa(new A("shared_ptr"));
//shared_ptr pb = pa->get_shared_ptr(); // 运行错误
return 0;
}
使用shared_from_this情况:
class A : public enable_shared_from_this<A>{
public:
string id;
A(string id):id(id){}
shared_ptr<A> get_shared_ptr(){return shared_from_this();}
~A(){}
};
int main() {
shared_ptr<A> pa(new A("shared_ptr"));
shared_ptr<A> pb = pa->get_shared_ptr();
return 0;
}
谨慎使用shared_from_this方法。
当需要使用shared_from_this方法时,应注意以下几点:
不可以在构造函数中调用shared_from_this方法在下面的代码中,shared_ptr shared_pa(new A(“shared_ptr”))实际上执行了3个动作:
首先调用enable_shared_from_this的构造函数;
其次调用A的构造函数;
最后调用shared_ptr的构造函数。是第3个动作设置了enable_shared_from_this的weak_ptr。
在类的继承树中不能有2个或更多个enable_shared_from_this
必须判断调用weak_ptr.lock()获取的shared_ptr的有效性
当通过weak_ptr.lock()方法获取shared_ptr时,必须判断该shared_ptr是否有效,因为此时我们期望的shared_ptr指向的对象也许已经被删除了。
class A{
public:
string id;
A(string id):id(id){cout<<id<<":构造函数"<<endl;}
~A(){cout<<id<<":析构函数"<<endl;}
};
int main() {
weak_ptr<A> weak_pa;
shared_ptr<A> shared_pa(new A("shared_ptr"));
weak_pa = shared_pa;
cout<<weak_pa.lock()<<endl;
shared_pa.reset();
cout<<weak_pa.lock()<<endl;
return 0;
}
输出结果为:
shared_ptr:构造函数
0xfd11c8
shared_ptr:析构函数
0
尽量使用make函数初始化智能指针
使用make_unique和make_shared初始化unique_ptr和shared_ptr具有如下优点(具体见《智能指针之make_unique与make_shared》):
效率更高
当用new创建一个对象的同时创建一个shared_ptr时,这时会发生两次动态申请内存:一次是给使用new申请的对象本身的,而另一次则是由shared_ptr的构造函数引发的为资源管理对象分配的。
当使用make_shared的时候,C++编译器只会一次性分配一个足够大的内存,用来保存这个资源管理者和这个新建对象。
异常安全
由于C++不保证函数实参求值顺序,若其中一个实参是用new初始化的智能指针右值时,可能会因为异常而产生内存泄漏。
可以使用auto自动推导类型。
使用shared_ptr指向动态数组时,必须使用自定义deleter,如果没有自定义deleter,shared_ptr在超出作用域时仅仅会释放指针所指向的对象的内存,即数组的第一个元素,数组的其他元素所在内存未被释放而造成内存泄露。
class A{};
shared_ptr<A> a(new A[2]{A(),A()},[](A *a){delete []a;});
使用shared_ptr时应避免循环引用。
share_ptr的类型转换不能使用C++常用的转型函数。
share_ptr的类型转换不能使用C++常用的转型函数,即static_cast,dynamic_cast,const_cast,而要使用static_pointer_cast,dynamic_pointer_cast,const_pointer_cast。
shared_ptr没有保证共享对象的线程安全性。