再說智能指針

一 STL的智能指針及使用

STL中智能指針有std::shared_ptr std::weak_ptr std::unique_ptr std::auto_ptr僻爽。其中auto_ptr在C++11時已經(jīng)被啟用,C++17刪除了蔗衡。
其中std::shared_ptr 與android 的強(qiáng)指針sp用法相似,而std::weak_ptr 與android中的wp用法相似逼纸。相關(guān)用法如下例用例

  // 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的區(qū)別是济蝉,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的智能指針雁乡。
在使用強(qiáng)弱指針前類要繼承RefBase。
sp和wp之間的相互轉(zhuǎn)化也比較簡單曲饱。

    sp<Foo> sp1(foo);
    wp<Foo> wp1(sp1);

    sp<Bar> sp1 = wpBuffer.promote();

三 STL和android的智能指針比較

總結(jié)下來珠月,主要的區(qū)別是
1 android的智能指針需要繼承RefBase方能使用,而STL則需要額外分配智能指針的分配空間驻谆。
2 由于是繼承庆聘,android 的sp和wp可以采用編譯檢查機(jī)制。

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要轉(zhuǎn)為shared_ptr需要使用Lock函數(shù)

// 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;
}

網(wǎng)上相關(guān)的解答比較少区端,比較有說服力的如下:


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.

總結(jié)起來就是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

輸出結(jié)果

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.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市埃跷,隨后出現(xiàn)的幾起案子邮利,更是在濱河造成了極大的恐慌垃帅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件方庭,死亡現(xiàn)場離奇詭異酱固,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)运悲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門班眯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳖敷,你說我怎么就攤上這事」髋耍” “怎么了崖媚?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長肴楷。 經(jīng)常有香客問我荠呐,道長赛蔫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任泥张,我火速辦了婚禮呵恢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘媚创。我一直安慰自己渗钉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布钞钙。 她就那樣靜靜地躺著鳄橘,像睡著了一般声离。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘫怜,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天术徊,我揣著相機(jī)與錄音宝磨,去河邊找鬼弧关。 笑死,一個胖子當(dāng)著我的面吹牛唤锉,可吹牛的內(nèi)容都是我干的世囊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼窿祥,長吁一口氣:“原來是場噩夢啊……” “哼株憾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晒衩,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤嗤瞎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后听系,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贝奇,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年靠胜,在試婚紗的時候發(fā)現(xiàn)自己被綠了掉瞳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡浪漠,死狀恐怖陕习,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情址愿,我是刑警寧澤该镣,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站响谓,受9級特大地震影響损合,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娘纷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一塌忽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧失驶,春花似錦、人聲如沸枣购。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涩堤,卻和暖如春眷蜓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胎围。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工吁系, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人白魂。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓汽纤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親福荸。 傳聞我的和親對象是個殘疾皇子蕴坪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345