views:

4102

answers:

4

Assignment Operator in C++ can be made virtual. Why is it required? Can we make other operators virtual too?

+1  A: 

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

dmckee
+2  A: 

It's required only when you want to guarantee that classes derived from your class get all of their members copied correctly. If you aren't doing anything with polymorphism, then you don't really need to worry about this.

I don't know of anything that would prevent you from virtualizing any operator that you want--they're nothing but special case method calls.

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

sblom
There are a few errors on that page. The code which he uses as an example of slicing doesn't actually slice. And that's ignoring the fact that the assigned is illegal anyway (const/non-const mismatch).
Functastic
+9  A: 

It depends on the operator.

The point of making an assignment operator virtual is to allow you from the benefit of being able to override it to copy more fields.

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

However, there is then a risk that your LHS is a Derived, and the RHS is a Base, so when the virtual operator runs in Derived your parameter is not a Derived and you have no way of getting fields out of it.

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

Uri
+5  A: 

The assignment operator is not required to be made virtual.

The discussion below is about operator=, but it also applies to any operator overloading that takes in the type in question, and any function that takes in the type in question.

The below discussion shows that the virtual keyword does not know about a parameter's inheritance in regards to finding a matching function signature. In the final example it shows how to properly handle assignment when dealing with inherited types.


Virtual functions don't know about parameter's inheritance:

A function's signature needs to be the same for virtual to come into play. So even know in the following example, operator= is made virtual. The call will never act as a virtual function in D because the parameters and return value of operator= are different.

The function B::operator=(const B& right) and D::operator=(const D& right) are 100% completely different and seen as 2 distinct 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 though define a virtual function to allow you to set default values for D when it is assigned to variable of type B. This is even if your B variable is really a D stored into a reference of a B. You will not get the D::operator=(const D& right) function.

In the below case, an assignment from 2 D objects stored inside 2 B references... the D::operator=(const B& right) override is used.

//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.right = 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;
}

Prints:

d1.x d1.y 99 100

d2.x d2.y 99 13

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

Without the virtual keyword on B::operator=(const B& right) you would have the same results as above but the value of y would not be initialized. I.e. it would use the B::operator=(const B& right)


One last step to tie it all together, RTTI:

You can use RTTI to properly handle virtual functions that take in your type. Here is the last piece of the puzzle to figure out how to properly handle 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;
  }
Brian R. Bondy
Brian, I have found some strange behavior represented in this question: http://stackoverflow.com/questions/969232/why-does-virtual-assignment-behave-differently-than-other-virtual-functions-of-th. Do you have any ideas?
David Rodríguez - dribeas