当使用win32api的时候
我们都会发现一个句柄的东西被多次传入api中
MessageBox (句柄, 显示内容,标题,MB_OK) ;
句柄最终指向的是一个结构体,很多api同时为一个结构体进行操作
可能这就是面向过程的语言进行面向对象编程吧
先定义一个结构体,进行初始化,调用一个函数并传入该结构体的地址
struct animal
{
int type;
int height;
int weight;
char* name;
};
void attack(animal* a)
{
printf("%s,ao ao ao !!!\n",a->name);
}
对同一个结构体的api调用多了我们就会发现,api太多了,以至于我们并不知道哪些api是用来操作这个结构体的,对结构体和函数进行封装的欲望迫切增大
我们可以把函数放在结构体里,创造一个新的类型,如果可以这样操作的话
struct animal
{
int type;
int height;
int weight;
char* name;
void attack(animal*a)
{
printf("%s,ao ao ao !!!\n",a->name);
}
};
这样就非常有意思了
我们创建一个结构体变量,并对该变量进行操作
int main()
{
animal tiger;
tiger.name = (char*)"老虎";
attack(&tiger);
return 0;
}
编译器1提示找不到attack标识符
所以把函数放在结构体内部的代价是貌似函数的作用域从全局下降成为局部(虽然它本质还是全局)
tiger.attack(&tiger); //这样就没有问题了
看起来挺别扭的,为了封装(别人定义的一个专有名词),以限制自由为代价让程序更加简单且安全,类似与未成年人保护法什么的
tiger.attack()是tiger结构体变量的,这个函数居然能传递同一个结构体的其他变量?
int main()
{
animal tiger;
tiger.name = (char*)"老虎";
animal cat;
cat.name = (char*)"猫";
tiger.attack(&cat);
return 0;
}
程序运行结果是
猫,ao ao ao !!!
这也太过分了,一个变量的函数形参居然可以传入了比人家的变量,不利于一夫一妻制传统的发扬(虽然说本质一个函数在内存中只有一份),编译器说,你不用自己手动传入结构体指针了,我来帮你传,里面任何用到指针的全部都可以不用写了,我编译的时候帮你添上
struct animal
{
int type;
int height;
int weight;
char* name;
void attack()
{
printf("%s,ao ao ao !!!\n", name);
}
};
编译器帮我们传参的证据
tiger.attack();
0076194A lea ecx,[ebp-14h]
0076194D call 007612E9
ebp-14h是结构体的首地址,它把值放到ecx中以供函数内部使用,而且在函数内部,它会把ecx传入的值放到栈中作为局部变量保存(this指针),看来编译器并没有骗我们,但是编译器禁止我们对this进行自加或者自减操作,这一点看来c++可比c严格多了
struct animal
{
int type;
int height;
int weight;
char* name;
void attack()
{
printf("%s,ao ao ao !!!\n", this->name);
}
};
如果我们不适用this编译器也会自动帮我们加上的,不过在有一些场合,编译器也不知道我们是不是要使用this,所以这时候我们就要手动添加了
有时候我们不想麻烦编译器,我们想自己做
struct animal
{
int type;
int height;
int weight;
char* name;
void attack(animal *a)
{
printf("%s,ao ao ao !!!\n", this->name);
printf("%s,ao ao ao !!!\n", a->name);
}
};
然而编译器还是不怕辛苦的帮我们传入把地址传入到acx寄存器里面,而且它会把我们自己传入的地址也压栈,也就是地址被传入了两份,是不是感觉有点多此一举,我们传入的地址被push进去了
证据
tiger.attack(&tiger);
0009194A lea eax,[ebp-14h]
0009194D push eax //我们传入的
0009194E lea ecx,[ebp-14h] //编译器传入的
00091951 call 000912EE
这两行的代码是第一行编译器会用它传入的ecx,而第二行编译器会用我们push进去的地址
printf("%s,ao ao ao !!!\n", this->name);
printf("%s,ao ao ao !!!\n", a->name);