也许是过于兴奋了,也许是对第二天的面试过于在意了,在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++
这个当然是没有什么问题的,我稍微解释了一下,就写出下面的代码
{
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的函数,就会为其搞一个虚函数表,也就是VTABLE。VTABLE实际上是一个函数指针的数组,每个虚函数占用这个数组的一个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()