Можно ли хранить объекты разных классов (производных одного абстрактного класса) в одном массиве или списке?

200
18 июня 2018, 19:30

Допустим имеется один абстрактный класс: Word - от него производим два класса: Noun и Verb. Тут сталкиваемся с вопросом: как хранить объекты этих классов? Создавать для каждого класса отдельный массив для храниения или есть вариант как их хранить в одном месте?

Answer 1

Обычно такое делают, храня в массиве либо ссылки (std::reference_wrapper<Word>, например) — но тогда надо куда-то складывать сами объекты, либо указатели (std::unique_ptr<Word>, в частности; Word * вряд ли имеет смысл).

Если хочется массив ссылок, два варианта, в принципе:

I. Положить рядом массив указателей, куда эти ссылки ссылаются.

std::vector<std::reference_wrapper<Word>> words;
std::vector<std::unique_ptr<Word>> library;

и тщательно вручную следить за соответствием массивов друг другу.

II. Либо сделать собственный боксинг:

template<typename T> struct Box: public std::unique_ptr<T> {
     using std::unique_ptr<T>::unique_ptr;
     operator T &() const { return **this; }
};
std::vector<Box<Word>> words;
words.push_back(std::make_unique<Noun>("мама"));

(Пример простой реализации, ни на что не претендующий.)

Ну вот самый простой пример:

$ cat word.cpp
#include <iostream>
#include <vector>
#include <memory>
struct Word {
    virtual int value() const = 0;
    virtual ~Word() = default;
};
struct Noun: Word { int value() const { return 42; }};
struct Verb: Word { int value() const { return 31416; }};
template<typename T> struct Box: std::unique_ptr<T> {
    using std::unique_ptr<T>::unique_ptr;
    operator T &() const { return **this; }
};
int main() {
    std::vector<Box<Word>> v;
    v.push_back(std::make_unique<Noun>());
    v.push_back(std::make_unique<Verb>());
    for(Word const &w: v) std::cout << w.value() << std::endl;
}

И если запустить, получится:

$ g++ word.cpp && ./a.exe
42
31416
Answer 2

Просто объекты хранить не стоит - сработает срезка.

Храните указатели (или интеллектуальные указатели) на абстрактный класс, и все будет отлично работать.

Answer 3

Для хранения иерархически связанных типов можно использовать массив указателей на базовый класс. А чтобы не заморачиваться с низкоуровневыми конструкциями, вполне подойдет std::vector, параметризованный типом умного указателя std::unique_ptr.

#include <memory>
#include <vector>
using namespace std;
struct Word {
    virtual ~Word() = 0;
};
Word::~Word() {};
struct Noun : Word { };
struct Verb : Word { };
int main()
{
    vector<unique_ptr<Word>> v;
    v.push_back(make_unique<Noun>());
    v.push_back(make_unique<Verb>());
}

Проверка компилируемости.

READ ALSO
Не создаётся QWidget без Qapplication

Не создаётся QWidget без Qapplication

Когда я скомпилировал мой qt проект , он показал такую ошибкуПочему ??

191
Что значит static class

Что значит static class

Что означает данная конструкция

255
std::future_error при пересоздании std::promise

std::future_error при пересоздании std::promise

Нужно синхронизировать два потока, пока один не запросит данные, другой их не отдает и ждет запроса на данные

230
Абстрактный класс. Наследование

Абстрактный класс. Наследование

Допустим, планируется какая-то система классов, например, устройствУ каждого устройства есть некоторые общие члены, например имя устройства,...

212