9

The following is a rough sample of what the code looks like, the question is how can I have DerivedOne and DerivedTwo have a overloaded << operator but store these objects in a vector of Base*.

As for what I want to achieve; I want to be able to loop through the objects vector and output the information that I tell it to in the DerivedOne and DerivedTwo.

vector<Base*> objects;

class Base
{
 private:
 object Data
 public:
 object getData() { return Data; }
};

class DerivedOne : public Base
{
}

class DerivedTwo : public Base
{
}

Now I know there is this, but it wont work for my purposes.

friend ostream &operator<<(ostream &stream, Object object)
{
    return stream << "Test" << endl;
}
Nathan Pierson
  • 5,461
  • 1
  • 12
  • 30
Baraphor
  • 249
  • 4
  • 16
  • 1
    Have you tried having the `friend` declaration for each of the derived classes within each of them? I'm not *certain*, but I think ADL will find the correct functions for you if you do that. – KRyan Aug 10 '12 at 16:29
  • Please see the answer to http://stackoverflow.com/questions/2059058/c-abstract-class-operator-overloading-and-interface-enforcement-question. – Michał Wróbel Aug 10 '12 at 16:33
  • @DragoonWraith: I don't think ADL will work there, since ADL works on the static type of `Base`, not the Dynamic type. – Mooing Duck Aug 10 '12 at 19:04

3 Answers3

20

Make your virtual methods private to separate how an object is used from how its behavior can be customized by derived classes.

Here's a similar to other answers solution but the virtual method is private:

#include <iostream>

namespace {
  class Base {
    // private (there is no need to call it in subclasses)
    virtual std::ostream& doprint(std::ostream&) const = 0;
  public:
    friend std::ostream& operator << (std::ostream& os, const Base& b) {
      return b.doprint(os); // polymorphic print via reference
    }

    virtual ~Base() {} // allow polymorphic delete
  };


  class DerivedOne : public Base {
    std::ostream& doprint(std::ostream& os) const {
      return os << "One";
    }
  public:
    DerivedOne() { std::cerr << "hi " << *this << "\n"; } // use << inside class
    ~DerivedOne() { std::cerr << "bye " << *this << "\n"; }
  };
}

Example

#include <memory>
#include <vector>

int main () {
  using namespace std;
  // wrap Base* with shared_ptr<> to put it in a vector
  vector<shared_ptr<Base>> v{ make_shared<DerivedOne>() };
  for (auto i: v) cout << *i << " ";
  cout << endl;
}

Output

hi One
One 
bye One
jfs
  • 399,953
  • 195
  • 994
  • 1,670
6

You can do something like this:

struct Base {
    virtual ~Base () {}
    virtual std::ostream & output (std::ostream &) const = 0;
};

std::ostream & operator << (std::ostream &os, const Base &b) {
    return b.output(os);
}

Then, when applying operator<< on Base, it will call the derived output method.

jxh
  • 69,070
  • 8
  • 110
  • 193
2

As far your problem is described you have a collection of dynamically allocated polymorphic objects.

The solution can change depending on how these object adhere to certain design disciplines:

  1. are you object "leaf only objects"? (not referring to other object themselves)?
  2. are there just single acyclic references (do your object form only trees of subobjects)?
  3. are there multiple path between references (a same object referred by two or more)
  4. are there cyclic references?
  5. are there references to other object members?

The situation can become very complex depending on the situation and depending if you can tolerate or not multiple output of a same object and how can you break eventual loops.

A first thing you cannot do is use Base as a value parameter (like in your , since that creates a copy (admitting your objects are copyable: if they refer to other object what does a copy do? multiplies the paths? Clones the referred object as well?)

If you are in situation (1.), you just need a virtual function taking std::ostream&, to be overridden in all the leaf objects. Then you need an overload for operator<<(std::ostream&, Base*) and another for (std::ostream&, const std::vector<Base*>&)

like this:

class Base
{
    ...
public:
    virtual ~Base() {} //must be polymorphic
protected:
    virtual void out(std::ostream& s) =0;

    friend std::ostream& operator<<(std::ostream& st, Base* pb)
    { pb->out(st); return st; }
};

class Derived1: public Base
{
    ...
protected:
    virtual void out(std::ostream& s)
    {
        s << "Derived1 at " << this << " address " << std::endl;
    }
};


class Derived2: public Base
{
    ...
protected:
    virtual void out(std::ostream& s)
    {
        s << "Derived2 at " << this << " address " << std::endl;
    }
};

std::ostream& operator<<(std::ostream& s, const std::vector<Base*>& v)
{
    s << "there are " << v.size() << " objects:" << std::endl;
    for(auto& i:v)
        s << i;
    return s;
}

If you are in sutuation (2.) yopu may ave a

class Derived3: public Base
{
    ...
    Base* ptrOwnedobject;
};

You can simply implement the Derived3::out function to recourse into the owned child.

If you are in situation (3.), and you cannot tolerate a same object to be printed more time you ave different parents sharing a same child. You should somehow mark the child so that you don't save it twice or more, and un-mark all of the children after finishing the printing (you will most likely set up that mechanism into the Base class).

If you are in situation (4.) then what said about (3.) becomes mandatory to avoid infinite recursion.

If you are in situation (5.) you must also discover which object a member belongs (not trivial). If you also have multiple inheritance and virtual bases... things are even more complex.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63