C++中虚函数的实现背后

上一篇 / 下一篇  2012-09-07 13:22:41 / 个人分类:C++

51Testing软件测试网4p sE9|._FE

  摘要:虚函数是很多编程语言中一个特性,比如c#,java,当然在c++语言中也有。这三种语言都是面向对象的语言。我们都知道面向对象语言有三个最基本的特征就是:继承,多态,封装。在c++语言中,这种多态的特征就是通过虚函 ...51Testing软件测试网 oOI8\Qh/O

6pA'p`C.t&MY:T(i0   虚函数是很多编程语言中一个特性,比如c#,java,当然在c++语言中也有。这三种语言都是面向对象的语言。我们都知道面向对象语言有三个最基本的 特征就是:继承,多态,封装。在c++语言中,这种多态的特征就是通过虚函数(virtual)来实现的。这种实现方法在其它语言中(比如c#)中也是如 此。

{+I:to*f0L0

8_F/}^,wC"go0  我要说的就是虚函数到底是怎么实现的。还是以例子来说明。我引用了上一篇随笔中的代码。

(D _V~I2Hd0
51Testing软件测试网)~b6w JO

class ClassA51Testing软件测试网 ^ `X X xT
{51Testing软件测试网T7D`,d0R
public:
/w~UkR k0    void fun1();51Testing软件测试网\qai$[!t@5` ?Vx;B
    void fun2();51Testing软件测试网-Nk7x],?M3^ S!N
    virtual void fun3();
"z\ BC6T,O Y$Q0};
9l@f9E,b"X]0void ClassA::fun1()
EL7vk7qOYu0{
8\qn8}F P7mm0    cout << "ClassA.fun1"<<endl;
)dVQ$m7d0};51Testing软件测试网 E1IFXI:I
void ClassA::fun2()51Testing软件测试网E]^ l:kTxi:H
{51Testing软件测试网3o#g9\PDu\d
    cout << "ClassA.fun2"<<endl;
/b bgC7u8i+?8[0};51Testing软件测试网i YI8?Z!`;C"Z
void ClassA::fun3()
0w:W W%K#HQB0{
Ar(Xi.f"Tc0    cout << "ClassA.fun3"<<endl;
D$eK2d:w_0};
L0Z4q(`0g0class ClassB : public ClassA51Testing软件测试网J+lY@![6e#@)^1G
{
%b,Q*\ @R3`@0public:
Z#SO e I$_,c`0    void fun1();51Testing软件测试网z1FO'??2D.Ds%c
    void fun2();51Testing软件测试网X6xZ|xE4}
    virtual void fun3();
:? ors A O8Q0};
p8UXmA0void ClassB::fun3()51Testing软件测试网6S1h;fq;Z`E]U!I
{
*zJ6k:b!A6T0    cout << "ClassB.fun3"<<endl;
)i[!I-e Y2^0};51Testing软件测试网4|)nc9z)H~
class ClassC : public ClassB51Testing软件测试网B(OP6F*^#T
{
D_.K? S[myuO0public:
Q@Z1@?0    void fun1();
`%a.^PU0    void fun2();
,g^4R?{ x,o/Z c0    virtual void fun3();51Testing软件测试网;n:bxN m-M7R} i
};51Testing软件测试网BCjy^+CVy
void ClassC::fun3()
.@jHM1}bMe0{51Testing软件测试网1U$_] h]w.p
    cout << "ClassC.fun3"<<endl;
gc,i WsT+fQ0};51Testing软件测试网 CP_P1FvF-r0[~
void main()51Testing软件测试网U\#A-C N
{51Testing软件测试网I-m}!MH5Q+_D5l
    ClassA *a[3];
i8Tk4B~Ab T0?\:M+[0    ClassA a1;51Testing软件测试网F;c5JE&S9cJ
    ClassB b1;
m2DEq%a_Vz*`i0    ClassC c1;

giZ@(O0

{gD ~V[ q p"J0    a1.fun3();
[9J5xxk&Ay;U0    b1.fun3();
hV kV w_3jRNi0    c1.fun3();
51Testing软件测试网8]h+|h'@

+@Rd1cT,Nt mc0    a[0] = &a1;51Testing软件测试网!tR%b9h/^
    a[1] = &b1;51Testing软件测试网}'yze8]2@ @
    a[2] = &c1;51Testing软件测试网#v.d yY2f;Y$N
    cout << "virtual function arraytest" <<endl;51Testing软件测试网`-[)UzUc_!X!K O*z
    for(int i=0;i<3;i++)51Testing软件测试网4XMppw$U1r
    {51Testing软件测试网G,[ `8un
        a[i]->fun3();
&g'B6NW"iVU:D0    }
51Testing软件测试网jZLtY O1R Uf

51Testing软件测试网BXr\:x b;leIZ

    cout << "((ClassA)&b1).fun3():";
(Bc[9Bgv`I#V0    ((ClassA*)&b1)->fun3();51Testing软件测试网t|Z\r~F7H
    //object slicing
G(mig/]4D0    cout << "object slicing"<<endl;
5CGK O]0    cout <<"((ClassA)b1).fun3():";
\+@.\6duZ*aOs-J0    ((ClassA)b1).fun3();
"\DQXV$UU0}
51Testing软件测试网i\ j`?Z _7r N

51Testing软件测试网5y9B1`]1F

  类继承结构图如下:51Testing软件测试网:G)e1I%N,eA

51Testing软件测试网9wQI9m!L)f ^}(F] v

  其中fun3是虚拟函数,对ClassB,ClassC子类中分别进行了重写。51Testing软件测试网+alk1X@r

51Testing软件测试网h*y3F0IN6s-G']

  下面我解释一下虚函数的背后是怎么实现的:

&y4e2F~)q#@051Testing软件测试网z!CfH A]

  我们都知道,虚函数可以做到动态绑定,为了实现动态绑定,编译器是通过一个表格(虚拟函数表),在运行时间接的调用实际上绑定的函数来达到动态 绑定,其中这个我刚所说的表格其实现就是一个“虚拟函数表”。这张表对我们程序来说是透明的。是编译器为我们的代码自动加上去的(更准确的讲,并不是为所 有的代码都添加一张虚拟函数表,而是只针对那些包括虚函数的代码才加上这张表的)。

3JN,N8m QH051Testing软件测试网0IYmd S~6i

  既然有了这么一张虚拟函数表,自然而然我们就会想到,这个虚拟函数表里到底是存放一些什么东西呢?很简单,即然叫做虚拟函数表,当然是存放虚拟 函数了,呵呵,在c++中,该表每一行的元素应该就是我们代码中虚拟函数地址了,也就是一个指针。有了这个地址,我们可以调用实际代码中的虚拟函数了。

vy%W-rdm\@b051Testing软件测试网u!UK?5^

  编译器既然为我们的代码加了一张虚拟函数表,那这张虚拟函数表怎么与我们的代码关联起来呢? 要实现动态绑定,我们应该利用这张虚拟函数表来调用虚拟函数,为了达到目的,编译器又会为我们的代码增加一个成员变量,这个成员变量就是一个指向该虚拟函 数表的指针,该成员变量通常被命名为:vptr。51Testing软件测试网SLz8~eA6z

aN]X'S4d1] T-~0  说到了这里,上面代码中的ClassA中的在内存中应该如下图所示:

dP\zz4Et%[0

51Testing软件测试网"duJyivRY)m

  每一个ClassA的实例,都会有一个虚拟函数表vptr,当我们在代码中通过这个实例来调用虚拟函数时,都是通过 vptr先找到虚拟函数表,接着在虚拟函数表中再找出指向的某个真正的虚拟函数地址。虚拟函数表中的内容就是类中按顺序声明的虚拟函数组织起来的。在派生 的时候,子类都会继承父类的虚拟函数表vptr,我们只在把这个vptr成员在继承体系中一般看待就成了。51Testing软件测试网/lm R;D*R A

  有一点要说明一下,当子类在改写了父类中的虚拟函数时,同时子类的vptr成员也会作修改,此时,子类的vptr成 员指向的虚拟函数表中的存放的虚拟函数指针不再是父类的虚拟函数地址了,而是子类所改写父类的虚拟函数地址。理解这一点就很容易想到了:原来多态体现在这 里!

g"X$_Fc0

  有了上面的说明,接下来ClassB,ClassC类的内存占据空间应该如下图所示:

6}:xDs)Bvl!p3R+E0

| E$ot N!]:pA0

  同理,ClassC也一样。(这些图画得真是丑啊!)

)T K8EEw*F0

  于是一个指向ClassA的对象的实例,调用 fun3就是ClassA::fun3(),一个指向ClassB的对象的实例,调用 fun3就是ClassB::fun3(),一个指向ClassC的对象的实例,调用 fun3就是ClassC::fun3(),这些调用通过都是通过虚拟函数表来进行的。51Testing软件测试网k/~ z E ~:k[0W

  最后,上面代码中main函数的示例的执行结果也就是恍然大悟了,答案就在上一篇随笔的回复里面。已经有人帮我回复了,就此谢过了! 欢迎大家一起探讨!51Testing软件测试网 @ w3h"dtI,mF


TAG:

 

评分:0

我来说两句

Open Toolbar