什么时候应该在C ++中使用“朋友”?

c++ oop encapsulation friend

162565 观看

30回复

5542 作者的声誉

我一直在阅读C ++常见问题解答,并对friend声明感到好奇。我个人从未使用过它,但是我对探索这种语言很感兴趣。

使用的一个好例子是friend什么?


再读一遍FAQ,我喜欢<< >>运算符重载的想法,并添加为这些类的朋友。但是我不确定这不会破坏封装。这些例外什么时候可以保持在OOP的严格程度之内?

作者: roo 的来源 发布者: 2008 年 8 月 20 日

回应 30


0

8031 作者的声誉

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

除了让您无需设置功能即可访问成员变量外,它实际上并没有任何好处。

作者: Ryan Fox 发布者: 2008 年 8 月 20 日

324

51331 作者的声誉

决定

首先(IMO)不要听别人说friend没有用。它是有益的。在许多情况下,您将拥有对象,这些对象具有不打算公开提供的数据或功能。对于大型代码库尤其如此,因为许多代码作者可能只对不同领域有所肤浅。

朋友说明符有ARE替代项,但它们通常很麻烦(cpp级具体类/屏蔽的typedef)或不够安全(注释或函数名称约定)。

回答问题;

friend指定符允许指定的类访问在创建该Friend语句的类中受保护的数据或功能。例如,在下面的代码中,任何人都可以问孩子一个名字,但是只有母亲和孩子可以更改名字。

您可以通过考虑更复杂的类(例如Window)来进一步介绍这个简单的示例。Window很可能将具有许多不应公开访问的功能/数据元素,但是相关类(例如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 );
};
作者: Andrew Grant 发布者: 2008 年 8 月 20 日

1

9285 作者的声誉

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

也许您可能还需要保护构造函数,并迫使人们使用您的“朋友”工厂。

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

作者: fulmicoton 发布者: 2008 年 8 月 20 日

8

3088 作者的声誉

您使用私有/受保护/公共权限控制成员和功能的访问权限?因此,假设这3个级别中的每个级别的概念都很明确,那么应该清楚我们缺少了一些东西...

例如,将成员/函数声明为受保护对象是非常通用的。您是说每个人都无法使用此功能(当然,除了继承的孩子)。但是异常呢?每个安全系统都会让您拥有某种“白名单”,对吗?

因此,friend使您可以灵活地进行坚如磐石的对象隔离,但允许为您认为合理的事物创建“漏洞”。

我猜人们说它是不需要的,因为总有一种设计可以不需要它。我认为这类似于对全局变量的讨论:永远不要使用它们,总有一种不用它们的方法...但是实际上,您会看到最终以(几乎)最优雅的方式出现的情况。 ..我认为与朋友的情况相同。

除了让您无需设置功能即可访问成员变量外,它实际上并没有任何好处

嗯,这并不是看待它的确切方式。想法是控制WHO可以访问具有或不具有设置功能的功能

作者: csmba 发布者: 2008 年 8 月 20 日

27

184545 作者的声誉

典型示例是重载operator <<。另一个常见用途是允许助手或管理员类访问您的内部组件。

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

  • 您的朋友不是您孩子的朋友。
  • 您孩子的朋友不是您的朋友。
  • 只有朋友可以触摸您的私密部位。
作者: Mark Harrison 发布者: 2008 年 8 月 20 日

41

422793 作者的声誉

@roo:封装在这里没有中断,因为类本身规定了谁可以访问其私有成员。如果封装可能是由班级外部引起的,例如,如果您operator <<宣称“我是班级的朋友” ,则封装将被破坏foo

friend代替使用public,不使用private

实际上,C ++ FAQ 已经回答了这个问题。

作者: Konrad Rudolph 发布者: 2008 年 8 月 20 日

-1

87365 作者的声誉

可以遵守最严格和最纯正的OOP原则,并确保任何类的数据成员都没有访问器,因此所有对象必须是唯一可以了解其数据的对象,唯一的处理方法是通过间接消息,即方法。

但是,即使C#都有内部可见性关键字,Java 在某些方面也具有其默认的程序包级别可访问性。实际上,C ++通过确切指定其他类以及只有其他类可以看到的类,从而最小化了对类的可见性的折衷,从而更接近于OOP的理想状态。

我并没有真正使用C ++,但是如果C#有好朋友,我会使用它,而不是我经常使用的Assembly-global 内部修饰符。它并没有真正破坏封装,因为.NET中的部署单元程序集。

但是,还有一个InternalsVisibleTo Attribute(otherAssembly),它就像一个跨程序集机制。Microsoft将其用于可视设计器程序集。

作者: Mark Cidade 发布者: 2008 年 8 月 20 日

3

5700 作者的声誉

为了进行TDD多次,我在C ++中使用了'friend'关键字。

朋友可以了解我的一切吗?


更新:我从Bjarne Stroustrup网站上找到了有关“ friend”关键字的有价值的答案。

就像成员身份一样,“朋友”是授予访问权限的明确机制。

作者: popopome 发布者: 2008 年 8 月 20 日

1

5542 作者的声誉

为了执行TDD多次,我在C ++中使用了“ friend”关键字。
朋友可以了解我的一切吗?

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

作者: roo 发布者: 2008 年 8 月 20 日

1

12784 作者的声誉

我使用的一个特定实例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;
}
作者: Matt Dillard 发布者: 2008 年 8 月 20 日

2

0 作者的声誉

关于运算符<<和operator >>,没有充分的理由结识这些运算符。的确,它们不应成为成员函数,但也不必成为朋友。

最好的办法是创建公共打印(ostream&)和读取(istream&)函数。然后,根据这些函数编写operator <<和operator >>。这提供了使您能够将这些功能虚拟化的附加好处,从而提供了虚拟序列化。

作者: Tanton 发布者: 2008 年 8 月 21 日

5

824 作者的声誉

当您构建容器并且想要为该类实现迭代器时,Friend会派上用场。

作者: rptony 发布者: 2008 年 8 月 25 日

153

8490 作者的声誉

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

可以说我不会将friend关键字用作您设计的必要组成部分。

作者: Daemin 发布者: 2008 年 8 月 30 日

3

501 作者的声誉

在我以前工作过的一家公司中,我们遇到了一个有趣的问题,当时我们用朋友来体面地对待。我在框架部门工作,我们在自定义OS上创建了基本的引擎级系统。在内部,我们有一个类结构:

         Game
        /    \
 TwoPlayer  SinglePlayer

所有这些类都是框架的一部分,并由我们的团队维护。该公司生产的游戏是在此框架的基础上构建的,该框架源自一个Games子公司。问题是Game具有与SinglePlayer和TwoPlayer需要访问的各种事物的接口,但是我们不想在框架类之外公开。解决方案是将这些接口设为私有,并允许TwoPlayer和SinglePlayer通过友谊访问它们。

确实,可以通过更好地实施我们的系统来解决整个问题,但是我们被束之高阁。

作者: Ray 发布者: 2008 年 9 月 4 日

10

2451 作者的声誉

安德鲁例子的另一个常见版本,可怕的代码对

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

换句话说,您可以使公共接口更小,并强制执行跨朋友函数中的类和对象的不变式。

作者: maccullt 发布者: 2008 年 9 月 10 日

-1

895 作者的声誉

朋友对于回调也很有用。您可以将回调实现为静态方法

class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};

在内部callback调用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;
}

隐藏下游不需要的东西变得更加容易。

作者: shash 发布者: 2008 年 12 月 12 日

91

423827 作者的声誉

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

朋友定义

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指针强制转换为能够使用数据成员指针访问派生类的数据字段。

作者: Johannes Schaub - litb 发布者: 2008 年 12 月 13 日

4

9390 作者的声誉

简短的答案是:在实际改善封装性时使用friend。提高可读性和可用性(运算符<<和>>是典范示例)也是一个很好的理由。

至于改进封装的示例,专门设计为与其他类的内部一起使用的类(想到的是测试类)是不错的选择。

作者: Gorpik 发布者: 2009 年 1 月 16 日

2

9486 作者的声誉

我仅使用friend-keyword对受保护的功能进行单元测试。有人会说您不应该测试受保护的功能。但是,我在添加新功能时发现此工具非常有用。

但是,我没有在类声明中直接使用关键字,而是使用了一个漂亮的模板技巧来实现这一点:

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: friendMe(this, someprotectedobject).someProtectedMethod();
 */
template
Friender & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender &)(instance);
}

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

friendMe(this, someClassInstance).someProtectedFunction();

至少适用于GCC和MSVC。

作者: larsmoa 发布者: 2009 年 9 月 7 日

16

214664 作者的声誉

编辑:再读一遍常见问题,我喜欢<< >>运算符重载并将其添加为这些类的朋友的想法,但是我不确定这不会破坏封装

如何破坏封装?

当您允许对数据成员的无限制访问时,将破坏封装。考虑以下类别:

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,我们可以通过查看类定义来查看谁有权访问的完整列表。

那么,这种封装的精确程度如何呢?相同数量的代码可以访问该类的私有成员。而且大家谁有权访问被列在类的定义。

friend不会破坏封装。这使某些Java程序员感到不舒服,因为当他们说“ OOP”时,实际上是指 “ Java”。当他们说“封装”时,并不是说“必须保护私有成员免受任意访问”,而是“一个Java类,其中唯一能够访问私有成员的功能是类成员”,即使对于有几个原因

首先,正如已经显示的那样,它过于严格。没有理由不应该允许朋友方法做同样的事情。

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

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

根据上述Java思维,这是完美封装的。 但是,它绝对允许任何人读取和修改x。那怎么办呢?(提示:没有)

底线:封装是关于能够控制哪些函数可以访问私有成员。这不是关于恰恰是这些功能的定义的位置。

作者: jalf 发布者: 2009 年 9 月 7 日

3

740 作者的声誉

另一种用途:朋友(+虚拟继承)可被用来避免从一个类派生(又名:“使类underivable”)=> 12

2开始

 class Fred;

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

 class Fred : private virtual FredBase {
 public:
   ...
 }; 
作者: Gian Paolo Ghilardi 发布者: 2009 年 9 月 7 日

4

1917 作者的声誉

C ++的创建者说这并没有打破任何封装原则,我将引用他的话:

“朋友”会违反封装吗? 不,不是的。就像成员身份一样,“朋友”是授予访问权限的明确机制。您不能(在符合标准的程序中)在不修改类源的情况下授予自己访问类的权限。

十分清楚...

作者: garzanti 发布者: 2010 年 2 月 11 日

1

2153 作者的声誉

Friend函数和类提供对私有和受保护类成员的直接访问,以避免在一般情况下破坏封装。大多数用法是与ostream一起使用的:我们希望能够输入:

Point p;
cout << p;

但是,这可能需要访问Point的私有数据,因此我们定义了重载运算符

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

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.

作者: Lubo Antonov 发布者: 2012 年 3 月 28 日

2

5338 作者的声誉

您必须在何时何地使用该friend关键字时非常小心,并且像您一样,我很少使用它。以下是一些使用注意事项friend和替代方法。

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

  • 使用访问器方法进行比较(检查每个ivar并确定相等性)。
  • 或者,您可以通过将其公开而直接访问所有成员。

第一种选择的问题在于,这可能是很多访问器,它比直接变量访问(略)慢,读起来又麻烦。第二种方法的问题是您完全破坏了封装。

如果可以定义一个外部函数,该函数仍然可以访问类的私有成员,那将是很好的选择。我们可以使用friend关键字:

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

equal(Beer, Beer)现在,该方法可以直接访问ab的私有成员(可能是char *brandfloat percentAlcohol等)。这是一个非常人为的示例,您将尽快将其应用于friend重载== operator,但我们将继续进行介绍。

注意事项:

  • A friend不是该类的成员函数
  • 这是一个普通功能,具有对该类私有成员的特殊访问权限
  • 请勿将所有访问器和mutator替换为朋友(您最好做所有事情public!)
  • 友谊不是互惠的
  • 友谊不是传递的
  • 友谊不是继承的
  • 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."

我只friends在用另一种方法很难做到时才使用。再举一个例子,很多矢量数学函数通常创建为friends因的互操作性Mat2x2Mat3x3Mat4x4Vec2Vec3Vec4,等它只是这么容易成为朋友,而不是让到处使用存取。正如所指出的那样,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经常使用,但是时不时地满足您的需求。希望这可以帮助!

作者: Ephemera 发布者: 2012 年 12 月 29 日

0

15356 作者的声誉

当不同的类(不从另一个类继承)使用另一个类的私有或受保护成员时,可以使用友谊。

朋友功能的典型用例是在两个不同类之间进行的操作,这些类访问二者的私有或受保护成员。

来自http://www.cplusplus.com/doc/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;
}
作者: kiriloff 发布者: 2014 年 2 月 6 日

8

310 作者的声誉

我找到了方便使用好友访问的地方:私有功能的单元测试。

作者: VladimirS 发布者: 2015 年 2 月 19 日

1

95 作者的声誉

在C ++中,“ friend”关键字在运算符重载和制作Bridge中很有用。

1.)运算符重载中的Friend关键字:运算符重载的
示例是:假设我们有一个“点”类,它具有两个浮点变量
“ x”(用于x坐标)和“ y”(用于y坐标)。现在我们必须重载"<<"(提取运算符),以便如果调用"cout << pointobj"它,它将打印x和y坐标(其中pointobj是Point类的对象)。为此,我们有两个选择:

   1.在“ ostream”类中重载“ operator <<()”函数。
   2.在“ Point”类中重载“ operator <<()”函数。
现在,第一个选项不好,因为如果我们需要为另一个不同的类再次重载该运算符,则必须再次对“ ostream”类进行更改。
这就是为什么第二个是最佳选择。现在编译器可以调用 "operator <<()"函数:

   1.使用ostream对象cout.As:cout.operator <<(Pointobj)(形成ostream类)。
2.不带对象的调用,例如:operator <<(cout,Pointobj)(来自Point类)。

因为我们已经在Point类中实现了重载。因此,要在没有对象的情况下调用此函数,我们必须添加"friend"关键字,因为我们可以在没有对象的情况下调用朋友功能。现在,函数声明将为:
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

2.)建立网桥的Friend关键字:
假设我们必须创建一个函数,其中必须访问两个或更多类的私有成员(通常称为“ bridge”)。操作方法:
要访问一个类的私有成员,它应该是该类的成员。现在,要访问其他类的私有成员,每个类都应将该函数声明为朋友函数。例如:假设有两个类A和B。一个函数"funcBridge()"要访问两个类的私有成员。然后两个类都应声明"funcBridge()"为:
friend return_type funcBridge(A &a_obj, B & b_obj);

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

作者: Shiv 发布者: 2016 年 3 月 25 日

0

131 作者的声誉

可能我从上面的答案中遗漏了一些东西,但是封装中的另一个重要概念是隐藏实现。减少对私有数据成员(类的实现细节)的访问,可以在以后轻松得多地修改代码。如果朋友直接访问私有数据,则对实现数据字段(私有数据)的任何更改都会破坏访问该数据的代码。使用访问方法通常可以消除这种情况。我认为非常重要。

作者: peterdcasey 发布者: 2016 年 4 月 7 日

0

5205 作者的声誉

这可能不是实际的用例情况,但可能有助于说明类之间使用friend的情况。

会所

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{};

如果您在这里查看这些类的关系;ClubHouse拥有各种不同类型的会员资格和会员访问权限。成员都是从父类或基类派生的,因为它们都共享一个通用的ID和枚举类型,并且外部类可以通过在基类中找到的访问函数来访问其ID和类型。

但是,通过成员及其派生类的此类层次结构以及它们与ClubHouse类的关系,VIPMember类是具有“特殊特权”的派生类中唯一的一个。基类和其他两个派生类无法访问ClubHouse的joinVIPEvent()方法,但VIP成员类具有该特权,就好像它可以完全访问该事件一样。

因此,对于VIPMember和ClubHouse,这是一条双向通行的道路,其他会员级别受到限制。

作者: Francis Cugler 发布者: 2017 年 2 月 12 日

2

41 作者的声誉

正如朋友声明的参考所言:

朋友声明出现在类主体中,并向出现朋友声明的类的私有和受保护成员授予函数或其他类访问权限。

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

作者: lixunhuan 发布者: 2017 年 8 月 18 日
32x32