我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
当前回答
friend关键字有很多好的用途。以下是我能立即看到的两种用法:
朋友的定义
友元定义允许在类作用域中定义函数,但该函数不会被定义为成员函数,而是被定义为外围命名空间的自由函数,并且除了依赖参数的查找之外通常不可见。这使得它对于操作符重载特别有用:
namespace utils {
class f {
private:
typedef int int_type;
int_type value;
public:
// let's assume it doesn't only need .value, but some
// internal stuff.
friend f operator+(f const& a, f const& b) {
// name resolution finds names in class-scope.
// int_type is visible here.
return f(a.value + b.value);
}
int getValue() const { return value; }
};
}
int main() {
utils::f a, b;
std::cout << (a + b).getValue(); // valid
}
私有CRTP基类
有时候,你会发现策略需要访问派生类:
// possible policy used for flexible-class.
template<typename Derived>
struct Policy {
void doSomething() {
// casting this to Derived* requires us to see that we are a
// base-class of Derived.
some_type const& t = static_cast<Derived*>(this)->getSomething();
}
};
// note, derived privately
template<template<typename> class SomePolicy>
struct FlexibleClass : private SomePolicy<FlexibleClass> {
// we derive privately, so the base-class wouldn't notice that,
// (even though it's the base itself!), so we need a friend declaration
// to make the base a friend of us.
friend class SomePolicy<FlexibleClass>;
void doStuff() {
// calls doSomething of the policy
this->doSomething();
}
// will return useful information
some_type getSomething();
};
在这个回答中,你会发现一个不做作的例子。在这个答案中使用了另一个代码。CRTP基类强制转换其this指针,以便能够使用数据成员指针访问派生类的数据字段。
其他回答
好友对于回调也很有用。可以将回调函数作为静态方法来实现
class MyFoo
{
private:
static void callback(void * data, void * clientData);
void localCallback();
...
};
回调在内部调用localCallback, clientData中有你的实例。在我看来,
还是……
class MyFoo
{
friend void callback(void * data, void * callData);
void localCallback();
}
这允许友元在cpp中被定义为c风格的函数,而不会使类变得混乱。
类似地,我经常看到的一种模式是将一个类的所有真正的私有成员放到另一个类中,该类在头文件中声明,在cpp中定义,并加为好友。这允许编码器向头文件的用户隐藏类的很多复杂性和内部工作。
在头文件中:
class MyFooPrivate;
class MyFoo
{
friend class MyFooPrivate;
public:
MyFoo();
// Public stuff
private:
MyFooPrivate _private;
// Other private members as needed
};
在cpp中,
class MyFooPrivate
{
public:
MyFoo *owner;
// Your complexity here
};
MyFoo::MyFoo()
{
this->_private->owner = this;
}
这样就更容易隐藏下游不需要看到的东西。
在做TDD的时候,我经常使用c++中的'friend'关键字。
朋友能知道我的一切吗?
更新:我从Bjarne Stroustrup网站上找到了这个关于“朋友”关键字的有价值的答案。
“好友”是一种授予访问权限的显式机制,就像会员资格一样。
在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友。
它实际上没有任何好处,除了让你在不使用设置函数的情况下访问成员变量。
我发现了一个使用好友访问的方便地方:私有函数的Unittest。
我使用friend的一个特定实例是在创建Singleton类时。friend关键字允许我创建一个访问器函数,这比总是在类上使用“GetInstance()”方法更简洁。
/////////////////////////
// Header file
class MySingleton
{
private:
// Private c-tor for Singleton pattern
MySingleton() {}
friend MySingleton& GetMySingleton();
}
// Accessor function - less verbose than having a "GetInstance()"
// static function on the class
MySingleton& GetMySingleton();
/////////////////////////
// Implementation file
MySingleton& GetMySingleton()
{
static MySingleton theInstance;
return theInstance;
}