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. Eclipse 安装反编译插件 Eclipse Class Decompiler

    Eclipse Class Decompiler在线安装方法 https://blog.csdn.net/tangjinquan1157/article/details/77506015 Eclips ...

  2. cxx11emu.h 和 logprint.h

    cxx11emu.h 和 logprint.h /* Start of cxx11emu.h */ #ifndef STDBP_CXX11EMU_H_ #define STDBP_CXX11EMU_H ...

  3. windows下pyinstaller打包踩坑记录

    示例: 需要打包的是 ReadConfig.py 文件,同文件夹下调用了Interface.py文件,ui文件夹下调用了 Ui_config.py和Ui_Error.py文件,Interface.py ...

  4. 关于Eclipse导入maven项目报空指针异常

    今天新建了一个maven项目,因为是通过公司的工具新建的,代码拉下来就有src.pom.xml文件. 导入Eclipse却报空指针异常.具体如下: An error has occurred. See ...

  5. c# 表达式目录树拷贝对象(根据对象类型动态生成表达式目录树)

    表达式目录树,在C#中用Expression标识,这里就不介绍表达式目录树是什么了,有兴趣可以自行百度搜索,网上资料还是很多的. 这里主要分享的是如何动态构建表达式目录树. 构建表达式目录树的代码挺简 ...

  6. hash文件-对文件进行数字签名

    (一)windows自带hash命令: certutil -hashfile D:\1.exe MD5              #  md5的hash值为32位certutil -hashfile ...

  7. urlib库

    urllib库是python中最基本的网络请求库,可以模拟浏览器的行为,向指定的服务器发送请求,并可以保存服务器返回的数据. urlopen() urllib.request模块提供了最基本的构造ht ...

  8. MySQL Binlog--事务日志和BINLOG落盘参数对磁盘IO的影响

    参数说明 innodb_flush_log_at_trx_commit和sync_binlog 两个参数是控制MySQL 磁盘写入策略以及数据安全性的关键参数,不同参数设置对磁盘IO影响不同. 参数i ...

  9. python(数据精度处理)

    一.取整处理 1.int() 向下取整 内置函数 1 n = 3.75 2 print(int(n))>>> 3 3 n = 3.25 4 print(int(n))>> ...

  10. c# MatchCollection类