簡體   English   中英

shared_ptr 和 operator new 的問題

[英]Problems with shared_ptr and operator new

I get a bug in a test code, where I have to test a class located at the bottom of a diamond heritage, and above all, which forms a circular dependency with an other class, too difficult to change (I promise it is not my代碼...)。

class A 的構造函數要求對 class B 的引用,並且 class B 要求對 ZA2F2ED4F8EBC2C6BBD4 A...
出於單元測試的目的,我想通過為 A 分配 memory 來打破這種依賴關系,但不構建實例。 然后通過將此 shared_ptr 傳遞給 B,一旦 B 被構建,通過構建 A 實例,其構造函數將引用 B。

我成功地在您可以在本地或任何在線編譯器執行的示例代碼中重現了該問題:

#include <iostream>
#include <memory>
using namespace std;

struct ILevel_0
{
    virtual ~ILevel_0() = 0;
};
ILevel_0::~ILevel_0() {}

struct Level_1_A : virtual ILevel_0
{
    int a;
};

struct Level_1_B : virtual ILevel_0
{
    int b;
};

struct Level_2 : Level_1_A, Level_1_B
{
    int c;
};

struct OtherStruct
{
    OtherStruct(const std::shared_ptr<ILevel_0>& lev) : _lev(lev) {}
    std::shared_ptr<ILevel_0> _lev;
};

int main()
{
    std::shared_ptr<Level_2> level2;
    
    void* level2_rawMemory = operator new(sizeof(Level_2));
    Level_2* level2Ptr = static_cast<Level_2*>(level2_rawMemory);
    level2.reset(level2Ptr);
    
    std::cout << "------ 1" << std::endl;
    
    std::shared_ptr<ILevel_0> level0 = level2;  // OK
    
    std::cout << "------ 2" << std::endl;

    // OtherStruct otherStruct(level2);   // KO  : crash after ------ 1
    
    std::cout << "------ 3" << std::endl;

    // Needed, else the shared_ptr's deleter would crash by calling the delete instruction
    new (level2.get()) Level_2{};

    std::cout << "------ 4" << std::endl;
    
    return 0;
}

可以看到一行(構建OtherStruct實例)被注釋掉了,這樣代碼代碼就可以編譯運行了,不會崩潰。 如果取消注釋,程序將崩潰,不打印“----- 2”。 我不明白為什么,我也不明白為什么前面的指令(level0 building)沒有崩潰。

感謝您的幫助,並為近似英語感到抱歉。

在 ASAN/UBSAN 下運行您的代碼:

在此處輸入圖像描述

走過去就發現

39          std::shared_ptr<ILevel_0> level0 = level2; // OK

是罪魁禍首。

然而,真正的罪犯是這樣的:

    void* level2_rawMemory = operator new(sizeof(Level_2));
    auto* level2Ptr = static_cast<Level_2*>(level2_rawMemory);
    level2.reset(level2Ptr);

它違反了 C++ memory model。 您不能將原始 memory 重新解釋為 Level_2 object。 特別是當數據類型不是 POD 時(它們不是,因為它們是虛擬的)。

而是使用

    Level_2* level2Ptr = new Level_2;
    level2.reset(level2Ptr);

或者確實

    level2.reset(new Level_2);

最好的:

std::shared_ptr<Level_2> level2 = std::make_shared<Level_2>();

實際上考慮安全指針轉換(請參閱使用 boost::function 並帶有指向派生 class 的共享指針的參數):

std::shared_ptr<ILevel_0> level0 = dynamic_pointer_cast<ILevel_0>(level2); // OK

帶修復的演示

住在科利魯

#include <iostream>
#include <memory>
#include <utility>

struct ILevel_0 { virtual ~ILevel_0() = default; };

struct Level_1_A : virtual ILevel_0 { int a{}; };
struct Level_1_B : virtual ILevel_0 { int b{}; };

struct Level_2 : Level_1_A, Level_1_B { int c{}; };

struct OtherStruct {
    explicit OtherStruct(std::shared_ptr<ILevel_0> lev) : _lev(std::move(lev)) {}
    std::shared_ptr<ILevel_0> _lev;
};

int main() {
    std::cout << std::unitbuf;
    std::shared_ptr<Level_2> level2 = std::make_shared<Level_2>();

    std::cout << "------ 1" << std::endl;
    auto level0 = std::dynamic_pointer_cast<ILevel_0>(level2); // OK

    std::cout << "------ 2" << std::endl;

    OtherStruct otherStruct(level2);

    std::cout << "------ 3" << std::endl;

    level2 = std::make_shared<Level_2>();

    std::cout << "------ 4" << std::endl;
}

印刷

------ 1
------ 2
------ 3
------ 4

並且沒有消毒劑警告。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM