У меня есть какая-то фабрика объектов (на основе шаблонов), которая довольно хорошо работает для моих целей. Но теперь я попытался поработать с классом, который является производным от QObject и чистого абстрактного класса (интерфейса), и у меня возникли странные ошибки во время выполнения.
Вот простая картина этого класса (Производная)
class Interface {
public:
Interface(){}
virtual ~Interface(){}
virtual int getResult() = 0;
};
class Derived : public QObject, public Interface {
Q_OBJECT
public:
explicit Derived(QObject *parent = 0);
int getResult();
};
и его реализация в производном.cpp:
#include "derived.h"Derived::Derived(QObject *parent)
: QObject(parent) {
}
int Derived::getResult() {
return 55;
}
Когда я пытаюсь привести пустой указатель к интерфейсу, я получаю неожиданное (для меня) поведение, это может быть либо ошибка времени выполнения, либо другой вызов метода (это зависит от размера классов).
#include "derived.h"void * create() {
return new Derived();
}
int main(int argc, char *argv[]) {
Interface * interface = reinterpret_cast<Interface *>(create());
int res = interface->getResult(); // Run-time error, or other method is called here
return 0;
}
Не могли бы вы объяснить, почему я не могу привести пустой указатель на интерфейс? И есть ли обходной путь?
Спасибо за ваши ответы
Переинтерпретация указателя на производный класс как указателя на базовый класс дает неопределенное поведение. Это подчеркивается множественным наследованием: поскольку существует более одного базового класса, а два базовых подобъекта должны иметь разные адреса, они не могут и то и другое иметь тот же адрес, что и производный объект. Таким образом, указатель возвращается вашим create
функция указывает на Derived
, но не обязательно Interface
субобъект. Это может указывать на QObject
подобъект или ни к одному.
Лучший вариант — вернуться Derived*
или же Interface*
от вашей функции; если это должно быть void*
по какой-то причине, тогда единственный четко определенный состав, который вы можете сделать, это вернуться к Derived*
, который затем может быть преобразован в Interface*
используя стандартные преобразования.
Используя void*
вы выбросили все статические и динамические знания о типе; единственный способ восстановить эту информацию — вернуть ее к правильному типу.
смешивание reinterpret_cast
с множественным наследованием, вероятно, вызовет всевозможные проблемы. В вашем случае void*
вероятно, будет указывать на QObject
часть Derived
, не Interface
часть.
Это, вероятно, будет работать лучше с dynamic_cast
, который может настроить указатель так, чтобы он указывал на правильный подобъект производного класса.
Обратите внимание, что в целом необходимость в этих типах приведений является признаком плохого дизайна. В вашем конкретном случае ваша заводская функция должна возвращать Interface*
, void*
в C ++, где нет взаимодействия с C, это просто ненужно и плохо.
Помимо всего этого, фабричные функции являются чем-то вроде Java, и требуются только при применении строгой методологии проектирования, которая часто добавляет больше пуха, чем способствуя чистому и безошибочному коду.
Если это не сработает, вы захотите dynamic_cast
. В Qt для QObjects вы можете увидеть, qobject_cast
имеет какие-либо достоинства в вашей ситуации.