目录
在 C++ 面向对象编程中,纯虚函数(Pure Virtual Function)是一个核心概念。它允许我们定义一个接口,而不需要实现具体的功能,从而实现多态性。
一、纯虚函数的基本概念
1.1 定义与语法
纯虚函数是在基类中声明的虚函数,它在基类中没有具体实现,而是要求任何派生类都必须提供自己的实现。纯虚函数的声明语法是在函数原型后加= 0
。
class Shape {
public:
// 纯虚函数
virtual double area() const = 0;
};
1.2 抽象类
包含至少一个纯虚函数的类称为抽象类(Abstract Class)。抽象类不能实例化,只能作为基类被继承。
// 错误:无法实例化抽象类
// Shape s; // 编译错误
// 正确:可以定义抽象类的指针或引用
Shape* ptr;
1.3 派生类的实现要求
任何继承抽象类的派生类必须实现所有纯虚函数,否则派生类也会被视为抽象类。
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
// 实现纯虚函数
double area() const override {
return 3.14 * radius * radius;
}
};
二、纯虚函数的使用场景
2.1 定义接口
纯虚函数最常见的用途是定义接口,强制派生类实现特定的功能。
class Drawable {
public:
virtual void draw() const = 0;
virtual ~Drawable() {}
};
class Rectangle : public Drawable {
public:
void draw() const override {
std::cout << "Drawing a rectangle." << std::endl;
}
};
2.2 实现多态
通过纯虚函数,可以实现运行时多态,使基类指针能够根据实际对象类型调用相应的函数。
void render(const Drawable& obj) {
obj.draw();
}
int main() {
Rectangle rect;
render(rect); // 输出: Drawing a rectangle.
}
2.3 设计框架
在框架设计中,纯虚函数可以定义框架的扩展点,允许用户通过继承实现自定义功能。
class Application {
public:
virtual void initialize() = 0;
virtual void run() = 0;
virtual void shutdown() = 0;
void execute() {
initialize();
run();
shutdown();
}
};
三、纯虚函数的特性
3.1 纯虚函数可以有实现
虽然纯虚函数在基类中没有声明体,但可以为其提供定义。不过,派生类仍需提供自己的实现。
class Base {
public:
virtual void func() = 0; // 纯虚函数声明
};
// 纯虚函数的定义
void Base::func() {
std::cout << "Base::func() implementation" << std::endl;
}
class Derived : public Base {
public:
void func() override {
std::cout << "Derived::func() implementation" << std::endl;
Base::func(); // 可以调用基类的实现
}
};
3.2 抽象类的构造函数和析构函数
抽象类可以有构造函数和析构函数。构造函数用于初始化抽象类的成员,析构函数通常声明为虚函数,以确保正确释放派生类对象。
class Base {
protected:
int value;
public:
Base(int v) : value(v) {}
virtual ~Base() {}
virtual void print() const = 0;
};
class Derived : public Base {
public:
Derived(int v) : Base(v) {}
void print() const override {
std::cout << "Value: " << value << std::endl;
}
};
3.3 纯虚函数与接口继承
纯虚函数实现了接口继承(Inheritance of Interface),而不是实现继承(Inheritance of Implementation)。派生类只继承函数的接口,而不继承实现。
四、纯虚函数与普通虚函数的对比
特性 | 纯虚函数 | 普通虚函数 |
---|---|---|
声明语法 | virtual void func() = 0; | virtual void func(); |
是否必须在基类实现 | 否 | 是 |
能否实例化包含它的类 | 否(抽象类) | 是(具体类) |
派生类是否必须实现 | 是 | 否(可继承基类实现) |
典型用途 | 定义接口 | 实现多态行为 |
五、纯虚函数的应用实例
5.1 图形库设计
设计一个图形库,使用纯虚函数定义各种图形的公共接口。
#include <iostream>
#include <vector>
class Shape {
public:
virtual double area() const = 0;
virtual void draw() const = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14 * radius * radius;
}
void draw() const override {
std::cout << "Drawing a circle with radius " << radius << std::endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override {
return width * height;
}
void draw() const override {
std::cout << "Drawing a rectangle with width " << width
<< " and height " << height << std::endl;
}
};
int main() {
std::vector<Shape*> shapes;
shapes.push_back(new Circle(5.0));
shapes.push_back(new Rectangle(4.0, 6.0));
for (const auto& shape : shapes) {
std::cout << "Area: " << shape->area() << std::endl;
shape->draw();
delete shape;
}
return 0;
}
5.2 游戏角色系统
设计一个游戏角色系统,使用纯虚函数定义角色的行为。
#include <iostream>
class Character {
public:
virtual void attack() = 0;
virtual void defend() = 0;
virtual ~Character() {}
};
class Warrior : public Character {
public:
void attack() override {
std::cout << "Warrior uses sword!" << std::endl;
}
void defend() override {
std::cout << "Warrior uses shield!" << std::endl;
}
};
class Mage : public Character {
public:
void attack() override {
std::cout << "Mage casts fireball!" << std::endl;
}
void defend() override {
std::cout << "Mage uses magic barrier!" << std::endl;
}
};
void simulateBattle(Character& attacker, Character& defender) {
attacker.attack();
defender.defend();
}
int main() {
Warrior warrior;
Mage mage;
simulateBattle(warrior, mage);
simulateBattle(mage, warrior);
return 0;
}
5.3 工厂模式实现
使用纯虚函数实现工厂模式,创建不同类型的产品。
#include <iostream>
#include <string>
// 产品接口
class Product {
public:
virtual void use() = 0;
virtual ~Product() {}
};
// 具体产品
class ConcreteProductA : public Product {
public:
void use() override {
std::cout << "Using Product A" << std::endl;
}
};
class ConcreteProductB : public Product {
public:
void use() override {
std::cout << "Using Product B" << std::endl;
}
};
// 工厂接口
class Factory {
public:
virtual Product* createProduct() = 0;
virtual ~Factory() {}
};
// 具体工厂
class ConcreteFactoryA : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductA();
}
};
class ConcreteFactoryB : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductB();
}
};
int main() {
Factory* factoryA = new ConcreteFactoryA();
Product* productA = factoryA->createProduct();
productA->use();
Factory* factoryB = new ConcreteFactoryB();
Product* productB = factoryB->createProduct();
productB->use();
delete productA;
delete factoryA;
delete productB;
delete factoryB;
return 0;
}
六、纯虚函数的注意事项
6.1 避免在构造函数和析构函数中调用纯虚函数
在基类的构造函数或析构函数中调用纯虚函数是不安全的,因为此时派生类部分可能尚未完全构造或已被销毁。
6.2 纯虚函数与默认参数
纯虚函数可以有默认参数,但调用时使用的参数值由调用点的静态类型决定,而非动态类型。
class Base {
public:
virtual void func(int x = 10) = 0;
};
class Derived : public Base {
public:
void func(int x = 20) override {
std::cout << "x = " << x << std::endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->func(); // 输出 x = 10,使用基类的默认参数
delete ptr;
return 0;
}
6.3 纯虚函数与多重继承
在多重继承中,一个类可以从多个抽象类继承纯虚函数,必须实现所有纯虚函数才能成为具体类。
七、总结
纯虚函数是 C++ 面向对象编程中的重要工具,它允许我们定义抽象接口,实现多态性,并强制派生类提供特定功能的实现。通过合理使用纯虚函数,可以设计出灵活、可扩展的类层次结构。