Страницы

Поиск по вопросам

вторник, 28 января 2020 г.

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

#cpp #наследование


Товарищи, подскажите, пожалуйста, одну вещь.

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

SuperClass ptrSuper[100];
for (int i = 0; i < 100; i++)
{
    if (i / 2)
        ptrSuper[i] = new Naslednik1(....);
    else
        ptrSuper[i] = new Naslednik2(....);
};


Как здесь можно поступить?
    


Ответы

Ответ 1



Это не ответ на ваш вопрос, т.к на него уже ответили, но просто некоторая информация для размышления. Если бы вариант вашего кода выглядел следующим образом, то эта запись также возможна, однако, в этом случае будет происходит нежелательное явление под названием срезка / slicing : SuperClass ptrSuper[100]; for (int i = 0; i < 100; i++) { if (i % 2) ptrSuper[i] = Naslednik1(....); else ptrSuper[i] = Naslednik2(....); }; Суть явления срезки заключается в том, что на деле вместо пересвязывания указателей (а это то, чего мы хотели на самом деле) происходит неочевидное полное копирование объектов, причем в данном случае копирование только части SuperClass. Т.е во всех созданных объектах лишняя информация, которая составляет "суть" классов Naslednik1, Naslednik2, будет безвозвратно утеряна. Другие примеры срезки можно изучить здесь.

Ответ 2



Смотрите, как я понял у вас есть иерарархия классов. Например SuperClass и от него наследуется два класса Derived1 и Derived2: class SuperClass { ... }; class Derived1 : public SuperClass { }; class Derived2 : public SuperClass { }; и вам необходим некий контейнер, в котором вы хотите хранить объекты этих классов. Скажу сразу тут желательно использовать указатели... Почему? Есть замечательная книжка Скотта Мейерса, там как раз очень хорошо объясняется в одном из правил, что прозойдёт "срезка", впрочем http://hashcode.ru/users/751/mikillskegg уже писал про это тут. дак вот, я бы хранил эти объекты так std::vector< *SuperClass > OurContainer; OurContainer.push_back( new Derived1 ); OurContainer.push_back( new Derived2 ); ... в конце не забываем удалять объекты их кучи, "пробагемся" по вектору с помощью итератора и удаляем каждый указатель.

Ответ 3



if (i%2) ptrSuper[i] = new Naslednik1(....); else ptrSuper[i] = new Naslednik2(....); в элементы с четными индексами (0,2 ... 98) попадут Naslednik1, а с нечетными (1 ... 99) Naslednik2 Это, если вы с описанием наследования ничего не напутали.

Ответ 4



Если указателю на базовый класс присвоить адрес производного класса, то не будет доступа к методам и переменным производного класса, которых нет в классе базовом. Так что лучше сделать два массива указателей на каждый производный класс, и спокойненько их заполнить с помощью new.

Ответ 5



Два момента: Используйте std::vector вместо стандартного массива Используйте std::shared_ptr вместо сырого указателя Используйте фабричный метод, вместо размещения логики создания экземпляра нужного типа в месте помещения созданного экземпляра в вектор Вот пример кода: // Может быть пока и не нужно, но значительно проще, когда // за процесс создания экземпляра отвечает отдельный метод. // Так проще об этом думать, и развивать std::shared_ptr CreateSuperClass(int index) { return index%2 ? std::make_shared(new Derived1) : std::make_shared(new Derived2); } std::vector> mySuperclasses; for(int n = 0; n < size; n++) { mySuperClasses.push_back(CreateSuperClass(n)); } Преимущества: Не нужно задумываться о процессе освобождения элементов вектора благодаря std::shared_ptr Процесс создания вынесен в отдельный метод, что упрощает чтение и позволяет изменять процесс создания не трогая дополнительные участки кода

Комментариев нет:

Отправить комментарий