3 虛函數(shù)機(jī)制 virtual mechanism
先看代碼:
class A
{
public:
virtual void print() { cout<<"A.."<<endl; }
};
class B : public A
{
public:
virtual void print() { cout<<"B.."<<endl; }
};
void test4()
{
A a1; //base
B b1;//child
a1 = b1;//A::operator= 對象b1賦值給a2
a1.print(); // prints A
A& a2=b1;// 引用a2指向b1
a2.print();//// prints B
}
實(shí)現(xiàn)條件:
To get polymorphic behavior in C++, the member functions called must be virtualand objects must be manipulated through pointers or references
Q:
- 為什么使用派生類和基類對象之間直接賦值不能實(shí)現(xiàn)渐北?? 必須用用指針或者引用?
- 為什么要用虛函數(shù)?
A:
為什么使用派生類和基類對象之間直接賦值不能實(shí)現(xiàn)?? 必須用用指針或者引用?
要實(shí)現(xiàn)多態(tài)殖卑,必須使用指針或者引用 因?yàn)槟J(rèn)的賦值運(yùn)算符并不會(huì)操作虛函數(shù)表
驗(yàn)證如下:[ Print C++ vtables using GDB]
1.1 vptr 理解成指針 因?yàn)椴恢纕ptr內(nèi)部結(jié)果 采用 gdb x查看變量值
因?yàn)榻o出代碼只提供一個(gè)函數(shù) 只需要打印4字節(jié)就可以了
(gdb) p sizeof(int) $10 = 4
1.2 打印 A a1; //base
(gdb) p a1
$11 = (A) { _vptr.A = 0x400e10 <vtable for A+16> }
(gdb) x/4x 0x400e10
0x400e10 <_ZTV1A+16>: 0x00400c9e 0x00000000 0x00004231 0x00000000
(gdb) x/4x 0x00400c9e
0x400c9e <A::print()>: 0xe5894855 0x10ec8348 0xf87d8948 0x400dd7be
父類A::_vptr.A 內(nèi)容是:
0x400c9e <A::print()>
1.3 打印 B b1;//child
執(zhí)行構(gòu)造函數(shù):A() -> B()
初始化_vptr
(gdb) p b1 $12 = (B) { = { _vptr.A = 0x400df0 <vtable for B+16> }, } (gdb) x/4x 0x400df0 0x400df0 <_ZTV1B+16>: 0x00400cc8 0x00000000 0x00000000 0x00000000 (gdb) x/4x 0x00400cc8 0x400cc8 <B::print()>: 0xe5894855 0x10ec8348 0xf87d8948 0x400ddbbe
這說明對象b1.vptr 記錄虛函數(shù)入口地址 0x400cc8 <B::print()>
只要a1.vptr 指向 b1.******** **vptr 即可 ******
1.4 a1=b1
調(diào)用 A::operator=
a1 _vptr 沒有發(fā)生變化 不可以
是不是復(fù)制操作有問題這個(gè)別人已經(jīng)驗(yàn)證了 A& operator = (const B& b) { *(int )this=(int *)&b; return *this; }
1.5 A& a2=b1; 發(fā)生發(fā)生了什么變化
(gdb) p (B)a2 { = { _vptr.A = 0x400cc8 <B::print()> }, }
(gdb) x/4x 0x400cc8 0x400cc8 <B::print()>: 0xe5894855 0x10ec8348 0xf87d8948 0x400ddbbe
一句話解釋: 1.默認(rèn)的賦值運(yùn)算符并不會(huì)操作虛函數(shù)表例隆。 2.要實(shí)現(xiàn)多態(tài)埋泵,必須使用指針或者引用
為什么要用虛函數(shù)
如果不沒有聲明虛函數(shù) 同名函數(shù)出現(xiàn)覆蓋現(xiàn)象寂诱!
A& a2=b1;
假如 b1 [AAAA BBBB]
a2 [AAAA]
A& a2=b1; 對象賦值 只是a.成員=b.成員 其他的就發(fā)生強(qiáng)制轉(zhuǎn)換 結(jié)果 a2 [AAAA]
函數(shù)之間不會(huì)賦值的就需要一個(gè)記錄 函數(shù)入口地址
圖片可能和代碼不符 你應(yīng)該可以看懂 沒有虛函數(shù)的對象數(shù)據(jù)布局
成員類型相同:
成員類型不同(對齊)
有虛函數(shù)的對象數(shù)據(jù)布局
跟深入地方請查看《Inside the C++ Object Model》
我理解
數(shù)據(jù)部分:
對象在執(zhí)行賦值 ==操作時(shí)候,如果類型不同會(huì)發(fā)生強(qiáng)制轉(zhuǎn)換
因此需要相同成員
vptr比較特殊 不能像普通成員一樣訪問
只能通過指針來實(shí)現(xiàn)不同對象賦值
通過命令 gdb x 查看 我只聲明一個(gè)virtual 因此 n=4
如果有清楚麻煩留言告知蒋搜!