首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

细谈C++多态性的“动”与“静”,该怎么解决

2012-03-23 
细谈C++多态性的“动”与“静”我们讨论多态的时候,先看看什么是硬编码和软编码:硬编码就是把代码写死了,导致

细谈C++多态性的“动”与“静”
我们讨论多态的时候,先看看什么是硬编码和软编码:硬编码就是把代码写死了,导致弹性不足,降低了可扩展性,例如在代码里的:
  if...else...;switch...case...         ...
  
  这些代码通常都属于硬编码,项目中的这些代码多了,就相当于说明这个代码的灵活性、扩展性、弹性等等的少了。

  所以,我们要尽量使用软编码,通俗点就是“别把话说死了,留点转弯的余地”。多态性就是这种软编码特性的反映,下面我们一起来研究一下多态性。

  多态性是一种抽象,把事物的特征抽象出来,然后事物的具体形态我们就不关心了。

  例如对工人这种事物来说,他的特征就是工作,至于是什么工人,他做什么工作,我们就不用关心了,只要我们以“工人.工作”这种方式去调用。那他就会为我们工作了。

  那为什么我们不抽象出其他的特征,只抽象出工作这个特征呢?因为我们只对这个特征感兴趣,他的什么吃饭、睡觉、如厕等的特性我们都不关心了。有了多态,我们就可以实现软编码了!

  讲解了多态的概念之后,我们来看看多态的实现(C++的实现):

  多态的实现是通过虚函数表(VTable),每个类如果有虚函数,那它就有一个虚函数表,所有的对象都共享这一个VTable。这个概念也叫做动态联编,还有静态联编,这些概念都是通过在程序执行的时候表现出来的性质来定的,我们下面会看看它的“动”和“静”究竟体现在哪里。

  先看一段代码:  

  class   C0
  ...{
  public:
  void   Test()
  ...{
  cout   < <   "call   C0   Test()。 "   < <   endl;
  }
  };  

  这个类没有虚函数,调用的时候就是静态调用。调用的代码如下:

  //   静态编译(早绑定   early   binding)
  C0   *pO0;
  C0   obj0;
  pO0   =   &obj0;
  pO0-> Test();
  它的反汇编代码如下:

  //   直接调用函数(已经知道地址)
  00401432   mov   ecx,dword   ptr   [ebp-0Ch]
  00401435   call   @ILT+160(C0::Test)   (004010a5)
  下面看看带虚函数的类:

  class   C1
  ...{
  public:
  virtual   void   Test()
  ...{
  cout   < <   "call   C1   Test() "   < <   endl;
  }
  };

  class   C11   :   public   C1
  ...{
  public:
  void   Test()
  ...{
  cout   < <   "call   C11   Test() "   < <   endl;
  }
  };  

  它的调用:

  C11   obj11;

  C1   *pObj1;
  pObj1   =   &obj11;
  //   这里生成的汇编代码
  //   0040144A   lea   edx,[ebp-14h]   //   寻址找到pObj1
  //   0040144D   mov   dword   ptr   [ebp-1Ch],edx

  pObj1-> Test();
  //   这里生成的汇编代码
  //   00401450   mov   eax,dword   ptr   [ebp-1Ch]   //   取得虚表地址
  //   00401453   mov   edx,dword   ptr   [eax]
  //   00401455   mov   esi,esp
  //   00401457   mov   ecx,dword   ptr   [ebp-1Ch]   //   根据虚表的位置来取得Test()函数
  //   0040145A   call   dword   ptr   [edx]   //   调用Test()函数  

  根据上述的汇编代码,我们可以知道,在多态调用函数的时候,程序执行以下步骤:

  1、寻址找到pObj1

  2、由于C11重载了Test虚函数,所以*pObj1指向的就是C11的VTable的地址

  3、调用pObj1-> Test()时,程序通过Vptr(虚表的指针,对象的首地址),找到VTable,再根据偏移调用Test函数。

  由于上述的多态调用过程是一个动态的过程(在运行时去“找”函数来调用),而不是编译完就直接把函数地址摆在那里了,所以被称作“动态联编”。

  上面把多态的“动”和“静”的特点结合代码说了一遍,希望能说清楚了。

  下面再验证一个类的虚表的问题,如果你对虚表已经很熟悉了,就不用再往下看了。

  在很多书上都已经说明了C++的对象模型,这里只是做个验证。看看这段代码:

  class   C1
  ...{
  public:
  virtual   void   Test()
  ...{
  cout   < <   "call   C1   Test() "   < <   endl;
  }
  };

  class   C11   :   public   C1
  ...{
  public:
  void   Test()
  ...{
  cout   < <   "call   C11   Test() "   < <   endl;
  }
  };

  class   C12   :   public   C1
  ...{
  public:
  void   Test()
  ...{
  cout   < <   "call   C12   Test() "   < <   endl;


  }
  };  

  我们可以知道   Test()   是虚函数,从C1派生的类必定有自己的虚表。而且根据别的资料,虚表指针是放在对象的首地址的,我们下面就来验证一下:

  //   验证首地址  
  C11   obj110;
  C11   obj111;

  printf( "obj110   的地址:%x   ",   &obj110);
  printf( "obj111   的地址:%x   ",   &obj111);
  printf( "obj110   虚表的地址:%x   ",   *(&obj110));
  printf( "obj111   虚表的地址:%x   ",   *(&obj111));  

  结果是:

  obj110   的地址:12ff7c
  obj111   的地址:12ff78
  obj110   虚表的地址:432098
  obj111   虚表的地址:432098

  由上面的结果我们可以验证:

  1、一个类一个VTABLE,而不是一个对象一个VTABLE。

  2、对象的首地址的内容就是VTABLE的地址。

  总结一下:

  C++的多态性包括其概念和实现,本文从编译器生成的代码来讨论C++多态特性,特别说明了为什么多态特性被称为“动态联编”,它和“静态联编”有什么不同,它们的“动”与“静”体现在哪里。另外还对对象的虚表做了些验证。好了,希望本文能对你认识C++的多态性有一定的帮助!  


转http://develop.csai.cn/c/200608091123521879.htm

[解决办法]
mark
[解决办法]
mark
[解决办法]
在C++中更应该用模板来代替这些!
[解决办法]
mark
[解决办法]
楼主应该没说完.

虚是动多态.根据实际情况来执行.
模板或是重载是静多态..运行时已经确定.

呵呵..我觉得这样才应该叫多态性的动与静



[解决办法]

其實主要是看 "抽象的行為 " 和 "實例的具體行為 "的綁定時間.

一個是編譯期, 一個是運行時( 生成類實例 時)

[解决办法]
支持原创
[解决办法]

[解决办法]
呀 和我昨天发得一个贴子一摸一样 我的是11点多AM发的 你的那 1点多PM。。。。。汗
[解决办法]
我觉得动态联边 静态联编 区别在于具体对象的虚拟表 当类事例化后 自动生成这个表 在编译时根据条用对象的不同调用不同的虚拟表函数 ;


患有一种说法 动态调用和静态调用 他们在程序执行时调用 或是已经在程序中调用
[解决办法]
2、对象的首地址的内容就是VTABLE的地址。
--------
-_-!
这个以偏概全鸟!
1. 不同编译器的实现非常的不同。你说的是当年c front的 "后遗症 "而已。
2. 不适用于莫有虚函数的对象
3. 不适用于多继承

BTW,虚函数也可以被“静态联编”。


最后,还是支持一下原创。


FT
--
Anything one man can imagine, other men can make real.
[解决办法]
写的不错,拿汇编来证明,厉害

热点排行