跳到内容

更多 C++ 习语/友元和委托代理

来自维基教科书,开放书籍,开放世界

委托代理

[编辑 | 编辑源代码]

控制对类的实现细节的访问粒度

C++ 中的 friend 声明提供了对类内部的完全访问权限。因此,朋友声明不受欢迎,因为它们破坏了精心设计的封装。C++ 的友元特性没有提供任何方法来选择性地授予对类私有成员子集的访问权限。C++ 中的友元是一个非此即彼的命题。例如,以下类 Foo 声明类 Bar 为其朋友。因此,类 Bar 可以访问类 Foo所有私有成员。这可能并不理想,因为它会增加耦合。类 Bar 不能在没有类 Foo 的情况下分发。

class Foo 
{
private:
  void A(int a);
  void B(float b);
  void C(double c);
  friend class Bar;
};

class Bar {
// This class needs access to Foo::A and Foo::B only.
// C++ friendship rules, however, give access to all the private members of Foo.
};

提供对成员子集的选择性访问是可取的,因为如果需要,剩余的私有成员可以在不破坏客户端代码的情况下更改接口。它有助于减少类之间的耦合。委托代理习语允许类精确地控制它们给予其朋友的访问权限。

解决方案和示例代码

[编辑 | 编辑源代码]

委托代理习语通过添加一层间接性来实现。想要控制对其内部细节的访问的客户端类,会指定一个委托代理并将其设为friendAttorney 类经过精心设计,充当Client 的代理。与典型的代理类不同,Attorney 只复制Client 的私有接口的一个子集。例如,假设类 Client 想要控制对其实现细节的访问。Client 希望其Attorney 只提供对Client::AClient::B 的访问权限。

class Client 
{
private:
  void A(int a);
  void B(float b);
  void C(double c);
  friend class Attorney;
};

class Attorney {
private:
  static void callA(Client & c, int a) {
    c.A(a);
  } 
  static void callB(Client & c, float b) {
    c.B(b);
  }
  friend class Bar;
};

class Bar {
// Bar now has access to only Client::A and Client::B through the Attorney.
};

Attorney 类将访问限制为一组紧密联系的函数。类 Attorney 的所有方法都是内联静态的,每个方法都接受对Client 实例的引用,并将函数调用转发给它。关于Attorney 的一些事情是习语化的。它的实现完全是私有的,这可以防止其他意外的类访问Client 的内部细节。Attorney 决定哪些其他类、成员函数或自由函数可以访问它。它将它们声明为friend 以允许访问它的实现以及通过它访问Client。如果没有AttorneyClient 将会声明相同的一组朋友,给予它们不受限制地访问Client 的内部细节的权限。

可以有多个委托代理类,提供对客户端实现细节的不同子集的访问权限。例如,类 AttorneyC 可以仅提供对 Client::C 方法的访问权限。一个有趣的情况出现时,委托代理类充当多个不同类的中介,并提供对它们实现细节的紧密访问权限。这种设计在继承层次结构的情况下是可行的,因为 C++ 中的友元不可继承,但是如果可以访问基类的私有虚函数,则可以调用派生类中的私有虚函数重写。在以下示例中,委托代理习语应用于类 Basemain 函数。Derived::Func 通过多态性被调用。但是,为了访问 Derived 的实现细节,可以应用相同的习语。

#include <iostream>

class Base {
private:
  virtual void Func(int x) = 0;
  friend class Attorney;
public:
  virtual ~Base() {}
};

class Derived : public Base {
private:
  virtual void Func(int x)  {
    // This is called even though main is not a friend of Derived.
    cout << "Derived::Func" << endl;
  }

public:
  ~Derived() {}
};

class Attorney {
private:
  static void callFunc(Base & b, int x) {
    return b.Func(x);
  }
  friend int main();
};

int main() {
  Derived d;
  Attorney::callFunc(d, 10);
}

已知用途

[编辑 | 编辑源代码]
[编辑 | 编辑源代码]

参考文献

[编辑 | 编辑源代码]
  • Bolton, Alan R. (01 January 2006). "友元和委托代理习语 - Dr. Dobb's". {{cite web}}: Check date values in: |date= (help)
华夏公益教科书