Как сделать так, чтобы код с невиртуальным деструктором в порожденном классе давал ошибку компиляции? Дело в том, что компилятор дает скомпилировать такой код
class A
{
public:
virtual ~A() {}
};
class B : public A
{
public:
~B() {}
};
где деструктор класса B также является виртуальным, несмотря на неуказание слова virtual. Как бы задать опции для современных компиляторов (g++ и MSVC++), чтобы такой код компилировался с ошибками, заставляя явно указывать virtual в определении деструкторов порожденных классов.
Re: "невиртуальный" деструктор порожденного класса
DS>Как бы задать опции для современных компиляторов (g++ и MSVC++), чтобы такой код компилировался с ошибками, заставляя явно указывать virtual в определении деструкторов порожденных классов.
Никак. Поведение, описанное в стандарте — причем, не только для деструкторов, но и для любых виртуальных функций вообще.
Да здравствует мыло душистое и веревка пушистая.
Re: "невиртуальный" деструктор порожденного класса
А зачем? Если дело в том, что Вы хотите быть уверенным в наличии виртуального деструктора у базового класса на этапе компиляции, то это можно реализовать и другими способами. Например, при помощи std::has_virtual_destructor и static_assert.
Re: "невиртуальный" деструктор порожденного класса
Здравствуйте, DmitryShm, Вы писали: DS>Как сделать так, чтобы код с невиртуальным деструктором в порожденном классе давал ошибку компиляции? Дело в том, что компилятор дает скомпилировать такой код
Пример
DS>
DS>class A
DS>{
DS> public:
DS> virtual ~A() {}
DS>};
DS>class B : public A
DS>{
DS> public:
DS> ~B() {}
DS>};
DS>
DS>где деструктор класса B также является виртуальным, несмотря на неуказание слова virtual. Как бы задать опции для современных компиляторов (g++ и MSVC++), чтобы такой код компилировался с ошибками, заставляя явно указывать virtual в определении деструкторов порожденных классов.
В связи с чем тебе это понадобилось? Какую проблему создает отсутствие слова virtual?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: "невиртуальный" деструктор порожденного класса
Здравствуйте, rg45, Вы писали:
R>В связи с чем тебе это понадобилось? Какую проблему создает отсутствие слова virtual?
Очевидно, что это проблема, когда читаешь код и думаешь, что функция на самом деле невиртуальная. Когда надо смотреть определение базового класса, что в случае уровней вложенности больше одного ставит задачу шерстить по заголовкам. А если человеку лень шерстить, как бывает в 99% случаях? Он просто подумает, что вызов невиртуальный, ругнется, и будет писать код с учетом этого. А потом ругнется намного сильнее, когда узнает через несколько дней, что есть на самом деле. Я про то, что код должен быть как можно более самодокументированным. И хотел бы пресекать дурной тон написания на уровне компиляции исходников.
Re[3]: "невиртуальный" деструктор порожденного класса
Здравствуйте, DmitryShm, Вы писали:
DS>Здравствуйте, rg45, Вы писали:
R>>В связи с чем тебе это понадобилось? Какую проблему создает отсутствие слова virtual?
DS>Очевидно, что это проблема, когда читаешь код и думаешь, что функция на самом деле невиртуальная. Когда надо смотреть определение базового класса, что в случае уровней вложенности больше одного ставит задачу шерстить по заголовкам. А если человеку лень шерстить, как бывает в 99% случаях? Он просто подумает, что вызов невиртуальный, ругнется, и будет писать код с учетом этого. А потом ругнется намного сильнее, когда узнает через несколько дней, что есть на самом деле. Я про то, что код должен быть как можно более самодокументированным. И хотел бы пресекать дурной тон написания на уровне компиляции исходников.
Полиморфное поведение закладываются именно в базовом классе. Раз уж тебя интересует возможность виртуального вызова, значит ты намереваешься обращаться к объекту через указатель (или ссылку) на базу, так ведь?. И какой толк от виртуальной функции (в данном случае от деструктора) в производном классе, если в базовом она не является виртуальной? Так что, знания о базовом классе нужны в любом случае.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: "невиртуальный" деструктор порожденного класса
Здравствуйте, rg45, Вы писали:
R>Полиморфное поведение закладываются именно в базовом классе. Раз уж тебя интересует возможность виртуального вызова, значит ты намереваешься обращаться к объекту через указатель (или ссылку) на базу, так ведь?. И какой толк от виртуальной функции (в данном случае от деструктора) в производном классе, если в базовом она не является виртуальной? Так что, знания о базовом классе нужны в любом случае.
Вопрос в том, насколько глубоко может быть спрятан базовый класс, и увидит ли программист его объявление. Скажем если наследование такое C -> B -> A, и A используется редко, и чаще используется указатель на базовый класс B, то у программиста отнимет больше времени разобраться, что функции у класса B виртуальные, если слова virtual написаны лишь в объявлении класса A. Отсюда и желание подсказывать компилятором добавлять virtual в объявлениях порожденных классов, помогая хорошему стилю написания.
Понятно, что это лишь частный случай заставить применять определенный утвержденный командой стиль программирования. Особенно актуально при работе с внешними разработчиками.
Я понимаю хороший стиль как такой, код которого легко читается и в этом коде быстро можно разобраться. Чем быстрее тем лучше. Каждая лишняя потраченная секунда множится на количество людей, а это на количество дней с момента, когда код уже забыт. Вот и считайте. И тут уже дело не в стандартах, как они позволяют писать, а просто в производительности труда. Было бы здорово иметь инструмент, который бы позволял жестко контролировать применяемый стиль программирования. Такие есть на уровне подсвечивания кода, и их применяем каждый день. Но я хотел бы чтобы перед выпуском очередной версии с тегом я мог бы жестко проверять код уже без визуальных средств на сборочной машине без запущенного X-сервера, а, например, на уровне компилятора (как выяснили выше это не получится) или на уровне какой-нибудь утилиты, которую можно внедрить в сборочный скрипт. Желательно чтобы она была кроссплатформенной.
Re: "невиртуальный" деструктор порожденного класса
Здравствуйте, DmitryShm, Вы писали:
DS>где деструктор класса B также является виртуальным, несмотря на неуказание слова virtual. Как бы задать опции для современных компиляторов (g++ и MSVC++), чтобы такой код компилировался с ошибками, заставляя явно указывать virtual в определении деструкторов порожденных классов.
Административно это делается через код ревью и введения стандарта написания кода в конторе. Если хочешь, чтобы деструктор всегда был виртуальный, то заставь всех наследоваться от одного базовго класса.
Sic luceat lux!
Re[2]: "невиртуальный" деструктор порожденного класса
Здравствуйте, Kernan, Вы писали: K>Административно это делается через код ревью и введения стандарта написания кода в конторе. Если хочешь, чтобы деструктор всегда был виртуальный, то заставь всех наследоваться от одного базовго класса.
У нас review есть (пользуемся плагином для Redmine), стандарт понятное дело тоже. Но если что-то можно сделать быстрее через использование утилит, то мы стараемся их применять. Заставлять наследоваться одного класса --- запросто. Только когда уже есть реализации, то никто не будет терять преимущества наследования реализаций и переписывать или рефакторить код, по крайней мере сразу. Так что ситуация с уровнями вложенности наследования больше 1 вполне практическая.
Re[5]: "невиртуальный" деструктор порожденного класса
Здравствуйте, DmitryShm, Вы писали:
R>>Полиморфное поведение закладываются именно в базовом классе. Раз уж тебя интересует возможность виртуального вызова, значит ты намереваешься обращаться к объекту через указатель (или ссылку) на базу, так ведь?. И какой толк от виртуальной функции (в данном случае от деструктора) в производном классе, если в базовом она не является виртуальной? Так что, знания о базовом классе нужны в любом случае. DS>Вопрос в том, насколько глубоко может быть спрятан базовый класс, и увидит ли программист его объявление. Скажем если наследование такое C -> B -> A, и A используется редко, и чаще используется указатель на базовый класс B, то у программиста отнимет больше времени разобраться, что функции у класса B виртуальные, если слова virtual написаны лишь в объявлении класса A. Отсюда и желание подсказывать компилятором добавлять virtual в объявлениях порожденных классов, помогая хорошему стилю написания.
Rule of Thumb:
Если у тебя есть A <- B <- C, и ты делаешь допустим delete объекта типа C (или других наследников) через указатель на B, то virtual ~B() желательно написать в B, даже если он уже есть в A. В общем случае, зачем полагаться на то что в A, ведь виртуальный деструктор в B тебе нужен независимо от того есть ли он в A или нет.
Другое дело, если ты через B не делаешь delete, то и virtual там писать не нужно, не зависимо от того есть ли оно в A. Как-то так.
Re: "невиртуальный" деструктор порожденного класса
Здравствуйте, DmitryShm, Вы писали:
DS>Как сделать так, чтобы код с невиртуальным деструктором в порожденном классе давал ошибку компиляции? Дело в том, что компилятор дает скомпилировать такой код
Почитал в википедии про С++x11 и нашел там описание ключевого слова override. http://ru.wikipedia.org/wiki/C%2B%2B11. Современные компиляторы еще не поддерживают эту фичу?
Нехай щастить
Re[2]: "невиртуальный" деструктор порожденного класса
Здравствуйте, PavelCH, Вы писали:
DS>>Как сделать так, чтобы код с невиртуальным деструктором в порожденном классе давал ошибку компиляции? Дело в том, что компилятор дает скомпилировать такой код PCH>Почитал в википедии про С++x11 и нашел там описание ключевого слова override. http://ru.wikipedia.org/wiki/C%2B%2B11. Современные компиляторы еще не поддерживают эту фичу?
Поддерживают даже старые, как расширение MSVC2008. Прочитай внимательней вопрос, он не про override — я тоже сначала про override подумал.
Хотя его можно временно использовать если хочется по-быстрому проверить — virtual в базе или нет.
Re: "невиртуальный" деструктор порожденного класса
Здравствуйте, DmitryShm, Вы писали:
DS>Как сделать так, чтобы код с невиртуальным деструктором в порожденном классе давал ошибку компиляции? Дело в том, что компилятор дает скомпилировать такой код
DS>
DS>class A
DS>{
DS> public:
DS> virtual ~A() {}
DS>};
DS>class B : public A
DS>{
DS> public:
DS> ~B() {}
DS>};
DS>
Придётся писать какой то скрипт, который бы занимался валидацией сорцов автоматически. На уровне только С++ такого сделать нельзя.
Re[2]: "невиртуальный" деструктор порожденного класса
Здравствуйте, PavelCH, Вы писали:
DS>>Как сделать так, чтобы код с невиртуальным деструктором в порожденном классе давал ошибку компиляции? Дело в том, что компилятор дает скомпилировать такой код
PCH>Почитал в википедии про С++x11 и нашел там описание ключевого слова override. http://ru.wikipedia.org/wiki/C%2B%2B11. Современные компиляторы еще не поддерживают эту фичу?
Очень жаль, что слова override и final сделаны опциональными (для обратной совместимости), польза от них могла бы быть гораздо более ощутимой, будь они обязательными.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: "невиртуальный" деструктор порожденного класса
Здравствуйте, rg45, Вы писали:
R>Очень жаль, что слова override и final сделаны опциональными (для обратной совместимости), польза от них могла бы быть гораздо более ощутимой, будь они обязательными.
Спорный вопрос.
Например есть A<-B. Где-то есть удаление B через указатель на A. У A виртуальный деструктор.
Если бы override не был опциональным, то он должен стоять у деструктора B.
Допустим все места где B удаляется через A убрали, и убрали виртуальный деструктор — теперь нужно убирать override у B (и у всех остальных дочерних классов).
Re[3]: "невиртуальный" деструктор порожденного класса
Здравствуйте, rg45, Вы писали:
R>Очень жаль, что слова override и final сделаны опциональными (для обратной совместимости), польза от них могла бы быть гораздо более ощутимой, будь они обязательными.
Можно предложить коммететчикам завести что-то вроде explicit virtual, который будет требовать явного указания при перекрытии, например...
IMHO, правда, это всё мало поможет против проблем с виртуальными функциями в С++...
Главная проблема тут в том, что когда мы меняем что-то в базе, хорошо бы сделать так, что бы во всех наследниках надо было бы проверить, что всё ещё актуально...
Можно, конечно, как-то ввести версионность. Скажем после слова virtual в угловых скобках писать уникальный ID, и требовать того же писать после override и final + добавить слово для случая "наследуем реализацию из базы".
Но С++ и так на ребус похож, к чему усугублять?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: "невиртуальный" деструктор порожденного класса
Здравствуйте, Evgeny.Panasyuk, Вы писали:
R>>Очень жаль, что слова override и final сделаны опциональными (для обратной совместимости), польза от них могла бы быть гораздо более ощутимой, будь они обязательными.
EP>Спорный вопрос. EP>Например есть A<-B. Где-то есть удаление B через указатель на A. У A виртуальный деструктор. EP>Если бы override не был опциональным, то он должен стоять у деструктора B. EP>Допустим все места где B удаляется через A убрали, и убрали виртуальный деструктор — теперь нужно убирать override у B (и у всех остальных дочерних классов).
И очень хорошо, что нужно. Это дает нам возможность, глядя только лишь на определение класса B, не заглядывая в A, сделать вывод о том, можно ли удалять объекты класса B по указателю на базу. В этом же и профит, собственно.
А вообще, снятие виртуальности с деструктора "боевого" базового класса — это не заурядный повседневный рефакторинг, тут и надо быть готовым к другим переменам в коде.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: "невиртуальный" деструктор порожденного класса
Здравствуйте, rg45, Вы писали:
R>И очень хорошо, что нужно. Это дает нам возможность, глядя только лишь на определение класса B, не заглядывая в A, сделать вывод о том, можно ли удалять объекты класса B по указателю на базу. В этом же и профит, собственно.
В общем случае всё равно нельзя. Например деструктор базы вообще может быть защищённым...
R>А вообще, снятие виртуальности с деструктора "боевого" базового класса — это не заурядный повседневный рефакторинг, тут и надо быть готовым к другим переменам в коде.
Это да. Это, IMHO, вообще довольно умозрительная ситуация. Типа по соображениям эффективности где-то динамический полиморфизм на статический меняют? так тут да, одними виртуальными деструкторами не отмахаешься
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: "невиртуальный" деструктор порожденного класса
Здравствуйте, rg45, Вы писали:
R>>>Очень жаль, что слова override и final сделаны опциональными (для обратной совместимости), польза от них могла бы быть гораздо более ощутимой, будь они обязательными. EP>>Спорный вопрос. EP>>Например есть A<-B. Где-то есть удаление B через указатель на A. У A виртуальный деструктор. EP>>Если бы override не был опциональным, то он должен стоять у деструктора B. EP>>Допустим все места где B удаляется через A убрали, и убрали виртуальный деструктор — теперь нужно убирать override у B (и у всех остальных дочерних классов). R>И очень хорошо, что нужно. Это дает нам возможность, глядя только лишь на определение класса B, не заглядывая в A, сделать вывод о том, можно ли удалять объекты класса B по указателю на базу. В этом же и профит, собственно.
Если объект удаляется через указатель на B, то почему бы не добавить вручную опциональный future-proof virtual/override. Если же он не удаляется через B то ничего хорошего в non-optional virtual/override не вижу.
Да, и эти non-optional virtual/override ограничивают гибкость кода. Например:
R>А вообще, снятие виртуальности с деструктора "боевого" базового класса — это не заурядный повседневный рефакторинг, тут и надо быть готовым к другим переменам в коде.
Не обязательно снятие "virtual" с деструктора базы. Достаточно убрать эту базу из родителей.