31.2 类继承

可以说关于类继承就是我们已经研究了的这个结构体,但是它现在扩展成类了。 让我们看个简单的例子:

  1. #include <stdio.h>
  2. class object
  3. {
  4. public:
  5. int color;
  6. object() { };
  7. object (int color) { this->color=color; };
  8. void print_color() { printf ("color=%d
  9. ", color); };
  10. };
  11. class box : public object
  12. {
  13. private:
  14. int width, height, depth;
  15. public:
  16. box(int color, int width, int height, int depth)
  17. {
  18. this->color=color;
  19. this->width=width;
  20. this->height=height;
  21. this->depth=depth;
  22. };
  23. void dump()
  24. {
  25. printf ("this is box. color=%d, width=%d, height=%d, depth=%d
  26. ", color, width,
  27. height, depth);
  28. };
  29. };
  30. class sphere : public object
  31. {
  32. private:
  33. int radius;
  34. public:
  35. sphere(int color, int radius)
  36. {
  37. this->color=color;
  38. this->radius=radius;
  39. };
  40. void dump()
  41. {
  42. printf ("this is sphere. color=%d, radius=%d", color, radius);};
  43. };
  44. int main()
  45. {
  46. box b(1, 10, 20, 30);
  47. sphere s(2, 40);
  48. b.print_color();
  49. s.print_color();
  50. b.dump();
  51. s.dump();
  52. return 0;
  53. };

让我们观察一下生成的dump()的代码和object::print_color(),让我们看看结构体对象的内存输出(作为32位代码) 所以,dump()方法其实是对应了好几个类,下面代码由MSVC 2008生成(/Ox+/Ob0) 优化的MSVC 2008 /Ob0

  1. ??_C@_09GCEDOLPA@color?$DN?$CFd?6?$AA@ DB color=%d’, 0aH, 00H ; string
  2. ?print_color@object@@QAEXXZ PROC ; object::print_color, COMDAT
  3. ; _this$ = ecx
  4. mov eax, DWORD PTR [ecx]
  5. push eax
  6. ; color=%d’, 0aH, 00H
  7. push OFFSET ??_C@_09GCEDOLPA@color?$DN?$CFd?6?$AA@
  8. call _printf
  9. add esp, 8
  10. ret 0
  11. ?print_color@object@@QAEXXZ ENDP ; object::print_color

优化的MSVC2008 /Ob0

  1. ?dump@box@@QAEXXZ PROC ; box::dump, COMDAT
  2. ; _this$ = ecx
  3. mov eax, DWORD PTR [ecx+12]
  4. mov edx, DWORD PTR [ecx+8]
  5. push eax
  6. mov eax, DWORD PTR [ecx+4]
  7. mov ecx, DWORD PTR [ecx]
  8. push edx
  9. push eax
  10. push ecx
  11. ; this is box. color=%d, width=%d, height=%d, depth=%d’, 0aH, 00H ; string
  12. push OFFSET ??_C@_0DG@NCNGAADL@this?5is?5box?4?5color?$DN?$CFd?0?5width?$DN?$CFd?0@
  13. call _printf
  14. add esp, 20 ; 00000014H
  15. ret 0
  16. ?dump@box@@QAEXXZ ENDP ; box::dump
  17. ?dump@sphere@@QAEXXZ PROC ; sphere::dump, COMDAT
  18. ; _this$ = ecx
  19. mov eax, DWORD PTR [ecx+4]
  20. mov ecx, DWORD PTR [ecx]
  21. push eax
  22. push ecx
  23. ; this is sphere. color=%d, radius=%d’, 0aH, 00H
  24. push OFFSET ??_C@_0CF@EFEDJLDC@this?5is?5sphere?4?5color?$DN?$CFd?0?5radius@
  25. call _printf
  26. add esp, 12 ; 0000000cH
  27. ret 0
  28. ?dump@sphere@@QAEXXZ ENDP ; sphere::dump

所以,这就是他的内存暑促后:(基类对象)

31.2 类继承 - 图1

继承的对象 Box:

31.2 类继承 - 图2

Sphere:

31.2 类继承 - 图3

让我们看看main()函数体:

  1. PUBLIC _main
  2. _TEXT SEGMENT
  3. _s$ = -24 ; size = 8
  4. _b$ = -16 ; size = 16
  5. _main PROC
  6. sub esp, 24 ; 00000018H
  7. push 30 ; 0000001eH
  8. push 20 ; 00000014H
  9. push 10 ; 0000000aH
  10. push 1
  11. lea ecx, DWORD PTR _b$[esp+40]
  12. call ??0box@@QAE@HHHH@Z ; box::box
  13. push 40 ; 00000028H
  14. push 2
  15. lea ecx, DWORD PTR _s$[esp+32]
  16. call ??0sphere@@QAE@HH@Z ; sphere::sphere
  17. lea ecx, DWORD PTR _b$[esp+24]
  18. call ?print_color@object@@QAEXXZ ; object::print_color
  19. lea ecx, DWORD PTR _s$[esp+24]
  20. call ?print_color@object@@QAEXXZ ; object::print_color
  21. lea ecx, DWORD PTR _b$[esp+24]
  22. call ?dump@box@@QAEXXZ ; box::dump
  23. lea ecx, DWORD PTR _s$[esp+24]
  24. call ?dump@sphere@@QAEXXZ ; sphere::dump
  25. xor eax, eax
  26. add esp, 24 ; 00000018H
  27. ret 0
  28. _main ENDP

继承的类必须永远将它们的范围添加到基类的范围中,所以这样可以让基类的方法对其范围生效。 当object::print_color()方法被调用时,会有一个指针指向box对象和sphere对象会被传递进去,它就是“this”。它可以和这些对象简单的互动,因为color域指向的永远是固定的地址(+0x00偏移)。 可以说,object::print_color()方法对于输入对象类型来说是不可知的,如果你创建一个继承类,例如继承了box类编译器会自动在depth域之后加上新域,而把box的类域固定在一个固定的位置。 因此,box::dump()方法会在访问color/width/height/depths的时候顺利工作,因为地址的固定,它会很容易的知道偏移。 GCC生成的代码基本一样,只有一个不一样的就是this的传递,就像之前说的一样,它是作为第一个参数传递的,而不是通过ECX传递的。