[英]Create a vector of base class objects and store derived class objects within
[英]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.