C++学习之多态

简介: C++学习之多态

C++ 多态的概念

在C++中,多态是面向对象程序设计中的一个重要概念,通过多态性可以实现基类和派生类对象的统一访问接口,从而提高代码的灵活性、可扩展性和易维护性。C++中的多态主要通过虚函数(virtual function)来实现。

具体来说,C++的多态性主要分为静态多态性(编译时多态)和动态多态性(运行时多态)两种:

  1. 静态多态性(编译时多态):通过函数重载(Function Overloading)和运算符重载(Operator Overloading)等方式实现的多态性。编译器在编译时会根据不同参数类型或数量,选择调用对应的函数或操作符重载函数。
  2. 动态多态性(运行时多态):通过虚函数(Virtual Function)和纯虚函数(Pure Virtual Function)实现的多态性。动态多态性允许子类对象在被当做父类对象使用时,调用子类重写的虚函数,即运行时确定函数的调用。

下面是一个简单示例来说明C++中动态多态的概念:

#include <iostream>
class Animal {
public:
    virtual void makeSound() {
        std::cout << "动物发出声音" << std::endl;
    }
};
class Dog : public Animal {
public:
    void makeSound() override {
        std::cout << "汪汪汪" << std::endl;
    }
};
class Cat : public Animal {
public:
    void makeSound() override {
        std::cout << "喵喵喵" << std::endl;
    }
};
int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();
    animal1->makeSound(); // 输出:"汪汪汪"
    animal2->makeSound(); // 输出:"喵喵喵"
    delete animal1;
    delete animal2;
    return 0;
}

在上面的例子中,Animal类有一个虚函数makeSound(),它被两个派生类Dog和Cat重写。在main函数中,通过基类指针指向派生类对象,实现了动态多态性。当调用makeSound()函数时,实际执行的是对应派生类的重写版本。

C++中的多态性使得我们可以以统一的方式操作不同类的对象,增加了代码的灵活性和可扩展性。希望这个解释能够帮助您理解C++中多态的概念。如果还有其他问题,请随时提出。

C++ 多态基本语法

下面是关于C++多态的基本语法:

  1. 定义基类和派生类:首先定义一个基类(父类),并在其中声明虚函数。然后从基类派生出一个或多个派生类(子类),可以选择重写基类中的虚函数。
class Shape {
public:
    virtual void draw() {
        std::cout << "绘制形状" << std::endl;
    }
};
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "绘制圆形" << std::endl;
    }
};
class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "绘制矩形" << std::endl;
    }
};
  1. 使用基类指针或引用:通过使用基类的指针或引用指向派生类的对象,实现多态性,便于以统一的方式操作不同类的对象。
int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();
    shape1->draw(); // 输出:"绘制圆形"
    shape2->draw(); // 输出:"绘制矩形"
    delete shape1;
    delete shape2;
    return 0;
}

在上面的例子中,基类Shape中声明了虚函数draw(),而派生类Circle和Rectangle分别重写了draw()函数。在主函数中,使用Shape指针shape1和shape2分别指向Circle和Rectangle对象实现多态。调用draw()函数时,会根据实际对象类型调用对应的重写函数。

  1. 注意事项
  • 虚函数只能在基类中声明,并且通过virtual关键字进行标识。
  • 在派生类中重写虚函数时,需要使用override关键字进行明确标识,便于编译器检查。
  • 在实际使用中,通常将基类析构函数声明为虚函数,以确保在删除基类指针时正确调用派生类的析构函数。

C++ 多态原理剖析

C++中的多态原理主要基于虚函数表(vtable)和虚函数指针(vptr)。下面是对C++多态原理的剖析:

  1. 虚函数表(vtable):每个包含虚函数的类都有一个虚函数表,用于存储虚函数的地址。虚函数表是一个静态的数据结构,类的每个实例对象不会拥有独立的虚函数表,而是指向类的共享虚函数表。
  2. 虚函数指针(vptr):每个类对象在内存中都会包含一个隐藏的指针,称为虚函数指针(vptr),它指向相应类的虚函数表。虚函数指针是一个编译器生成的额外成员,通常被添加到类的开头位置。
  3. 动态联编:当通过基类的指针或引用调用虚函数时,编译器通过虚函数指针查找并调用正确的虚函数。这个过程被称为动态联编(dynamic binding)或延迟绑定(late binding)。通过动态联编,程序能够在运行时根据实际对象类型决定调用哪个虚函数。
  4. 操作过程
  • 创建对象时,编译器分配空间存储对象数据成员和额外的虚函数指针。
  • 当调用虚函数时,实际调用的是通过虚函数指针找到的虚函数。
  • 当派生类重写了基类的虚函数时,编译器会更新派生类的虚函数表和虚函数指针。

以下是一个示例来说明多态的原理:

class Shape {
public:
    virtual void draw() {}
};
class Circle : public Shape {
public:
    void draw() override {}
};
class Rectangle : public Shape {
public:
    void draw() override {}
};
int main() {
    Shape* shape = new Circle();
    shape->draw(); // 调用的是派生类Circle的虚函数
    delete shape;
    return 0;
}

在上面的代码中,通过基类指针shape指向派生类Circle的实例对象。当调用shape->draw()时,实际上会根据对象类型找到派生类Circle的虚函数表,并使用虚函数指针调用正确的虚函数。

C++的多态性的实现依赖于虚函数表和虚函数指针,通过动态联编实现了对象的运行时多态特性。

C++ 多态纯虚函数和抽象类

下面解释纯虚函数和抽象类的概念以及它们之间的关系:

  1. 纯虚函数
  • 纯虚函数是在基类中声明的虚函数,但没有给出具体的实现,只有函数原型,形式为virtual void functionName() = 0;
  • 包含了纯虚函数的类被称为抽象类(abstract class)。
  • 抽象类不能实例化对象,因为它包含未定义的纯虚函数,需要子类来提供具体实现才能被实例化。

示例代码:

class Shape {
public:
    virtual void draw() = 0; // 纯虚函数
};
class Circle : public Shape {
public:
    void draw() override {
        // 实现绘制圆形的函数
    }
};
class Rectangle : public Shape {
public:
    void draw() override {
        // 实现绘制矩形的函数
    }
};
  1. 抽象类
  • 包含了至少一个纯虚函数的类被称为抽象类。抽象类无法被实例化,但可以作为基类用于派生其他类。
  • 如果一个类从抽象类继承而来,但没有实现所有纯虚函数,它仍然会被视为抽象类。

示例代码:

class Animal {
public:
    virtual void makeSound() = 0; // 纯虚函数
};
class Dog : public Animal {
public:
    void makeSound() override {
        // 实现狗叫的函数
    }
};
class Cat : public Animal {
public:
    // 没有实现makeSound,Cat类仍然是抽象类
};

通过使用纯虚函数和抽象类,我们可以定义一个接口,要求所有的派生类都实现某些方法,从而确保了多态性的有效应用。在实际开发中,抽象类和纯虚函数通常用于设计框架或接口,以便各个子类提供自己的实现。

C++ 多态虚析构和纯虚析构

在C++中,虚析构函数(virtual destructor)和纯虚析构函数(pure virtual destructor)都与多态性相关。下面将解释它们的概念和用途:

  1. 虚析构函数
  • 虚析构函数是基类的析构函数,在其前面加上virtual关键字即可声明为虚析构函数。
  • 当通过基类指针或引用删除派生类对象时,使用虚析构函数可以确保正确调用派生类的析构函数。
  • 虚析构函数在基类中定义,但不一定需要在派生类中重新实现(除非有特殊的资源释放需求)。

示例代码:

class Base {
public:
    virtual ~Base() {}
};
class Derived : public Base {
public:
    ~Derived() {}
};
int main() {
    Base* ptr = new Derived();
    delete ptr; // 调用派生类的析构函数
    return 0;
}

在上述示例中,通过基类指针ptr指向派生类Derived的实例对象。当调用delete ptr时,由于基类的析构函数声明为虚析构函数,编译器会正确调用派生类Derived的析构函数。

  1. 纯虚析构函数
  • 纯虚析构函数是一个在基类中声明为纯虚函数的虚析构函数。
  • 纯虚析构函数没有具体的实现,其声明形式为= 0
  • 在包含纯虚析构函数的类被继承时,派生类必须提供自己的析构函数实现。

示例代码:

class AbstractBase {
public:
    virtual ~AbstractBase() = 0; // 纯虚析构函数(没有实现)
};
AbstractBase::~AbstractBase() {} // 提供纯虚析构函数的默认实现
class ConcreteDerived : public AbstractBase {
public:
    ~ConcreteDerived() override {} // 派生类提供自己的析构函数实现
};
int main() {
    AbstractBase* ptr = new ConcreteDerived();
    delete ptr; // 调用派生类的析构函数
    return 0;
}

在上述示例中,AbstractBase是一个抽象类,其中包含了一个纯虚析构函数。派生类ConcreteDerived继承自AbstractBase,并提供了自己的实现。通过基类指针ptr删除派生类对象时,派生类的析构函数将被正确调用。

需要注意的是,类中包含纯虚析构函数的情况很少见,并且只在极特殊的情况下使用。因为纯虚析构函数意味着该类无法实例化对象,只能作为基类来被继承。

关注我,不迷路,共学习,同进步

关注我,不迷路,共学习,同进步

相关文章
|
23小时前
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
10 4
2023/11/10学习记录-C/C++对称分组加密DES
|
4月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
89 0
|
23天前
|
存储 编译器 数据安全/隐私保护
【C++】多态
多态是面向对象编程中的重要特性,允许通过基类引用调用派生类的具体方法,实现代码的灵活性和扩展性。其核心机制包括虚函数、动态绑定及继承。通过声明虚函数并让派生类重写这些函数,可以在运行时决定具体调用哪个版本的方法。此外,多态还涉及虚函数表(vtable)的使用,其中存储了虚函数的指针,确保调用正确的实现。为了防止资源泄露,基类的析构函数应声明为虚函数。多态的底层实现涉及对象内部的虚函数表指针,指向特定于类的虚函数表,支持动态方法解析。
29 1
|
2月前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
2月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
47 2
C++入门12——详解多态1
|
2月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
87 1
|
2月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
26 1
|
4月前
|
存储 编译器 C++
|
5月前
|
存储 编译器 C++
【C++】深度解剖多态(下)
【C++】深度解剖多态(下)
56 1
【C++】深度解剖多态(下)
|
5月前
|
机器学习/深度学习 算法 C++
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)