簡體   English   中英

將派生的 class 對象存儲在基礎 class 變量中

[英]Store derived class objects in base class variables

我想將幾個類的實例存儲在一個向量中。 由於所有類都從相同的基礎 class 繼承,這應該是可能的。

想象一下這個程序:

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

class Base
{
    public:
    virtual void identify ()
    {
        cout << "BASE" << endl;
    }
};

class Derived: public Base
{
    public:
    virtual void identify ()
    {
        cout << "DERIVED" << endl;
    }
};

int main ()
{
    Derived derived;
    
    vector<Base> vect;
    vect.push_back(derived);
    
    vect[0].identify();
    return 0;
}

我希望它打印"DERIVED" ,因為identify()方法是virtual 相反vect[0]似乎是一個Base實例,它打印"BASE"

我想我可以以某種方式編寫自己的容器(可能源自vector ),它能夠做到這一點(也許只保存指針......)。

我只是想問一下是否有更多的 C++'ish 方式來做到這一點。 而且我想完全兼容vector (如果其他用戶應該使用我的代碼,只是為了方便)。

您所看到的是Object Slicing
您將派生類的對象存儲在一個向量中,該向量應該存儲基類的對象,這會導致對象切片,並且正在存儲的對象的派生類特定成員被切掉,因此存儲在向量中的對象只是充當基類的對象。

解決方案:

您應該在向量中存儲指向基類對象的指針:

vector<Base*> 

通過存儲指向 Base 類的指針,就不會進行切片,並且您也可以實現所需的多態行為。
由於您要求以C++ish方式執行此操作,因此正確的方法是使用合適的智能指針,而不是在向量中存儲原始指針。 這將確保您不必手動管理內存, RAII會自動為您完成。

你正在經歷切片。 向量復制derived對象,插入一個新的Base類型。

TL;DR:您不應該從可公開復制/可移動的類繼承。


實際上可以在編譯時防止對象切片:在這種情況下,基礎對象不應該是可復制的。

案例 1:抽象基礎

如果基礎是抽象的,則無法實例化,因此您無法體驗切片。

案例2:混凝土基礎

如果基礎不是抽象的,那么它可以被復制(默認情況下)。 你有兩個選擇:

  • 完全防止復制
  • 只允許兒童復制

注意:在 C++11 中,移動操作會導致同樣的問題。

// C++ 03, prevent copy
class Base {
public:

private:
    Base(Base const&);
    void operator=(Base const&);
};

// C++ 03, allow copy only for children
class Base {
public:

protected:
    Base(Base const& other) { ... }
    Base& operator=(Base const& other) { ...; return *this; }
};

// C++ 11, prevent copy & move
class Base {
public:
    Base(Base&&) = delete;
    Base(Base const&) = delete;
    Base& operator=(Base) = delete;
};

// C++ 11, allow copy & move only for children
class Base {
public:

protected:
    Base(Base&&) = default;
    Base(Base const&) = default;
    Base& operator=(Base) = default;
};

我會使用vector<Base*>來存儲它們。 如果你說vector<Base> ,就會發生切片。

這確實意味着在從向量中刪除指針后,您必須自己刪除實際對象,否則應該沒問題。

// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived

#include <iostream>
#include <vector>

using namespace std;

class Base
{

public:

virtual void identify ()
{
    cout << "BASE" << endl;
}
};

class Derived: public Base
{
public:
virtual void identify ()
{
    cout << "DERIVED" << endl;
}
};

int main ()

{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived

pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address 
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}

正如這里提到的所有其他人一樣,由於復制構造時會發生 object 切片,因此您無法將派生的 object 插入到鹼基向量中。

如果目標是避免 memory 分配,您可以使用std::variant ,但向量將不再是基本class

using HierarchyItem = std::variant<Base, Derived>;

int main()
{
    vector<HierarchyItem> vect;
    vect.push_back(Derived());

    std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);

    return 0;
}

暫無
暫無

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

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