2014-11-05 285 views
1

我知道由std::shared_ptr管理的對象不是delete d由reset(),除非它是管理它在該點的唯一shared_ptr。我知道當有多個shared_ptr管理同一個對象時,對被管理對象值的更改通過指向它的所有shared_ptr s反映出來,而對這些shared_ptr的值(而不是其管理對象的值)通過reset()婷它引起的(即改變shared_ptr從一個指向原來的管理對象,以一個指向什麼或別的東西),不改變其他shared_ptr S'值(即它們都依然指向原來的管理對象,和原來的管理對象仍然存在):重置一個shared_ptr的嵌套智能指針到一個shared_ptr(或到的unique_ptr),看似矛盾

#include <memory> 
#include <vector> 
#include <iostream> 
using namespace std; 
int main() { 
    vector<shared_ptr<int>> vec{ make_shared<int>(5) }; 
    shared_ptr<int> sptr(vec[0]); 
    ++ *sptr; 
    cout << *vec[0] << endl; // 6 
    vec[0].reset(); 
    vec.pop_back(); 
    cout << *sptr << endl; // 6 
} 

但是,邏輯是使用兩個層次:○當我失去了聯繫f間接。給定一個名爲Wrapper類和shared_ptr<shared_ptr<Wrapper>>並初始化爲任意數量的其他shared_ptr<shared_ptr<Wrapper>> S的前,爲什麼會發生這種配置允許reset()呼籲任何內部shared_ptr有效reset()所有其他內shared_ptr S'

我的猜測是:任何 ER的shared_ptr S中的管理對象是內shared_ptr(不是Wrapper)和改變的值的內shared_ptr(由reset()婷內shared_ptr,其改變從一個指向一個Wrapper實例,一個指向任何內部shared_ptr的值)在所有shared_ptr S反射,有效地引起所有shared_ptr結束了在失去間接管理實例,從而刪除Wrapper實例。

但是,同樣的邏輯,是不是復位內指針只會導致特定的內部指針在Wrapper失去管理的一個?由於所有其他外部指針指向(構建與他們,即那些)自身的內部指針,將不是那些外的人繼續在Wrapper有間接管理,因爲重置一個內部指針不改變Wrapper的價值,這應該仍然可以訪問由其他內部指針?這對我來說是一個悖論。

如果重置一個內部指針有效地重置所有的內部指針,那麼意味着內部指針'use_count()1正好在reset()之前。只有這樣,我想多shared_ptr S能出現,同時在1保持use_count()將通過錯覺管理相同的對象:他們管理不同的對象具有相同的值(在不同的地址,即對象)。我通過製作一個名稱爲Wrapperint包裝來測試此功能,該包裝的唯一數據成員是包裝的intstaticinstance_count,用於跟蹤當前存在的Wrapper實例的數量。

struct Wrapper { 
    Wrapper(int par = 0) : num(par) { ++instance_count; } 
    Wrapper(const Wrapper& src) : num(src.num) { ++instance_count; } 
    ~Wrapper() { --instance_count; } 
    int num; 
    static int instance_count; 
}; 
int Wrapper::instance_count = 0; 

int main() { 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
     make_shared<shared_ptr<Wrapper>>(
      make_shared<Wrapper>(Wrapper(5)) 
     ) 
    ); 
                  // - Output - 
    cout << Wrapper::instance_count << endl;    // 1 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(dual_ptr_1); 
    cout << Wrapper::instance_count << endl;    // 1 
    cout << dual_ptr_1->use_count() << endl;    // 1 
    cout << dual_ptr_2->use_count() << endl;    // 1 
    cout << dual_ptr_1.use_count() << endl;     // 2 
    cout << dual_ptr_2.use_count() << endl;     // 2 
    // note that above, the '->' operator accesses 
    // inner ptr while '.' operator is for outer ptr 
    cout << (*dual_ptr_1)->num << endl;      // 5 
    cout << (*dual_ptr_2)->num << endl;      // 5 
    dual_ptr_2->reset(); 
    cout << Wrapper::instance_count << endl;    // 0 
    cout << dual_ptr_1->use_count() << endl;    // 0 
    cout << dual_ptr_2->use_count() << endl;    // 0 
    cout << dual_ptr_1.use_count() << endl;     // 2 
    cout << dual_ptr_2.use_count() << endl;     // 2 
} 

顯然有指向1Wrapper對象2內的指針;內部指針'use_count最多爲1(銷燬前); Wrapper類的instance_count最多爲1(破壞前);並且間接管理的對象可以通過兩個外部指針訪問(這意味着兩個外部指針都不會被其他指針移動構建)。並重置一個內部指針有效地重置所有這些;所以我仍然不明白這似乎是悖論。

我還問在這個崗位約在上面的代碼有內shared_ptr S按unique_ptr條取代的情況下同樣的問題,內make_shared換成make_uniqueuse_count()註釋掉的內部指針(因爲unique_ptr缺少該方法),它給出相同的輸出。這對我來說似乎是一個悖論,因爲unique_ptr在這裏似乎並不獨特。

+1

我不確定你的期望是什麼,'dual_ptr_1'和'dual_ptr_2'都共享單個內部'shared_ptr'的所有權,而後者又管理'Wrapper'的單個實例。 'dual_ptr_2-> reset();'銷燬單個'Wrapper'實例,這樣你就可以共享一個空的'shared_ptr'了。 – user657267 2014-11-05 02:30:42

+0

正如其他人所說的。可以這樣想:'Wrapper * w = new Wrapper();包裝** a =&w;包裝** b =&w;刪除* a; * a = nullptr;'< - 您會驚訝現在'* b'現在爲空嗎? – cdhowie 2014-11-05 03:31:08

回答

3

提供類名爲Wrappershared_ptr<shared_ptr<Wrapper>>和 初始化 任意數量的其他shared_ptr<shared_ptr<Wrapper>> S的前,爲什麼會發生這種配置允許reset()呼籲任何 內的shared_ptr有效reset()所有其他內shared_ptrs

有沒有其他內shared_ptr S,你有包含的對象的單個實例,即

dual_ptr_1 
      \ 
      --> shared_ptr --> Wrapper 
     /
dual_ptr_2 

而且不

dual_ptr_1 --> shared_ptr 
         \ 
          --> Wrapper 
         /
dual_ptr_2 --> shared_ptr 

後,你到dual_ptr_2->reset();調用此更改爲

dual_ptr_1 
      \ 
      --> shared_ptr --> (empty) 
     /
dual_ptr_2 
1

接受的答案顯示了OP代碼中發生的情況:兩個外部shared_ptr s指向相同的內部shared_ptr,它指向Wrapper對象。 (我指的是接受答案的未編輯版本中的圖表;它在我的答案時沒有被編輯過)。接受的答案有另一個圖表,顯示了OP預期發生但未發生的情況,我將其稱爲:

案例A - 兩個指向不同內部指針指向相同對象的指針(請參閱圖的接受答案)。

這裏的代碼使得情況A:

int main() { 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
     make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(5)) 
    ); 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
     make_shared<shared_ptr<Wrapper>>(*dual_ptr_1) 
    ); 
    cout << dual_ptr_1.use_count() << endl; // 1 
    cout << dual_ptr_2.use_count() << endl; // 1 
    cout << dual_ptr_1->use_count() << endl; // 2 
    cout << dual_ptr_2->use_count() << endl; // 2 
} 

我指的是dual_ptr_1作爲第一外指針和shared_ptr它所指向作爲第一內指針。我將dual_ptr_2稱爲第二個外部指針,將shared_ptr指向它作爲第二個內部指針。兩個外部指針指向不同的內部指針。第二個外部指針不是複製構建的或者分配給第一個外部指針,所以外部指針的use_count1。第二個外部指針不指向第一個內部指針,而是指向從第一個內部指針複製構建的無名內部指針。雖然第二個外部指針仍在管理第二個內部指針,但後者的無名稱不會導致後者超出範圍。第二個內部指針指向與第一個內部指針相同的Wrapper,因爲第二個內部指針是從第一個內部指針複製構建的。由於此shared_ptr複製構造,內指針的use_count2。每個內部指針必須爲reset()爲空或其他東西,分配給別的東西,或者超出範圍以便銷燬Wrapper(內部指針不需要經過相同的操作,只要每個指針至少經歷其中一個)。

這裏的另一個,情形B - 相同的圖案A,但具有一個有錯誤的實現和不同的控制檯輸出:

int main() { 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
     make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(5)) 
    ); 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
     make_shared<shared_ptr<Wrapper>>(&(**dual_ptr_1)) 
    ); // (*) 
    cout << dual_ptr_1.use_count() << endl; // 1 
    cout << dual_ptr_2.use_count() << endl; // 1 
    cout << dual_ptr_1->use_count() << endl; // 1 
    cout << dual_ptr_2->use_count() << endl; // 1 
} // <- Double free runtime error at closing brace. 

// Replacing line (*) with: 
// shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
//  new shared_ptr<Wrapper>(&(**dual_ptr_1)) 
//); 
// has much the same effect, possibly just without compiler optimization. 

情形B是例A的越野車變型中,其中,在差情況B是第二個內部指針是從一個指向Wrapper對象的原始指針構造的,而不是從第一個內部指針複製構建或分配的。因此,任何內指針的use_count都停留在1(而不是2),即使它們都指向相同的地址。所以,這些內部指針的每個行爲就好像它是唯一一個管理對象的行爲。在main()的右大括號處會出現一個雙自由運行時錯誤,因爲最後一個超出範圍的內指針試圖釋放已被前一個內指針超出範圍釋放的內存。

這裏的案例C - 兩個外部指針指向指向不同Wrapper對象的不同的內部指針:

int main() { 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
     make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(5)) 
    ); 
    shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
     make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(**dual_ptr_1)) 
    ); 
    cout << dual_ptr_1.use_count() << endl; // 1 
    cout << dual_ptr_2.use_count() << endl; // 1 
    cout << dual_ptr_1->use_count() << endl; // 1 
    cout << dual_ptr_2->use_count() << endl; // 1 
} 

雖然Wrapper對象具有相同的價值,它們是不同的對象,他們是在不同的地址。第二個內部指針Wrapper對象是從第一個內部指針的對象Wrapper構建的副本。