跳到內容

更多 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)
華夏公益教科書