Why does virtual assignment behave differently than other virtual functions of the same signature?

While playing with the virtual assignment operator implementation, I ended up with ridiculous behavior. This is not a compiler failure, since g ++ 4.1, 4.3, and VS 2005 have the same behavior.

In principle, the virtual operator = behaves differently than any other virtual function with respect to the code actually executed.

struct Base { virtual Base& f( Base const & ) { std::cout << "Base::f(Base const &)" << std::endl; return *this; } virtual Base& operator=( Base const & ) { std::cout << "Base::operator=(Base const &)" << std::endl; return *this; } }; struct Derived : public Base { virtual Base& f( Base const & ) { std::cout << "Derived::f(Base const &)" << std::endl; return *this; } virtual Base& operator=( Base const & ) { std::cout << "Derived::operator=( Base const & )" << std::endl; return *this; } }; int main() { Derived a, b; af( b ); // [0] outputs: Derived::f(Base const &) (expected result) a = b; // [1] outputs: Base::operator=(Base const &) Base & ba = a; Base & bb = b; ba = bb; // [2] outputs: Derived::operator=(Base const &) Derived & da = a; Derived & db = b; da = db; // [3] outputs: Base::operator=(Base const &) ba = da; // [4] outputs: Derived::operator=(Base const &) da = ba; // [5] outputs: Derived::operator=(Base const &) } 

The effect is that the virtual operator = has a different behavior than any other virtual function with the same signature ([0] compared to [1]), invoking the basic version of the operator when called through real derived objects ([1]) or Derived links ([3]), while it performs the function of a normal virtual function when called using basic links ([2]) or when the values โ€‹โ€‹lvalue or r are basic, and the other is a derived link ([4], [5 ]).

Is there any reasonable explanation for this odd behavior?

+21
c ++ inheritance assignment-operator virtual-functions
Jun 09 '09 at 10:20
source share
5 answers

Here's how to do it:

If I changed [1] to

 a = *((Base*)&b); 

then everything works as you expect. An assignment statement is automatically created there in Derived , which looks like this:

 Derived& operator=(Derived const & that) { Base::operator=(that); // rewrite all Derived members by using their assignment operator, for example foo = that.foo; bar = that.bar; return *this; } 

In your example, the compilers have enough information to suggest that a and b are of type Derived , and therefore they prefer to use the automatically generated statement above, which calls yours. This is how you got [1]. My pointer throws power to compilers to do it their own way, because I tell the compiler to forget that b is of type Derived and therefore uses Base .

Other results can be explained equally.

+13
Jun 09 '09 at 10:58
source share

In this case, the three operators =:

 Base::operator=(Base const&) // virtual Derived::operator=(Base const&) // virtual Derived::operator=(Derived const&) // Compiler generated, calls Base::operator=(Base const&) directly 

This explains why it looks like this: Base :: operator = (Base const &) is called "practically" in the case of [1]. It is called from a version generated by the compiler. The same applies to case [3]. In case 2, the right-hand argument "bb" is of type Base &, therefore Derived :: operator = (Derived &) cannot be called.

+5
Jun 09 '09 at 11:18
source share

A custom assignment operator is not defined for the Derived class. Therefore, the compiler synthesizes one and the internal assignment operator of the base class is called from this synthesized assignment operator for the Derived class.

 virtual Base& operator=( Base const & ) //is not assignment operator for Derived 

Therefore, a = b; // [1] outputs: Base::operator=(Base const &) a = b; // [1] outputs: Base::operator=(Base const &)

In the Derived class, the assignment operator of the Base class has been overridden and, therefore, the overridden method gets an entry in the virtual table of the Derived class. When a method is invoked through reference or pointers, then the override method of the Derived class is invoked due to VTable login permission at run time.

 ba = bb; // [2] outputs: Derived::operator=(Base const &) 

==> internally ==> (Object-> VTable [Assignement operator]) Get the record for the assignment operator in the VTable of the class to which the object belongs, and call the method.

+4
Jun 09 '09 at 10:58
source share

If you did not provide a suitable operator= (that is, the correct type of return and arguments), then by default operator= a compiler is provided that overloads any custom one. In your case, this will call Base::operator= (Base const& ) before copying the Derived members.

Refer to this link for details on how the = operator is created virtual.

+3
Jun 09 '09 at 11:13
source share

The reason for the presence of the compiler in the standard assignment operator= . Which is called in the script a = b , and since we know that by default it invokes the base assignment operator internally.

An additional explanation on virtual assignment can be found at: https://stackoverflow.com/a/16744/

+2
Nov 13 '14 at 10:38
source share



All Articles