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

前端 未结 30 1606
孤街浪徒
孤街浪徒 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:49

    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.

    0 讨论(0)
  • 2020-11-22 10:49

    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!

    0 讨论(0)
  • 2020-11-22 10:49

    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.

    0 讨论(0)
  • 2020-11-22 10:51

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

    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.

    0 讨论(0)
  • 2020-11-22 10:53

    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.

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