更多 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.
};
提供对成员子集的选择性访问是可取的,因为如果需要,剩余的私有成员可以在不破坏客户端代码的情况下更改接口。它有助于减少类之间的耦合。委托代理习语允许类精确地控制它们给予其朋友的访问权限。
委托代理习语通过添加一层间接性来实现。想要控制对其内部细节的访问的客户端类,会指定一个委托代理并将其设为friend
。Attorney
类经过精心设计,充当Client
的代理。与典型的代理类不同,Attorney
只复制Client
的私有接口的一个子集。例如,假设类 Client
想要控制对其实现细节的访问。Client
希望其Attorney
只提供对Client::A
和Client::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
。如果没有Attorney
,Client
将会声明相同的一组朋友,给予它们不受限制地访问Client
的内部细节的权限。
可以有多个委托代理类,提供对客户端实现细节的不同子集的访问权限。例如,类 AttorneyC
可以仅提供对 Client::C
方法的访问权限。一个有趣的情况出现时,委托代理类充当多个不同类的中介,并提供对它们实现细节的紧密访问权限。这种设计在继承层次结构的情况下是可行的,因为 C++ 中的友元不可继承,但是如果可以访问基类的私有虚函数,则可以调用派生类中的私有虚函数重写。在以下示例中,委托代理习语应用于类 Base
和 main
函数。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)