70

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

想象一下这个程序:

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

我希望它打印“派生”,因为“识别”方法是虚拟的。相反,“vect [0]”似乎是一个“基础”实例,它会打印

根据

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

4

5 回答 5

83

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

解决方案:

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

vector<Base*> 

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

于 2012-01-08T13:02:55.770 回答
7

你正在经历切片。向量复制derived对象,插入一个新的类型Base

于 2012-01-08T13:03:45.077 回答
6

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;
};
于 2013-08-21T07:40:36.807 回答
3

我会vector<Base*>用来存放它们。如果你说vector<Base>,就会发生切片。

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

于 2012-01-08T13:03:14.610 回答
1
// 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;
}
于 2019-08-04T17:01:15.867 回答