2010-10-23 19 views
6

Je voudrais créer une classe associée à une autre classe dans une sorte de relation parent-enfant. Pour cela, la classe "enfant" a besoin d'une référence à son parent.En C++, initialisez un membre de classe avec le pointeur 'this' pendant la construction

Par exemple:

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

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

Maintenant, le problème est que je ne peux pas initialiser l'instance Froozle directement, ni en utilisant la liste d'instanciation du constructeur de Foo, car this références ne sont pas autorisés là. Mis à part l'ajout d'une autre méthode setParent(T*) (que je n'aime pas trop parce que cela signifie que je dois laisser l'instance TEvent<> dans un état invalide), y at-il un moyen d'y parvenir?

Répondre

11

Il est correct d'utiliser this dans la liste d'initialisation, tant qu'il n'est pas utilisé pour accéder à des membres qui n'ont pas encore été initialisés.

+0

Que dit-il. Tant que les objets que vous passez 'this' pour" savoir "ne pas les toucher pendant leur construction (parce que l'objet' this' fait référence à n'est pas entièrement construit à ce moment), cela est bien, même si certains compilateurs (notamment VC) émet un avertissement pour cela. – sbi

+0

VC++ est le point clé ici. Cela fonctionne en effet. On dirait que je dois supprimer cette "erreur" de force. Merci beaucoup! – sunside

+1

Je souhaite que VC++ n'émette un avertissement que lorsque le membre de "this" à l'intérieur du ctor a accédé. BTW, google :: La classe LogMessage dans [google-glog] (http://code.google.com/p/google-glog) stocke "this" dans la liste mem-initializer de ctor à des fins de débogage. J'ai dû recompiler avec l'avertissement #pragma (4355: désactivé) pour le faire fonctionner dans VC2010. –

1

Ceci est supposé fonctionner; en fait,

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

compile bien pour moi avec à la fois g ++ 4.4.5 et clang ++ 2.8.

Qu'est-ce qui échoue pour vous?

+0

L'indicateur de compilation 'traiter les avertissements en tant qu'erreurs' de VC et l'en-tête de la documentation MSDN qui indique "Message d'erreur". :) Vous avez absolument raison - Cela fonctionne, je ne l'ai pas vu. Merci beaucoup! – sunside

10

de la norme 12.6.2/7 « bases et des membres Initialiser » (Souligné par l'auteur):

noms dans l'expression liste d'une mem-initialiseur sont évalués dans le cadre du constructeur pour lequel le mem-initializer est spécifié.

[Exemple:

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) {} 
}; 

initialise X::r de se référer à X::a, initialise X::b avec la valeur du paramètre de constructeur i, initialise X::i avec la valeur du paramètre de constructeur i et initialise X::j avec la valeur de X::i; ceci a lieu chaque fois qu'un objet de class X est créé. ]

[Note: parce que les mem-initialiseur sont évalués dans le cadre du constructeur , le pointeur this peut être utilisé dans l'expression liste des un mem-initialiseur de se référer à l'objet étant initialisé. ]

+1

Ça a l'air fou. : D – sunside

+0

Est-ce que cela signifie que "ceci" n'est autorisé que dans la liste mem-initializer de ctor ou est-il également autorisé dans le corps de ctor? J'ai essayé d'utiliser "ceci" dans le corps de ctor dans VC2010 ++ plusieurs fois et tout compile et fonctionne bien avec l'avertissement C4355 désactivé, mais juste parce que cela fonctionne ne signifie pas que je suis autorisé à. Jusqu'à présent, je n'en ai trouvé aucun dans le document [open-standard doc] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf) qui indique si c'est permis de se référer à "ceci" dans le corps de ctor ou non. –

+0

@DavidLee: à l'intérieur du constructeur, 'this' pointe vers l'objet en construction (comme dans la liste d'initialisation décrite ci-dessus). Vous pouvez certainement utiliser 'this' dans le corps du constructeur. –

1

Si vous cherchez à supprimer l'avertissement, faites juste ceci:

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

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

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

Le indirection suffit pour l'arrêter.

+0

Heh. Pourquoi un appel de méthode est-il correct mais une référence directe non? Compilateur idiot :) – ephemient

1

Je ne pense pas que cela échoue sur vous, sauf si vous avez le niveau d'avertissement mis à 4 (ou similaire, je suppose que Visual Studio) et ont activé "traiter les avertissements comme des erreurs".

Fondamentalement, cet avertissement est une bonne chose, car il ne vous laissera pas accidentellement utiliser le pointeurthis quand ce qu'il montre du doigt est encore à construire.

Cependant, lorsque vous savez ce que vous faites this est passé dans la liste d'initialisation, l'avertissement et l'erreur causée par ce sera ennuyeux.

Vous pouvez vous en débarrasser (encore une fois, en supposant Visual Studio) en décorant le constructeur (à moins qu'il est défini dans la déclaration de classe - alors vous devez décorer toute la classe):

// warning C4355: 'this' : used in base member initializer list 
#pragma warning (push) 
#pragma warning (disable : 4355) 
some_class::some_class() 
: ... 
{ 
} 
#pragma warning (pop)