簡體   English   中英

刪除std :: unique_ptr中的派生類 <Base> 集裝箱

[英]Deleting derived classes in std::unique_ptr<Base> containers

我有點困惑。 基本上,我有兩個不同的資源管理器(AudioLibrary和VideoLibrary),它們都從共享的BaseLibrary類繼承。 此基類包含對音頻和視頻的引用。 音頻和視頻都從名為Media的父類繼承。

我將數據保存在地圖中,填充了unique_ptr。 但令我驚訝的是,我發現當這些指針最終通過.erase被刪除時,只調用了Media的基本析構函數。

我想我錯過了一些東西,但我認為編譯器/運行時庫會知道它指向視頻或音頻並將其稱為析構函數。

似乎並非如此。 我被迫做這樣的事情來實際收回我的所有資源:

void AudioLibrary::deleteStream( const std::string &pathFile )
{
    auto baseStream = mStreams[ pathFile ].release();
    mStreams.erase( pathFile );

    // Re-cast!
    auto aStream = static_cast<AudioStream*>( baseStream );
    delete aStream;
}

這是正常的行為嗎?

更新:

你沒事 - 當然這是析構函數缺失的'虛擬'。 我想我最近剛想到的虛擬和繼承意味着什么越來越少,有點讓我的頭腦失去其功能,而不是概念本身。 偶爾會發生在我身上。

unique_ptr<T>的默認刪除器是恰當命名的default_delete<T> 這是一個無狀態函子,在其T *參數上調用delete

如果希望在銷毀基類的unique_ptr時調用正確的析構函數,則必須使用虛擬析構函數,或者在刪除器中捕獲派生類型。

您可以使用函數指針刪除器和無捕獲的lambda輕松完成此操作:

std::unique_ptr<B, void (*)(B *)> pb
    = std::unique_ptr<D, void (*)(B *)>(new D,
        [](B *p){ delete static_cast<D *>(p); });

當然,這意味着您需要將刪除器的模板參數添加到unique_ptr所有用途中。 將其封裝在另一個類中可能更優雅。

另一種方法是使用shared_ptr ,因為它確實捕獲派生類型,只要您使用std::shared_ptr<D>(...)創建派生的shared_ptr或者最好是std::make_shared<D>(...)

這是因為您尚未將Media析構函數聲明為virtual 如您所見,如果您這樣做,例如:

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

struct AudioLibrary : Media {};
struct VideoLibrary : Media {};

int main() {
    std::map<int, std::unique_ptr<Media>> map;
    map[0] = std::unique_ptr<Media>(new AudioLibrary());
    map[1] = std::unique_ptr<Media>(new VideoLibrary());
}

演示

兩個析構函數都將被調用。

暫無
暫無

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

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