我一直在阅读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指针,以便能够使用数据成员指针访问派生类的数据字段。
其他回答
我发现了一个使用好友访问的方便地方:私有函数的Unittest。
好友对于回调也很有用。可以将回调函数作为静态方法来实现
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;
}
这样就更容易隐藏下游不需要看到的东西。
您使用私有/受保护/公共权限控制成员和函数的访问权限? 所以假设这3个层面中的每一个都很清楚,那么很明显我们遗漏了一些东西……
例如,将成员/函数声明为protected是非常通用的。你的意思是,这个功能对每个人来说都是遥不可及的(当然,继承的孩子除外)。但是异常呢?每个安全系统都让你有某种类型的“白名单”,对吗?
所以,“朋友”让你拥有坚如磐石的对象隔离的灵活性,但也允许为你认为合理的事情创造一个“漏洞”。
我想人们之所以说不需要它,是因为总有一种设计可以不需要它。我认为这类似于讨论全局变量:你永远不应该使用它们,总有办法不用它们……但在现实中,你会看到在某些情况下,这是(几乎)最优雅的方式……我认为朋友之间也是如此。
它实际上没有任何好处,除了让你在不使用设置函数的情况下访问成员变量
但这并不是正确的看法。 其理念是控制WHO可以访问什么,是否有设置功能与此关系不大。
树的例子是一个很好的例子: 在一些不同的类中实现一个对象 具有继承关系。
也许您还需要它具有一个构造函数protected和force 人们用你的“朋友”工厂。
... 好吧,坦白地说,没有它你也能生活。
在我之前工作过的一家公司里,我们遇到了一个有趣的问题,我们用朋友来产生良好的影响。我在我们的框架部门工作,我们在自定义操作系统上创建了一个基本的引擎级系统。在内部我们有一个类结构:
Game
/ \
TwoPlayer SinglePlayer
所有这些类都是框架的一部分,由我们的团队维护。该公司所制作的游戏便是基于这一源自games子游戏的框架。问题是Game有各种单人和双人玩家需要访问的东西的接口,但我们不想在框架类之外公开。解决方案是将这些接口设为私有,并允许双人和单人玩家通过友谊访问它们。
事实上,这整个问题本可以通过更好地实施我们的系统来解决,但我们被锁定在我们所拥有的东西上。