深入分析 虚函数 纯虚函数 的作用及区别

来源:互联网 发布:企政oa软件 编辑:程序博客网 时间:2024/05/19 18:48
在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念。因为它充分体现 了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广。比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说, 它们都是虚函数。难怪有人甚至称虚函数是C++语言的精髓。 
那么,什么是虚函数呢,我们先来看看微软的解释: 
虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。                                                          这个定义说得不是很明白。MSDN中还给出了一个例子,但是它的例子也并不能很好的说明问题。我们自己编写这样一个例子: 
复制代码代码如下:

#include "stdio.h" 
#include "conio.h" 
class Parent
{     
public: 
    char data[20];     
    void Function1(); 
    virtual void Function2(); // 这里声明Function2是虚函数     
}parent; 
void Parent::Function1() 

    printf("This is parent,function1\n"); 

void Parent::Function2() 

    printf("This is parent,function2\n"); 

class Child: public Parent 

    void Function1(); 
    void Function2(); 

} child; 
void Child::Function1() 

    printf("This is child,function1\n"); 

void Child::Function2() 

    printf("This is child,function2\n"); 

int main(int argc, char* argv[]) 
{     
    Parent *p; // 定义一个基类指针     

    if ( _getch()=='c' ) // 如果输入一个小写字母c 
        p=&child; // 指向继承类对象 
    else 
        p=&parent; // 否则指向基类对象 

    p->Function1(); // 这里在编译时会直接给出Parent::Function1()的 入口地址。    
    p->Function2(); // 注意这里,执行的是哪一个Function2? 

    return 0; 
}

用任意版本的Visual C++或Borland C++编译并运行,输入一个小写字母c,得到下面的结果:
This is parent,function1 
This is child,function2 
为什么会有第一行的结果呢?因为我们是用一个Parent类的指针调用函数Fuction1(),虽然实际上这个指针指向的是Child类的对象,但编译器 无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent类的函数来理解并编译,所以我们看到了 第一行的结果。
  
那么第二行的结果又是怎么回事呢?我们注意到,Function2()函数在基类中被virtual关键字修饰,也就是 说,它是一个虚函数。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。如果我们在运行上面的程序时任意输入 一个非c的字符,结果如下: 
This is parent,function1 
This is parent,function2 
  
请注意看第二行,它的结果出现了变化。程序中仅仅调用了一个Function2()函数,却可以根据用户的输入自动决定到底调用基类中的Function2 还是继承类中的Function2,这就是虚函数的作用。我们知道,在MFC中,很多类都是需要你继承的,它们的成员函数很多都要重载,比如编写MFC应 用程序最常用的CView::OnDraw(CDC*)函数,就必须重载使用。把它定义为虚函数(实际上,在MFC中OnDraw不仅是虚函数,还是纯虚 函数),可以保证时刻调用的是用户自己编写的OnDraw。虚函数的重要用途在这里可见一斑。 
----------------------------------------------------------- 
再看下面的 
派生类的大小问题C++中虚函数和纯虚函数的概念,差别和分别存在的原因 
首先:强调一个概念 
定义一个函数为虚函数,不代表函数为不被实现的函数,定义它为虚函数是为了允许用基类的指针来调用子类的这个函数 
定义一个函数为纯虚函数,才代表函数没有被实现,定义他是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。 
对继承的影响: 
普通的类(没有虚函数,纯虚函数)就可以被继承,而且工作的相当好 
关于这个问题有以下疑问: 
纯虚函数难道就是为了实现接口?接口存在的意义? 
我实在弄不懂,我干嘛要预先定义好?未来的事情本难料,就等有一天我的类中需要使用某个函数,在添加一个函数不就可以? 
关于实例化一个类: 
有纯虚函数的类是不可能生成类对象的,如果没有纯虚函数则可以。比如: 
复制代码代码如下:

class CA 

public: 
    virtual void fun() = 0; // 说明fun函数为纯虚函数 
    virtual void fun1(); 
}; 
class CB 

public: 
    virtual void fun(); 
    virtual void fun1(); 
}; 
// CA,CB类的实现 
... 
void main() 

    CA a; // 不允许,因为类CA中有纯虚函数 
    CB b; // 可以,因为类CB中没有纯虚函数

    ... 
}

--------------------------------------------------------------- 
虚函数在多态中间的使用: 
多态一般就是通过指向基类的指针来实现的。 
dog mydogwangwang; 
mydogwangwang.born(); 
一定是返回“dog” 
那么 
horse myhorsepipi; 
myhorsepipi.born(); 
一定是返回“horse” 
也是多态呀? 
///////////////////////////////////////////////// 
有一点你必须明白,就是用父类的指针在运行时刻来调用子类: 
例如,有个函数是这样的: 
复制代码代码如下:

void animal::fun1(animal *maybedog_maybehorse) 

     maybedog_maybehorse->born(); 


参数maybedog_maybehorse在编译时刻并不知道传进来的是dog类还是horse类,所以就把它设定为animal类,具体到运行时决定了才决定用那个函数。 
也就是说用父类指针通过虚函数来决定运行时刻到底是谁而指向谁的函数。 
复制代码代码如下:

//用虚函数 
#include <iostream.h> 
class animal 

public: 
    animal(); 
    ~animal(); 
    void fun1(animal *maybedog_maybehorse); 
    virtual void born(); 
}; 
void animal::fun1(animal *maybedog_maybehorse) 

    maybedog_maybehorse->born(); 

animal::animal() 


animal::~animal() 


void animal::born() 

    cout<< "animal"; 

class dog: public animal 

public: 
    dog(); 
    ~dog(); 
    virtual void born(); 
}; 
dog::dog() 


dog::~dog() 


void dog::born()

    cout<<"dog"; 

class horse:public animal 

public: 
    horse(); 
    ~horse(); 
    virtual void born(); 
}; 
horse::horse() 


horse::~horse() 


void horse::born()

    cout<<"horse"; 

void main() 

    animal a; 
    dog b; 
    horse c;     
    a.fun1(&c); 

//output: horse 
//不用虚函数 
#include <iostream.h> 
class animal 

public: 
    animal(); 
    ~animal(); 
    void fun1(animal *maybedog_maybehorse); 
    void born();     
}; 
void animal::fun1(animal *maybedog_maybehorse) 

    maybedog_maybehorse->born(); 

animal::animal() 


animal::~animal() 


void animal::born() 

    cout<< "animal"; 

class dog: public animal 

public: 
    dog(); 
    ~dog(); 
    void born(); 
}; 
dog::dog() 


dog::~dog() 


void dog::born()

    cout<<"dog"; 

class horse:public animal 

public: 
    horse(); 
    ~horse(); 
    void born(); 
}; 
horse::horse() 


horse::~horse() 


void horse::born()

    cout<<"horse"; 

void main() 

    animal a; 
    dog b; 
    horse c;     
    a.fun1(&c); 

//output: animal

--------------------------------------------------------------- 
有纯虚函数的类是抽象类,不能生成对象,只能派生。他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。 
--------------------------------------------------------------- 
定义纯虚函数就是为了让基类不可实例化化, 
因为实例化这样的抽象数据结构本身并没有意义. 
或者给出实现也没有意义 
实际上我个人认为纯虚函数的引入,是出于两个目的:
1.为了安全.因为避免任何需要明确但是因为不小心而导致的未知的结果. 提醒子类去做应做的实现. 

2.为了效率,不是程序执行的效率,而是为了编码的效率.

·························································································································································································

C++虚函数与纯虚函数用法与区别(转)

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)

  

2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。

 

3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。

 

4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。

 

5. 虚函数的定义形式:virtual {method body}

  纯虚函数的定义形式:virtual { } = 0;

在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。

 

6. 虚函数必须实现,如果不实现,编译器将报错,错误提示为:

error LNK****: unresolved external symbol "public: virtual void __thiscall
ClassName::virtualFunctionName(void)"

7. 对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。

 

8. 实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以覆盖

该虚函数,由多态方式调用的时候动态绑定。

9. 虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的

函数

10. 多态性指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
a.编译时多态性:通过重载函数实现
运行时多态性:通过虚函数实现。

 

11. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。

 

复制代码
 1   //father class 2  3   class Virtualbase 4  5   { 6  7   public: 8  9   virtual void Demon()= 0; //prue virtual function10 11   virtual void Base() {cout<<"this is farther class"<};12 13   }14 15   //sub class16 17   class SubVirtual :public Virtualbase18 19   {20 21   public:22 23   void Demon() { cout<<" this is SubVirtual!"< 24 25   void Base() { cout<<"this is subclass Base"< 26 27   }28 29   /*30 31   instance class and sample32 33   */34 35   void main()36 37   {38 39   Virtualbase* inst = new SubVirtual(); //multstate pointer40 41   inst->Demon();42 43   inst->Base();44 45   // inst = new Virtualbase();46 47   // inst->Base()48 49   return ;50 51 }

0 0
原创粉丝点击