为什么 C + + 不允许继承友谊?

为什么友谊在 C + + 中不是可选的继承?我知道由于显而易见的原因,及物性和反身性是被禁止的(我这样说只是为了避免简单的常见问题引用答案) ,但是缺少类似 virtual friend class Foo;的东西让我感到困惑。有人知道这个决定背后的历史背景吗?难道友谊真的只是一种有限的技巧,后来发现了一些鲜为人知的体面用途?

编辑以便澄清: 我说的是下面的场景,没有,A 的孩子接触到 B 或者 B 及其孩子。我还可以想象,可以选择授予对好友函数的重写权限,等等。

class A {
int x;
friend class B;
};


class B {
// OK as per friend declaration above.
void foo(A& a, int n) { a.x = n; }
};


class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };

接受的答案是: 作为 洛基说,这种效果可以通过在好友基类中创建受保护的代理函数来或多或少地模拟,因此没有严格的 需要来向类或虚方法层次结构授予友谊。我不喜欢对样板代理的需要(好友基础实际上变成了这样) ,但我认为这比一种更可能在大多数时候被误用的语言机制更可取。我认为是时候买下并阅读 Stroupstrup 的 C + + 的设计与演化了,我已经看到很多人在这里推荐它,以便更好地洞察这类问题... ..。

65413 次浏览

A guess: If a class declares some other class/function as a friend, it's because that second entity needs privileged access to the first. What use is there in granting the second entity privileged access to an arbitrary number of classes derived from the first?

Because I may write Foo and its friend Bar (thus there is a trust relationship).

But do I trust the people who write classes that are derived from Bar?
Not really. So they should not inherit friendship.

Any change in the internal representation of a class will require a modification to anything that is dependent on that representation. Thus all members of a class and also all friends of the class will require modification.

Therefore if the internal representation of Foo is modified then Bar must also be modified (because friendship tightly binds Bar to Foo). If friendship was inherited then all class derived from Bar would also be tightly bound to Foo and thus require modification if Foo's internal representation is changed. But I have no knowledge of derived types (nor should I. They may even be developed by different companies etc). Thus I would be unable to change Foo as doing so would introduce breaking changes into the code base (as I could not modify all class derived from Bar).

Thus if friendship was inherited you are inadvertently introducing a restriction on the ability to modify a class. This is undesirable as you basically render useless the concept of a public API.

Note: A child of Bar can access Foo by using Bar, just make the method in Bar protected. Then the child of Bar can access a Foo by calling through its parent class.

Is this what you want?

class A
{
int x;
friend class B;
};


class B
{
protected:
// Now children of B can access foo
void foo(A& a, int n) { a.x = n; }
};


class D : public B
{
public:
foo(A& a, int n)
{
B::foo(a, n + 5);
}
};

C++ Standard, section 11.4/8

Friendship is neither inherited nor transitive.

If friendship would be inherited, then a class that wasn't meant to be a friend would suddenly have access to your class internals and that violates encapsulation.

A derived class can inherit only something, which is 'member' of the base. A friend declaration is not a member of the befriending class.

$11.4/1- "...The name of a friend is not in the scope of the class, and the friend is not called with the member access operators (5.2.5) unless it is a member of another class."

$11.4 - "Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship."

and further

$10.3/7- "[Note: the virtual specifier implies membership, so a virtual function cannot be a nonmember (7.1.2) function. Nor can a virtual function be a static member, since a virtual function call relies on a specific object for determining which function to invoke. A virtual function declared in one class can be declared a friend in another class. ]"

Since the 'friend' is not a member of the base class in the first place, how can it be inherited by the derived class?

A friended class may expose its friend through accessor functions, and then grant access through those.

class stingy {
int pennies;
friend class hot_girl;
};


class hot_girl {
public:
stingy *bf;


int &get_cash( stingy &x = *bf ) { return x.pennies; }
};


class moocher {
public: // moocher can access stingy's pennies despite not being a friend
int &get_cash( hot_girl &x ) { return x.get_cash(); }
};

This allows finer control than optional transitivity. For example, get_cash may be protected or may enforce a protocol of runtime-limited access.

Because it's just unnecessary.

The usage of the friend keyword is itself suspicious. In term of coupling it's the worst relationship (way ahead of inheritance and composition).

Any change to the internals of a class have a risk to impact the friends of this class... do you really want an unknown number of friends ? You would not even be able to list them if those who inherit from them could be friends also, and you would run in the risk of breaking your clients code each time, surely this is not desirable.

I freely admit that for homework/pet projects dependency is often a far away consideration. On small size projects it doesn't matter. But as soon as several persons work on the same project and this grows into the dozens of thousands of lines you need to limit the impact of changes.

This bring a very simple rule:

Changing the internals of a class should only affect the class itself

Of course, you'll probably affect its friends, but there are two cases here:

  • friend free function: probably more of a member function anyway (I am think std::ostream& operator<<(...) here, which is not a member purely by accident of the language rules
  • friend class ? you don't need friend classes on real classes.

I would recommend the use of the simple method:

class Example;


class ExampleKey { friend class Example; ExampleKey(); };


class Restricted
{
public:
void forExampleOnly(int,int,ExampleKey const&);
};

This simple Key pattern allows you to declare a friend (in a way) without actually giving it access to your internals, thus isolating it from changes. Furthermore it allows this friend to lend its key to trustees (like children) if required.

Friend function in a class assigns the extern property to the function. i.e. extern means that the function has been declared and defined somewhere out of the class.

Hence it means friend function is not a member of a class. So the inheritance only allows you to inherit the properties of a class not external things. And also if inheritance is allowed for friend functions, then a third party class inheriting.

Friend is good in inheritance like style interface for container But for me, as the first say, C++ lack the propagatable inheritance

class Thing;


//an interface for Thing container's
struct IThing {
friend Thing;
protected:
int IThing_getData() = 0;
};


//container for thing's
struct MyContainer : public IThing {
protected: //here is reserved access to Thing
int IThing_getData() override {...}
};


struct Thing {
void setYourContainer(IThing* aContainerOfThings) {
//access to unique function in protected area
aContainerOfThings->IThing_getData(); //authorized access
}
};


struct ChildThing : public Thing {
void doTest() {
//here the lack of granularity, you cannot access to the container.
//to use the container, you must implement all
//function in the Thing class
aContainerOfThings->IThing_getData(); //forbidden access
}
};

For me the problem of C++ is the lack of very good granularity to control all access from anywhere for anything :

friend Thing can become friend Thing.* to grant access to all child of Thing

And more, friend [named area] Thing.* to grant access for a precise are in the Container class via special named area for the friend.

Ok stop the dream. But now, you know an interesting usage of friend.

In another order, you can also found interesting to known all class are friendly with self. In other word, a class instance can call all
members of another instance of same name without restriction:

class Object {
private:
void test() {}
protected:
void callAnotherTest(Object* anotherObject) {
//private, but yes you can call test() from
//another object instance
anotherObject)->test();
}
};

Simple logic : 'I have a friend Jane. Just because we became friends yesterday does not make all of her friends mine.'

I still need to approve those individual friendships, and the level of trust would be accordingly.