抽象类

来自cppreference.com
< cpp‎ | language

定义不能被实例化,但可以用作基类的抽象类型。

语法

纯虚 函数是它的声明符拥有下列语法的虚函数

声明符 虚说明符(可选) = 0

在这里序列 = 0 被称作 纯说明符,且要么紧跟 声明符 之后,要么紧跟可选的 虚说明符overridefinal)之后出现。

纯说明符 不能在成员函数定义或友元声明中出现。

struct Base
{
    virtual int g();
    virtual ~Base() {}
};
 
struct A : Base
{
    // OK:声明三个成员虚函数,其中两个是纯虚函数
    virtual int f() = 0, g() override = 0, h();
 
    // OK:析构函数也可以是纯虚函数
    ~A() = 0;
 
    // 错误:函数定义中的纯说明符
    virtual int b() = 0 {}
};

抽象类(abstract class) 是定义或继承了至少一个最终覆盖函数纯虚 函数的类。

解释

抽象类用于表示一般性概念(例如 Shape、Animal 等),它可以用作具体类(例如 Circle、Dog 等)的基类。

除了作为从其派生的类的基类子对象之外,不能创建抽象类的对象,且不能声明抽象类类型的非静态数据成员。

抽象类型不能用作形参类型,函数返回类型,或显式转换的类型(注意,这是在函数定义点和函数调用点检查的,因为在函数声明点其形参和返回类型可以是不完整类型)。

可以声明到抽象类的指针或引用。

struct Abstract
{
    virtual void f() = 0;  // 纯虚
}; // "Abstract" 是抽象的
 
struct Concrete : Abstract
{
    void f() override {}   // 非纯虚
    virtual void g();      // 非纯虚
}; // "Concrete" 不是抽象的
 
struct Abstract2 : Concrete
{
    void g() override = 0; // 纯虚覆盖函数
}; // "Abstract2" 是抽象的
 
int main()
{
    // Abstract a;   // 错误:不能创建抽象类的对象
    Concrete b;      // OK
    Abstract& a = b; // OK:到抽象基类的引用
    a.f();           // 虚派发到 Concrete::f()
    // Abstract2 a2; // 错误:不能创建抽象类的对象(g() 的最终覆盖函数是纯虚的)
}

可以为纯虚函数提供定义(而且如果纯虚函数是析构函数就必须提供):派生类的成员函数可以自由地用有限定的函数标识调用抽象基类的纯虚函数。此定义必须在类体之外提供(函数声明的语法不允许纯说明符 = 0 和函数体一起出现)。

从抽象类的构造函数或析构函数中进行纯虚函数的虚调用是未定义行为(无论纯虚函数是否拥有定义)。

struct Abstract
{
    virtual void f() = 0; // 纯虚
    virtual void g() {}  // 非纯虚
 
    ~Abstract()
    {
        g();           // OK:调用 Abstract::g()
        // f();        // 未定义行为
        Abstract::f(); // OK:非虚调用
    }
};
 
// 纯虚函数的定义
void Abstract::f() { std::cout << "A::f()\n"; }
 
struct Concrete : Abstract
{
    void f() override
    {
        Abstract::f(); // OK:调用纯虚函数
    }
 
    void g() override {}
 
    ~Concrete()
    {
        g(); // OK:调用 Concrete::g()
        f(); // OK:调用 Concrete::f()
    }
};

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
CWG 390 C++98 可以调用未定义的纯虚析构函数 此时需要析构函数定义
CWG 2153 C++98 纯说明符 可以在友元声明中出现 已禁止

参阅