Я нахожу довольно странным, что неиспользуемые виртуальные функции все еще должны быть определены, в отличие от неиспользуемых обычных функций. Я немного понимаю неявные vtables и vpointers, которые создаются при создании объекта класса - это в некоторой степени отвечает на вопрос (что функция должна быть определена так, чтобы указатели на можно определить виртуальную функцию), но это отодвигает мой запрос еще дальше.
Зачем нужно создавать запись vtable для функции, если нет абсолютно никакой вероятности, что виртуальная функция вообще будет вызвана?
class A{
virtual bool test() const;
};
int main(){
A a; //error: undefined reference to 'vtable for A'
}
Несмотря на то, что я объявил A::test(), он никогда не использовался в программе, но все равно выдает ошибку. Может ли компилятор не запустить программу и понять, что test() никогда не вызывался, и, следовательно, не требовать для него записи vtable? Или это неразумно ожидать от компилятора?
Ответ 1 · рейтинг 6 · Edward Strange
Потому что разработчику компилятора неизбежно будет очень трудно решить проблему, когда полезность возможности оставлять виртуальные функции неопределенными в лучшем случае сомнительна. У авторов компиляторов наверняка есть задачи получше.
Кроме того, вы используете эту функцию, даже если вы ее не вызываете. Вы берете его адрес.
Ответ 2 · рейтинг 3 · Mike Nakis
ОП говорит, что он уже знает о vtables и vpointers, поэтому он понимает, что есть разница между неиспользуемыми виртуальными функциями и неиспользуемыми невиртуальными функциями: на неиспользуемую невиртуальную функцию нигде не ссылаются, а на виртуальную функцию ссылаются как минимум один раз в vtable своего класса. Итак, по сути, вопрос заключается в том, почему компилятору не хватает ума воздержаться от размещения ссылки на виртуальную функцию в vtable, если эта функция нигде не используется. Это позволило бы функции также стать неопределенной.
Компилятор обычно видит только один файл .cpp за раз, поэтому он не знает, есть ли у вас где-то исходный файл, который вызывает эту функцию.
Некоторые инструменты поддерживают такой анализ, они называют его "глобальным" анализом или чем-то подобным. Вы даже можете найти его встроенным в некоторые компиляторы и доступным через некоторые опции компилятора. Но он никогда не включается по умолчанию, потому что это чрезвычайно замедлит компиляцию.
На самом деле, причина, по которой вы можете оставить невиртуальную функцию неопределенной, также связана с отсутствием глобального анализа, но по-другому: если бы компилятор знал, что вы пропустили определение функции, он, вероятно, хотя бы предупредить. Но так как он не делает глобальный анализ, он не может. Об этом свидетельствует тот факт, что если вы делаете попытку использовать неопределенную функцию, ошибка не будет обнаружена компилятором: она будет обнаружена компоновщиком.
Итак, просто определите пустую виртуальную функцию, содержащую ASSERT(FALSE), и продолжайте свою жизнь.
Ответ 3 · Vincent Fourmond
Весь смысл виртуальных функций в том, что их можно вызывать через указатель базового класса. Если вы никогда не использовали виртуальную функцию базового класса, то зачем вы ее определили? Если он используется, вам нужно либо оставить родительскую реализацию (если она не чисто виртуальная), либо определить свою собственную реализацию, чтобы код, использующий ваши объекты через базовый класс, действительно мог его использовать. В этом случае функция используется, просто не используется напрямую.