Виртуальный деструктор
От: ksandro Мухосранск  
Дата: 06.12.16 15:35
Оценка:
Все мы знаем в каких случаях и почему деструктор базового класса должен быть виртуальным. Но вот про деструктор потомка обычно ничего конкретного не говорится. Однако в C++11 появилось ключевое слово override и возможность явно указать использование деволтного деструктора.

struct Parent
{
  virtual ~Parent()
  {
  }
    
};

struct Child: public Parent
{
  ~Child() override = default;
};


Насколько корректно с точки зрения стандарта использовать одновременно "override" и "=default", будет ли диструктор сгенерирован правильно в этом случае. И, если это корректно, можно ли считать, что именно так и желательно писать всегда, чтобы гарантировать что деструктор родителя виртуальный.
Re: Виртуальный деструктор
От: VTT http://vtt.to
Дата: 06.12.16 18:02
Оценка:
Здравствуйте, ksandro, Вы писали:

K>Все мы знаем в каких случаях и почему деструктор базового класса должен быть виртуальным. Но вот про деструктор потомка обычно ничего конкретного не говорится. Однако в C++11 появилось ключевое слово override и возможность явно указать использование деволтного деструктора.


K>
K>struct Parent
K>{
K>  virtual ~Parent()
K>  {
K>  }
    
K>};

K>struct Child: public Parent
K>{
K>  ~Child() override = default;
K>};

K>


K>Насколько корректно с точки зрения стандарта использовать одновременно "override" и "=default", будет ли диструктор сгенерирован правильно в этом случае.


Мне представляется, что да.
Но такая запись по идее может приводить к генерации деструктора, единственной заботой которого будет вызов деструктора базового класса.

K>И, если это корректно, можно ли считать, что именно так и желательно писать всегда, чтобы гарантировать что деструктор родителя виртуальный.


Можно еще более явно написать:
static_assert(::std::has_virtual_destructor< Parent >::value);
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re: Виртуальный деструктор
От: N. I.  
Дата: 06.12.16 19:48
Оценка:
ksandro:

K>Насколько корректно с точки зрения стандарта использовать одновременно "override" и "=default"


Я не видел, чтоб такое где-то запрещалось.

K>будет ли диструктор сгенерирован правильно в этом случае.


Он будет такой же, как если бы его определили так:

~Child() override {}

(раз деструктор виртуальный, значит, тривиальным ему по-любому не быть).

K>И, если это корректно, можно ли считать, что именно так и желательно писать всегда, чтобы гарантировать что деструктор родителя виртуальный.


Зачем производному классу нужна эта гарантия? Правильное удаление — это забота того, кто владеет объектом. Владелец может использовать проверку виртуального деструктора, если ему так спокойнее. Например, можно сделать аналог std::unique_ptr<T>, который будет требовать std::has_virtual_destructor<T>::value == true при любой попытке подсунуть ему в конструктор указатель на производный от T тип:

struct B {};
struct D1 : B
{
    virtual ~D1() {}
};
struct D2 : D1 {};

unique_ptr_safe<B> ub(new B);                        // OK: the same object type
unique_ptr_safe<B> ubm(make_unique_safe<B>());       // OK: the same object type
unique_ptr_safe<B> ubd1(new D1);                     // error: B has no virtual dtor
unique_ptr_safe<B> ubmd1 = make_unique_safe<D1>();   // error: B has no virtual dtor
unique_ptr_safe<D1> ud1d2(new D2);                   // OK: D1 has virtual dtor
unique_ptr_safe<D1> ud1md2 = make_unique_safe<D2>(); // OK: D1 has virtual dtor

Пока кто-то не начнёт передавать владение через "небезопасные" указатели (или использовать плохие сторонние deleter-ы), unique_ptr_safe будет удалять только то, что умеет, а на просьбы получить во владение объект, который он удалять не умеет, будет отвечать ошибкой компиляции.

С реализацией через косвенный вызов правильного delete можно вообще не обращать внимание на наличие виртуального деструктора:

unique_ptr_universal<B> ubd1(new D1);                        // OK: invokes 'delete static_cast<D1 *>(get_address())' in dtor
unique_ptr_universal<B> ubmd1 = make_unique_universal<D1>(); // OK: invokes 'delete static_cast<D1 *>(get_address())' in dtor

std::shared_ptr<B> sbd1(new D1);                             // OK: makes a deleter for D1
std::shared_ptr<B> sbud1(make_unique_safe<D1>());            // OK: makes a deleter for D1
std::shared_ptr<B> sbmd1 = make_shared_safe<D1>();           // OK: makes a deleter for D1

Лениво сейчас расписывать реализацию unique_ptr_safe и unique_ptr_universal — там, в общем-то, и так всё очевидно: надо использовать шаблонные конструкторы и в них в первом случае проверять std::has_virtual_destructor<T>::value, а в другом случае сохранять указатель на соответствующую специализацию шаблонной функции, которая потом позовёт нужный deleter (понадобится хранить два адреса: отдельно для базового подобъекта и отдельно для целого объекта).
Отредактировано 06.12.2016 20:31 N. I. . Предыдущая версия . Еще …
Отредактировано 06.12.2016 19:53 N. I. . Предыдущая версия .
Re: Виртуальный деструктор
От: Videoman Россия https://hts.tv/
Дата: 07.12.16 12:26
Оценка:
Здравствуйте, ksandro, Вы писали:

K>Насколько корректно с точки зрения стандарта использовать одновременно "override" и "=default", будет ли диструктор сгенерирован правильно в этом случае. И, если это корректно, можно ли считать, что именно так и желательно писать всегда, чтобы гарантировать что деструктор родителя виртуальный.


default следует читать как "сгенерировать деструктор по умолчанию для данного класса", а не "использовать уже готовый по умолчанию". Т.е. будет все ок. Тоже самое происходит, если в классе так объявлен конструктор копирования. Конструктор копирования будет сгенерирован именно для этого класса.
Re: Виртуальный деструктор
От: rg45 СССР  
Дата: 09.12.16 08:54
Оценка: 1 (1) +4
Здравствуйте, ksandro, Вы писали:

K>Насколько корректно с точки зрения стандарта использовать одновременно "override" и "=default", будет ли диструктор сгенерирован правильно в этом случае. И, если это корректно, можно ли считать, что именно так и желательно писать всегда, чтобы гарантировать что деструктор родителя виртуальный.


Виртуальнось деструктора, заложенная в базовом классе, не может быть отменена никакими ключевыми словами. Ключевое слово "override" не является обязательным и выражает лишь гарантию того, что функция переопределяет виртуальную функцию базового класса. "default" говорит только о том, что тело деструктора сгенерировано компилятором, а не написано программистом, и тоже никак не влияет на виртуальность. Таким образом, если деструктор базового класса объявлен виртуальным, то деструкторы всех производных классов также будут виртуальными, независимо от их оформления.
--
Не можешь достичь желаемого — пожелай достигнутого.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.