Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Base class reference - assign other type to it

Tags:

c++

reference

What happens in the following example?

struct B { };
struct D1 : B  { };
struct D2 : B  { };
int main()
{
    D1 d;
    D2 d2;
    B& x = d;
    x = d2;
}

I know the reference is not re-assigned. x still refers to d, but then how can you assign d2 to d?

Some more:

struct B
{
    B () { x = 0; }
    int x;
    virtual void foo () { cout << "B" << endl; }
};
struct D1 : B
{
    D1 () { x = 1; }
    virtual void foo () { cout << "D1" << endl; }
};
struct D2 : B
{
    D2 () { x = 2; }
    virtual void foo () { cout << "D2" << endl; }
};

int main()
{
D1 d;
D2 d2;
B& x = d;
x.foo();   //D1
               //x.x is 1 here
x = d2;
x.foo();   //also D1
               //but x.x is 2 here
}

It seems like x.x was updated, but the vftable was not... Why?

like image 741
AMCoder Avatar asked Jun 16 '12 17:06

AMCoder


People also ask

Can you assign a base class object to a derived class reference?

No, it is not possible. Consider a scenario where an ACBus is a derived class of base class Bus.

What is base class reference?

A base class reference variable may be assigned the address of either a base class object or a derived class object. True/False? True, because all derived class object is an instance of the base class, but not the other way round.

Why would one create a base class object with reference to the derived class?

One reason for this could be that BaseClass is abstract (BaseClasses often are), you want a BaseClass and need a derived type to initiate an instance and the choice of which derived type should be meaningful to the type of implementation.

Is struct can be used as a base class for another class?

Struct cannot be a base class. So, Struct types cannot abstract and are always implicitly sealed. Abstract and sealed modifiers are not allowed and struct member cannot be protected or protected internals.


2 Answers

x refers to the B base class subobject of d. The assignment x = d2 slices the B base subobject from d2 and assigns its value to the subobject of d.

This is usually not done intentionally.

EDIT:

It seems like x.x was updated, but the vftable was not... Why?

That is what the assignment operator B::operator= does. Base classes in C++ are totally unaware that they are base classes. Also, the type of an object cannot ever be changed during its lifetime. The closest alternative is C++11's std::move, which can transfer the old B object inside a D1 into a fresh D2 object. You would then destroy the old object.

like image 136
Potatoswatter Avatar answered Oct 09 '22 13:10

Potatoswatter


If you want, you can implement the = by yourself and "avoid" the slicing by checking for the appropriate concrete type (or giving an error). See below example with errors.

struct B { 
  virtual B& operator = (B& b) = 0;
};
struct D1 : B  { 
  D1& operator = (B& b) {
    if ( dynamic_cast<D1*>(&b) == 0 ) {
      cerr << "Cannot assign non D1 to D1" << endl;
      exit(255);
    }
    // handle the assignments
    return *this;
  }
};
struct D2 : B  { 
  int c;
  D2& operator = (B& b) {
    if ( dynamic_cast<D2*>(&b) == 0 ) {
      cerr << "Cannot assign non D2 to D2" << endl;
      exit(255);
    }
    // handle the assignments
    return *this;
  }
};
like image 23
eran Avatar answered Oct 09 '22 13:10

eran