虚函数和纯虚函数
在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. 详细说明
虚函数的特点
-
多态性:
- 虚函数允许在运行时决定调用哪个函数实现,这称为运行时多态。
- 通过基类指针或引用,可以调用派生类中重写的函数。
-
默认实现:
- 基类中可以为虚函数提供默认实现,派生类可以选择是否覆盖。
- 如果派生类没有重写虚函数,调用基类的实现。
-
非抽象类:
- 包含虚函数的类仍然是具体类,可以被实例化。
-
语法:
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; } };
纯虚函数的特点
-
抽象性:
- 包含纯虚函数的类是抽象类,不能被实例化。
- 纯虚函数没有实现,派生类必须提供具体实现。
-
接口定义:
- 纯虚函数用于定义接口,确保派生类实现特定的行为。
- 类似于其他语言中的接口(如Java中的
interface
)。
-
强制重写:
- 派生类如果不实现所有继承的纯虚函数,仍然是抽象类,无法实例化。
-
语法:
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
实现。LaserPrinter
和InkjetPrinter
类重写了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
。Car
和Motorcycle
类必须实现startEngine
函数。- 通过基类指针调用
startEngine
,实现了多态性。
6. 关键点总结
特性 | 虚函数(Virtual Function) | 纯虚函数(Pure Virtual Function) |
---|---|---|
定义 | virtual void func(); | virtual void func() = 0; |
实现 | 可以在基类中实现 | 不在基类中实现,派生类必须实现 |
类类型 | 基类可以实例化 | 基类是抽象类,不能实例化 |
强制性 | 派生类可以选择实现或使用基类实现 | 派生类必须实现,或者继续保持抽象 |
典型用途 | 提供可选的默认行为 | 定义接口,强制派生类实现特定行为 |
关键点
-
虚函数提供灵活性:
- 允许基类提供默认实现,派生类可以根据需要选择覆盖或继承。
- 适用于需要共享某些通用行为,但允许具体类进行定制的场景。
-
纯虚函数强制实现:
- 定义接口或抽象基类,确保所有具体类实现特定功能。
- 适用于需要统一接口但行为由具体类定义的场景。
-
抽象类与具体类:
- 含有纯虚函数的类是抽象类,无法实例化,只能作为基类。
- 含有虚函数但无纯虚函数的类是具体类,可以实例化。
-
多态性的实现:
- 虚函数和纯虚函数都是实现多态性的手段。
- 通过基类指针或引用,可以调用具体类的实现,增强代码的灵活性和可扩展性。
7. 进一步阅读与学习
原文地址:https://blog.csdn.net/qq_45993770/article/details/143641363
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!