在 C++ 中,关键字 struct 和 class 几乎意味着同样的事情,像以下这样的代码是完全合法的:
struct Drawable {
virtual void draw() = 0;
virtual ~Drawable() = default;
};
class Painting : public Drawable {
void draw() override;
}
以下是一些需要注意的细节:
C++ 并没有像某些其他语言那样的 abstract 关键字。在 C++ 中,抽象成员函数是虚函数(virtual),并且使用 = 0 来代替函数定义。virtual 关键字表示该函数可以在派生类中被重写(specialized)。= 0 的部分本质上表示该函数必须重写。当重写虚函数时,通常称之为“覆盖”(overriding)该函数。那些必须被覆盖的函数称为纯虚函数(pure virtual functions)。
可以为抽象成员函数提供定义:虽然不常见,但这是可能的。这在某些情况下可能很有用,例如基类希望提供某个服务的默认实现,但仍然要求派生类至少考虑是否提供自己的实现。下面是一个示例:
#include <iostream>
struct X { virtual int f() const = 0; };
int X::f() const { return 3; }
struct D : X { int f() const override {
return X::f() + 1; }
};
void g(X &x) { std::cout << x.f() << '\n'; }
int main() {
D d;
// X x; // 非法:X 拥有纯虚成员函数
g(d);
}
C++ 类具有析构函数,用于处理对象到达其生命周期终点时的行为。与许多其他流行语言不同,C++ 中的自动变量和静态对象具有确定性的生命周期,高效地使用析构函数是这门语言的一种惯用法。在多态类(即至少有一个虚函数的类)中,通常应提供一个虚析构函数(例如这里的 virtual ~Drawable()),以确保在如下情形中,通过指针 p 这样的间接方式销毁对象时,实际销毁的是所指向的对象(如 Painting),而不是指针静态类型所表示的对象(如 Drawable):
//
// 以下代码假设 Painting 是 Drawable 的一个公有派生类,
// 如本节前面所建议的那样
//
Drawable *p = new Painting;
// ...
delete p; // <-- here
一个类可以从一个结构体派生,同样,一个结构体也可以从一个类派生,在结构上等价。主要区别在于:对于结构体,默认的继承方式是公有(public)的(不过可以改为受保护的或私有的),其成员也是如此;而对于类,默认的继承方式和成员访问权限都是私有(private)的(但同样可以更改)。
顺便指出,在 C++ 中,基类中的成员函数带有访问限定符(例如 Drawable::draw() 是 public)的情况下,派生类中的同名函数也可以有不同的访问限定符(例如 Painting::draw() 是 private),这完全允许。而一些其他流行的编程语言则不允许这种做法。