虚函数 多态

   也许是过于兴奋了,也许是对第二天面试过于在意了,在11号抵达上海以后,一个晚上都没有怎么睡着,美美洗了个澡就奔到了微软MSN上海研究中心,也就是繁华徐家汇里面美罗大厦,稍等片刻就被这个中心leader ,也就是我面试官wong带进了一个特别办公室-我想,也许就是专门为了面试而建一个小房间吧~

       开始是一些关于MSN和我个人信息方面brief talk,,基本上交流得不错,然后第一个问题就把我信心击毁了:tell me your understanding about threading model

       What model?I am sorry to beg your pardon.这就是我第一反应下

回答!虽然我其实知道他是问什么,但是我确不知道线程模型任何东西,概念和细节,只是知道这个东西。急了,没办法,最后只好招供,不知道。我这种“坦率”让wong很惊讶,当然我知道他更多惊讶来自于我无知!接下来一个COM apartment 问题又让我蒙了,我支支吾吾,最后也只有投降。这个时候面试官就指着我简历问我:

but you tell me you are familiar with COM in your resume,

as you know apartment is a vary basic concerpt in COM.

我一下子脸红了,因为我知道这个简历上某些东西,其实是包含了一定程度上夸张。和他解释了一半天我理解COM和我是怎么用COM,当然结果并不能是wong满意,也许是从来没有遇到象我这种开头应聘者吧,wong摊开我简历,直视我眼睛,what is your talent?毫无疑问,我会回答C++,于是这样一个问题开始了,

write a code to explain virtual function in C++

这个当然是没有什么问题,我稍微解释了一下,就写出下面代码

Class Base
{
Public:
    Base();
    Virtual 
void function()
    
{
        Cout
<<”Base::function”<<endl;
    }

Protected:
    Virtual 
~Base()
    
{
        Cout
<<Base destructed!<<endl;
    }

}
 ;
Class Derived:
public  Base
{
Public:
    Derived();
    Void function ()    
    
{
        
out<<”Derived::fuction”<<endl;
    }

Protected:
    
~Derived()
    
{
        Cout
<<”Derived destructed!<<endl;
    }

}
;
int  main()
{
    Base
* pBase=new Derived;     
    
    pBase
->function();
    
    
return 0;
}

// output:Derived::function

 

 

接着wong指着pBase->function();问我是怎么实现?我就解释这个关于RTTI一些知识,但是wong提醒我解释多态是怎么执行,我就和他讨论虚函数表方面东西,没想到他紧逼着问我背后到底是怎么知道这个pBase就是指向子类对象那个pBase,然后怎么知道去执行就是子类这个函数。虽然我一再和wong强调这个RTTI功能实现实在是有编译器帮我们实现,当时实际上我没有把多态这个问题解释清楚,因为很久没有怎么看C++了,所以无法精确回答出来,这再次让我备受打击,那个时候,我觉得非常绝望!

       回来整理一下这个问题答案,其实是很简单   

       编译器是如何针对虚函数产生可以再运行时刻确定被调用函数代码呢?也就是说,虚函数实际上是如何被编译器处理呢?Lippman在深度探索C++对象模型中不同章节讲到了几种方式,其中 “标准”方式就是所谓VTABLE”机制。编译器发现一个类中有被声明为virtual函数,就会为其搞一个虚函数表,也就是VTABLEVTABLE实际上是一个函数指针数组,每个虚函数占用这个数组一个slot。一个类只有一个VTABLE,不管它有多少个实例。派生类有自己VTABLE,但是派生类VTABLE基类VTABLE有相同函数排列顺序,同名虚函数被放在两个数组相同位置上。在创建类实例时候,编译器还会在每个实例内存布局中增加一个vptr字段,该字段指向本类VTABLE。通过这些手段,编译器在看到一个虚函数调用时候,就会将这个调用改写,针对上面例子:
void bar(Base* a)
{
    a-> function ();
}

会被改写为:
void bar(Base* a){
    (a->vptr[1])();
}
    
因为派生类和基类function ()函数具有相同VTABLE索引,而他们vptr又指向不同VTABLE,因此通过这样方法可以在运行时刻决定调用哪个function ()函数。

这里提供一个我整理出来有关虚函数和多态一些深入认识

///文件:C++函数深度剖析

///作者:MingGe

///日期:2006/1/22

1. 简介
    
虚函数是 C++ 中用于实现多态 (polymorphism) 机制。核心理念就是通过基类访问派生类定义函数。假设我们有下面类层次:

class A
{
public:
    virtual void foo() { cout << "A::foo() is called" << endl;}
};

class B: public A
{
public:
    virtual void foo() { cout << "B::foo() is called" << endl;}
};

那么,在使用时候,我们可以:
A * a = new B();
a->foo();       //
在这里, a 虽然是指向 A 指针,但是被调用函数 (foo) 却是 B !

    
这个例子是虚函数一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数调用并不是在编译时刻被确定,而是在运行时刻被确定。由于编写代码时候并不能确定被调用是基类函数还是哪个派生类函数,所以被成为“虚”函数。

    
虚函数只能借助于指针或者引用来达到多态效果 ,如果是下面这样代码,则虽然是虚函数,但它不是多态
class A
{
public:
    virtual void foo();
};

class B: public A
{
    virtual void foo();
};

void bar()
{
    A a;
    a.foo();   // A::foo()
被调用
}

1.1
多态
    
在了解了虚函数意思之后,再考虑什么是多态就很容易了。仍然针对上面类层次,但是使用方法变复杂了一些:
void bar(A * a)
{
    a->foo();  //
被调用 A::foo() 还是 B::foo()
}

因为 foo() 是个虚函数,所以在 bar 这个函数中,只根据这段代码,无从确定这里被调用 A::foo() 还是 B::foo() ,但是可以肯定说:如果 a 指向 A 实例,则 A::foo() 被调用,如果 a 指向 B 实例,则 B::foo() 被调用。

这种同一代码可以产生不同效果特点,被称为“多态”。

1.2
多态有什么用?
    
多态这么神奇,但是能用来做什么呢?这个命题我难以用一两句话概括,一般 C++ 教程(或者其它面向对象语言教程)都用一个画图例子来展示多态用途,我就不再重复这个例子了,如果你不知道这个例子,随便找本书应该都有介绍。我试图从一个抽象角度描述一下,回头再结合那个画图例子,也许你就更容易理解。

    
在面向对象编程中,首先会针对数据进行抽象(确定基类)和继承(确定派生类),构成类层次。这个类层次使用者在使用它们时候,如果仍然在需要基类时候写针对基类代码,在需要派生类时候写针对派生类代码,就等于类层次完全暴露在使用者面前。如果这个类层次有任何改变(增加了新类),都需要使用者“知道”(针对新类写代码)。这样就增加了类层次其使用者之间耦合,有人把这种情况列为程序中 bad smell ”之一。

    
多态可以使程序员脱离这种窘境。再回头看看 1.1 例子, bar() 作为 A-B 这个类层次使用者,它并不知道这个类层次中有多少个类,每个类都叫什么,但是一样可以很好工作,当有一个 C 类从 A 类派生出来后, bar()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值