将派生类对象存储在基类变量中
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
的基类,然后让你的Base
和Derived
都继承自该基类会发生什么。
@Mr Lister 感谢您的评论,但这不会改变结果。
另见this。
【参考方案1】:
您看到的是 Object Slicing。 您将派生类的对象存储在一个向量中,该向量应该存储基类的对象,这会导致对象切片,并且正在存储的对象的派生类特定成员被切掉,因此存储在向量中的对象只是充当基类的对象。
解决方法:
你应该在向量中存储指向基类对象的指针:
vector<Base*>
通过存储指向基类的指针,就不会进行切片,并且您也可以实现所需的多态行为。
由于您要求C++ish
这样做,正确的方法是使用合适的 Smart pointer 而不是在向量中存储原始指针。这将确保您不必手动管理内存,RAII 会自动为您完成。
【讨论】:
使用Vector<unique_ptr<Base>>
代替Vector<Base*>
。然后用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<Base*>
来存储它们。如果你说vector<Base>
,就会发生切片。
这确实意味着在从向量中删除指针后,您必须自己删除实际对象,否则应该没问题。
【讨论】:
【参考方案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;
【讨论】:
以上是关于将派生类对象存储在基类变量中的主要内容,如果未能解决你的问题,请参考以下文章