Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Abstract class operator overloading and interface enforcement question

Tags:

c++

People also ask

What are the restrictions and limitations to operator overloading?

New operators can not be created. 2) Arity of the operators cannot be changed. 3) Precedence and associativity of the operators cannot be changed. 4) Overloaded operators cannot have default arguments except the function call operator () which can have default arguments.

Can we overload address of operator?

You cannot overload the following operators: . You cannot overload the preprocessor symbols # and ## . An operator function can be either a nonstatic member function, or a nonmember function with at least one parameter that has class, reference to class, enumeration, or reference to enumeration type.

Can we overload member access operator?

The class member access operator (->) can be overloaded but it is bit trickier. It is defined to give a class type a "pointer-like" behavior. The operator -> must be a member function. If used, its return type must be a pointer or an object of a class to which you can apply.

What are the two types of operator overloading?

Overloading unary operator. Overloading binary operator. Overloading binary operator using a friend function.


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

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.


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