Namespaces
Variants

Class declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Class/struct types
Union types

Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

类是由类说明符定义的用户定义类型,它出现在 声明 语法的 decl-specifier-seq 中。

目录

语法

类说明符具有以下语法:

类关键字 属性  (可选) 类头名称 类属性说明  (可选) 基句  (可选)
{ 成员声明 }
(1)
类关键字 属性  (可选) 基句  (可选)
{ 成员声明 }
(2)
1) 命名类定义
2) 未命名类定义
class-key - class struct union 三者之一。 class struct 关键字仅在默认 成员访问权限 和默认 基类访问权限 方面存在差异。若使用 union ,则声明引入一个 联合体类型
attr - (C++11 起) 任意数量的 属性 ,可包含 alignas 说明符
class-head-name - 正在定义的类名,可选择进行 限定修饰
class-property-specs - 以下说明符的列表,每个说明符在序列中最多出现一次。
说明符 作用
final
(C++11 起)
指定该类 不可被继承
trivially_relocatable_if_eligible
(C++26 起)
标记该类在符合条件时可平凡重定位
replaceable_if_eligible
(C++26 起)
标记该类在符合条件时可替换
base-clause - 一个或多个基类及其继承模式的列表(参见 派生类
member-specification - 访问说明符、成员对象及成员函数声明与定义的列表( 参见下文

前置声明

以下形式的声明

类关键字 属性 标识符 ;

声明一个稍后将在当前作用域中定义的类类型。在定义出现之前,该类名具有 不完整类型 。这允许类之间相互引用:

class Vector; // 前向声明
class Matrix
{
    // ...
    friend Vector operator*(const Matrix&, const Vector&);
};
class Vector
{
    // ...
    friend Vector operator*(const Matrix&, const Vector&);
};

如果某个源文件仅使用类的指针和引用,这将有助于减少 #include 依赖:

// 在 MyStruct.h 文件中
#include <iosfwd> // 包含 std::ostream 的前置声明
struct MyStruct
{
    int value;
    friend std::ostream& operator<<(std::ostream& os, const S& s);
    // 定义在 MyStruct.cpp 文件中提供,该文件使用 #include <ostream>
};

如果在局部作用域中出现前向声明,它会 隐藏 先前声明的类、变量、函数以及可能出现在外围作用域中的所有同名声明:

struct s { int a; };
struct s; // 无实际作用(s 已在当前作用域内定义)
void g()
{
    struct s; // 新局部结构体 s 的前向声明
              // 这将隐藏全局结构体 s 直至当前块结束
    s* p; // 指向局部结构体 s 的指针
    struct s { char* p; }; // 局部结构体 s 的定义
}

请注意,新的类名也可能通过 详细类型说明符 引入,该说明符作为其他声明的一部分出现,但前提是 名称查找 无法找到先前声明的同名类。

class U;
namespace ns
{
    class Y f(class T p); // 声明函数 ns::f 并声明 ns::T 和 ns::Y
    class U f(); // U 指向 ::U
    // 可以使用指向 T 和 Y 的指针及引用
    Y* p;
    T* q;
}

成员声明

成员规范,即类定义的 主体 ,是由花括号括起的任意数量以下元素的序列:

1) 形式如下的成员声明
attr  (可选) decl-specifier-seq  (可选) member-declarator-list  (可选) ;
attr - (C++11 起) 任意数量的 属性
decl-specifier-seq - 说明符 序列。仅在构造函数、析构函数和用户定义类型 转换函数 的声明中可选
member-declarator-list - 类似于 初始化声明符列表 ,但额外允许 位域声明 纯说明符 和虚函数说明符( override final (C++11 起) ,且不允许 直接非列表初始化语法

此声明可以声明 静态 和非静态 数据成员 成员函数 、成员 类型别名 、成员 枚举 以及 嵌套类 。也可以是 友元声明

class S
{
    int d1;             // 非静态数据成员
    int a[10] = {1, 2}; // 带初始化器的非静态数据成员 (C++11)
    static const int d2 = 1; // 带初始化器的静态数据成员
    virtual void f1(int) = 0; // 纯虚成员函数
    std::string d3, *d4, f2(int); // 两个数据成员和一个成员函数
    enum { NORTH, SOUTH, EAST, WEST };
    struct NestedS
    {
        std::string s;
    } d5, *d6;
    typedef NestedS value_type, *pointer_type;
};
2) 函数定义,同时声明并定义 成员函数 友元函数 。成员函数定义后的分号是可选的。所有在类体内定义的函数都会自动成为 inline ,除非它们隶属于某个 命名模块 (since C++20)
class M
{
    std::size_t C;
    std::vector<int> data;
public:
    M(std::size_t R, std::size_t C) : C(C), data(R*C) {} // constructor definition
    int operator()(std::size_t r, std::size_t c) const // member function definition
    {
        return data[r * C + c];
    }
    int& operator()(std::size_t r, std::size_t c) // another member function definition
    {
        return data[r * C + c];
    }
};
3) 访问说明符 public: , protected: , 和 private:
class S
{
public:
    S();          // public constructor
    S(const S&);  // public copy constructor
    virtual ~S(); // public virtual destructor
private:
    int* ptr; // private data member
};
4) using声明
class Base
{
protected:
    int d;
};
class Derived : public Base
{
public:
    using Base::d;    // 将Base的受保护成员d设为Derived的公开成员
    using Base::Base; // 继承所有基类的构造函数(C++11)
};
5) static_assert 声明:
template<typename T>
struct Foo
{
    static_assert(std::is_floating_point<T>::value, "Foo<T>: T must be floating point");
};
6) 成员模板声明
struct S
{
    template<typename T>
    void f(T&& n);
    template<class CharT>
    struct NestedS
    {
        std::basic_string<CharT> s;
    };
};
7) 别名声明
template<typename T>
struct identity
{
    using type = T;
};
(C++11 起)
8) 成员类模板的 推导指引
struct S
{
    template<class CharT>
    struct NestedS
    {
        std::basic_string<CharT> s;
    };
    template<class CharT>
    NestedS(std::basic_string<CharT>) -> NestedS<CharT>;
};
(C++17 起)
9) using-enum声明
enum class color { red, orange, yellow };
struct highlight
{
    using enum color;
};
(自 C++20 起)

局部类

类声明可以出现在函数体内,这种情况下它定义的是 局部类 。此类名称仅存在于函数作用域内,在外部无法访问。

  • 局部类的成员只能在该类的定义中声明,但 嵌套类 成员还可以在该类最近的封闭 块作用域 中声明。
  • 局部类内部嵌套的类同样是局部类。
  • 局部类不能拥有静态数据成员。
  • 局部类的成员函数没有链接属性。
  • 局部类的成员函数必须在类体内完整定义。
  • 局部类 (除 闭包类型 外) (C++14 起) 不能拥有成员模板。
  • 局部类不能拥有 友元模板
  • 局部类不能在类定义内定义 友元函数
  • 函数(包括成员函数)内部的局部类可以访问与外围函数相同的名称。
  • 局部类不能用作模板参数。
(C++11 前)

#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
    std::vector<int> v{1, 2, 3};
    struct Local
    {
        bool operator()(int n, int m)
        {
            return n > m;
        }
    };
    std::sort(v.begin(), v.end(), Local()); // 自 C++11 起
    for (int n : v)
        std::cout << n << ' ';
    std::cout << '\n';
}

输出:

3 2 1

关键词

class struct union

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 适用标准 发布时行为 正确行为
CWG 1693 C++98 成员声明不能为空 允许空声明
CWG 1930 C++98 decl-specifier-seq 包含存储类说明符或cv限定符时,
member-declarator-list 可为空
该列表必须非空
CWG 2890 C++98 未明确嵌套类的成员可在何处声明 已明确说明

参见

C 文档 关于 结构体声明