类声明
来自cppreference.com
类(class)是用户定义类型,以 类说明符 定义,它在声明语法的 声明说明符序列 中出现。类说明符拥有下列语法:
类关键词 属性(可选) 类头名 final (可选) 基类子句(可选) { 成员说明 }
|
(1) | ||||||||
类关键词 属性(可选) 基类子句 { 成员说明 }
|
(2) | ||||||||
1) 定义具名类
2) 定义无名类
类关键词 | - | class,struct 和 union 之一。除了默认成员访问和默认基类访问之外,关键词 struct 和 class 是等同的。如果关键词是 union,那么声明引入一个联合体类型。 |
属性 | - | (C++11 起) 任意数量的属性,可以包含 alignas 指定符 |
类头名 | - | 所定义的类的名字,可以有限定 |
final
|
- | (C++11 起) 出现时,该类无法被派生 |
基类子句 | - | 一个或多个基类以及各自所用的继承模型的列表(见派生类) |
成员说明 | - | 访问说明符、成员对象及成员函数的声明和定义的列表(见下文) |
前置声明
下列形式的声明
类关键词 属性 标识符 ;
|
|||||||||
声明一个将稍后在此作用域定义的类类型。直到定义出现前,此类名具有不完整类型。这允许类之间互相引用:
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) 具有下列形式的成员声明
属性(可选) 声明说明符序列(可选) 成员声明符列表(可选) ;
|
|||||||||
属性 | - | (C++11 起) 任意数量的属性 |
声明说明符序列 | - | 说明符的序列。它只能在构造函数,析构函数,以及用户定义转换函数中省略。 |
成员声明符列表 | - | 与 初始化声明符列表相同,但额外允许位域定义、纯说明符和虚说明符(override 或 final ) (C++11 起),并且不允许直接非列表初始化语法。
|
这种声明可以声明静态及非静态的数据成员与成员函数、成员 typedef、成员枚举以及嵌套类。它也可以是友元声明。
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) 函数定义,同时声明并定义成员函数或者友元函数。成员函数定义之后的分号是可选的。所有定义于类体之内的函数自动为内联的,除非它们附着到具名模块 (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) {} // 构造函数定义 int operator()(size_t r, size_t c) const // 成员函数定义 { return data[r * C + c]; } int& operator()(size_t r, size_t c) // 另一个成员函数定义 { return data[r * C + c]; } };
3) 访问说明符
public:
、protected:
和 private:
:
class S { public: S(); // 公开的构造函数 S(const S&); // 公开的复制构造函数 virtual ~S(); // 公开的虚析构函数 private: int* ptr; // 私有的数据成员 };
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 必须是浮点数"); };
6) 成员模板声明:
struct S { template<typename T> void f(T&& n); template<class CharT> struct NestedS { std::basic_string<CharT> s; }; };
(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 起) |
(C++20 起) |
局部类
类声明可以在函数体内出现,此时它定义局部类。这种类的名字只存在于函数作用域中,且无法在函数外访问。
- 局部类不能拥有静态数据成员
- 局部类的成员函数无链接
- 局部类的成员函数必须完全在类体内定义
- 除闭包类型以外的 (C++14 起)局部类不能拥有成员模板
- 局部类不能拥有友元模板
- 局部类不能在类定义内定义友元函数
- 函数(包括成员函数)内的局部类的外围函数能访问的名字也可以被该局部类访问
|
(C++11 前) |
运行此代码
#include <vector> #include <algorithm> #include <iostream> 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 << ' '; }
输出:
3 2 1
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 1693 | C++98 | 成员声明不能为空 | 允许空声明 |