C++對象模型----關於對象
第一章 關於對象
在C語言中,數據和處理數據的操作(函數)是分開聲明的,也就是說,語言本身並沒有支持數據和函數之間的關聯性.我們把這種程序方法成為程序性,由一組分布在各個以功能為導向的函數中的算法所驅動,它們處理的是共同的外部數據.舉個例子,如果聲明一個struct Point3d,像這樣:
typedef struct point3d
{
float x;
float y;
float z;
} Point3d;
欲打印一個Point3d,可能就得定義一個像這樣的函數:
void Point3d_print (const Point3d *pd) {
printf((%g, %g, %g), pd->x, pd->y, pd->z);
}
或者,如果要想更有效率一些,就定義一個宏:
#define Point3d_print(pd)
printf((%g, %g, %g), pd->x, pd->y, pd->z);
也可直接在程序中完成其操作
在C++中,Point3d有可能用獨立的抽象數據類型(abstract data type)來實現:
class Point3d
{
public:
Point3d(float xval = 0.0, float yval = 0.0, float zval = 0.0)
: x(xval), y(yval), z(zval) {}
float getX() { return x; }
float getY() { return y; }
float getZ() { return z; }
void setX(float xval) { x = xval; }
private:
float x;
float y;
float z;
};
inline ostream & operator << (ostream &os, const Point3d &pt) {
os << ( << pt.getX() << , << pt.getY() << , << pt.getZ() << );
}
更進一步來說,不管哪一種形式,它們都可以被參數化,可以是坐標類型的參數化.
template
Point3d(type xval = 0.0, type yval = 0.0, zval = 0.0);
也可以是坐標類型和坐標數目兩者都參數化:
template
Point(type coords[dim]);
很明顯,不只是程序風格上有截然的不同,在程序的思考上也有明顯的差異,從軟件工程的眼光來看一個ADT或者class hierarchy的數據封裝比在C程序中程序性使用全局數據好.,但是這被那些被要求快速讓一個應用程序上馬應戰,並且執行起來又快又有效率的程序員所忽略,畢竟C的吸引力就在於它的精瘦和簡易.
加上封裝後的布局成本(Layout Costs for Adding Encapsulation)
程序員看到Point3d轉換成C++之後,第一個可能會問的問題就是:加上了封裝之後,布局成本增加了多少?答案是class Point3d並沒有增加成本,三個data member直接內含在每一個class object之中,就像C struct的情況一樣,而member functions雖然含在class的聲明內,卻不出現在object中,每一個non-inline member function只會誕生一個函數實體,至於每一個擁有零個或者一個定義的inline function則會在其每一個使用者(模塊)身上產生一個函數實體.Point3d支持封裝性質,這一點並未帶給它任何空間或執行期的不良效應.C++在布局以及存取時間上主要的額外負擔是由virtual引起,包括:
virtual function機制 用於支持一個有效率的執行器綁定(runtime binding)
virtual base class 用以實現多次出現在繼承體系中的base class,有一個單一而被共享的實體
此外,還有一些多重繼承下的額外負擔,發生在一個derived class和其第二或者後繼之base class的轉換之間.然而,一般言之,並理由說C++程序一定比C龐大或者遲緩.
1.1 C++對象模式(The C++ Object Model)
在C++中,有兩種class data members:static 和 nonstatic,以及三種class member functions:static,nonstatic和virtual,已知下面這個class Point聲明:
class Point {
public:
Point(float xval);
virtual ~Point();
float getX() const;
static int PointCount();
protected:
virtual ostream& print(ostream &os) const;
float x;
static int point_count;
};
這個class Point在機器中會被怎樣表現呢?也就是說,如何模擬(modeling)出各種data members和function members呢?
簡單對象模型 (A Simple Object Model)
第一個模型非常簡單,它可能是為了盡量降低C++編譯器的設計復雜度而開發出來的,缺點則是空間和執行期的效率低下.在這個簡單模型中,一個object是一系列的slot(槽),每一個slot指向一個member.Members按照聲明次序,各自被指定一個slot.每一個data member或function member都有自己的一個slot.
在這個簡單模型中,members本身並不被放在object之中,只有指向member的指針才被放在object內,這麼做可以避免members有不同的類型,因而需要不同的存儲空間所導致的問題.Object中的members是以slot的索引值來尋址,本例中x的索引值是6,point_count的索引值為7.一個class object的大小很容易計算出來:指針大小,乘以class中聲明的members數目.
雖然這個模型並沒有被應用於實際產品上,不過關於索引或slot數目的觀念,倒是被應用到C++的指向成員的指針(point-to-member)觀念之中.
表格驅動對象類型 (A Table-driven Object Model)
為了對所有classes的所有objects都有一致的表達方式,另一種對象模型是把所有與members相關的信息抽出來,放在data member table和一個member function table之中,class object本身則內含這兩個表格的指針,Member function table是一系列的slots,每一個slot指出一個member function; Data member table則直接含有data本身.
雖然這個模型也沒有實際應用於真正的C++編譯器上,但member function table這個觀念卻稱為支持virtual functions的一個有效方案.
C++對象模型(The C++ Object Model)
Stroustrup當初設計(當前仍占有優勢)的C++對象模型是從簡單對象模型派生而來的,並對內存空間和存取時間做了優化.在此模型中,Nonstatic data members被配置於每一個class object之內,static data members則被存放在所有的class object之外,Static和nonstatic function members也被放在所有的class object之外,Virtual functions則以兩個步驟支持之:
1.每一個class產生出一堆指向virtual functions的指針,放在表格之中,這個表格被稱為virtual table(vtbl)
2.每一個class object被添加了一個指針,指向相關的virtual table,通常這個指針被稱為vptr.vptr的設定和重置都由每一個class的constructor,destructor和copy assignment運算符自動完成,每一個class所關聯的type_info object也經由virtual table被指出來,通常是放在表格的第一個slot處.