Подтвердить что ты не робот

Предоставление классу "друг" доступа только к некоторым частным членам

Предположим, что у меня есть три класса С++ FooA, FooB и FooC.

FooA имеет функцию-член с именем Hello, я хочу вызвать эту функцию в классе FooB, но я не хочу, чтобы класс FooC мог ее вызвать. Лучший способ понять это - объявить FooB как класс друзей FooA. Но пока я это делаю, все частные и защищенные члены FooA будут выставлены, что для меня совершенно неприемлемо.

Итак, я хочу знать, есть ли какой-либо механизм в С++ (03 или 11) лучше класса friend, который может решить эту дилемму.

И я предполагаю, что будет хорошо, если возможен следующий синтаксис:

class FooA
{
private friend class FooB:
    void Hello();
    void Hello2();
private:
    void Hello3();
    int m_iData;
};

class FooB
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // right
        objA.Hello2() // right
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error
    }
};
4b9b3361

Ответ 1

Нет ничего, чтобы сделать класс другом одной конкретной функции, но вы можете сделать FooB другом класса "ключ" с частным конструктором, а затем FooA::Hello взять этот класс в качестве игнорируемого параметра. FooC не сможет предоставить параметр и, следовательно, не может вызвать Hello:

Является ли этот ключ-ориентированный шаблон защиты доступа известной идиомой?

Ответ 2

Я думаю, вы можете использовать Attorney-Client здесь.

В вашем примере пример должен выглядеть следующим образом

class FooA
{
private:
    void Hello();
    void Hello2();
    void Hello3();
    int m_iData;

    friend class Client;
};

class Client
{
private:
   static void Hello(FooA& obj)
   {
      obj.Hello();
   }
   static void Hello2(FooA& obj)
   {
      obj.Hello2();
   }
   friend class FooB;
};

class FooB
{
    void fun()
    {
        FooA objA;
        Client::Hello(objA);  // right
        Client::Hello2(objA); // right
        //objA.Hello3() // compile error
        //ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        /*FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error*/
    }
};

Ответ 3

Нет, и это не является ограничением. На мой взгляд, ограничение заключается в том, что friend - дробящее оружие для взлома вокруг недостатков дизайна - существует в первую очередь.

Ваш класс FooA не знает бизнеса о FooB и FooC и "который должен иметь возможность использовать его". У него должен быть открытый интерфейс, и не важно, кто его может использовать. Это точка интерфейса! Функции вызова внутри этого интерфейса всегда должны оставлять FooA в приятном, безопасном, счастливом, согласованном состоянии.

И если вы обеспокоены тем, что вы случайно можете использовать интерфейс FooA откуда-то, на что вы этого не хотели, ну просто не делайте этого; С++ не является языком, подходящим для защиты от подобных ошибок пользователя. В этом случае ваше тестовое покрытие должно быть достаточным.

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

Если это проблема для семантики вашего дизайна программы, я вежливо предлагаю, чтобы ваш дизайн имел недостаток.

Ответ 4

Самое безопасное решение - использовать другой класс как "промежуточный" для ваших двух классов, а не сделать один из них. friend. Один из способов сделать это предлагается в ответе @ForEveR, но вы можете также можно найти некоторые прокси-классы и другие шаблоны проектирования, которые могут применяться.

Ответ 5

Вы можете частично открыть интерфейсы класса для указанного клиента, наследуя его от класса интерфейса.

class FooA_for_FooB
{
public:
    virtual void Hello() = 0;
    virtual void Hello2() = 0;
};

class FooA : public FooA_for_FooB
{
private: /* make them private */
    void Hello() override;
    void Hello2() override;
private:
    void Hello3();
    int m_iData;
};

class FooB
{
    void fun()
    {
        FooA objA;
        FooA_for_FooB &r = objA;
        r.Hello()  // right
        r.Hello2() // right
        objA.Hello3() // compile error
        objA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        objA.m_iData = 0; // compile error
    }
};

Здесь управление доступом расширен базовым классом FooA_for_FooB. По ссылке типа FooA_for_FooB, FooB может получить доступ к членам, определенным в FooA_for_FooB. Однако FooC не может получить доступ к этим элементам, поскольку они были переопределены как частные члены в FooA. Ваша цель может быть достигнута путем использования типа FooA_for_FooB внутри FooC или любых других мест, кроме FooB, которые можно сохранить, не обращая особого внимания.

Этот подход не нуждается в friend, что делает вещи простыми.

Аналогичную вещь можно сделать, сделав все частным в базовом классе и выборочно обернув и выставив некоторые из членов как общедоступные в производном классе. Однако этот подход может иногда требовать уродливого понижения. (Потому что базовый класс станет "валютой" среди всей программы.)

Ответ 6

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

Есть два способа, которыми вы могли бы быть более конкретными в том, что вы показываете:

  • Наследовать от FooA, таким образом, открываются только защищенные и общедоступные методы.

  • Подружитесь только с определенным методом, таким образом, только этот метод будет иметь доступ:

.

 friend void FooB::fun();

Ответ 7

Вам понадобится наследование. Попробуйте следующее:

// _ClassA.h
class _ClassA
{
  friend class ClassA;
private:
  //all your private methods here, accessible only from ClassA and _ClassA.
}

// ClassA.h
class ClassA: _ClassA
{
  friend class ClassB;
private:
  //all_your_methods
}

Таким образом у вас есть: ClassB - единственный, который сможет использовать ClassA. ClassB не может получить доступ к _ClassA методам, которые являются частными.