C++中类的多态与虚函数的使用

 

类的多态特性是支持面向对象的语言最主要的特性,有过非面向对象语言开发经历的人,通常对这一章节的内容会觉得不习惯,因为很多人错误的认为,支持类的封装的语言就是支持面向对象的,其实不然,Visual BASIC 6.0 是典型的非面向对象的开发语言,但是它的确是支持类,支持类并不能说明就是支持面向对象,能够解决多态问题的语言,才是真正支持面向对象的开发的语言,所以务必提醒有过其它非面向对象语言基础的读者注意!

  多态的这个概念稍微有点模糊,如果想在一开始就想用清晰用语言描述它,让读者能够明白,似乎不太现实,所以我们先看如下代码:

//例程1 
#include <iostream>     
using namespace std;   
   
class Vehicle 
{   
public:   

    Vehicle(float speed,int total) 
    { 
        Vehicle::speed=speed; 

        Vehicle::total=total; 
    } 
    void ShowMember() 
    { 
        cout<<speed<<"|"<<total<<endl; 

    } 
protected:   
    float speed; 
    int total; 

};   
class Car:public Vehicle   
{   
public:   
    Car(int aird,float speed,int total):Vehicle(speed,total)   
    {   

        Car::aird=aird;   
    } 
    void ShowMember() 
    { 
        cout<<speed<<"|"<<total<<"|"<<aird<<endl; 

    } 
protected:   
    int aird; 
};   
 
void main()   
{   

    Vehicle a(120,4); 
    a.ShowMember(); 
    Car b(180,110,4); 

    b.ShowMember(); 
    cin.get(); 
}

  在c++中是允许派生类重载基类成员函数的,对于类的重载来说,明确的,不同类的对象,调用其类的成员函数的时候,系统是知道如何找到其类的同名成员,上面代码中的a.ShowMember();,即调用的是Vehicle::ShowMember(),b.ShowMember();,即调用的是Car::ShowMemeber();。

  但是在实际工作中,很可能会碰到对象所属类不清的情况,下面我们来看一下派生类成员作为函数参数传递的例子,代码如下:

//例程2 
#include <iostream>     
using namespace std;   
   
class Vehicle 
{   
public:   

    Vehicle(float speed,int total) 
    { 
        Vehicle::speed=speed; 

        Vehicle::total=total; 
    } 
    void ShowMember() 
    { 
        cout<<speed<<"|"<<total<<endl; 

    } 
protected:   
    float speed; 
    int total; 

};   
class Car:public Vehicle   
{   
public:   
    Car(int aird,float speed,int total):Vehicle(speed,total)   
    {   

        Car::aird=aird;   
    } 
    void ShowMember() 
    { 
        cout<<speed<<"|"<<total<<"|"<<aird<<endl; 

    } 
protected:   
    int aird; 
};   
 
void test(Vehicle &temp) 


    temp.ShowMember(); 

 
void main()   

    Vehicle a(120,4); 

    Car b(180,110,4); 
    test(a); 
    test(b); 
    cin.get(); 
}

  例子中,对象a与b分辨是基类和派生类的对象,而函数test的形参却只是Vehicle类的引用,按照类继承的特点,系统把Car类对象看做是一个Vehicle类对象,因为Car类的覆盖范围包含Vehicle类,所以test函数的定义并没有错误,我们想利用test函数达到的目的是,传递不同类对象的引用,分别调用不同类的,重载了的,ShowMember成员函数,但是程序的运行结果却出乎人们的意料,iostream>     
using namespace std;   
   
class Vehicle 
{   
public:   

    Vehicle(float speed,int total) 
    { 
        Vehicle::speed = speed; 
        Vehicle::total = total; 
    } 
    virtual void ShowMember()//虚函数 
    { 
        cout<<speed<<"|"<<total<<endl; 

    } 
protected:   
    float speed; 
    int total; 

};   
class Car:public Vehicle   
{   
public:   
    Car(int aird,float speed,int total):Vehicle(speed,total)   
    {   

        Car::aird = aird;   
    } 
    virtual void ShowMember()//虚函数,在派生类中,由于继承的关系,这里的virtual也可以不加 
    { 

        cout<<speed<<"|"<<total<<"|"<<aird<<endl; 

    } 
public:   
    int aird; 
}; 
 
void test(Vehicle &temp) 


    temp.ShowMember(); 

 
int main()   
{   
    Vehicle a(120,4); 

    Car b(180,110,4); 
    test(a); 
    test(b); 
    cin.get(); 
}

  iostream>     
using namespace std;   
   

class Vehicle 
{   
public:  
    Vehicle(float speed,int total) 
    { 

        Vehicle::speed=speed; 
        Vehicle::total=total; 
    } 

    virtual void ShowMember() 
    { 
        cout<<speed<<"|"<<total<<endl; 

    } 
    virtual ~Vehicle() 
    { 

        cout<<"载入Vehicle基类析构函数"<<endl; 

        cin.get(); 
    } 
protected:   
    float speed; 

    int total; 
};   
class Car:public Vehicle   
{   

public:   
    Car(int aird,float speed,int total):Vehicle(speed,total)   
    {   

        Car::aird=aird;   
    } 
    virtual void ShowMember() 
    { 

        cout<<speed<<"|"<<total<<"|"<<aird<<endl; 

    } 
    virtual ~Car() 
    { 

        cout<<"载入Car派生类析构函数"<<endl; 

        cin.get(); 
    } 
protected:   
    int aird; 

};   
 
void test(Vehicle &temp) 


    temp.ShowMember(); 

void DelPN(Vehicle *temp) 

    delete temp; 

void main() 
{   
    Car *a=new Car(100,1,1); 
    a->ShowMember(); 

    DelPN(a); 
    cin.get(); 
}

  从上例代码的运行结果来看,当调用DelPN(a);后,在析构的时候,系统成功的确定了先调用Car类的析构函数,而如果将析构函数的virtual修饰去掉,再观察结果,会发现析构的时候,始终只调用了基类的析构函数,由此我们发现,多态的特性的virtual修饰,不单单对基类和派生类的普通成员函数有必要,而且对于基类和派生类的析构函数同样重要。

 
 

《挑战30天C++入门极限》C++中类的多态与虚函数的使用的更多相关文章

  1. 《挑战30天C++入门极限》新手入门:关于C++中的内联函数(inline)

        新手入门:关于C++中的内联函数(inline) 在c++中,为了解决一些频繁调用的小函数大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数. 可能说到这里,很 ...

  2. C++中类的多态与虚函数的使用

    C++的三大特性:封装.继承.多态.以前学的时候自己没去总结,记得在一本c++入门的书讲得还是比较清楚.今天上网找了一下多态,找到下面这篇文章写得比较清晰. http://pcedu.pconline ...

  3. 《挑战30天C++入门极限》图例实解:C++中类的继承特性

        图例实解:C++中类的继承特性 整个c++程序设计全面围绕面向对象的方式进行,类的继承特性是c++的一个非常非常重要的机制,继承特性可以使一个新类获得其父类的操作和数据结构,程序员只需在新类中 ...

  4. 《挑战30天C++入门极限》入门教程:实例详解C++友元

        入门教程:实例详解C++友元 在说明什么是友元之前,我们先说明一下为什么需要友元与友元的缺点: 通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为pu ...

  5. 《挑战30天C++入门极限》C++面向对象编程入门:构造函数与析构函数

        C++面向对象编程入门:构造函数与析构函数 请注意,这一节内容是c++的重点,要特别注意! 我们先说一下什么是构造函数. 上一个教程我们简单说了关于类的一些基本内容,对于类对象成员的初始化我们 ...

  6. 《挑战30天C++入门极限》C++面向对象编程入门:类(class)

        C++面向对象编程入门:类(class) 上两篇内容我们着重说了结构体相关知识的操作. 以后的内容我们将逐步完全以c++作为主体了,这也意味着我们的教程正式进入面向对象的编程了. 前面的教程我 ...

  7. 《挑战30天C++入门极限》C++类静态数据成员与类静态成员函数

        C++类静态数据成员与类静态成员函数 在没有讲述本章内容之前如果我们想要在一个范围内共享某一个数据,那么我们会设立全局对象,但面向对象的程序是由对象构成的,我们如何才能在类范围内共享数据呢? ...

  8. 《挑战30天C++入门极限》C++类对象的复制-拷贝构造函数

        C++类对象的复制-拷贝构造函数 在学习这一章内容前我们已经学习过了类的构造函数和析构函数的相关知识,对于普通类型的对象来说,他们之间的复制是很简单的,例如: int a = 10; int ...

  9. 《挑战30天C++入门极限》新手入门:C/C++中的结构体

        新手入门:C/C++中的结构体 什么是结构体? 简单的来说,结构体就是一个可以包含不同数据类型的一个结构,它是一种可以自己定义的数据类型,它的特点和数组主要有两点不同,首先结构体可以在一个结构 ...

随机推荐

  1. 转:Java接口和抽象类

    转:http://www.cnblogs.com/dolphin0520/p/3811437.html 一.抽象类 在了解抽象类之前,先来了解一下抽象方法.抽象方法是一种特殊的方法:它只有声明,而没有 ...

  2. 谷歌浏览器解决ajax跨域问题

    在用mui和H5+做混合开发,会利用HBuildx去真机调试,可真机调试总有问题所在,懂得人自然懂,而我们直接打开页面显示的只有一个静态的页面,是获取不到数据的在这里我想说的不是代码中利用jsonp, ...

  3. vue + elementui 使用多选按钮实现单选功能

    CommonRadio.vue <template> <div> <el-checkbox-group v-model="checkList" @ch ...

  4. VsCode使用setting sync 同步自己的插件和设置等

    直接再 Vscode中安装就可以,然后: 1. 可以点看setting sync插件在vscode 这个时候可以按照提示进行设置(也可以参考下:https://www.cnblogs.com/kenz ...

  5. (在客户端)https抓包解密

    (一)原理分析 https的数据包是用对称秘钥(https协议协商出来的随机数)加密后的密文. 对称秘钥在传输线路上是密文的(被非对称加密过),但是在client.server端是明文的(因为要用于加 ...

  6. python Linux 环境 (版本隔离工具)

    python Linux 环境 (版本隔离工具) 首先新建用户,养成良好习惯useradd python 1.安装pyenv GitHub官网:https://github.com/pyenv/pye ...

  7. Mongodb之增删改查操作

    一.创建一个数据库 在我们使用MongoDB数据库时引进了这样一个知识,“对于mongodb,使用了不存在的对象,就等于在创建这个对象”,所以创建数据库的操作就比较简单 在我们使用mysql数据库时u ...

  8. centos7误删除python2.7导致的python和yum不可用处理

    centos7误删除python2.7后导致yum不可用: 问题截图 解决方法: 1.在opt目录下创建site_package/python3递归目录,然后下载以下RPM包,并存入/opt/site ...

  9. Linux GRUB手动安装方法详解

    需要手工安装 GRUB 主要有两种情况: Linux 系统原先不是使用 GRUB 作为引导程序而现在想要使用 GRUB 来作为引导程序: MBR 中的引导程序被覆盖,需要在 MBR 中重新安装 GRU ...

  10. 分布式结构化存储系统-Kudu简介

    分布式结构化存储系统-Kudu简介 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Hadoop生态系统发展到现在,存储层主要由HDFS和HBase两个系统把持着,一直没有太大突破. ...