C++ Abstract class operator overloading and interface enforcement question

Viewed 11449

(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

5 Answers

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

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

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.

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".

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.

Related