===================================================================================================================
c++是一門面向對象的語言,但是它和c#,java不同,它沒有反射機制。沒有反射機制使得c++在語言的一些設計方面與其他語言有點不一樣,主要體現在智能化方面,許多東西得程序員明確指定,例如本文要講的virtual關鍵字。virtual是在運行時才體現的,而c++在運行時無法使用反射來確定當前類的父類是否有此方法,此方法是否被重載等信息,所以必須在寫代碼時用virtual來明確指明,然後通過編譯器做一些特殊處理(也就是使用虛表)。
我們見到virtual最多的地方是在c++裏面的多態實現。
- // dynamic_poly.h
- #include <iostream>
- // 公共抽象基類Vehicle
- class Vehicle
- {
- public:
- virtual void run() const = 0;
- };
- // 派生於Vehicle的具體類Car
- class Car: public Vehicle
- {
- public:
- virtual void run() const
- {
- std::cout << "run a car/n";
- }
- };
- // 派生於Vehicle的具體類Airplane
- class Airplane: public Vehicle
- {
- public:
- virtual void run() const
- {
- std::cout << "run a airplane/n";
- }
- };
- // dynamic_poly_1.cpp
- #include <iostream>
- #include <vector>
- #include "dynamic_poly.h"
- // 通過指針run任何vehicle
- void run_vehicle(const Vehicle* vehicle)
- {
- vehicle->run(); // 根據vehicle的具體類型調用對應的run()
- }
- int main()
- {
- Car car;
- Airplane airplane;
- run_vehicle(&car); // 調用Car::run()
- run_vehicle(&airplane); // 調用Airplane::run()
- }
上面的例子來自於http://www.vckbase.com/document/viewdoc/?id=948,這篇文章裏面還提到了多態的一些別的例子。關於多態的文章還有孟巖寫的一篇http://blog.csdn.net/wuliming_sc/archive/2009/01/31/3855906.aspx,可以看看。
在很多多態的例子中,我們都可以看到將基類的方法聲明爲純虛函數(virtual void run() const = 0;),這樣可以要求子類必須實現這個方法,同時可以體現面向接口編程。
對於使用了virtual之後編譯器會做些什麼,我覺得用代碼更容易說明問題。下面的例子來自http://www.cppblog.com/zhangyq/archive/2009/06/13/87597.html。
1 編譯器會爲這個類的虛函數添加一個虛表,類似下面的:
// Pseudo-code (not C++, not C) for a static table defined within file Base.cpp
// Pretend FunctionPtr is a generic pointer to a generic member function
// (Remember: this is pseudo-code, not C++ code)
FunctionPtr Base::__vtable[5] = {
&Base::virt0, &Base::virt1, &Base::virt2, &Base::virt3, &Base::virt4
};
2 然後增加一個指向虛表的指針爲每一個類對象,這個指針是隱藏的
// Your original C++ source code
class Base {
public:
...
FunctionPtr* __vptr; ← supplied by the compiler, hidden from the programmer
...
};
3 編譯器在構造中初始化這個指針
Base::Base(...arbitrary params...)
: __vptr(&Base::__vtable[0]) ← supplied by the compiler, hidden from the programmer
...
{
...
}
在派生類中,它也會增加一個隱藏的虛表,但是它可以overrides基類的虛函數如:
// Pseudo-code (not C++, not C) for a static table defined within file Der.cpp
// Pretend FunctionPtr is a generic pointer to a generic member function
// (Remember: this is pseudo-code, not C++ code)
FunctionPtr Der::__vtable[5] = {
&Der::virt0, &Der::virt1, &Der::virt2, &Base::virt3, &Base::virt4
};
從上面的代碼我們可以非常容易的劃出class的結構圖,當然可以用/d1reportSingleClassLayout來顯示class的佈局圖(http://blog.csdn.net/chief1985/archive/2009/10/23/4720191.aspx)。
使用virtual的地方還有虛擬繼承和虛擬析構函數。
虛擬繼承的例子如下,來自http://blog.csdn.net/wuliming_sc/archive/2009/01/31/3855607.aspx:
- class Point2d{
- public:
- //...
- protected:
- float _x;
- float _y;
- };
- class Vertex : public virtual Point2d{
- public:
- //...
- protected:
- Vertex *next;
- };
- class Point3d : public virtual Point2d{
- public:
- //...
- protected:
- float _z;
- };
- class Vertex3d: public Vertex, public Point3d{
- public:
- //...
- protected:
- float mumble;
- };
使用虛擬繼承一般都是出現了菱形繼承(c++允許多重繼承),這種情況下若不使用虛擬繼承,便會導致以下情況:
1.公共基類子對象的重複創建問題。
2.成員函數的名字衝突問題
3. 數據成員的名字衝突問題