再说智能指针

一 STL的智能指针及使用

STL中智能指针有std::shared_ptr std::weak_ptr std::unique_ptr std::auto_ptr。其中auto_ptr在C++11时已经被启用,C++17删除了。
其中std::shared_ptr 与android 的强指针sp用法相似,而std::weak_ptr 与android中的wp用法相似。相关用法如下例用例

  // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";

unique_ptr和shared_ptr的区别是,unique_ptr 不能copy。

unique_ptr myPtr(new T);       // Okay
unique_ptr myOtherPtr = myPtr; // Error: Can't copy unique_ptr
unique_ptr myPtr(new T);                  // Okay
unique_ptr myOtherPtr = std::move(myPtr); // Okay, resource now stored in myOtherPtr

二 android的智能指针

android使用自己的智能指针sp和wp,没有使用STL的智能指针。
在使用强弱指针前类要继承RefBase。
sp和wp之间的相互转化也比较简单。

    sp sp1(foo);
    wp wp1(sp1);

    sp sp1 = wpBuffer.promote();

三 STL和android的智能指针比较

总结下来,主要的区别是
1 android的智能指针需要继承RefBase方能使用,而STL则需要额外分配智能指针的分配空间。
2 由于是继承,android 的sp和wp可以采用编译检查机制。

std::weak_ptr gw;
 
void observe()
{
    std::cout << "use_count == " << gw.use_count() << ": ";
    if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
    std::cout << *spt << "\n";
    }
    else {
        std::cout << "gw is expired\n";
    }
}
 
int main()
{
    {
        auto sp = std::make_shared(42);
    gw = sp;
 
    observe();
    }
 
    observe();
}

如果weak_ptr要转为shared_ptr需要使用Lock函数

// weak_ptr::operator= example
#include 
#include 

int main () {
  std::shared_ptr sp1,sp2;
  std::weak_ptr wp;
                                       // sharing group:
                                       // --------------
  sp1 = std::make_shared (10);    // sp1
  wp = sp1;                            // sp1, wp

  sp2 = wp.lock();                     // sp1, wp, sp2
  sp1.reset();                         //      wp, sp2

  sp1 = wp.lock();                     // sp1, wp, sp2

  std::cout << "*sp1: " << *sp1 << '\n';
  std::cout << "*sp2: " << *sp2 << '\n';

  return 0;
}

网上相关的解答比较少,比较有说服力的如下:


It automatically allows you to create sp from any object implementing RefBase, while for shared pointer you can shoot yourself in the foot while trying to wrap raw pointer into shared one.

So while for shared_ptr you might need this: http://en.cppreference.com/w/cpp/memory/enable_shared_from_this

for sp you can almost safely pass raw pointer to sp contructor.

总结起来就是android的智能指针使用可以针对任何继承RefBase的类。比如前面的例子:

Foo* foo = new Foo(&isDeleted);
sp sp1(foo);

而如果你用STL的智能指针时

#include 
#include 
 
struct Good: std::enable_shared_from_this // note: public inheritance
{
    std::shared_ptr getptr() {
        return shared_from_this();
    }
};
 
struct Bad
{
    std::shared_ptr getptr() {
        return std::shared_ptr(this);
    }
    ~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
 
int main()
{
    // Good: the two shared_ptr's share the same object
    std::shared_ptr gp1 = std::make_shared();
    std::shared_ptr gp2 = gp1->getptr();
    std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';
 
    // Bad: shared_from_this is called without having std::shared_ptr owning the caller 
    try {
        Good not_so_good;
        std::shared_ptr gp1 = not_so_good.getptr();
    } catch(std::bad_weak_ptr& e) {
        // undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)
        std::cout << e.what() << '\n';    
    }
 
    // Bad, each shared_ptr thinks it's the only owner of the object
    std::shared_ptr bp1 = std::make_shared();
    std::shared_ptr bp2 = bp1->getptr();
    std::cout << "bp2.use_count() = " << bp2.use_count() << '\n';
} // UB: double-delete of Bad

输出结果

gp2.use_count() = 2
bad_weak_ptr
bp2.use_count() = 1
Bad::~Bad() called
Bad::~Bad() called
*** glibc detected *** ./test: double free or corruption

必须要使用enable_shared_from_this ,解释如下:

Publicly inheriting from std::enable_shared_from_this provides the type T with a member function shared_from_this. If an object t of type T is managed by a std::shared_ptr named pt, then calling T::shared_from_this will return a new std::shared_ptr that shares ownership of t with pt.

这就验证了前面的一个回答

It saves a memory allocation. When you write:

std::shared_ptr pFoo{new Foo(bar)};
pFoo actually has a pointer to a shared data structure (allocated on the heap), which has the reference counters, and the pointer to the actual Foo object. By making objects be derived from RefBase, you can embed the reference counts in the object itself (saving the additional memory allocation).

Interestingly, with C++11 onwards, you can avoid the additional memory allocation by using std::make_shared which will do a single memory allocation and construct the shared data structure and the Foo object in it.

The fact there is no compile time checking of the derivation from RefBase is carelessness. m_ptr should have been declared as RefBase *m_ptr, and then operator * (etc) should have done a static_cast to T*. In fact, I would probably have made sp inherit from sp_base which had the comparison operators as public, and the other functions as protected.

Edit

On second thoughts, there is quite a bit of compile time checking. If T doesn't have an incStrong member, the compilation will fail, and it almost certainly won't unless it derives from RefBase. I still think converting a T* to a RefBase* would have been a better check, but the one that is there is probably good enough.

你可能感兴趣的:(再说智能指针)