从C 到.NET 揭开多态的面纱

2008-02-23 05:25:01来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

 多态是面向对象理论中的重要概念之一,从而也成为现代程式设计语言的一个主要特性,从应用角度来说,多态是构建高灵活性低耦合度的现代应用程式架构所不可忽缺的能力。从概念的角度来说,多态使得程式员能够不必关心某个对象的具体类型,就能够使用这个对象的“某一部分”功能。这个“某一部分”功能能够用基类来呈现,也能够用接口来呈现。后者显得更为重要——接口是使程式具备可扩展性的重要特性,而接口的实现依赖于语言对多态的实现,或干脆就象征着语言对多态的实现。

  本文并不大算赘述多态的应用,因为其应用实在俯拾皆是,其概念理论也早已完善。这里,我们打算从实现的角度来看一看一门语言在其多态特性的背后做了些什么——知其所以然,使用时方能游刃有余。

  或许您在学习一门语言的时候,曾对多态的特性很迷惑,虽然教科书上所讲的很简单,也很明了——正如他的原本理念相同,但是您也想知道语言(编译器)在背后都干了些什么,为什么一个派生类对象就能够被当作其基类对象来使用?用指向派生类对象的基类指针调用虚函数时凭什么能够精确的到达正确的函数?类的内部是如何布局的?

  我们这样考虑:假设语言不支持多态,而我们又必须实现多态,我们能够怎么做?

  多态的雏形:

class B
{
 public:
  int flag; //为表示简洁,0代表基类,1代表派生类
  void f(){cout<<”in B::f()”;} //非虚函数
};

class D:public B
{
 public:
  void f(){cout<<”in D::f()”;} //非虚函数
};

void call_virtual(B* pb)
{
 if(pb->flag==0) //假如是基类,则直接调用f
  pb->f(); //调用的是基类的f
 else //假如是派生类,则强制转化为派生类指针再调用f
  (D*)pb->f(); //调用的是派生类的f
}

  这样,能够正好符合“根据具体的对象类型调用相应的函数”的理念。但是这个原始方案有一些缺点:;例如,分发“虚函数”的代码要自己书写,不够优雅,不具备可扩展性(当继承体系扩大时,这堆代码将变得臃肿无比),不具备封闭性(假如加入了一个新的派生类,则“虚函数”调用的代码必须作改变,然而假如恰巧这个调用是无法改变的(例如,库函数),则意味着,一个用户加入的派生类将无法兼容于那个库函数)等等。结果就是——这个方案不具备通用性。

  但是,这个方案能够说明一些本质性的问题:flag数据成员用于标识对象所属的具体类型,从而调用者能够根据他来确定到底调用哪个函数。但是,可不能够不必“知道”对象的具体类型就能够调用正确的函数呢?能够,改进的方案如下:

class B
{
 public:
  void (*f)(); //函数指针,派生类对象能够通过给他重新赋值来改变对象的行为
};

class D:public B
{};

void call_virtual(B* pb)
{
 (*(pb->f))(); //间接调用f所指的函数
}

void B_Mem()
{
 cout<<”I am B”;
}

void D_Mem()
{
 cout<<”I am D”;
}

int main()
{
 B b;
 b.f=&B_Mem; //B_Mem代表B的“虚函数”
 D d;
 d.f=&D_Mem; //以D_Mem来覆盖(override)B的虚函数
 call_virtual(&b); //输出“I am B”
 call_virtual(&d); //输出“I am D”
}


  在这个改进的例子中,派生类对象能够通过修改函数指针f的指向,从而获得特定的行为,这里重要的是,call_virtual函数不再需要通过丑陋的if-else语句来判断对象的具体类型,而只是简单的通过一个指针来调用“虚函数”——这时候,假如派生类需要改变具体的行为,则能够将相应的函数指针指向他自己的函数即可,这招“偷梁换柱”通过增加一个间接层的办法“神不知鬼不觉”地将“虚函数”替换(Override)掉了。



[1] [2] [3] 下一页

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: 深入研究 C 中的 STL Deque 容器

下一篇: 用C 访问SQL Server 2000的实例

热门词条
热门标签