我正在寫一個文件管理器,當我打開一個文件夾兩次時發現可重現的崩潰。 要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全局變量。
有了這些代碼:
- 編譯和運行無需調試在Visual Studio 2013,碰撞(Win8的:程序停止工作)。
- 將main更改爲test_2(),再次崩潰。
- 更改回test_1,如上設置3斷點並進行調試。它首先在#2,然後#1,然後#3,然後彈出一個對話框彈出「已觸發一個斷點」,並在「下一個語句」爲
no breakpoint
ntdll中斷。 - 再次更改爲test_2,在構建第一個
t
後立即設置一個斷點,並在調試窗口中查看t
。t[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編譯器)?有任何想法嗎?
我看到一個錯誤的調用std :: initializer_list構造函數,begin參數是好的,但結束參數是未初始化的。 –
@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