将派生类对象存储在基类变量中

Posted

技术标签:

【中文标题】将派生类对象存储在基类变量中【英文标题】:Store derived class objects in base class variables 【发布时间】:2012-01-08 13:00:17 【问题描述】:

我想在一个向量中存储几个类的实例。由于所有类都继承自同一个基类,这应该是可能的。

想象一下这个程序:

#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”方法是虚拟的。相反,'vect[0]' 似乎是一个 'Base' 实例,它会打印

基础

我想我可以以某种方式编写自己的容器(可能从向量派生)能够做到这一点(也许只保存指针......)。我只是想问一下是否有更多的 C++ish 方法可以做到这一点。而且我希望完全兼容矢量(只是为了方便其他用户使用我的代码)。

【问题讨论】:

我想知道如果你创建一个带有纯虚函数identify 的基类,然后让你的BaseDerived 都继承自该基类会发生什么。 @Mr Lister 感谢您的评论,但这不会改变结果。 另见this。 【参考方案1】:

您看到的是 Object Slicing。 您将派生类的对象存储在一个向量中,该向量应该存储基类的对象,这会导致对象切片,并且正在存储的对象的派生类特定成员被切掉,因此存储在向量中的对象只是充当基类的对象。

解决方案:

你应该在向量中存储指向基类对象的指针:

vector<Base*> 

通过存储指向基类的指针,就不会进行切片,并且您也可以实现所需的多态行为。 由于您要求C++ish 这样做,正确的方法是使用合适的 Smart pointer 而不是在向量中存储原始指针。这将确保您不必手动管理内存,RAII 会自动为您完成。

【讨论】:

使用Vector&lt;unique_ptr&lt;Base&gt;&gt; 代替Vector&lt;Base*&gt;。然后用vec.emplace_back(new Derived())代替vec.push_back @ShitalShah 如果我想在这个新的 Derived 中分配一些东西,然后再将它存储在向量中?现在我有这样的东西: void Add(const CThing & ins) things.push_back(ins); 【参考方案2】:

您正在体验切片。向量复制derived 对象,插入Base 类型的新对象。

【讨论】:

【参考方案3】:

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;
;

【讨论】:

我无法让它工作。您能否扩展此示例以使其正常工作(C++ 11 和经典版本)。不幸的是,我无法发布我到目前为止所做的事情。 @Zingam:没有它就很难触及问题的核心;我建议您发布一个新问题,如果您没有得到有用的答案,请不要犹豫,回答此评论并将我指向您的问题。 C++ 11 版本将构造函数声明为公共而不是私有有什么原因吗?删除它们是公开的还是私有的有关系吗? @Zingam:被删除方法的可访问性根本不重要,因此主要是将所有特殊方法组合在一起而不是将它们分开。【参考方案4】:

我会使用vector&lt;Base*&gt; 来存储它们。如果你说vector&lt;Base&gt;,就会发生切片。

这确实意味着在从向量中删除指针后,您必须自己删除实际对象,否则应该没问题。

【讨论】:

【参考方案5】:
// 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;

【讨论】:

以上是关于将派生类对象存储在基类变量中的主要内容,如果未能解决你的问题,请参考以下文章

在基类中存储指向派生类函数的指针

在基类对象内部创建派生类对象

在基类类型的向量中保存的基对象和派生对象被切片

在基类中创建派生类的对象

我应该在哪里存储对象列表?

当我在 C++ 中派生一个类时,它是不是会创建一个基类对象并将其作为我的成员变量存储在派生类中?