Хранить объекты производного класса в переменных базового класса

Я хотел бы сохранить экземпляры нескольких классов в векторе. Поскольку все классы наследуются от одного базового класса, это должно быть возможно.

представьте себе эту программу:

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

Я ожидал, что он напечатает "производный", потому что метод" identify " является виртуальным. Вместо этого "vect[0] "кажется" базовым " экземпляром, и он печатает

базовый

Я думаю, я мог бы написать свой собственный контейнер (вероятно, полученный из вектора) так или иначе, что способен это сделать (возможно, удерживая только указатели...). Я просто хотел спросить, есть ли более метод c++ish для этого. И я хотел бы быть полностью вектор-совместимым (просто для удобства, если другие пользователи когда-либо будут использовать мой код).

4 ответов


что вы видите, это Объект Нарезки.
Вы храните объект производного класса в векторе, который должен хранить объекты базового класса, это приводит к срезанию объекта, а определенные члены производного класса хранящегося объекта срезаются, таким образом, объект, хранящийся в векторе, просто действует как объект базового класса.

устранение:

вы должны хранить указатель на объект базового класса в вектор:

vector<Base*> 

при сохранении указателя на базовый класс не было бы нарезки, и вы также можете достичь желаемого полиморфного поведения.
Поскольку вы просите C++ish способ сделать это, правильный подход заключается в использовании подходящего смарт-указатель вместо хранения необработанного указателя в векторе. Это гарантирует, что вам не придется вручную управлять памятью,RAII сделает это за вас автоматически.


вы испытываете нарезки. Вектор копирует derived объект, новый тип это.


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>, произойдет нарезка.

Это означает, что вам придется удалить сам себя объекты после удаления указателей из вектора, но в противном случае вы должны быть хорошо.