C++中的抽象类是一种不能被直接实例化的特殊类,它的核心作用是作为其他类的基类,定义统一的接口规范,让派生类必须实现这些接口,从而保证多态行为的正确性。抽象类的存在让代码的结构更清晰,也方便后续的功能扩展和维护。

C++抽象类的定义与特性
抽象类的判定标准非常简单:只要一个类中包含至少一个纯虚函数,这个类就是抽象类。抽象类有以下几个核心特性:
- 不能直接创建抽象类的对象,尝试实例化会直接触发编译错误
- 抽象类可以作为指针或引用的类型,指向或引用它的派生类对象,这是实现运行时多态的基础
- 抽象类的派生类如果没有实现所有纯虚函数,那么派生类也会成为抽象类,同样不能被实例化
- 抽象类可以有普通的成员变量、普通成员函数和构造函数,构造函数通常用于初始化基类的成员,不能被声明为虚函数
纯虚函数的定义规范
纯虚函数是抽象类的核心组成部分,它的定义有严格的语法规范,开发者需要严格遵守才能写出正确的代码。
基本语法格式
纯虚函数的声明格式为:在函数声明的末尾加上=0,并且不需要提供函数体实现。注意=0只是语法标记,不代表函数返回值为0。
// 纯虚函数的标准定义示例
class Base {
public:
// 纯虚函数声明,末尾加=0,无函数体
virtual void func() = 0;
// 带参数的纯虚函数示例
virtual int calculate(int a, int b) = 0;
// 带返回值的纯虚函数示例
virtual std::string get_name() = 0;
};
定义注意事项
- 纯虚函数必须声明为
virtual函数,否则=0的语法是不合法的 - 纯虚函数不能有函数体,但是如果是类外定义的话,需要注意类内声明是纯虚,类外不能重复写
=0,不过通常纯虚函数不需要类外实现 - 如果派生类没有实现基类的所有纯虚函数,那么派生类仍然是抽象类,无法实例化
- 纯虚函数可以被派生类继承后重写,重写时需要保证函数签名(返回值、参数列表、const属性)和基类完全一致,否则会被视为新的函数,不会覆盖基类的纯虚函数
抽象类与纯虚函数的使用示例
下面通过一个实际的代码示例展示抽象类和纯虚函数的完整用法,场景是定义一个图形基类,派生出圆形和矩形类,统一实现面积计算接口。
#include <iostream>
#include <string>
// 抽象类:图形基类
class Shape {
public:
// 纯虚函数:计算面积
virtual double get_area() = 0;
// 纯虚函数:获取图形名称
virtual std::string get_shape_name() = 0;
// 普通虚函数:打印图形信息,提供默认实现
virtual void print_info() {
std::cout << "图形名称:" << get_shape_name() << ",面积:" << get_area() << std::endl;
}
// 虚析构函数,保证派生类析构时能正确调用基类析构
virtual ~Shape() {}
};
// 派生类:圆形
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
// 实现基类的纯虚函数
double get_area() override {
return 3.14159 * radius * radius;
}
std::string get_shape_name() override {
return "圆形";
}
};
// 派生类:矩形
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
// 实现基类的纯虚函数
double get_area() override {
return width * height;
}
std::string get_shape_name() override {
return "矩形";
}
};
int main() {
// 错误示例:不能实例化抽象类
// Shape s; // 编译会报错
// 正确用法:用抽象类指针指向派生类对象
Shape* shape1 = new Circle(5.0);
Shape* shape2 = new Rectangle(4.0, 6.0);
shape1->print_info();
shape2->print_info();
delete shape1;
delete shape2;
return 0;
}
上面的代码中,Shape类包含两个纯虚函数,因此是抽象类,不能直接创建对象。派生类Circle和Rectangle都实现了所有纯虚函数,因此可以被实例化。通过基类指针调用print_info方法时,会根据实际指向的对象类型调用对应的get_area和get_shape_name实现,这就是运行时多态的体现。
常见错误与避坑指南
- 忘记将基类的析构函数声明为虚函数:如果抽象类的析构函数不是虚函数,那么通过基类指针删除派生类对象时,只会调用基类的析构函数,派生类的资源无法释放,会导致内存泄漏
- 派生类重写纯虚函数时函数签名不一致:比如基类纯虚函数是
virtual void func(int a) = 0;,派生类写成void func(double a) override,此时override关键字会触发编译错误,提示没有可覆盖的函数,如果没有写override则不会报错,但会新增一个函数,不会覆盖基类的纯虚函数 - 尝试给纯虚函数写函数体:在类内声明纯虚函数时,不能在声明后直接写函数体,比如
virtual void func() = 0 { }是不合法的,会直接编译报错