LINUX.ORG.RU

> Подскажите зачем нужны виртуальные деструкторы?

чтоб они выполнялись все, а не только «верхний»

Где лучше и где не стоит их использовать?


лучше везде, не стоит разве-что при оптимизации, если знаешь, что делаешь

lester ★★★★ ()

Где лучше и где не стоит их использовать?

лучше - в любом классе, предполагающем полиморфное поведение. не стоит - там, где ты очень хорошо понимаешь, что делаешь

jtootf ★★★★★ ()

использовать надо в случае, когда будешь работать с потомками данного класса, используя указатели/ссылки типа базового класса, и при этом в классе есть виртуальные методы

annulen ★★★★★ ()

Следовать простому правилу: если в классе есть хоть один виртуальный метод, то и деструктор тоже должен быть виртуальным.

Dendy ★★★★★ ()
Ответ на: комментарий от annulen

>использовать надо в случае, когда будешь работать с потомками данного класса, используя указатели/ссылки типа базового класса, и при этом в классе есть виртуальные методы

Именно. Т.е. чтоб выполнялся именно тот деструктор, что соответствует типу объекта, а не какому-то родительскому типу, к которому он может быть приведён.

И виртаульные методы тут непричём,

anonymous ()
Ответ на: комментарий от anonymous

>И виртаульные методы тут непричём,

да, скорее их наличие свидетельствует о соответствующем использовании объектов класса :)

annulen ★★★★★ ()
Ответ на: комментарий от anonymous

Во, нашёл в Страуструбе 3е издание, см. страница 478, п.15.6.

anonymous ()

Нужен ли виртуальный деструктор в том случае, если вся иерархия классов содержит только методы? Т.е. родительский тип поставляет интерфейс, а дочерние — его реализуют.

annoynimous ★★★★★ ()

У нас один лектор говорил, что конструкторы всегда должны быть либо виртуальными, либо защищенными (protected). Категоричненько, но здравый смысл в этом есть.

gizzka ★★ ()
Ответ на: комментарий от annoynimous

>Нужен ли виртуальный деструктор в том случае, если вся иерархия классов содержит только методы? Т.е. родительский тип поставляет интерфейс, а дочерние — его реализуют.

Есс-но нужен. Классический случай.

anonymous ()
Ответ на: комментарий от annoynimous

Нужен ли виртуальный деструктор в том случае, если вся иерархия классов содержит только методы? Т.е. родительский тип поставляет интерфейс, а дочерние — его реализуют.

Да.

Begemoth ★★★★★ ()
Ответ на: комментарий от gizzka

конструкторы всегда должны быть либо виртуальными

В С++ вируальных конструкторов нет, вообще-то.

Begemoth ★★★★★ ()
Ответ на: комментарий от gizzka

>конструкторы всегда должны быть либо виртуальными

Виртуальный конструктор - это вообще экзотика, которая прямыми средствами языка не реализуется, только через костыли.

anonymous ()
Ответ на: комментарий от anonymous

это не экзотика, это незнание языка. В стандарте ясно написано - конструкторы не должны быть virtual и const.

alex_custov ★★★★★ ()
Ответ на: комментарий от alex_custov

> это не экзотика, это незнание языка.

это расширенное сознание:)

dilmah ★★★★★ ()
Ответ на: комментарий от Begemoth

А что этот виртуальный деструктор делать должен, я извиняюсь? Полей у классов нет по условию.

annoynimous ★★★★★ ()
Ответ на: комментарий от Begemoth

Деструкторы, конечно же. Бедное мое расширенное сознание :)

gizzka ★★ ()
Ответ на: комментарий от alex_custov

>это не экзотика, это незнание языка.

Да ну. Автор языка сам ясно пишет, что хрень реализуемая при желании и даже пример был. Влом искать, но где-то близко к 15.6.

anonymous ()
Ответ на: комментарий от Dendy

> Следовать простому правилу: если в классе есть хоть один виртуальный метод, то и деструктор тоже должен быть виртуальным.

зачастую излишне, имхо.

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

Obey-Kun ★★★★★ ()
Ответ на: комментарий от annoynimous

Тут фишка в том, что он должен быть обязательно виртуальным в потомке. А в самом интерфейсном классе он есс-но ничего не делает.

anonymous ()
Ответ на: комментарий от Obey-Kun

> если мне надо, чтобы у какой-то дочки был свой деструктор, сделаю у родителя виртуальный... пока не надо — хай будет обычный. банально быстрее.

проверял? может компилятор такие случаи автоматом соптимизирует

lester ★★★★ ()
Ответ на: комментарий от alex_custov

это не экзотика, это незнание языка. В стандарте ясно написано - конструкторы не должны быть virtual и const.

:)

class SomeClass {
public:
  SomeClass() {
    constructThis();
  }

private:
  virtual void constructThis() const {
    ...
  }
};
Obey-Kun ★★★★★ ()
Ответ на: комментарий от Obey-Kun

В принципе, если нет наследования, нечего делать его виртуальным.

annoynimous ★★★★★ ()
Ответ на: комментарий от lester

> проверял? может компилятор такие случаи автоматом соптимизирует

нет, не проверял, но не надеюсь на компилятор в таких случаях, тем более что компиляторы разные бывают

Obey-Kun ★★★★★ ()
Ответ на: комментарий от annoynimous

> В принципе, если нет наследования, нечего делать его виртуальным.

ясен пень :)) только тут не про это

Obey-Kun ★★★★★ ()
Ответ на: комментарий от gizzka

> У нас один лектор говорил, что конструкторы всегда должны быть либо виртуальными, либо защищенными (protected).

убей лектора молотком. первое не имеет отношения к C++, второе к здравому смысле.

Obey-Kun ★★★★★ ()
Ответ на: комментарий от Obey-Kun

тьфу, конечно так:

class SomeClass { 
public: 
  SomeClass() { 
    constructThis(); 
  } 
 
protected: 
  virtual void constructThis() const { 
    ... 
  } 
}; 

:)

Obey-Kun ★★★★★ ()
Ответ на: комментарий от Obey-Kun

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

anonymous ()
Ответ на: комментарий от Obey-Kun

Ну это и есть костыль про который я писал выше.

anonymous ()
Ответ на: комментарий от Obey-Kun

каким-то VB попахивает) хотя признаюсь, иногда бывает полезно вынести общую часть конструктора для родителя и потомков, если он большой

annulen ★★★★★ ()
Ответ на: комментарий от anonymous

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

annulen ★★★★★ ()
Ответ на: комментарий от annoynimous

>Полей у классов нет по условию.

если у потомков тоже нет полей (по крайней мере, динамических), то деструктор вообще не нужен никому из них

annulen ★★★★★ ()
Ответ на: комментарий от Obey-Kun

первое не имеет отношения к C++, второе к здравому смысле

Просто опечатка, должен был написать «деструкторы». Зато в итоге народ чего-то обсуждает, все довольны.

gizzka ★★ ()
Ответ на: комментарий от annoynimous

А что этот виртуальный деструктор делать должен, я извиняюсь? Полей у классов нет по условию.

если в этой иерархии деструкторы есть (с какой-нибудь логикой RFID, не требующей состояния у объектов), то они должны быть виртуальными; если их нет, то о чём вообще речь?

jtootf ★★★★★ ()
Ответ на: комментарий от Obey-Kun

Возможно это будет для вас новостью, но виртуальные методы не работают ни в конструкторах, ни в деструкторах.

Dendy ★★★★★ ()
Ответ на: комментарий от annoynimous

А что этот виртуальный деструктор делать должен, я извиняюсь? Полей у классов нет по условию.

Сам - ничего, но для него компилятор выделит элемент в таблице виртуальных функций, который и будет использоваться для вызова деструктора фактического класса объекта.

Begemoth ★★★★★ ()
Ответ на: комментарий от Dendy

> Возможно это будет для вас новостью, но виртуальные методы не работают ни в конструкторах, ни в деструкторах.

Спасибо за инфо. Всегда был непонятен этот аспект :).

А как именно, по стандарту, они там работают? Или компилятор не даст так поступить?

Obey-Kun ★★★★★ ()
Ответ на: комментарий от Obey-Kun

А как именно, по стандарту, они там работают? Или компилятор не даст так поступить?

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

Begemoth ★★★★★ ()
Ответ на: комментарий от Obey-Kun

> А как именно, по стандарту, они там работают? Или компилятор не даст так поступить?

Компилятор даст, не работать будет в рантайме. Идея в том, что конструктор - самый первый вызываемый метод, он должен конструировать только себя, а деструктор - последний метод, он только уничтожает самого себя. Перед конструктором и после деструктора конкретного класса другие его методы вызываться не могут.

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

Для примера - в Objective-C конструкторов и деструкторов нет вообще.

Dendy ★★★★★ ()
Ответ на: комментарий от Obey-Kun

>А как именно, по стандарту, они там работают? Или компилятор не даст так поступить?

В деструкторах они в действительности работают, там просто применяется раннее связывание. Про это тоже в Страуструбе было.

anonymous ()
Ответ на: комментарий от Begemoth

Они там работают как обычные функции - т.е. вызывается функция того же класса, что и конструктор/деструктор.

Уточню, они работают в пределах текущей таблицы виртуальных функций, которая может быть ещё недостроена или частично разрушена.

class A
{
public:
    void runFoo() { foo(); }
    virtual void foo() { printf( "A" ); }
};

class B : public A
{
public:
    ~B() { runFoo(); }
    virtual void foo() { printf( "B" ); }
};

class C : public B
{
public:
    virtual void foo() { printf( "C" ); }
};

...
delete new C;

Напечатает «B».

Dendy ★★★★★ ()
Ответ на: комментарий от anonymous

Нет, _обязательно_ в базовом

#include <stdio.h>

struct A
{
        ~A() {
                printf("free a\n");
        }
};

struct B: public A
{
        virtual ~B()
        {
                printf("free b\n");
        }
};

int main()
{
        A * a = new B;

        delete a;
}
$ ./a.out
free a

# ставим virtual где надо и получаем
$ ./a.out
free b
free a

Reset ★★★★★ ()
Ответ на: комментарий от Obey-Kun

сам убейся молотком, лектор дельные вещи говорит, а из тебя «знание» c++ так и прет.

Reset ★★★★★ ()
Ответ на: комментарий от Obey-Kun
#include <stdio.h>
#include <vector>

struct A
{
        ~A() {
                printf("free a\n");
        }
};

struct B: public A
{
        std::vector < double > v;
        B(): v(1000000) {}
};

int main()
{
        A * a = new B;
        delete a;
}

если мне надо, чтобы у какой-то дочки был свой деструктор

деструктор не нужен, а потекли

Reset ★★★★★ ()
Ответ на: комментарий от Reset

> деструктор не нужен, а потекли

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

Obey-Kun ★★★★★ ()
Ответ на: комментарий от Obey-Kun

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

Dendy ★★★★★ ()
Ответ на: комментарий от Dendy

> Следовать простому правилу: если в классе есть хоть один виртуальный метод, то и деструктор тоже должен быть виртуальным.

Неправильно. Если вероятность наследования от этого класса ненулевая, деструктор надо делать виртуальным. Т.е проще сказать «деструктор делать виртуальным всегда» :-)

no-dashi ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.