关于smart pointers(智能指针)你必须知道的事

1  shared_ptr禁止了隐式转换(explict),不能把一个指针赋值给shared_ptr. 例:shared_ptr<string> pNico = new string("nico");

2 make_shared初始化shared_ptr要比调用shared_ptr构造函数更快更安全,:shared_ptr<string> pJutta = make_shared<string>("jutta");  shared_ptr<string> pNico{new string("nico")};.因为前者只需要一次allocations,而后者需要两次,一次是指针指向的对象,一次是shared_ptr自身,

3 shared_ptr默认的deleter是 delete 而非delete[],因此当shared_ptr指向一个数组的时候需要自定义一个deleter.

    std::shared_ptr<int> p(new int[10]); // ERROR, but compiles
    std::shared_ptr<int> ptr(new int[10], [](int *p) { delete[] p; });  //ok
    std::shared_ptr<int> ptr1(new int[10], std::default_delete<int[]>());//ok

4 shared_ptr虽然解决了内存管理的难题,但是也带来了新的问题,循环引用导致内存泄露,对于此可以用weak_ptr来解决。

5 不要把一个原始指针赋值多个shared_ptr组,因为这样会导致指针被delete对此,产生野指针。一定要理解下面两种写法的区别。对于unique_ptr也是同样的道理

int* p = new int;
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p); // ERROR: two shared pointers manage allocated int
shared_ptr<int> sp1(new int);
shared_ptr<int> sp2(sp1); // OK

6 当你需要把this指针转换为shared_ptr时不要直接转化,要继承std::enable_shared_from_this<>。下面的结果可以看到如果直接用this构造会导致多个shared_ptr 组指向一个指针,也就是5所说的问题

class RightFromThis: public std::enable_shared_from_this<RightFromThis>
{
public:
    shared_ptr<RightFromThis> getSharedPtr()
    {
        return shared_from_this();
    }
};

class WrongFromThis          //error
{
public:
    shared_ptr<WrongFromThis> getSharedPtr()
    {
        return shared_ptr<WrongFromThis>(this);
    }
};


int
main()
{
    shared_ptr<RightFromThis> shared_ptr1 = std::make_shared<RightFromThis>();
    auto ptr1 = shared_ptr1->getSharedPtr();
    shared_ptr<WrongFromThis> shared_ptr2 =  std::make_shared<WrongFromThis>();;
    auto ptr2 = shared_ptr2->getSharedPtr();
    int shared_ptr1_use = shared_ptr1.use_count();   //2
    int ptr1_use = ptr1.use_count();                 //2
    int shared_ptr2_use = shared_ptr2.use_count();   //1
    int ptr2_use = ptr2.use_count();                 //1

}

7 当一个指针依赖另外另一个指针时,我们可以用一个特殊的构造函数来解决。当我们调用标记1的代码时,就表示pi依赖于px当我们调用reset px并不会立即释放指针指向的对象,因为此时pi还在引用该指针。

class C
{
public:
    ~C(){
        printf("~C\n");
    }
    int a;
};


class X
{
public:
    ~X(){
        printf("~X\n");
    }
    C a;
};

int
main()
{
    shared_ptr<X> px(new X);
    shared_ptr<C> pi(px, &px->a);    //标记1
    px.reset();
    printf("Done\n");
}

8  当shared_ptr指向一个void类型的指针,当我们需要转换指针类型时需要注意,当继承体系种的类型转换也是同样的。

    shared_ptr<void> sp(new int); // shared pointer holds a void* internally
    shared_ptr<int>(static_cast<int*>(sp.get())); // ERROR: undefined behavior
    static_pointer_cast<int*>(sp); // OK

9 unique_ptr禁止copy,但是有move操作.

std::unique_ptr<ClassA> source()
{
    std::unique_ptr<ClassA> ptr(new ClassA); // ptr owns the new object
    ...
    return ptr; // move not copy 
}
void g()
{
    std::unique_ptr<ClassA> p;
    for (int i=0; i<10; ++i) 
    {
        p = source(); // p gets ownership of the returned object
        // (previously returned object of f() gets deleted)
        ...
    }
} // last-owned object of p ge

10 unique_ptr指向一个数组时,他的处理方式跟shared_ptr稍有不同,但是shared_ptr却不行,前者它调用的是unique_ptr的一个数组类型的模板特例class unique_ptr<_Tp[], _Dp>,它在释放内存时调用的是delete[],而shared_ptr没有提供class shared_ptr<_Tp[], _Dp>,因此shared_ptr是没有提供[]操作的。另外我们要为unique_ptr提供自定义的deleter时需要提供额外的模板参数,

std::unique_ptr<int[]> p(new int[10]); // OK call delete[]
std::unique_ptr<int> p(new int[10]); //ERROR runtime erroe,call delete
std::shared_ptr<int[]> p(new int[10]); // ERROR: does not compile
std::unique_ptr<int,void(*)(int*)> p(new int[10],[](int* p) {delete[] p;})

11 shared_ptr的性能问题,.unique_ptr针对性能和灵活性进行了优化.因为unique_ptr不能拷贝所以它本身qw e的内部结构是一个指针和一个delter指针,因此unique_ptr的性能跟普通的指针没有什么区别,并且更安全,易用,但是shared_ptr不同,shared_ptr不光需要这些,它还需要一个counter表示引用对象的数量甚至还需要另外一个counter来表示weak_ptr引用数量,即使没有shared_ptr在引用该对象,在所有的引用的weak_ptr销毁前,你也需要这个counter,并且为了保证counter的在多线程下可用性它是原子的,但是它在保证counter线程安全的同时也会丧失一些性能。在发生拷贝shared_ptr会比普通的指针慢很多。

12 线程安全问题,上面说了shared_ptr的counter是原子的,在多线程环境中传递shared_ptr时counter线程安全的,但是这并不表明shared_ptr是线程安全的,你可以把shared_ptr堪称一个对象,它的counter线程安全,但是它自己并不是线程安全的。

int
main()
{

    shared_ptr<Class A> ptr = make_shared<Class A>();
    weak_ptr<Class A> weak_ptr1(ptr);
    int usecount1 = ptr.use_count();
    int usecount2 = weak_ptr1.use_count();
    {
        /*Do something with usecount1 or usecoun .Not do that,
         * usecount1 和 usecount2 并不是线程安全的,此时两个值也许在其他线程
         * 已经被改了
         * */
    }

    if(weak_ptr1.expired()) {
        shared_ptr<X> sharedPtr = weak_ptr1.lock();
        /*Do something with sharedPtr,not do that
         * 此时sharedPtr一定不为null吗,不一定,如果你把ptr以引用的方式传递
         * 到其他线程中,可能被reset了,
         * */

        if(sharedPtr) {
            //如果sharedPtr没有传递到其他线程中去,这样应该是安全的
        }
    }
}

13 unique_ptr不需要任何开销。它们的“智能”基于特殊的构造函数和特殊的析构函数以及复制语义。对于无状态或空删除器unique_ptr应该消耗与普通指针相同的内存量,并且与使用普通指针和手动删除相比,应该没有运行时开销。但是,为了避免引入不必要的开销,您应该使用函数对象(包括lambdas)作为deleter,以便编译器理想情况下进行零开销的最佳优化。

14 在你不是百分百确定你暴漏出的原始指针会不会在其他地方被delete,特别是传入一些其他三方库时,或者不确定它会被修改(比如int *a = 0,int *b = 1,a = b),不要调用get方法暴漏原始指针,并且传递到别的地方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值