自学内容网 自学内容网

虚函数和纯虚函数

在C++中,虚函数(Virtual Function)和纯虚函数(Pure Virtual Function)都是实现多态性的重要工具。虽然它们在概念上相关,但有着不同的用途和特性。以下将详细解释它们的区别、用途以及如何在实际编程中使用它们。


1. 基本定义

虚函数(Virtual Function)

虚函数是在基类中声明的,可以在派生类中被重写(覆盖)的函数。它允许通过基类指针或引用调用派生类中的函数实现,从而实现运行时多态。

class Base {
public:
    virtual void display() {
        std::cout << "Display from Base" << std::endl;
    }
};

纯虚函数(Pure Virtual Function)

纯虚函数是一个没有实现的虚函数,基类中声明为纯虚函数的类被称为抽象类。纯虚函数强制派生类必须提供该函数的具体实现,否则派生类也将成为抽象类,无法实例化。

class AbstractBase {
public:
    virtual void display() = 0; // 纯虚函数
};

2. 主要区别

特性虚函数(Virtual Function)纯虚函数(Pure Virtual Function)
定义方式virtual 返回类型 函数名(参数列表)virtual 返回类型 函数名(参数列表) = 0;
实现可以在基类中提供默认实现不提供实现,必须在派生类中实现
类类型基类可以是具体类(可实例化)基类必须是抽象类(不可实例化)
强制性派生类可以选择是否重写派生类必须重写,否则也是抽象类
用途提供默认行为,允许派生类覆盖定义接口,强制派生类实现特定行为

3. 详细说明

虚函数的特点

  1. 多态性

    • 虚函数允许在运行时决定调用哪个函数实现,这称为运行时多态
    • 通过基类指针或引用,可以调用派生类中重写的函数。
  2. 默认实现

    • 基类中可以为虚函数提供默认实现,派生类可以选择是否覆盖。
    • 如果派生类没有重写虚函数,调用基类的实现。
  3. 非抽象类

    • 包含虚函数的类仍然是具体类,可以被实例化。
  4. 语法

    class Base {
    public:
        virtual void display() {
            std::cout << "Display from Base" << std::endl;
        }
    };
    
    class Derived : public Base {
    public:
        void display() override { // 重写虚函数
            std::cout << "Display from Derived" << std::endl;
        }
    };
    

纯虚函数的特点

  1. 抽象性

    • 包含纯虚函数的类是抽象类,不能被实例化。
    • 纯虚函数没有实现,派生类必须提供具体实现。
  2. 接口定义

    • 纯虚函数用于定义接口,确保派生类实现特定的行为。
    • 类似于其他语言中的接口(如Java中的interface)。
  3. 强制重写

    • 派生类如果不实现所有继承的纯虚函数,仍然是抽象类,无法实例化。
  4. 语法

    class AbstractBase {
    public:
        virtual void display() = 0; // 纯虚函数
        virtual ~AbstractBase() = default; // 虚析构函数
    };
    
    class Derived : public AbstractBase {
    public:
        void display() override { // 必须实现纯虚函数
            std::cout << "Display from Derived" << std::endl;
        }
    };
    

4. 使用场景对比

虚函数的使用场景

  • 提供默认行为:基类提供一个通用的实现,派生类可以根据需要选择是否覆盖。
  • 可选覆盖:派生类可以继承基类的行为,或者根据需求提供特定实现。

示例

#include <iostream>
#include <vector>

class Animal {
public:
    virtual void makeSound() {
        std::cout << "Some generic animal sound." << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() override { // 重写虚函数
        std::cout << "Woof!" << std::endl;
    }
};

class Cat : public Animal {
    // 不重写 makeSound,使用基类的默认实现
};

int main() {
    std::vector<Animal*> animals = { new Dog(), new Cat() };

    for(auto animal : animals) {
        animal->makeSound();
    }

    // 输出:
    // Woof!
    // Some generic animal sound.

    // 清理内存
    for(auto animal : animals) {
        delete animal;
    }

    return 0;
}

纯虚函数的使用场景

  • 定义接口:创建一个接口,确保所有派生类实现特定的函数。
  • 强制实现:确保所有具体类实现某些关键功能,避免遗漏。

示例

#include <iostream>
#include <vector>

// 抽象基类
class Shape {
public:
    virtual void draw() = 0; // 纯虚函数,强制派生类实现
    virtual ~Shape() = default;
};

// 具体类
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;
    }
};

int main() {
    // Shape s; // 错误:无法实例化抽象类

    std::vector<Shape*> shapes = { new Circle(), new Rectangle() };

    for(auto shape : shapes) {
        shape->draw();
    }

    // 输出:
    // Drawing a Circle.
    // Drawing a Rectangle.

    // 清理内存
    for(auto shape : shapes) {
        delete shape;
    }

    return 0;
}

5. 示例对比

为了更直观地理解虚函数和纯虚函数的区别,以下通过两个完整的示例进行对比。

示例1:使用虚函数

#include <iostream>
#include <vector>

class Printer {
public:
    virtual void print() {
        std::cout << "Printing from Printer" << std::endl;
    }
};

class LaserPrinter : public Printer {
public:
    void print() override {
        std::cout << "Printing from LaserPrinter" << std::endl;
    }
};

class InkjetPrinter : public Printer {
public:
    void print() override {
        std::cout << "Printing from InkjetPrinter" << std::endl;
    }
};

int main() {
    std::vector<Printer*> printers = { new Printer(), new LaserPrinter(), new InkjetPrinter() };

    for(auto printer : printers) {
        printer->print();
    }

    // 输出:
    // Printing from Printer
    // Printing from LaserPrinter
    // Printing from InkjetPrinter

    // 清理内存
    for(auto printer : printers) {
        delete printer;
    }

    return 0;
}

说明

  • Printer 类提供了一个默认的 print 实现。
  • LaserPrinterInkjetPrinter 类重写了 print 函数。
  • 当调用 print 时,基类和派生类的实现都会被调用,具体取决于对象的实际类型。

示例2:使用纯虚函数

#include <iostream>
#include <vector>

// 抽象基类
class Vehicle {
public:
    virtual void startEngine() = 0; // 纯虚函数
    virtual ~Vehicle() = default;
};

// 具体类
class Car : public Vehicle {
public:
    void startEngine() override {
        std::cout << "Car engine started." << std::endl;
    }
};

class Motorcycle : public Vehicle {
public:
    void startEngine() override {
        std::cout << "Motorcycle engine started." << std::endl;
    }
};

int main() {
    // Vehicle v; // 错误:无法实例化抽象类

    std::vector<Vehicle*> vehicles = { new Car(), new Motorcycle() };

    for(auto vehicle : vehicles) {
        vehicle->startEngine();
    }

    // 输出:
    // Car engine started.
    // Motorcycle engine started.

    // 清理内存
    for(auto vehicle : vehicles) {
        delete vehicle;
    }

    return 0;
}

说明

  • Vehicle 类是一个抽象类,定义了一个纯虚函数 startEngine
  • CarMotorcycle 类必须实现 startEngine 函数。
  • 通过基类指针调用 startEngine,实现了多态性。

6. 关键点总结

特性虚函数(Virtual Function)纯虚函数(Pure Virtual Function)
定义virtual void func();virtual void func() = 0;
实现可以在基类中实现不在基类中实现,派生类必须实现
类类型基类可以实例化基类是抽象类,不能实例化
强制性派生类可以选择实现或使用基类实现派生类必须实现,或者继续保持抽象
典型用途提供可选的默认行为定义接口,强制派生类实现特定行为

关键点

  1. 虚函数提供灵活性

    • 允许基类提供默认实现,派生类可以根据需要选择覆盖或继承。
    • 适用于需要共享某些通用行为,但允许具体类进行定制的场景。
  2. 纯虚函数强制实现

    • 定义接口或抽象基类,确保所有具体类实现特定功能。
    • 适用于需要统一接口但行为由具体类定义的场景。
  3. 抽象类与具体类

    • 含有纯虚函数的类是抽象类,无法实例化,只能作为基类。
    • 含有虚函数但无纯虚函数的类是具体类,可以实例化。
  4. 多态性的实现

    • 虚函数和纯虚函数都是实现多态性的手段。
    • 通过基类指针或引用,可以调用具体类的实现,增强代码的灵活性和可扩展性。

7. 进一步阅读与学习

  • C++ 官方文档:深入了解虚函数纯虚函数.
  • 设计模式:学习如何在设计模式中使用虚函数和纯虚函数,如模板方法模式策略模式.
  • C++ 多态性:理解运行时多态的工作原理及其在面向对象编程中的应用。

原文地址:https://blog.csdn.net/qq_45993770/article/details/143641363

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!