汇编分析C++class
文章目录
- this指针不神奇
-
- 静态成员方法
- 构造函数
-
- 拷贝构造和移动构造
- 运算符重载
-
- 拷贝赋值和移动赋值
- 多态机制
-
- 虚函数
-
- 虚表
-
- 对象怎么获取虚表
- 构造函数禁止为虚函数
- 继承体系中析构函数必为虚函数
- 模板对CPU不可见
- malloc和new
-
- free和delete
- 请配对new和delete
-
- free如何确定释放空间的大小
this指针不神奇
我们都知道面向对象是C++和C最大的区别,C++通过类将一组属性和一组方法捆绑在一起,分别称为成员属性和成员方法。对于非静态成员方法的访问必须通过实例化对象才能访问,对于非静态成员方法来说一般都会去读写特定的对象属性,因此如何确定读写哪个对象的属性就显得尤为重要了,这个问题通过this指针来解决,所谓this指针就是一个实例化对象的地址,编译器在编译时会自动的向调用非静态成员方法的地方添加一个隐含参数,这个参数就是this,方法内通过this寻址就可以确定目标读写对象
所谓的成员方法只是语法层面的说法,在汇编层面来说本质都是普通的函数
C++代码
class A{
public:
void class_fun(){} //形式上无参,实则有一个参数
};
void normal_fun(long a){
}
int main(){
A a;
a.class_fun();
normal_fun((long)&a);
return 0;
}
汇编代码
A::class_fun():
;...
mov QWORD PTR [rbp-8], rdi
;...
normal_fun(long):
;...
mov QWORD PTR [rbp-8], rdi
;...
main:
;...
lea rax, [rbp-1]
mov rdi, rax
call A::class_fun()
lea rax, [rbp-1]
mov rdi, rax
call normal_fun(long)
;...
通过汇编结果可以很容易看出对于normal_fun和class_fun的调用都由寄存器rdi保存参数,进入函数体后从中读取参数;通过对比normal_fun可以判断class_fun的的确确是有一个参数的,这个参数就是this
静态成员方法
根据上述结果不难知道为什么静态成员方法不能访问非静态成员属性了,因为对于静态成员方法来说它没有隐藏的this指针,自然就不知道读写哪一个对象的属性了,但是却可以读写静态成员属性(因为静态成员属性属于类而非对象)
C++代码
class A{
public:
static void static_fun(){}
};
int main(){
A::static_fun();
return 0;
}
汇编代码
A::static_fun():
push rbp
mov rbp, rsp
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
call A::static_fun()
mov eax, 0
pop rbp
ret
从汇编结果分析你看不到任何传参操作,即编译器不会为静态成员方法设置this指针
构造函数
构造函数也与普通函数一样,它也具有隐含的参数this,并且它是一个void返回类型的函数(void不需要显式声明)
C++ code
class A{
public: A(){x=1;}
protected: int x;
};
class B:public A{
public: B(){y=1;}
protected: int y;
};
int main(){
A a;B b;
return 0;
}
assembly code
A::A() [base object constructor]:
;...
mov QWORD PTR [rbp-8], rdi ;读取this
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax], 1
;...
B::B() [base object constructor]:
;...
mov QWORD PTR [rbp-8], rdi ;读取this
mov rax, QWORD PTR [rbp-8]
mov rdi, rax
call A::A() [base object constructor]
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax+4], 1
;...
通过汇编可以看出派生类在构造函数中会先隐式调用基类的构造函数(对于汇编结果的line10~~line12),而后在执行派生类自己的构造函数,但是大多数情况下需要程序员在派生类的执行构造函数之前显