When should you use 'friend' in C++?

前端 未结 30 1608
孤街浪徒
孤街浪徒 2020-11-22 10:12

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.

相关标签:
30条回答
  • 2020-11-22 10:59

    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 );
    };
    
    0 讨论(0)
  • 2020-11-22 10:59

    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.

    0 讨论(0)
  • 2020-11-22 11:00

    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.

    0 讨论(0)
  • 2020-11-22 11:00

    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.

    0 讨论(0)
  • 2020-11-22 11:01

    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.
    0 讨论(0)
  • 2020-11-22 11:02

    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.

    0 讨论(0)
提交回复
热议问题