C ++ Virtual Assignment Operator

The assignment operator in C ++ can be made virtual. Why is this required? Can we make other operators virtual?

+57
c ++ operator-overloading virtual virtual-functions
Mar 21 '09 at 19:28
source share
6 answers

The assignment operator does not need to be made virtual.

The discussion below is operator= , but it also applies to any operator overload that accepts the type in question, and any function that accepts this type.

The discussion below shows that the virtual keyword is unaware of parameter inheritance in relation to finding a match signature. The final example shows how to properly handle assignments when working with inherited types.




Virtual functions do not know about parameter inheritance:

The functional signature must be the same for the virtual game. So even I know in the following example, the = operator is made virtual. The call will never act like a virtual function in D, because the parameters and the return value of the = operator are different.

The function B::operator=(const B& right) and D::operator=(const D& right) 100% completely different and are considered as two different functions.

 class B { public: virtual B& operator=(const B& right) { x = right.x; return *this; } int x; }; class D : public B { public: virtual D& operator=(const D& right) { x = right.x; y = right.y; return *this; } int y; }; 



Default values ​​and having 2 overloaded operators:

You can define a virtual function that will allow you to set default values ​​for D if it is assigned to a variable of type B. This is even if your variable B is really D stored in reference B. You will not get the function D::operator=(const D& right) .

In the following case, we use the assignment of two D objects stored inside links 2 B ... we use the override D::operator=(const B& right) .

 //Use same B as above class D : public B { public: virtual D& operator=(const D& right) { x = right.x; y = right.y; return *this; } virtual B& operator=(const B& right) { x = right.x; y = 13;//Default value return *this; } int y; }; int main(int argc, char **argv) { D d1; B &b1 = d1; d1.x = 99; d1.y = 100; printf("d1.x d1.y %i %i\n", d1.x, d1.y); D d2; B &b2 = d2; b2 = b1; printf("d2.x d2.y %i %i\n", d2.x, d2.y); return 0; } 

Print

 d1.x d1.y 99 100 d2.x d2.y 99 13 

Which shows that D::operator=(const D& right) never used.

Without the virtual keyword in B::operator=(const B& right) , you will have the same results as above, but the y value will not be initialized. That is, he would use B::operator=(const B& right)




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;//default value } return *this; } 
+41
Mar 21 '09 at 20:05
source share
β€” -

It depends on the operator.

The point of creating the virtual assignment operator is to allow you to take advantage of the ability to override it to copy more fields.

So, if you have Base & and you have Derived & as a dynamic type and Derived has more fields, the right things are copied.

However, there is a risk that your LHS is derived and RHS is the base, so when the virtual operator is running in Derived, your parameter is not derived, and you have no way to get fields from it.

Here is a good discussion: http://icu-project.org/docs/papers/cpp_report/the_assignment_operator_revisited.html

+24
Mar 21 '09 at 19:34
source share

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;//default value } return *this; } 

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: // The virtual keyword is optional here because this // method has already been declared virtual in B class /* virtual */ const D& operator =(const B& b){ // Copy fields for base class B::operator =(b); try{ const D& d = dynamic_cast<const D&>(b); // Copy D fields y = dy; } catch (std::bad_cast){ // Set default values or do nothing } return *this; } // Overload the assignment operator // It is required to have the virtual keyword because // you are defining a new method. Even if other methods // with the same name are declared virtual it doesn't // make this one virtual. virtual const D& operator =(const D& d){ // Copy fields from B B::operator =(d); // Copy D fields y = dy; return *this; } int y; }; 

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{ /* virtual */ const D2& operator =(const B& b){ D::operator =(b); // Maybe it a D instance referenced by a B reference. try{ const D2& d2 = dynamic_cast<const D2&>(b); // Copy D2 stuff } catch (std::bad_cast){ // Set defaults or do nothing } return *this; } /* virtual */ const D2& operator =(const D& d){ D::operator =(d); try{ const D2& d2 = dynamic_cast<const D2&>(d); // Copy D2 stuff } catch (std::bad_cast){ // Set defaults or do nothing } return *this; } }; 

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.

+6
Sep 04
source share

virtual assignment is used in the following scenarios:

 //code snippet Class Base; Class Child :public Base; Child obj1 , obj2; Base *ptr1 , *ptr2; ptr1= &obj1; ptr2= &obj2 ; //Virtual Function prototypes: Base& operator=(const Base& obj); Child& operator=(const Child& obj); 

case 1: obj1 = obj2;

In this virtual concept, it does not play any role, since we call operator= class Child .

case 2 & 3: * ptr1 = obj2;
* ptr1 = * ptr2;

Here the appointment will not be as expected. The reason operator= is called instead of the Base class.

It can be fixed using: 1) Casting

 dynamic_cast<Child&>(*ptr1) = obj2; // *(dynamic_cast<Child*>(ptr1))=obj2;` dynamic_cast<Child&>(*ptr1) = dynamic_cast<Child&>(*ptr2)` 

2) Virtual concept

Now just using virtual Base& operator=(const Base& obj) will not help, since there are different signatures in Child and Base for operator= .

We need to add Base& operator=(const Base& obj) to the Child class along with its usual definition Child& operator=(const Child& obj) . It is important to include a later definition, since in the absence of this assignment operator, it will be called by default. ( obj1=obj2 may not give the desired result)

 Base& operator=(const Base& obj) { return operator=(dynamic_cast<Child&>(const_cast<Base&>(obj))); } 

case 4: obj1 = * ptr2;

In this case, the compiler searches for the definition of operator=(Base& obj) in Child , since operator= is called in Child. But since it is not there and Base cannot be promoted to Child implicitly, it will be through an error. (Listing required, as obj1=dynamic_cast<Child&>(*ptr1); )

If we implement according to case2 & 3, this scenario will be considered.

As you can see, virtual assignment makes the call more elegant when assigned with the help of pointers / references of the base class.

Is it possible to make other operators virtual? Yes

+5
Nov 13 '14 at 10:11
source share

It is only required when you want to guarantee that classes derived from your class will correctly copy all their members. If you are not doing anything with polymorphism, you really do not need to worry about it.

I don’t know anything that would prevent you from virtualizing any operator that you want - they are nothing more than special case calls.

This page provides an excellent and detailed description of how it all works.

+4
Mar 21 '09 at 19:31
source share

An operator is a method with special syntax. You can consider it like any other method ...

+3
Mar 21 '09 at 19:30
source share



All Articles