2013-11-25 22 views
1

我正在寫一個文件管理器,當我打開一個文件夾兩次時發現可重現的崩潰。 要mininize相關代碼:全局靜態const shared_ptr被另一個shared_ptr的析構函數奇怪地竊取並刪除了,爲什麼?

#include <vector> 
#include <memory> 
#include <boost/smart_ptr.hpp> 

namespace myns { 
    using std::shared_ptr; 
} 

class Base { 
public: 
    virtual ~Base() {} 
}; 

class Derived_1 : public Base { 
public: 
    ~Derived_1() {} // breakpoint 1 
}; 

myns::shared_ptr<Derived_1> Derived_1_ptr() { 
    static const myns::shared_ptr<Derived_1> r{new Derived_1}; 
    return r; 
} 

class Derived_2 : public Base {}; 
myns::shared_ptr<Derived_2> Derived_2_ptr() { 
    static const myns::shared_ptr<Derived_2> r{new Derived_2}; 
    return r; 
} 
std::vector<myns::shared_ptr<Base>> all_derived_ptrs() { 
    return{Derived_1_ptr(), Derived_2_ptr()}; // no breakpoint 
} 

void test_1() { 
    all_derived_ptrs(); // breakpoint 2 
    all_derived_ptrs(); // breakpoint 3 
} 

void test_2() { 
    { 
     std::vector<myns::shared_ptr<Base>> t{Derived_1_ptr(), Derived_2_ptr()}; 
    } 
    { 
     std::vector<myns::shared_ptr<Base>> t{Derived_1_ptr(), Derived_2_ptr()}; 
    } 
} 

int main() { 
    test_1(); 
    return 0; 
} 

Derived_1_ptr()和Derived_2_ptr()實際上是解決initialization order problem全局變量。

有了這些代碼:

  1. 編譯和運行無需調試在Visual Studio 2013,碰撞(Win8的:程序停止工作)。
  2. 將main更改爲test_2(),再次崩潰。
  3. 更改回test_1,如上設置3斷點並進行調試。它首先在#2,然後#1,然後#3,然後彈出一個對話框彈出「已觸發一個斷點」,並在「下一個語句」爲no breakpoint ntdll中斷。
  4. 再次更改爲test_2,在構建第一個t後立即設置一個斷點,並在調試窗口中查看tt[0]有1個強大的裁判和t[1]有2個強大的裁判。

因此,我可以推斷出該第一矢量被構造時,Derived_2_ptr()被正確地複製(++ REFERENCE_COUNT),而Derived_1_ptr()似乎抄截從靜態常量R(移動?)(REFERENCE_COUNT仍然是1)。當第一個向量被銷燬時Derived_2_ptr()正確執行--reference_count並且仍然存在,但Derived_1_ptr()失去其唯一引用並在創建第二個向量之前死亡(它是一個全局靜態變量!)。

爲什麼? 可以移動的構造函數應用於static const的東西嗎?

我嘗試將Derived_x_ptr()的返回類型更改爲const myns::shared_ptr<Derived_x>&myns::shared_ptr<Base>,並且兩者均正常工作。另外,如果我刪除Derived_2_ptr()並使all_derived_ptrs return{Derived_1_ptr(), nullptr} Derived_1_ptr()神奇地工作。

如果我使用boost :: shared_ptr來代替,問題是隱藏的(沒有程序停止對話框),但〜Derived_1()仍然被調用得太早。

這是怎麼回事?標準是否允許這種舉動?它是微軟特定的東西,還是VS2013特定的東西(我沒有其他C++ 11編譯器)?有任何想法嗎?

+0

我看到一個錯誤的調用std :: initializer_list構造函數,begin參數是好的,但結束參數是未初始化的。 –

+0

@HansPassant:你的意思是std :: initializer_list :: initializer_list(const _Elem * _First_arg,const _Elem * _Last_arg)?是的,如果我在這裏斷點_Last_arg是未初始化的,但我認爲沒關係,因爲[_First_arg,_Last_arg)是一個範圍:_First_arg是Derived_1,_First_arg + 1是Derived_2,_First_arg + 2 == _ Last_arg(標記範圍) – jingyu9575

回答

相關問題