Во-первых, этот код не компилируется, поскольку в C ++ void *
не может быть неявно преобразован в A *
.Требуется явное приведение.
Во-вторых, пример с malloc
совершенно не имеет значения.malloc
выделяет необработанную память, причем абсолютно не имеет отношения к каким-либо конкретным типам.В этом случае malloc
знает примечание о любом A
и не создает объект типа A
.
По этим причинам реальный пример для этого вопроса должен выглядеть следующим образом
struct A
{
virtual void foo(); // unused and unimplemented
virtual void bar () {}
};
int main ()
{
A obj; // ok
A* pn = new A; // linker error
}
И вопрос в том, почему первое объявление не выдает ошибку, в отличие от второго.
С формальной точки зрения ваша программа недействительна, поскольку она нарушает формальные требования языка C ++ (в частности, ODR).На практике оба объявления могут или должны вызывать одну и ту же ошибку, поскольку в обоих случаях объект формально требует указатель на VMT.В этом случае VMT не может быть создан, так как некоторые функции не определены.Однако первое объявление просто проскальзывает только потому, что компилятор смог оптимизировать все ссылки на VMT для первого объявления (а не для второго).Также вполне возможно, что компилятор смог оптимизировать весь объект obj
, поскольку на него больше не ссылаются.
В GCC (поскольку вы, похоже, используете GCC), легковызовите ту же ошибку и для первого объявления
struct A
{
virtual void foo(); // unused and unimplemented
virtual void bar () {}
};
int main ()
{
A obj; // linker error
A *p = &obj;
p->bar();
}
Приведенный выше код вызовет ту же ошибку компоновщика в GCC, хотя неопределенная функция foo
все еще не используется в этом коде.
Другими словами, это просто вопрос добавления достаточного количества кода, чтобы заставить компилятор поверить, что объект VMT необходим.В этом случае разница в поведении между объявлениями не имеет ничего общего с языком C ++.Это просто проблема реализации, специфичная для вашего компилятора.