1.类初始化
通过类对象调用函数时,this指针会作为第一个参数
cpp
class MySharePtr {
public:
MySharePtr(int val) : a(val) {
// a = val; // 不使用初始化列表的方式
}
int a = 10;
};
// 使用初始化列表的方式, 直接使用传入的参数进行构造
MySharePtr::MySharePtr(int) [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov rax, QWORD PTR [rbp-8]
mov edx, DWORD PTR [rbp-12]
mov DWORD PTR [rax], edx
nop
pop rbp
ret
// 不使用初始化列表的方式, 在有默认值的情况下,明显是先按照默认值赋值后,再使用传入的参数进行构造
MySharePtr::MySharePtr(int) [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax], 10
mov rax, QWORD PTR [rbp-8]
mov edx, DWORD PTR [rbp-12]
mov DWORD PTR [rax], edx
nop
pop rbp
ret
因此对于常量成员或引用类型成员,必须在初始化列表中进行初始化,因为它们无法在构造函数体内赋值。
动态内存调用,因为事先知道类的大小,因此首先将该类大小作为第一个参数,调用new函数申请空间,再调用相应构造函数。
2.多重继承下的虚函数
- 之前,看其他博客上,有很多人说多重继承下虚函数表指针是依次放在类存储空间头部的。
但,今天在看gcc13.2编译后的汇编代码时,发现虚函数表指针在存储的布局如下所示
cpp
vptr base class1
base class1 datamember
vptr base class2
base class2 datamember
vtable for Soccerr:
.quad 0
.quad typeinfo for Soccerr
.quad Soccerr::printId()
.quad Soccerr::printId3()
.quad Soccerr::printId4()
.quad -16
.quad typeinfo for Soccerr
.quad PlayerSP::printIdSP()
这种方式在类初始化时比较简单,因为已经知道基类大小,只需要初始化基类1,偏移基类1大小,初始化基类2即可。
- 上述的
.quad -16
表示的是,将Soccerr
类转换为PlayerSP
基类的偏移量。