滴水逆向笔记系列-c++总结1-34.this指针-35.继承_构造_析构函数
第三十四课 c++1 this指针
1.结构体参数传递
结构体直接作为函数参数传递时,本质上是将结构体成员copy一份传到函数的栈中,这样会比较浪费空间。所以一般我们使用结构体指针传参
struct Base{
int x;
int y;
/*int Max(Base* pb){ //结构体指针传参
return pb->x + pb->y;
}*/
};
int Max(Base* pb){ //结构体指针传参
return pb->x + pb->y;
}
int main(int argc,char* argv[]){
Base base;
base.x = 1;
base.y = 2;
printf("%d\n",Max(&base));
printf("%d",sizeof(base)); //打印一下结构体的大小
return 0;
}
当函数放在结构体内和结构体外时,我们可以看到打印出来的结构体大小都是8;但是调用的时候就需要使用base.Max()去调用
总结:
- 函数在结构体内外从底层来看是没有区别的,都是直接找到函数地址就可以调用,但是从编译器角度来看,为了方便管理,所以需要用base.Max()去调用
- 而且函数就算放在结构体里面,但函数地址也不在结构体里面
封装:
- 把函数扔到结构体里面了就是封装,
- 为什么扔到结构体里面?因为使用里面的变量方便,因为创建函数的时候会替我们自动传递结构体的首地址
- 可以把结构体看作就是一个类,那么用这个结构体(类)创建的就是对象
- 结构体里的变量,函数都叫成员
2.this指针
this就是上面所说的成员函数会被自动多传一个结构体首地址,这个就是this指针
用下来感觉最大的用处就是区分函数和类里重复名的变量,this->day就是类里的,day就是函数里的局部函数
0x01 this指针的用处
用处一:初始化函数内区分传参与成员变量
在下面代码中,在Max()函数里感觉有没有this好像都可以编译运行,不需要this也没问题
但是在init初始化函数里,没有使用this就会出问题了,编译器并不知道哪个是传参进来的xy,哪个是base结构体里的成员变量xy
所以使用this指针即可分别处理
用处二:返回当前对象的首地址
0x02 this使用注意
- 编译器不允许我们对this指针重新赋值
因为this指针在c++里面是个关键词,编译器认为他只需要代表结构体首地址即可,不需要去进行运算
作业
1、
#include <stdio.h>
struct xy
{
/*int x = 1;
int y = 2;
int add()
{
return x + y;
}
int decrease()
{
return x - y;
}
int ride()
{
return x * y;
}
int divide()
{
return x / y;
}*/
};
int a = 3;
int b = 4;
int add()
{
return a + b;
}
int main() {
xy xy;
//xy.add();
printf("size: %d",sizeof(xy));
add();
}
2、成员函数多传了一个this(结构体首地址)给ecx寄存器
3、空结构体大小
4、
第三十五课 c++2 继承 构造-析构函数
1.重载
函数包括构造函数都可以存在多个一样的函数,这种就叫重载
只要传参个数或者类型不一样即可,根据你的传参个数或者类型判断你使用的是哪个函数
析构函数不能重载
2.继承
- 使用继承和没使用继承自己创建变量的汇编语句是一样的,没有区别
- 相当于编译器替我们复制了这些重复的成员变量而已
总结:
1、什么是继承?
继承就是数据的复制
2、为什么要用继承?
减少重复代码的编写
3、Person 称为父类或者基类
4、Teacher、Student称为子类或者派生类
5、t和s可以称为对象或者实例.
6、可以用父类指针指向子类的对象,但是最好不要用子类指针指向父类对象。
可以使用子类指针去访问,也可以用父类的指针访问,甚至不用转换类型
相当于编译器帮忙把父类的成员变量赋值到子类对象里面
但是Person* pt = &t
不能pt->a
或者pt->b
父类指针访问子类的成员变量
子类指针访问父类成员变量编译器是不允许的,但是底层来说只要把类型强转一下骗过编译器即可执行,
但一般不建议这么执行,因为父类只有两个变量的空间,但是使用子类指针时,我们指向了四个变量的空间,很容易访问越界,访问出奇怪的东西,后面调试很麻烦
3.多层继承
正常情况
父类与子类重名的情况(还有一种情况看多态笔记的两个例子)
可以看到大小照样还是24,只不过编译器在赋值的时候自己会乱,不知道你要赋值哪个,所以需要z.X::a去赋值
而且在反汇编中和上面还是一样的,在底层没有区别,重名的变量名字只是给程序员自己看的,底层不管你重不重名
4.多重继承
5.子类的构造函数自动调用父类构造函数
反汇编看看怎么回事
在创建对象时,sub自动调用了自己的构造函数(即使我们没写构造函数)
跟进sub构造函数后发现里面就调用了父类的构造函数
跟进Base的构造函数
作业![image.png](https://cdn.nlark.com/yuque/0/2024/png/28321768/1704703595978-4a67128f-6be3-427f-bd25-06f2b8f0dc50.png#averageHue=%23f6f4f2&clientId=u91ffb77e-9eb5-4&from=paste&height=286&id=ul3IE&originHeight=501&originWidth=1178&originalType=binary&ratio=1.75&rotation=0&showTitle=false&size=85773&status=done&style=none&taskId=u8fd696ea-7842-47a1-a5fc-93bccaf9b31&title=&width=673.1428571428571)
#pragma warning(disable:4996)
#include <stdio.h>
#include <windows.h>
struct DateInfo
{
int year;
int month;
int day;
DateInfo(int year , int month , int day)
{
}
DateInfo()
{
year = 2015;
month = 4;
day = 2;
}
void SetDay(int day)
{
this->day = day;
}
int GetDay()
{
return this->day;
}
void SetYear(int year)
{
this->year = year;
}
int GetYear()
{
return this->year;
}
void SetMonth(int month)
{
this->month = month;
}
int GetMonth()
{
return this->month;
}
};
struct TimeInfo:DateInfo
{
int Hour;
int Minute;
int Second;
void SetHour(int Hour)
{
this->Hour = Hour;
}
int GetHour()
{
return this->Hour;
}
void SetMinute(int Minute)
{
this->Minute = Minute;
}
int GetMinute()
{
return this->Minute;
}
void SetSecond(int Second)
{
this->Second = Second;
}
int GetSecond()
{
return this->Second;
}
};
struct MyString
{
PVOID addr;
char* str;
int size;
MyString()
{
str = (char*)malloc(1024);
printf("构造函数执行成功\n");
}
void mallocstr()
{
str = (char*)malloc(size);
}
~MyString()
{
free(str);
}
void SetString(char* str)
{
strcpy(this->str, str);
}
void PrintString()
{
printf("string:%s\nsize:%d", str,size);
}
void Size()
{
this->size = strlen(str);
}
};
int main(int argc, char* argv[])
{
/*TimeInfo time;
DateInfo* pd = & time;
printf("父类指针访问子类对象:%d\n", pd->day);
time.SetHour(6);
TimeInfo* pt = &time;
printf("子类指针访问子类对象:%d\n",pt->Hour);*/
MyString ms;
const char* string = "abcde";
ms.SetString((char*)string);
ms.Size();
ms.PrintString();
}