Context
Моя цель - создать класс базового контейнера, который содержит и управляет несколькими объектами базового класса, а затем класс производного контейнера, который содержит и управляет несколькими объектами производного класса.По совету этого ответа я попытался сделать это, каждый из которых содержит массив указателей (Base**
и Derived**
) и приведен от Derived**
к Base**
при инициализациибазовый контейнерный класс.
Однако я столкнулся с проблемой - несмотря на то, что при компиляции все нормально, при манипулировании содержащимися объектами у меня возникли бы ошибки segfaults или были бы вызваны неправильные методы .
Проблема
Я свел проблему к следующему минимальному случаю:
#include <iostream>
class Base1 {
public:
virtual void doThing1() {std::cout << "Called Base1::doThing1" << std::endl;}
};
class Base2 {
public:
virtual void doThing2() {std::cout << "Called Base2::doThing2" << std::endl;}
};
// Whether this inherits "virtual public" or just "public" makes no difference.
class Derived : virtual public Base1, virtual public Base2 {};
int main() {
Derived derived;
Derived* derivedPtrs[] = {&derived};
((Base2**) derivedPtrs)[0]->doThing2();
}
Вы можете ожидать, что это выведет "Called Base2::doThing2
", но…
$ g++ -Wall -Werror main.cpp -o test && ./test
Called Base1::doThing1
В самом деле - код вызывает Base2::doThing2
, но Base1::doThing1
в итоге вызывает .У меня также был этот segfault с более сложными классами, так что я предполагаю, что он связан с адресами (возможно, с vtable - ошибка, похоже, не возникает без virtual
методов).Вы можете запустить его здесь и увидеть сборку, к которой он компилируется, здесь .
Вы можете увидеть мою фактическую структуру здесь - она более сложная, но он связывает это с контекстом и объясняет, почему мне нужно что-то вроде этого.
Почему приведение Derived**
→ Base**
не работает должным образом, когда Derived*
→ Base*
делает и, что более важно, как правильно обрабатывать массив производных объектов как массив базовых объектов (или, если это не так, еще один способ создать класс контейнера, который может содержатьнесколько производных объектов)?
Я не могу индексировать перед апкастингом (((Base2*) derivedPtrs[0])->doThing2()
), я боюсь, поскольку в полном коде этот массив является членом класса - и я не уверен, что это хорошоИдея (или даже возможная) для приведения вручную в каждом месте содержащиеся объекты используются в контейнерах классов.Поправьте меня, если это способ справиться с этим, хотя.
(Я не думаю, что это имеет значение в этом случае, но я нахожусь в среде, где std::vector
недоступен.)
Редактировать: Решение
Во многих ответах предполагается, что приведение каждого указателя по отдельности - единственный способ получить массив, который может содержать производныеобъекты - и это действительно так.Однако для моего конкретного случая использования мне удалось решить проблему с помощью templates !Предоставляя параметр типа для того, что должен содержать контейнерный класс, вместо того, чтобы содержать массив производных объектов, тип массива может быть установлен на производный тип во время компиляции (например, BaseContainer<Derived> container(length, arrayOfDerivedPtrs);
).
Ниже приведена версия неработающего кода "фактической структуры", исправленного с помощью шаблонов.