类
◼ C++中可以使用struct、class来定义一个类
◼ struct和class的区别
struct的默认成员权限是public
class的默认成员权限是private
struct Person {
// 成员变量(属性)
int m_age;
// 成员函数(方法)
void run() {
cout << "Person::run() - " << m_age << endl;
}
};
int main() {
// 利用类创建对象
Person person;
person.m_age = 20;
person.run();
}
class的默认成员权限是private
class Person {
// 成员变量(属性)
int age;
// 成员函数(方法)
void run() {
cout << "Person::run()" << endl;
}
};
#include <iostream>
using namespace std;
class Person {
public:
// 成员变量(属性)
int m_age;
// 成员函数(方法)
void run() {
cout << "Person::run()" << endl;
}
};
int main(){
Person person;
person.m_age = 20;
Person *p = &person;
p->m_age = 40;
p->run();
}
◼ 每个人都可以有自己的编程规范,没有统一的标准,没有标准答案,没有最好的编程规范
◼ 变量名规范参考
- 全局变量:g_
- 成员变量:m_
- 静态变量:s_
- 常量:c_
- 使用驼峰标识
◼ 可以尝试反汇编struct和class,看看是否有其他区别
class Car {
public:
int m_price;
void run() {
cout << "Car::run() " << m_price << endl;
}
};
int main() {
// mov dword ptr [ebp-8],0Ah
// mov dword ptr [ebp-8],0Ah
Car car;
car.m_price = 10;
car.run(); // call 函数地址
}
- Class
- Struct
_car$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _car$[ebp], 10 ; 0000000aH
lea ecx, DWORD PTR _car$[ebp]
call void Car::run(void) ; Car::run
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
通过汇编可以看出,struct和class 在调用处的汇编代码完全一致,可以说明,struct和class唯一的不同,就是权限控制访问不同。
对象的内存布局
#include <iostream>
using namespace std;
struct Person {
int m_id;
int m_age;
int m_height;
void display() {
cout << "id = " << m_id
<< ", age = " << m_age
<< ", height = " << m_height << endl;
}
};
// 全局区(数据段)
// Person person;
int main() {
// 这个person对象内存在栈空间
Person person;
person.m_id = 1;
person.m_age = 2;
person.m_height = 3;
cout << "&person == " << &person << endl;
cout << "&person.m_id == " << &person.m_id << endl;
cout << "&person.m_age == " << &person.m_age << endl;
cout << "&person.m_height == " << &person.m_height << endl;
return 0;
}
&person == 0x7ffee6844880
&person.m_id == 0x7ffee6844880
&person.m_age == 0x7ffee6844884
&person.m_height == 0x7ffee6844888
◼ 思考:如果类中有多个成员变量,对象的内存又是如何布局的?
this
struct Person {
// 成员变量(属性)
int m_age;
// 成员函数(方法)
void run() {
cout << "Person::run() - " << m_age << endl;
}
};
int main() {
// 利用类创建对象
Person person;
person.m_age = 20;
person.run();
Person person2;
person2.m_age = 30;
person2.run();
}
Person::run() - 20
Person::run() - 30
◼ ==this是指向当前对象的指针==
void Person::run(void) ENDP ; Person::run
_person2$ = -8 ; size = 4
_person$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
sub esp, 8
mov DWORD PTR _person$[ebp], 20 ; 00000014H
lea ecx, DWORD PTR _person$[ebp] ;lea 将ebp-4 的地址值放入ecx
call void Person::run(void) ; Person::run
mov DWORD PTR _person2$[ebp], 30 ; 0000001eH
lea ecx, DWORD PTR _person2$[ebp] ;lea 将ebp-8 的地址值放入ecx
call void Person::run(void) ; Person::run
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
◼ 对象在调用成员函数的时候,会自动传入当前对象的内存地址
将run函数修改一下来看汇编代码
void run() {
// this指针存储着函数调用者的地址
// this指向了函数调用者
// cout << "Person::run() - " << this->m_age << endl;
// this->m_age = 3;
m_age = 3;
/*
// ebp-8是this指针的地址
mov dword ptr [ebp-8],ecx
mov eax,dword ptr [ebp-8]
mov dword ptr [eax],3
*/
}
_this$ = -4 ; size = 4
void Person::run(void) PROC ; Person::run, COMDAT
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], 3
mov esp, ebp
pop ebp
ret 0
void Person::run(void) ENDP ; Person::run
_person2$ = -8 ; size = 4
_person$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
sub esp, 8
mov DWORD PTR _person$[ebp], 20 ; 00000014H
lea ecx, DWORD PTR _person$[ebp]
call void Person::run(void) ; Person::run
mov DWORD PTR _person2$[ebp], 30 ; 0000001eH
lea ecx, DWORD PTR _person2$[ebp]
call void Person::run(void) ; Person::run
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
可以看出,类函数调用前会将对象的地址放入ecx 然后进入函数调用后,第一条语句,就是把ecx的值给了this变量来存储。
◼ 可以利用this.m_age来访问成员变量么?
- 不可以,因为this是指针,==必须用this->m_age==
指针访问对象成员的本质
struct Person {
int m_id;
int m_age;
int m_height;
void display() {
cout << "id = " << m_id
<< ", age = " << m_age
<< ", height = " << m_height << endl;
}
};
void test() {
Person person;
person.m_id = 5;
Person *p = &person;
p->m_id = 10;
p->m_age = 10;
p->m_height = 10;
p->display();
}
来分析这段汇编代码
_p$ = -20 ; size = 4
_person$ = -16 ; size = 12
__$ArrayPad$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
sub esp, 20 ; 00000014H
mov eax, DWORD PTR ___security_cookie
xor eax, ebp
mov DWORD PTR __$ArrayPad$[ebp], eax
mov DWORD PTR _person$[ebp], 5 ;ebp-16 赋值 5
lea eax, DWORD PTR _person$[ebp] ;lea 将_person的地址值给到 eax
mov DWORD PTR _p$[ebp], eax ;将eax的值放入 ebp-20的中 即 Person *p = &person;从上面汇编来看,所以-->说&是取地址
mov ecx, DWORD PTR _p$[ebp] ;将[ebp-20]的值给ecx ecx=&person
mov DWORD PTR [ecx], 10 ; 0000000aH ;ecx=&person=10
mov edx, DWORD PTR _p$[ebp] ;将[ebp-20]的值给edx edx=&person
mov DWORD PTR [edx+4], 10 ; 0000000aH ;[edx+4]=&person+4=10
mov eax, DWORD PTR _p$[ebp]
mov DWORD PTR [eax+8], 10 ; 0000000aH
mov ecx, DWORD PTR _p$[ebp]
call void Person::display(void) ; Person::display
xor eax, eax
mov ecx, DWORD PTR __$ArrayPad$[ebp]
xor ecx, ebp
call @__security_check_cookie@4
mov esp, ebp
pop ebp
ret 0
_main ENDP
从这段汇编代码可以看出,指针访问成员变量,想获取对象的地址,然后通过对象的地址偏移 +0 +4 +8即通过对象的内存布局来直接操作地址来赋值。
◼ 思考:==最后打印出来的每个成员变量值是多少?==
struct Person {
int m_id;
int m_age;
int m_height;
void display() {
cout << "id = " << m_id
<< ", age = " << m_age
<< ", height = " << m_height << endl;
}
};
int main() {
Person person;
person.m_id = 10;
person.m_age = 20;
person.m_height = 30;
Person *p = (Person *) &person.m_age;
p->m_id = 40;
p->m_age = 50;
// 将person对象的地址传递给display函数的this
person.display();
return 0;
}
id = 10, age = 40, height = 50
如果将person.display()换成p->display()呢?
// 会将指针p里面存储的地址传递给display函数的this
// 将&person.m_age传递给display函数的this
p->display();
id = 40, age = 50, height = 0