Problems understanding c ++ output

I bring a lot of assumptions from Java into my C ++ study, which seems to have puzzled me again. I don’t have a vocabulary to eloquently say what I expect to see from the next program, so I’ll just talk about it and say what I expected to see:

#include <iostream> #include <vector> using namespace std; class Piece {}; class Man : public Piece {}; class Square { Piece p; public: Square(Piece p) : p(p) { }; Piece get_piece() const { return p; } }; ostream& operator<<(ostream& os, const Piece& p) { cout << "Piece"; return os; } ostream& operator<<(ostream& os, const Man& m) { cout << "Man"; return os; } ostream& operator<<(ostream& os, const Square& s) { cout << s.get_piece() << '\n'; return os; } int main() { Square sq = Square(Man()); cout << sq; } 

When I run this program, the conclusion is Piece , but I was expecting to see Man . Is this called runtime polymorphism? I thought this was reserved for functions, but I don't know. The "equivalent" program in Java prints what I expect, Man , but I don’t know how to get this C ++ program to do this. What am I missing?

+4
source share
2 answers

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"; // note: you want to use out, not std::cout here } }; 

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.

+6
source

It's all about “static polymorphism,” for example, about method overloading. The specific version of operator<< is selected by the compiler based on the type of compilation time of the variables that it sees; since get_piece() returns Piece that the operator<< version is selected.

I must point out that you are mistaken in an equivalent Java program: overloading the Java method is also arbitrated by the compiler and is based on compile time types. In a truly equivalent Java program, Piece will be displayed.

+5
source

Source: https://habr.com/ru/post/1497623/


All Articles