`
gyht0808
  • 浏览: 115576 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

理解虚基类 虚函数与纯虚函数的概念

阅读更多
引言

     一直以来都没有写过一篇关于概念性的文章,因为我觉得这些概念性的东西书本上都有并且说的也很详细写来也无用,今天突发奇想想写一写,下面就和大家讨论一下虚基类、虚函数与纯虚函数,一看名字就让人很容易觉得混乱。不过不要紧待看完本文后你就会理解了。

正文

       虚基类
       在说明其作用前先看一段代码

class A
{
public:
    int iValue;
};

class B:public A
{
public:
    void bPrintf(){cout<<"This is class B"<<endl;};
};

class C:public A
{
public:
    void cPrintf(){cout<<"This is class C"<<endl;};
};

class D:public B,public C
{
public:
    void dPrintf(){cout<<"This is class D"<<endl;};
};

void main()
{
    D d;
    cout<<d.iValue<<endl; //错误,不明确的访问
    cout<<d.A::iValue<<endl; //正确
    cout<<d.B::iValue<<endl; //正确
    cout<<d.C::iValue<<endl; //正确
}
从代码中可以看出类B C都继承了类A的iValue成员,因此类B C都有一个成员变量iValue ,而类D又继承了B C,这样类D就有一个重名的成员 iValue(一个是从类B中继承过来的,一个是从类C中继承过来的).在主函数中调用d.iValue 因为类D有一个重名的成员iValue编译器不知道调用 从谁继承过来的iValue所以就产生的二义性的问题.正确的做法应该是加上作用域限定符 d.B::iValue 表示调用从B类继承过来的iValue。不过 类D的实例中就有多个iValue的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。

class A
{
public:
    int iValue;
};

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

class C:virtual public A
{
public:
    void cPrintf(){cout<<"This is class C"<<endl;};
};

class D:public B,public C
{
public:
    void dPrintf(){cout<<"This is class D"<<endl;};
};

void main()
{
    D d;
    cout<<d.iValue<<endl; //正确
}
在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如iValue这个成员,从类 D这个角度上来看,它是从类B与类C继承过来的,而类B C又是从类A继承过来的,但它们只保留一个副本。因此在主函数中调用d.iValue时就不 会产生错误。

       虚函数
       还是先看代码

class A
{
public:
    void funPrint(){cout<<"funPrint of class A"<<endl;};
};

class B:public A
{
public:
    void funPrint(){cout<<"funPrint of class B"<<endl;};
};

void main()
{
    A *p; //定义基类的指针
    A a;
    B b;
    p=&a;
    p->funPrint();
    p=&b;
    p->funPrint();
}
大家以为这段代码的输出结果是什么?有的人可能会马上回答funPrint of class A 与 funPrint of class B 因为第一次输出是引用类A的实例啊,第二次输出是引用类B的实例啊。那么我告诉你这样想就错啦,答案是funPrint of class A 与 funPrint of class A 至于为什么输出这样的结果不在本文讨论的范围之内;你就记住,不管引用的实例是哪个类的当你调用的时候系统会调用左值那个对象所属类的方法。比如说 上面的代码类A B都有一个funPrint 函数,因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了。可是如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到虚函数(也就是动态联篇)

class A
{
public:
    virtual void funPrint(){cout<<"funPrint of class A"<<endl;};
};

class B:public A
{
public:
    virtual void funPrint(){cout<<"funPrint of class B"<<endl;};
};

void main()
{
    A *p; //定义基类的指针
    A a;
    B b;
    p=&a;
    p->funPrint();
    p=&b;
    p->funPrint();
}
在基类的成员函数前加virtual关键字表示这个函数是一个虚函数,所谓虚函数就是在编译的时候不确定要调用哪个函数,而是动态决定将要调 用哪个函数,要实现虚函数必须派生类的函数名与基类相同,参数名参数类型等也要与基类相同。但派生类中的virtual关键字可以省略,也表示这是一个虚函数。下面来解决一下代码,声明一个基类的指针(必须是基类,反之则不行)p,把p指向类A的实例a,调用funPrint函数,这 时系统会判断p所指向的实例的类型,如果是A类的实例就调用A类的funPrint函数,如果是B类的实例就调用B类的funPrint函数。

    纯虚函数
    与其叫纯虚函数还不如叫抽象类,它只是声明一个函数但不实现它,让派生类去实现它,其实这也很好理解。

class Vehicle
{
public:
    virtual void PrintTyre()=0; //纯虚函数是这样定义的
};

class Camion:public Vehicle
{
public:
    virtual void PrintTyre(){cout<<"Camion tyre four"<<endl;};
};

class Bike:public Vehicle
{
public:
    virtual void PrintTyre(){cout<<"Bike tyre two"<<endl;};
};

void main()
{
    Camion c;
    Bike b;
    b.PrintTyre();
    c.PrintTyre();
}
如上代码,定义了一个交通工具类(Vehicle),类中有一函数可打印出交通工具的轮胎个数,但交通工具很多轮胎个数自然也就不确定,所以 就把它定义为纯虚函数,也就是光定义函数名不去实现它,类Camion继承了Vehicle并实现了里面的代码,打印出有4个轮胎。Bike类也是一样。有一点须要注意一下,纯虚函数不能实化化,但可以声明指针。


总结

    虚基类
    1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。
    2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。
    3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。
    4, 最派生类是指在继承结构中建立对象时所指定的类。
    5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。
    6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。
    7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

    虚函数
    1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。
    2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。
    3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。
    4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。
    5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。

    纯虚函数
    1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。
    2, 纯虚函数的作用是为派生类提供一个一致的接口。
    3, 纯虚函数不能实化化,但可以声明指针。
分享到:
评论

相关推荐

    虚基类、虚函数与纯虚函数

    在C++编程语言中,虚基类、虚函数与纯虚函数是面向对象编程中用于实现多态性的重要概念。本文将深入解析这三个概念,并通过示例代码来具体阐述其用法与作用。 ### 虚基类 虚基类(Virtual Base Class)是用来解决...

    Java与C++在虚函数,纯虚函数上的区别

    ### Java与C++在虚函数、纯虚函数上的区别详解 #### Java的抽象函数与C++的虚函数 在深入探讨两者之间的差异之前,我们先理解一下Java与C++各自如何处理多态性,尤其是通过虚函数和抽象函数实现。 **Java**中,...

    函数覆盖,虚函数,纯虚函数对比

    在C++编程语言中,函数覆盖、虚函数和纯虚函数是面向对象编程中的关键概念,它们涉及到类的继承和多态性。本文将详细探讨这三个概念,以及它们之间的对比。 1. **函数覆盖(Function Overriding)** 函数覆盖发生...

    实验三虚函数与多态纯虚函数完整版.pdf

    本实验通过具体的代码示例,帮助理解如何通过虚函数和纯虚函数实现多态,以及如何在实际编程中应用这些概念。通过阅读和分析这份文档,读者应该能够加深对C++中虚函数、纯虚函数和多态的理解,并在自己的项目中灵活...

    C++ 虚基类 继承 多态示例

    // 定义纯虚函数,强制所有派生类实现 }; ``` 然后,各个形状类会继承`Shape`并提供自己的`getArea()`实现: ```cpp class Square : public Shape { private: double side; public: Square(double s) : side(s)...

    C++_虚函数和纯虚函数区别(高清版).rar

    理解虚函数和纯虚函数的区别对于C++程序员来说至关重要,这有助于构建灵活、可扩展的面向对象程序。通过合理使用这两种特性,我们可以设计出层次清晰、易于维护的代码结构。在实际编程中,虚函数常用于基类和派生类...

    C++中的虚函数和纯虚函数用法(ZT)

    纯虚函数是一种特殊的虚函数,它在基类中只有声明而没有实现,必须在派生类中提供具体的实现。纯虚函数使得包含它的类成为抽象类,抽象类不能直接创建对象,只能作为其他类的基类。 **纯虚函数的定义形式:** ```...

    虚函数和纯虚函数.pdf

    虚函数和纯虚函数是C++面向对象编程中的核心概念,它们主要涉及到继承和多态性的实现。在C++中,虚函数(virtual function)允许我们通过基类指针或引用调用派生类中重定义的同名函数,从而实现动态绑定或运行时多态...

    C抽象基类和纯虚成员函数-C教程共3页.pdf.zip

    在C++中,一个纯虚函数是在基类中声明为虚函数,并且没有提供实现的函数。其声明形式为`virtual 函数名() = 0;`。纯虚函数的存在使得包含它的类成为抽象的,因此不能直接创建该类的实例。纯虚函数定义了一个必须由...

    C++继承与派生虚基类[参照].pdf

    本文主要讨论C++中的继承和派生虚基类,通过实验设计方案,实验内容和实验步骤,来详细介绍虚函数、抽象基类、多态性和文件输入输出的概念。 一、实验设计方案 实验名称:类和类的继承 实验时间:2011-9-20——...

    C++实验六 多态性和虚函数的应用 课程 实验报告

    1. 纯虚函数:纯虚函数是不提供具体实现的虚函数,用`=0`表示。如果一个类含有纯虚函数,那么这个类就是抽象类,不能实例化对象。 2. 抽象类:抽象类作为基类使用,用于定义接口。在C++中,抽象类不能被实例化,但...

    c++纯虚函数的用法及详解

    ### C++纯虚函数的用法及详解 #### 一、引言 在学习C++的过程中,虚函数是一个重要的概念,特别是在实现多态性时。本文将深入探讨纯虚函数的概念及其...理解纯虚函数的工作原理对于任何C++开发者来说都是至关重要的。

    c++虚函数与虚函数表

    ### C++虚函数与虚函数表的理解 #### 一、虚函数的概念 在C++中,虚函数(Virtual Function)是一种特殊类型的成员函数,它允许基类指针或引用指向派生类对象,并通过该基类指针或引用调用派生类中重写的同名函数。...

    C++虚函数及虚函数表解析

    每个具有虚函数的类都有一个与之关联的虚函数表,其中包含了该类及其所有基类虚函数的地址。当通过指针调用虚函数时,编译器会查找该指针所指向对象的虚函数表,然后调用表中对应的函数地址。这个过程在运行时完成,...

    C++中虚函数与纯虚函数的用法

    纯虚函数(Pure Virtual Functions)是虚函数的一种特殊形式,它在基类中声明为纯虚函数时,基类变为抽象类,不能实例化。纯虚函数的声明方式是在函数声明后跟一个`= 0`。例如: ```cpp class Base { public: ...

    多态性与虚函数习题

    - **解释**: 虚函数的重新定义需要保持与基类中虚函数相同的参数个数和类型。 8. **答案**: A.对象指针 - **解释**: 动态联编通常是通过对象指针或引用实现的,这里指针是最常见的用法。 9. **答案**: C....

    虚函数与抽象函数的区别

    在C#语言中,虚函数(Virtual Function)与抽象函数(Abstract Function)是面向对象编程中非常重要的概念,它们在实现多态性方面扮演着关键角色。然而,两者之间存在本质的区别,理解这些区别对于正确地设计类层次...

Global site tag (gtag.js) - Google Analytics