1. 程式人生 > >C++對象模型——關於對象(第一章)

C++對象模型——關於對象(第一章)

poi 相同 bsp 擁有 配置 軟件 cto 之間 模式

第一章 關於對象

在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 <class type>
Point3d(type xval = 0.0, type yval = 0.0, zval = 0.0);
也能夠是坐標類型和坐標數目兩者都參數化:
template <class type, int dim>
Point(type coords[dim]);
非常明顯,不僅僅是程序風格上有截然的不同,在程序的思考上也有明顯的差異,從軟件project的眼光來看"一個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呢?

1.1.1 簡單對象模型 (A Simple Object Model)

第一個模型很easy,它可能是為了盡量減少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的大小非常easy計算出來:"指針大小,乘以class中聲明的members數目".
盡管這個模型並沒有被應用於實際產品上,只是關於索引或slot數目的觀念,倒是被應用到C++的"指向成員的指針"(point-to-member)觀念之中.

1.1.2 表格驅動對象類型 (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的一個有效方案.

1.1.3 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處.
C++對象模型的主要長處在於它空間和存取時間的效率主要缺點是。假設應用程序代碼本身並未改變,但所用的class object的nonstatic data members有所改動(可能是添加、移除或改動),那麽那些應用程序代碼相同得又一次編譯。

關於這點,前面的的表格驅動模型就提供了較大的彈性,由於它多提供了一層間接性。只是它也因此付出空間和運行效率雙方面的代價。


C++對象模型——關於對象(第一章)