Virtual friend functions for a base class?

2019-03-15 16:34发布

问题:

I'm in the proccess of learning the language and this is a noob doubt.

Is it possible to use a virtual friend function? I don't know if it's possible, I didn't even test it but it could be useful in some situations. For example, for the overloaded operator<<().

DerivedClass dc;
BaseClass &rbc = dc;
cout << rbc;

My guess is it's possible, but I'm not sure since a friend function is not implemented in the class design, and theoretically is not part of it (though in this example, conceptually it makes sense that operator<<() should be a method, but due to syntax limitations it's not possible to implement it as one).

EDIT: my concern is related with this example:

BaseClass bc;
DerivedClass dc;
BaseClass *pArr[2];
pArr[1] = bc;
pArr[2] = dc;
for (int i = 0; i < 2; i++)
    cout << pArr[i];

in this array of mixed objects, I want the correct operator<<() called for each one.

回答1:

Nope, friend virtual functions doesn't make sense at all.

friend functions are such, that are not methods (a.k.a. member functions) and have the right to access private/protected members of a class.

virtual functions can only be member functions. You can't have virtual non-member function.


You can make the operator<< take a reference to a base class and then call some virtual member function. This way, you can make the operator<< "almost virtual" :)


For example

class A
{
public:
    virtual void f() const { std::cout << "base"; }
};
class B: public A
{
public:
    virtual void f() const { std::cout << "derived"; }
};

std::ostream& operator<<(std::ostream& os, const A& a )
{
     a.f();
     return os;
}

int main()
{
    B b;
    std::cout << b << std::endl;

    return 0;
}

will print derived.



回答2:

Virtual Friend Function Idiom

friend functions in C++ can not be declared virtual and therefore no dynamic binding of friend functions is possible. Applying a friend function to an entire hierarchy of classes becomes awkward if an overloaded friend function is needed for every class in the hierarchy. This lack of support for dynamic binding makes it hard to justify that are in fact an extension of the class's interface. Virtual friend function idiom addresses this concern elegantly.

You need to use virtual Friend function idiom. What it says in gist is keep a virtual function in Base and let the friend function call that function. which will polymorphically call that function of Derived Class

Copying the example directly from the link

class Base {
  public:
    friend ostream& operator << (ostream& o, const Base& b);
  protected:
    virtual void print(ostream& o) const{ ... }
};
/* make sure to put this function into the header file */
inline std::ostream& operator<< (std::ostream& o, const Base& b){
  b.print(o); // delegate the work to a polymorphic member function.
  return o;
}

class Derived : public Base {
  protected:
    virtual void print(ostream& o) const{ ... }
};


回答3:

You can solve this without friend functions, using public virtual methods only:

struct BaseClass {
  virtual void print(std::ostream& os) const;
};

struct DerivedClass {
  virtual void print(std::ostream& os) const;
};

std::ostream& operator<<(std::ostream& os, const BaseClass& obj) {
  obj.print(os);
  return os;
}

If it doesn't make sense for the print method to be public, then the ostream& operator<< can be declared as friend.



回答4:

Virtual Functions are something else than friend functions. Virtual Functions are used only with use of inheritance in the program with one class as base class and other as derived classes. Virtual functions are used for dynamic binding of objects. It means that you can store an object of derived class in a pointer of base class and still call the method of that partiular derived class. The concept is known as Polymorphism.

Friend functions are used to access the private interface of a class. They could be used even when no inheritance has been used in your class.



回答5:

You can't be both a friend and a virtual function of the same class. However, a friend operator could call a virtual function from the object it is printing.

ostream& operator<<(ostream& stream, const BaseClass& rbc)
{
    rbc.print_on(stream);
    return stream;
}