Re: vptr в деструкторе
От: rg45 СССР  
Дата: 22.10.12 09:01
Оценка: 3 (1)
Здравствуйте, RSDN_new_user, Вы писали:

RSD>Доброго дня,

RSD>возник вопрос по vptr — у меня в VS2005 получается, что в деструкторе он уже не работает. Как так получается, если члены класса, разрушаются после выполнения деструктора? Он разве не является членом класса?

vptr — это способ реализации виртуальности, а не член класса. Значения vptr объектов базового и производного классов различны. Поэтому при конструировании объекта, при переходе от конструктора базового класса к конструктору производного, vptr изменяет свое значение. И при разрушении объектов тоже изменяет от деструктора к деструктору, только в обратном порядке.

А ты что, хочешь из деструктора базового класса позвать переопределенную виртуального функцию производного? Нет уж, это вам не C#, тут все строго — когда работает деструктор базового класса, объект производного уже разрушен, и нечего обращаться к мертвому объекту!
--
Справедливость выше закона. А человечность выше справедливости.
Re[3]: vptr в деструкторе
От: watch-maker  
Дата: 22.10.12 10:30
Оценка: 2 (1)
Здравствуйте, RSDN_new_user, Вы писали:

RSD>Здравствуйте, rg45, Вы писали:


R>>vptr — это способ реализации виртуальности, а не член класса. Значения vptr объектов базового и производного классов различны. Поэтому при конструировании объекта, при переходе от конструктора базового класса к конструктору производного, vptr изменяет свое значение. И при разрушении объектов тоже изменяет от деструктора к деструктору, только в обратном порядке.


RSD>Это как это? Два адреса в одном vprt? Почему тогда базовый класс с vptr имеет размер 4 байта, и наследник тоже 4? Там разве не один указатель на таблицу в которую заносятся адреса виртуальных функций объекта?

vptr — указатель на константную таблицу. Но сам указатель vptr — не константа, и может изменяется в процессе жизненного цикла экземпляра. Так после разрушения производного класса, указатель vptr перезаписывается, после чего он уже указывает на таблицу для базового класса.

R>>А ты что, хочешь из деструктора базового класса позвать переопределенную виртуального функцию производного? Нет уж, это вам не C#, тут все строго — когда работает деструктор базового класса, объект производного уже разрушен, и нечего обращаться к мертвому объекту!


RSD>При создании объекта:

RSD>1. Конструктор CBase создает свои члены, настраивает vptr с таблицей, выполняет тело.
RSD>2. Конструктор CDerived создает свои члены, настраивает таблицу на которую указывает доставшийся в наследство vptr, перезаписывая там адрес func на свою.

Почти всегда это не так. Таблица ничем не переписывается. Просто существует две таблицы: для базового и для производного классов. Изменяется только указатель — сначала он указывал на первую таблицу, затем на вторую.

RSD>3. Деструктор, невиртуальный, базового класса по идее должен откатить свои изменения. Деструктор производного я никак не звал. Кто поломал таблицу Или есть правило, что все вызовы в деструкторах и конструкторах идут без использования таблицы виртуальных функций


Перед вызовом деструктора базового класса, в данном случае, в vptr заносится указатель на таблицу, соответствующую базовому классу. То есть таблица есть, её можно использовать, но она соответствует базовому классу, так как деструктор производного класса уже отработал (или нет, как в этом примере, но это не важно) и производный экземпляр уже не существует.

RSD>В общем, где я ошибаюсь, и как это формулируется стандартом тогда?

Формулируется, но не в терминах указателей на таблиц. Ибо можно, например, реализовать иерархию классов и без них.

When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the con- structor or destructor’s class, or overriding it in one of the other base classes of the most derived object.

Re[3]: vptr в деструкторе
От: rg45 СССР  
Дата: 22.10.12 10:39
Оценка: 2 (1)
Здравствуйте, RSDN_new_user, Вы писали:

R>>vptr — это способ реализации виртуальности, а не член класса. Значения vptr объектов базового и производного классов различны. Поэтому при конструировании объекта, при переходе от конструктора базового класса к конструктору производного, vptr изменяет свое значение. И при разрушении объектов тоже изменяет от деструктора к деструктору, только в обратном порядке.


RSD>Это как это? Два адреса в одном vprt? Почему тогда базовый класс с vptr имеет размер 4 байта, и наследник тоже 4? Там разве не один указатель на таблицу в которую заносятся адреса виртуальных функций объекта?


Нет, адрес один, но он изменяется в процессе конструирования и разрушения объекта

R>>А ты что, хочешь из деструктора базового класса позвать переопределенную виртуального функцию производного? Нет уж, это вам не C#, тут все строго — когда работает деструктор базового класса, объект производного уже разрушен, и нечего обращаться к мертвому объекту!


RSD>А вот так:


RSD>class CBase {

RSD>public:
RSD> ~CBase() { func(); } // не виртуальный!
RSD> virtual func() {}
RSD>};

RSD>class CDerived : public CBase {

RSD>public:
RSD> virtual func() {}
RSD>};


RSD> CBase* pB = new CDerived;

RSD> delete pB;


RSD>При создании объекта:

RSD>1. Конструктор CBase создает свои члены, настраивает vptr с таблицей, выполняет тело.
RSD>2. Конструктор CDerived создает свои члены, настраивает таблицу на которую указывает доставшийся в наследство vptr, перезаписывая там адрес func на свою.
RSD>3. Деструктор, невиртуальный, базового класса по идее должен откатить свои изменения. Деструктор производного я никак не звал. Кто поломал таблицу Или есть правило, что все вызовы в деструкторах и конструкторах идут без использования таблицы виртуальных функций

RSD>В общем, где я ошибаюсь, и как это формулируется стандартом тогда?


Стандарт не оперирует таким понятием как vptr, в нем просто описывается, каким должно быть поведение — смотри параграфы: 9 Classes, 10 Derived classes и 13 Special member functions.

О vptr можно говорить при рассмотрении того или иного компилятора. Что касается конкретно msvc, откомпилируй и запусти на выполнение следующий пример, тебе станет все понятно:
  Скрытый текст
#include <iostream>

class A
{
public:

   A() { void* vptr = *reinterpret_cast<void**>(this); std::cout << vptr << std::endl; }
   ~A() { void* vptr = *reinterpret_cast<void**>(this); std::cout << vptr << std::endl; }

   virtual void foo() { }

};

class B : public A
{
public:

   B() { void* vptr = *reinterpret_cast<void**>(this); std::cout << vptr << std::endl; }
   ~B() { void* vptr = *reinterpret_cast<void**>(this); std::cout << vptr << std::endl; }
};

int main()
{
   B b;
}

Обрати внимание, что на этапе конструирования и разрушения значения vptr различны для одного и того же объекта. И это не смотря на то, что в производном классе нет ни новых виртуальных функций, ни переопределения существующих. Просто, как только определяется какой-либо производный класс от класса с виртуальными функциями, компилятор сразу же заводит для него таблицу виртуальных функций. И при конструировании и разрушении объекта vptr указывает то на одну таблицу, то на другую.
--
Справедливость выше закона. А человечность выше справедливости.
vptr в деструкторе
От: RSDN_new_user  
Дата: 22.10.12 08:37
Оценка:
Доброго дня,
возник вопрос по vptr — у меня в VS2005 получается, что в деструкторе он уже не работает. Как так получается, если члены класса, разрушаются после выполнения деструктора? Он разве не является членом класса?
Write code for people
Re: vptr в деструкторе
От: Vain Россия google.ru
Дата: 22.10.12 09:33
Оценка:
Здравствуйте, RSDN_new_user, Вы писали:

RSD>Доброго дня,

RSD>возник вопрос по vptr — у меня в VS2005 получается, что в деструкторе он уже не работает. Как так получается, если члены класса, разрушаются после выполнения деструктора? Он разве не является членом класса?
Ну так вроде это член добавленный компилятором. Компилятор хочет — разрушает, хочет — нет
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: vptr в деструкторе
От: RSDN_new_user  
Дата: 22.10.12 10:03
Оценка:
Здравствуйте, rg45, Вы писали:

R>vptr — это способ реализации виртуальности, а не член класса. Значения vptr объектов базового и производного классов различны. Поэтому при конструировании объекта, при переходе от конструктора базового класса к конструктору производного, vptr изменяет свое значение. И при разрушении объектов тоже изменяет от деструктора к деструктору, только в обратном порядке.


Это как это? Два адреса в одном vprt? Почему тогда базовый класс с vptr имеет размер 4 байта, и наследник тоже 4? Там разве не один указатель на таблицу в которую заносятся адреса виртуальных функций объекта?

R>А ты что, хочешь из деструктора базового класса позвать переопределенную виртуального функцию производного? Нет уж, это вам не C#, тут все строго — когда работает деструктор базового класса, объект производного уже разрушен, и нечего обращаться к мертвому объекту!


А вот так:


class CBase {
public:
~CBase() { func(); } // не виртуальный!
virtual func() {}
};

class CDerived : public CBase {
public:
virtual func() {}
};


CBase* pB = new CDerived;
delete pB;


При создании объекта:
1. Конструктор CBase создает свои члены, настраивает vptr с таблицей, выполняет тело.
2. Конструктор CDerived создает свои члены, настраивает таблицу на которую указывает доставшийся в наследство vptr, перезаписывая там адрес func на свою.
3. Деструктор, невиртуальный, базового класса по идее должен откатить свои изменения. Деструктор производного я никак не звал. Кто поломал таблицу Или есть правило, что все вызовы в деструкторах и конструкторах идут без использования таблицы виртуальных функций

В общем, где я ошибаюсь, и как это формулируется стандартом тогда?
Write code for people
Re[2]: vptr в деструкторе
От: RSDN_new_user  
Дата: 22.10.12 10:14
Оценка:
Здравствуйте, Vain, Вы писали:

V>Ну так вроде это член добавленный компилятором. Компилятор хочет — разрушает, хочет — нет


Это не вопрос, но пусть тогда соблюдает порядок действий, обратный созданию объекта. А то получается, что он нарушен — вот тут пример: http://www.rsdn.ru/forum/cpp/4937316?tree=tree
Автор: RSDN_new_user
Дата: 22.10.12
Write code for people
Re[2]: vptr в деструкторе
От: Mr.Delphist  
Дата: 22.10.12 10:48
Оценка:
Здравствуйте, Vain, Вы писали:

V>Ну так вроде это член добавленный компилятором. Компилятор хочет — разрушает, хочет — нет


Основная опасность, подозреваю — в том что разрушенный член не обязательно очищается (нулями или дебаг-паттерном), поэтому о попытке обращения к официально мёртвому объекту невнимательный программист заподозрит далеко не сразу.
Re[3]: vptr в деструкторе
От: dead0k  
Дата: 22.10.12 11:53
Оценка:
Здравствуйте, RSDN_new_user, Вы писали:

RSD>3. ... Кто поломал таблицу ...


UB

RSD>В общем, где я ошибаюсь, и как это формулируется стандартом тогда?


5.3.5 3)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.