2016-04-16 2 views
0

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

class Base 
{ 
public: 
    Base() {cout << "Base constr" << endl;} 
    virtual void func() {cout << "Base func()" << endl;} 
    virtual ~Base() {cout << "Base destr" << endl;} 
}; 

class Layer1 : public Base 
{ 
public: 
    Layer1() {cout << "Layer1 constr" << endl;} 
    virtual void func() {cout << "Layer1 func()" << endl;} 
    virtual ~Layer1() {cout << "Layer1 destr" << endl;} 
}; 

class Layer2 : public Layer1 
{ 
public: 
    Layer2() {cout << "Layer2 constr" << endl;} 
    virtual void func() {cout << "Layer2 func()" << endl;} 
    ~Layer2() {cout << "Layer2 destr" << endl;} 
}; 

int main(int argc, char** argv) 
{ 
    Layer2 * l2ptr = (Layer2 *) new Base; 
    l2ptr->func(); 
    delete l2ptr; 

    return 0; 
} 

Выход:

Base constr 
Base func() 
Base destr 

Я имею в виду в точке, где удалить называется l2ptr. С первого взгляда кажется, что нужно вызвать деструктор Layer2, но не был создан объект Layer2. Кроме того, Layer2 деструктор не virtual. Когда я делаю это виртуальным, вывод будет таким же. Зачем?

И следующий вопрос: какие проблемы и соображения существуют на объекте родительского класса по его указателю на дочерний класс?

EDIT: Если изменить Layer2 класс к этому

class Layer2 : public Layer1 
{ 
public: 
    Layer2() {cout << "Layer2 constr" << endl;} 
    virtual void func() {cout << "Layer2 func()" << endl;} 
    void func2() {cout << "Layer2 func2()" << endl;} 
    virtual ~Layer2() {cout << "Layer2 destr" << endl;} 
}; 

И main() как это:

int main(int argc, char** argv) 
{ 
    Layer2 * l2ptr = (Layer2 *) new Base; 
    l2ptr->func(); 
    l2ptr->func2(); 
    delete l2ptr; 

    return 0; 
} 

Он по-прежнему работает, и выход:

Base constr 
Base func() 
Layer2 func2() 
Base destr 

Еще раз, Зачем?

+4

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

+0

У вас UB мало после '(Layer2 *) новой базы ... – Jarod42

+0

http://blog.llvm.org/2016/04/undefined-behavior-is-magic.html – JVApen

ответ

0

Ваш код имеет неопределенное поведение, как указано - например, при доступе к объекту Base с помощью указателя на Layer2.

Неопределенное поведение включает в себя работу (и вообще что-то еще). В этом случае бывает, что макет объектов достаточно схожим, что ничего не происходит слишком плохо. Указатели vtable находятся в одном и том же месте; вызовы виртуальных функций правильно переходят к версиям Base, потому что объект является Base и содержит указатель на таблицу v. Невиртуальные функции переходят к типу, который вы сказали компилятору.

(. Реализации не должны использовать виртуальные таблицы, но они обычно делают)

Но это все еще неопределенное поведение; не делайте этого, и определенно не полагайтесь на него.

+0

Спасибо, я понял. Я действительно не пытаюсь разобраться с UB, но мне интересно «почему». Еще два вопроса: (1), но почему деструкторы Layer2 и Layer1 не вызываются при удалении l2ptr? (2) Теперь, как удалить и вообще C++ работает, когда дело доходит до вызова деструкторов (кстати, я знаю, что delete - это функция lib, которая освобождает кучу mamory)? –

+0

Деструкторы - все виртуальные; когда вы делаете 'delete l2ptr', он смотрит в vtable, находит деструктор' Base' и вызывает его. Если бы у вас был реальный объект 'Layer2', vtable указывал бы на его деструктор, который затем вызывал бы его базовые деструкторы. –

+0

«Деструкторы - все виртуальные», что вы имеете в виду? Всегда? Когда я объявляю Layer2 как виртуальный, это правильно: деструктор вызывается путем разрешения отправки vtable. Но когда я не объявляю его виртуальным, он статически разрешимо. Но в этом случае он не работает –

4

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

У вас есть вещи, в основном в обратном направлении, что вы, вероятно, предназначены (или того, что вы должны иметь намерение в любом случае), который будет иметь main что-то на таком порядке:

int main(int argc, char** argv) 
{ 
    Base *bptr = new Layer2; 
    bptr->func();  

    Layer2 *l2ptr = dynamic_cast<Layer2 *>(bptr); 

    if (l2ptr) 
     l2ptr->func2(); 

    delete bptr; 
} 

Здесь мы используем pointer to Base для обратитесь к объекту типа Layer2, а не наоборот. Поскольку Layer2 получен из Base, это разрешено, и поведение имеет смысл.

Это позволяет нам говорить о деталях, почему он делает то, что он делает.

Деструктор не отмечен virtual в Layer2 в основном не имеет значения: так как это отмечено в базовом классе virtual, остается virtual во всех производных классах:

C++ стандарт, § [class.virtual]/2:

Если виртуальная функция-член vf объявлена ​​в классе Base и в классе Derived, полученном прямо или косвенно из Base, функция-член vf с тем же именем, list-type-list (8.3.5) cv-qualification и ref-qualifier (или отсутствие такого же), что и Base :: vf, тогда Deri ved :: vf также является виртуальным (независимо от того, объявлено ли оно или нет), и оно переопределяет База :: vf. C++ стандарта

, § [class.virtual]/6:

Даже если деструкторы не наследуется, деструктор в производном классе перекрывает базовый класс деструктор объявлен виртуальным;

+0

Это было так, как было в моем посте. Но ваше предположение и предлагаемый код оценены. И о наследовании виртуальной реальности от базы: где я могу найти эту информацию в C++-ссылке? –

+0

Большое спасибо вам. Уважение. –

0

Поправьте меня, если я ошибаюсь, но вы никогда не построить объект либо Layer2 или Layer1; только объект Base, который вы бросаете (в стиле C, не обращайте внимание на вас, а не на C++) на указатель на тип Layer2. То есть func2 не будет доступен объекту, на который указывает ваш указатель, но компилятор принимает его, потому что он проверяет только статический тип вашего указателя.

Смежные вопросы