Unlike Java C ++, object and value references are distinguished, i.e. the objects themselves. When you pass objects of a derived type to a function that takes a value of the base type, you get a sparse object: it will contain only a copy of the base part of the object and nothing of the derived type. For example, your constructor
Square(Piece piece)
takes its argument by value and will always have a type of Piece and never has any derived type: the argument, if it has a derived type, received a cut. You can pass objects by reference using a type notation
Square(Piece& piece)
if the object referenced by Piece is modified or
Square(Piece const& piece)
if the object referenced by Piece must be immutable. In your context, you most likely also want to deal with lifecycle management of your objects, which is probably best done using objects allocated on the heap, using new and supported by some smart pointer, for example, std :: shared_ptr`.
Now to your output functions: the called function is always statically resolved based on the static type, that is, the type declared and visible at compile time. When the correct function is called, if it is declared virtual , it sends a possible redefinition function based on the dynamic type of the object, that is, the virtual dispatch is performed at runtime. For your output statements, this means that they are only selected based on the static type, which in your case is always Piece . The usual way to deal with this is to use the virtual function and send this function from the actual output statement, for example:
class Piece { protected: virtual std::ostream& do_print(std::ostream& out) const = 0; public: std::ostream& print(std::ostream& out) const { return this->do_print(out); } }; std::ostream& operator<< (std::ostream& out, Piece const& piece) { return piece.print(out); } class Man: public Piece { protected: std::ostream& do_print(std::ostream& out) { return out << "Man";
With this setting, you can call the Piece static output operator using a reference to an object of this type and get the output selected by the dynamic type, for example:
class Square { std::shared_ptr<Piece> d_piece; public: Square(std::shared_ptr<Piece> piece): d_piece(piece) {} Piece const& get_piece() const { return *this->d_piece; } }; std::ostream& operator<< (std::ostream& out, Square const& square) { return out << square.get_piece(); }
An incorrect transition from print() to do_print() is actually not required, but if you have several overloads with virtual overload with the same name and you override only one of them, all other versions in the base class get hidden. Since print() not overridden and do_print() not called by the user, the problem of overload hiding is somewhat reduced.