list 繼承 __list_imp,所以 __link_nodes_at_front() 的 base::__end_ 是 __list_imp 的 __end_。從上面四個 function 看起來,__end_.__prev_ 指到 list 的最後一個 node,__end_.__next_ 指向 list 第一個 node。也就是說,__end_ 是用來記錄 list 的第一個及最後一個 node。
d-pointer 是個 design pattern,也叫 Opaque pointer 或 Pimpl idiom。它分開給外面看的公開 class interface 跟實作的 class。做法是在公開 class 放個實作 class 的 pointer,公開 class 各 function 只是 call implementation class 的 function。公開 class 中跟實作相關的 member 只有那個 pointer,所以 size 會固定是 pointer 的 size。老覺得在哪看過類似的東西……
Qt 用 d-pointer 主要是為了保持 binary compatibility。
Binary Compatibility
有 binary compatibility 表示 application 在以舊版 library compile 後,新版 library binary 可以直接取代舊版 library 而不需要重新 compile application。要達到 binary compatibility 就不能改變已經公開出去的 C++ class 的 size 或 memory layout,即不能增加 member 或者更改 function 跟 member 的順序。如果改了,application 認定的 library class size 或 memory layout 跟新版 library 不同,會把 memory 踩爛掉。
Qt 使用 d-pointer 達到不修改既有公開 class 還能增加 feature 或修改實作細節。
voidQStandardItemPrivate::setChild(int row, int column, QStandardItem *item, bool emitChanged) { Q_Q(QStandardItem); ... if (rows <= row) q->setRowCount(row + 1); ... }
透過 q_func() 拿到的 q 是轉型成 QStandardItem* 的 pointer。
這個例子裡,因為 QStandardItem 跟 QStandardItemPrivate 沒有繼承別人,它們自己分別記錄了 d_ptr 跟 q_ptr,所以 d_func() 跟 q_func() 有沒有轉型根本沒差。但其他繼承 QObjectPrivate 的 class,q_ptr 是從 QObjectData 來的,type 是 QObject*,如果要直接使用 sub class 的 member 就得轉型,d_ptr 同理。除非用 virtual function 做多型,不過這個狀況用 virtual function 很怪,因為這樣 base class QObject 得有所有 sub class 會用到的 interface,這邊顯然不是這個意思,用 virtual function 不合理。
要執行一隻程式須將它的 instruction 跟 data load 到 memory。現代 OS 結合 virtual memory 以及 memory management 的 paging 機制來 load process。
virtual memory 將 process 認知的 memory space 跟 physical memory 分離,藉此達到 process 不一定要在它認定的 physical memory address 上執行。如果實際有的記憶體比較少,virtual memory 也可以在記憶體不足的情況下讓 process 以為它有這麼多 memory 可用。
每個 process 有自己的 virtual memory space,virtual memory space 的大小由平台的定址 bit 數決定,例如 32 bit 的平台可定址的 address 就從 0 到 2^32 - 1,總大小為 4 GB。process 在 32 bit 系統中的 virtual memory space 大小是 4GB,但不是 4GB 都可以為 process 使用,process 只能用 OS 分配給它的空間,亂用的話會被強制結束。
paging 簡單來說是將 memory space 分成固定 size 的 page,現在 OS 大多使用 4K size 的 page。virtual memory space 跟 physical memory space 都以 page 切分,MMU(Memory Management Unit)負責 virtual page 與 physical page 的 mapping。virtual page 不存在 physical memory 而該 virtual page 又要被使用時會發生 page fault,此時 OS 負責將 virtual page 從 disk 中讀到 memory 並建立 virtual page 與 physical page 之間的 mapping。
結合 virtual memory 跟 paging 的 load process 步驟:
建立這個 process 的 virtual memory space,實際上是建立 virtual page 對應到 physical page 所需的 data structure。
由 executable file header 的資訊建立 virtual space 與 executable file 的關係,也就是 process 的 virtual memory space 哪一塊對應到 executable file 的哪一塊。
將 PC register 設到執行入口,process 開始執行。
process 執行到的程式或需要的 data 沒有 load 進 physical memory,發生 page fault。OS 依照 virtual space 與 executable file 的對應關係知道要從 disk load 哪個 page 到 physical memory,load 進 physical memory 後建立 virtual apge 與該 physical page 的關係,這段即一般講 load 所指的動作。控制權還給 process 繼續跑。
Symbol table '.symtab' contains 11 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 5: 0000000000000000 0 SECTION LOCAL DEFAULT 6 6: 0000000000000000 0 SECTION LOCAL DEFAULT 7 7: 0000000000000000 0 SECTION LOCAL DEFAULT 5 8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 sum 9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 main 10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
Relocation section '.rela.text' at offset 0x2a0 contains 3 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000014 000c00000002 R_X86_64_PC32 0000000000000000 sum - 4 00000000001b 000300000002 R_X86_64_PC32 0000000000000000 .data + 0 00000000001f 000c0000000b R_X86_64_32S 0000000000000000 sum + 0
Relocation section '.rela.data' at offset 0x2e8 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000008 000300000001 R_X86_64_64 0000000000000000 .data + 10
Relocation section '.rela.eh_frame' at offset 0x300 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0
$ readelf -r main.o
Relocation section '.rela.text' at offset 0x238 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 00000000000f 000a00000002 R_X86_64_PC32 0000000000000000 foo - 4
Relocation section '.rela.eh_frame' at offset 0x250 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0