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

C++ 您应该在何时使用';朋友';在C++;?,c++,oop,encapsulation,friend,C++,Oop,Encapsulation,Friend,我一直在通读这份声明,对这份声明感到好奇。我个人从未使用过它,但是我对探索这种语言感兴趣 使用朋友的好例子是什么 阅读FAQ的时间再长一点,我喜欢操作符重载的想法,并将其添加为这些类的朋友。然而,我不确定这如何不破坏封装。这些异常何时能保持在OOP的严格范围内?在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友 除了让您不使用设置函数就访问成员变量之外,它实际上没有任何用处。首先(IMO)不要听那些说friend没有用处的人说。这是有用的。在许多情况下,您将拥有数据或功能不打算公开

我一直在通读这份声明,对这份声明感到好奇。我个人从未使用过它,但是我对探索这种语言感兴趣

使用
朋友
的好例子是什么



阅读FAQ的时间再长一点,我喜欢
操作符重载的想法,并将其添加为这些类的朋友。然而,我不确定这如何不破坏封装。这些异常何时能保持在OOP的严格范围内?

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

除了让您不使用设置函数就访问成员变量之外,它实际上没有任何用处。

首先(IMO)不要听那些说
friend
没有用处的人说。这是有用的。在许多情况下,您将拥有数据或功能不打算公开的对象。这一点在大型代码库中尤其明显,因为许多作者可能只对不同领域略知一二

友元说明符有多种替代方法,但它们通常比较麻烦(cpp级别的具体类/屏蔽的typedef)或不简单(注释或函数名约定)

关于答案

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

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

也许你还需要一个构造函数来保护它并强制它 人们使用你的“朋友”工厂


。。。好吧,坦白说,你可以不使用它。

你使用私有/受保护/公共权限控制成员和函数的访问权限? 因此,假设这三个层次中的每一个都是清晰的,那么应该很清楚我们遗漏了什么

例如,将成员/函数声明为protected是非常通用的。您的意思是每个人都无法使用此功能(当然继承的孩子除外)。但是例外情况呢?每个安全系统都会让你有某种类型的“白名单”,对吗

因此,friend让您可以灵活地进行坚固的对象隔离,但允许为您认为合理的事情创建“漏洞”

我想人们会说不需要它,因为总有一种设计可以不用它。我认为这类似于对全局变量的讨论:你永远不应该使用它们,总有一种方法可以不用它们……但在现实中,你会看到这样的情况(几乎)最优雅的方式…我认为朋友也是如此

除了让您不使用设置函数就访问成员变量之外,它实际上没有任何用处

嗯,这并不是正确的看法。
其思想是控制谁可以访问什么,有没有设置函数与此无关。

典型的例子是重载操作符:这里没有破坏封装,因为类本身决定谁可以访问其私有成员。只有当这可能是由类外引起的,例如。如果您的
操作符您可以遵守最严格和最纯粹的OOP原则,并确保任何类的数据成员都没有访问器,因此所有对象都必须是唯一可以了解其数据的对象,唯一的操作方法是通过间接消息,即方法

<> P>但即使C++有一个强的>内部<强>可见性关键字,java也有其默认的<>强>包可访问级别。对于C++,它实际上通过更接近于OOP的理想,通过指定<强> > < <强> >其他类和<强> > <强>其他类,从而最小化可视性到类的妥协。我们可以看到它

<>我并不真正使用C++,但是如果C语言有朋友,我会代替汇编全局的<强>内部<强>修饰符,实际上我使用了很多。它并不真正地破坏包内,因为在.NET<强>中的部署单位是一个程序集。

但是,C++有一个强的>国际化的Visualto to /Stand属性(其他程序集),它的行为就像一个交叉组件<强>朋友>强>机制。微软使用这个工具来设计可视化设计器程序集。

< P>。 朋友能知道我的一切吗


更新:我发现这个关于“朋友”关键字的有价值的答案

“朋友”是一种明确的授予访问权限的机制,就像成员资格一样


在TDD中,我多次使用C++中的“朋友”关键字。
不,这只是单向的友谊:`(

我在创建类时使用的一个特定实例是
friend
。通过
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;
}
// 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;
}
关于operator>没有很好的理由让这些操作符成为朋友。的确,它们不应该是成员函数,但它们也不需要成为朋友

最好的方法是创建公共打印(ostream&)和读取(istream&)函数
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);
}
class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};
class MyFoo
{
    friend void callback(void * data, void * callData);
    void localCallback();
}
class MyFooPrivate;
class MyFoo
{
    friend class MyFooPrivate;
public:
    MyFoo();
    // Public stuff
private:
    MyFooPrivate _private;
    // Other private members as needed
};
class MyFooPrivate
{
public:
   MyFoo *owner;
   // Your complexity here
};

MyFoo::MyFoo()
{
    this->_private->owner = this;
}
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
}
// 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();
};
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();
class c1 {
public:
  int x;
};

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

class c3 {
  friend int foo();
private:
  int x;
};
class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};
 class Fred;

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

 class Fred : private virtual FredBase {
 public:
   ...
 }; 
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;
    }
}; 
class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};
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 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;
}
1.Overload "operator <<()" function in "ostream" class. 2.Overload "operator<<()" function in "Point" class. 1.Using ostream object cout.As: cout.operator<<(Pointobj) (form ostream class).
2.Call without an object.As: operator<<(cout, Pointobj) (from Point class).
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{};