C語言中,數據與處理數據的操作(函數)是分開聲明的,這種程式方法被稱為程式性的;而在c++中,則是使用abstract data type(ADT)或class hierarchy的數據封裝 c++對於結構體和函數(不包含virtual和non-inline)的封裝並沒有增加佈局成本,對於C++而言 ...
- C語言中,數據與處理數據的操作(函數)是分開聲明的,這種程式方法被稱為程式性的;而在c++中,則是使用abstract data type(ADT)或class hierarchy的數據封裝
- c++對於結構體和函數(不包含virtual和non-inline)的封裝並沒有增加佈局成本,對於C++而言,在佈局以及存取時間上主要的額外負擔是由virtual引起
- virtual function
- virtual base class
The c++ Object Model
-
c++中,有兩種class data members:static and nonstatic,和三種class member functions:static、non-static and virtual
-
現有以下class聲明:
class Point { public: Point(float xval); virtual ~Point(); float x() const; static int PointCount(); protected: virtual ostream& print( ostream & os ) const; float _x; static int _point_count; }
-
此 class 可以通過三種object models表示
A Simple Object Model
-
一個object是一系列slots,每一個slots指向一個member,Members按其聲明順序,各被指定一個slot。每一個data member or function member都有自己的一個slot
-
目的:儘量減低C++ complier的設計複雜度,但會損失空間和執行器的效率
- 意義:應用到c++的"指向成員的指針"觀念中
A Table-driven Object Model
- 將所有members的信息抽離出來放在一個member data table 和 一個member function table中,而class則含有指向這兩個table的pointer
- 意義:成為virtual function的一個方案
The c++ Object Model
- 此模型從A Simple Object Model派生而來,並對記憶體空間和存取時間優化
- Nonstatic data members被配置於每一個class object內,static data members、Static function members、 nonstatic function members則被存放於個別的class object外。而用virtual functions來支持此model:
- virtual tabel(vtbl)中存放一些指向由class生成的virtual functions的pointer
- 每個class object安插一個指針(vptr),此指針指向相關的virtual table。而vptr的setting 和 resetting都有class里的constructor、destructor、copy assignment運算符自動完成。每個class關聯的type_info object(支持runtime type identification,RTTI)亦由virtual table指出,通常放於表格中的第一個slot
- 優點:空間和存取時間效率高
- 缺點:若應用程式本身未改變,而class內的nonstatic data members有所修改,則應用程式代碼需重新編譯
- c++支持單一繼承、多重繼承、虛擬繼承。虛擬繼承中,base class不管在繼承串鏈中被派生多少次,永遠只存在一個實例subobject
A Keyword Distinction
- C++為了維護和C之間的相容性,為此付出了很多犧牲
int (*pq) ();
- 當語言無法區分此函數是declaration還是invocation時,需要一個超越語言範圍的規則,此規則會將此函數判斷為declaration
- struct的一個合理用途:將class object某個數據做封裝,達到與C的相容的空間佈局,只有composition才支持
- template並沒有相容struct
An Object Distinction
- c++程式設計模型支持三種programming paradigms:
- procedural model
- abstract data type model
- object-oriented model
- object-oriented model中,被指定的object的真實類型在每個特定執行點前無法解析,只有通過pointer 和 reference才能完成;相反,abstract data type model中,所處理的是一個固體且單一類型的實例,真實類型早在編譯時期就已定義,因此速度更快且空間排布更緊湊,但沒有彈性
- c++用三種方法支持多態:
- 經由一組隱式轉化。如把derived class pointer轉化為public base type的pointer
- 經由virtual function機制
- 經由dynamic_cast和typeid運算符
- 多態主要用途:由一個共同的介面影響類型封裝,此介面通常被定義在抽象的base class
- class object所需的記憶體大小:
- nonstatic data members的總和大小
- 任何由於alignment(將數值調整到某數倍數)的需求而padding的空間
- 支持virtual產生的額外負擔
The Type of a Pointer
- "指針類型"告訴編譯器如何解釋目的地址中的內容和大小,這也是為什麼pointer和reference支持多態
- 我們並不知道void*指針涵蓋的地址空間,因此void*的指針只含有一個地址,且不能操作所指向的object
- cast本質上是編譯器指令,它並不改變指針所含的地址,而隻影響"所指記憶體的大小和其內容"的解釋方式
Adding Polymorphism
class A
{
public:
virtual action1();
...
}
class B : public A
{
//以下皆為B獨有
public:
...
void rotate();
protected:
enum action2 {...};
int n;
}
B b;
A* p1 = &b;
B* p2 = &b;
-
顯然,我們並不不能用p1處理A以外的members,唯一方法是通過virtual
//以下都可行 (static_cast<B*> (p1))->n; if( B*p3 = dynamic_cast<B*>(p1) ) //此為run-time operation,成本較高 p3->n;
p1->action1()
- p1將在編譯器決定以下兩點:
- 固定的可用介面
- 此介面的access level
B b;
A a1 = b; //造成sliced
a1.action1(); //調用A中的
- 為什麼a1中的vptr不會指向b的virtual table?
- 編譯器需確保若object含有一個或一個以上的vptrs,這些vptrs內容不會被base class object初始化或改變
- 為什麼action1()依舊是調用的A的?
- 多態並不適用於直接存取objects