Подтвердить что ты не робот

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

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

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

#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", потому что метод "ident" является виртуальным. Вместо этого 'vect [0]' кажется экземпляром "Base", и он печатает

БАЗА

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

4b9b3361

Ответ 1

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

Решение:

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

vector<Base*> 

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

Ответ 2

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

Ответ 3

TL; DR: вы не должны наследовать от общедоступного класса с возможностью копирования/перемещения.


Фактически возможно предотвратить срез объектов во время компиляции: базовый объект не должен копироваться в этом контексте.

Случай 1: абстрактная база

Если база абстрактна, то она не может быть создана и, следовательно, вы не сможете нарезать.

Случай 2: конкретное основание

Если база не абстрактна, ее можно скопировать (по умолчанию). У вас есть два варианта:

  • предотвратить копирование вообще
  • разрешить копирование только для детей

Примечание: в С++ 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;
};

Ответ 4

Я бы использовал vector<Base*> для их хранения. Если вы скажете vector<Base>, произойдет нарезка.

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