2010-04-30 9 views

Répondre

7

Vous ne pouvez pas utiliser raisonnablement les références comme variable d'instance car il est impossible d'initialiser les variables d'instance et les références ne peuvent pas être réinstallées.

Une alternative pourrait être simplement d'utiliser des pointeurs (éventuellement intelligents) à la place.

Une autre possibilité que vous se rapproche de C++ - comme le comportement est d'utiliser un membre de style PIMPL pour vos membres C++:

struct CppImpl { 
    SomeClass& ref; 
    CppImpl(SomeClass& ref) : ref(ref) {} 
}; 

@interface A : NSObject { 
    CppImpl* pimpl;  
} 
- (id)initWithRef:(SomeClass&)ref; 
@end 

@implementation  
- (id)initWithRef:(SomeClass&)ref { 
    if(self = [super init]) { 
     pimpl = new CppImpl(ref); 
    } 
    return self; 
} 
// clean up CppImpl in dealloc etc. ... 
@end 
+0

ouais, c'est tout! –

+0

Je ne vois pas pourquoi vous ne stockez pas simplement un pointeur sur SomeClass lui-même. La différence entre une référence et un pointeur est (surtout) une sémantique que votre interface transporte parfaitement. Vois ma réponse. – Sebastian

+0

@Sebastian Ceci est discutable si vous init avec un seul membre (à quelle fréquence avez-vous seulement un membre?). Cela devient intéressant si vous initiez avec plus d'un membre et souhaitez revenir à un comportement déterministe correct. FWIW, j'ai mentionné les pointeurs et cette approche comme options possibles. –

0

boost :: ref() peut vous aider?

+0

Pas vraiment - 'reference_wrapper ' n'a pas de constructeur par défaut et vous ne pouvez pas initialiser les variables d'instance. –

0

Une solution plus générique est d'utiliser reference_wrapper<T> au lieu d'un struct personnalisé. Le résultat final est similaire.

Ensuite, si vous avez seulement besoin de stocker un membre, vous n'obtenez pas beaucoup d'avantages sur les pointeurs en utilisant la structure ou ce wrapper. (Merci Georg!)

Je l'ai utilisé la réponse de Georg comme point de départ pour l'exemple:

// This bare interface can be used from regular Objective-C code, 
// useful to pass around as an opaque handle 
@interface A : NSObject 
@end 

// This interface can be shown to appropriate Objective-C++ code 
@interface A (Private) // @interface A() if it's just for this class's .mm file 
- (id)initWithRef:(SomeClass &)ref; 
@property (readonly, nonatomic) SomeClass &ref; 
@end 


@implementation A { 
    reference_wrapper<SomeClass> *_refWrapper;  
} 

- (id)init { 
    // and/or throw an exception 
    return nil; 
} 

- (id)initWithRef:(SomeClass &)ref { 
    self = [super init]; 
    if(self) { 
     _refWrapper = new reference_wrapper<SomeClass>(ref); 
    } 
    return self; 
} 

- (SomeClass &)ref { 
    // reference_wrapper<T> is implicitly convertible to T& 
    return *_refWrapper; 
    // This would also work: 
    // return _refWrapper->get(); 
} 

- (void)dealloc { 
    delete _refWrapper; 
} 

@end 

Ce modèle d'en-tête multiple est utile de passer autour d'une poignée opaque dans le code Objective-C, tout en fournissant Objective-C++ caractéristiques à quelques privilégiés (même si c'est juste l'implémentation de la classe objc).

+0

Pourquoi ne pas stocker un pointeur (éventuellement intelligent) directement sur l'instance ici? Je ne pense pas que l'emballage vous apporte quelque chose dans ce scénario. –

+0

Pourquoi pas en effet. En y réfléchissant, dans des situations aussi simples que l'exemple, je n'ai généralement stocké qu'un pointeur. –

2

première phrase de Georg est tout à fait correct:

Vous ne pouvez pas utiliser des références sensiblement comme variable d'instance, car il n'y a pas moyen d'initialiser les variables d'instance et les références ne peuvent pas être remis en place.

Mais je ne pense pas que sa solution soit la meilleure.

La différence sémantique entre un pointeur et une référence est faible. Une référence est essentiellement un pointeur qui ne peut pas être nul. Donc c'est certainement une bonne idée d'utiliser des références dans votre interface pour rendre évident qu'un nullptr n'est pas un argument d'initialisation valide. Mais en interne, vous pouvez simplement stocker un pointeur:

@interface A : NSObject { 
    SomeClass* p;  
} 
- (id)initWithRef:(SomeClass&)ref; 
@end 

@implementation A 
- (id)initWithRef:(SomeClass&)ref { 
    if(self = [super init]) { 
     p = &ref; 
    } 
    return self; 
} 
@end 

Il n'y a pas plus (dans le pire des cas: manuel) l'allocation de mémoire, pas la gestion des ressources du tout, pas indirection supplémentaire, etc. Chaque membre A peut affirmer simplement que p != nullptr.

+0

Affirmer dans chaque membre de A semble lourd et sujette aux erreurs, si vous pouvez préférer des modèles qui vous obligent à affirmer/vérifier une seule fois. –

+0

Ce n'est pas différent que d'affirmer que votre pointeur pimpl est toujours valide dans chaque membre. Peut-être que c'est trop paranoïaque, mais cela s'applique aux deux solutions. – Sebastian

Questions connexes