基本虛函數使用示例:
#include <stdio.h>
#include <stdlib.h>
class parent {
public:
virtual void echo()
{
printf("This is parent\n");
}
};
class child : public parent {
public:
void echo() //這裏加不加關鍵詞virtual結果一樣,如果之後不會再有類繼承child類,那麼此處直接這樣沒問題,如果之後還可能有繼承,那麼加virtual
{
printf("This is child\n");
}
};
int main()
{
parent *p = new parent();
parent *c = new child();
p->echo();
c->echo();
delete p;
delete c;
return 0;
}
輸出爲:
上面的例子中新建了child類對象,但是用parent指針指向,可以發現調用echo輸出的是child類中的內容。但若是在child類中加任一虛函數echo2(),然後c->echo2(),會發現編譯錯誤。這是因爲基類指針不能夠調用子類自己定義的成員函數。
但是不是就意味着一定不能夠通過基類指針訪問呢?其實不然,最簡單的做法就是將指針強制轉換爲繼承類指針,如下:
#include <stdio.h>
#include <stdlib.h>
class parent {
public:
virtual void echo()
{
printf("This is parent\n");
}
};
class child : public parent {
public:
void echo()
{
printf("This is child\n");
}
virtual void echo2()
{
printf("This is child 2\n");
}
};
int main()
{
parent *p = new parent();
parent *c = new child();
p->echo();
c->echo();
((child *)c)->echo2();
delete p;
delete c;
return 0;
}
輸出爲:
當然如果僅僅這樣,並不能真正搞明白虛函數的原理,所以用下面的方法來實現
#include <stdio.h>
#include <stdlib.h>
typedef void (*Echo)(void);
class parent {
public:
virtual void echo()
{
printf("This is parent\n");
}
};
class child : public parent {
public:
virtual void echo()
{
printf("This is child\n");
}
virtual void echo2()
{
printf("This is a virtual function of child\n");
}
};
int main()
{
parent *p = new parent();
parent *c = new child();
p->echo();
c->echo();
Echo e = (Echo)*((int *)*(int *)c + 1);
e();
delete p;
delete c;
return 0;
}
輸出爲:
可以看到成功的訪問了child類中自己定義的虛函數。爲什麼爲出現這種情況呢?這是因爲每一個實例類對象存儲地址的最開始存放一個虛函數表指針,虛函數表中存放虛函數,虛函數包括繼承而來和自己類定義的所有虛函數,出現的先後順序由虛函數聲明時確定,如果子類在繼承時重新定義了基類中定義的虛函數,那麼直接覆蓋到虛函數表中基類虛函數對應位置,而並不順序在後邊新建。如下圖:
通過同樣的方法,還可以訪問設置爲private類型的虛函數
#include <stdio.h>
#include <stdlib.h>
typedef void (*Echo)(void);
class parent {
private:
virtual void echo()
{
printf("This is parent\n");
}
};
class child : public parent {};
int main()
{
child c;
Echo e = (Echo)*((int *)*(int *)&c);
e();
return 0;
}
輸出爲:
經過上述的測試,應該對虛函數有了更進一步的理解。