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?
No, it is not possible. Consider a scenario where an ACBus is a derived class of base class Bus.
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.
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.
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.
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.
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;
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With