您何时应该使用'朋友'在c++?

发布于 2025-01-27 12:31:26 字数 405 浏览 6 评论 0 原文

我一直在阅读 c ++ faq ,对 朋友 声明。我个人从未使用过它,但是我有兴趣探索该语言。

使用 friend 的好例子是什么?


阅读FAQ更长的时间,我喜欢<< >>> 操作员重载和添加这些类的朋友的想法。但是,我不确定这不会打破封装。这些例外何时可以保持在OOP的严格范围内?

I have been reading through the C++ FAQ and was curious about the friend declaration. I personally have never used it, however I am interested in exploring the language.

What is a good example of using friend?


Reading the FAQ a bit longer I like the idea of the << >> operator overloading and adding as a friend of those classes. However I am not sure how this doesn't break encapsulation. When can these exceptions stay within the strictness that is OOP?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

南风起 2025-02-03 12:31:26

首先(IMO)不要听那些说 friend 的人没有用。这很有用。在许多情况下,您将拥有具有数据或功能的对象,这些对象不打算公开可用。大型代码库尤其如此,许多作者可能只能表面上熟悉不同领域。

还有朋友指定符的替代方案,但通常它们很麻烦(CPP级混凝土类/蒙版Typedefs)或没有万无一失的(注释或功能名称公约)。

回答;

friend 指定符允许指定的类访问“ Friend”语句的类中的受保护数据或功能的访问。例如,在以下代码中,任何人都可以要求孩子姓名,但只有母亲和孩子才能更改名称。

您可以通过考虑更复杂的类(例如窗口)来进一步以这个简单的示例。很可能有一个窗口具有许多功能/数据元素,不应公开访问,而是相关类(例如WindowManager)所需的。

class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;

public:

  string name( void );

protected:

  void setName( string newName );
};

Firstly (IMO) don't listen to people who say friend is not useful. It IS useful. In many situations you will have objects with data or functionality that are not intended to be publicly available. This is particularly true of large codebases with many authors who may only be superficially familiar with different areas.

There ARE alternatives to the friend specifier, but often they are cumbersome (cpp-level concrete classes/masked typedefs) or not foolproof (comments or function name conventions).

Onto the answer;

The friend specifier allows the designated class access to protected data or functionality within the class making the friend statement. For example in the below code anyone may ask a child for their name, but only the mother and the child may change the name.

You can take this simple example further by considering a more complex class such as a Window. Quite likely a Window will have many function/data elements that should not be publicly accessible, but ARE needed by a related class such as a WindowManager.

class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;

public:

  string name( void );

protected:

  void setName( string newName );
};
清秋悲枫 2025-02-03 12:31:26

在工作中,我们使用朋友大量测试代码。这意味着我们可以为主要应用程序代码提供适当的封装和信息隐藏。但是,我们也可以使用单独的测试代码,该代码使用朋友检查内部状态和数据进行测试。

可以说我不会将朋友关键字作为您设计的重要组成部分。

At work we use friends for testing code, extensively. It means we can provide proper encapsulation and information hiding for the main application code. But also we can have separate test code that uses friends to inspect internal state and data for testing.

Suffice to say I wouldn't use the friend keyword as an essential component of your design.

白色秋天 2025-02-03 12:31:26

friend 关键字具有许多很好的用途。这是我立即可见的两种用途:

朋友定义

朋友定义允许在类cope中定义一个函数,但是该函数不会定义为成员函数,而是封闭名称空间的免费函数,并且不会正常可见,除了依赖参数的查找。这使其对于运营商的重载特别有用:

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();
};

您会在 this 答案。 CRTP基础施放了该指针,以便能够使用Data-Member-Pointers访问派生类的数据场。

The friend keyword has a number of good uses. Here are the two uses immediately visible to me:

Friend Definition

Friend definition allows to define a function in class-scope, but the function will not be defined as a member function, but as a free function of the enclosing namespace, and won't be visible normally except for argument dependent lookup. That makes it especially useful for operator overloading:

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
}

Private CRTP Base Class

Sometimes, you find the need that a policy needs access to the derived class:

// 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();
};

You will find a non-contrived example for that in this answer. Another code using that is in this answer. The CRTP base casts its this pointer, to be able to access data-fields of the derived class using data-member-pointers.

独闯女儿国 2025-02-03 12:31:26

@rooo :封装在这里没有打破,因为课程本身本身决定谁可以访问其私人成员。只有当您的操作员&lt;&lt; 会宣布“我是class foo >的朋友”时,只有在班级外部引起的封装才会被打破。

朋友代替 public 的使用,而不是使用 private

实际上,c ++常见问题已经回答了这一点。

@roo: Encapsulation is not broken here because the class itself dictates who can access its private members. Encapsulation would only be broken if this could be caused from outside the class, e.g. if your operator << would proclaim “I'm a friend of class foo.”

friend replaces use of public, not use of private!

Actually, the C++ FAQ answers this already.

不美如何 2025-02-03 12:31:26

规范的例子是超载运算符&lt;&lt;。另一个常见的用途是允许辅助或管理员类访问您的内部。

这是我听说过C ++朋友的几个准则。最后一个特别令人难忘。

  • 您的朋友不是您孩子的朋友。
  • 您孩子的朋友不是您的朋友。
  • 只有朋友可以触摸您的私人零件。

The canonical example is to overload operator<<. Another common use is to allow a helper or admin class access to your internals.

Here are a couple of guidelines I heard about C++ friends. The last one is particularly memorable.

  • Your friends are not your child's friends.
  • Your child's friends are not your friends.
  • Only friends can touch your private parts.
仙女 2025-02-03 12:31:26

编辑:阅读常见问题解答更长的时间,我喜欢&lt;&lt; &gt;&gt;操作员超负荷并添加为这些类的朋友,但是我不确定这不会破坏封装

如何破坏封装?

当您允许无限制访问数据成员时,您会断开封装。考虑以下类:

class c1 {
public:
  int x;
};

class c2 {
public:
  int foo();
private:
  int x;
};

class c3 {
  friend int foo();
private:
  int x;
};

c1 明显未封装的。任何人都可以在其中读取和修改 x 。我们无法执行任何类型的访问控制。

C2 显然已封装。无法公开访问 X 。您所能做的就是调用 foo 函数,该功能在类上执行一些有意义的操作。

C3 ?那封装不太封装吗?它是否允许对 x 的无限制访问?它允许未知功能访问吗?

否。它允许精确一个功能访问类的私人成员。就像 C2 一样。就像 C2 一样,具有访问权限的一个函数不是“某些随机,未知函数”,而是“类定义中列出的函数”。就像 c2 一样,我们可以看到,只需查看类定义,a 完整谁访问了。

那么,这少封装到底如何呢?相同数量的代码可以访问班级的私人成员。 拥有访问权限的每个人都在类定义中列出。

朋友不会断开封装。这使一些Java人程序员感到不舒服,因为当他们说“ OOP”时,他们实际上是 “ Java”。当他们说“封装”时,他们并不是说“私人成员必须受到任意访问的保护”,而是“ Java类,其中唯一能够访问私人成员的功能是班级成员”,即使这是完全胡说八道< em>出于多种原因。

首先,如前所述,它太限制了。没有理由不应该允许朋友方法这样做。

其次,它不是限制性足够的。考虑第四节:

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

根据上述Java的心态,这是完美封装的。
但是,它绝对允许任何人阅读和修改x 。这甚至有意义? (提示:不是)

底线:
封装是关于能够控制哪些功能可以访问私人成员。它不是 准确地说是这些函数的定义所在的位置。

edit: Reading the faq a bit longer I like the idea of the << >> operator overloading and adding as a friend of those classes, however I am not sure how this doesn't break encapsulation

How would it break encapsulation?

You break encapsulation when you allow unrestricted access to a data member. Consider the following classes:

class c1 {
public:
  int x;
};

class c2 {
public:
  int foo();
private:
  int x;
};

class c3 {
  friend int foo();
private:
  int x;
};

c1 is obviously not encapsulated. Anyone can read and modify x in it. We have no way to enforce any kind of access control.

c2 is obviously encapsulated. There is no public access to x. All you can do is call the foo function, which performs some meaningful operation on the class.

c3? Is that less encapsulated? Does it allow unrestricted access to x? Does it allow unknown functions access?

No. It allows precisely one function to access the private members of the class. Just like c2 did. And just like c2, the one function which has access is not "some random, unknown function", but "the function listed in the class definition". Just like c2, we can see, just by looking at the class definitions, a complete list of who has access.

So how exactly is this less encapsulated? The same amount of code has access to the private members of the class. And everyone who has access is listed in the class definition.

friend does not break encapsulation. It makes some Java people programmers feel uncomfortable, because when they say "OOP", they actually mean "Java". When they say "Encapsulation", they don't mean "private members must be protected from arbitrary accesses", but "a Java class where the only functions able to access private members, are class members", even though this is complete nonsense for several reasons.

First, as already shown, it is too restricting. There's no reason why friend methods shouldn't be allowed to do the same.

Second, it is not restrictive enough. Consider a fourth class:

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

This, according to aforesaid Java mentality, is perfectly encapsulated.
And yet, it allows absolutely anyone to read and modify x. How does that even make sense? (hint: It doesn't)

Bottom line:
Encapsulation is about being able to control which functions can access private members. It is not about precisely where the definitions of these functions are located.

苍暮颜 2025-02-03 12:31:26

安德鲁(Andrew)示例的另一个常见版本是可怕的代码耦合,

parent.addChild(child);
child.setParent(parent);

而不必担心是否始终在一起完成两行,并且以一致的顺序完成,您可以将方法私有化并具有朋友函数来执行一致性:

class Parent;

class Object {
private:
    void setParent(Parent&);

    friend void addChild(Parent& parent, Object& child);
};

class Parent : public Object {
private:
     void addChild(Object& child);

     friend void addChild(Parent& parent, Object& child);
};

void addChild(Parent& parent, Object& child) {
    if( &parent == &child ){ 
        wetPants(); 
    }
    parent.addChild(child);
    child.setParent(parent);
}

换句话说,您可以保持公共接口保持较小的,强制执行不变的,这些不变性在朋友函数中跨越类和对象。

Another common version of Andrew's example, the dreaded code-couplet

parent.addChild(child);
child.setParent(parent);

Instead of worrying if both lines are always done together and in consistent order you could make the methods private and have a friend function to enforce consistency:

class Parent;

class Object {
private:
    void setParent(Parent&);

    friend void addChild(Parent& parent, Object& child);
};

class Parent : public Object {
private:
     void addChild(Object& child);

     friend void addChild(Parent& parent, Object& child);
};

void addChild(Parent& parent, Object& child) {
    if( &parent == &child ){ 
        wetPants(); 
    }
    parent.addChild(child);
    child.setParent(parent);
}

In other words you can keep the public interfaces smaller and enforce invariants that cut across classes and objects in friend functions.

灵芸 2025-02-03 12:31:26

您可以使用私人/保护/公共权利控制会员的访问权限和功能吗?
因此,假设这三个级别中的每个级别的每一个都很清楚,那么我们应该很明显我们缺少某些东西……

例如,将成员/函数声明为保护是相当通用的。您说的是,此功能是每个人(当然是继承的孩子)。但是例外呢?每个安全系统都可以让您拥有某种类型的“白色列表”,对吗

?感觉是有道理的,

我认为这是不需要的,因为我认为这与全球变量的讨论相似。他们...但是实际上,您会看到最终成为(几乎)最优雅的方式的情况...我认为这与朋友是同一情况。

除了让您不使用设置函数的情况下访问成员变量外,它并没有真正的好处

,这并不是查看它的方法。
这个想法是控制谁可以访问什么,具有或不设置函数与之无关。

You control the access rights for members and functions using Private/Protected/Public right?
so assuming the idea of each and every one of those 3 levels is clear, then it should be clear that we are missing something...

The declaration of a member/function as protected for example is pretty generic. You are saying that this function is out of reach for everyone (except for an inherited child of course). But what about exceptions? every security system lets you have some type of 'white list" right?

So friend lets you have the flexibility of having rock solid object isolation, but allows for a "loophole" to be created for things that you feel are justified.

I guess people say it is not needed because there is always a design that will do without it. I think it is similar to the discussion of global variables: You should never use them, There is always a way to do without them... but in reality, you see cases where that ends up being the (almost) most elegant way... I think this is the same case with friends.

It doesn't really do any good, other than let you access a member variable without using a setting function

well that is not exactly the way to look at it.
The idea is to control WHO can access what, having or not a setting function has little to do with it.

弃爱 2025-02-03 12:31:26

我找到了使用朋友访问的方便的地方:私人功能的UNITSEST。

I found handy place to use friend access: Unittest of private functions.

断念 2025-02-03 12:31:26

C ++的创建者说,这并不是任何封装原则,我会引用他:

“朋友”是否违反封装?
不,它没有。 “朋友”是授予访问权限的明确机制,就像会员资格一样。您无法(在标准符合程序中)在不修改其来源的情况下授予自己访问类。

很清楚...

The creator of C++ says that isn't broking any encapsulation principle, and I will quote him:

Does "friend" violate encapsulation?
No. It does not. "Friend" is an explicit mechanism for granting access, just like membership. You cannot (in a standard conforming program) grant yourself access to a class without modifying its source.

Is more than clear...

少女的英雄梦 2025-02-03 12:31:26

当您构建一个容器时,朋友会派上方便,并且想为该课程实现迭代器。

Friend comes handy when you are building a container and you want to implement an iterator for that class.

姐不稀罕 2025-02-03 12:31:26

我们曾在我以前在我们使用朋友来体面影响的公司工作的公司出现一个有趣的问题。我在框架部门工作,我们在自定义操作系统上创建了一个基本的发动机级系统。在内部,我们有一个课堂结构:

         Game
        /    \
 TwoPlayer  SinglePlayer

所有这些课程都是框架的一部分,并由我们的团队维护。该公司制作的游戏是建立在此框架之上的,该框架源于其中一个儿童游戏。问题在于,游戏与单个玩家和Twoplayer需要访问的各种事物具有界面,但我们不希望在框架类别之外展示。解决方案是将这些接口私有化,并允许Twoplayer和单人游戏通过友谊访问它们。

坦白说,这整个问题本可以通过更好地实施我们的系统来解决,但我们被锁定在我们所拥有的。

We had an interesting issue come up at a company I previously worked at where we used friend to decent affect. I worked in our framework department we created a basic engine level system over our custom OS. Internally we had a class structure:

         Game
        /    \
 TwoPlayer  SinglePlayer

All of these classes were part of the framework and maintained by our team. The games produced by the company were built on top of this framework deriving from one of Games children. The issue was that Game had interfaces to various things that SinglePlayer and TwoPlayer needed access to but that we did not want expose outside of the framework classes. The solution was to make those interfaces private and allow TwoPlayer and SinglePlayer access to them via friendship.

Truthfully this whole issue could have been resolved by a better implementation of our system but we were locked into what we had.

拿命拼未来 2025-02-03 12:31:26

简短的答案是:使用 friend 实际上改进封装。提高可读性和可用性(运营商&lt;&lt;&gt;&gt;是规范的例子)也是一个很好的理由。

至于改进封装的示例,专门设计用于与其他类(想到的测试课程)合作的课程是良好的候选人。

The short answer would be: use friend when it actually improves encapsulation. Improving readability and usability (operators << and >> are the canonical example) is also a good reason.

As for examples of improving encapsulation, classes specifically designed to work with the internals of other classes (test classes come to mind) are good candidates.

情话已封尘 2025-02-03 12:31:26

在C ++“朋友”中,关键字可用于操作员的过载和制作桥梁。

1.)运算符重载中的朋友关键字:
操作员重载的示例是:假设我们有一个具有两个float变量的类“点”
“ x”(用于x坐标)和“ y”(用于y坐标)。现在,我们必须超载“&lt;&lt;“ (提取操作员),这样,如果我们调用“ cout&lt&lt;&lt; pointobj” ,则它将打印x和y坐标(其中pointOBJ是类点的对象)。为此,我们有两个选择:

   1.Overload "operator <<()" function in "ostream" class.
   2.Overload "operator<<()" function in "Point" class.

Now First option is not good because if we need to overload again this operator for some different class then we have to again make change in "ostream" class.

That's why second is best option. Now compiler can call
"operator <<()" function:

   1.Using ostream object cout.As: cout.operator<<(Pointobj) (form ostream class).
2.Call without an object.As: operator<<(cout, Pointobj) (from Point class).

BeaCause我们已经在积分类中实施了重载。因此,要在没有对象的情况下调用此函数,我们必须添加“朋友” 关键字,因为我们可以在没有对象的情况下调用朋友函数。
现在函数声明将为:
“朋友ostream&amp; ocerator&lt;&lt;(ostream&amp; cout,point&amp; pointobj);“

2.)Friend制作桥梁的关键字:
假设我们必须发挥一个函数,在该功能中,我们必须访问两个或多个类的私人成员(通常称为“桥”)。
如何做到这一点:
要访问班级的私人成员,应该是该课程的成员。现在,要访问其他班级的私人成员,每个班级应将该功能声明为朋友函数。例如 :
假设有两个A和B类B。函数“ funcbridge()” 想要访问两个类的私人成员。然后两个类都应声明“ funcbridge()” as:
朋友return_type funcbridge(a&amp; a_obj,b&amp; b_obj);

我认为这将有助于了解朋友关键字。

In C++ "friend" keyword is useful in Operator overloading and Making Bridge.

1.) Friend keyword in operator overloading :
Example for operator overloading is: Let say we have a class "Point" that has two float variable
"x"(for x-coordinate) and "y"(for y-coordinate). Now we have to overload "<<"(extraction operator) such that if we call "cout << pointobj" then it will print x and y coordinate (where pointobj is an object of class Point). To do this we have two option:

   1.Overload "operator <<()" function in "ostream" class.
   2.Overload "operator<<()" function in "Point" class.

Now First option is not good because if we need to overload again this operator for some different class then we have to again make change in "ostream" class.

That's why second is best option. Now compiler can call
"operator <<()" function:

   1.Using ostream object cout.As: cout.operator<<(Pointobj) (form ostream class).
2.Call without an object.As: operator<<(cout, Pointobj) (from Point class).

Beacause we have implemented overloading in Point class. So to call this function without an object we have to add"friend" keyword because we can call a friend function without an object.
Now function declaration will be As:
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

2.) Friend keyword in making bridge :
Suppose we have to make a function in which we have to access private member of two or more classes ( generally termed as "bridge" ) .
How to do this:
To access private member of a class it should be member of that class. Now to access private member of other class every class should declare that function as a friend function. For example :
Suppose there are two class A and B. A function "funcBridge()" want to access private member of both classes. Then both class should declare "funcBridge()" as:
friend return_type funcBridge(A &a_obj, B & b_obj);

I think this would help to understand friend keyword.

简美 2025-02-03 12:31:26

作为

朋友声明出现在班级主体中,并授予了私人和受保护的 的函数或其他类别的成员。

因此,正如提醒的那样,某些答案中存在技术错误,这些答案说 friend 只能访问受保护成员。

As the reference for friend declaration says:

The friend declaration appears in a class body and grants a function or another class access to private and protected members of the class where the friend declaration appears.

So just as a reminder, there are technical errors in some of the answers which say that friend can only visit protected members.

酒浓于脸红 2025-02-03 12:31:26

另一个用途: friend (+虚拟继承)可用于避免从类派生(aka:“使class class ofer class noverable vivable”)=&gt; 1 2

来自 2

 class Fred;

 class FredBase {
 private:
   friend class Fred;
   FredBase() { }
 };

 class Fred : private virtual FredBase {
 public:
   ...
 }; 

Another use: friend (+ virtual inheritance) can be used to avoid deriving from a class (aka: "make a class underivable") => 1, 2

From 2:

 class Fred;

 class FredBase {
 private:
   friend class Fred;
   FredBase() { }
 };

 class Fred : private virtual FredBase {
 public:
   ...
 }; 
紫﹏色ふ单纯 2025-02-03 12:31:26

为了多次进行TDD,我在C ++中使用了“朋友”关键字。

朋友可以知道我的一切吗?


更新:我从 bjarne stroustroup site

“朋友”是授予访问的明确机制,就像会员资格一样。

To do TDD many times I've used 'friend' keyword in C++.

Can a friend know everything about me?


Updated: I found this valuable answer about "friend" keyword from Bjarne Stroustrup site.

"Friend" is an explicit mechanism for granting access, just like membership.

南风几经秋 2025-02-03 12:31:26

您必须非常谨慎地使用 friend 关键字的何时/地点,并且像您一样,我很少使用它。以下是有关使用 friend 和替代方案的一些注释。

假设您要比较两个对象,以查看它们是否相等。您可以:

  • 使用访问者方法进行比较(检查每个IVAR并确定平等)。
  • 或者,您可以通过公开来直接访问所有成员。

第一个选项的问题在于,这可能是很多访问者,这比直接变量访问速度(稍微)慢,难以读取和麻烦。第二种方法的问题是您完全打破了封装。

很好的是,如果我们可以定义一个外部功能,该功能仍然可以访问班级的私人成员。我们可以使用 friend 关键字来完成此操作:

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

方法均等(啤酒,啤酒)现在可以直接访问 a b /code>的私人成员(可以是 char *brand float percental ochol 等。这是一个相当理想的例子,您会尽快应用 friend /代码>到Overloaded ==操作员,我们要注意的是

  • friend 不是类的成员功能
  • 。具有特殊访问班级私人成员的普通功能
  • 不会用朋友替换所有登记器和突变器(您最好将所有内容 public !)
  • 友谊不是互惠的
  • 友谊
  • 友谊不是继承的
  • ,或者,作为 c ++ faq解释:“仅仅因为我授予您友谊的访问权限并不能自动授予您的孩子访问我,因此不会自动授予您的朋友访问我,并且不会自动授予我访问您。

朋友以其他方式更难做到这一点。作为另一个示例,由于 mat2x2 mat3x3 mat4x4 friends >, vec2 vec3 vec4 等。变得更容易成为朋友,而不必在各地使用访问者。正如指出的那样,当应用于&lt;&lt; 时, friend 通常是有用的 == 运算符,但也可以用于这样的事情:

class Birds {
public:
    friend Birds operator +(Birds, Birds);
private:
    int numberInFlock;
};


Birds operator +(Birds b1, Birds b2) {
    Birds temp;
    temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
    return temp;
}

正如我所说,我根本不使用 friend ,但时不时地是正是您需要的。希望这有帮助!

You have to be very careful about when/where you use the friend keyword, and, like you, I have used it very rarely. Below are some notes on using friend and the alternatives.

Let's say you want to compare two objects to see if they're equal. You could either:

  • Use accessor methods to do the comparison (check every ivar and determine equality).
  • Or, you could access all the members directly by making them public.

The problem with the first option, is that that could be a LOT of accessors, which is (slightly) slower than direct variable access, harder to read, and cumbersome. The problem with the second approach is that you completely break encapsulation.

What would be nice, is if we could define an external function which could still get access to the private members of a class. We can do this with the friend keyword:

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

The method equal(Beer, Beer) now has direct access to a and b's private members (which may be char *brand, float percentAlcohol, etc. This is a rather contrived example, you would sooner apply friend to an overloaded == operator, but we'll get to that.

A few things to note:

  • A friend is NOT a member function of the class
  • It is an ordinary function with special access to the private members of the class
  • Don't replace all accessors and mutators with friends (you may as well make everything public!)
  • Friendship isn't reciprocal
  • Friendship isn't transitive
  • Friendship isn't inherited
  • Or, as the C++ FAQ explains: "Just because I grant you friendship access to me doesn't automatically grant your kids access to me, doesn't automatically grant your friends access to me, and doesn't automatically grant me access to you."

I only really use friends when it's much harder to do it the other way. As another example, many vector maths functions are often created as friends due to the interoperability of Mat2x2, Mat3x3, Mat4x4, Vec2, Vec3, Vec4, etc. And it's just so much easier to be friends, rather than have to use accessors everywhere. As pointed out, friend is often useful when applied to the << (really handy for debugging), >> and maybe the == operator, but can also be used for something like this:

class Birds {
public:
    friend Birds operator +(Birds, Birds);
private:
    int numberInFlock;
};


Birds operator +(Birds b1, Birds b2) {
    Birds temp;
    temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
    return temp;
}

As I say, I don't use friend very often at all, but every now and then it's just what you need. Hope this helps!

酒儿 2025-02-03 12:31:26

似乎我迟到了14年。但是来了。

TLDR TLDR

朋友类在那里,以便您可以将封装扩展到包含您数据结构的一组类。

TLDR

通常由多个类组成。与传统类(由您的编程语言支持)类似,您的数据结构是一个广义类,它在该数据上还具有数据和不变性,这些数据跨越了多个类的对象。封装可以保护那些不变的人免受外部数据的意外修改,以便数据结构的操作(“成员函数”)正常工作。朋友类将封装从类扩展到您的广义类。

太长的

A 是与不变的一起指定数据类型值的子集,称为有效状态。对象是类的有效状态。类的A 成员函数将给定对象从有效状态移至另一个。

至关重要的是,对象数据不会从类成员函数外部进行修改,因为这可能会破坏类不变(即将对象移至无效状态)。 封装禁止从班级外部访问对象数据。这是编程语言的重要安全功能,因为它很难使班级不变。

一类通常是实现数据结构的自然选择,因为数据结构的属性(例如性能)取决于其数据的不变性(例如红色%e2%80%93Black_tree“ rel =“ nofollow noreferrer”>红黑树不变式)。但是,有时单个类不足以描述数据结构。

A 数据结构是将数据从有效状态转移到另一个数据的任何数据,不变性和功能。这是一个班级的概括。微妙的区别在于,数据可以散布在数据类型上,而不是集中在单个数据类型上。

数据结构示例

数据结构的原型示例是 vertex ),边缘(class edge )和图形(类 Graph )。这些课程没有独立的意义。图类创建 vertex s and edge 由其成员函数(例如 graph.addvertex() and and graph.addedge(avertex) ,bvertex)),并返回与它们的指针(或类似)。 vertex s and edge S同样被其拥有 Graph (例如 graph.removevertex(vertex)和和和<代码> graph.removeedge(edge))。 顶点对象的收集, edge 对象和图形对象一起编码数学图。在此示例中,目的是 vertex / edge 对象在 Graph> Graph 对象之间未共享(其他设计选择也是可能的)。

Graph 对象可以存储其所有顶点和边缘的列表,而每个 vertex 可以存储一个指向其拥有的指针 graph 。因此, Graph 对象代表整个数学图,每当需要数学图时,您都会将其传递。

不变的示例

图形数据结构不变的是 vertex 在其所有者 Graph 的列表中列出。这个不变的跨越 vertex 对象和 Graph 对象。多种类型的多种对象可以参与给定的不变。

封装示例

类似于类,数据结构受益于封装,它可以防止意外修改其数据。这是因为数据结构需要保留不变性,才能像一类一样以应许的方式运行。

在图形数据结构示例中,您会声明 vertex Graph 的朋友,也使构造函数和数据成员的 vertex >私有,以便只能通过 Graph 创建顶点。特别是, vertex 将具有一个私有构造函数,它可以接受指向其拥有图的指针。该构造函数在 graph.addvertex()中被称为,这是可能的,因为 vertex graph 的朋友。 (但是请注意, Graph 不是 vertex 的朋友:无需 vertex 能够访问 Graph 的顶点列表,例如。)

术语

数据结构的定义本身就像类一样。我建议我们开始使用术语“广义类”来用于将数据从有效状态转移到另一个数据的任何数据,不变性和功能。然后,C ++类是一种特定类型的广义类。然后,不言而喻的朋友类是将封装从C ++类扩展到广义类的精确机制。

(实际上,我希望“班级”一词被替换为“广义班级”的概念,并将“本地班级”用于编程语言支持的类的特殊情况。然后,当教课程时,您将了解这两个本地阶级和这些广义班级。

Seems I'm about 14 years late to the party. But here goes.

TLDR TLDR

Friend classes are there so that you can extend encapsulation to the group of classes which comprise your data structure.

TLDR

Your data structure in general consists of multiple classes. Similarly to a traditional class (supported by your programming language), your data structure is a generalized class which also has data and invariants on that data which spans across objects of multiple classes. Encapsulation protects those invariants against accidental modification of the data from the outside, so that the data-structure's operations ("member functions") work correctly. Friend classes extend encapsulation from classes to your generalized class.

The too long

A class is a datatype together with invariants which specify a subset of the values of the datatype, called the valid states. An object is a valid state of a class. A member function of a class moves a given object from a valid state to another.

It is essential that object data is not modified from outside of the class member functions, because this could break the class invariants (i.e. move the object to an invalid state). Encapsulation prohibits access to object data from outside of the class. This is an important safety feature of programming languages, because it makes it hard to inadvertedly break class invariants.

A class is often a natural choice for implementing a data structure, because the properties (e.g. performance) of a data structure is dependent on invariants on its data (e.g. red-black tree invariants). However, sometimes a single class is not enough to describe a data structure.

A data structure is any set of data, invariants, and functions which move that data from a valid state to another. This is a generalization of a class. The subtle difference is that the data may be scattered over datatypes rather than be concentrated on a single datatype.

Data structure example

A prototypical example of a data structure is a graph which is stored using separate objects for vertices (class Vertex), edges (class Edge), and the graph (class Graph). These classes do not make sense independently. The Graph class creates Vertexs and Edges by its member functions (e.g. graph.addVertex() and graph.addEdge(aVertex, bVertex)) and returns pointers (or similar) to them. Vertexs and Edges are similarly destroyed by their owning Graph (e.g. graph.removeVertex(vertex) and graph.removeEdge(edge)). The collection of Vertex objects, Edge objects and the Graph object together encode a mathematical graph. In this example the intention is that Vertex/Edge objects are not shared between Graph objects (other design choices are also possible).

A Graph object could store a list of all its vertices and edges, while each Vertex could store a pointer to its owning Graph. Hence, the Graph object represents the whole mathematical graph, and you would pass that around whenever the mathematical graph is needed.

Invariant example

An invariant for the graph data structure then would be that a Vertex is listed in its owner Graph's list. This invariant spans both the Vertex object and the Graph object. Multiple objects of multiple types can take part in a given invariant.

Encapsulation example

Similarly to a class, a data structure benefits from encapsulation which protects against accidental modification of its data. This is because the data structure needs to preserve invariants to be able to function in promised manner, exactly like a class.

In the graph data structure example, you would state that Vertex is a friend of Graph, and also make the constructors and data-members of Vertex private so that a Vertex can only be created and modified by Graph. In particular, Vertex would have a private constructor which accepts a pointer to its owning graph. This constructor is called in graph.addVertex(), which is possible because Vertex is a friend of Graph. (But note that Graph is not a friend of Vertex: there is no need for Vertex to be able to access Graph's vertex-list, say.)

Terminology

The definition of a data structure acts itself like a class. I propose that we start using the term 'generalized class' for any set of data, invariants, and functions which move that data from a valid state to another. A C++ class is then a specific kind of a generalized class. It is then self-evident that friend classes are the precise mechanism for extending encapsulation from C++ classes to generalized classes.

(In fact, I'd like the term 'class' to be replaced with the concept of 'generalized class', and use 'native class' for the special case of a class supported by the programming language. Then when teaching classes you would learn of both native classes and these generalized classes. But perhaps that would be confusing.)

不回头走下去 2025-02-03 12:31:26

关于运营商&lt;&lt;和操作员&gt;&gt;没有充分的理由结交这些运营商的朋友。的确,他们不应该是会员功能,但也不需要成为朋友。

最好的办法是创建公共打印(Ostream&amp;)并读取(istream&amp;)功能。然后,写操作员&lt;&lt;和操作员&gt;&gt;就这些功能而言。这给您带来了允许您虚拟函数的额外好处,从而提供虚拟序列化。

With regards to operator<< and operator>> there is no good reason to make these operators friends. It is true that they should not be member functions, but they don't need to be friends, either.

The best thing to do is create public print(ostream&) and read(istream&) functions. Then, write the operator<< and operator>> in terms of those functions. This gives the added benefit of allowing you to make those functions virtual, which provides virtual serialization.

以可爱出名 2025-02-03 12:31:26

我只使用Friend-keyword来获得Unitest Wearp的功能。有人会说您不应该测试受保护的功能。但是,在添加新功能时,我会发现此非常有用的工具。

但是,我不直接在类声明中使用关键字,而是使用漂亮的模板黑客来实现这一点:

template<typename T>
class FriendIdentity {
public:
  typedef T me;
};

/**
 * A class to get access to protected stuff in unittests. Don't use
 * directly, use friendMe() instead.
 */
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
  Friender() {}
  virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
  friend ToFriend;
#else
  friend class FriendIdentity<ToFriend>::me;
#endif
};

/**
 * Gives access to protected variables/functions in unittests.
 * Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
 */
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender<Tester, ParentClass> &)(instance);
}

这使我能够执行以下操作:

friendMe(this, someClassInstance).someProtectedFunction();

在GCC和MSVC上工作。

I'm only using the friend-keyword to unittest protected functions. Some will say that you shouldn't test protected functionality. I, however, find this very useful tool when adding new functionality.

However, I don't use the keyword in directly in the class declarations, instead I use a nifty template-hack to achive this:

template<typename T>
class FriendIdentity {
public:
  typedef T me;
};

/**
 * A class to get access to protected stuff in unittests. Don't use
 * directly, use friendMe() instead.
 */
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
  Friender() {}
  virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
  friend ToFriend;
#else
  friend class FriendIdentity<ToFriend>::me;
#endif
};

/**
 * Gives access to protected variables/functions in unittests.
 * Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
 */
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender<Tester, ParentClass> &)(instance);
}

This enables me to do the following:

friendMe(this, someClassInstance).someProtectedFunction();

Works on GCC and MSVC atleast.

浅笑轻吟梦一曲 2025-02-03 12:31:26

树示例是一个很好的例子:
在没有几个不同的类中实现对象
具有继承关系。

也许您也可能需要它以保护构造函数并强制
人们使用您的“朋友”工厂。

...好吧,坦率地说,您可以没有它。

The tree example is a pretty good example :
Having an object implemented in a few different class without
having an inheritance relationship.

Maybe you could also need it to have a constructor protected and force
people to use your "friend" factory.

... Ok, Well frankly you can live without it.

请爱~陌生人 2025-02-03 12:31:26

做很多次我在C ++中使用过“朋友”的关键字。
朋友可以知道我的一切吗?

不,这只是一种一种一种方式友谊:`(

To do TDD many times I've used 'friend' keyword in C++.
Can a friend know everything about me?

No, its only a one way friendship :`(

提赋 2025-02-03 12:31:26

我使用 friend 的一个特定实例是创建 singletleton classe。 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;
}

One specific instance where I use friend is when creating Singleton classes. The friend keyword lets me create an accessor function, which is more concise than always having a "GetInstance()" method on the class.

/////////////////////////
// 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;
}
一身软味 2025-02-03 12:31:26

朋友功能和课程可直接访问私人和受保护的班级成员,以避免在一般情况下破坏封装。大多数用法是关于Ostream:我们希望能够键入:

Point p;
cout << p;

但是,这可能需要访问点的私人数据,因此我们定义了超载运算

friend ostream& operator<<(ostream& output, const Point& p);

符,但是,显而易见的封装含义。首先,现在,朋友的班级或功能可以完全访问班级的所有成员,即使是与其需求无关的成员。其次,班级和朋友的实现现在被淘汰到班级内部变化可以破坏朋友的地步。

如果您将朋友视为班级的扩展,那么从逻辑上讲,这不是问题。但是,在这种情况下,为什么有必要首先将朋友带出来。

为了实现“朋友”声称要实现的同一目标,但是如果不违反封装,就可以做到这一点:

class A
{
public:
    void need_your_data(B & myBuddy)
    {
        myBuddy.take_this_name(name_);
    }
private:
    string name_;
};

class B
{
public:
    void print_buddy_name(A & myBuddy)
    {
        myBuddy.need_your_data(*this);
    }
    void take_this_name(const string & name)
    {
        cout << name;
    }
}; 

封装没有破坏,B类无法访问A中的内部实现,但结果与我们一样b宣布为A的朋友。
编译器将优化函数调用,因此这将导致与直接访问相同的说明。

我认为使用“朋友”只是一个具有可争议的好处的捷径,但成本确定。

Friend functions and classes provide direct access to private and protected members of class to avoid breaking encapsulation in the general case. Most usage is with ostream: we would like to be able to type:

Point p;
cout << p;

However, this may require access to the private data of Point, so we define the overloaded operator

friend ostream& operator<<(ostream& output, const Point& p);

There are obvious encapsulation implications, however. First, now the friend class or function has full access to ALL members of the class, even ones that do not pertain to its needs. Second, the implementations of the class and the friend are now enmeshed to the point where an internal change in the class can break the friend.

If you view the friend as an extension of the class, then this is not an issue, logically speaking. But, in that case, why was it necessary to spearate out the friend in the first place.

To achieve the same thing that 'friends' purport to achieve, but without breaking encapsulation, one can do this:

class A
{
public:
    void need_your_data(B & myBuddy)
    {
        myBuddy.take_this_name(name_);
    }
private:
    string name_;
};

class B
{
public:
    void print_buddy_name(A & myBuddy)
    {
        myBuddy.need_your_data(*this);
    }
    void take_this_name(const string & name)
    {
        cout << name;
    }
}; 

Encapsulation is not broken, class B has no access to the internal implementation in A, yet the result is the same as if we had declared B a friend of A.
The compiler will optimize away the function calls, so this will result in the same instructions as direct access.

I think using 'friend' is simply a shortcut with arguable benefit, but definite cost.

浅唱々樱花落 2025-02-03 12:31:26

在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友。

除了让您无需使用设置功能而访问成员变量之外,它并没有真正的好处。

When implementing tree algorithms for class, the framework code the prof gave us had the tree class as a friend of the node class.

It doesn't really do any good, other than let you access a member variable without using a setting function.

难得心□动 2025-02-03 12:31:26

可以遵守最严格,最纯粹的OOP原则,并确保任何班级的数据成员甚至都具有 concectors ,以便所有对象必须是只有能够以唯一的方式对数据了解他们的数据的人是通过间接消息,即方法。

但是,即使C#也具有内部可见性关键字,Java的默认值 package 对某些事物的级别可访问性。 C ++实际上是通过最小化可见性妥协到类中的OOP理想来实现的。进入它。

我实际上并不使用C ++,但是如果C#有 friend s,我会代替汇编全球内部修饰符,我实际上使用了很多。它并没有真正破坏裁员,因为.NET 中的部署单位是一个组件。

但是,还有 internalsvisibleto 属性(其他设备),它的作用像跨组件 friend 机制。 Microsoft将其用于Visual 设计器组件。

You could adhere to the strictest and purest OOP principles and ensure that no data members for any class even have accessors so that all objects must be the only ones that can know about their data with the only way to act on them is through indirect messages, i.e., methods.

But even C# has an internal visibility keyword and Java has its default package level accessibility for some things. C++ comes actually closer to the OOP ideal by minimizinbg the compromise of visibility into a class by specifying exactly which other class and only other classes could see into it.

I don't really use C++ but if C# had friends I would that instead of the assembly-global internal modifier, which I actually use a lot. It doesn't really break incapsulation, because the unit of deployment in .NET is an assembly.

But then there's the InternalsVisibleToAttribute(otherAssembly) which acts like a cross-assembly friend mechanism. Microsoft uses this for visual designer assemblies.

許願樹丅啲祈禱 2025-02-03 12:31:26

当不同的班级(不是另一个阶层)使用另一个班级的私人或受保护的成员时,您可能会使用友谊。

朋友功能的典型用例是操作
在两个不同类别之间进行私人或受保护
两者的成员。

来自 http://www.cplusplus.com/doc/doc/tutorial/tutorial/inheritance/

您可以在此示例中看到非成员方法访问类的私人成员。该方法必须在该班级中为班级的朋友声明。

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}

You may use friendship when different classes (not inheriting one from the other) are using private or protected members of the other class.

Typical use cases of friend functions are operations that are
conducted between two different classes accessing private or protected
members of both.

from http://www.cplusplus.com/doc/tutorial/inheritance/ .

You can see this example where non-member method accesses the private members of a class. This method has to be declared in this very class as a friend of the class.

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}
燕归巢 2025-02-03 12:31:26

可能我错过了上面答案中的一些东西,但是封装中的另一个重要概念是隐藏实施。减少对私人数据成员的访问(类的实现详细信息),可以更轻松地修改代码。如果朋友直接访问私人数据,则任何更改实现数据字段(私有数据)的任何更改,请打破访问该数据的代码。使用访问方法大多消除了这一点。我认为很重要。

Probably I missed something from the answers above but another important concept in encapsulation is hiding of implementation. Reducing access to private data members (the implementation details of a class) allows much easier modification of the code later. If a friend directly accesses the private data, any changes to the implementation data fields (private data), break the code accessing that data. Using access methods mostly eliminates this. Fairly important I would think.

掩耳倾听 2025-02-03 12:31:26

这可能不是实际的用例情况,但可能有助于说明课程之间的朋友使用。

俱乐部会所

class ClubHouse {
public:
    friend class VIPMember; // VIP Members Have Full Access To Class
private:
    unsigned nonMembers_;
    unsigned paidMembers_;
    unsigned vipMembers;

    std::vector<Member> members_;
public:
    ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}

    addMember( const Member& member ) { // ...code }   
    void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
    Amenity getAmenity( unsigned memberID ) { // ...code }

protected:
    void joinVIPEvent( unsigned memberID ) { // ...code }

}; // ClubHouse

成员班级的

class Member {
public:
    enum MemberShipType {
        NON_MEMBER_PAID_EVENT,   // Single Event Paid (At Door)
        PAID_MEMBERSHIP,         // Monthly - Yearly Subscription
        VIP_MEMBERSHIP,          // Highest Possible Membership
    }; // MemberShipType

protected:
    MemberShipType type_;
    unsigned id_;
    Amenity amenity_;
public:
    Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
    virtual ~Member(){}
    unsigned getId() const { return id_; }
    MemberShipType getType() const { return type_; }
    virtual void getAmenityFromClubHouse() = 0       
};

class NonMember : public Member {
public:
   explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}   

   void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class PaidMember : public Member {
public:
    explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class VIPMember : public Member {
public:
    friend class ClubHouse;
public:
    explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }

    void attendVIPEvent() {
        ClubHouse::joinVIPEvent( this->id );
    }
};

便利设施

class Amenity{};

如果您在这里查看这些课程的关系,则 ;俱乐部会所拥有各种不同类型的会员资格和会员资格。成员都源自超级或基类,因为它们都共享一个常见的ID和枚举类型,外部类可以通过在基类中找到的访问功能访问其ID和类型。

但是,通过这种成员及其派生阶级的层次结构及其与Clubhouse阶级的关系,唯一具有“特殊特权”的阶级是Vipmember班级。基类和其他两个派生类无法访问Clubhouse的JoinVipeVent()方法,但是VIP成员类具有该特权,就好像它可以完全访问该活动一样。

因此,借助Vipmember和会所,这是一条两条通道的两条通道,在其他会员类也有限。

This may not be an actual use case situation but may help to illustrate the use of friend between classes.

The ClubHouse

class ClubHouse {
public:
    friend class VIPMember; // VIP Members Have Full Access To Class
private:
    unsigned nonMembers_;
    unsigned paidMembers_;
    unsigned vipMembers;

    std::vector<Member> members_;
public:
    ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}

    addMember( const Member& member ) { // ...code }   
    void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
    Amenity getAmenity( unsigned memberID ) { // ...code }

protected:
    void joinVIPEvent( unsigned memberID ) { // ...code }

}; // ClubHouse

The Members Class's

class Member {
public:
    enum MemberShipType {
        NON_MEMBER_PAID_EVENT,   // Single Event Paid (At Door)
        PAID_MEMBERSHIP,         // Monthly - Yearly Subscription
        VIP_MEMBERSHIP,          // Highest Possible Membership
    }; // MemberShipType

protected:
    MemberShipType type_;
    unsigned id_;
    Amenity amenity_;
public:
    Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
    virtual ~Member(){}
    unsigned getId() const { return id_; }
    MemberShipType getType() const { return type_; }
    virtual void getAmenityFromClubHouse() = 0       
};

class NonMember : public Member {
public:
   explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}   

   void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class PaidMember : public Member {
public:
    explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class VIPMember : public Member {
public:
    friend class ClubHouse;
public:
    explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }

    void attendVIPEvent() {
        ClubHouse::joinVIPEvent( this->id );
    }
};

Amenities

class Amenity{};

If you look at the relationship of these classes here; the ClubHouse holds a variety of different types of memberships and membership access. The Members are all derived from a super or base class since they all share an ID and an enumerated type that are common and outside classes can access their IDs and Types through access functions that are found in the base class.

However through this kind of hierarchy of the Members and its Derived classes and their relationship with the ClubHouse class the only one of the derived class's that has "special privileges" is the VIPMember class. The base class and the other 2 derived classes can not access the ClubHouse's joinVIPEvent() method, yet the VIP Member class has that privilege as if it has complete access to that event.

So with the VIPMember and the ClubHouse it is a two way street of access where the other Member Classes are limited.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文