Effective Modern C++ - 4: 智能指針

part4 智能指針

1 裸指針問題

(1) 沒指明 指向 單個對象還是數(shù)組

(2) 沒指明 是否應(yīng)該銷毀 所指內(nèi)容

(3) delete形式必須對: delete / delete []; 否則, 結(jié)果未定義

(4) 很難確保銷毀 準(zhǔn)確地沿著代碼中的每一條路徑(包括由 異常導(dǎo)致的路徑)執(zhí)行一次

    缺少路徑 -> 資源泄漏; 多次銷毀 -> 未定義行為

(5) 通常無法判斷指針是否懸空, 即 是否指向原對象

智能指針 幾乎可以做原始指針?biāo)茏龅囊磺?/code>邻悬,但錯誤使用的機會更少

2 std::auto_ptr 問題

std::auto_ptr copy 動作(copy ctor 和 copy assignment)只在源和目的 auto_ptr都沒用 const 修飾 時具有 move 語義症昏; 否則, 編譯報錯

原因: copy ctor 和 copy assignment 的形參都是 non-const 引用

auto_ptr(auto_ptr& __a) throw() 
    : _M_ptr(__a.release() ) {} 
    
auto_ptr& 
operator=(auto_ptr& __a);

=> auto_ptr 無法用于 容器

C++98/11 容器 push_back 形參類型是 const 左值引用/ 多了 右值引用, push_back 底層對 引用的對象進行 copy 語義操作 => auto_ptr 作實參調(diào) 容器 push_back 時, 編譯報錯

    push_back(const value_type& __x);

    void push_back (value_type&& val); // C++11 新增

3 std::unique_ptr 完成 std::auto_ptr 所做的一切, 以及更多

避免 auto_ptr 會扭曲 copy 對象的含義的問題

item18 std::unique_ptr 用于 獨享所有權(quán)的資源管理

(1) 不支持 copy 語義, 只支持 move語義: 移動后, 源 unique_ptr 為空

(2) 編譯器不支持裸指針(如 new 返回值) 直接賦值給 unique_ptr

解決: 用 reset

移動構(gòu)造/移動賦值

unique_ptr( unique_ptr&& u ) noexcept;

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;
unique_ptr& operator=( unique_ptr&& r ) noexcept;

template< class U, class E >
unique_ptr& 
operator=( unique_ptr<U, E>&& r ) noexcept;

unique_ptr& 
operator=( std::nullptr_t ) noexcept;

(3) unique_ptr 作 STL 容器的元素時, 不能直接傳遞, 因為不能拷貝和賦值 -> 解決: 用 std::move() 傳遞

unique_ptr<int> up(new int(20) );
 
vector<unique_ptr<int> > vec;
vec.push_back(std::move(up) );  

(4) unique_ptr 作 STL 容器的元素時, 不能作為類的成員變量

class A
{
private:
    vector<unique_ptr<int> > vec; //錯誤
};

remember

(1) unique_ptr 小父丰、快肝谭、只支持移動、獨享所有權(quán)

(2) 資源銷毀: 默認(rèn) 用 delete; 第2模板形參可指定 用戶 Deleter

(3) std::unique_ptr 可容易蛾扇、高效地轉(zhuǎn)換為 std::shared_ptr

1 常見用法

(1) 在類層次中, 作 對象的 Factory Method 返回類型

caller 獨占 工廠返回的資源

class Investment { … };

class Stock: public Investment 
{ … };
 
class Bond: public Investment 
{ … };
 
class RealEstate: public Investment 
{ … };
template<typename... Ts>        // return std::unique_ptr
    std::unique_ptr<Investment> // to an object created
makeInvestment(Ts&&... params); // from the given args
{
    // ...
    
    auto pInvestment =               // pInvestment is of type  std::unique_ptr<Investment>
        makeInvestment( arguments ); 
    // …
} // destroy *pInvestment

(2) 所有權(quán) 遷移場景

路徑: 工廠返回資源-> 被移動到容器中 -> 移動到對象的成員 data 中 -> 對象銷毀 -> 成員銷毀過程中: 先銷毀原資源

所有權(quán)鏈 中斷(因 異常等原因)時, 也能保證 原資源被銷毀: 只要保證管理資源的 unique_ptr 的 Dtor 能被觸發(fā)

2 第2模板參數(shù)可用于指定 Deleter

默認(rèn)下, 銷毀將通過 delete 進行

unique_ptr 還可以指定 Deleter (函數(shù)/函數(shù)對象/lambda)

(1) Deleter 不含狀態(tài)時優(yōu)先選 lambda/函數(shù)對象

auto delInvmt = [](Investment* pInvestment) // custom deleter, a lambda expression
{                                           
    makeLogEntry(pInvestment);          
    delete pInvestment;                 
};

通過基類指針 delete 子類對象 => 基類 Dtor 必須為 virtual

class Investment {
public:
    …                       
    virtual ~Investment();  // base dtor must be virtual 
    …                       
};
template<typename... Ts>                                 
    std::unique_ptr<Investment, decltype(delInvmt)> // return type
makeInvestment(Ts&&... params)
{
    // (1) 創(chuàng)建 空 unique_ptr
    std::unique_ptr<Investment, decltype(delInvmt)> 
        pInv(nullptr, delInvmt); 
    
    // (2) 指向合適對象
    if ( /* a Stock object should be created */ )
    {
        pInv.reset(new Stock(std::forward<Ts>(params)...));
    }
    else if ( /* a Bond object should be created */ )
    {
        pInv.reset(new Bond(std::forward<Ts>(params)...));
    }
    else if ( /* a RealEstate object should be created */ )
    {
        pInv.reset(new RealEstate(std::forward<Ts>(params)...));
    }
    
    // (3) 返回
    return pInv;
}

(2) Deleter 含狀態(tài)時優(yōu)先選 函數(shù)(指針)

void delInvmt2(Investment* pInvestment) // custom deleter as function
{                                       
     makeLogEntry(pInvestment); 
     delete pInvestment;
}

template<typename... Ts>                // return type has
std::unique_ptr<Investment,             // size of Investment*
                void (*)(Investment*)>  // plus at least size
makeInvestment(Ts&&... params);         // of function pointer!

3 管理 單個對象 / 數(shù)組unique_ptr<T> / unique_ptr<T[]>

unique_ptr 管理數(shù)組通常沒有實踐價值

std::array / std::vector / std::string 通橙林颍總是比原始數(shù)組更好

1個例外: 類 C 的 API, 返回一個裸指針指向堆數(shù)組

4 unique_ptr 最吸引人的特性: 容易、高效地轉(zhuǎn)換為 shared_ptr

工廠總是返回 unique_ptr(比 shared_ptr 高效), client 需要時轉(zhuǎn)換成 shared_ptr

std::shared_ptr<Investment> sp = // converts std::unique_ptr  to std::shared_ptr
    makeInvestment( arguments );

item19 std::shared_ptr 用于 共享所有權(quán)的資源管理

shared_ptr 內(nèi)存模型.png

1 Deleter 不再作 第2模板形參, 而是作 Ctor 的第2參數(shù)

auto loggingDel = [](Widget *pw) // custom deleter
{                                   
    makeLogEntry(pw);
    delete pw;
};
 
std::unique_ptr< Widget,            // deleter type is
        decltype(loggingDel)>       // part of ptr type
    upw(new Widget, loggingDel);
 
std::shared_ptr<Widget>             // deleter type is not
    spw(new Widget, loggingDel);    // part of ptr type
auto customDeleter1 = [](Widget *pw) { … }; // custom deleters,
auto customDeleter2 = [](Widget *pw) { … }; // each with a different type
            
std::shared_ptr<Widget> pw1(new Widget, customDeleter1);
std::shared_ptr<Widget> pw2(new Widget, customDeleter2);
std::vector<std::shared_ptr<Widget>> vpw{ pw1, pw2 };

2 錯誤用法

(1) 用 裸指針 賦值給 不同的 shared_ptr: 不能 直接賦值

原因: 第2個之后的 shared_ptr 會建立新的 Control Block => 1份資源 對應(yīng) 多份 RC(引用計數(shù))

auto pw = new Widget; // pw is raw ptr
…
std::shared_ptr<Widget> spw1(pw, loggingDel); // create control block for *pw
…
std::shared_ptr<Widget> spw2(pw, loggingDel); // create 2nd control block for *pw!

解決: 必須通過第1個 shared_ptr 中轉(zhuǎn)

std::shared_ptr<Widget> spw1(new Widget, // direct use of new
                             loggingDel);

std::shared_ptr<Widget> spw2(spw1);     // spw2 uses same
                                        // control block as spw1

(2) 對象自身指針 this, 直接用于 賦給/構(gòu)造 shared_ptr -> 與 (1) 的問題本質(zhì)相同

std::vector<std::shared_ptr<Widget>> processedWidgets;

class Widget 
{
public:
    …
    void process();
    …
};

void Widget::process()
{
    … // process the Widget
    processedWidgets.emplace_back(this); // add it to list of
}                                        // processed Widgets;
                                         // this is wrong!

解決: 類繼承自 std::enable_shared_from_this + 工廠函數(shù) + this 換成 shared_from_this()

CRTP

class Widget: public std::enable_shared_from_this<Widget> 
{
public:
    // factory function that perfect-forwards args to a private ctor
    template<typename... Ts>
        static std::shared_ptr<Widget> 
    create(Ts&&... params);
    
    …
    void process(); // as before
    …
private:
    … // ctors
};
void Widget::process()
{
    // as before, process the Widget
    …
    // add std::shared_ptr to current object to processedWidgets
    processedWidgets.emplace_back(shared_from_this());
}

CRTP 奇怪的循環(huán)模板模式: 子類 作 基類模板實參

(1) 靜態(tài)多態(tài): 基類 non-virtual 接口函數(shù)static_cast 強轉(zhuǎn) this基類指針, 然后調(diào)子類的實現(xiàn)函數(shù)

實例化基類時, 編譯器已經(jīng)知道子類的 實現(xiàn)函數(shù)聲明

template <class T> 
struct Base
{
    void interface()
    {
        // ...
        static_cast<T*>(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        T::static_sub_func();
        // ...
    }
};

struct Derived : Base<Derived>
{
    void implementation()镀首;

    static void static_sub_func();
};

(2) 子類實例創(chuàng)建和析構(gòu)獨立的計數(shù)

item20 std::weak_ptr 用于可能懸掛類似 std::shared_ptr 的指針

(1) weak_ptr -> shared_ptr

[1] 構(gòu)造: 作 arg 構(gòu)造

[2] 賦值: lock() 成員函數(shù)

(2) shared_ptr -> weak_ptr

[1] 構(gòu)造: 作 arg 構(gòu)造

[2] 賦值: 作 arg 賦值

1 監(jiān)控相應(yīng)的 std::shared_ptr 所管理的資源 是否 被銷毀, 不影響 資源的 RC

std::weak_ptr 懸掛 == 過期( expired ) <=> 資源 被銷毀

auto spw =                      // after spw is constructed,
    std::make_shared<Widget>(); // the pointed-to Widget's  ref count (RC) is 1
    
…

std::weak_ptr<Widget> wpw(spw); // wpw points to same Widget as spw.
                                // RC remains 1
…
spw = nullptr;  // RC goes to 0, Widget is destroyed.
                // wpw now dangles
                
if (wpw.expired() ) … // if wpw doesn't point to an object…
                      // true 
std::shared_ptr<Widget> spw1 
    = wpw.lock();        // if wpw's expired, spw1 is null

auto spw2 = wpw.lock();  // same as above, but uses auto

由 weak_ptr 構(gòu)造 shared_ptr

std::shared_ptr<Widget> spw3(wpw);  // if wpw's expired,
                                    // throw std::bad_weak_ptr

2 weak_ptr + shared_ptr + unique_ptr 融合的例子: 基于 cache(緩存) 的 快速加載

    std::unique_ptr<const Widget> 
loadWidget(WidgetID id);
    std::shared_ptr<const Widget> 
fastLoadWidget(WidgetID id)
{
    // (1) 無序 map: 資源 ID, 資源 的 weak_ptr(監(jiān)控資源是否被銷毀)
    static std::unordered_map<WidgetID,
            std::weak_ptr<const Widget> > cacheControlBlock;
    
    // (2) 取出資源的管理指針: 取出 指定 ID 的 weak_ptr, 賦值給 shared_ptr
    auto objPtr = cacheControlBlock[id].lock(); // objPtr is std::shared_ptr
                                                // to cached object (or null
                                                // if object's not in cache)
    
    // (3) 若 shared_ptr 為空(即 資源不在 cache), 加載資源(用工廠函數(shù)), 用緩存控制塊管理起來
    if (!objPtr)                            // if not in cache
    { 
        objPtr = loadWidget(id);            // load it
        cacheControlBlock[id] = objPtr;     // cache it
    }
    
    // (4) 返回 資源的管理指針
    return objPtr;
}

3 指針懸掛 & 循環(huán)引用

指針懸掛 & 循環(huán)引用.png

A/B 內(nèi)部都有 指針指向?qū)Ψ?/code>, 且外部都被 shared_ptr 管理

(1) 1方(如 A)內(nèi)部應(yīng)該放 shared_ptr

(2) 另1方(如 B)內(nèi)部

[1] 裸指針 -> 問題: A 銷毀后, B 的指針懸掛, 而 B 檢測不到指針懸掛

[2] shared_ptr -> 問題: 循環(huán)引用 + A/B 的 RC = 2 => A/B lifetime 結(jié)束時, 由于 A/B 的 RC 只能減為 1 => A/B 均內(nèi)存泄漏

[3] weak_ptr -> A 的 RC = 1, B 的 RC = 2 => A/B lifetime 結(jié)束時, A/B 的 RC 減為 0/1, A 銷毀坟漱, B能檢測到自己的指針懸掛 => B 的 RC 減為 0, B 銷毀

item21 std::make_unique 和 std::make_shared 優(yōu)先于直接 new

template<typename T, typename... Ts>
    std::unique_ptr<T> 
make_unique(Ts&&... params)
{
    return std::unique_ptr<T>(
        new T(std::forward<Ts>(params)...) );
}

item22 使用 Pimp 時, 在實現(xiàn)文件中定義 特殊成員函數(shù)

remember

(1) Pimp 通過 減少編譯依賴性 來 減少構(gòu)建時間

(2) unique_ptr 用于 pImpl 指針, 必須要在所屬類的 頭文件中聲明/源文件中 unique_ptr 所指類定義之后定義 特殊成員函數(shù)(Dtor + Move Ctor/Assignment 或 Copy Ctor/Assignment)

(3) shared_ptr 沒有像 unique_ptr 的要求

// "widget.h"
class Widget  
{ 
public:
    Widget();
    …
private:
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3; // Gadget is some user-defined type
}; 

1 pimp 所指 structpimp 所屬類的成員, 在 pimp 所屬類的頭文件中 只聲明不定義

// "widget.h"
class Widget    
{ 
public:
    Widget();
    ~Widget(); // dtor is needed—see below
    …
private:
    struct Impl; // declare implementation struct
    Impl *pImpl; // and pointer to it
};

pimp 所指類 在 pimp 所屬類的源文件中 定義

// "widget.cpp"
#include "widget.h" // in impl. file "widget.cpp"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl  // definition of Widget::Impl
{ 
    std::string name; // with data members formerly
    std::vector<double> data; // in Widget
    Gadget g1, g2, g3;
};

Widget::Widget() // allocate data members for
    : pImpl(new Impl) // this Widget object
{}

Widget::~Widget() // destroy data members for
{ delete pImpl; } // this object

2 pimp 從 裸指針 換成 unique_ptr

// in "widget.h"
class Widget 
{ 
public:
    Widget();
    …
private:
    struct Impl; 
    std::unique_ptr<Impl> pImpl; // use smart pointer instead of raw pointer
}; 
// "widget.cpp"
#include "widget.h" 
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl     // as before
{ 
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()                        // per Item 21, create
    : pImpl(std::make_unique<Impl>())   // std::unique_ptr
    {}                                  // via std::make_unique

3 client 編譯報錯

#include "widget.h"
Widget w; // error!

原因: 對不完整的類型 sizeof 或 delete

編譯器 自動生成的 Dtor 銷毀 unique_ptr

[1] 先用 static_assert 保證 unique_ptr 所指對象 Widget::Impl完整的(Dtor 能看到 Widget::Impl 的定義)

默認(rèn) Dtor 在 頭文件中 inline => 看不到 源文件中 Widget::Impl 的定義 => static_assert fail

[2] 再調(diào) unique_ptr 的 Dtor: delete unique_ptr 管理的 cache

解決: 頭文件中顯式聲明 Dtor, 源文件中 Widget::Impl 定義之后 定義 Dtor

// "widget.h"
class Widget { 
public:
    Widget();
    ~Widget();      // declaration only
    …
private:            // as before
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};
// "widget.cpp"
#include "widget.h" 
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl     // as before
{ 
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()                        // per Item 21, create
    : pImpl(std::make_unique<Impl>())   // std::unique_ptr
    {}  
    
Widget::~Widget() // ~Widget definition 
    {}
Widget::~Widget() = default; // same effect as above

4 Pimp 自然地支持 移到語義, 但 聲明 Dtor 會阻止編譯器自動生成 move Ctor/Assignment -> 解決: 顯式聲明 move Ctor/Assignment

// "widget.h"
class Widget { 
public:
    Widget();
    ~Widget();
    
    Widget(Widget&& rhs);            // declarations
    Widget& operator=(Widget&& rhs); // only
    …
private: // as before
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};
// "widget.cpp"
#include <string> 
…  
struct Widget::Impl { … }; 
Widget::Widget() // as before
    : pImpl(std::make_unique<Impl>() )
    {}

Widget::~Widget() = default;            // as before

Widget::Widget(Widget&& rhs) = default;             // defini-
Widget& Widget::operator=(Widget&& rhs) = default;  // tions

<=>

Widget::Widget(Widget&& rhs)
    : pImpl(rhs.pImpl) {}

Widget& 
Widget::operator=(Widget&& rhs)
{
    pImpl = rhs.pImpl; // unique_ptr 的 移動賦值 
}

5 copy 語義

class Widget { // still in "widget.h"
public:
    … // other funcs, as before
    
    Widget(const Widget& rhs);            // declarations
    Widget& operator=(const Widget& rhs); // only
    
private:                    // as before
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};
#include "widget.h" // as before,
…                   // in "widget.cpp"

struct Widget::Impl { … };          // as before

Widget::~Widget() = default;        // other funcs, as before

Widget::Widget(const Widget& rhs)   // copy ctor
    : pImpl(std::make_unique<Impl>(*rhs.pImpl) )
{}

Widget& 
Widget::operator=(const Widget& rhs) // copy operator=
{
    *pImpl = *rhs.pImpl;
    return *this;
}

6 unique_ptr 換成 shared_ptr

std::shared_ptr Deleter 類型 不是 智能指針類型的一部分 =>

[1] 編譯器生成 更大的 運行時數(shù)據(jù)結(jié)構(gòu)更慢的 運行時代碼

[2] 所屬類 調(diào)用其 Dtor/Copy ctor(Assignment)不要求 shared_ptr 所指 對象是完整(能被看到)的

class Widget { // in "widget.h"
public:
    Widget();
    …               // no declarations for dtor
                    // or move operations
private:
    struct Impl; 
    std::shared_ptr<Impl> pImpl; // std::shared_ptr
};                               // instead of std::unique_ptr
Widget w1;
auto w2(std::move(w1)); // move-construct w2
w1 = std::move(w2); // move-assign w1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者更哄。
  • 序言:七十年代末芋齿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子成翩,更是在濱河造成了極大的恐慌觅捆,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件麻敌,死亡現(xiàn)場離奇詭異栅炒,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門赢赊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棒呛,“玉大人,你說我怎么就攤上這事域携。” “怎么了鱼喉?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵秀鞭,是天一觀的道長。 經(jīng)常有香客問我扛禽,道長锋边,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任编曼,我火速辦了婚禮豆巨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掐场。我一直安慰自己往扔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布熊户。 她就那樣靜靜地躺著萍膛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嚷堡。 梳的紋絲不亂的頭發(fā)上蝗罗,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音蝌戒,去河邊找鬼串塑。 笑死,一個胖子當(dāng)著我的面吹牛北苟,可吹牛的內(nèi)容都是我干的桩匪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粹淋,長吁一口氣:“原來是場噩夢啊……” “哼吸祟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桃移,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屋匕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后借杰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體过吻,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纤虽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乳绕。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逼纸,靈堂內(nèi)的尸體忽然破棺而出洋措,到底是詐尸還是另有隱情,我是刑警寧澤杰刽,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布菠发,位于F島的核電站,受9級特大地震影響贺嫂,放射性物質(zhì)發(fā)生泄漏滓鸠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一第喳、第九天 我趴在偏房一處隱蔽的房頂上張望糜俗。 院中可真熱鬧,春花似錦曲饱、人聲如沸悠抹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锌钮。三九已至,卻和暖如春引矩,著一層夾襖步出監(jiān)牢的瞬間梁丘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工旺韭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留氛谜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓区端,卻偏偏與公主長得像值漫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子织盼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • C++智能指針 原文鏈接:http://blog.csdn.net/xiaohu2022/article/deta...
    小白將閱讀 6,864評論 2 21
  • auto_ptr與unique_ptr auto_ptr與unique_ptr都是獨占所有權(quán)的智能指針類型杨何,前者由...
    RC_HT閱讀 246評論 0 0
  • 智能指針 C++11 引入了 3 個智能指針類型: std::unique_ptr<T> :獨占資源所有權(quán)的指針。...
    linjinhe閱讀 691評論 0 0
  • 本文根據(jù)眾多互聯(lián)網(wǎng)博客內(nèi)容整理后形成沥邻,引用內(nèi)容的版權(quán)歸原始作者所有危虱,僅限于學(xué)習(xí)研究使用,不得用于任何商業(yè)用途唐全。 智...
    深紅的眼眸閱讀 701評論 0 0
  • 0.什么是智能指針? auto_ptr, shared_ptr, weak_ptr, unique_ptr這四個是...
    AI秘籍閱讀 623評論 0 1