class

1. 概念

​ 一组具有相同属性和行为的对象的集合,体现了面向对象的抽象和封装的两个特点

2. 语法:

typedef class _MyClass_{
};

3. C++四大特性

  1. 抽象:数据抽象,过程抽象,只关注目标有什么特点,不在意具体实现
  2. 封装:即隐藏对象的属性和实现细节,仅对外公开接口
  3. 继承:子类继承父类的属性和行为
  4. 多态:不同的对象接受同一消息而产生不同的行为 (不同对象使用同一成员函数产生不同结果)

4. 三种访问属性

​ public,protected,private

5. 构造和析构

​ 构造函数:必须在public里,且与类名相同,无返回值
​ 析构函数:前面加~,必须在public里,且与类名相同,无返回值

6. 成员函数定义

​ 需要用 :: 表示作用域

7. 成员函数重载

​ 函数名字一样,函数的参数个数不同或函数的参数类型不同(返回值不同不行)

8. 类成员函数调用约定

thiscall(c++):是c++成员函数特有的调用方式,不属于关键字,无法显式声明为thiscall调用方式

  1. 为什么面向对象可以用共同的方法来处理不同的数据?

    (这个问题可以用调用约定:fastcall=c++中的thiscall)

  2. 调用成员函数时,首先将对象地址赋给ecx,再call函数(ecx放当前对象地址) (函数在代码段code,成员变量在栈区)

    在成员函数开栈之后,在压入基本的ebx,esi,edi之后会push ecx,即把v1变量地址压入

  3. (为什么会有这一步,即push ecx,因为后面要用到edi,eax,ecx三个寄存器来初始化,ecx中保存的数据会被覆盖掉,所以需要push把ecx中的值存进去)

    最后有一步:pop,ecx;即把之前存的数据又放回ecx中,其他还原;在函数执行前,把ecx中的数据放入this内存中

9. 类指针

this指针:保存的当前的对象地址(ecx) (传入不同对象地址,同样的方法产生不同效果)
调用成员函数时,编译器利用寄存器ecx保存了对象的首地址,并以寄存器传参的方式将其传递到成员函数中,即this指针(thiscall)

thiscall成员函数要点

lea ecx, [mem]          ; 取对象首地址并存入ecx,要注意观察内存

call FUN_ADDRESS        ; 调用成员函数
                      	; 在函数调用内,ecx 尚未重新赋值之前
mov XXX,    ecx         ; 发现函数内使用ecx中的数据,说明函数调用前对ecx的赋值实际是在传递参数
                        ; 其后 ecx 中的内容会传递给其他寄存器

mov [reg+i], XXX        ; 寄存器相对间接寻址方式,如果能排除数组访问,reg中保存的是结构体或者类对象的首地址                        

使用指针访问结构体或类成员的公式

type *p;
p->member的地址=指针p的地址值+ member在type中的偏移量

求结构体成员的偏移量

#define offsetof(s,m) (size_t)&(((s *)0)->m)

10. 访问私有成员

10.1 指针

​ 对于私有成员:虽然语法上进行了封装,但实际使用中,可以通过指针来破解

int *v10=(int*)&v1;
_tprint(_T("%d\r\n"),*v10);
_tprint(_T("%s\r\n"),(TCHAR*)(v10+1));
一样可以取出来
void _MyClass_::ShowName(const TCHAR* name)
{
	memcpy(Name, name, (_tcslen(name)+1)*sizeof(TCHAR));
}

10.2 汇编

11. .cpp的结构体

​ 对于c++.cpp文件,结构体和类差不多一样,也有this指针
​ (但是结构体默认属性public,类private)

12. 类的组成

12.1 成员变量和成员函数

​ 对象中先定义的数据成员在低地址处,后定义的数据成员在高地址处,依次排列,对象大小只包含数据成员,类成员函数属于执行代码,不属于类对象数据

12.2 类中不能定义自身对象

​ 类需要在申请内存的过程中计算出自身的实际大小,以用于实例化;
​ 如果在类中定义了自身的对象,在计算各数据成员的长度时,又会回到自身,这样就形成了递归定义,而这个递归并没有出口,是一个无限的循环递归;
​ 但是,自身类型的指针除外,任何类型的指针在32位下所占用的内存大小始终为4字节,等同于一个常量值,因此将其作为类的数据成员不会影响长度的计算

12.3 对象长度

对象长度的计算公式:对象长度=sizeof(数据成员1)+sizeof(数据成员2)+...+sizeof(数据成员n)

四种特殊情况下该公式不符合

  1. 空类

    空类中没有任何成员,按照公式计算出来是0字节,类型长度为0,空类就无法取得实例对象的地址,this指针失效,不能实例化;

    类中有成员函数,但没有成员变量,需要实例化,分配1字节的空间用于类的实例化(实际上这1字节的数据并没有被使用)

  2. 内存对齐

  3. 静态数据成员
    静态数据成员虽然在类中被定义,但存放位置与全局变量一致

  4. 类中有虚函数

    对象中有隐含成员虚表指针(32位4字节,64位8字节)

13. 静态数据成员

​ 程序运行前,静态数据成员就已经存在,而此时类还没有实例对象,访问时无需this指针,静态数据成员为对象共有

14. 结构体和类的异同

相同点:都具有构造函数、析构函数和成员函数,
区别:结构体的访问控制默认为public,而类的默认访问控制是private

posted @ 2023-02-04 01:00  修竹Kirakira  阅读(26)  评论(0编辑  收藏  举报