1 类型相关:1.1 类型;1.2 类型修饰;1.3 存储类型;1.4 类型转换;
2 运算符替代写法;3 流程控制;4 异常处理; 5 类相关; 6 其它 7 C++运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
alignas(since C++11) alignof(since C++11) and && and_eq &= atomic_cancel(TM TS) atomic_commit(TM TS) atomic_noexcept(TM TS) auto(1) bitand & bitor | char8_t(since C++20) char16_t(since C++11) char32_t(since C++11) class(1) |
compl ~ concept(since C++20) consteval(since C++20) constexpr(since C++11) constinit(since C++20) co_await(since C++20) co_return(since C++20) co_yield(since C++20) decltype(since C++11) default(1) delete(1) |
export(1)(3) extern(1) inline(1) mutable(1) noexcept(since C++11) not ! not_eq != nullptr(since C++11) or || |
or_eq |= reflexpr(reflection TS) register(2) requires(since C++20) sizeof(1) static_assert(since C++11) struct(1) synchronized(TM TS) |
thread_local(since C++11) using(1) xor ^ xor_eq ^= |
(1) meaning changed or new meaning added in C++11.
(2) meaning changed in C++17.
(3) meaning changed in C++20.
(TM TS) Transactional Memory Technical Specification ISO/IEC TS 19841:2015
1 2 |
Strorage | type-specifier | (类型说明符) | type(类型) | 类型相关 |
Class | qualifier(限定符) | modifier(修饰符) | ||
static | const | short | char | sizeof |
register | volatile | long | int | typedef |
extern | restrict(C11) | signed | float | |
auto | unsigned | double | ||
struct | [] | |||
union | * | |||
enum | void | |||
控制结构相关 | ||||
if | switch | while | break | return |
else | case | for | continue | |
default | do | goto | ||
类相关 | 类型相关1 | 异常处理 | 运算符替代写法 | others |
class | bool | try | and | new |
public | char8_t(20) | throw | and_eq | delete |
protected | char16_t(11) | catch | bitand | alignas(11) |
private | char32_t(11) | noexcept(11) | bitor | alignof(11) |
friend | consteval(20) | static_assert(11) | compl | concept(20) |
this | constexpr(11) | 类型相关2 | not | requires(20) |
virtual | decltype(11) | using | not_eq | export |
explicit | wchar_t | namespace | or | asm |
mutable | static_cast | TRUE | or_eq | thread_local(11) |
operator | const_cast | FALSE | xor | co_await(20) |
dynamic_cast | template | xor_eq | co_return(20) | |
reinterpret_cast | typename | co_yield(20) | ||
nullptr(11) | typeid |
允许你往更大的字节数去对齐,但不能指定比本身还小的对齐。
alignof:查看对齐的字节数。
#include <iostream> using namespace std; int main() { struct alignas(1) Point { int a; char b; }p; cout<<alignof(p)<<endl; // 4 cout<<sizeof(p)<<endl; // 8 #pragma pack(push) #pragma pack(1) struct Point2 { int a; char b; }p2; #pragma pop(pop) cout<<alignof(p2)<<endl; // 1 cout<<sizeof(p2)<<endl; // 5 return 0; }
asm (assembly)
允许在 C++ 程序中嵌入汇编代码。
int a=5; int b=4; __asm { mov eax, a; // 可以像这样直接对eax赋值 mov ebx, b; add eax, ebx mov a, eax; // 可以直接将eax的值赋给a }
事务性内存(transactional memory)是在事务中结合语句组的并发同步机制,事务具有
原子性(atomic)(要么语句全部发生,要么全部不发生)
隔离性(isolated)(事务中的语句不会观察到另一事务写入一半,即使它们并行执行)
注意,在事务内和事务外访问变量而无其他的外部同步,是数据竞争。
如同在一个全局锁下执行复合语句:程序中的所有最外层同步块都以一个单独的全序执行。在该顺序中,每个同步块的结尾同步于(synchronize with)下个同步块的开始。内嵌于其他同步块的同步块没有特殊语义。
同步块不是事务(不同于后面的原子块),并可以调用事务不安全的函数。
#include <iostream> #include <vector> #include <thread> int f() { static int i = 0; synchronized { // 开始同步块 std::cout << i << " -> "; ++i; // 每次调用 f() 都获得 i 的唯一值 std::cout << i << '\n'; return i; // 结束同步块 } } int main() { std::vector<std::thread> v(10); for(auto& t: v) t = std::thread([]{ for(int n = 0; n < 10; ++n) f(); }); for(auto& t: v) t.join(); } 输出: 0 -> 1 1 -> 2 2 -> 3 ... 99 -> 100
以任何方式(抵达结尾,执行 goto、break、continue 或 return,或抛出异常)离开同步块都会退出该块,而若所退出的块是外层块,则这在单一全序中同步于下个同步块。若使用 std::longjmp 退出同步块则行为未定义。
不允许用 goto 或 switch 进入同步块。
尽管同步块如同在一个全局锁下执行,我们仍然期待各实现检验每个块内的代码,并为事务安全代码使用乐观并发(在可用时以硬件事务性内存为后盾),为非事务安全代码使用最小锁定。当同步块调用非内联函数时,除非该函数声明为 transaction_safe(见下文)或使用 [[optimize_for_synchronized]] 属性(见下文),否则编译器可能必须放弃推测执行(spculative execution),并在整个调用周围持有一个锁。
atomic_noexcept 复合语句 atomic_cancel 复合语句 atomic_commit 复合语句
1) 若抛出异常,则调用 std::abort
2) 若抛出异常,则调用 std::abort,除非该异常是用于事务取消的异常之一(见后述),这种情况下事务被取消(cancel):程序中所有由该原子块的各操作的副作用所修改的内存位置的值,被还原到该原子块的执行开始时它们曾拥有的值,而异常照常持续栈回溯。
3) 若抛出异常,则正常地提交事务。
用于 atomic_cancel 块中的事务取消的异常有 std::bad_alloc、std::bad_array_new_length、std::bad_cast、std::bad_typeid、std::bad_exception、std::exception 和所有从它派生的标准库异常,以及特殊异常类型 std::tx_exception
不允许原子块中的 复合语句 执行任何非 transaction_safe 的表达式或语句,或调用非 transaction_safe 的函数(这是编译时错误)。
// 每次调用 f() 都取得 i 的唯一值,即使以并行进行 int f() { static int i = 0; atomic_noexcept { // 开始事务 // printf("before %d\n", i); // 错误:不能调用非事务安全的函数 ++i; return i; // 提交事务 } }
以除异常之外的任何方式(抵达结尾、goto、break、continue、return)离开原子块时,将提交事务。若用 std::longjmp 退出原子块则行为未定义。
事务安全的函数
可在函数声明中用关键词 transaction_safe 将其显式声明为事务安全。
extern volatile int * p = 0; struct S { virtual ~S(); }; int f() transaction_safe { int x = 0; // OK:非 volatile p = &x; // OK:指针非 volatile int i = *p; // 错误:通过 volatile 泛左值读取 S s; // 错误:调用不安全的析构函数 } int f(int x) { // 隐式事务安全 if (x <= 0) return 0; return x + f(x-1); }
若通过指向事务安全函数的引用或指针调用非事务安全的函数,则行为未定义。
auto(自动,automatic)
① 是存储类型标识符,表明变量"自动"具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型。(until C++11)
② 对于变量,指定将从其初始值自动推断声明的变量的类型。(since C++11)
③ 对于函数,指定将从其返回语句推导返回类型。(since C++14)
④ 对于非类型模板参数,指定将从参数派生类型。(since C++17)
bool(布尔)
bool表示逻辑类型,其值可选为 true(真)或者 false(假)。C++ 中的 bool 类型可以和 int 混用,具体来说就是 0 代表 false,非 0 代表 true。bool 类型常用于条件判断和函数返回值。
break(中断、跳出)
用在switch语句或者循环语句中。程序遇到 break 后,即跳过该程序段,继续后面的语句执行。
通常和if、case语句配合使用。
用于 switch 语句中,用于判断不同的条件类型。
标签语句,提供执行入口,符合条件时进入,直至break或default:或switch结束。
catch 和 try 语句一起用于异常处理。在try中(包括其调用的函数)抛出异常,catch按抛出的数据类型匹配异常。
char(字符,character)
C++ 中的基本数据结构,其值一般为 0~255 的 int。这 256 个字符对应着 256 个 ASCII 码。char 类型的数据需要用单引号 ' 括起来。
class(类)是 C++ 面向对象设计的基础。
① 使用 class 关键字声明一个类。
② 用于声明一个强类型的enum。(since C++11)
enum class Color { red, green = 20, blue };
③ 在模板函数或模板类中,用来声明一个类型参数,此时可用typename代替。
类模板,函数模板,以及非模板函数(常为类模板的成员),可以与约束(constraint)关联,它指定对模板实参的一些要求,这些要求可被用于选择最恰当的函数重载和模板特化。
这种要求的具名集合被称为概念(concept)。每个概念都是谓词,于编译时求值,并成为以之作为一项约束的模板接口的一部分。
template < 模板形参列表 > concept 概念名 = 约束表达式;
限制只能打印int类型
template <class T> concept IntLimit = std::is_same_v<int, std::decay_t<T>>; //制约T塌陷后的类型必须与int相同 template <IntLimit T> void print_int(T v) { std::cout << v << std::endl; } int main() { print_int(1); print_int(1.0); //error C2672: “print_int”: 未找到匹配的重载函数 }ref
const(常量的,constant)
表示所修饰的对象或变量不能被改变。
① 修饰函数时,该函数不能改变在该函数外面声明的变量也不能调用任何非const函数。在函数的声明与定义时都要加上const,放在函数参数列表的最后一个括号后。
② 用 const 声明一个变量,意味着该变量就是一个带类型的常量,可以代替 #define,且比 #define 多一个类型信息,且它执行内链接,可放在头文件中声明;但在 C 中,其声明则必须放在源文件(即 .C 文件)中,在 C 中 const 声明一个变量,除了不能改变其值外,它仍是一具变量。如:
const double pi(3.14159); 或 const double pi = 3.14159;
用于修饰指针时要考虑修饰的是指针本身还是指针所指向的内存单元:
const int* p; // 修饰int,表示不能用p更新其指向的内存单元 int const* p; // 同上 int* const p; // 修饰p,表示指针p本身是常量,不能被更新 // 区别其实就是以*为分隔,看const是在*前还在*后 const int* const p; // 综合上述,两者同为const
区别的原则:const是在*的左边还是右边。左边修饰指向的类型,表示不能用指针的解引用去更新指向的值。右边表示修饰指针本身,表示指针本身是一个常量,不能再指向其它内存单元(变量)。
consteval - 指定函数是立即函数(immediate function),即每次调用该函数必须产生编译时常量。
consteval 说明符声明函数或函数模板为立即函数,即该函数的每次潜在求值的调用(即不求值语境外的调用)必须(直接或间接)产生编译时常量表达式。它不可应用于析构函数、分配函数或解分配函数。
consteval 说明符蕴含 inline 。同一声明说明符序列中允许出现至多一个 constexpr 、 consteval 及 constinit 说明符。若某个函数或函数模板的任何声明含有 consteval 说明符,则该函数或函数模板的所有声明必须均含该说明符。
立即函数是 constexpr 函数,而且只要情况符合,必须满足适用于 constexpr 函数或 constexpr 构造函数的要求。
当最内层的非块作用域不是某个立即函数的函数形参作用域时,对立即函数的潜在求值的调用必须产生常量表达式;这种调用称为立即调用( immediate invocation )。
consteval int sqr(int n) { return n*n; } constexpr int r = sqr(100); // OK int x = 100; int r2 = sqr(x); // 错误:调用不产生常量 consteval int sqrsqr(int n) { return sqr(sqr(n)); // 在此点非常量表达式,但是 OK } constexpr int dblsqr(int n) { return 2*sqr(n); // 错误:外围函数并非 consteval 且 sqr(n) 不是常量 }
constexpr 声明说明符 (C++11 起)
constexpr if 语句 (C++17 起)
constexpr - 指定变量或函数的值可在常量表达式中出现
constexpr 说明符声明 可以在编译时求得函数或变量的值。 然后这些变量和函数(若给定了合适的函数实参)即可用于仅允许编译时常量表达式之处。用于对象或非静态成员函数 (C++14 前)声明的 constexpr 说明符蕴含 const。用于函数声明的 constexpr 说明符或 static 成员变量 (C++17 起)蕴含 inline。若函数或函数模板的任何声明拥有 constexpr 说明符,则每个声明必须都含有该说明符。
以 if constexpr 开始的语句被称为constexpr if 语句。
在 constexpr if 语句中,条件 的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。
constinit - 断言变量拥有静态初始化,即零初始化与常量初始化,否则程序非良构。
constinit 说明符声明拥有静态或线程存储期的变量。若变量以 constinit 声明,则其初始化声明必须应用 constinit 。若以 constinit 声明的变量拥有动态初始化,则程序非良构。若在初始化声明点无可达的 constinit 声明,则程序非良构,不需要诊断。
constinit 不能和 constexpr 或 consteval 一同使用。声明的变量为引用时, constinit 等价于 constexpr 。声明的变量为对象时, constexpr 强制对象必须拥有静态初始化和常量析构,并使对象有 const 限定,然而 constinit 不强制常量析构和 const 限定。结果是拥有 constexpr 构造函数且无 constexpr 析构函数的类型(例如 std::shared_ptr
const char *g() { return "dynamic initialization"; } constexpr const char *f(bool p) { return p ? "constant initializer" : g(); } constinit const char *c = f(true); // OK constinit const char *d = f(false); // 错误
constinit 亦能用于非初始化声明,以告知编译器 thread_local 变量已被初始化。
extern thread_local constinit int x; int f() { return x; } // 无需检查防卫变量
const_cast<type_to> (expression)
该运算符用来修改类型的 const 或 volatile 属性。除了 const 或 volatile 修饰之外, type_to 和 expression 的类型是一样的。常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
int i = 3; // i is not declared const const int& rci = i; const_cast<int&>(rci) = 4; // OK: modifies i
协程是能暂停执行以在之后恢复的函数。协程是无栈的:它们通过返回到调用方暂停执行,并且从栈分离存储恢复所要求的数据。这允许编写异步执行的顺序代码(例如不使用显式的回调来处理非阻塞 I/O),还支持对惰性计算的无限序列上的算法及其他用途。
若函数的定义做下列任何内容之一,则它是协程:
用 co_await 运算符暂停执行,直至恢复
task<> tcp_echo_server() { char data[1024]; for (;;) { std::size_t n = co_await socket.async_read_some(buffer(data)); co_await async_write(socket, buffer(data, n)); } } // 用关键词 co_yield 暂停执行并返回一个值 generator<int> iota(int n = 0) { while(true) co_yield n++; } // 用关键词 co_return 完成执行并返回一个值 lazy<int> f() { co_return 7; }
每个协程必须具有能够满足一组要求的返回类型,标注于下。
协程不能使用变长实参,普通的 return 语句,或占位符返回类型(auto 或 Concept)。
constexpr 函数、构造函数、析构函数及 main 函数 不能是协程。
协程状态由非数组 operator new 在堆上分配。
continue(继续)
用于循环结构。它使程序跳过代码段后部的部分,与 break 不同的是,continue 不是进入代码段后的部分执行,而是重新开始新的循环。因而它是"继续循环"之意,不是 break(跳出)。常和if语句配合使用。
default(默认、缺省)
① 用于 switch 语句。当 switch 所有的 case 都不满足时,将进入 default 执行。default 只能放在 switch 语句所有的 case 之后,并且是可选的。
② explicitly-defaulted function definition: as an explicit instruction to the compiler to generate special member function(since C++11) or a comparison operator (since C++20) for a class.
delete(删除)
① 释放程序动态申请的内存空间。delete 后面通常是一个指针或者数组 [],并且只能 delete 通过 new 关键字申请的指针,否则会发生段错误。
Complex* pc = new Complex(1,2); // ... delete pc; // 编译器转化为: Complex::~Complex(pc); // ① 调用析构函数 operator delete(pc); // ② 释放内存,内部调用free(pc)
② deleted functions (since C++11)
作为运算符式的函数名,operator delete,operator delete[]
do-while是一类循环结构。与while循环不同,do-while循环保证至少要进入循环体一次。
double(双精度)
C++ 中的基本数据类型,以双精度形式存储一个浮点数。(IEEE-754)
dynamic_cast(动态转换),允许在运行时刻进行类型转换,从而使程序能够在一个类层次结构安全地转换类型。dynamic_cast 提供了两种转换方式,把基类指针转换成派生类指针,或者把指向基类的左值转换成派生类的引用。
struct V { virtual void f() {}; // must be polymorphic to use runtime-checked dynamic_cast }; struct A : virtual V {}; struct B : virtual V { B(V* v, A* a) { // casts during construction (see the call in the constructor of D below) dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B* dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B } };
else 紧跟在 if 后面,用于对 if 不成立的情况的选择。
else与if按最近原则匹配,if后尽量不要省略else,避免悬挂。
enum(枚举,enumeration)
对于一个整型变量,如果只有几种有限的取值可能时,可以把这些值枚举出来,并用符号常量代替之。
通常与switch…case语句一起搭配使用。
explicit(显式的)
禁止单参数构造函数被用于自动型别转换,其中比较典型的例子就是容器类型。在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数。
① 为了访问其他编译单元(如另一代码文件)中的变量或对象,对普通类型(包括基本数据类、结构和类),可以利用关键字 extern,来使用这些变量或对象;但是对模板类型,则必须在定义这些模板类对象和模板函数时,使用标准 C++ 新增加的关键字 export(导出)。
Used to mark a template definition exported, which allows the same template to be declared, but not defined, in other translation units. (until C++11)
② Marks a declaration, a group of declarations, or another module as exported by the current module. (since C++20)
extern(外部的)
① 声明变量或函数为外部链接,即该变量或函数名在其它文件中可见。被其修饰的变量(外部变量)是静态分配空间的,即程序开始时分配,结束时释放。用其声明的变量或函数应该在别的文件或同一文件的其它地方定义(实现)。在文件内声明一个变量或函数默认为可被外部使用。
② 指定使用另一语言进行链接,这时需要与特定的转换符一起使用。目前仅支持 C 转换标记,来支持 C 编译器链接。使用这种情况有两种形式:
extern "C" 声明语句 extern "C" { 声明语句块 }
如.cpp文件调用.c中声明和定义的函数。
使用C编译器编译的库或要用C编译器编译的代码,如C99允许用变量做数组下标,也就是可以声明可变长度的数组。
③ explicit template instantiation declaration (or "extern template")
extern template class|struct template-name < argument-list > ; (2) (since C++11)
false(假的),C++ 的基本数据结构 bool 类型的值之一。等同于 int 的 0 值。
float(浮点数)
C++ 中的基本数据类型,精度小于 double。(IEEE754)
① for 是 C++ 中的循环结构之一,一般用于重复次数确定的循环,在for()的()内写三个表达式(用分号“;”分隔,分别表示循环变量的初始化、取逻辑值的判断语句,循环变量更新语句),第一个语句可以写到for前面,第二、三个语句可以写到循环体中,但两个分号“;”都不能被省略。
② range-based for loop: as the declaration of the loop (since C++11)
friend(友元)声明友元关系。友元可以访问与其有 friend 关系的类中的 private/protected 成员,通过友元直接访问类中的 private/protected 成员的主要目的是提高效率。友元包括友元函数和友元类。
goto(转到)
用于无条件跳转到某一标号处开始执行。对应语言的jmp或jp系列条件跳转命令。在没有语句块和控制结构语句之前,通常通过goto语句实现跳转。往后跳可形成循环,往前跳可形成条件分支。
if(如果)
C++ 中的条件语句之一,可以根据后面的 bool 类型的值选择进入一个分支执行。
inline(内联)函数的定义将在编译时在调用处展开。inline 函数一般由短小的语句组成,可以提高程序效率。
int(整型,integer)
C++ 中的基本数据类型,用于表示整数,数据范围(内存单元数量)小于 long。
long(长整型,long integer)
C++ 中的基本数据类型,用于表示长整数。
mutable(易变的)
C++ 中一个不常用的关键字。只能用于类的非静态和非常量数据成员。由于一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,对象的状态也会随之发生变化。如果一个类的成员函数被声明为 const 类型,表示该函数不会改变对象的状态,也就是该函数不会修改类的非静态数据成员。但是有些时候需要在该类函数中对类的数据成员进行赋值,这个时候就需要用到 mutable 关键字。
namespace(命名空间)用于在逻辑上组织类,是一种比类大的结构。用于包裹变量、自定义类型声明、函数声明,形成一个定义域。
用于新建一个对象。new 运算符总是返回一个指针。
new、delete在C++中定义为操作符函数,用于动态分配堆上内存。C++编译器对new、delete的底层实现还是调用了malloc()和free(),但做了封装,且C++编译器对new、delete语句在编译时做了一些额外的工作。
Complex* pc = new Complex(1,2); // 在堆上new一个对象: // 编译器转化为: Complex *pc; void* mem = operator new(sizeof(Complex)); // ① 分配内存,内部调用malloc(n) pc = static_cast<Complex*>(mem); // ② 转型 pc->Complex::Copmlex(1,2); // ③ 调用构造函数
Complex* pc = new Complex(1,2); // 在堆上new一个对象: // 编译器转化为: Complex *pc; void* mem = operator new(sizeof(Complex)); // ① 分配内存,内部调用malloc(n) pc = static_cast<Complex*>(mem); // ② 转型 pc->Complex::Copmlex(1,2); // ③ 调用构造函数
noexcept 运算符进行编译时检查,若表达式声明为不抛出任何异常则返回 true。
它可用于函数模板的 noexcept 说明符中,以声明函数将对某些类型抛出异常,但不对其他类型抛出。
noexcept( 表达式 )
返回 bool 类型的纯右值。
noexcept (1)
noexcept(表达式) (2)
1) 与 noexcept ( true ) 相同
2) 若 表达式 求值为 true,则声明函数为不抛出任何异常。
noexcept 说明是函数类型的一部分,可以作为任何函数声明符的一部分出现。
noexcept 说明不是函数类型的一部分(正如同动态异常说明),而且只能在声明函数、变量、函数类型的非静态数据成员、函数指针、函数引用或成员函数指针时,以及在以上这些声明中声明类型为函数指针或函数引用的形参或返回类型时,作为 lambda 声明符或顶层函数声明符的一部分出现。它不能出现于 typedef 或类型别名声明中。
operator(操作符)
用于操作符重载。这是 C++ 中的一种特殊的函数。
operator op (1) operator type (2) operator new operator new [] (3) operator delete operator delete [] (4) operator "" suffix-identifier (5) (since C++11) operator co_await (6) (since C++20)
private(私有的)
C++ 中的访问控制符。被标明为 private 的字段只能在本类成员函数以及友元函数或友元类的成员函数中访问。
如果是B是private继承自A,则A的全部成员虽是A的一部分,但不能被访问(因为private部分只能由其成员函数访问,因为其private继承,成员函数也成了私有)。
protected(受保护的)
C++ 中的访问控制符。被标明为 protected 的字段只能在本类以及其继承类和友元中访问。
如果是B是protected继承自A,则A的public和protected成员在B中变成了protected。
public(公有的)
C++ 中的访问控制符。被标明为 public 的字段可以在任何类
register(寄存器)
声明的变量称着寄存器变量,在可能的情况下会直接存放在机器的寄存器中;但对 32 位编译器不起作用,当 global optimizations(全局优化)开的时候,它会做出选择是否放在自己的寄存器中;不过其它与 register 关键字有关的其它符号都对32位编译器有效。
reinpreter_cast<type_to> (expression)
type_to 必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
类模板,函数模板,以及非模板函数(常为类模板的成员),可以与约束(constraint)关联,它指定对模板实参的一些要求,这些要求可被用于选择最恰当的函数重载和模板特化。
这种要求的具名集合被称为概念(concept)。每个概念都是谓词,于编译时求值,并成为以之作为一项约束的模板接口的一部分。
约束是逻辑操作和操作数的序列,它指定对于模板实参的要求。它们可在 requires 表达式(见下文)中出现,也可直接作为概念的主体。
//限定只能调用存在name成员函数的类 class A { public: std::string_view name() const { return "A"; } }; class B { public: std::string_view class_name() const { return "B"; } }; template <typename T> concept NameLimit = requires(T a) { a.name(); //制约T的实例a必须要有name成员函数 }; template <NameLimit T> void print_name(T a) { std::cout << a.name() << std::endl; } int main() { A a; B b; print_name(a); print_name(b); //error C2672 : “print_name”: 未找到匹配的重载函数 } //限定只能调用返回值可以转换为std::string的函数 template <typename T> concept ReturnLimit = requires(T t) { {t()} -> std::convertible_to<std::string> ; //函数返回值必须可以转换为std::string std::is_function<T>; //T必须为函数 }; template <ReturnLimit T> void print_string(T func) { std::cout << func() << std::endl; } std::string str1() { return "123"; } constexpr const char* str2() { return "str2"; } std::basic_string<char8_t> str3() { return u8"str3"; } int main() { std::string t; t = std::string_view("213"); print_string(&str1); print_string(&str2); print_string(&str3); //error C2672: “print_string”: 未找到匹配的重载函数 return 0; }
return(返回)
用于在函数中返回值。程序在执行到 return 语句后立即返回,return 后面的语句无法执行到。
short(短整型,short integer)
C++ 中的基本数据类型,用于表示整数,数值范围(或内存单元大小)小于 int。
signed(有符号)
表明该类型是有符号数,和 unsigned 相反。数字类型(整型和浮点型)都可以用 signed 修饰。但默认就是 signed,所以一般不会显式使用。
由于 C++ 每种类型的大小都是由编译器自行决定的,为了增加可移植性,可以用 sizeof 运算符获得该数据类型占用的字节数。
static(静态的)
① 存续期:静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为 0,使用时可改变其值。
unsigned int LCG16(unsigned int seed = 5323) // our PRNG { static unsigned int state{ seed }; // Generate the next number // Due to our use of large constants and overflow, it would be // hard for someone to casually predict what the next number is // going to be from the previous one. state = 8253729 * state + 2396403; // first we modify the state return state % 32768; // then we use the new state // to generate the next number in the sequence }
② 文件链接性:静态变量或静态函数,只有本文件内的代码才可访问它,它的名字(变量名或函数名)在其它文件中不可见。因此也称为"文件作用域"。
③ 静态成员:在 C++ 类的成员变量被声明为 static(称为静态成员变量),意味着它被该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。类的静态成员变量必须在声明它的文件范围内进行初始化才能使用,private 类型的也不例外。
tatic_assert 声明
进行编译时断言检查。
static_assert ( 布尔常量表达式 , 消息 ) (C++11 起)
static_assert ( 布尔常量表达式 ) (C++17 起)
static_assert 声明可以出现在命名空间和块作用域中(作为块声明),也可以在类体中(作为成员声明)。
若 布尔常量表达式 返回 true,则此声明无效果。否则发布编译时错误,而若存在 消息,则诊断消息中包含其文本。
static_cast<type_to>(expression)
该运算符把 expression 转换为 type_to 类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
① 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
② 用于基本数据类型之间的转换,如把 int 转换成 char,把 int 转换成 enum。这种转换的安全性也要开发人员来保证。
③ 把空指针转换成目标类型的空指针。
④ 把任何类型的表达式转换成void类?
注意 static_cast 不能转换掉 expression 的 const、volitale、或者 __unaligned 属性。
struct(结构)
类似于 class 关键字,与 C 语言兼容(class 关键字是不与 C 语言兼容的),可以实现面向对象程序设计。
enum struct|class name { enumerator = constexpr , enumerator = constexpr , ... }
switch(转换)
类似于 if-else-if 语句,是一种多分枝语句。它提供了一种简洁的书写,并且能够生成效率更好的代码。但是,switch 后面的判断只能是int(char也可以,但char本质上也是一种int类型)。switch 语句最后的 default 分支是可选的。
template(模板)
C++ 中泛型机制的实现。
关键字this是一个prvalue(纯右值)表达式,其值是调用成员函数的对象的地址。
#include<iostream> class T { int x; public: void foo() // implicitly including a pointer: void foo(T* this) { x = 6; // same as this->x = 6; this->x = 5; // explicit use of this-> std::cout<<x; } }; void main() { T obj; obj.foo(); // obj. as obj* this; }
#include<iostream> class T { int x; public: void foo() // implicitly including a pointer: void foo(T* this) { x = 6; // same as this->x = 6; this->x = 5; // explicit use of this-> std::cout<<x; } }; void main() { T obj; obj.foo(); // obj. as obj* this; }
this指针有时可以显式使用,有时不能。
thread_local 关键词仅允许搭配声明于命名空间作用域的对象、声明于块作用域的对象及静态数据成员。它指示对象具有线程存储期。它能与 static 或 extern 结合,以分别指定内部或外部连接(但静态数据成员始终拥有外部链接),但额外的 static 不影响存储期。
线程(thread)存储期。这类对象的存储在线程开始时分配,并在线程结束时解分配。每个线程拥有其自身的对象实例。只有声明为 thread_local 的对象拥有此存储期。 thread_local 能与 static 或 extern 一同出现,它们用于调整连接。
throw(抛出)
用于实现 C++ 的异常处理机制,可以通过 throw 关键字"抛出"一个异常。
try或try调用的函数中throw exception。
true(真的)
C++ 的基本数据结构 bool 类型的值之一。等同于 int 的非 0 值。
try(尝试)
用于实现 C++ 的异常处理机制。可以在 try 中调用可能抛出异常的函数,然后在 try 后面的 catch 中捕获并进行处理。
try或try调用的函数中throw exception。
try { std::string("abc").substr(10); // throws std::length_error } catch (const std::exception& e) { // reference to the base of a polymorphic object std::cout << e.what(); // information from length_error printed }
typedef(类型定义,type define)
typedef typeDeclaration newTypeName;
类型说明定义了一个数据类型的新名字而不是定义一种新的数据类型。定义名表示这个类型的新名字。
// simple typedef typedef unsigned long ulong; // type: unsigned long,definition: ulong // common C idiom to avoid having to write "struct S" typedef struct { int a; int b; } S, *pS, stArr[3]; // function pointer typedef void (*pf)(int,double); // std::add_const, like many other metafunctions, use member typedefs template< class T> struct add_(const { typedef const T type; // 型别定义 };
指出指针或引用指向的对象的实际派生类型。
Queries information of a type.
Used where the dynamic type of a polymorphic object must be known and for static type identification.
typeid( type ) typeid( expression )
The header
string str=typeid(变量名或对象名).name();
在模板函数或模板类中,typename(类型名字)关键字告诉编译器把一个特殊的名字解释成一个类型。在下列情况下必须对一个 name 使用 typename 关键字:
1 一个唯一的name(可以作为类型理解),它嵌套在另一个类型中的。
2 依赖于一个模板参数,就是说:模板参数在某种程度上包含这个name。当模板参数使编译器在指认一个类型时产生了误解。
In a template declaration, typename can be used as an alternative to class to declare type template parameters and template template parameters (since C++17).
Inside a declaration or a definition of a template, typename can be used to declare that a dependent qualified name is a type.
Inside a declaration or a definition of a template, (until C++11) typename can be used before a non-dependent qualified type name. It has no effect in this case.
Inside a requirements for type requirements (since C++20)
union(联合)
不同的数据成员共用一段内存单元空间,使用相同的地址,其内存的大小取决于所有成员中占用空间最大的成员。
A union is a struct in which all members are allocated at the same address so that the union occupies only as much space as its largest member. Naturally, a union can hold a value for only one member at a time.
使用场合:当两个结构体的成员只有一个不同时,两个结构体可以通过一个union合并成一个结构体:
struct staff{ int num; char name[10]; char sex; char job; union { int banji; char position[10]; }category; }staff[12];
unsigned(无符号)
表明该类型是无符号数,和 signed 相反。
表明使用 namespace。
virtual(虚的)
① C++ 中用来实现多态机制,声明虚函数(包括虚析构函数,不能是虚构造函数)。
② 虚继承其类,解决菱形继承问题。
void(空的)
可以作为函数返回值,表明不返回任何数据;可以作为参数,表明没有参数传入(C++中不是必须的);void*可以作为指针使用。
void通常用于表示类型暂定,对于强类型语言,为了实现一些运算符或函数的通用性,通常首先将类型表示为void,最终类型转换为具体类型。如malloc()、new、qsort()等。
volatile(不稳定的)
① 限定一个对象可被外部进程(操作系统、硬件或并发线程等)改变,声明时的语法如下:
int volatile nVint;
这样的声明是不能达到最高效的,因为它们的值随时会改变,系统在需要时会经常读写这个对象的值。因此常用于像中断处理程序之类的异步进程进行内存单元访问。
“直接存取原始内存地址而不是可能优化到寄存器中的值”(有与register变量相反的味道)。在嵌入式开发中同硬件、中断、RTOS等打交道时都需要使用volatile变量。
② volatile-qualified member functions。
wchar_t(wide character typedef)是宽字符类型,每个 wchar_t 类型占 2 个字节,16 位宽。汉字的表示就要用到 wchar_t。
Required to be large enough to represent any supported character code point (32 bits on systems that support Unicode. A notable exception is Windows, where wchar_t is 16 bits and holds UTF-16 code units) It has the same size, signedness, and alignment as one of the integer types, but is a distinct type.
while用于表示一个循环,相对于for的重复次数相对确定,while常用于表示一些重复次数不确定的循环。
AlternativeType |
since C++11 alignas(since C++11) alignof(since C++11) char16_t(since C++11) char32_t(since C++11) constexpr(since C++11) decltype(since C++11) noexcept(since C++11) static_assert(since C++11) thread_local(since C++11) nullptr(since C++11) |
since C++20 char8_t(since C++20) concept(since C++20) consteval(since C++20) constinit(since C++20) co_await(since C++20) co_return(since C++20) co_yield(since C++20) requires(since C++20) atomic_cancel(TM TS) atomic_commit(TM TS) atomic_noexcept(TM TS) reflexpr(reflection TS) synchronized(TM TS) |
Note that and, bitor、or、xor、compl、bitand、and_eq、or_eq、xor_eq、not, and not_eq (along with the digraphs <%, %>, <:, :>, %:, and %:%:) provide an alternative way to represent standard tokens.
In addition to keywords, there are identifiers with special meaning, which may be used as names of objects or functions, but have special meaning in certain contexts.
override(C++11) final(C++11) import(C++20) module(C++20) transaction_safe(TM TS) transaction_safe_dynamic(TM TS) |
Also, all identifiers that contain a double underscore __ in any position and each identifier that begins with an underscore followed by an uppercase letter is always reserved and all identifiers that begin with an underscore are reserved for use as names in the global namespace. See identifiers for more details.
The namespace std is used to place names of the standard C++ library. See Extending namespace std for the rules about adding names to it.
The name posix is reserved for a future top-level namespace. The behavior is undefined if a program declares or defines anything in that namespace.(since C++11)
The following tokens are recognized by the preprocessor when in context of a preprocessor directive:
__has_include(since C++17) __has_cpp_attribute(since C++20) |
用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式。即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改;这样做的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码。如 int *restrict ptr, ptr 指向的内存单元只能被 ptr 访问到,任何同样指向这个内存单元的其他指针都是未定义的,直白点就是无效指针。restrict 的出现是因为 C 语言本身固有的缺陷,C 程序员应当主动地规避这个缺陷,而编译器也会很配合地优化你的代码。
C++也是int *__restrict pi;
void *pv; int *pi = pv; // void*默认为int*
C99可变数组VLA?C++用vector。
C++向下兼容C语言(C99除外)。编译器实现时可能有些差异。
原子类型的对象是仅有的免除数据竞争的对象,即它们可以被两个线程共时修改,或先被一个修改再被另一个读取。.
所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。
在以往的C++标准中并没有对原子操作进行规定,我们往往是使用汇编语言,或者是借助第三方的线程库,例如intel的pthread来实现。在新标准C++11,引入了原子操作的概念,并通过这个新的头文件提供了多种原子操作数据类型,例如,atomic_bool,atomic_int等等,如果我们在多个线程中对这些类型的共享资源进行操作,编译器将保证这些操作都是原子性的,也就是说,确保任意时刻只有一个线程对这个资源进行访问,编译器将保证,多个线程访问这个共享资源的正确性。从而避免了锁的使用,提高了效率。
// 用原子数据类型作为共享资源的数据类型 atomic_long total(0); //long total = 0; void click() { for(int i=0; i<1000000;++i) { // 仅仅是数据类型的不同而以,对其的访问形式与普通数据类型的资源并无区别 total += 1; } }
结果正确!耗时只是使用mutex互斥对象的四分之一!也仅仅是不采用任何保护机制的时间的4倍。可以说这是一个非常不错的成绩了。
原子操作的实现跟普通数据类型类似,但是它能够在保证结果正确的前提下,提供比mutex等锁机制更好的性能,如果我们要访问的共享资源可以用原子数据类型表示,那么在多线程程序中使用这种新的等价数据类型,是一个不错的选择。
提供一种基于控制表达式的类型,在编译时选择数个表达式之一的方法。
_Generic ( 控制表达式 , 关联列表 ) (C11 起)
#include <stdio.h> #include <math.h> // tgmath.h 宏 cbrt 的可能实现 #define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \ )(X) int main(void) { double x = 8.0; const float y = 3.375; printf("cbrt(8.0) = %f\n", cbrt(x)); // 选择默认的 cbrt printf("cbrtf(3.375) = %f\n", cbrt(y)); // 将 const float 转换成 float, // 然后选择 cbrtf }
输出:
cbrt(8.0) = 2.000000 cbrtf(3.375) = 1.500000
指明函数不会返回到其调用点。
_Noreturn function_declaration (C11 起)
_Noreturn 关键词出现于函数声明中,指定函数不会由于执行到 return 语句或抵达函数体结尾而返回(可通过执行 longjmp 返回)。若声明 _Noreturn 的函数返回,则行为未定义。若编译器能检测此错误,则推荐编译器诊断。
_Noreturn 指定符可以在同一函数声明中出现多于一次,行为与只出现一次相同。
此指定符通常通过便利宏 noreturn 使用,该宏于头文件 stdnoreturn.h 提供。
#include <stdlib.h> #include <stdio.h> #include <stdnoreturn.h> // 在 i <= 0 时导致未定义行为 // 在 i > 0 时退出 noreturn void stop_now(int i) // 或 _Noreturn void stop_now(int i) { if (i > 0) exit(i); } int main(void) { puts("Preparing to stop..."); stop_now(2); puts("This code is never executed."); }
输出:
Preparing to stop...
复浮点类型模仿数学的复数,即可以写成一个实数与一个实数乘虚数单位的和的数: a + bi
三种复数类型是
float _Complex (亦可用作float complex,若包含了 <complex.h> )
double _Complex (亦可用作 double complex,若包含了 <complex.h> )
long double _Complex (亦可用作 long double complex ,若包含了 <complex.h> )
注意:同所有类型指定符,允许任意顺序: long double complex 、 complex long double ,甚至 double complex long 都指名同一类型。
#include <complex.h> #include <stdio.h> int main(void) { double complex z = 1 + 2*I; z = 1/z; printf("1/(1.0+2.0i) = %.1f%+.1fi\n", creal(z), cimag(z)); }
输出:
1/(1.0+2.0i) = 0.2-0.4i
虚浮点类型模仿数学的虚数,即可以写成实数乘虚数单位的数: bi 三种虚数类型是
float _Imaginary (亦可用作 float imaginary ,若包含了 <complex.h> )
double _Imaginary (亦可用作 double imaginary ,若包含了 <complex.h> )
long double _Imaginary (亦可用作 long double imaginary ,若包含了 <complex.h> )
注意:同所有类型指定符,允许任意顺序: long double imaginary 、 imaginary long double ,甚至 double imaginary long 都指名同一类型。
#include <complex.h> #include <stdio.h> int main(void) { double imaginary z = 3*I; z = 1/z; printf("1/(3.0i) = %+.1fi\n", cimag(z)); } // 输出: 1/(3.0i) = -0.3i
Precedence | Operator | Description | Example | Associativity |
1 | () | Grouping operator | (a + b) / 4; | left to right |
[] | Array access | array[4] = 2; | ||
-> | Member access from a pointer | ptr->age = 34; | ||
. | Member access from an object | obj.age = 34; | ||
:: | scoping operator | className::age = 2; | ||
++ | Post-increment | for(int i=0;i<10;i++)… | ||
-- | Post-decrement | for(int i=0;i<10;i--)… | ||
2 | ! | Logical negation | if(!done)… | right to left (前置单元运算符) |
~ | Bitwiae complement | flags = ~flags | ||
++ | Pre-increment | for(i=0;i<9;++i)… | ||
-- | Pre-decrement | for(i=0;i<9;--i)… | ||
- | Unary minus | int i = -1; | ||
+ | Unary plus | int i = +1; | ||
* | Dereference | data = *ptr; | ||
& | Address of | address = &obj; | ||
(type) | Cast to a given type | int i = (int)floatNum; | ||
sizeof | Return size in bytes | int size = sizeof(int); | ||
3 | ->* | Member pointer selector | ptr->*var = 24; | left to right |
.* | Member pointer selector | obj.*var = 24; | ||
4 | * | Multiplication | int i = 2 * 4; | left to right |
/ | Division | float f = 10 / 3; | ||
% | Modulus | int rem = 28 % 10; | ||
5 | + | Addition | int i = 2 + 3; | left to right |
- | Substraction | int i = 3 - 1; | ||
6 | << | Bitwise shift left | int flags = 32 << 1; | left to right |
>> | Bitwise shift right | inf flags = 32 >> 1; | ||
7 | < | Comparison less-than | if(i < 32)… | left to right |
<= | Comparison less-than-or-equal to | if(i <= 32)… | ||
> | Comparison greater-than | if(i > 32)… | ||
>= | Comparison greater-than-or-equal-to | if(i >= 32)… | ||
8 | == | Comparison equal-to | if(i == 32)… | left to right |
!= | Comparison not-equal-to | if(i != 32)… | ||
9 | & | Bitwise AND | flags = flags & 16; | left to right |
10 | ^ | Bitwise exclusive OR | flags = flags ^ 16; | left to right |
11 | | | Bitwise inclusive(normal) OR | flags = flags | 16; | left to right |
12 | && | Logical AND | if(conditionA && conditionB)… | left to right |
13 | || | Logical OR | if(conditionA || conditionB)… | left to right |
14 | ?: | Ternary conditional(if-then-else) | int i(a>b) ? a : b; | right to left |
15 | = | Assignment operator | int a = b; | |
+= | Increment and assign | a += 3; | ||
-= | Decrement and assign | b -= 4; | ||
*= | Multiply and assign | a *= 5; | ||
/= | Divide and assign | b /= 2; | ||
%= | Modulo and assign | a %= 10; | ||
&= | Bitwise AND and assign | flags &= new_flags; | ||
^= | Bitwise exclusive OR and assign | flags ^= new_flags; | ||
|= | Bitwise inclusive(normal) OR and assign | flags |= new_flags; | ||
<<= | Bitwise shift left and assign | flags <<= 2; | ||
>>= | Bitwise shift right and assign | flags >>= 2; | ||
16 | , | Sequential evaluation operator | for(i=0,j=0;i<9;i++,j++)… | left to right |