Domanda

Ho una classe (classe A) progettata per essere ereditata da altre classi scritte da altre persone. Ho anche un'altra classe (classe B), che eredita anche da A.

B deve accedere ad alcune funzioni membro di A alle quali non dovrebbero accedere altre classi ereditarie.

Quindi, le funzioni di questi membri di A dovrebbero essere pubbliche per B, ma private per gli altri.

Come posso risolverlo senza usare la direttiva "amico"?

Grazie.

EDIT: esempio del perché ne ho bisogno.

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!
  }
};
È stato utile?

Soluzione

Quello che dici è: ci sono due set di sottoclassi di A. Un set dovrebbe avere accesso, l'altro no. È sbagliato avere una sola marca di sottoclassi (ovvero B) "vedere" i membri di A.

Se ciò che vuoi dire è: solo noi possiamo usare questa parte della funzionalità, mentre i nostri clienti non possono, ci sono altri resort.

(Il riutilizzo della funzionalità per ereditarietà spesso ti mette di fronte a questo tipo di problemi. Se vai al riutilizzo per aggregazione, potresti aggirarlo.)

Un suggerimento:

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

Ma sarebbe meglio seguire la strada dell'aggregazione e dividere l'attuale " A " in una parte visibile e invisibile:

class InvisibleFunctionality {
};

class VisibleFunctionality {
};

class B {
    InvisibleFunctionality m_Invisible;
    VisibleFunctionality m_Visible;
};

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

Altri suggerimenti

Non puoi. Ecco a cosa serve un amico.

Un'alternativa sarebbe cambiare il design / l'architettura del tuo programma. Ma per suggerimenti su questo avrei bisogno di un po 'più di contesto.

Bene - se vuoi esattamente quello che hai descritto, allora amico è la soluzione migliore. Ogni standard di codifica raccomanda di non usare amico ma dove il design alternativo è più complesso - quindi forse vale la pena fare un'eccezione.

Per risolvere il problema senza amici sarà necessaria un'architettura diversa

Una soluzione potrebbe essere quella di utilizzare una forma del pImpl idiom da cui deriva la "B" dall'oggetto di implementazione interno, mentre gli altri client derivano dalla classe esterna.

Un altro potrebbe essere quello di posizionare un ulteriore livello di ereditarietà tra 'A' e il " altri client " ;. Qualcosa del tipo:

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

Tuttavia, questa soluzione presenta ancora problemi. 'ARestricted' non può essere convertito in 'A', quindi questo dovrebbe essere risolto da qualche altro & Quot; getter & Quot; per un'. Tuttavia, è possibile denominare questa funzione in modo tale che non possa essere richiamata accidentalmente:

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

Dopo aver provato a pensare ad altre soluzioni e supponendo che la tua gerarchia debba essere come descrivi, ti consiglio di usare amico !

EDIT: Quindi xtofl ha suggerito di rinominare i tipi da 'A' a 'AInternal' e "Limitato" a "A".

Funziona, tranne che ho notato che 'B' non sarebbe più una 'A'. Tuttavia, AInternal potrebbe essere ereditato praticamente - e quindi 'B' potrebbe derivare sia da 'AInternal' che da '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);
}

Ovviamente ora hai basi virtuali e eredità multiple! Hmmm .... ora, è meglio o peggio di una singola dichiarazione amico ?

Penso che tu abbia un problema più grande qui. Il tuo design non sembra sano.

1) Penso che il costrutto "amico" sia problematico per cominciare

2) se 'amico' non è quello che vuoi, devi riesaminare il tuo progetto.

Penso che sia necessario fare qualcosa che porti a termine il lavoro, usando "amico" o sviluppare un'architettura più solida. Dai un'occhiata a alcuni modelli di progettazione , sono sicuro che troverai qualcosa di utile.

EDIT:

Dopo aver visto il tuo codice di esempio, devi assolutamente effettuare nuovamente l'archiviazione. La Classe A potrebbe non essere sotto il tuo controllo, quindi è un po 'complicato, ma forse vorrai che tu voglia ripetere la Classe B per essere un & Quot; has-a & Quot; class invece di un " is-a " 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
    }

};

Trovo che questa sia una sfida interessante. Ecco come risolverei il problema:

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

Se hai intenzione di utilizzare questo tipo di pattern per tutto il tempo, puoi ridurre il codice usando i template.

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

Se capisco:

  • A verrà suddiviso in sottoclassi da altri sviluppatori.
  • B sarà sottoclassato da altri sviluppatori ed eredita da A.
  • A ha alcuni metodi che non desideri siano accessibili a sviluppatori esterni tramite B.

Non penso che questo possa essere fatto senza usare l'amico. Non ho modo di conoscere i membri di una superclasse disponibili solo per gli eredi diretti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top