Значит так, имеем:
1) GCC 4.1.3
2) Код, в котором куча классов с виртуальными функциями, но без виртуальных деструкторов (это чистые интерфейсы)
3) GCC выдает "warning: 'class T' has virtual functions but non-virtual destructor" при включении -Wall
Особенности:
1) Просто добавить виртуальные деструкторы — нельзя. Это by design. Нигде в том коде невозможно удалить объект такого класса через delete на базовый указатель.
2) В этом GCC нет специального ключа, отключающего этот warning (или он называется как-то нестандартно)
3) -Wall хотелось бы сохранить.
Есть ли еще какой-то способ подавить этот warning?
Спасибо.
Здравствуйте, wander, Вы писали:
W>Значит так, имеем: W>1) GCC 4.1.3 W>2) Код, в котором куча классов с виртуальными функциями, но без виртуальных деструкторов (это чистые интерфейсы) W>3) GCC выдает "warning: 'class T' has virtual functions but non-virtual destructor" при включении -Wall
W>Особенности: W>1) Просто добавить виртуальные деструкторы — нельзя. Это by design. Нигде в том коде невозможно удалить объект такого класса через delete на базовый указатель.
Интересно знать, в чём смысл такого дизайна? И почему нельзя просто добавить.
Это автомагически добавит ранее отсутствующую таблицу виртуальных функций на которой хотели сэкономить?
Как у микрософта, __declspec(novtable)?
W>Есть ли еще какой-то способ подавить этот warning?
Через прагму запретить все варнинги для данной одной конкретной строчки (где он возникает) ?
Только боюсь не выйдет, т.к. ранние версии GCC не умели _Pragma("GCC diagnostic ignore \"что-надо"").
И через #pragma не умели.
Здравствуйте, fk0, Вы писали:
fk0> Интересно знать, в чём смысл такого дизайна? И почему нельзя просто добавить.
Этот код — часть большого plugin-based проекта.
Эти интерфейсы — это интерфейсы для написания плагинов с их использованием. Плагины могут быть написаны с
использованием разных компиляторов и даже разных языков (порядок работы чем-то напоминает COM).
Добавляемый в такой интерфейс виртуальный деструктор вынужден быть inline, а это при такой организации кода
недопустимо (я думаю вы и сами уже догадались почему).
Для разрушения объектов предусмотрена специальная функция-деструктор, которая гарантированно уничтожит объект
там же, где он создавался.
fk0> Через прагму запретить все варнинги для данной одной конкретной строчки (где он возникает) ? fk0>Только боюсь не выйдет, т.к. ранние версии GCC не умели _Pragma("GCC diagnostic ignore \"что-надо""). fk0>И через #pragma не умели.
Здравствуйте, wander, Вы писали:
fk0>> Интересно знать, в чём смысл такого дизайна? И почему нельзя просто добавить. W>Этот код — часть большого plugin-based проекта.
W>Эти интерфейсы — это интерфейсы для написания плагинов с их использованием. Плагины могут быть написаны с W>использованием разных компиляторов и даже разных языков (порядок работы чем-то напоминает COM). W>Добавляемый в такой интерфейс виртуальный деструктор вынужден быть inline,
А вот с этого момента поподробнее. Кто его вынуждает быть inline и каким способом???
В C++ такого не предусмотрено, чтоб кого-то вынуждать.
W>Для разрушения объектов предусмотрена специальная функция-деструктор, которая гарантированно уничтожит объект W>там же, где он создавался.
В каком смысле "там же"? И чем обычный деструктор кардинально отличается от спец. функции?
Мне кажется с точки зрения компилятора -- ни чем. В конечном счёте функция как функция. Когда
уже до машинного кода дошло.
Мне кажется, здесь речь о каком-то архитектурном паттерне, который нарушает стандарт в каких-то
частях, но пытаться что-то изменить уже поздно. Но я никак не понимаю, что за паттерн.
Здравствуйте, fk0, Вы писали:
fk0> А вот с этого момента поподробнее. Кто его вынуждает быть inline и каким способом??? fk0>В C++ такого не предусмотрено, чтоб кого-то вынуждать.
В C++ нет интерфейсов. Интерфейсы в C++ — это классы с чистовиртуальными функциями.
Но деструктор отличается от остальных функций тем, что у него, даже если он чистовиртуальный, должна быть реализация.
Если нужен виртуальный деструктор в интерфейсном хедере, то есть два пути:
1) либо мы предоставляем реализацию деструктора в inline прямо в хедере,
2) либо обязать тех, кто реализует интерфейс, создавать эту реализацию в своей какой-то единице трансляции, но это неудобно.
Скажем так, из соображений удобства использования, деструктор, если он виртуальный, вынужден быть inline.
А выход простой из этого. Виртуальные деструкторы вообще не нужны. Мы все равно благодаря Release не удаляем через явный delete ничего.
А Release сама по себе виртуальная достаточно, чтобы знать фактический тип наследника и правильно его уничтожить.
fk0> В каком смысле "там же"? И чем обычный деструктор кардинально отличается от спец. функции? fk0>Мне кажется с точки зрения компилятора -- ни чем. В конечном счёте функция как функция. Когда fk0>уже до машинного кода дошло.
В том же модуле, с использованием того же аллокатора и т.д. Удалять в одном модуле через delete объект, который создал другой модуль (возможно на другом языке) — это неправильно.
А вот виртуальная функция Release, которая имплементирована в другом модуле — другое дело. При этом ее вызов никак не привязан к внутреннему
ABI модуля (в отличие от вызова delete, которому нужно ABI конкретной реализации C++).
_____
UPD. Добавлю, что если мы делаем "заголовочный файл" интерфейса для другого языка (отличного от C++), то деструктор там выглядит чужеродно.
А коль скоро он не нужен для работы, то и писать его и прокидывать в слои совместимости с другими языками, не нужно.
Ну вот посмотри на интерфейсы COM. Там тоже нет виртуальных деструкторов. Мотивация точно такая же.
fk0> Но я никак не понимаю, что за паттерн.
А я не пойму к чему эти вопросы?
Хочешь ревью этой системы провести? Или что?
Здравствуйте, wander, Вы писали:
W>Здравствуйте, fk0, Вы писали:
fk0>> А вот с этого момента поподробнее. Кто его вынуждает быть inline и каким способом??? fk0>>В C++ такого не предусмотрено, чтоб кого-то вынуждать.
W>В C++ нет интерфейсов. Интерфейсы в C++ — это классы с чистовиртуальными функциями. W>Но деструктор отличается от остальных функций тем, что у него, даже если он чистовиртуальный, должна быть реализация.
W>Если нужен виртуальный деструктор в интерфейсном хедере, то есть два пути: W>1) либо мы предоставляем реализацию деструктора в inline прямо в хедере, W>2) либо обязать тех, кто реализует интерфейс, создавать эту реализацию в своей какой-то единице трансляции, но это неудобно. W>Скажем так, из соображений удобства использования, деструктор, если он виртуальный, вынужден быть inline.
Надуманно. На самом деле 3) оставляем на совести того, кто пишет код.
Делать или не делать inline -- никто не заставляет.
W>А выход простой из этого. Виртуальные деструкторы вообще не нужны. Мы все равно благодаря Release не удаляем через явный delete ничего. W>А Release сама по себе виртуальная достаточно, чтобы знать фактический тип наследника и правильно его уничтожить.
Подразумевается, вызов instance->Release() в том числе и освобождает память?
Ну отлично. Виртуальный деструктор в данном случае действительно не нужен, но его
присутствие никому не мешает же, чтоб прямо осознанно бороться с ним. Проще его оставить,
потому, что в той системе программировния откуда он появляется, его отсутствие скорей
странно и является потенциальным источником ошибок.
В конце концов, ведь в C++-коде, не выходя за рамки C++ и без COM-подобных интерфейсов
можно просто по-тихому взять и унаследовать класс. И начать пользоваться каким-то
странным образом и вне рамок изначально запланированного сценария. Конечно можно сказать,
мол не стоит так делать, но с другой стороны явно никто же не запрещает и получиться
так может случайно и очень неочевидным образом. В том и смысл навязываемых в языке
C++ ограничений, чтоб даже в таких случаях сохранить работоспособность в противовес
утечкам ресурсов, падениям с невообразимыми бэктрейсами и т.п.
W>А коль скоро он не нужен для работы, то и писать его и прокидывать в слои совместимости с другими языками, не нужно. W>Ну вот посмотри на интерфейсы COM. Там тоже нет виртуальных деструкторов. Мотивация точно такая же.
Там управление памяться делегировано самому объекту, который не копируемый и не перемещаемый, потому и так.
fk0>> Но я никак не понимаю, что за паттерн. W>А я не пойму к чему эти вопросы? W>Хочешь ревью этой системы провести? Или что?
Я хочу сказать, борьба с виртуальным деструктором -- странное дело.
Здравствуйте, fk0, Вы писали:
fk0> Надуманно. На самом деле 3) оставляем на совести того, кто пишет код.
Нет. Тут не все так просто.
fk0> Подразумевается, вызов instance->Release() в том числе и освобождает память?
Конечно. Тут довольно сложно сделать иначе, учитывая все вводные.
Когда я в первом ответе упомянул COM, это было не просто так.
fk0> Там управление памяться делегировано самому объекту, который не копируемый и не перемещаемый, потому и так.
Тут тоже так.
fk0> Я хочу сказать, борьба с виртуальным деструктором -- странное дело.
Нет. Тут есть подводные камни. На самом деле я даже мог бы это доказать, но это приведет к написанию статьи.
Слишком много времени нужно будет потратить, а профита от этого для меня нет.
Может быть чуть позже найду время и напишу, но явно не прямо сейчас.