1. 程式人生 > >【深度探索C++物件模型】(1)關於物件

【深度探索C++物件模型】(1)關於物件

哎 再開新坑,希望19年能把開的這幾個坑都填上。
class : 類
class object : 類物件

1 C++物件模型

簡單來說,C++物件模型的例項的組成包括下面幾個部分:

  • Nonstatic data members與**virtual pointer(vptr)**儲存在class object內
  • Static data membersStatic functions members以及Nonstatic functions members儲存在class object外。
  • 每個類(並不是物件)都會產生指向每個Virtual functions
    的指標,這些指標放在virtual table (vtbl)裡,而每個該類的物件都有一個指向vtbl的指標——virtual pointer(vptr)

vtbl[0]指向type_info objext
vtbl[1]指向解構函式
vtbl[2]指向預設建構函式

class Point{
public:
    Point(float xval);          //_vtbl[1]
    virtual ~Point();           //_vtbl[2]
    float x() const;
    static int PointCount
(); protected: virtual ostream& print(ostream &os) const; float _x; static int _point_count; }; Point pt = new Point();

在這裡插入圖片描述

繼承關係也可以指定為virtual,即共享的意思:

  class istream : virtual public ios{ /*   ...   */};
  class ostream : virtual public ios{ /*   ...   */};
  class isotream : public
istream , public ostream{ /* ... */};

base class不管在繼承鏈中被派生(derived)多少次,永遠只會存在一個例項(稱為subobject)
在這裡插入圖片描述

2 物件的差異

由於被指定的object的真實型別在每一個特定執行點之前是無法被解析的(一個指標/引用可以指向它的子型別),所以只有通過pointer或者reference的間接處理,才可以支援OO程式設計所需的多型性質。

C++支援多型的方法:
  • 經由一組隱式的轉化操作:將一個derived class指標或引用轉化為指向其public base type的指標或引用,如shape *ps = new circle(); , circle der; shape &ps = der;
  • 經由virtual function機制。
  • 經由dynamic_cast和typeid運算子。

一個class object所需的記憶體空間由如下組成:

  • nonstatic data members以及其alignment需求(將數值調整至某數的倍數,32位機一般為4bytes(32位))的總和。
  • 為了支援virtual而內部產生的額外負擔(overhead)。

而一個指標(通常引用是由指標實現的,但sizeof引用為被指向物件的大小),無論他是指向什麼型別,指標本身所需的記憶體大小是固定的:32位機上為4bytes,64位機上為8bytes。

2.1 指標的型別

對於同一機器,指向不同型別的指標本質上是相同的,它們唯一的不同是其定址的object不同,也就是說指標型別會告訴編譯器如何去解釋某起始地址後面一個大小的記憶體內容,而void *指標只能持有一個地址,需要cast後才能指出記憶體的大小及其內容。

2.2 加上多型之後

class AnimalZoo{
	public:
		int loc;
		string name;
		virtual void dance(){
			cout<<"AZ DANCE"<<endl;
		}
		void rotate(){
			cout<<"AZ rotate"<<endl;
		}
};

class Bear:virtual public AnimalZoo{
public:
	void rotate(){
		cout<<"Bear rotate"<<endl;
	}
	void hibernate(){
		cout<<"Bear hibernate"<<endl;
	} 
	void dance(){
		cout<<"Bear DANCE"<<endl;
	}
	
	int cell_block;
};

int main(){
	Bear b;
	Bear *pb = &b;
	Bear &rb = *pb;
	AnimalZoo *pz = &b;
	
	pz->rotate(); //AZ rotate
	pb->rotate(); //Bear rotate
	pz->dance();  //Bear DANCE
	pb->dance();  //Bear DANCE
	//pz->hibernate(); error 
	pb->hibernate(); //Bear hibernate
	system("pause");
	return 0;
}
  1. pz指標呼叫ZooAnimal出現的public方法介面、public member以及Bear虛擬函式,除此以外不能用pz直接處理Bear的任何members。
  2. 對於虛擬函式,呼叫的例項由所指物件型別決定。
  3. 對於普通函式,呼叫的例項由指標型別決定
  4. pb所涵蓋的地址包含整個Bear object , pt所涵蓋的地址只包含ZooAnimal subobject。

當一個base class object被直接初始化為(或被指定為)一個derived class object時,derived object就會被**切割(sliced)**以塞入較小的base type記憶體中,derived type將沒有留下任何蛛絲馬跡。多型於是不再呈現,而一個嚴格的編譯器可以在編譯器解析一個“通過此object而觸發的virtual function呼叫操作”,因而回避virtual機制。如果virtual function被定義為inline,則更有效率上的大收穫。

一個pointer或一個reference之所以支援多型,是因為她們並不引發記憶體中有任何與型別有關的記憶體委託操作,會受到改變的只有她們所指向的記憶體的大小和內容解釋方式而已