Question

J'ai une classe (classe A) conçue pour être héritée par d'autres classes écrites par d'autres personnes.J'ai aussi une autre classe (classe B), qui hérite également de A.

B doit accéder à certaines fonctions membres de A auxquelles les autres classes héritières ne devraient pas accéder.

Ainsi, les fonctions membres de A doivent être publiques pour B, mais privées pour les autres.

Comment puis-je le résoudre sans utiliser la directive « ami » ?

Merci.

MODIFIER:Exemple pourquoi j'en ai besoin.

class A
{
public:
  void PublicFunc()
  {
    PrivateFunc();
    // and other code
  }
private:
  virtual void PrivateFunc();
};

class B : public class A
{
private:
  virtual void PrivateFunc()
  {
    //do something and call A's PrivateFunc
    A::PrivateFunc(); // Can't, it's private!
  }
};
Était-ce utile?

La solution

Ce que tu dis c'est :il existe deux ensembles de sous-classes de A.Un ensemble devrait avoir accès, l’autre non.Cela semble mal d'avoir une seule marque de sous-classes (c'est-à-direB) « voir » les membres de A.

Si ce que tu veux dire c'est :seulement nous peut utiliser ce une partie de la fonctionnalité, alors que nos clients ne le peuvent pas, il existe d'autres stations.

(La réutilisation des fonctionnalités par héritage vous confronte souvent à ce genre de problèmes.Si vous optez pour la réutilisation par agrégation, vous risquez de le contourner.)

Une suggestion:

// separate the 'invisible' from the 'visible'.
class A_private_part {
protected: 
  int inherited_content();
public:
  int public_interface();          
};

class B_internal : public A_private_part {
};

class A_export : private A_private_part {
public:
    int public_interface() { A_private_part::public_interface(); }
};

// client code
class ClientClass : public A_export {
};

Mais il serait préférable de suivre la voie de l'agrégation et de diviser le "A" actuel en une partie visible et une partie invisible :

class InvisibleFunctionality {
};

class VisibleFunctionality {
};

class B {
    InvisibleFunctionality m_Invisible;
    VisibleFunctionality m_Visible;
};

// client code uses VisibleFunctionality only
class ClientClass {
    VisibleFunctionality m_Visible;
};

Autres conseils

Vous ne pouvez pas. C’est à ça que sert ami.

Une alternative serait de changer la conception / architecture de votre programme. Mais pour obtenir des conseils à ce sujet, il me faudrait un peu plus de contexte.

Bien - si vous voulez exactement ce que vous avez décrit, alors ami est la meilleure solution. Toutes les normes de codage recommandent de ne pas utiliser friend , mais si la conception alternative est plus complexe, il peut être utile de faire une exception.

Pour résoudre le problème sans ami, il faudra une architecture différente

Une solution pourrait consister à utiliser un formulaire de idiome de pImpl d'où dérive 'B' à partir de l'objet d'implémentation interne, tandis que les autres clients proviennent de la classe externe.

Une autre solution consisterait à placer une couche d'héritage supplémentaire entre "A" et les & "autres clients &"; Quelque chose comme:

class A {
public:
  void foo ();
  void bar ();
};

class B : public A {  // OK access to both 'foo' and 'bar'
};

class ARestricted : private A {
public:
  inline void foo () { A::foo (); };    // Forwards 'foo' only
};

Cependant, cette solution a toujours ses problèmes. 'ARestricted' ne peut pas se convertir en 'A', ce problème devrait donc être résolu par un autre & "Getter &"; pour un'. Cependant, vous pouvez nommer cette fonction de manière à ne pas pouvoir être appelée accidentellement:

  inline A & get_base_type_A_for_interface_usage_only () { return *this; }

Après avoir essayé d’autres solutions et en supposant que votre hiérarchie doit être conforme à votre description, je vous recommande d’utiliser simplement ami !

MODIFIER: xtofl a donc suggéré de renommer les types 'A' en 'AInternal' et «Sans restriction» à «A».

Cela fonctionne, sauf que j'ai remarqué que "B" ne serait plus un "A". Cependant, AInternal pourrait être hérité virtuellement - et ensuite 'B' pourrait dériver à la fois de 'AInternal' et de 'A'!

class AInternal {
public:
  void foo ();
  void bar ();
};

class A : private virtual AInternal {
public:
  inline void foo () { A::foo (); };    // Forwards 'foo' only
};

// OK access to both 'foo' and 'bar' via AInternal
class B : public virtual AInternal, public A {
public:
  void useMembers ()
  {
    AInternal::foo ();
    AInternal::bar ();
  }
};

void func (A const &);

int main ()
{
  A a;
  func (a);

  B b;
  func (b);
}

Bien sûr, vous avez maintenant des bases virtuelles et un héritage multiple! Hmmm .... maintenant, est-ce mieux ou pire qu'une déclaration d'un seul ami ?

Je pense que vous avez un plus gros problème ici. Votre conception ne semble pas saine.

1) Je pense que la construction 'ami' est problématique pour commencer

2) Si 'ami' n'est pas ce que vous voulez, vous devez réexaminer votre dessin.

Je pense que vous devez soit faire quelque chose qui se contente de faire le travail, en utilisant "ami", soit développer une architecture plus robuste. Jetez un coup d'œil à certains modèles de conception , je suis sûr que vous trouverez quelque chose d'utile.

EDIT:

Après avoir vu votre exemple de code, vous devez absolument ré-archiver. La classe A n'est peut-être pas sous votre contrôle, donc c'est un peu délicat, mais vous voulez peut-être refaire la classe B pour qu'elle soit un & "A-a &"; classe au lieu d'un & "; est-un &"; classe.

public Class B
{
    B() 
    {

    }

    void someFunc()
    {
       A a; //the private functions is now called and a will be deleted when it goes out of scope
    }

};

Je trouve cela un défi intéressant. Voici comment je résoudrais le problème:

class AProtectedInterface
{
public:
    int m_pi1;
};

class B;
class A : private AProtectedInterface
{
public:
    void GetAProtectedInterface(B& b_class);

    int m_p1;
};

class B : public A
{
public:
    B();
    void SetAProtectedInterface(::AProtectedInterface& interface);

private:
    ::AProtectedInterface* m_AProtectedInterface;
};

class C : public A
{
public:
    C();
};

C::C()
{
    m_p1 = 0;
//    m_pi1 = 0; // not accessible error
}

B::B()
{
    GetAProtectedInterface(*this);

    // use m_AProtectedInterface to get to restricted areas of A
    m_p1 = 0;
    m_AProtectedInterface->m_pi1 = 0;
}

void A::GetAProtectedInterface(B& b_class)
{
    b_class.SetAProtectedInterface(*this);
}

void B::SetAProtectedInterface(::AProtectedInterface& interface)
{
    m_AProtectedInterface = &interface;
}

Si vous utilisiez ce type de modèle tout le temps, vous pourriez réduire le code à l'aide de modèles.

template<class T, class I>
class ProtectedInterfaceAccess : public I
{
public:
    void SetProtectedInterface(T& protected_interface)
    {
        m_ProtectedInterface = &protected_interface;
    }

protected:
    T& GetProtectedInterface()
    {
        return *m_ProtectedInterface;
    }

private:
    T* m_ProtectedInterface;
};

template<class T, class I>
class ProtectedInterface : private T
{
public:
    void SetupProtectedInterface(I& access_class)
    {
        access_class.SetProtectedInterface(*this);
    }
};

class Bt;
class At : public ProtectedInterface <::AProtectedInterface, Bt>
{
public:
    int m_p1;
};

class Bt : public ProtectedInterfaceAccess<::AProtectedInterface, At>
{
public:
    Bt();
};

class Ct : public At
{
public:
    Ct();
};

Ct::Ct()
{
    m_p1 = 0;
    // m_pi1 = 0; // not accessible error
}

Bt::Bt()
{
    SetupProtectedInterface(*this);

    m_p1 = 0;
    GetProtectedInterface().m_pi1 = 0;
}

Si je comprends bien:

  • A sera sous-classé par d'autres développeurs.
  • B sera sous-classé par d'autres développeurs et héritera de A.
  • A propose des méthodes que vous ne souhaitez pas rendre accessibles aux développeurs externes via B.

Je ne pense pas que cela puisse être fait sans l'aide d'un ami. Je ne connais aucun moyen de rendre les membres d'une super-classe uniquement accessibles aux héritiers directs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top