从child
base
class base
{
public:
virtual int get() const { return 0;}
~virtual base(){}
};
class child : public base
{
public:
virtual int get() const override { return 1; }
};
在添加到容器时被切片,例如
std::vector<base> bases;
bases.push_back(child());
,下面的代码返回0
bases.at(0).get();
即使push_back的签名是:
void push_back( const T& value );
void push_back( T && value );
不应该招致要切片的对象。
例如,下面的代码会导致bad_cast
dynamic_cast<child &>(bases.at(0))
而与基地的孩子一起打电话会表现出多态行为
void toto(base const & e)
{
std::cout << e.get() << std::endl;
}
暗示对象不再是孩子;切片在哪里?究竟发生了什么?
是否可以设置base
或child
以便放置在容器中并且对象不会失去其多态性?
这是一个基本原理问题,并且已经知道一个对象被复制了,所以请不要建议指针/ shared_ptr,因为这已在其他地方得到解决。
答案 0 :(得分:6)
切片在哪里?
您
std::vector<base> bases;
存储普通base
个对象。您无法在其中添加任何其他内容,只能添加base
个对象。因此,如果你在那里添加了一个对象,那么它将是一个base
对象。如果您将其他类型传递给push_back
,则会将其转换为base
(如果可能);在你的情况下,这意味着切片。我不认为您对特定实现的std::vector
函数内部切片的确切位置感兴趣。
是否可以设置
base
或child
以应对此问题 放在容器中,物体不会丢失它 多态性?
为了保持多态性,您需要存储指向base
s 1 的指针。除了容器之外,还有很多方法可以复制它。最简单的方法是编写类似
child c;
...
std::vector<base*> v;
v.push_back(new child(c)); // and yes, smart pointers are better
您当然可以为push_back
添加包装,以避免每次都输入new
。
1您无法在向量中存储引用,但请参阅this question以获取更多选项。
答案 1 :(得分:3)
即使push_back的签名是:
不应该招致要切片的对象。
这是事实。
例如,下面的代码会导致bad_cast
dynamic_cast<child &>(bases.at(0))
是的,确实如此。并且dynamic_cast
不在std::
。
而与基地的孩子一起打电话会表现出多态行为
void toto(base const & e) { std::cout << e.get() << std::endl; }
是的,确实如此。
切片在哪里?究竟发生了什么?
问题本身不是push_back
。 切片在push_back
分配传递给内部对象的内容时发生,不是多态的,而不是在参数本身被绑定时。
要保持多态行为,必须使用指针(最好是智能指针)或引用;但是,普通引用不能存储在容器中,您必须使用它们的包装器,如std::reference_wrapper
。
There is a very comprehensive (and imho underrated) answer关于多态性为何如此运作的原因。
答案 2 :(得分:2)
这不起作用:多态性使用VTables,它存储指向您类的方法的指针。问题:如果复制它,VTable指针将被设置为基类地址。
此外它无法工作,因为派生类可以作为基类更大,因此无法计算内存消耗。但是容器需要知道对象有多大。