вопрос
От: Аноним  
Дата: 25.08.10 07:40
Оценка:
Добрый день. Есть такой пример:
class B
{
protected:
A* _holder;
public: 
B(A* holder){ _holder = holder; }
DoSomething()
{
//не обращается более к методам и полям класса, пользует только локальные переменные
}
Method()
{
   _holder->DelMe();
   DoSomething();
}
};

class A
{
public:
B BObj;
A()
{
  BObj = new B(this);
}
DelMe()
{
  if (BObj) delete BObj;
}
};

void Do();
int Main()
{
A* aObj = new A();
a->BOjb->Method();
Do();
delete a;
}

void Do()
{
//do something
}

Меня смущает B::Method, возможно ли тут возникновение ошибки, когда она может возникнуть?
добавлена раскраска — Кодт
Re: вопрос
От: Erop Россия  
Дата: 25.08.10 07:47
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Добрый день. Есть такой пример:


Используй для оформления кода тэги [c] [/c]!

А>class B {
А>Method()
А>{
А>   _holder->DelMe();
А>}
А>};

А>class A
А>{
А>DelMe()
А>{
А>  if (BObj) delete BObj;
А>}
А>};

А>Меня смущает B::Method, возможно ли тут возникновение ошибки, когда она может возникнуть?
Ну, если я верно понял твой вопрос, то иы спрашиваешь, можно ли в методе класса говорить delete this? Да можно.
Но при этом DoSomething() должен быть статическим, так как вызов нестатического метода -- это разыменование, а разыменование удалённого указателя -- это UB
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: вопрос
От: IMar Россия  
Дата: 26.08.10 15:48
Оценка:
Здравствуйте, Erop, Вы писали:

E>Но при этом DoSomething() должен быть статическим, так как вызов нестатического метода -- это разыменование, а разыменование удалённого указателя -- это UB


Вызов нестатического метода DoSomething означает, что в метод неявно передаётся указатель на текущий объект this. Но автор пишет что там нет обращения к полям класса — this разыменовываться не будет. Так что нестатическая Функция здесь нормально работает, я проверил на всякий случай
Re[3]: вопрос
От: dilmah США  
Дата: 26.08.10 16:08
Оценка:
IM>Но автор пишет что там нет обращения к полям класса — this разыменовываться не будет. Так что нестатическая Функция здесь нормально работает, я проверил на всякий случай

это не отменяет UB.
И собственно, возникает вопрос, если нет обращения к полям, то почему он не объявлен статическим?
Re[4]: вопрос
От: Кодт Россия  
Дата: 26.08.10 18:13
Оценка:
Здравствуйте, dilmah, Вы писали:

D>И собственно, возникает вопрос, если нет обращения к полям, то почему он не объявлен статическим?


Цитата дня:

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


Такие харакири-методы издревле смущают; и смущают вполне обоснованно: ведь мы инвалидируем this до того, как умер последний владеющий указатель.
(А this-указатели — очевидно, владеющие, хотя и не блюдущие конвенцию о подсчёте ссылок или там эстафете. Их право владения — прирождённое).

Посему, если есть такая возможность, надо просто согласовать фактическое и конвенциальное владение объектом.
Например, если у нас используется подсчёт ссылок, то мы договариваемся о следующем:
— самая внешняя вызывающая сторона (там, где написано obj->foo()) хранит этот obj как конвенциальный владеющий указатель — например, CComPtr<Obj> obj;
— тем самым, все внутренние this-указатели (в том числе, из-за вложенных this->bar() == bar()) находятся под прикрытием этого конвенциального;
— харакири может произойти только в короткой и выверенной цепочке методов (а лучше всего — в одном методе Release безо всяких вложенностей), вызванной непосредственно из конвенциального указателя.

Но, как говорится, жизнь — сложная штука.

Харакири встречаются в оконных системах (в частности, MFC и VCL), где системный объект-окно (HWND) или поток (HANDLE) владеет своим пользовательским объектом-представителем (CWnd или CWinThread).
И предположительно (по задумке фреймворка и недогляду программиста — см. CWinThread::m_bAutoDelete), владеет им монопольно.
Поэтому там практикуется харакири объекта из функции деструктора окна — WM_DESTROY => CWnd::OnDestroy() и по выходу из THREADPROC.
Это порочная практика, поскольку DestroyWindow может произойти откуда-нибудь из недр цепочки вложенных обработчиков сообщений (методов этого самого объекта-представителя). Да и просто, внешняя ссылка на объект-представитель оказывается внезапно разрушенной.

Известных способов борьбы — два.

1) Чистота рук. Запрет на вложенные сообщения, если они могут вести к уничтожению окна. (Это и без представителей-то плохо — когда по выходу из SendMessage обнаруживаешь, что твой HWND инвалидировался). Только пинг-понг. Спросил-ответил.
Для простых объектов (для контролов, например) вполне пригодно.

2) Создание пула свежеумирающих объектов. Харакири-метод не делает delete this, а помещает this в пул. Затем приложение в подходящий момент времени — когда заведомо нет this-ссылок (например, в CWinApp::OnIdle) удаляет содержимое пула.
Кстати говоря, подобный подход практикуется в Objective C с его весьма забавной смолтокоподобной системой владения.

И кстати, тот же MFC практикует создание заведомо временных объектов (например, CWnd::FromHandle(hwnd) или там CGdiObj::FromHandle()) с немедленным помещением в пул смерти.
Это удобно — не надо заботиться об освобождении. Только нельзя просто так запоминать такие указатели, нужно вместо FromHandle сконструировать собственный объект и аттачить хэндл.
Перекуём баги на фичи!
Re[3]: вопрос
От: Erop Россия  
Дата: 26.08.10 19:29
Оценка:
Здравствуйте, IMar, Вы писали:

IM>Вызов нестатического метода DoSomething означает, что в метод неявно передаётся указатель на текущий объект this. Но автор пишет что там нет обращения к полям класса — this разыменовываться не будет. Так что нестатическая Функция здесь нормально работает, я проверил на всякий случай


Нет гарантий.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: вопрос
От: IMar Россия  
Дата: 27.08.10 09:10
Оценка:
Здравствуйте, dilmah, Вы писали:
D>это не отменяет UB.
Есть ссылка на стандарт где это было бы сказано?
Re[5]: вопрос
От: dilmah США  
Дата: 27.08.10 10:02
Оценка: 2 (1)
D>>это не отменяет UB.
IM>Есть ссылка на стандарт где это было бы сказано?

После деаллокации памяти этот указатель нельзя использовать вообще никак, то есть даже читать этот указатель, передавать его как аргумент функции.

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

3.8 — Object Lifetime [basic.life]
..........................
The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (class.dtor), the destructor call starts, or
the storage which the object occupies is reused or released.
..........................
-5- ........................ or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer
that refers to the storage location where the object will be or was located may be used but only in limited ways. Such a pointer refers to allocated storage (basic.stc.dynamic.deallocation),
and using the pointer as if the pointer were of type void*, is well-defined. Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below.
........................ If the object will be or was of a non-POD class type, the program has undefined behavior if:
the pointer is used to access a non-static data member or call a non-static member function of the object, or ..............

Re[6]: вопрос
От: IMar Россия  
Дата: 27.08.10 12:01
Оценка:
Да, всё верно
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.