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删除派生类对象时,派生类的析构函数将被正确调用。

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

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

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

相关文章
|
3天前
|
算法 数据处理 C++
|
1天前
|
编译器 程序员 C++
【C++高阶】掌握C++多态:探索代码的动态之美
【C++高阶】掌握C++多态:探索代码的动态之美
7 0
|
3天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
3天前
|
存储 编译器 C++
C++基础知识(七:多态)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
3天前
|
算法 C++ 容器
|
3天前
|
存储 调度 C++
|
3天前
|
存储 安全 C++