c++ shared_ptr 使用注意事项.

条款1:不要把一个原生指针给多个shared_ptr管理

int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); //logic error

ptr对象被删除了2次

这种问题比喻成“二龙治水”,在原生指针中也同样可能发生。

条款2:不要把this指针给shared_ptr

class Test{
public:
    void Do(){  m_member_sp =  shared_ptr<Test>(this);  }
private:
    shared_ptr<Test> m_member_sp;
};

Test* t = new Test;
shared_ptr<Test> local_sp(t);
p->Do();

发生什么事呢,t对象被删除了2次!

t对象给了local_sp管理,然后在m_member_sp =  shared_ptr<Test>(this)这句里又请了一尊神来管理t。

这就发生了条款1里“二龙治水”错误。

条款3:不要让shared_ptr的循环引用.

//---------------------------------------------
class Dog;
class Animal{
public:
    ~Animal(){
        cout << "Animal Deconstructor" << endl;
    }
    shared_ptr<Dog> _dog;
};
//---------------------------------------------
class Dog{
public:
    ~Dog(){
        cout << "Dog Deconstructor" << endl;
    }
    shared_ptr<Animal> _animal;
};

//---------------------------------------------

int main(){

    shared_ptr<Dog> dog = make_shared<Dog>();
    shared_ptr<Animal> animal = make_shared<Animal>();

    dog->_animal = animal;
    animal->_dog = dog;

    cout << "--------------------------" << endl;

    return 0;
}

这样 Animal 和 Dog 都不会被回收.

解决办法,将 shared_ptr<Dog> _dog  或   shared_ptr<Animal> _animal 其中的一个换成  weak_ptr.

条款4:不要在函数实参里创建shared_ptr

function ( shared_ptr<int>(new int), g( ) );  //有缺陷

可能的过程是先new int,然后调g( ),g( )发生异常,shared_ptr<int>没有创建,int内存泄露

shared_ptr<int> p(new int());

f(p, g());  //Boost推荐写法

条款5:对象内部生成shared_ptr

前面说过,不能把this指针直接扔给shared_ptr. 但是没有禁止在对象内部生成自己的shared_ptr

//这是Boost的例子改的。

class Y: public boost::enable_shared_from_this<Y>

{

boost::shared_ptr<Y> GetSelf()

{

return shared_from_this();

}

};

原理是这样的。普通的(没有继承enable_shared_from_this)类T的shared_ptr<T> p(new T).

p作为栈对象占8个字节,为了记录(new T)对象的引用计数,p会在堆上分配16个字节以保存

引用计数等“智能信息”。share_ptr没有“嵌入(intrusive)”到T对象,或者说T对象对share_ptr毫不知

情。Y对象则不同,Y对象已经被“嵌入”了一些share_ptr相关的信息,目的是为了找到“全局性”的

那16字节的本对象的“智能信息”。

原理说完了,就是陷阱

Y y;

boost::shared_ptr<Y> p=  y.GetSelf(); //无知的代码,y根本就不是new出来的

Y* y = new Y;

boost::shared_ptr<Y> p=  y->GetSelf(); //似是而非,仍旧程序崩盘。

Boost文档说,在调用shared_from_this()之前,必须存在一个正常途径创建的shared_ptr

boost::shared_ptr<Y> spy(new Y)

boost::shared_ptr<Y> p =  spy->GetSelf(); //OK

条款6 :处理不是new的对象要小心。

int* pi = (int*)malloc(4)

shared_ptr<int> sp( pi ) ; //delete马嘴不对malloc驴头。

条款7:多线程对引用计数的影响。

如果是轻量级的锁,比如InterLockIncrement等,对程序影响不大

如果是重量级的锁,就要考虑因为share_ptr维护引用计数而造成的上下文切换开销。

1.33版本以后的shared_ptr对引用计数的操作使用的是Lock-Free(类似InterLockIncrement函数族)

的操作,应该效率不错,而且能保证线程安全(库必须保证其安全,程序员都没有干预这些隐藏事物的机会)。

Boost文档说read,write同时对shared_ptr操作时,行为不确定。这是因为shared_ptr本身有两个成员px,pi。

多线程同时对px读写是要出问题的。与一个int的全局变量多线程读写会出问题的原因一样。

条款8:对象数组用shared_array

int* pint = new int[100];

shared_array<int> p (pint );

既然shared_ptr对应着delete;显然需要一个delete[]对应物shared_array

条款9:学会用删除器

struct Test_Deleter

{

void  operator ()( Test* p){   ::free(p);   }

};

Test* t = (Test*)malloc(sizeof(Test));

new (t) Test;

shared_ptr<Test> sp( t ,  Test_Deleter() ); //删除器可以改变share_ptr销毁对象行为

有了删除器,shared_array无用武之地了。

template<class T>

struct Array_Deleter

{

void  operator ()( T*){   delete[] p;   }

};

int* pint = new int[100];

shared_ptr<int> p (pint, Array_Deleter<int>() );

条款10:学会用分配器

存放引用计数的地方是堆内存,需要16-20字节的开销。

如果大量使用shared_ptr会造成大量内存碎片。

shared_ptr构造函数的第3个参数是分配器,可以解决这个问题。

shared_ptr<Test> p( (new Test), Test_Deleter(), Mallocator<Test>() );

注意删除器Test_Deleter是针对Test类的。分配器是针对shared_ptr内部数据的。

Mallocator<Test>()是个临时对象(无状态的),符合STL分配器规约。

template <typename T>

class Mallocator {

//略。。。。。。

T * allocate(const size_t n) const {

return singleton_pool<T,sizeof(T)>::malloc();

}

//略。。。。。。

Mallocator传入Test,实际分配的类型确是

class boost::detail::sp_counted_impl_pda<class Test *,

struct Test_Deleter,

class Mallocator<class Test> >

这是用typeid(T).name()打印出来的。可能和rebind相关。

条款11 weak_ptr在使用前需要检查合法性。

weak_ptr<K> wp;

{

shared_ptr<K>  sp(new K);  //sp.use_count()==1

wp = sp; //wp不会改变引用计数,所以sp.use_count()==1

shared_ptr<K> sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象

}

shared_ptr<K> sp_null = wp.lock(); //sp_null .use_count()==0;

因为上述代码中sp和sp_ok离开了作用域,其容纳的K对象已经被释放了。

得到了一个容纳NULL指针的sp_null对象。在使用wp前需要调用wp.expired()函数判断一下。

因为wp还仍旧存在,虽然引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。

直到最后一个weak_ptr对象被析构,这块“堆”存储块才能被回收。否则weak_ptr无法直到自己

所容纳的那个指针资源的当前状态。

条款12 不要new shared_ptr<T>

本来shared_ptr就是为了管理指针资源的,不要又引入一个需要管理的指针资源shared_ptr<T>*

条款13  尽量不要get

class B{...};

class D : public B{ ...};  //继承层次关系

shared_ptr<B> sp (new D);    //通过隐式转换,储存D的指针。

B* b = sp.get();             //shared_ptr辛辛苦苦隐藏的原生指针就这么被刨出来了。

D* d = dynamic_cast<D*>(b);  //这是使用get的正当理由吗?

正确的做法

shared_ptr<B> spb (new D)  ;

shared_ptr<D> spd = shared_dynamic_cast<D>(spb); //变成子类的指针

shared_ptr在竭尽全力表演的像一个原生指针,原生指针能干的事,它也基本上能干。

另一个同get相关的错误

shared_ptr<T> sp(new T);

shared_ptr<T> sp2( sp.get() ) ;//又一个“二龙治水”实例,指针会删2次而错误。

条款14 不要memcpy shared_ptr

shared_ptr<B> sp1 (new B)  ;

shared_ptr<B> sp2;

memcpy(&sp2,&sp1,sizeof(shared_ptr<B>)); //sp2.use_count()==1

很显然,不是通过正常途径(拷贝构造,赋值运算),引用计数是不会正确增长的。

条款15 使用BOOST预定义的宏去改变shared_ptr行为。

shared_ptr行为由类似BOOST_SP_DISABLE_THREADS这样的宏控制。需要去学习他们到底是干什么的。

大师Andrei Alexandrescu设计了一种基于模板策略设计模式的智能指针,通过几个模板参数去定制化

智能指针的行为。Boost却不以为然,官方解释是:需要统一的接口,这样利于大规模书写。

smart_ptr<T,OwnershipPolicy,ConversionPolicy,CheckingPolicy,StoragePolicy> sp(new T);

上述接口缺点是外形复杂,看上去像个大花脸。优点是客户程序员可以轻易的定制行为。

条款17 构造函数里调用shared_from_this抛例外

class Holder:public enable_shared_from_this<Holder>{

public:

Holder() {

shared_ptr<Holder> sp = shared_from_this();

int x = sp.use_count();

}

};

同前面条款5,不符合enable_shared_from_this使用前提。

总结:

学习了一天就总结出10多条条款,长期研究一下恐怕就出现条款100了。为什么还要使用shared_ptr呢?

有很多开源库用shared_ptr,而且shared_ptr具有“传染性”(某网友语:像毒品沾上就甩不掉),

抛开它就会有更严重的多龙治水现象。shared_ptr作为原生指针的替代品,能解决一定的内存泄露问题。

实际上初学原生指针时,每个人都遇到过野指针,删两次,忘记删除等问题。学习shared_ptr也会遇到。

shared_ptr的确能改善上述问题,并不能完全解决问题。shared_ptr可能在将来占主流,它最可能号令江湖,

否则一大堆auto_ptr,weak_ptr,原生指针,scoped_ptr共存就把人搞糊涂了。

内容转载自: http://blog.sina.com.cn/s/blog_62b4e3ff0100v1tc.html

时间: 2024-08-12 09:22:47

c++ shared_ptr 使用注意事项.的相关文章

c++ shared_ptr 使用注意事项. 2

1.抛弃临时对象,让所有的智能指针都有名字. 2.类向外传递 this 的  shared_ptr 让类继承   enable_shared_from_this. 然后返回  shared_from_this(); 3. 例子有时间再补上.

C++智能指针 shared_ptr

C++智能指针 shared_ptr shared_ptr 是一个标准的共享所有权的智能指针, 允许多个指针指向同一个对象. 定义在 memory 文件中(非memory.h), 命名空间为 std. shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针, 当然这需要额外的开销: (1) shared_ptr 对象除了包括一个所拥有对象的指针外, 还必须包括一个引用计数代理对象的指针. (

智能指针(shared_ptr,unique_ptr)作为函数参数或者返回值时的一些注意事项

当智能指针作为函数的参数或者返回值时,一直在纠结到底是用智能指针对象本身还是用原始指针.Herb Sutter大师的文章很好的解决了这个疑惑,参见网址: https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ 总结起来如下 1. 不要传递shared_ptr本身,而是用原始指针.因为会有性能损失,原子操作的自增自减等. 使用f(widget *w) 不使用f(shared_ptr< widget >

shared_ptr 和auto_ptr智能指针

shared_ptr:计数的智能指针 它是一个包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象.shared_ptr也可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷. 线程安全性: shared_ptr 本身不是 100% 线程安全的.它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr

auto_ptr,shared_ptr 智能指针的使用

Q: 那个auto_ptr是什么东东啊?为什么没有auto_array?A: 哦,auto_ptr是一个很简单的资源封装类,是在<memory>头文件中定义的.它使用“资源分配即初始化”技术来保证资源在发生异常时也能被安全释放(“exception safety”).一个auto_ptr封装了一个指针,也可以被当作指针来使用.当其生命周期到了尽头,auto_ptr会自动释放指针.例如: #include<memory> using namespace std;  struct X

c/c++ 智能指针 shared_ptr 使用

智能指针 shared_ptr 使用 上一篇智能指针是啥玩意,介绍了什么是智能指针. 这一篇简单说说如何使用智能指针. 一,智能指针分3类:今天只唠唠shared_ptr shared_ptr unique_ptr weak_ptr 二,下表是shared_ptr和unique_ptr都支持的操作 操作 功能描述 shared_ptr<T> sp 空智能指针,可以指向类型为T的对象 unique_ptr<T> up 空智能指针,可以指向类型为T的对象 p 将p用作一个条件判断,如果

shared_ptr 用法

引入 shared_ptr 是c++为了提高安全性而添加的智能指针,方便了内存管理. 特点 shared_ptr 是通过指针保持对象共享所有权的智能指针.多个 shared_ptr 对象可占有同一对象.这便是所谓的引用计数(reference counting).一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除.这在非环形数据结构中防止资源泄露很有帮助.使得指针可以共享对象,并且不用考虑内存泄漏问题 shared_ptr 可以支持普通指针的所有操作,完全可以

面向对象注意事项

在面向对象中,有实例变量和类变量,实例变量为类对象的实例成员,而类变量不仅类可以直接调用,而且类的对象也可以调用.类对象可以对实例变量进行添加.修改.删除操作等... 下面就用个示例来做参考: #!/usr/bin/env python # -*- coding:utf-8 -*- class PersonInfo(object): commity_data = 123 def __init__(self,name,age): self.name = name self.age = age de

实现类似shared_ptr的引用计数

13.27 定义使用引用计数版本的HasPtr #include<iostream> #include<string> #include<new> using namespace std; class HasPtr { public: HasPtr(const string &s=string()):ps(new string(s)),i(0),use(new size_t(1)) {cout<<"constructer"<