68、C++面向对象编程中的多态性深入解析

C++面向对象编程中的多态性深入解析

1. 多态处理的演示

在测试员工层次结构时,会创建 SalariedEmployee CommissionEmployee BasePlusCommissionEmployee 这三个具体类的对象。程序首先使用静态绑定来操作这些对象,然后使用 Employee 指针的向量进行多态处理。

以下是相关代码示例:

// Fig. 20.17: fig20_17.cpp
// Processing Employee derived-class objects individually 
// and polymorphically using dynamic binding.
#include <iostream>
#include <iomanip>
#include <vector>
#include "Employee.h"
#include "SalariedEmployee.h" 
#include "CommissionEmployee.h"  
#include "BasePlusCommissionEmployee.h" 
using namespace std;

void virtualViaPointer( const Employee * const ); // prototype
void virtualViaReference( const Employee & ); // prototype

int main()
{
    // set floating-point output formatting
    cout << fixed << setprecision( 2 );

    // create derived-class objects                        
    SalariedEmployee salariedEmployee(                     
        "John", "Smith", "111-11-1111", 800 );              
    CommissionEmployee commissionEmployee( 
        "Sue", "Jones", "333-33-3333", 10000, .06 );
    BasePlusCommissionEmployee basePlusCommissionEmployee( 
        "Bob", "Lewis", "444-44-4444", 5000, .04, 300 );

    cout << "Employees processed individually using static binding:\n\n";

    // output each Employee’s information and earnings using static binding
    salariedEmployee.print();
    cout << "\nearned $" << salariedEmployee.earnings() << "\n\n";
    commissionEmployee.print();
    cout << "\nearned $" << commissionEmployee.earnings() << "\n\n";
    basePlusCommissionEmployee.print();
    cout << "\nearned $" << basePlusCommissionEmployee.earnings() << "\n\n";

    cout << "Employees processed polymorphically via dynamic binding:\n\n";

    // create vector of three base-class pointers
    vector< Employee * > employees( 3 );        
    // initialize vector with pointers to Employees
    employees[ 0 ] = &salariedEmployee;            
    employees[ 1 ] = &commissionEmployee;          
    employees[ 2 ] = &basePlusCommissionEmployee;  

    cout << "Virtual function calls made off base-class pointers:\n\n";
    for ( const Employee *employeePtr : employees )
        virtualViaPointer( employeePtr );        

    cout << "Virtual function calls made off base-class references:\n\n";
    for ( const Employee *employeePtr : employees )              
        virtualViaReference( *employeePtr ); // note dereferencing

    return 0;
}

// call Employee virtual functions print and earnings off a   
// base-class pointer using dynamic binding                   
void virtualViaPointer( const Employee * const baseClassPtr ) 
{                                                             
    baseClassPtr->print();                                     
    cout << "\nearned $" << baseClassPtr->earnings() << "\n\n";
} 

// call Employee virtual functions print and earnings off a  
// base-class reference using dynamic binding                
void virtualViaReference( const Employee &baseClassRef )     
{                                                            
    baseClassRef.print();                                     
    cout << "\nearned $" << baseClassRef.earnings() << "\n\n";
} 

在上述代码中,静态绑定的函数调用在编译时就确定了要调用的函数,因为使用的是对象名而不是指针或引用。而多态处理则通过 Employee 指针的向量来实现,在运行时根据指针实际指向的对象类型来调用相应的函数。

2. 多态、虚函数和动态绑定的底层实现

C++在编译包含一个或多个虚函数的类时,会为该类构建一个虚函数表(vtable)。vtable 包含指向类的虚函数的指针,程序在调用虚函数时会使用 vtable 来选择合适的函数实现。

以下是不同类的 vtable 情况:
| 类名 | 虚函数 1(earnings) | 虚函数 2(print) |
| ---- | ---- | ---- |
| Employee | 0(纯虚函数) | 指向显示员工全名和社保号的函数 |
| SalariedEmployee | 指向返回员工周薪的函数 | 指向显示员工类型、姓名、社保号和周薪的函数 |
| CommissionEmployee | 指向返回员工销售提成的函数 | 指向显示员工类型、姓名、社保号、提成率和销售总额的函数 |
| BasePlusCommissionEmployee | 指向返回员工基本工资加销售提成的函数 | 指向显示员工类型、姓名、社保号、提成率、销售总额和基本工资的函数 |

多态的实现通过三个层次的指针:
1. vtable 中的函数指针 :指向虚函数实际执行的代码。
2. 对象的 vtable 指针 :当创建包含虚函数的类的对象时,编译器会为对象附加一个指向该类 vtable 的指针。
3. 接收虚函数调用的对象句柄 :如 Employee 指针或引用。

baseClassPtr->print() 调用为例,其执行流程如下:

graph TD;
    A[选择 employees 中的元素并传递给 virtualViaPointer] --> B[解引用指针得到对象];
    B --> C[解引用对象的 vtable 指针得到 vtable];
    C --> D[跳过偏移量选择 print 函数指针];
    D --> E[解引用 print 函数指针并执行函数];
3. 运行时类型信息和动态类型转换

在某些情况下,需要在运行时确定对象的具体类型并进行相应操作。例如,为 BasePlusCommissionEmployee 员工增加 10% 的基本工资。

以下是相关代码示例:

// Fig. 20.19: fig20_19.cpp
// Demonstrating downcasting and runtime type information.
#include <iostream>
#include <iomanip>
#include <vector>
#include <typeinfo>
#include "Employee.h"
#include "SalariedEmployee.h" 
#include "CommissionEmployee.h"  
#include "BasePlusCommissionEmployee.h" 
using namespace std;

int main()
{
    // set floating-point output formatting
    cout << fixed << setprecision( 2 ); 

    // create vector of three base-class pointers
    vector < Employee * > employees( 3 );

    // initialize vector with various kinds of Employees
    employees[ 0 ] = new SalariedEmployee(              
        "John", "Smith", "111-11-1111", 800 );           
    employees[ 1 ] = new CommissionEmployee(            
        "Sue", "Jones", "333-33-3333", 10000, .06 );     
    employees[ 2 ] = new BasePlusCommissionEmployee(    
        "Bob", "Lewis", "444-44-4444", 5000, .04, 300 ); 

    // polymorphically process each element in vector employees
    for ( Employee *employeePtr : employees ) 
    {
        employeePtr->print(); // output employee information
        cout << endl;

        // determine whether element points to a BasePlusCommissionEmployee 
        BasePlusCommissionEmployee *derivedPtr =                        
            dynamic_cast < BasePlusCommissionEmployee * >( employeePtr );
        if ( derivedPtr != nullptr ) // true for "is a" relationship
        {
            double oldBaseSalary = derivedPtr->getBaseSalary();
            cout << "old base salary: $" << oldBaseSalary << endl;
            derivedPtr->setBaseSalary( 1.10 * oldBaseSalary );
            cout << "new base salary with 10% increase is: $" 
                << derivedPtr->getBaseSalary() << endl;
        } 

        cout << "earned $" << employeePtr->earnings() << "\n\n";
    }   

    // release objects pointed to by vector’s elements
    for ( const Employee *employeePtr : employees ) 
    {
        cout << "deleting object of "               
            << typeid( *employeePtr ).name() << endl;
        delete employeePtr;
    } 

    return 0;
}

在上述代码中,使用 dynamic_cast 来判断当前 Employee 指针是否指向 BasePlusCommissionEmployee 对象,如果是则进行相应的工资调整。同时,使用 typeid 来获取对象的类型信息。

通过使用 dynamic_cast ,可以避免一些编译错误。如果直接将基类指针赋值给派生类指针,会导致编译错误,因为 C++ 不允许这种操作,而 dynamic_cast 会进行类型检查。

4. 性能考量与优化建议

在使用多态性、虚函数和动态绑定的过程中,性能是一个需要考虑的重要因素。虽然这些特性为编程带来了很大的灵活性,但也会引入一定的开销。

4.1 性能开销分析
  • 时间开销 :每次调用虚函数时,程序需要通过虚函数表(vtable)来查找实际要执行的函数,这涉及到指针的解引用和内存访问操作,会增加额外的执行时间。特别是在频繁调用虚函数的场景下,这种开销会更加明显。
  • 空间开销 :每个包含虚函数的类都有一个虚函数表,并且每个该类的对象都需要一个指向虚函数表的指针,这会增加内存的使用量。
4.2 优化建议
  • 合理使用虚函数 :在设计类时,只将确实需要多态行为的函数声明为虚函数,避免不必要的虚函数声明。
  • 避免过度使用多态 :在对性能要求极高的场景下,如实时系统,应谨慎使用多态性。可以考虑使用更简单的编程方式,如 switch 语句来替代多态处理。
  • 选择合适的数据结构 :一些 C++ 标准库类,如 array vector ,在实现时没有使用多态和虚函数,以避免执行时的开销,从而获得最佳性能。在合适的场景下,可以优先选择这些数据结构。
5. 总结与实践建议

多态性是面向对象编程中的一个强大特性,它通过虚函数和动态绑定实现了代码的灵活性和可扩展性。在实际编程中,我们需要根据具体的需求和场景来合理使用多态性。

以下是一些实践建议:
1. 设计类层次结构时 :明确哪些函数需要实现多态行为,将这些函数声明为虚函数。同时,确保抽象基类中包含纯虚函数,以强制派生类实现这些函数。
2. 使用多态处理时 :通过基类指针或引用操作派生类对象,利用虚函数的动态绑定特性,让程序在运行时根据对象的实际类型调用相应的函数。
3. 处理特定类型对象时 :当需要对特定类型的对象进行特殊处理时,使用 dynamic_cast 进行类型检查和转换,避免出现未定义行为。
4. 性能优化时 :根据项目的性能要求,合理权衡多态性带来的灵活性和性能开销,采取相应的优化措施。

为了更好地理解和掌握多态性,建议进行以下实践操作:
1. 编写示例代码 :根据本文中的示例代码,自己动手编写一些简单的程序,加深对多态性、虚函数和动态绑定的理解。
2. 调试和测试 :在编写代码的过程中,进行调试和测试,观察程序的运行结果,验证多态性的实现效果。
3. 分析性能 :使用性能分析工具,分析程序中多态函数调用的性能开销,找出性能瓶颈并进行优化。

通过不断的实践和学习,我们可以更加熟练地运用多态性,编写出高质量、高性能的 C++ 程序。

操作 说明
设计类层次结构 明确虚函数,包含纯虚函数
使用多态处理 通过基类指针或引用操作对象
处理特定类型对象 使用 dynamic_cast 进行类型检查和转换
性能优化 合理权衡灵活性和性能开销
graph LR;
    A[设计类层次结构] --> B[使用多态处理];
    B --> C[处理特定类型对象];
    C --> D[性能优化];

希望这些内容能够帮助你更好地理解和应用 C++ 中的多态性。在实际编程中,不断探索和实践,你会发现多态性的强大之处。

【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点探讨其系统建模与控制策略,结合Matlab代码与Simulink仿真实现。文章详细分析了无人机的动力学模型,特别是引入螺旋桨倾斜机构后带来的全驱动特性,使其在姿态与位置控制上具备更强的机动性与自由度。研究涵盖了非线性系统建模、控制器设计(如PID、MPC、非线性控制等)、仿真验证及动态响应分析,旨在提升无人机在复杂环境下的稳定性和控制精度。同时,文中提供的Matlab/Simulink资源便于读者复现实验并进一步优化控制算法。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真经验的研究生、科研人员及无人机控制系统开发工程师,尤其适合从事飞行器建模与先进控制算法研究的专业人员。; 使用场景及目标:①用于全驱动四旋翼无人机的动力学建模与仿真平台搭建;②研究先进控制算法(如模型预测控制、非线性控制)在无人机系统中的应用;③支持科研论文复现、课程设计或毕业课题开发,推动无人机高机动控制技术的研究进展。; 阅读建议:建议读者结合文档提供的Matlab代码与Simulink模型,逐步实现建模与控制算法,重点关注坐标系定义、力矩分配逻辑及控制闭环的设计细节,同时可通过修改参数和添加扰动来验证系统的鲁棒性与适应性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值