纯虚函数必须在声明末尾写=0且无函数体,含纯虚函数的类为抽象类不可实例化,但可定义指针或引用实现多态;派生类未重写所有纯虚函数仍为抽象类;纯虚函数可有默认实现,但调用需显式作用域限定。
纯虚函数的定义语法必须带 = 0
在 C++ 类中声明纯虚函数,核心是函数声明末尾加上
,且该函数不能有函数体(哪怕写成
也会编译失败)。它只出现在类定义内部,用于表明该函数必须由派生类实现。
常见错误包括:
漏掉
,结果变成普通虚函数,基类可实例化,失去抽象约束
在类内写了函数体,如
,编译器直接报错
把
写在函数声明之外,比如放在注释后或换行后,会被视为语法错误
正确写法示例:
含纯虚函数的类自动成为抽象类
只要一个类中至少有一个纯虚函数,该类就不可实例化——即使其他所有函数都已实现。这不是靠关键字
控制(C++ 没这关键字),而是语言规则强制的。
使用场景上要注意:
立即学习
“
C++免费学习笔记(深入)
”;
C函数速查手册(CHM版)
C函数速查手册(CHM版)
下载
不能对抽象类调用
或声明
,否则编译失败,错误信息类似:
可以定义指向抽象类的指针或引用,这是多态调用的基础,例如
派生类若未重写全部纯虚函数,它本身仍是抽象类,也无法实例化
纯虚函数可以有默认实现,但调用需显式限定
纯虚函数允许在类外提供定义(即函数体),但这不改变其“纯虚”性质,也不影响抽象类判定。这种写法常用于提供通用逻辑,同时强制接口契约。
关键点在于:派生类中若要调用该默认实现,必须显式通过
作用域
解析符调用,例如
;否则仅靠
会触发纯虚调用崩溃(运行时 undefined behavior)。
示例:
接口设计中慎用数据成员和非虚析构函数
把抽象类当接口用时(类似 Java 的
),容易忽略两个隐性陷阱:
抽象类中定义了数据成员,会导致所有派生类对象大小增加,且破坏“零开销抽象”原则;接口应尽量只含纯虚函数
析构函数不是虚函数,会导致
指向派生类对象的基类指针时,派生类析构函数不被调用——内存泄漏或资源未释放
务必为抽象基类声明虚析构函数,哪怕它是空的:
如果基类析构函数是 protected 且 non-virtual,也可能掩盖资源管理问题,这类细节在大型继承体系里往往到上线才暴露。
= 0{}= 0virtual void foo() = 0 { }= 0class Shape {
public:
virtual double area() const = 0; // ✅ 纯虚函数
virtual ~Shape() = default;
};abstractnew ShapeShape s;error: cannot declare variable 's' to be of abstract type 'Shape'Shape* p = new Circle;Base::func()func()class Base {
public:
virtual void log() const = 0;
};
void Base::log() const { std::cout << "default log\n"; } // ✅ 合法定义
class Derived : public Base {
public:
void log() const override {
Base::log(); // ✅ 必须这样调用默认实现
// log(); // ❌ 错误:递归调用自身,非预期行为
}
};
interfacedeleteclass Interface {
public:
virtual ~Interface() = default; // ✅ 推荐:= default 更清晰
virtual void do_something() = 0;
};