1.關於vptr和vtbl
之前的學習已經了解到c++多型主要靠虛函式實現,如果說c++的class在實現上相比c的struct有什麼開銷的話,那麼虛函式表(vtbl)的維護和每個物件例項裡虛表指標(vptr)將是比較明顯的開銷。
對於如下三個類
class a
virtual void vfunc2()
void func1()
void func2()
};class b : public a
void funcb()
};class c : public b
void funcc()
};
非虛成員函式:a::func1(),a::func2(),b::funcb(),c::funcc()會單獨在記憶體裡存乙份
虛成員函式:a::vfunc1(),a::vfunc2(),b::vfunc1(),c::vfunc1()也會單獨存乙份,但是這四個虛函式會由虛函式表來記錄,由於這個例子裡有三個類,因此記憶體裡會有三份虛函式b::vfunc1(),a::vfunc2(),表,我們假設它們為a,b,c表。 a表裡會有兩個指標,分別指向a::vfunc1(),a::vfunc2()的位址,b表裡兩個指標,分別指向b::vfunc1(),a::vfunc2(),同理,c表裡的指標指向c::vfunc1(),a::vfunc2()。
對於用基類指標new子類的情況:a *pa = new b; 這個例項物件裡放的也是b類對應的虛函式表,因為編譯器做了個向上轉型(upcasting)。
其實理解了虛函式表在記憶體的形式後,呼叫虛函式的**可以這麼表示: (*(pa->vptr)[n])(pa) 因為第乙個引數肯定是*this。
學習群裡u6th9d當時給我們提供了一些虛函式相關考驗題
cat cat("cat");
dog dog("dog");
animal* pcat = &cat;
animal* pdog = &dog;
std::cout << "l01: ";
pcat->say();
std::cout << "l02: ";
pdog->say();
std::cout << "l03: ";
cat.say();
std::cout << "l04: ";
dog.say();
void* tmp = ((void**)pcat)[0];
((void**)pcat)[0] = ((void**)pdog)[0];
((void**)pdog)[0] = tmp;
std::cout << "l05: ";
pcat->say();
std::cout << "l06: ";
pdog->say();
std::cout << "l07: ";
cat.say();
std::cout << "l08: ";
dog.say();
之前也提到,成員物件的第乙個內容是虛表,因此中間那段就是交換了物件裡的虛表指標,使得*pcat裡的vptr指向dog類的vtbl,*pdog的vptr指向cat類的vtbl,結果:
l01: cat miaomiao~~
l02: dog wangwang~~
l03: cat miaomiao~~
l04: dog wangwang~~
l05: cat wangwang~~
l06: dog miaomiao~~
l07: cat miaomiao~~
l08: dog wangwang~~
l05和l06確實交換了,但是l07和l08並沒有表,我當時沒弄清楚,續表指標不是都變了嗎?為什麼呼叫的還是原來的函式呢?原因在於對動態繫結的理解
2.動態繫結
為了c++的多型性,是有動態繫結和靜態繫結這兩種說法的:
靜態繫結:繫結的物件是靜態型別,也就是編譯期就能決定的,是確定的,不會更改的,比如 a a; a的內容雖然會在執行期發生改變,但是a就是a,這點是不會變的。
動態繫結:繫結的物件是動態型別,動態型別就是指在編譯期無法決定的,因為它可能在執行期發生改變,比如指標:a* pa; pa可以在執行時重新指向其他物件,或者轉型指向b類或者c類。
通過vptr和vtbl實現虛函式是基於動態繫結的,因此基於指標呼叫的虛函式pcat,pdog呼叫的函式會隨著虛表指標的改變發生改變,但是用過普通物件直接呼叫,例如l07和l08,這是靜態繫結,在編譯期就已經決定了要呼叫的函式,因此不會改變。
第五周學習筆記
課程位址 第十三講,高斯混合模型 主要內容 值得注意的地方 1.em演算法的收斂 em演算法通過最大化似然函式的下界 使用jesen不等式得到 來近似最大化似然函式,其優化過程相當與對於其下界函式的座標上公升。2.高斯混合模型的優化 q step中的優化是乙個條件極值問題,約束是 j j 1 sig...
Boolan C 第二週學習筆記
第二週的課堂筆記,主要記錄一些以前自己不知道得東西。1.所謂的建構函式的三原則 其實預設情況下,編譯器缺省會提供拷貝構造,拷貝賦值,析構函式,它們所執行的就是預設的賦值與析構操作。並不是所有自定義的類都需要手動去寫這些函式,這次課程提供的乙個典型的情況就是類成員裡有指標時的情況,因為指標new出來的...
第五周學習筆記 201711671206
2.類的祖先類 object類 class a等同於class a object 3.在同乙個包中,子類繼承父類中的成員變數以及成員方法 除了private的 如果不在同乙個包裡面的話就繼承保護和公有的 4.成員變數的隱藏 所宣告的成員變數的名字和從父類繼承來的名字相同,子類就會隱藏繼承來的成員變數...