Brian R. Bondi wrote:
The final step to tie it all together, RTTI:
You can use RTTI to properly handle the virtual functions that are used in your type. Here's the last piece of the puzzle to figure out how to properly handle an assignment when dealing with possibly inherited types.
virtual B& operator=(const B& right) { const D *pD = dynamic_cast<const D*>(&right); if(pD) { x = pD->x; y = pD->y; } else { x = right.x; y = 13;
I would like to add a few comments to this decision. With the assignment operator specified above, there are three problems.
The compiler generates an assignment operator that takes a const D & argument, which is not virtual and does not do what you can imagine.
The second problem is the return type, you are returning a base reference to a derived instance. This is probably not a problem, as the code works anyway. However, it is better to return links accordingly.
The third release, the assignment operator of a derived type does not call the assignment operator of the base class (what if there are private fields that you would like to copy?), Declaring the assignment operator as virtual will not force the compiler to generate it for you. This is rather a side effect of the absence of at least two overloads of the assignment operator to obtain the desired result.
Given the base class (the same as in the column that I quoted):
class B { public: virtual B& operator=(const B& right) { x = right.x; return *this; } int x; };
The following code completes the RTTI solution that I quoted:
class D : public B{ public:
It may seem like a complete solution, it is not. This is not a complete solution, because when you exit D, you will need 1 operator =, which accepts const B & , 1 operator =, which accepts const D & and one operator that accepts const D2 & . The conclusion is obvious, the number of operator = () overloads is equivalent to the number of superclasses + 1.
Given that D2 inherits from D, consider how the two inherited operator = () methods look.
class D2 : public D{ const D2& operator =(const B& b){ D::operator =(b);
Obviously, the operator = (const D2 &) just copies the fields, imagine it was there. We can notice the pattern in the inherited operator = () overloads. Unfortunately, we cannot determine the virtual template methods that take care of this template, we need to copy and paste the same code several times to get the full polymorphic assignment operator, the only solution I can see. Also applies to other binary operators.
Edit
As mentioned in the comments, the least that can be done to make life easier is to define the assignment operator of the upper class of the superclass = () and call it from all other methods of superclass operator = (). Also, when copying fields, the _copy method can be defined.
class B{ public: // _copy() not required for base class virtual const B& operator =(const B& b){ x = bx; return *this; } int x; }; // Copy method usage class D1 : public B{ private: void _copy(const D1& d1){ y = d1.y; } public: /* virtual */ const D1& operator =(const B& b){ B::operator =(b); try{ _copy(dynamic_cast<const D1&>(b)); } catch (std::bad_cast){ // Set defaults or do nothing. } return *this; } virtual const D1& operator =(const D1& d1){ B::operator =(d1); _copy(d1); return *this; } int y; }; class D2 : public D1{ private: void _copy(const D2& d2){ z = d2.z; } public: // Top-most superclass operator = definition /* virtual */ const D2& operator =(const B& b){ D1::operator =(b); try{ _copy(dynamic_cast<const D2&>(b)); } catch (std::bad_cast){ // Set defaults or do nothing } return *this; } // Same body for other superclass arguments /* virtual */ const D2& operator =(const D1& d1){ // Conversion to superclass reference // should not throw exception. // Call base operator() overload. return D2::operator =(dynamic_cast<const B&>(d1)); } // The current class operator =() virtual const D2& operator =(const D2& d2){ D1::operator =(d2); _copy(d2); return *this; } int z; };
There is no need for the default setpoint method, because it will receive only one call (when overloading the base operator = ()). Changes when copying fields are performed in one place, and all operator = () overloads are affected and carry their intended purpose.
Thanks to sehe for the suggestion.