虚函数专题

来源:互联网 发布:剑灵萝莉捏脸数据图 编辑:程序博客网 时间:2024/06/09 19:27

分析一【举例】

分析二【C++中虚函数学习笔记

分析三【关于虚函数的一个小例子

========================================================

分析一

 

#include<iostream.h>
class A
{
public:
A();
~A();
virtual void foo() { cout << "A::Vfoo() is called" << endl;}
void function() {...}
};
class B: public A
{
public:
B();
~B();
virtual void foo() { cout << "B::Vfoo() is called" << endl;}
void function() { ...};
void main()
{
A * a = new B;
a->foo(); //B的被调用
a->function();//A的被调用
}
// a->foo(); 在这里,a虽然是指向A的指针,但是被调用的虚函数(foo)却是B的,为什么?这个是虚函数的

-----------------------------------------------------------------------------------------------------------------

首先,virtual 是有传递性的。在你的代码中的体现就是:在B的定义中去除foo这个函数前面的那个virtual的话,该函数还是被认为是虚拟函数的;
其次,每个类如果具有虚拟函数(这些虚拟函数有的是自己定义的,有的是通过继承得到的),那么,编译器在存储该类的定义时,会用一个指针(名叫vptr)指向一个数组X。该数组X中的元素的类型是函数指针类型。每一个元素指向一个虚拟函数。这个数组X的学名叫虚拟函数表。这张虚拟函数表的构建方法是:这张虚拟函数表包含基类的虚拟函数,也包含派生类的虚拟函数表。但是,如果派生类中有重写了基类的虚拟函数的话,那么虚拟函数表中对应的元素指向的是派生类的,而不是指向基类的。对于你的代码而言,本来X中存放的函数指针指向了A中的foo,但是因为B重写了这个虚拟函数,于是,编译器将这个指针重新定向,让它指向B中的foo;
然后,如果在程序中出现一个类对象(假设为a),a调用了它的一个成员函数,而该函数是虚拟函数。那么,编译器就会先判断a的实际类型,从而获得正确的vptr,然后在它的虚拟函数表中寻找到相对应的函数指针,最后通过这个函数指针调用正确的虚拟函数。对于你的代码,a的实际类型是B,所以a的虚拟函数表中的函数指针指向的函数是B中的foo;
还有,每个类只有一张虚拟函数表,所有的对象共用这张表。

----------------------------------------------------------------

我是这样理解的:

A * a = new B; // 一个基类类型的指针a,指到了子类对象B的头上。
a->function(); // 这个是为什么要引入虚函数和多态的原因,虽然表面上看起来像是调用B的function(),但由于a是A类的指针。所以无论它指到谁的头上,调用的仍然是A类的函数!假如换成“.”操作,比如:b=new B;b.function()的话,就可以调用类B的了。
a->foo(); // 这个就是虚函数和多态的作用了,解决了上面的问题,指到谁就掉谁。这样可以做到动态选择对哪个对象操作。注意:多态操作是通过对基类指针操作实现的。
------------------------------------------------------------------

编译器对于
a->foo();的调用形式如下

int * vptr = ( int * ) * ( int * )a;
int foo_offset = 0;
int * foo_addr = ( int * ) * ( vptr + foo_offset );

typedef void ( * PFunc )();
PFunc pFunc = ( PFunc ) foo_addr;

pFunc();

 

 

 

 

==============================================================

分析二  C++中虚函数学习笔记

 

因为最近学习C++的面向对象,所以了解了面向对象的三大特点: 封装、继承、多态性,学习多态性的时候,首先涉及的就是虚函数,我就把我学习虚函数的一些想法记录下来。
虚函数是为了实现某种功能而假设的函数,虚函数只能是类中的一个成员函数,不能是静态成员,使用关键字virtual用于在类中说明改函数是虚函数。 虚函数更是为了实现面向对象的多态性而产生的,使用虚函数和多态性能够简化代码长度,支持更简单的顺序,便于程序的调试,维护。
虚函数的定义方法:
class A
{
    public:
        virtual void fun();    //define virtual function
};
void A::fun() { ... }    //member function describe

上面定义了一个虚函数,然后在类体外进行了函数的具体描述。

在类的继承当中,当基类中声明了某个虚函数,即使在派生类中没有声明虚函数,那么在以后的继承结构中都是虚函数,当然如果有多重继承,在每个派生类中还是推荐对每个虚函数进行显式的声明。

为了说明虚函数在派生类中的应用,我写段代码作例子:

//test.cpp
//code by heiyeluren

#include "stdio"
class cbase
{
public:
 virtual void vfoo()
 {
  printf("vfoo from cbase/n");
 };
 void foo()
 {
  printf("foo from cbase/n");
 }
};

class cderivd : public cbase
{
public:
 virtual void vfoo()
 {
  printf("vfoo from cderivd/n");
 };
 void foo()
 {
  printf("foo from cderivd/n");
 };
};

int main(int argc, char* argv[])
{
 cbase* pbase = new cderivd();
 pbase->foo(); //非虚函数,根据指针类型决定调用哪个foo,本例指针类型为cbase,所以调用的是cbase::foo()
 pbase->vfoo(); //虚函数,调用的是派生类的vfoo
 delete pbase;

 cderivd* pd = new cderivd();
 pd->foo(); //非虚函数,本例指针类型为cderivd*,所以调用cderivd::foo();
 pd->vfoo();
 delete pd;

 cderivd d;
 d.foo();
 d.vfoo();
 ((cbase)d).foo();//将d强行切割为cbase,这时调用的无论是foo还是vfoo都将是base的
 ((cbase)d).vfoo();

 getchar();
 return 0;
}

程序在DevCPP下编译通过,输出:
foo from cbase
vfoo from cderivd
foo from cderivd
vfoo from cderivd
foo from cderivd
vfoo from cderivd
foo from cbase
vfoo from cbase

那么就能看出那个是虚函数,那个不是了吧。

说的不对的,请高手原谅,我也刚学C++,恩,能说的就这么多,下次说说多态。88~~~~

==========================================================

分析三

虚函数的提出其实和C++的多态有着莫大的关系,先看一个例子;

 

从结果可以看到:
This is A
This is B
This is A
This is A
将父类的print函数改为虚函数,也是将void print()改为virtual void print(),则结果就不同了:
This is A
This is B
This is A
This is B

原创粉丝点击