PS:阅读文章之前应当有一定的汇编、C和C++的基础。
面向对象的程序设计有三大特性:封装、继承和多肽。这里只讨论后两种特性继承和多肽,因为C++中封装性实际上是由编译器去识别关键字public、private和protected来实现的。封装性的目的也是为了增加代码健壮性,减少写代码时出错的概率。也就是说只要能编译通过,在C++的类中无论是public、protected还是private,最终生成的汇编代码其实是一致的。
而继承和多肽是C++对C语言的补充,使得代码写起来更加简单。
先来看继承。
C++中的继承有两个方面的意思,第一子类拥有父类所有的成员变量,第二子类也拥有父类的所有成员函数,所以很多C++教材会说一句话,子类拥有父类的所有特性。
如果一个类中没有成员函数,例如:
#include
class Point{
public:
int x;
int y;
};
class PointEx : public Point{
public:
int z;
};
int main(){
Point * pPoint = new Point;
PointEx * pPointEx = new PointEx;
pPoint->x = 1;
pPoint->y = 2;
pPointEx->x = 3;
pPointEx->y = 4;
pPointEx->z = 5;
delete pPoint;
delete pPointEx;
return 0;
}
反汇编代码如下:
Point * pPoint = new Point;
00AF34FE 6A 08 push 8
00AF3500 E8 72 DC FF FF call operator new (0AF1177h)
00AF3505 83 C4 04 add esp,4
00AF3508 89 85 FC FE FF FF mov dword ptr [ebp-104h],eax
00AF350E 8B 85 FC FE FF FF mov eax,dword ptr [ebp-104h]
00AF3514 89 45 F8 mov dword ptr [pPoint],eax
PointEx * pPointEx = new PointEx;
00AF3517 6A 0C push 0Ch
00AF3519 E8 59 DC FF FF call operator new (0AF1177h)
00AF351E 83 C4 04 add esp,4
00AF3521 89 85 08 FF FF FF mov dword ptr [ebp-0F8h],eax
00AF3527 8B 85 08 FF FF FF mov eax,dword ptr [ebp-0F8h]
00AF352D 89 45 EC mov dword ptr [pPointEx],eax
pPoint->x = 1;
00AF3530 8B 45 F8 mov eax,dword ptr [pPoint]
00AF3533 C7 00 01 00 00 00 mov dword ptr [eax],1
pPoint->y = 2;
00AF3539 8B 45 F8 mov eax,dword ptr [pPoint]
00AF353C C7 40 04 02 00 00 00 mov dword ptr [eax+4],2
pPointEx->x = 3;
00AF3543 8B 45 EC mov eax,dword ptr [pPointEx]
00AF3546 C7 00 03 00 00 00 mov dword ptr [eax],3
pPointEx->y = 4;
00AF354C 8B 45 EC mov eax,dword ptr [pPointEx]
00AF354F C7 40 04 04 00 00 00 mov dword ptr [eax+4],4
pPointEx->z = 5;
00AF3556 8B 45 EC mov eax,dword ptr [pPointEx]
00AF3559 C7 40 08 05 00 00 00 mov dword ptr [eax+8],5
在main函数中,new出两个类的实例各一个,然后通过指针逐一给他们赋值。
我们也可以用纯C语言实现一样的结果:
#include
typedef struct Point{
int x;
int y;
}Point_t;
typedef struct PointEx{
Point_t point;
int z;
}PointEx_t;
int main(){
Point * pPoint = new Point;
PointEx * pPointEx = new PointEx;
pPoint->x = 1;
pPoint->y = 2;
pPointEx->point.x = 3;
pPointEx->point.y = 4;
pPointEx->z = 5;
delete pPoint;
delete pPointEx;
return 0;
}
反汇编:
Point * pPoint = new Point;
012734FE 6A 08 push 8
01273500 E8 72 DC FF FF call operator new (1271177h)
01273505 83 C4 04 add esp,4
01273508 89 85 FC FE FF FF mov dword ptr [ebp-104h],eax
0127350E 8B 85 FC FE FF FF mov eax,dword ptr [ebp-104h]
01273514 89 45 F8 mov dword ptr [pPoint],eax
PointEx * pPointEx = new PointEx;
01273517 6A 0C push 0Ch
01273519 E8 59 DC FF FF call operator new (1271177h)
0127351E 83 C4 04 add esp,4
01273521 89 85 08 FF FF FF mov dword ptr [ebp-0F8h],eax
01273527 8B 85 08 FF FF FF mov eax,dword ptr [ebp-0F8h]
0127352D 89 45 EC mov dword ptr [pPointEx],eax
pPoint->x = 1;
01273530 8B 45 F8 mov eax,dword ptr [pPoint]
01273533 C7 00 01 00 00 00 mov dword ptr [eax],1
pPoint->y = 2;
01273539 8B 45 F8 mov eax,dword ptr [pPoint]
0127353C C7 40 04 02 00 00 00 mov dword ptr [eax+4],2
pPointEx->point.x = 3;
01273543 8B 45 EC mov eax,dword ptr [pPointEx]
01273546 C7 00 03 00 00 00 mov dword ptr [eax],3
pPointEx->point.y = 4;
0127354C 8B 45 EC mov eax,dword ptr [pPointEx]
0127354F C7 40 04 04 00 00 00 mov dword ptr [eax+4],4
pPointEx->z = 5;
01273556 8B 45 EC mov eax,dword ptr [pPointEx]
01273559 C7 40 08 05 00 00 00 mov dword ptr [eax+8],5
这也就是说C++中类的成员变量的继承其实就相当于是C语言的结构体嵌套,而在写代码的时候可以省略其中父类的嵌套,就像上面的C代码中pPointEx->point.x在C++中可以省略父类的实例引用直接写成pPointEx->x。
class Point{
public:
int x;
int y;
};
int main(){
int size = sizeof(Point);
return 0;
}
size 8 int
class Point{
public:
int x;
int y;
public:
void set(int _x, int _y){
this->x = _x;
this->y = _y;
}
};
int main(){
int size = sizeof(Point);
return 0;
}
size 8 int
int main(){
Point * pPoint = new Point;
pPoint->set(1, 2);
delete pPoint;
return 0;
}
pPoint->set(1, 2);
00CA1AD7 6A 02 push 2
00CA1AD9 6A 01 push 1
00CA1ADB 8B 4D F8 mov ecx,dword ptr [pPoint]
00CA1ADE E8 02 F7 FF FF call Point::set (0CA11E5h)
当调用函数的时候,形参从右到左依次入栈,然后将pPoint的this指针传递给ecx,最后跳转到set执行。
class Point{
public:
int x;
int y;
public:
void __cdecl set(int _x, int _y){
this->x = _x;
this->y = _y;
}
};
int main(){
Point * pPoint = new Point;
pPoint->set(1, 2);
delete pPoint;
return 0;
}
pPoint->set(1, 2);
00991AD7 6A 02 push 2
00991AD9 6A 01 push 1
00991ADB 8B 45 F8 mov eax,dword ptr [pPoint]
00991ADE 50 push eax
00991ADF E8 06 F7 FF FF call Point::set (9911EAh)
00991AE4 83 C4 0C add esp,0Ch
然后是用于实现这种功能C代码:
typedef struct Point{
int x;
int y;
}Point_t;
void __cdecl set(Point_t * _p, int _x, int _y){
_p->x = _x;
_p->y = _y;
}
int main(){
Point_t * pPoint = new Point;
set(pPoint, 1, 2);
delete pPoint;
return 0;
}
对应的反汇编:
set(pPoint, 1, 2);
00B61417 6A 02 push 2
00B61419 6A 01 push 1
00B6141B 8B 45 F8 mov eax,dword ptr [pPoint]
00B6141E 50 push eax
00B6141F E8 B2 FD FF FF call set (0B611D6h)
00B61424 83 C4 0C add esp,0Ch
typedef struct Point{
int x;
int y;
}Point_t;
void __cdecl set(Point_t * _p, int _x, int _y){
_p->x = _x;
_p->y = _y;
}
typedef struct PointEx{
Point point;
int z;
}PointEx_t;
int main(){
PointEx_t * pPointEx = new PointEx_t;
set((Point_t *)pPointEx, 1, 2);
delete pPointEx;
return 0;
}
- pPointEx 0x002d1d38 {point={...} z=-842150451 } PointEx *
- point {x=1 y=2 } Point
x 1 int
y 2 int
z -842150451 int
typedef struct PointEx{
int z;
Point point;
}PointEx_t;
这段代码中先声明的是子类的新成员z然后再声明的父类的一个实例point。
- pPointEx 0x00741d38 {z=1 point={...} } PointEx *
z 1 int
- point {x=2 y=-842150451 } Point
x 2 int
y -842150451 int
父可以看出原因在于子类声明的时候,必须首先声明父类的一个实例,否则变量对应的关系会发生错乱。