C++基础知识(七:多态)

简介: 多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。


目录

多态概念与实践

1. 多态的基础

2. 函数重载(静态多态)

3. 动态多态

4. 虚析构函数

【1】多态的前提

【2】虚函数(virtual)

【4】虚析构函数

示例:


多态概念与实践

多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。

1. 多态的基础

  • 继承与多态:继承是实现多态的前提条件。通过继承,子类可以继承父类的属性和方法,并在此基础上进行扩展或重写,从而实现不同类之间相同接口的多种行为。

2. 函数重载(静态多态)

  • 定义:在同一作用域内,函数名相同但参数列表不同(类型、数量或顺序不同),实现不同的功能,这就是静态多态,编译器在编译时期就能确定调用哪个函数。

3. 动态多态

  • 虚函数:通过在基类中声明虚函数(使用virtual关键字),允许子类重写(override)该函数,实现运行时的多态性。虚函数的关键在于,它允许通过基类指针或引用调用子类的实现。
  • 虚函数表(V-Table):每个包含虚函数的类都有一个虚函数表,存储虚函数的地址。对象实例包含一个指向虚函数表的指针,该指针在对象创建时初始化。当子类重写了虚函数,子类的虚函数表会在相应位置存储子类的函数地址。
  • 实现机制:在运行时,通过基类指针调用虚函数时,实际调用的是该指针所指向对象的虚函数表中的函数地址,从而实现动态绑定,达到多态的效果。

4. 虚析构函数

  • 重要性:当使用基类指针删除派生类对象时,如果没有虚析构函数,只会调用基类的析构函数,导致派生类特有的资源无法被正确释放。因此,为保证多态对象能被正确销毁,基类的析构函数应声明为虚函数。
  • 工作原理:虚析构函数确保通过基类指针删除对象时,会首先调用派生类的析构函数,然后再调用基类的析构函数,从而彻底释放派生类的资源。

【1】多态的前提

  1. 继承:多态的实现通常基于类的继承关系。一个类(子类)继承自另一个类(父类),子类可以继承父类的属性和方法,并且可以覆盖或扩展父类的行为。
  2. 虚函数:在基类中定义虚函数(使用virtual关键字),是实现动态多态的关键。虚函数允许子类重写父类中的同名函数,这样在运行时,根据对象的实际类型来决定调用哪个版本的函数。
  3. 父类指针或引用:通过父类的指针或引用来指向子类的对象,这是多态调用的常见方式。这样,即使使用的是父类的接口,也能调用到子类重写后的方法,实现动态行为。
  4. 方法重写:子类在继承父类的过程中,可以重写(override)父类中的虚函数,提供自己的实现。这是多态表现不同行为的基础。

在子类中重写父类的虚函数就是函数重写的过程,可以实现多态

【2】虚函数(virtual)

只要基类中是虚函数,后面的所有子类中该函数都是虚函数

常规来说,在继承时,给父类中的函数加上virtual关键字,定义成一个虚函数,

在子类中,可以对父类中的虚函数进行函数重写(override)

只要有虚函数的类,都会有一个虚函数表和一个虚(函数表)指针

虚指针是指向虚函数表的指针;

虚函数表,存储所有的虚函数的信息

虚函数表:保存所有虚函数的入口地址,每一个包含虚函数的类都会有一张虚函数表

如果发生继承关系,子类先复制父类的虚函数表,如果子类对某个虚函数重写,就去更改虚函数表中,该函数的入口地址

虚函数表指针:指向虚函数表的指针,父类中有一个虚函数表指针,子类中的虚函数表指针是从父类中继承下来的虚函数表指针,指向子类的虚函数表(虚函数表指针存在类中的第一个位置)

image.gif 编辑

【4】虚析构函数

由于实现多态,需要使用父类的指针,指向子类的空间,父类指针可以操作的空间,只有父类自己的部分,所以,在delete父类指针时,并不会释放调子类的空间

解决方法:给基类(父类)的析构函数前面加上virtual关键字,只要基类是虚析构函数,后面继承的所有子类都是虚析构函数,虚析构函数会引导父类的指针,释放掉子类的空间

示例:

#include <iostream>
using namespace std;
class Person {
public:
    virtual ~Person() { cout << "Person的析构" << endl; }
    virtual void play() { cout << "吃饭" << endl; }
    virtual void fun() { cout << "fun" << endl; }
};
class Student : public Person {
public:
    void play() override { cout << "打游戏" << endl; }
    ~Student() { cout << "Student的析构" << endl; }
};
class SubStudent : public Student {
public:
    void play() override { cout << "第二次继承" << endl; }
    void fun() override { cout << "fun的第二次继承" << endl; }
};
int main() {
    Person *p = new Student;
    p->play(); // 通过父类指针调用子类重写的play方法
    delete p;  // 正确释放资源,由于虚析构函数的存在,Student的析构也会被调用
    // 下面是注释掉的代码示例,展示了多态的其他用法
    // Person *p1 = new SubStudent;
    // p1->play();  // 第二次继承
    // p1->fun();   // fun的第二次继承
    return 0;
}

image.gif

image.gif 编辑

可以看到这里虽然是Person类型的指针但是调用出来的函数确实student的play()。

相关文章
|
2天前
|
存储 编译器 C++
|
2天前
|
C语言 C++ 开发者
C++基础知识(一:命名空间的各种使用方法)
C++在C的基础上引入了更多的元素,例如类,类的私密性要比C中的结构体更加优秀,引用,重载,命名空间,以及STL库,模板编程和更多的函数,在面向对象的编程上更加高效。C语言的优势则是更加底层,编译速度会更快,在编写内核时大多数都是C语言去写。 在C++中,命名空间(Namespace)是一种组织代码的方式,主要用于解决全局变量、函数或类的命名冲突问题。命名空间提供了一种封装机制,允许开发者将相关的类、函数、变量等放在一个逻辑上封闭的区域中,这样相同的名字在不同的命名空间中可以共存,而不会相互干扰。
|
4天前
|
程序员 C++
【C++】揭开C++多态的神秘面纱
【C++】揭开C++多态的神秘面纱
|
14天前
|
C++
C++一分钟之-继承与多态概念
【6月更文挑战第21天】**C++的继承与多态概述:** - 继承允许类从基类复用代码,增强代码结构和重用性。 - 多态通过虚函数实现,使不同类对象能以同一类型处理。 - 关键点包括访问权限、构造/析构、菱形问题、虚函数与动态绑定。 - 示例代码展示如何创建派生类和调用虚函数。 - 注意构造函数初始化、空指针检查和避免切片问题。 - 应用这些概念能提升程序设计和维护效率。
21 2
|
2天前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。
|
2天前
|
算法 编译器 C++
C++基础知识(三:哑元和内联函数和函数重载)
在C++编程中,"哑元"这个术语虽然不常用,但可以理解为在函数定义或调用中使用的没有实际功能、仅作为占位符的参数。这种做法多见于模板编程或者为了匹配函数签名等场景。例如,在实现某些通用算法时,可能需要一个特定数量的参数来满足编译器要求,即使在特定情况下某些参数并不参与计算,这些参数就可以被视为哑元。
|
2天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
2天前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。
|
2天前
|
存储 编译器 C++
C++基础知识(六:继承)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
2天前
|
存储 算法 程序员
C++基础知识(八:STL标准库(Vectors和list))
C++ STL (Standard Template Library标准模板库) 是通用类模板和算法的集合,它提供给程序员一些标准的数据结构的实现如 queues(队列), lists(链表), 和 stacks(栈)等. STL容器的提供是为了让开发者可以更高效率的去开发,同时我们应该也需要知道他们的底层实现,这样在出现错误的时候我们才知道一些原因,才可以更好的去解决问题。

热门文章

最新文章