Гиперссылка — Запутался о не чистых виртуальных классах в C ++, для чего они?

У меня есть класс, объявленный следующим образом:

class TestFoo {
public:
TestFoo();
virtual void virtualFunction();
void nonVirtualFunction();
};

что я пытаюсь реализовать таким образом

TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}

который при компиляции возвращает ошибку:

undefined reference to vtable for TestFoo

Я старался :

TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}
void TestFoo::virtualFunction(){}

который компилируется нормально, что соответствует ответам на эти посты:

Неопределенная ссылка на vtable

неопределенная ссылка на vtable

Меня смущает то, что я думал, что весь смысл объявления виртуальной функции заключается в том, что мне не нужно ее определять. В этом примере я не планирую создавать какой-либо экземпляр TestFoo, но создаю экземпляры (конкретных) классов, наследуемых от TestFoo. Но все же я хочу определить функцию nonVirtualFunction для каждого подкласса TestFoo.

Что-то я не правильно понял?

Спасибо !

1

Решение

Ваше описание идеально соответствует случаю абстрактного класса. Объявите вашу виртуальную функцию как:

    virtual void VirtualFunction () = 0;

Это означает, что вы не реализуете функцию в этом классе. В результате класс становится абстрактным. То есть никакие голые объекты этого класса не могут быть созданы.

Кроме того, вы должны предоставить виртуальный деструктор.

Обновить: Некоторые уточнения …

Язык позволяет переопределить не виртуальную функцию. Хотя в некоторых случаях может быть вызвана неправильная версия:

derived D;    // rB is a reference to base class but it
base & rB=D;  // points to an object of the derived class

rB.NonVirtualFunction ();  // The base-class version is called

По этой причине переопределение не виртуальной функции в настоящее время настоятельно не рекомендуется. См. Скотт Мейерс «Эффективное C ++, третье издание: 55 конкретных способов улучшить ваши программы и разработки», пункт 36: «Никогда не переопределяйте унаследованную не виртуальную функцию».

См. Также пункт 7: «Объявление виртуальных деструкторов в полиморфных базовых классах». Пример:

base * pB = new derived;
delete pB;                // If base's destructor is not virtual,
// ~derived() will not be called.

Если вам интересно, почему по умолчанию не все виртуально, причина в том, что вызов виртуальной функции немного медленнее, чем вызов не виртуальной. Да, и объекты классов с виртуальными функциями занимают еще несколько байтов каждый.

1

Другие решения

весь смысл объявления виртуальной функции заключается в том, что я бы не стал
нужно определить это

Не совсем, он говорит: «Я могу заменить реализацию этой функции чем-то другим в производном классе».

Возможно, я неправильно понял ваш вопрос, но вы, кажется, подразумеваете, что не думаете, что можете определить чисто виртуальную функцию-член в C ++, что вы можете. Вы можете объявлять один следующим образом.

virtual void virtualFunction() = 0;

Обычно чисто виртуальная функция не будет определенный, но конечно можно. Это говорит: «Нет реализации по умолчанию для этой функции, потому что это не всегда имеет смысл, но я предоставлю вам реализацию, на которую вы можете выбрать».

Кстати, если класс имеет любой виртуальные функции, вы также должны определить виртуальный деструктор, поскольку вполне законно (и часто рекомендуется) иметь базовый класс (умный) указатель на производный класс — без виртуального деструктора объект может не быть deleteбуду правильно.

7

… Я думал, что весь смысл объявления
Виртуальная функция заключается в том, что мне не нужно было бы ее определять …

Для этого объекта у вас есть функция под названием чисто виртуальный методы:

virtual void virtualFunction() = 0;  // no linking error now

Обратите внимание, что virtual метод не может оставаться нереализованным. Причина в том, что для каждого virtual метод объявленный внутри class Тело должно быть vtable запись. Невозможность найти его тело приводит к ошибке компоновки.

Цель этого ограничения:
Если класс не является абстрактным — то есть он имеет хотя бы одну виртуальную функцию — вы не сможете гарантировать компилятору, что вы не собираетесь объявлять объект TestFoo, Что происходит, когда вы делаете следующее:

DerivedOfTestFoo obj1;
TestFoo obj2 = obj1, *p = &obj2; // object slicing
p->virtualFunction(); // where is the body?

Другая ситуация; в конструкторе нет virtual механизм:

TestFoo::TestFoo () {
this->virtualFunction(); // where is the body?
}

Мы можем заключить, что компиляторы следуют правилу: «Лучше быть в безопасности, чем потом сожалеть». 🙂

3

Если вы хотите сделать эту виртуальную функцию чистой виртуальной функцией, то не хотите ее определять, virtual void virtualFunction () = 0;

0
По вопросам рекламы [email protected]