一 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<int> sptr;
// takes ownership of pointer
sptr.reset(new int);
*sptr = 10;
// get pointer to data without taking ownership
std::weak_ptr<int> 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<int> 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<T> myPtr(new T); // Okay
unique_ptr<T> myOtherPtr = myPtr; // Error: Can't copy unique_ptr
unique_ptr<T> myPtr(new T); // Okay
unique_ptr<T> myOtherPtr = std::move(myPtr); // Okay, resource now stored in myOtherPtr
二 android的智能指针
android使用自己的智能指针sp和wp,没有使用STL的智能指针。
在使用强弱指针前类要继承RefBase。
sp和wp之间的相互转化也比较简单。
sp<Foo> sp1(foo);
wp<Foo> wp1(sp1);
sp<Bar> sp1 = wpBuffer.promote();
三 STL和android的智能指针比较
总结下来,主要的区别是
1 android的智能指针需要继承RefBase方能使用,而STL则需要额外分配智能指针的分配空间。
2 由于是继承,android 的sp和wp可以采用编译检查机制。
std::weak_ptr<int> 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<int>(42);
gw = sp;
observe();
}
observe();
}
如果weak_ptr要转为shared_ptr需要使用Lock函数
// weak_ptr::operator= example
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> sp1,sp2;
std::weak_ptr<int> wp;
// sharing group:
// --------------
sp1 = std::make_shared<int> (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<Foo> sp1(foo);
而如果你用STL的智能指针时
#include <memory>
#include <iostream>
struct Good: std::enable_shared_from_this<Good> // note: public inheritance
{
std::shared_ptr<Good> getptr() {
return shared_from_this();
}
};
struct Bad
{
std::shared_ptr<Bad> getptr() {
return std::shared_ptr<Bad>(this);
}
~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
int main()
{
// Good: the two shared_ptr's share the same object
std::shared_ptr<Good> gp1 = std::make_shared<Good>();
std::shared_ptr<Good> 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<Good> 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<Bad> bp1 = std::make_shared<Bad>();
std::shared_ptr<Bad> 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<T> 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<T> named pt, then calling T::shared_from_this will return a new std::shared_ptr<T> that shares ownership of t with pt.
这就验证了前面的一个回答
It saves a memory allocation. When you write:
std::shared_ptr<Foo> 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<Foo> 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<T> 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.