簡體   English   中英

包含多態對象的向量:靜態斷言錯誤

[英]Vector containing polymorphic objects: static assertion error

自大學畢業以來沒有使用過C ++之后,我嘗試使用帶有2種類型的子對象的向量,顯然我弄錯了。

最初,我使用了一個指針向量,該指針可以工作,但是如果我理解正確,則清除該指針會泄漏內存。

我遇到的錯誤使我相信,這與類中的靜態計數器(最后一個成員銷毀了嗎?)有關,但是刪除它並不能解決問題。

錯誤導致出現在stl_construct.h中:

#if __cplusplus >= 201103L
      // A deleted destructor is trivial, this ensures we reject such types:
      static_assert(is_destructible<_Value_type>::value,
            "value type is destructible");
#endif

好吧,但是我的析構函數都是顯式聲明的。 我記得父類應該使用虛擬析構函數並將其修復,但是問題仍然相同。

將構造函數/析構函數在虛擬父類上移到公共位置應該(也確實沒有)改變事情。

現在,我假設我以某種方式濫用了向量。 這是我的示例:

主要:

#include <stdio.h>
#include "Foo.h"
#include <iostream>
Bar buildBar();

int main(int argc, char **argv)
{
    std::vector<Foo> Foos;
    Foos.reserve(1);
    Foos.push_back(buildBar());
    for (int idx=(0);sizeof(Foos);idx++)
    {
        try {
            std::cout <<  "x = " << Foos.at(idx).getLoc().at(0);
        }
        catch (std::exception& e) {
                std::cout <<  idx << ": Index out of range" << std::endl;
            }
    }
Foos.clear();
return 0;
}

Bar buildBar()
{
    Bar* temp = new Bar(5,6);
    return *temp;
}

Foo.h,將構造函數移至標頭:

#ifndef FOO_H
#define FOO_H
#include <vector>
class Foo
{
public:
    //int maxoID(){return lastoID;} //0-indexed
    /* Other things */
    virtual std::vector<int> getLoc(){ return {x_Base,y_Base};}
    Foo(): 
    //oID(-1), 
    x_Base(-1),
    y_Base(-1){}
    virtual ~Foo(){}
protected:
    int x_Base;
    int y_Base;
};

class Bar : public Foo
{
public:
    Bar(int x1, int y1):
        x_End(-1),
        y_End(-1)
        { 
            x_Base = x1; 
            y_Base = y1;
        }
    ~Bar(){}
    std::vector<int> getLoc() {return {x_Base,y_Base,x_End,y_End};}
protected:
    int x_End;
    int y_End;

};

#endif // FOO_H

首先,對您對原始指針的使用提出質疑對您有好處! 人們經常盲目地使用它們,最終會導致其他問題。

當前的問題是您有對象切片 Bar插入vector<Foo> ,會丟失有關Bar重要信息。 如果調用僅接受Foo而不接受Foo&Foo*的函數,也會發生相同的情況。

根據您的使用,您可以使用std::unique_ptrstd::shared_ptrstd::reference_wrapper

請注意,您可以使用原始指針,它們不僅會自動泄漏內存,而且vector不會對內存負責(這是使用我指出的智能指針之一的妙處)

這是完全可以接受的:

int main()
{
    Foo* f = new Foo;
    {
        std::vector<Foo*> v;
        v.push_back(f);
    } // v goes out of scope and is cleaned up
    delete f; // the vector won't have cleaned this up, it's our responsibility    
}

相反,使用unique_ptr會使事情變得簡單得多。 當然,在這樣簡短的示例中,原始指針易於使用,但是在較大的程序中,它可能會失控:

還要注意,正如@juanchopanza在注釋中指出的那樣buildBar函數會泄漏內存。 你打電話時

return *temp;

創建副本后,您將丟失temp的內存地址,因此無法將其刪除。

Bar buildBar()
{
    Bar* temp = new Bar(5,6);
    return *temp;
}

Bar b = buildBar();
Bar* p = &b; // this only reference the copy, b
delete p; // this is bad, this will (double) delete the memory from b, which is not the same as temp.

b超出范圍時將自動清除(如果您也嘗試將其刪除,這是很糟糕的),但是您無法刪除temp

歡迎回到C ++!

您不能將派生類型放入基本類型的向量中。 實例只能是基本類型。 如果嘗試將派生類型添加到此向量,則它將僅復制派生類的基本部分。

第一次您是對的-您需要一個基指針向量。 只要在從向量中刪除指針時刪除指針,就不會泄漏。

但是,這是2017年,我們現在通常希望避免新的/刪除的內容。 因此,您可以使用unique_ptr(C ++'11)向量,當它們超出范圍時會自動刪除內存。

std::vector<std::unique_ptr<Bar>> v;
v.push_back(std::make_unique<Bar>(0, 0));

您還可以使用變體向量(C ++'17),它們是新式的類型安全聯合。

typedef std::variant<Bar, Foo> MyBar
std::vector<MyBar> v;

在一個元素上使用std::visitstd::get來獲取其類型。

編輯:這是一些代碼,可從變體向量中提取元素

bool apply(int& i)
{
    std::cout << "integer: " << i << std::endl;
    return true;
}

bool apply(double& d)
{
    std::cout << "double: " << d << std::endl;
    return true;
}

void test()
{
    typedef std::variant<int, double> asdf;

    std::vector<asdf> v;

    v.push_back(10);
    v.push_back(0.5);

    auto myLambda = [](auto&& arg) { return apply(arg); };  // use of auto (the 2nd one) is required.

    for (auto& elem : v)
    {
        std::visit(myLambda, elem);
    }
}

暫無
暫無

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

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