26

(edited from original post to change "BaseMessage" to "const BaseMessage&")

Hello All, I'm very new to C++, so I hope you folks can help me "see the errors of my ways".

I have a hierarchy of messages, and I'm trying to use an abstract base class to enforce an interface. In particular, I want to force each derived message to provide an overloaded << operator.

When I try doing this with something like this:

class BaseMessage
{
public:

// some non-pure virtual function declarations
// some pure virtual function declarations

virtual ostream& operator<<(ostream& stream, const BaseMessage& objectArg) = 0;

}

the compiler complains that

"error: cannot declare parameter ‘objectArg’ to be of abstract type ‘BaseMessage’

I believe there are also "friend" issues involved here, but when I tried to declare it as:

virtual friend ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;

the compiler added an addition error

"error: virtual functions cannot be friends"

Is there a way to ensure that all of my derived (message) classes provide an "<<" ostream operator?

Thanks Much,

Steve

Steve S.
  • 263
  • 1
  • 3
  • 6
  • Nilolai's answer is the best solution for what you're trying to accomplish. However, the specific error you were getting is because you're trying to pass a BaseMessage object by value (the second argument to your virtual operator<<). This can't work because BaseMessage includes a pure virtual function (the same virtual operator<<), so it isn't possible to construct an instance of a BaseMessage to pass by value. Notice that Nilolai's version of operator<< takes its second argument by reference (it will be some class derived from Base). – Stephen C. Steel Jan 13 '10 at 18:40

5 Answers5

44

The common convention for this is to have a friend output operator at the base level and have it call private virtual function:

class Base
{
public:

    /// don't forget this
    virtual ~Base();

    /// std stream interface
    friend std::ostream& operator<<( std::ostream& out, const Base& b )
    {
        b.Print( out );
        return out;
    }

private:

    /// derivation interface
    virtual void Print( std::ostream& ) const =0;
};
Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • This is the only working solution I've seen on the page here: everyone else is talking outa their arse :) (try compiling your code people!) +1 – jkp Jan 13 '10 at 18:19
  • This worked great, thanks Nikolai I can see many places to use this pattern. -- Steve – Steve S. Jan 13 '10 at 19:06
  • I didn't know you could define the friend function directly inside the class declaration like that. Good to know! Textbooks would always put the friend declaration inside and the definition outside. Does defining directly inside a class also work with a `swap` friend function? – Emile Cormier Jan 13 '10 at 20:08
3

Abstract class can't be instantiated ,so do this:

virtual ostream& operator<<(ostream& stream, const Base &objectArg) = 0; 

Virtual function must be instance member function whereas friend function is non-member function , so it can't be declared as virtual.

Similarly static function can't be virtual since its a class method not instance method.

My Suggestion is :

class Base {
 public:
 virtual ostream&  print (ostream& stream) const = 0; 
};


class Derived :public Base {
 public:
 virtual ostream&  print (ostream& stream) const { //do something } 
};

ostream& operator <<(ostream& stream, const BaseMessage &objectArg) 
{
  return objectArg.print(stream); 
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Ashish
  • 8,441
  • 12
  • 55
  • 92
3

The stream operators like:

virtual ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;

simply cannot be member functions, and so cannot be virtual functions. The reason for this is that when you say something like:

a << b;

you are really saying

a.operator<<( b );

In this case a is a stream, not an instance of your class, so the operator cannot be a member of your class. You should typically make it a free (non-member) function, which accesses your class instance via suitable member functions.

1

The declaration for operator<<() is wrong. For the binary version of op <<, you don't have to declare the second parameter -- it is assumed to be this if op<< is a member function of the class:

virtual ostream& operator<<(ostream& stream) = 0;

Moreover, as mentioned by others the stream insertion operators have to be global functions. Making them member functions just won't work.

Also not something not related to your question, but a problem nontheless. In your original implementation you passed an abstract base class object by value, rather than by reference or by pointer. When you do this, you "slice the object". Your intention, I'm sure, was to pass a base class pointer to a polymorphic type to the function, and then have the function call methods polymorphically. For example, you were trying to do something similar to this:

#include <cstdio>
#include <string>
#include <iostream>
using namespace std;

class Base 
{
public:
    virtual void dump() 
    {
        cout << "Base";
    };
};

class Der : public Base
{
public:
    void dump()
    {
        cout << "Der";
    };
};

void DumpIt(Base b)
{
    b.dump();
}


int main() 
{
    Der obj;
    DumpIt(obj);
    return 0;

}

...and expecting the output to be "Der" But in fact the output is "Base" because of Object Slicing. Because the DumpIt() function takes the Base object by value, a new temporary Base object is created based on the original. In order to get the functionality you were expecting, you need to pass by reference or by pointer:

void DumpIt(Base & b)
{
    b.dump();
}

The output from this function is "Der".

John Dibling
  • 99,718
  • 31
  • 186
  • 324
0

The problem here is that "BaseMessage objectArg" is saying that the objectArg object should be passed by value.

This is impossible since you have made the class abstract with your pure virtual call. A pass by reference "BaseMessage& objectArg" or a pass by pointer "BaseMessage* objectArg" would make this error go away.

Pass by value means that it creates a new instance of the variable using the copy constructor. Since you have made sure that it is impossible to create an instance of BaseMessage, this cannot be done.

daramarak
  • 6,115
  • 1
  • 31
  • 50