Streaming Operator and Polymorphic Base Class Lists


Keywords:c++ 


Question: 

I have a vector of classes that I would like would like to display their respective parameters to the screen. Each class inherits from CBase and the vector is simply a list of pointers of type CBase.

I would like to avoid coupling the display code to the class. Therefore, I put the stream operators outside the class definition.

So my classes are defined as

class CBase 
{
public:
    virtual ~CBase(){}
};

class CChildA : public CBase
{
};

class CChildB : public CBase
{
};

and the vector is setup like this:

 void main()
{

    CChildA A;
    CChildB B;

    std::vector<CBase*> myList;
    myList.push_back(&A);
    myList.push_back(&B);

    display(myList);
}

and the individual display operators might be:

std::ostream & operator<<(std::ostream & os, const CChildA & item)
{
    os << "Child A Values Here";
    return os;
}

std::ostream & operator<<(std::ostream & os, const CChildB & item)
{
    os << "Child B Values Here";
    return os;
}

To implement the display function we have a problem because << will not select the right class, and not all classes will have a stream operator define (for example, class CChildC not shown here). Therefore, a first attempt of display might be:

void display(std::vector<CBase*> &aList)
{
    for(std::vector<CBase*>::iterator it = aList.begin(); it != aList.end(); it++)
    {
        if(CChildA * ca = dynamic_cast<CChildA*>(*it))
            std::cout << *ca << "\n";
        else if(CChildB * cb = dynamic_cast<CChildB*>(*it))
            std::cout << *cb << "\n"; 
    }
}

But I have been told that the use of dynamic_cast is frowned on. Is there an easy way to make this happen without dynamic_cast?


1 Answer: 

At this point I have to ask you,

Should all your derived classes implement the streaming operation?

If the answer to that is 'Yea' or 'Of course!', then why are you not making it part of your interface?

Yeah, you will be coupled to the fact that you now support printing the object to a stream. But, you just said it yourself, it's a property that all the derived classes must have.

Let's think of another question -

If i should add a new derived class, should other places in the code change?

Well... sure, but these should be small changes. You don't want to change every place that was using the streaming operator. That would be a violation of the OCP - Open Close Principle.


Regarding this -

I would like to avoid coupling the display code to the class. Therefore, I put the stream operators outside the class definition.

You either:

  1. Have the wrong assumption that the Display should be decoupled from the Base
  2. Got an ISP - Interface Segregation Principle violation.

If every derived class should be displayable and the container holding the data should display each element - just add the relevant method to the Base class.

If most of your code is irrelevant to the display code, and these classes really do have nothing to do with the display mechanism - Each derived class should probably inherit from two interfaces (purely abstract classes)

  1. The Base class
  2. The IDisplayable (name it as you wish)

And the display method you are showing should receive a vector<IDisplayable*> instead of vector<Base*>.

Note

If you are forced to take a vector<Base*>, you should iterate the elements and cast them to IDisplayable* (static_cast or dynamic_cast, that really depends on your situation)

This is different from your current solution as you only need one cast (instead of having a cast for each derived class)

Note2

I'm not a big fan of overloading the '<<' and '>>' operators. I would consider a pure virtual function called 'display' in the interface instead of virtual operators.