Безопасно ли использовать указатель «this» в списке инициализации? - PullRequest
48 голосов
/ 20 февраля 2011

У меня есть два класса с отношением родитель-потомок (класс Parent "has-a" Child класс), а класс Child имеет указатель обратно на Parent.Было бы неплохо инициализировать родительский указатель при создании дочернего элемента следующим образом:

class Child;
class Parent;

class Child
{
public:
 Child (Parent* parent_ptr_) : parent_ptr(parent_ptr_) {};

private:
 Parent* parent_ptr;
};

class Parent
{
public:
    Parent() : child(this) {};

private:
    Child child;
}

Теперь я знаю, что люди рекомендуют не использовать this в списке инициализации и C ++ FAQ говорит, что я получу предупреждение компилятора (кстати, на VS2010, я не получаю предупреждение), но мне действительно нравится это лучше, чем вызов некоторой функции set в конструкторе Parent.Мои вопросы:

  • Четко ли определен родительский указатель this при создании объекта Child?
  • Если так, почему это считается плохой практикой использованияэто как указано выше?

Спасибо,

Боаз

РЕДАКТИРОВАТЬ: Спасибо Тимбо, это действительно дубликат (да, я даже выбрал те же имена классов).Итак, давайте добавим некоторую ценность: как насчет ссылок?Возможно ли / безопасно ли сделать следующее?:

class Child
{
public:
 Child (Parent& parnet_ptr_) : parent_ptr(parent_ptr_) {};

private:
 Parent* parent_ptr;
};

class Parent
{
public:
    Parent() : child(*this) {};

private:
    Child child;
}

Ответы [ 3 ]

47 голосов
/ 20 февраля 2011

Да.Безопасно использовать указатель this в списке инициализации , если он не используется для прямого или косвенного доступа к неинициализированным элементам или виртуальным функциям , поскольку объект еще не полностью создан.Объект child может хранить указатель this Parent для дальнейшего использования!

11 голосов
/ 20 февраля 2011

Указатель родителя this, в «терминах указателя», является четко определенным (в противном случае откуда родительский конструктор узнает, над каким экземпляром он работает?), Но:

  • поля, объявленные после объекта Child, еще не инициализированы;
  • код в конструкторе еще не запущен;
  • также, обычные предупреждения ос помощью виртуальных членов из конструктора примените 1 .

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

Тем не менее, если весь дочерний объект делает с указателем родителя в его конструкторе, это сохранить его для использования позже (=> когдаэто будет действительно построено), в этом нет ничего плохого.

  1. Т.е. виртуальная диспетчеризация не работает в конструкторах, потому что vtable еще не был обновлен конструктором производного класса.См. Например здесь .
4 голосов
/ 20 февраля 2011

Поведение четко определено до тех пор, пока вы не попытаетесь разыменовать указатель до тех пор, пока после объект Parent не будет полностью построен (как говорит @Sergey в комментарии ниже, если конструируемый объект фактически получается из Parent, тогда все его конструкторов должны быть завершены).

...