2010-05-04 10 views
2

dérivée Disons que j'ai la hiérarchie de classe suivante:Friendness et classe

class Base 
{ 
    protected: 

    virtual void foo() = 0; 

    friend class Other; 
}; 

class Derived : public Base 
{ 
    protected: 

    void foo() { /* Some implementation */ }; 
}; 

class Other 
{ 
    public: 

    void bar() 
    { 
     Derived* a = new Derived(); 

     a->foo(); // Compiler error: foo() is protected within this context 
    }; 
}; 

Je suppose que je pourrais changer trop a->Base::foo() mais depuis foo() est virtuelle pure dans la classe Base, l'appel se traduira par l'appel Derived::foo() de toute façon .

Cependant, le compilateur semble refuser a->foo(). Je suppose que c'est logique, mais je ne peux pas vraiment comprendre pourquoi. Est-ce que je manque quelque chose? Ne peut pas (ne devrait pas) gérer ce cas particulier?

Merci.

Répondre

6

Lorsque vous qualifiez un nom de méthode avec un nom de classe, comme dans Base::foo() expédition dynamique (reliure exécution) ne sont pas applicables. Il toujours appelez l'implémentation Base de foo(), peu importe si foo() est virtuel ou non. Puisque dans ce cas il est purement virtuel, il n'y a pas d'implémentation et le compilateur se plaint.

Votre deuxième problème est qu'en C++, l'amitié n'est pas héritée. Si vous voulez Other avoir un accès spécial à Derived, il doit être un ami de Derived spécifiquement.

Ceci, d'autre part, fonctionne:

Base* a = new Derived(); 

a->foo(); 

Parce qu'ici, vous appellent foo() sur un Base*foo() est publique, et puisque vous n'êtes pas qualifiez foo() avec un nom de classe, il utilise dynamique envoyer et finit par appeler la version Derived de Foo.

+0

En fait, quand je spécifie 'a-> Base :: foo()' le compilateur semble correct avec lui. Es-tu sûr de ça ? – ereOn

+0

Êtes-vous allé jusqu'au bout de la liaison? L'éditeur de liens doit se plaindre d'une référence indéfinie à la méthode 'Base :: foo'. –

+0

Je n'étais pas à l'étape de la liaison, vous avez raison. Merci pour la méthode alternative/plus correcte. – ereOn

0

Essayez de mettre cette "classe d'amis Autre;" dans la classe dérivée. Mise à jour: Maintenant, pensez-y, je suis d'accord avec Tyler que vous devriez changer un à un pointeur de base.

Base* a = new Derived(); 
+0

Merci. Je voudrais l'éviter (si je peux). – ereOn

0

Cependant, le compilateur semble refuser cela.

Refuser quoi? Il semble que vous disiez que le compilateur refuse d'autoriser Other à appeler la fonction foo() via un pointeur de base. Cela ne devrait certainement pas être le cas.

Pour répondre à votre question de base, l'amitié n'est pas héritée .... période. La portée des autorisations est vérifiée au même stade que la résolution des noms et puisque foo() est protégé dans les noms que vous utilisez, vous ne pouvez pas l'appeler.

Le polymorphisme d'une part est résolu par la redirection de pointeur et n'a rien à voir avec la résolution de nom ou l'autorisation d'accès.

+0

Mon mauvais ... J'ai édité ma question et enlevé l'ambiguïté concernant "cela" qui était destiné à se référer à 'a-> foo()'. – ereOn

1

Je suppose que vous pouvez le faire

void bar() 
{ 
    Base* a = new Derived(); 

    a->foo(); 
}; 
0

Il est regrettable, mais la convivialité est intrinsèquement brisé en C++ à mon avis:

  • Non hérité
  • Donner un accès illimité à tous les internes, aucune possibilité de le limiter

J'ai abandonné l'utilisation "as- est "et j'utilise maintenant principalement le modèle Key (faute d'un meilleur nom).

/// 
/// Key definition 
/// 
class Friend; 

class FriendKey: boost::noncopyable { friend class Friend; FriendKey() {} }; 

/// 
/// Base/Derived definition 
/// 
class Base 
{ 
public: 

    void mySpecialMethod(const FriendKey&) { this->mySpecialMethodImpl(); } 

private: 
    virtual void mySpecialMethodImpl() = 0; 
}; // class Base 

class Derived: public Base 
{ 
public: 

private: 
    virtual void mySpecialMethodImpl() {} 
}; // class Derived 

/// 
/// Friend definition 
/// 
class Friend 
{ 
public: 
    void mySpecialCall() 
    { 
    Derived d; 
    d.mySpecialMethod(FriendKey()); 
    } 
}; // class Friend 

Le concept est simple: chaque classe déclare une clé (possible même dans l'en-tête en avant), et ceux qui souhaitent accorder un accès spécial à ne leur permettra à cette clé.

Ce n'est pas parfait, car on peut bien sûr en abuser (par transitivité de la touche). Mais alors en C++ vous pouvez tout abuser, c'est donc plus un problème de protection contre Murphy que de Machiavel.