1.對象模型(objiect model):關於vptr和vtbl以及關於Dynamic Binding
繼承時除了繼承成員變量外,還繼承成員函數;繼承的成員函數是繼承它的調用權;
動態綁定:即通過指向對象的指針找到相應的虛函數虛表,找到其中的第n個,把它當成函數指針,去調用這個函數;
(*(p->vptr)[n])(p)或(*p->vptr[n](p));
n就是這個虛函數在虛函數表格中的第幾個;
第一,通過指針;
第二,向上轉型;
第三,調用虛函數;
只要符合這三個條件,編譯器就會走上面那條路線;
2.對象模型(objiect model):關於Dynamic Binding
假設:A是父類,B繼承A,C繼承B;
B b;
A a = (A)b;
a.vfun1(); //通過對象調用,不是指針,所以是靜態綁定; //vfun1()是虛函數
A* pa = new B;
pa->vfun1(); //pa是基類指針,但是指向派生類對象,由於動態綁定的存在,它會找到派生類重寫的虛函數,調用它,調用路線如上述1;
3.對象模型(objiect model):關於this指針
示例代碼:
CDocument::OnFileOpen() //父類的OnFileOpen()函數
{
...
serialize();
...
}
virtual serialize();
class CMyDoc:
public CDocument
{
virtual serialize(){...};
}
int main()
{
CMyDoc myDoc;
...
myDoc.OnFileOpen();
}
在main()函數裏,先定義了一個CMyDoc類型的對象myDoc,然後myDoc調用類的成員函數OnFileOpen();這個OnFileOpen不是CMyDoc寫的,而是CMyDoc的父類CDocument寫的,在父類CDocument裏,它做完了OnFileOpen所有基礎的工作,剩下一個serialize;這個函數是一個虛函數,CMyDoc在繼承的時候重寫了它;所以,結果上面那些操作,語句myDoc.OnFileOpen()會先執行父類已經做好的工作,等到調用serialize函數的時候,編譯器會找到CMyDoc重寫的serialize的內容,執行它;
所以OnFlieOpen()既有父類已經做好的內容,也有子類自己的部分;
其中,編譯器在調用 myDoc.OnFileOpen()時,用C的寫法,可以寫成這樣:CDocument::OnFileOpen(&myDoc);因爲調用OnFileOpen的是MyDoc,所以this指針實際上是指向myDoc的。
在C++裏,所有的成員函數一定都有一個隱藏的this指針作爲參數;
4.談談const
const member function(常量成員函數)
const要放只能放在成員函數後頭,一般的全局函數是不可以在函數描述後加const的;
放在那個位置的意思就是告訴編譯器設計者本人不打算改變成員變量?
const算不算函數簽名的一部分;算
變量是共享的,就必須Copy On Write
例:
charT
operator[](size_type pos)const
{..../*不必考慮COW*/}
reference
operator[](size_type pos)
{.../*一定要考慮COW*/}
C++語法規定,當成員函數的const和non-const同事存在時,const objiect只能調用const版本;
non-const objiect只能調用non-const版本;
5.關於new和delete的重載
new底層實際調用malloc,delete底層調用free
//下面是這四個重載函數的接口
//inline void* operator new(size_t size) //重載這四個操作符一點要有參數
{cout << " " ; return myAlloc(size);}
//inline void* operator new[](size_t size)
{cout << " " ; return myAlloc(size);}
//inline void* operator delete(void* ptr)
{cout << " " ; return myFree(ptr);}
//inline void* operator delete[](void* ptr)
{cout << " " ; return myFree(ptr);}
void* myAlloc(size_t size)
{return malloc(szie);}
void myFree(void* ptr)
{return free(ptr);}
上面四個重載函數只是提供一個接口,實際上new delete new[] delete[] 底層仍然調用malloc和free,在這些接口函數裏,我們可以自己進行一些其他的操作,防止內存泄漏。
它們一旦被重載,影響深遠,慎重慎重。
class Foo
{
public:
void* operator new(size_t size);
void* operator delete(void*,size_t);//size_t可要可不要
};
try
{
void* mem = operator new(sizeof(Foo));
p = static_cast<Foo*>(mem);
p->Foo::Foo();
}
p->~Foo();
operator delete(p);
Foo* p = new Foo;
...
delete p;
示例接口
Foo* pf = new Foo; //若沒有重載,就使用全局的new和delete,如果重載就調用設計者設計的;
delete pf;
語法上提供,有下面一種用法
Foo* pf = ::new Foo;
::delete pf;
這樣使用者會跳過設計者重載的這樣使用者會跳過設計者重載的delete和new函數而是用全局的;
下面我們看看重載了new和delete new[]和delete[]的對象的大小;
對於下面這種情況,被new出來的Foo對象的大小是12;
Foo* p = new Foo(7);
delete p;
對於這種情況,被new[]和delete[]出來的對象大小是64(12*5+4),除了5個Foo對象之外,還有一個count計數器,記着使用者new了多少個Foo對象;
Foo* pArray = new Foo[5];
delete[] pArry;
重載new()和delete()
我們可以重載class member operator new(),寫出多個版本,前提是每一個版本都必須有獨特的參數類,第一參數必須是size_;
我們也可以重載class member operator delete(),寫出多個版本。但它們絕對不會被到delete調用,只有當new所調用的構造函數。。。
即使operator delete()沒有一一對應operator new(),編譯器也不會報錯;