Хранить производные объекты класса в переменных базового класса
Я хотел бы хранить экземпляры нескольких классов в векторе. Поскольку все классы наследуются от одного и того же базового класса, это должно быть возможно.
Представьте себе эту программу:
#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", и он печатает
БАЗА
Думаю, я мог бы написать свой собственный контейнер (возможно, полученный из вектора) каким-то образом, который способен это сделать (возможно, только указатели...). Я просто хотел спросить, есть ли для этого метод С++. И я хотел бы быть полностью векторно-совместимым (просто для удобства, если другие пользователи должны когда-либо использовать мой код).
Ответы
Ответ 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>
, произойдет нарезка.
Это означает, что вам нужно будет удалить фактические объекты самостоятельно после удаления указателей с вашего вектора, но в противном случае вы должны быть в порядке.