0

As you can see, only the overloaded version of the stream insertion operator for the base class is called on both instances. I understand why it's so. It's because there is no dynamic binding. But, how do I fix it?

#include <iostream>

using namespace std;

class A {
    int i;
    char c;
public:
    A(int i = 0, char c = ' ') {
        this->i = i;
        this->c = c;
    }
    int getI() { return i; }
    char getC() { return c; }
    friend ostream& operator << (ostream&, A&);
};

class B : public A {
    double d;
public:
    B(int i = 0, char c = ' ', double d = 0.0) : A(i, c), d(d) {}
    friend ostream& operator << (ostream&, B&);
};

ostream& operator << (ostream& out, A& a) {
    out << "\nInteger: " << a.i << "\nCharacter: " << a.c << endl;
    return out;
}

ostream& operator << (ostream& out, B& b) {
    out << "\nInteger: " << b.getI() << "\nCharacter: " << b.getC() << "\nDouble: " << b.d << endl;
    return out;
}

int main() {
    A* a = new A (10, 'x');
    B* b = new B(20, 'y', 5.23);
    A* array[] = { a, b };
    cout << *(array[0]);
    cout << "\n______________________________\n";
    cout << *(array[1]);
    delete a;
    delete b;
    cin.get();
    return 0;
}

How can I make cout << *(array[1]); call the overloaded stream insertion operator that takes an object of B as one of it's arguments?

User2k14
  • 69
  • 5
  • Read this: ["What is object slicing?"](https://stackoverflow.com/questions/274626/what-is-object-slicing). Then look how you're `A` array suffers from the what is described in the question and answers. – WhozCraig Apr 27 '15 at 01:59
  • @WhozCraig There won't be slicing, the objects are being passed by reference. – phantom Apr 27 '15 at 02:02
  • @Phantom look at the declaration `array`. You can't have arrays of native references. It is a flat-out array of `A` only. The initializer using `b` (`array[1]`) is sliced. – WhozCraig Apr 27 '15 at 02:03
  • @WhozCraig Oops, was looking at the `operator <<` overloads. – phantom Apr 27 '15 at 02:05
  • @Phantom yeah, and looking closer, that array does *nothing* but introduce problems via slicing. It serves no useful purpose at all. The OP could have just sent `a` and `b` to fire the operators. – WhozCraig Apr 27 '15 at 02:06
  • [Very related question](http://stackoverflow.com/questions/29885223/overloading-the-stream-insertion-operator-for-a-class). – R Sahu Apr 27 '15 at 02:06
  • Thanks for fixing the slicing problem. – WhozCraig Apr 27 '15 at 02:09

2 Answers2

2

You can define virtual member helper functions.

class A {
public:
    virtual void toStream(ostream& out) const {
        out << "\nInteger: " << i << "\nCharacter: " << c << endl;
    }
};

class B : public A {
public:
    virtual void toStream(ostream& out) const {
        out << "\nInteger: " << getI() << "\nCharacter: " << getC() << "\nDouble: " << d << endl;
    }
};

ostream& operator << (ostream& out, const A& a) {
    a.toStream(out);
    return out;
}

You don't even need 2 operator<<()'s anymore.

For operator >> (istream& in, A& a), similar tricks could be done.

class A {
public:
    virtual void fromStream(istream& in) {
        // Fill data members with in
    }
};

class B : public A {
public:
    virtual void fromStream(istream& in) {
        // Fill data members with in
    }
};

istream& operator >> (istream& in, A& a) {
    a.fromStream(in);
    return in;
}
timrau
  • 22,578
  • 4
  • 51
  • 64
  • What is the solution as a friend function? – User2k14 Apr 27 '15 at 02:55
  • If you insist in not adding virtual member functions, then you could declare destructors as virtual, and use `dynamic_cast` to check if the `A&` passed in was indeed `B&`. – timrau Apr 27 '15 at 02:59
  • Oh...that look a bit clumsy. Just for the sake of completeness, can you show how to apply the previous solution that you posted to the stream extraction (>>) operator? – User2k14 Apr 27 '15 at 03:10
  • May be you can define a function called `fromStream` this time. :) – User2k14 Apr 27 '15 at 03:11
  • @User2k14 done. Basically the same with some modifications in constness of parameters. – timrau Apr 27 '15 at 03:15
  • Thanks man. Love you. BTW, the input stream operator doesn't work for std::string objects. :/ – User2k14 Apr 27 '15 at 03:31
  • It says: `error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::istream' (or there is no acceptable conversion)` – User2k14 Apr 27 '15 at 03:36
  • @User2k14 Sounds strange. You may create another question for this issue. – timrau Apr 27 '15 at 03:39
0

If A knows it will be inherited from, you can use Non Virtual Interface: make the operators friend, and write a protected (it's not part of the class interface) virtual function that performs the write.

Ekleog
  • 1,054
  • 7
  • 19