Хранить объекты производного класса в переменных базового класса
Я хотел бы сохранить экземпляры нескольких классов в векторе. Поскольку все классы наследуются от одного базового класса, это должно быть возможно.
представьте себе эту программу:
#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 сделает это за вас автоматически.
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>
, произойдет нарезка.
Это означает, что вам придется удалить сам себя объекты после удаления указателей из вектора, но в противном случае вы должны быть хорошо.