五、C++智能指针基础
1、void func(){
std::shared_ptr<int> a1(new int(10));
// 不需要delete,内存会自动地释放掉.
}
2、四种智能指针,都在#include <memory>中:
auto_ptr:C++98的,不建议再使用.
shared_ptr:通过引用计数实现指针共享.
unique_ptr:一个指针只能由一个人来使用,即一份资源某一时刻只能由一个unique_ptr来管理.
weaked_ptr:与shared_ptr搭配使用.
3、shared_ptr的使用:
std::shared_ptr<MyClass> objectPtr1(new MyClass());
objectPtr1.use_count(); // 引用计数
std::shared_ptr<MyClass> objectPtr2(objectPtr1);
objectPtr2.reset(); // use_count-1
或objectPtr2 = nullptr; // use_count-1
std::shared_ptr<MyClass> objectPtr3;
objectPtr1.swap(objectPtr3); //
或std::swap(objectPtr1, objectPtr3);//
auto p = objectPtr1.get(); // 获得原始指针
p->func();
objectPtr1->func();
(*objectPtr1).func();
if(objectPtr1.unique()) // 效率比下面那句高.
if(1 == objectPtr1.use_count()) // 作用同上.
objectPtr1可以作为值传递(会改变引用计数),也可以作为常引用传递(不会改变引用计数).
要注意看需求来设计函数的接口:
void func(const MyClass& m); // func(*objectPtr1);
void func(std::shared_ptr<MyClass> m); // func(objectPtr1);
void func(const std::shared_ptr<MyClass>& m); // func(objectPtr1);
可以自定义销毁资源的函数:
void myDeleter(MyClass* p){
delete p;
}
std::shared_ptr<MyClass> objectPtr(new MyClass(), myDeleter);
4、shared_ptr通过用weaked_ptr打破循环引用。只要当一方改用weaked_ptr就能打破。
当外部有其他shared_ptr管理weaked_ptr所管理的资源的时候,weaked_ptr所指向的指针是有效的;当外部都没有shared_ptr管理weaked_ptr所管理的资源的时候,weaked_ptr所指向的指针是无效的。
std::shared_ptr<MyClass> sharePtr(new MyClass(1));
std::weaked_ptr<MyClass> weakPtr(sharePtr); // sharePtr的引用计数不变
auto sharePtrTmp = weakPtr.lock(); // 调用lock的时刻,如果weakPtr所管理的资源(即new MyClass()),其引用计数不为0的时候,则返回一个有效的智能指针shared_ptr,此时加上sharePtrTmp这份引用计数的话,执行完这语句后引用计数应该是大于等于2。否则返回一个空指针。
weakPtr.reset(); // 不再管理之前管理的那份资源.
weakPtr.reset(new MyClass(2)); // 不再管理之前管理的那份资源,而开始管理另一份资源.
weakPtr.lock(); // 返回空指针.
if(weakPtr.expired()){} // weaked_ptr所管理的资源是否有效.
5、this转换为智能指针:
void MyClass::func(){
std::shared_ptr<MyClass> sharePtr(this); // 错误做法,会造成一个资源有两个智能指针在控制,从而导致析构函数被调用了两次.
auto sharePtr = enable_shared_from_this(); // 正确写法.
}
std::shared_ptr<MyClass> sharePtr(new MyClass());
sharePtr.func();
正确做法是继承模板类enable_shared_from_this:
class MyClass : public std::enable_shared_from_this<MyClass>{
...
}
但是如果继承了enable_shared_from_this,下面这句话就不推荐了,因为继承了enable_shared_from_this就意味着我们希望MyClass是一种以智能指针的形式来管理的类型:
MyClass m1;
6、使用智能指针的默认选择是std::unique_ptr,除非要共享。
std::unique_ptr<MyClass> uniPtr(new MyClass(1));
if(uniPtr){
uniPtr->func();
(*uniPtr).func();
}
auto p = uniPtr.get(); // 获得原始指针
uniPtr.release(); // 不再管理所管理的资源.
delete p;
uniPtr.reset();
uniPtr.reset(new MyClass(2)); // 先放原有的资源释放,再重新去管理另外一份资源.
std::unique_ptr没有拷贝构造函数,但是有移动构造函数。
void func(std::unique_ptr<MyClass> uniPtrTmp){
...
}
func(std::move(uniPtr)); // 移交资源给uniPtrTmp.后面不能再使用uniPtr了.
std::unique_ptr转换为std::shared_ptr:
std::shared_ptr<MyClass> sharePtr(std::move(uniPtr));
7、使用智能指针的注意事项:
①绝对不要自己手动地管理资源,也即不要自己new和delete;
②一个裸的指针不要用两个shared_ptr管理;unique_ptr也一样。
auto pMyClass = new MyClass();
std::shared_ptr<MyClass> sharePtr1(pMyClass);
std::shared_ptr<MyClass> sharePtr2(pMyClass); // 错误写法.
因为sharePtr1和sharePtr2的引用计数是相互独立的。
③用weaked_ptr打破循环引用,用裸指针也可以;
④当需要在类的内部接口中,如果需要将this作为智能指针来使用的话,需要用该类派生自enable_shared_from_this。
⑤shared_ptr、weaked_ptr和裸指针相比,在内存占用上会大很多,并且效率上也会有影响,尤其是在多线程模式下。
std::shared_ptr<MyClass> sharePtr1(new MyClass()); // 会new两次
std::shared_ptr<MyClass> sharePtr2 = std::make_shared<MyClass>(); // 只会new一次。建议使用这个。
⑥enable_shared_from_this的shared_from_this()在构造析构函数的时候是不能使用的。
⑦某些情况下会出现内存不会降的问题,尤其是我们使用weaked_ptr来处理循环引用的时候。
⑧如果有可能,优先使用类的实例,其次再考虑使用std::unique_ptr,最后才考虑用std::shared_ptr。
MyClass m1; // 使用类的实例