Я уверен, что это опасный код. Тем не менее, я хотел проверить, есть ли у кого-нибудь идея какие точно пойдет не так.
Предположим, у меня есть структура этого класса:
class A {
protected:
int a;
public:
A() { a = 0; }
int getA() { return a; }
void setA(int v) { a = v; }
};
class B: public A {
protected:
int b;
public:
B() { b = 0; }
};
И затем предположим, что я хочу иметь способ автоматического расширения класса следующим образом:
class Base {
public:
virtual ~Base() {}
};
template <typename T>
class Test: public T, public Base {};
Один действительно важная гарантия того, что я не могу дать Base
ни Test
будет иметь любые другие переменные-члены или методы. Это по сути пустые классы.
(Потенциально) опасный код ниже:
int main() {
B *b = new B();
// dangerous part?
// forcing Test<B> to point to to an address of type B
Test<B> *test = static_cast<Test<B> *>(b);
//
A *a = dynamic_cast<A *>(test);
a->setA(10);
std::cout << "result: " << a->getA() << std::endl;
}
Обоснование для того, чтобы сделать что-то вроде этого, заключается в том, что я использую класс, похожий на Test, но для того, чтобы он работал в настоящее время, обязательно нужно создать новый экземпляр T (т.е. Test), а также скопировать переданный экземпляр. Было бы здорово, если бы я мог просто указать Test на адрес памяти T.
Если бы Base не добавил виртуальный деструктор, а так как Test ничего не добавил, я бы подумал, что этот код на самом деле в порядке. Однако добавление виртуального деструктора заставляет меня беспокоиться, что информация о типе может быть добавлена в класс. Если это так, то это может привести к нарушениям доступа к памяти.
Наконец, я могу сказать, что этот код отлично работает на моем компьютере / компиляторе (clang), хотя это, конечно, не является гарантией того, что он не делает плохих вещей с памятью и / или не потерпит неудачу на другом компиляторе / машине.
Виртуальный деструктор Base::~Base
будет вызываться при удалении указателя. поскольку B
не имеет надлежащего vtable (вообще нет в коде, размещенном здесь), который не очень хорошо закончится.
Это работает только в этом случае, потому что у вас есть утечка памяти, вы никогда не удаляете test
,
Ваш код производит неопределенное поведение, так как он нарушает строгие псевдонимы. Даже если это не так, вы вызываете UB, поскольку ни B, ни A не являются полиморфными классами, а указанный объект не является полиморфным классом, поэтому dynamic_cast
не может преуспеть. Вы пытаетесь получить доступ к базовому объекту, который не существует, чтобы определить тип среды выполнения при использовании dynamic_cast
,
Одна действительно важная гарантия, которую я могу дать, заключается в том, что
ни Test не будет иметь никаких других переменных-членов или методов. Они есть
по существу пустые классы.
Это совсем не важно — это совершенно не имеет значения. Стандарт должен был бы поручить EBO, чтобы это даже начало иметь значение, а это не так.
Пока вы не выполняете никаких операций с Test<B>*
и избегайте любой магии, такой как умные указатели или автоматическое управление памятью, все будет в порядке.
Вы должны обязательно искать скрытый код, такой как отладочные отпечатки или протоколирование, которые будут проверять объект. У меня произошел сбой отладчиков за попытку просмотреть значение указателя, установленного следующим образом. Держу пари, это причинит вам некоторую боль, но вы должны быть в состоянии заставить это работать.
Я думаю, что настоящая проблема заключается в обслуживании. Сколько времени пройдет, прежде чем какой-нибудь разработчик сделает операцию над Test<B>*
?