1.關于vptr和vtbl
之前的學習已經(jīng)了解到c++多態(tài)主要靠虛函數(shù)實現(xiàn)掷倔,如果說c++的class在實現(xiàn)上相比c的struct有什么開銷的話,那么虛函數(shù)表(vtbl)的維護和每個對象實例里虛表指針(vptr)將是比較明顯的開銷。
對于如下三個類
class A {
public:
virtual void vfunc1() { cout << "A::vfunc1\n"; }
virtual void vfunc2() { cout << "A::vfunc2\n"; }
void func1() { cout << "A::func1\n"; }
void func2() { cout << "A::func2\n"; }
};
class B : public A {
public:
virtual void vfunc1() { cout << "B::vfunc1\n"; }
void funcb() { cout << "B::funcb\n"; }
};
class C : public B {
public:
virtual void vfunc1() { cout << "C::vfunc1\n"; }
void funcc() { cout << "C::funcc\n"; }
};
非虛成員函數(shù):A::func1(),A::func2(),B::funcb()冠句,C::funcc()會單獨在內(nèi)存里存一份
虛成員函數(shù):A::vfunc1(),A::vfunc2()幸乒,B::vfunc1()懦底,C::vfunc1()也會單獨存一份,但是這四個虛函數(shù)會由虛函數(shù)表來記錄逝变,由于這個例子里有三個類基茵,因此內(nèi)存里會有三份虛函數(shù)B::vfunc1(),A::vfunc2()壳影,表拱层,我們假設它們?yōu)锳,B宴咧,C表根灯。 A表里會有兩個指針,分別指向A::vfunc1()掺栅,A::vfunc2()的地址烙肺,B表里兩個指針,分別指向B::vfunc1()氧卧,A::vfunc2()桃笙,同理,C表里的指針指向C::vfunc1()沙绝,A::vfunc2()搏明。
對于用基類指針new子類的情況:A pa = new B; 這個實例對象里放的也是B類對應的虛函數(shù)表鼠锈,因為編譯器做了個向上轉(zhuǎn)型(upcasting)。
其實理解了虛函數(shù)表在內(nèi)存的形式后星著,調(diào)用虛函數(shù)的代碼可以這么表示: ((pa->vptr)[n])(pa) 因為第一個參數(shù)肯定是*this购笆。
2.動態(tài)綁定
為了C++的多態(tài)性,是有動態(tài)綁定和靜態(tài)綁定這兩種說法的:
靜態(tài)綁定:綁定的對象是靜態(tài)類型虚循,也就是編譯期就能決定的同欠,是確定的,不會更改的横缔,比如 A a; a的內(nèi)容雖然會在運行期發(fā)生改變铺遂,但是a就是a,這點是不會變的茎刚。
動態(tài)綁定:綁定的對象是動態(tài)類型娃循,動態(tài)類型就是指在編譯期無法決定的,因為它可能在運行期發(fā)生改變斗蒋,比如指針:A* pa; pa可以在運行時重新指向其他對象笛质,或者轉(zhuǎn)型指向B類或者C類泉沾。
3.const
const對象不能調(diào)用non-const函數(shù),只能調(diào)用const函數(shù)妇押。
4.關于new,delete和重載
new和delete也是常用的跷究,新申明的數(shù)組需要使用內(nèi)存時,需要new一段size大小的空間敲霍,當使用結束時delete掉俊马,即釋放內(nèi)存,他倆是配合著使用的肩杈,不能忘寫其中一個柴我。
全局的new有六種重載形式:
void *operator new(std::size_t count)
throw(std::bad_alloc); //一般的版本
void *operator new(std::size_t count, //兼容早版本的new
const std::nothrow_t&) throw(); //內(nèi)存分配失敗不會拋出異常
void *operator new(std::size_t count, void *ptr) throw();
//placement版本
void *operator new[](std::size_t count) //
throw(std::bad_alloc);
void *operator new[](std::size_t count, //
const std::nothrow_t&) throw();
void *operator new[](std::size_t count, void *ptr) throw();
5.C++的各種new簡介
1.new T
第一種new最簡單,調(diào)用類的(如果重載了的話)或者全局的operator new分配空間,然后用類型后面列的參數(shù)來調(diào)用構造函數(shù)
2. new T[]
這種new用來創(chuàng)建一個動態(tài)的對象數(shù)組,他會調(diào)用對象的operator new[]來分配內(nèi)存(如果沒有則調(diào)用operator new,搜索順序同上),然后調(diào)用對象的默認構造函數(shù)初始化每個對象
3.new()T 和new() T[]
這是個帶參數(shù)的new,這種形式的new會調(diào)用operator new(size_t,OtherType)來分配內(nèi)存
這里的OtherType要和new括號里的參數(shù)的類型兼容,這種語法通常用來在某個特定的地址構件對象,稱為placement new,前提是operator new(size_t,void*)已經(jīng)定義,通常編譯器已經(jīng)提供了一個實現(xiàn),包含頭文件即可,這個實現(xiàn)只是簡單的把參數(shù)的指定的地址返回,因而new()運算符就會在括號里的地址上創(chuàng)建對象.需要說明的是,第二個參數(shù)不是一定要是void*,可以識別的合法類型,這時候由C++的重載機制來決定調(diào)用那個operator new.當然,我們可以提供自己的operator new(size_,Type),來決定new的行為
4. operator new(size_t)
這個的運算符分配參數(shù)指定大小的內(nèi)存并返回首地址,可以為自定義的類重載這個運算符,方法就是在類里面聲明加上void *operator new(size_t size)
5.operator new[](size_t)
這個也是分配內(nèi)存,,只不過是專門針對數(shù)組,也就是new T[]這種形式,當然,需要時可以顯式調(diào)用
6.operator new(size_t size, OtherType other_value)和operator new[](size_t size, OtherType other_value)
需要強調(diào)的是,new用來創(chuàng)建對象并分配內(nèi)存,它的行為是不可改變的,可以改變的是各種operator new,我們就可以通過重載operator new來實現(xiàn)我們的內(nèi)存分配方案.