时间:2021-05-20
c++11添加了新的智能指针,unique_ptr、shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated)。
但是在实际的使用过程中,很多人都会有这样的问题:
推荐使用第一种方法~
忘记delete
class Box{public: Box() : w(new Widget()) {} ~Box() { // 忘记delete w }private: Widget* w;};异常安全
void process(){ Widget* w = new Widget(); w->do_something(); // 可能会发生异常 delete w;}shared_ptr通常使用在共享权不明的场景。有可能多个对象同时管理同一个内存时。
对象的延迟销毁。陈硕在《Linux多线程服务器端编程》中提到,当一个对象的析构非常耗时,甚至影响到了关键线程的速度。可以使用BlockingQueue<std::shared_ptr<void>>将对象转移到另外一个线程中释放,从而解放关键线程。
weak_ptr是为了解决shared_ptr双向引用的问题。即:
class B;struct A{ shared_ptr<B> b;};struct B{ shared_ptr<A> a;};auto pa = make_shared<A>();auto pb = make_shared<B>();pa->b = pb;pb->a = pa;pa和pb存在着循环引用,根据shared_ptr引用计数的原理,pa和pb都无法被正常的释放。
对于这种情况, 我们可以使用weak_ptr:
class B;struct A{ shared_ptr<B> b;};struct B{ weak_ptr<A> a;};auto pa = make_shared<A>();auto pb = make_shared<B>();pa->b = pb;pb->a = pa;weak_ptr不会增加引用计数,因此可以打破shared_ptr的循环引用。
通常做法是parent类持有child的shared_ptr, child持有指向parent的weak_ptr。这样也更符合语义。
1.unique_ptr
因为C++的zero cost abstraction的特点,unique_ptr在默认情况下和裸指针的大小是一样的。
所以内存上没有任何的额外消耗,性能是最优的。
2.shared_ptr
首先需要理清楚的概念就是对象所有权的概念。所有权在rust语言中非常严格,写rust的时候必须要清楚自己创建的每个对象的所有权。
但是C++比较自由,似乎我们不需要明白对象的所有权,写的代码也能正常运行。但是明白了对象所有权,我们才可以正确管理好对象生命周期和内存问题。
C++引入了智能指针,也是为了更好的描述对象所有权,简化内存管理,从而大大减少我们C++内存管理方面的犯错机会。
我们大多数场景下用到的应该都是unique_ptr。
unique_ptr代表的是专属所有权,即由unique_ptr管理的内存,只能被一个对象持有。
所以,unique_ptr不支持复制和赋值,如下:
auto w = std::make_unique<Widget>();auto w2 = w; // 编译错误如果想要把w复制给w2, 是不可以的。因为复制从语义上来说,两个对象将共享同一块内存。
因此,unique_ptr只支持移动, 即如下:
auto w = std::make_unique<Widget>();auto w2 = std::move(w); // w2获得内存所有权,w此时等于nullptrunique_ptr代表的是专属所有权,如果想要把一个unique_ptr的内存交给另外一个unique_ptr对象管理。只能使用std::move转移当前对象的所有权。转移之后,当前对象不再持有此内存,新的对象将获得专属所有权。
如上代码中,将w对象的所有权转移给w2后,w此时等于nullptr,而w2获得了专属所有权。
在使用shared_ptr之前应该考虑,是否真的需要使用shared_ptr, 而非unique_ptr。
shared_ptr代表的是共享所有权,即多个shared_ptr可以共享同一块内存。
因此,从语义上来看,shared_ptr是支持复制的。如下:
auto w = std::make_shared<Widget>();{ auto w2 = w; cout << w.use_count() << endl; // 2} cout << w.use_count() << endl; // 1shared_ptr内部是利用引用计数来实现内存的自动管理,每当复制一个shared_ptr,引用计数会+1。当一个shared_ptr离开作用域时,引用计数会-1。当引用计数为0的时候,则delete内存。
同时,shared_ptr也支持移动。从语义上来看,移动指的是所有权的传递。如下:
auto w = std::make_shared<Widget>();auto w2 = std::move(w); // 此时w等于nullptr,w2.use_count()等于1我们将w对象move给w2,意味着w放弃了对内存的所有权和管理,此时w对象等于nullptr。
而w2获得了对象所有权,但因为此时w已不再持有对象,因此w2的引用计数为1。
只在函数使用指针,但并不保存
假如我们只需要在函数中,用这个对象处理一些事情,但不打算涉及其生命周期的管理,不打算通过函数传参延长shared_ptr的生命周期。
对于这种情况,可以使用raw pointer或者const shared_ptr&。
即:
void func(Widget*);void func(const shared_ptr<Widget>&) 实际上第一种裸指针的方式可能更好,从语义上更加清楚,函数也不用关心智能指针的类型。
在函数中保存智能指针
假如我们需要在函数中把这个智能指针保存起来,这个时候建议直接传值。void func(std::shared_ptr ptr);这样的话,外部传过来值的时候,可以选择move或者赋值。函数内部直接把这个对象通过move的方式保存起来。 这样性能更好,而且外部调用也有多种选择。
我们往往会需要在类内部使用自身的shared_ptr,例如:
class A{public:private: std::shared_ptr<widget> widget;}class Widget{public: void do_something(A& a) { a.widget = std::make_shared<Widget>(this); }}我们需要把当前shared_ptr对象同时交由对象a进行管理。意味着,当前对象的生命周期的结束不能早于对象a。因为对象a在析构之前还是有可能会使用到a.widget。
如果我们直接a.widget = this;, 那肯定不行, 因为这样并没有增加当前shared_ptr的引用计数。shared_ptr还是有可能早于对象a释放。
如果我们使用a.widget = std::make_shared<Widget>(this);,肯定也不行,因为这个新创建的shared_ptr,跟当前对象的shared_ptr毫无关系。当前对象的shared_ptr生命周期结束后,依然会释放掉当前内存,那么之后a.widget依然是不合法的。
对于这种,需要在对象内部获取该对象自身的shared_ptr, 那么该类必须继承std::enable_shared_from_this<T>。代码如下:
class Widget : public std::enable_shared_from_this<Widget>{public: void do_something(A& a) { a.widget = shared_from_this(); } }这样才是合法的做法。
1.重点理解三种智能指针的使用场景,
2.优点:
不用手动管理内存,尤其是根本不知道释放时机的时候
3.缺点:
shared_ptr的内存占用高(多了一个引用计数),对多线程不友好(对引用计数的操作要原子性)
写起来麻烦
以上就是c++11 新特性——智能指针使用详解的详细内容,更多关于c++ 智能指针的资料请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
C++中的智能指针首先出现在“准”标准库boost中。随着使用的人越来越多,为了让开发人员更方便、更安全的使用动态内存,C++11也引入了智能指针来管理动态对象
在前面一篇文章中,我们了解了C++11中引入的智能指针之一shared_ptr和weak_ptr,今天,我们来介绍一下另一种智能指针unique_ptr。往期文
虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题。////
如题,我们今天要讲的是C++11引入的三种智能指针中的:weak_ptr。在学习weak_ptr之前最好对shared_ptr有所了解。如果你还不知道share
C++中boost::share_ptr智能指针的使用方法最近项目中使用boost库的智能指针,感觉智能指针还是蛮强大的,在此贴出自己学习过程中编写的测试代码,