В C ++ инициализируйте член класса указателем this во время создания - PullRequest
9 голосов
/ 24 октября 2010

Я хотел бы создать класс, который связан с другим классом в каком-то роде-родительских отношениях.Для этого «дочернему» классу нужна ссылка на его родителя.

Например:

template <typename T>
class TEvent {
    private: T* Owner;
    public: TEvent(T* parent) : Owner(parent) {}
};

class Foo {
    private: TEvent<Foo> Froozle; // see below
};

Теперь проблема в том, что я не могу ни инициализировать экземпляр Froozle напрямую, нииспользуя список экземпляров конструктора Foo, потому что this ссылки там не разрешены.Помимо добавления другого метода setParent(T*) (который мне не очень нравится, потому что это означает, что я должен оставить экземпляр TEvent<> в недопустимом состоянии), есть ли способ достичь этого?

Ответы [ 5 ]

14 голосов
/ 24 октября 2010

Можно использовать this в списке инициализации, если оно не используется для доступа к любым элементам, которые еще не были инициализированы.

11 голосов
/ 24 октября 2010

Из стандарта 12.6.2 / 7 «Инициализация баз и элементов» (выделено мое):

Имена в списке выражений mem-initializer оцениваются в области видимости конструктора.для которого указана инициализация mem.

[Пример:

class X {
    int a;
    int b;
    int i;
    int j;

public:
    const int& r;
    X(int i): r(a), b(i), i(i), j(this->i) {}
};

инициализирует X::r для ссылки X::a, инициализирует X::b значением параметра конструктора i, инициализирует X::iзначение параметра конструктора i и инициализирует X::j значением X::i;это происходит каждый раз, когда создается объект class X.]

[Примечание: поскольку mem-initializer оцениваются в области видимости конструктора, указатель this можно использовать в списке выражений mem-initializer для ссылки на объектинициализируется.]

2 голосов
/ 24 октября 2010

Я не думаю, что это не сработает на вас, если вы не установили уровень предупреждения на 4 (или похожий, я полагаю, Visual Studio) и не включили «обрабатывать предупреждения как ошибки».предупреждение - это хорошо, так как оно не позволит вам случайно использовать указатель this, когда то, на что он указывает, еще не построено.

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

Вы можете избавиться от этого (опять же, предполагая Visual Studio), украшая конструктор (еслиопределяется в объявлении класса - тогда вы должны украсить весь класс):

// warning C4355: 'this' : used in base member initializer list
#pragma warning (push)
#pragma warning (disable : 4355)
some_class::some_class()
: ...
{
}
#pragma warning (pop)
2 голосов
/ 24 октября 2010

Это должно работать;на самом деле,

template<class T>
class Child {
private:
    T *parent;
public:
    Child(T *parent) : parent(parent) {}
};
class Parent {
private:
    Child<Parent> child;
public:
    Parent() : child(this) {}
};

прекрасно компилируется для меня как с g ++ 4.4.5, так и clang ++ 2.8.

Что у вас не получается?

1 голос
/ 24 октября 2010

Если вы хотите подавить предупреждение, просто сделайте это:

class Foo
{
public:
    Foo() :
    Froozle(get_this())
    {}

private:
    Foo* get_this()
    {
        return this;
    }

    TEvent<Foo> Froozle; // see below
};

Направление достаточно, чтобы остановить его.

...