Slicing and operator overloading in C ++

Background Information

I programmed in Java for a while, and I just switched to C ++ just a few months ago, so I apologize if the answer is just something stupid that I missed! Now that everything has been said, the time has come for this problem! I am developing a basic text engine, and I recently ran into an interesting and unlikely problem. I tried to test it on a smaller scale in the program below and decided to just show it (unlike my actual game code) so as not to strangle the screen and make the problem less confusing. The problem modeled below reflects the problem with my actual code, simply without fluffy distractors.

Problem

Essentially, the problem is polymorphism. I want to overload the output operator "<for a display function that is unique for each object in the hierarchy. The problem is that when I call this operator from the list in which these hierarchy elements are stored, they lose their identity and call the base class output operator. Usually this could be solved by replacing the operational overloads with a simple mapping method, marking the virtual mapping method and continuing my lucky day. I don’t really mind making changes to the code, but now I'm just wondering. Collec overload operators in a hierarchy that leads to the fact that I'm going here?

Code [Example]

#include <vector> #include <iostream> #include <string> using namespace std; class Polygon { friend ostream& operator<<(ostream& os, const Polygon& p); public: private: }; class Rectangle : public Polygon { friend ostream& operator<<(ostream& os, const Rectangle& r); public: private: }; class Square : public Rectangle { friend ostream& operator<<(ostream& os, const Square& s); public: private: }; ostream& operator<<(ostream& os, const Polygon& p) { os << "Polygon!" << endl; return os; } ostream& operator<<(ostream& os, const Rectangle& r) { os << "Rectangle!" << endl; return os; } ostream& operator<<(ostream& os, const Square& s) { os << "Square!" << endl; return os; } int main() { vector<Polygon*> listOfPoly; listOfPoly.push_back(new Polygon()); listOfPoly.push_back(new Rectangle()); listOfPoly.push_back(new Square()); for(Polygon* p : listOfPoly) { cout << *p; } } 

Output for code [Example]

 Polygon! Polygon! Polygon! 

Desired output for code [Example]

 Polygon! Rectangle! Square! 
+6
source share
3 answers

Is there a way to overload the operators in the hierarchy, which leads to what I'm going to do here?

No.

The problem is that the operators are not in your hierarchy. The friend keyword here is simply forward-declaring a free function and giving it privileged access to the class. This does not make it a method, so it cannot be virtual.


Note that operator overloading is just syntactic sugar. Expression

 os << shape; 

can either go to a free function (like you are here)

 ostream& operator<< (ostream&, Polygon&); 

or a member of the left operand, for example

 ostream& ostream::operator<<(Polygon&); 

(obviously, the second case does not exist here, because you will need to modify std::ostream ). That the syntax cannot resolve is a member of the right operand.

So, you can have a free function operator (which is not necessarily virtual) or a method in the left operand (which can be virtual), but not a method in the right operand.


The usual solution is to have one overload for the top level of the hierarchy that is sent to the virtual method. So:

 class Polygon { public: virtual ostream& format(ostream&); }; ostream& operator<<(ostream& os, const Polygon& p) { return p.format(os); } 

Now we simply implement Polygon::format and redefine it in derived classes.


Aside, using friend still carries the smell of code. In general, he believed that a better style gives your class a public interface complete enough so that external code does not need privileged access to work with it.

A further digression for reference information: multiple sending is a thing, and C ++ overload resolution does an excellent job when all types of arguments are known statically. What is not being processed is to search for the dynamic type of each argument at runtime, and then try to find the best overload (which, if you count multiple class hierarchies, is clearly non-trivial).

If you replace the polymorphic run-time list with the polymorphic compile-time tuple and go to it, the original overload scheme will be sent correctly.

+5
source

The operator is not a virtual member. This means that it is not possible to send to a derived class. Dynamically, you can send only virtual functions. A typical strategy in this case is to create a regular operator that sends a virtual function to the interface to do the work.

By the way, as an added bonus, new is a rather useless language function in C ++. You need to find smart pointers, otherwise each line of code that you write, you just have to rewrite for endless life problems.

Creating virtual statements is a pretty bad idea, as a rule. This is because you can dynamically send this messages, but operators are often used with a type that implements them in RHS, or as non-members. Overloads of non-member operators are more powerful than members.

+3
source

You can add a virtual Display () function to the Rectangle base class. Each class in the hierarchy can override a function and implement it differently.

You only need to define a single <<statement that accepts Polygon & as a parameter. The function itself simply calls the virtual display function.

 class Polygon { public: virtual void Display(ostream& os) const { os << "Polygon" << endl; } }; class Rectangle : public Polygon { public: virtual void Display(ostream&os) const override { os << "Rectangle" << endl; } }; ostream& operator<<(ostream& os, const Polygon& p) { p.Display(os); return os; } 
+1
source

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


All Articles