jeudi 15 mai 2014

c ++ - notion de virtuel - Stack Overflow


I am very new to CPP, learning about late binding polymorphism.


As per what I have read and understood, virtual keyword is used for late binding. which internally at compilation time create a vtable pointed by vptr. So, for example


class BASE{
public:
virtual void f1(){cout<<"BASE F1\n";}
virtual void f2(){cout<<"BASE F2\n";}
void f3(){cout <<"BASE F3\n";}
};

class D1:public BASE{
public:
virtual void f1(){cout<<"D1 F1\n";}
void f2(){cout<<"D1 F2\n";}
};

class DD1:public D1{
public:
void f1(){cout<<"DD1 F1\n";}
void f2(){cout <<"DD1 F2\n";}
};

Here, BASE will be having 2 function in base class vtable:


BASE::f1() 
BASE::f1()

D1 which inherits from BASE, will have inherited vtable:


D1::f1()
BASE::f1

DD1 which inherits from D1, will not have any vtable of its own.


when we create an object:


//case 1:
BASE *b = new D1();
b->f1();//will print "D1 F1"
b->BASE::f1();//will print "BASE F1"
b->f2();//will print "D1 F2"
//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"
b1->D1::f1();//will print "D1 F1"
b1->BASE::f1();//will print"BASE F1"

But, at case: b1->D1::f1(); its giving an compilation error


 error: ‘D1’ is not a base of ‘BASE’

Question: why? should not it print D1 F1 as its virtual function.


After going throw fdump, I found one more interesting thing, which is little confusing;


Vtable for BASE
BASE::_ZTV4BASE: 4u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4BASE)
16 (int (*)(...))BASE::f1
24 (int (*)(...))BASE::f2

Class BASE
size=8 align=8
base size=8 base align=8
BASE (0x7fbc3d2ff120) 0 nearly-empty
vptr=((& BASE::_ZTV4BASE) + 16u)

Vtable for D1
D1::_ZTV2D1: 4u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI2D1)
16 (int (*)(...))D1::f1
24 (int (*)(...))D1::f2

Class D1
size=8 align=8
base size=8 base align=8
D1 (0x7fbc3d31f2d8) 0 nearly-empty
vptr=((& D1::_ZTV2D1) + 16u)
BASE (0x7fbc3d2ff180) 0 nearly-empty
primary-for D1 (0x7fbc3d31f2d8)

Vtable for DD1
DD1::_ZTV3DD1: 4u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI3DD1)
16 (int (*)(...))DD1::f1
24 (int (*)(...))DD1::f2

Class DD1
size=8 align=8
base size=8 base align=8
DD1 (0x7fbc3d31f3a8) 0 nearly-empty
vptr=((& DD1::_ZTV3DD1) + 16u)
D1 (0x7fbc3d31f410) 0 nearly-empty
primary-for DD1 (0x7fbc3d31f3a8)
BASE (0x7fbc3d2ff1e0) 0 nearly-empty
primary-for D1 (0x7fbc3d31f410)

Question: will not Virtual table of Class BASE get inherited by Class D1, Class D1 vTable & Class BASE get inherited by DD1.? How will inheritance of virtual table take place?





DD1 which inherits from D1, will not have any vtable of its own.



No, wrong. It will have its own vtable since it overrides virtual functions (the virtual keyword is implied here, since once a function is declared virtual in a base class, it’s virtual everywhere).



Question: why? should not it print D1 F1 as its virtual function.



The static type of b1 is Base*, even if its dynamic type is DD1*. Thus you cannot call b1->D1::f1 because that syntax instructs the compiler to statically resolve the call to a function that (again, statically) is not available in b1. If you absolutely want to execute this call, and know that b1’s dynamic type is in fact D1 (or derived from it), you can cast it to change the static type of the object:


static_cast<D1*>(b1)->D1::f1();



I will suggest you to pick up some good book(I suggest Thinking in C++, freely available) and go through the chapter on Virtual Functions to get clarity on this confusing topic.


That said, there are few things you have got wrong.


Quote: D1 which inherits from BASE, will have inherited vtable:


D1::f1()

BASE::f1

Actually what happens is that the vtable contents are replaced in case of derived class if it chooses to override the base class virtual functions. In your case you have done that in D1. So the vtable of D1 will have the D1 functions (yes both of them, f1() and f2() ).


So VTable of D1 is:


D1::f1()
D1::f2()

Base class functions disappear/get overwritten in the D1 vTable.


DD1 vtable has the functions of DD1 in them, instead of the D1.


Regarding the error you see, the answer to that is already posted.




At least a few of your expectations are wrong:



//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"

Conceivably correct. Maybe it should produce "D1 F1", language lawyers, please help.



b1->D1::f1();//will print "D1 F1"

Why should it do that? b1 is of type BASE *, the compiler has no reason to beleive that the object is in fact a D1, if you want to assert that you need an explicit cast.



b1->BASE::f1();//will print"BASE F1"

Yes.





"Why should not it print D1 F1 as its virtual function?"



Because the pointer b1 is of type BASE. You're effectively trying to access the D1 class from a BASE object, which the compiler will not allow. What you need to do is inform the compiler that the b1 pointer is a valid D1 object, like so:


dynamic_cast<D1*> (b1) -> f1()



You cannot do fully qualified name (FQN) method call from the Base class, which has no clue about class D1.


Here's the solution - downcast to DD1 or D1 and then do FQN call:


(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"


I am very new to CPP, learning about late binding polymorphism.


As per what I have read and understood, virtual keyword is used for late binding. which internally at compilation time create a vtable pointed by vptr. So, for example


class BASE{
public:
virtual void f1(){cout<<"BASE F1\n";}
virtual void f2(){cout<<"BASE F2\n";}
void f3(){cout <<"BASE F3\n";}
};

class D1:public BASE{
public:
virtual void f1(){cout<<"D1 F1\n";}
void f2(){cout<<"D1 F2\n";}
};

class DD1:public D1{
public:
void f1(){cout<<"DD1 F1\n";}
void f2(){cout <<"DD1 F2\n";}
};

Here, BASE will be having 2 function in base class vtable:


BASE::f1() 
BASE::f1()

D1 which inherits from BASE, will have inherited vtable:


D1::f1()
BASE::f1

DD1 which inherits from D1, will not have any vtable of its own.


when we create an object:


//case 1:
BASE *b = new D1();
b->f1();//will print "D1 F1"
b->BASE::f1();//will print "BASE F1"
b->f2();//will print "D1 F2"
//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"
b1->D1::f1();//will print "D1 F1"
b1->BASE::f1();//will print"BASE F1"

But, at case: b1->D1::f1(); its giving an compilation error


 error: ‘D1’ is not a base of ‘BASE’

Question: why? should not it print D1 F1 as its virtual function.


After going throw fdump, I found one more interesting thing, which is little confusing;


Vtable for BASE
BASE::_ZTV4BASE: 4u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4BASE)
16 (int (*)(...))BASE::f1
24 (int (*)(...))BASE::f2

Class BASE
size=8 align=8
base size=8 base align=8
BASE (0x7fbc3d2ff120) 0 nearly-empty
vptr=((& BASE::_ZTV4BASE) + 16u)

Vtable for D1
D1::_ZTV2D1: 4u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI2D1)
16 (int (*)(...))D1::f1
24 (int (*)(...))D1::f2

Class D1
size=8 align=8
base size=8 base align=8
D1 (0x7fbc3d31f2d8) 0 nearly-empty
vptr=((& D1::_ZTV2D1) + 16u)
BASE (0x7fbc3d2ff180) 0 nearly-empty
primary-for D1 (0x7fbc3d31f2d8)

Vtable for DD1
DD1::_ZTV3DD1: 4u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI3DD1)
16 (int (*)(...))DD1::f1
24 (int (*)(...))DD1::f2

Class DD1
size=8 align=8
base size=8 base align=8
DD1 (0x7fbc3d31f3a8) 0 nearly-empty
vptr=((& DD1::_ZTV3DD1) + 16u)
D1 (0x7fbc3d31f410) 0 nearly-empty
primary-for DD1 (0x7fbc3d31f3a8)
BASE (0x7fbc3d2ff1e0) 0 nearly-empty
primary-for D1 (0x7fbc3d31f410)

Question: will not Virtual table of Class BASE get inherited by Class D1, Class D1 vTable & Class BASE get inherited by DD1.? How will inheritance of virtual table take place?




DD1 which inherits from D1, will not have any vtable of its own.



No, wrong. It will have its own vtable since it overrides virtual functions (the virtual keyword is implied here, since once a function is declared virtual in a base class, it’s virtual everywhere).



Question: why? should not it print D1 F1 as its virtual function.



The static type of b1 is Base*, even if its dynamic type is DD1*. Thus you cannot call b1->D1::f1 because that syntax instructs the compiler to statically resolve the call to a function that (again, statically) is not available in b1. If you absolutely want to execute this call, and know that b1’s dynamic type is in fact D1 (or derived from it), you can cast it to change the static type of the object:


static_cast<D1*>(b1)->D1::f1();


I will suggest you to pick up some good book(I suggest Thinking in C++, freely available) and go through the chapter on Virtual Functions to get clarity on this confusing topic.


That said, there are few things you have got wrong.


Quote: D1 which inherits from BASE, will have inherited vtable:


D1::f1()

BASE::f1

Actually what happens is that the vtable contents are replaced in case of derived class if it chooses to override the base class virtual functions. In your case you have done that in D1. So the vtable of D1 will have the D1 functions (yes both of them, f1() and f2() ).


So VTable of D1 is:


D1::f1()
D1::f2()

Base class functions disappear/get overwritten in the D1 vTable.


DD1 vtable has the functions of DD1 in them, instead of the D1.


Regarding the error you see, the answer to that is already posted.



At least a few of your expectations are wrong:



//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"

Conceivably correct. Maybe it should produce "D1 F1", language lawyers, please help.



b1->D1::f1();//will print "D1 F1"

Why should it do that? b1 is of type BASE *, the compiler has no reason to beleive that the object is in fact a D1, if you want to assert that you need an explicit cast.



b1->BASE::f1();//will print"BASE F1"

Yes.




"Why should not it print D1 F1 as its virtual function?"



Because the pointer b1 is of type BASE. You're effectively trying to access the D1 class from a BASE object, which the compiler will not allow. What you need to do is inform the compiler that the b1 pointer is a valid D1 object, like so:


dynamic_cast<D1*> (b1) -> f1()


You cannot do fully qualified name (FQN) method call from the Base class, which has no clue about class D1.


Here's the solution - downcast to DD1 or D1 and then do FQN call:


(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"

0 commentaires:

Enregistrer un commentaire