天天看點

C++ 編譯器C++編譯器C++中的對象在記憶體中的分布

C++編譯器

  • 當我們定義了一個類的時候, C++編譯器在預設的情況下會為我們添加預設的構造方法, 拷貝構造方法, 析構函數和=運算符
  • 在第一次建立對象的語句中如: MyString myString = "hello, world!";中, 如果我們定義的構造函數為如下, 則就是隐式調用構造方法, 如果構造方法使用了explicit修飾則會報錯, 總之在第一次建立對象的語句中, 就算出現了=, 隻能調用構造方法, 而不是=方法, 因為我們是要構造對象, =真正起作用是在這個對象建立完畢之後
MyString(const char *str="") {
    this->len = strlen(str);
    this->values = new char[this->len + 1];
    strcpy(this->values, str);
}           

C++中的對象在記憶體中的分布

沒有繼承的情況下

  • 就是一個結構體的分布

單繼承的情況下(方法使用virtual修飾)

  • A為基類, B為派生類, 在建立B的執行個體的時候, 在記憶體中B的記憶體中會有一個虛指針(virt_pa)指向一個虛函數表, 注意是一個虛指針(virt_pa), 預設先拷貝A中的虛函數表, 如果B中有新的虛函數則注冊到這個表中, 若B中重載了A中的虛函數, 則将虛函數表中的那個對應的虛函數改成B的虛函數(這就是實作多态的關鍵)

在多繼承的情況下(方法都是用virtual修飾)

  • A和B為C的基類
  • 和單繼承類似, 隻不過多出了一個虛指針(virt_pb), 這個虛指針對應的虛函數表在C對象建立的時候會自動拷貝B的虛函數表, 而另外一個虛指針(virt_pa)指向的虛函數表則拷貝A的虛函數表, 接着在看C類中定義的函數, 如果C中出現了新的虛函數, 則将這個虛函數放到C第一個繼承的基類在C對象中對應的虛指針指向的虛函數表中, 這裡指的就是A, 也就是所B中多出來的foo函數的位址會放到virt_pa指向的虛函數表中, 如果C重載了函數, 則判斷是重載了哪個函數, 這個函數從哪個基類中繼承過來的, 知道了是從哪個基類中繼承過來的, 就可以通過對應的虛指針找到被重載的函數在虛函數表中的位置, 将其替換成C中重載了的函數, 比如, A中有一個foo函數, C中也有一個foo函數, 則編譯器會通過virt_pa指針在對應的表中将C::foo的位址替換到A::foo所在的位置

多重單繼承

  • A -> B -> C
  • 和單繼承非常的類似, 也是隻有一個指針, 這個指針為virt_pa, 可以知道是超級基類的, 在建立C對象的時候, 将A和B中的虛函數表拷貝到C中的virt_pa對應的虛函數表中, 在考慮重載

虛繼承

  • A -> B
  • 虛繼承與上面的繼承最大的不同就是在建立了B對象的時候, 會有兩個虛指針, 一個是virt_pa, 另外一個是virt_pb, 多出來一個與B相關的指針; 除此之外, virt_pb指向的虛函數表隻儲存B類中定義的所有的虛函數, 如果B重載了A中的某一個方法, 則就會将B中那個函數的指針拷貝到virp_pa指向的虛函數表中的被重載函數的位置

繼續閱讀