Публичный и частный доступ к одним и тем же функциям-членам

StackOverflow https://stackoverflow.com/questions/504257

  •  21-08-2019
  •  | 
  •  

Вопрос

У меня есть класс (class A), который предназначен для наследования другими классами, написанными другими людьми.У меня также есть другой класс (класс B), который также наследуется от A.

B должен получить доступ к некоторым функциям-членам A, которые не должны быть доступны другим наследующим классам.

Итак, эти функции-члены A должны быть общедоступными для B, но закрытыми для других.

Как я могу решить это без использования директивы 'friend'?

Спасибо.

Редактировать:Пример, зачем мне это нужно.

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!
  }
};
Это было полезно?

Решение

То, что вы говорите, это:существует два набора подклассов A.Один набор должен иметь доступ, другой набор - нет.Кажется неправильным иметь только один бренд подклассов (т.е.Б) "увидеть" членов группы А.

Если то, что вы имеете в виду, это:Только мы может использовать это часть функциональности, в то время как наши клиенты не могут, есть другие курорты.

(Повторное использование функциональности по наследованию часто ставит вас в тупик с такого рода проблемами.Если вы перейдете к повторному использованию путем агрегирования, вы можете обойти это.)

Предложение:

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

Но лучше было бы пойти по пути агрегирования и разделить текущую букву "А" на видимую и невидимую части:

class InvisibleFunctionality {
};

class VisibleFunctionality {
};

class B {
    InvisibleFunctionality m_Invisible;
    VisibleFunctionality m_Visible;
};

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

Другие советы

Ты не можешь.Вот для чего нужен друг.

Альтернативой могло бы быть изменение дизайна / архитектуры вашей программы.Но для подсказок по этому поводу мне понадобилось бы немного больше контекста.

Что ж, если вы хотите именно того, что вы описали, тогда друг это лучшее решение.Каждый стандарт кодирования рекомендует не использовать друг но там, где альтернативный дизайн более сложный - тогда, возможно, стоит сделать исключение.

Для решения проблемы без friend потребуется другая архитектура

Одним из решений могло бы быть использование формы Идиома прыщавости где 'B' является производным от внутреннего объекта реализации, в то время как другие клиенты являются производными от внешнего класса.

Другим вариантом может быть размещение дополнительного уровня наследования между 'A' и "другими клиентами".Что - то вроде:

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

Однако у этого решения все еще есть свои проблемы.'ARestricted' не может быть преобразован в 'A', поэтому это должно быть решено каким-либо другим "получателем" для 'A'.Однако вы могли бы назвать эту функцию таким образом, чтобы она не могла быть вызвана случайно:

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

Попытавшись придумать другие решения и предположив, что ваша иерархия должна быть такой, как вы описали, я рекомендую вам просто использовать друг!

Редактировать: Итак xtofl ( хтофл ) предложил переименовать типы 'A' в 'AInternal' и 'ARestricted' в 'A'.

Это работает, за исключением того, что я заметил, что буква "Б" больше не будет буквой "А".Однако AInternal может быть унаследован виртуально - и тогда 'B' может происходить как от 'AInternal', так и от '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);
}

Конечно, теперь у вас есть виртуальные базы и множественное наследование!Хммм .... итак, это лучше или хуже, чем одиночный друг декларация?

Я думаю, что здесь у вас проблема посерьезнее.Ваш дизайн не кажется мне удачным.

1) Я думаю, что конструкция "друг" с самого начала проблематична

2) если "друг" - это не то, что вы хотите, вам нужно пересмотреть свой дизайн.

Я думаю, вам либо нужно сделать что-то, что просто выполняет работу, используя "друг", либо разработать более надежную архитектуру.Взгляните на некоторые шаблоны проектирования, Я уверен, вы найдете что-нибудь полезное.

Редактировать:

После просмотра вашего примера кода вам определенно нужно переделать архив.Класс A может быть не под вашим контролем, так что это немного сложно, но, возможно, вы захотите переделать класс B, чтобы он был классом "has-a" вместо класса "is-a".

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
    }

};

Я нахожу это интересной задачей.Вот как я бы решил эту проблему:

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

Если вы собираетесь использовать такого рода шаблоны постоянно, вы могли бы сократить объем кода с помощью шаблонов.

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

Если я понимаю:

  • A будет переведен в подкласс другими разработчиками.
  • B будет подклассом других разработчиков и наследуется от A.
  • У A есть некоторые методы, которые вы не хотите, чтобы внешние разработчики получали доступ через B.

Я не думаю, что это можно сделать без использования friend .Я не знаю способа сделать членов суперкласса доступными только для прямых наследников.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top