C++中实现多态有几种方式小结

 更新时间:2024年12月05日 11:00:15   作者:黑果果的思考  
在C++中,多态是一种面向对象编程的特性,允许以统一的方式处理不同类型的对象,并根据实际对象的类型来执行相应的操作,本文给大家介绍了C++中实现多态有几种方式小结,需要的朋友可以参考下

多态的概念

多态 指的是 具有继承关系 的不同对象在调用同一函数 时形成的不同状态!

举个例子:比如我们平时买高铁/火车票时,不同的人买票买的结果都是不一样的!学生是半价、成人是全价、军人是军人优先票 等!而这三种对象本质都是继承自同一个 Person 的父类,所以他们执行同一操作时不同的结果就是多态!

• 构成多态的两个必要条件

1、必须通过基类/父类的 指针或者引用 调用虚函数

2、派生类必须得对基类的虚函数进行重写

举个栗子先见一见:

class Person
{
public:
	// 虚函数
	virtual void BuyTicket()
	{
		cout << "Person::买票-成人-全价" << endl;
	}
};
 
class Student : public Person // 构成继承关系
{
public:
	// 虚函数
	virtual void BuyTicket()
	{
		cout << "Student::买票-学生-半价" << endl;
	}
};
 
int main()
{
	// 父类对象的指针 调用虚函数
	Person* pp = new Student;
	pp->BuyTicket();
	// 父类对象的引用 调用子类的对象
	Student st;
	Person& rp = st;
	rp.BuyTicket();
 
	Person p;
	Person& r = p;// 父类对象的引用 调用虚函数
	r.BuyTicket();
	Person* ptr = &p;// 父类对象的指针 调用虚函数
	ptr->BuyTicket();
 
	return 0;
}

此时,不同的对象去以父类的指针/引用去调用虚函数时结果是不一样的

下面由小编给大家总结一下C++实现多态的几种方式!

一)虚函数(Virtual Functions)实现多态

概念:

虚函数是在基类中使用关键字virtual声明的成员函数。当一个类包含虚函数时,编译器会为该类创建一个虚函数表(v - table),这个表存储了虚函数的地址。当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型(即指针或引用所指向或引用的实际子类对象)来查找虚函数表,从而调用子类中重写后的虚函数,实现多态行为。

代码示例:

class Shape 
{
public:
    virtual void draw() = 0; 
};
class Circle : public Shape 
{
public:
    void draw() override 
    {
        std::cout << "Drawing a circle." << std::endl;
    }
};
class Rectangle : public Shape 
{
public:
    void draw() override 
    {
        std::cout << "Drawing a rectangle." << std::endl;
    }
};

在这个例子中,Shape是一个抽象基类,draw是一个纯虚函数。Circle和Rectangle是Shape的子类,它们重写了draw函数。当使用
Shape* shapePtr;
shapePtr = new Circle();
shapePtr->draw();
或者
Shape circleObj;
Shape& shapeRef = circleObj;
shapeRef.draw();
(circleObj是Circle类的对象)这样的方式调用draw函数时,会根据对象是Circle还是Rectangle来动态地调用相应子类的draw函数,实现多态。

二)函数指针实现多态(较少使用,但在某些特定场景下有效)

概念:

可以通过定义函数指针,让函数指针指向不同的函数实现来达到类似多态的效果。函数指针可以根据具体的情况(如运行时的条件)来改变它所指向的函数,从而实现不同的行为。不过这种方式与虚函数相比,没有自动的动态绑定机制,需要手动管理函数指针的指向。

代码示例:

// 定义函数指针类型,该函数接受无参数,返回void
typedef void (*DrawFunction)();
class Shape 
{
public:
    DrawFunction drawFunction;
    Shape(DrawFunction df) : drawFunction(df) {}
    void draw() 
    {
        drawFunction();
    }
};
void drawCircle() 
{
    std::cout << "Drawing a circle using function pointer." << std::endl;
}
void drawRectangle() 
{
    std::cout << "Drawing a rectangle using function pointer." << std::endl;
}
int main() 
{
    Shape circleShape(drawCircle);
    Shape rectangleShape(drawRectangle);
    circleShape.draw();
    rectangleShape.draw();
    return 0;
}

在这个例子中,Shape类中有一个DrawFunction类型的函数指针drawFunction。在构造函数中,可以传入不同的函数来初始化这个函数指针。当调用draw方法时,就会执行函数指针所指向的函数。通过这种方式,可以实现根据不同的对象(这里通过不同的构造方式)来执行不同的绘制函数,达到类似多态的效果。不过这种方式需要手动设置函数指针,而且对于继承体系的支持不如虚函数方便。
代码示例:

#include <iostream>
// 定义函数指针类型,该函数接受两个整数参数并返回一个整数
typedef int (*MathOperation)(int, int);
int add(int a, int b) 
{
    return a + b;
}
int subtract(int a, int b) 
{
    return a - b;
}
int main()
{
    MathOperation operation;
    operation = add;
    int result1 = operation(5, 3);
    operation = subtract;
    int result2 = operation(5, 3);
    std::cout << "加法结果: " << result1 << std::endl;
    std::cout << "减法结果: " << result2 << std::endl;
    return 0;
}

在这个例子中,MathOperation是一个函数指针类型。通过将operation函数指针先后指向add函数和subtract函数,实现了根据需要调用不同函数的效果,从而在一定程度上实现了多态。

三)模板(Templates)实现编译时多态(也称为参数化多态)

概念:

模板是 C++ 中的泛型编程机制。虽然它不是真正的多态(因为它是在编译时确定具体的函数或类的版本,而不是运行时),但在某些情况下可以实现类似多态的代码复用和灵活性。通过模板,可以编写通用的代码,这些代码可以根据不同的类型参数生成不同的具体实现,从而适应多种数据类型或类类型的需求。

代码示例(函数模板):

template<typename T>
void draw(T& shape) 
{
    shape.draw();
}
class Circle 
{
public:
    void draw() 
    {
        std::cout << "Drawing a circle using template." << std::endl;
    }
};
class Rectangle 
{
public:
    void draw() 
    {
        std::cout << "Drawing a rectangle using template." << std::endl;
    }
};
int main() 
{
    Circle circle;
    Rectangle rectangle;
    draw(circle);
    draw(rectangle);
    return 0;
}

在这个例子中,draw是一个函数模板,它可以接受不同类型的参数(只要这个类型有draw方法)。当传入Circle或Rectangle对象时,会在编译时根据对象的类型生成对应的draw函数调用,实现了对不同类型对象的通用处理,有点类似于多态的效果,但这种方式是基于编译时的类型推导,而不是像虚函数那样的运行时多态。

四)抽象基类与纯虚函数结合实现多态(与虚函数方式紧密相关)

概念:

抽象基类是包含纯虚函数的类,不能被实例化。纯虚函数是在基类中声明但没有定义的虚函数,它强制子类必须重写这个函数。通过这种方式,可以定义一个通用的接口,子类必须实现这个接口,从而实现多态。当通过基类指针或引用调用这些纯虚函数时,会根据子类的实际实现来调用相应的函数。

代码示例(与前面虚函数示例结合):

class Shape
{
public:
virtual void draw() = 0;
};

这里Shape是抽象基类,draw是纯虚函数。子类必须重写draw函数才能实例化,这样就确保了在通过Shape基类指针或引用调用draw函数时,能够根据具体子类的实现来获得不同的绘制行为,实现多态。这种方式明确了接口规范,并且和虚函数的动态绑定机制一起,是 C++ 实现多态的重要方式之一。

五)函数重载(Function Overloading)实现有限的多态性

概念:

函数重载是指在同一个作用域内,可以有多个同名函数,它们的参数列表(参数个数、类型、顺序)不同。当调用一个重载函数时,编译器会根据传入的实际参数来确定调用哪一个具体的函数版本。这种方式在一定程度上实现了多态性,因为相同的函数名可以根据不同的参数类型执行不同的操作。

代码示例:

#include <iostream>
class Calculator 
{
public:
    int add(int a, int b)
    {
        return a + b;
    }
    double add(double a, double b) 
    {
        return a + b;
    }
};
int main() 
{
    Calculator calculator;
    int intResult = calculator.add(3, 5);
    double doubleResult = calculator.add(3.5, 2.5);
    std::cout << "整数相加结果: " << intResult << std::endl;
    std::cout << "浮点数相加结果: " << doubleResult << std::endl;
    return 0;
}

在这个例子中,Calculator类中有两个add函数,一个用于整数相加,一个用于浮点数相加。编译器会根据传入add函数的参数类型来决定调用哪个版本,这就像一种简单的多态,根据参数类型的不同选择不同的操作方式。不过,函数重载是在编译时确定调用的函数版本,而不是像虚函数那样在运行时动态绑定。

六)函数对象(仿函数,Functors):

原理:函数对象是一个类,它重载了函数调用运算符operator()。这样,这个类的对象就可以像函数一样被调用。例如:

class AddFunctor 
{
public:
    int operator()(int a, int b) const 
    {
        return a + b;
    }
};
class SubtractFunctor 
{
public:
    int operator()(int a, int b) const 
    {
        return a - b;
    }
};
int main() 
{
    AddFunctor addObj;
    SubtractFunctor subtractObj;
    int result1 = addObj(3, 2);
    int result2 = subtractObj(3, 2);
    return 0;
}

应用场景:在 STL 算法中广泛使用。例如,std::sort函数可以接受一个比较函数对象来确定排序的顺序。不同的比较函数对象可以实现不同的排序规则(如升序、降序),通过这种方式实现了基于不同规则的排序多态性。同时,函数对象可以携带状态(通过类的成员变量),这是函数指针所不具备的优势,在一些需要记录中间状态的场景中非常有用。

以上就是C++中实现多态有几种方式小结的详细内容,更多关于C++实现多态方式的资料请关注脚本之家其它相关文章!

相关文章

  • C语言中的逗号运算符详解

    C语言中的逗号运算符详解

    在C语言中逗号“,”也是一种运算符,称为逗号运算符,其功能是把两个表达式连接起来组成一个表达式, 称为逗号表达式,这篇文章主要介绍了C语言中的逗号运算符,需要的朋友可以参考下
    2022-11-11
  • C++中单链表的建立与基本操作

    C++中单链表的建立与基本操作

    以下是对C++中单链表的建立与基本操作进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2013-10-10
  • C++实现LeetCode(105.由先序和中序遍历建立二叉树)

    C++实现LeetCode(105.由先序和中序遍历建立二叉树)

    这篇文章主要介绍了C++实现LeetCode(105.由先序和中序遍历建立二叉树),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++交换指针实例

    C++交换指针实例

    这篇文章主要介绍了C++交换指针实例,针对C与C++交换指针的方法进行了较为详细的对比分析,非常具有实用价值,需要的朋友可以参考下
    2014-10-10
  • 基于C语言实现的aes256加密算法示例

    基于C语言实现的aes256加密算法示例

    这篇文章主要介绍了基于C语言实现的aes256加密算法,结合具体实例形式详细分析了C语言实现的aes256加密算法实现步骤与使用技巧,需要的朋友可以参考下
    2017-02-02
  • VSCode配置C语言环境的方法

    VSCode配置C语言环境的方法

    这篇文章主要介绍了VSCode配置C语言环境的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • C++中的各种容器的使用方法汇总

    C++中的各种容器的使用方法汇总

    这篇文章主要介绍了C++中的各种容器的使用方法,本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01
  • 深入探讨linux下进程的最大线程数、进程最大数、进程打开的文件数

    深入探讨linux下进程的最大线程数、进程最大数、进程打开的文件数

    本篇文章是对linux下进程的最大线程数、进程最大数、进程打开的文件数进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言二叉树的三种遍历方式的实现及原理

    C语言二叉树的三种遍历方式的实现及原理

    这篇文章主要介绍了C语言二叉树的三种遍历方式的实现及原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • 深入学习C语言mmap和shm*的使用方法技巧

    深入学习C语言mmap和shm*的使用方法技巧

    本文将详细介绍mmap和shm的工作原理,包括它们在内存映射和共享内存方面的优势和适用场景,同时,文章还会分享一些使用mmap和shm的技巧和经验,以帮助读者优化并提高程序性能,使你能够在实际项目中更好地利用这些技术来加速数据共享和多线程应用
    2023-10-10

最新评论