| 关键字 | 说明 | 代码示例 |
|---|---|---|
auto | 声明自动变量(在C99标准中已废弃) | auto int x = 5; |
break | 跳出最近的一个循环或switch语句 | for(;;) { if(x > 10) break; } |
case | 用于switch语句中,表示一个分支的开始 | switch(x) { case 1: printf("One"); break; } |
char | 声明字符型变量或函数返回类型 | char ch = 'a'; |
const | 声明常量,其值不能被修改 | const int MAX = 100; |
continue | 跳过当前循环的剩余部分,开始下一次循环迭代 | for(int i = 0; i < 10; i++) { if(i % 2 == 0) continue; printf("%d ", i); } |
default | switch语句中的默认分支,如果没有其他case匹配 | switch(x) { default: printf("No match found"); } |
do | do-while循环的开始,至少执行一次循环体 | int x = 0; do { x++; } while(x < 5); |
double | 声明双精度浮点型变量或函数返回类型 | double d = 3.14; |
else | 与if配合使用,表示if不满足条件时执行的代码块 | if(x > 10) printf("Greater than 10"); else printf("10 or less"); |
enum | 枚举类型,允许为整型常量赋予更易读的名称 | enum { SUNDAY, MONDAY, TUESDAY }; |
extern | 声明外部变量或函数,即在其他文件中定义 | extern int externVar; |
float | 声明单精度浮点型变量或函数返回类型 | float f = 3.14f; |
for | 用于创建循环,重复执行一段代码 | for(int i = 0; i < 5; i++) printf("%d ", i); |
goto | 跳转到程序中标记的另一位置 | goto end; ... end: printf("End of program"); |
if | 条件语句,根据条件执行不同的代码块 | if(x > 10) printf("Greater than 10"); |
inline | 建议编译器将函数内联展开,减少函数调用开销(C99标准) | inline int max(int a, int b) { return (a > b) ? a : b; } |
int | 声明整型变量或函数返回类型 | int i = 10; |
long | 声明长整型变量或函数返回类型 | long l = 1234567890L; |
register | 建议编译器将变量存储在寄存器中以加快访问速度(现代编译器通常忽略此关键字) | register int regVar; |
return | 从函数返回值或退出主函数 | return 0; |
short | 声明短整型变量或函数返回类型 | short s = 10; |
signed | 声明有符号类型变量(默认情况下整型是无符号的) | signed char c = -1; |
sizeof | 计算变量或类型所占的内存大小 | printf("Size of int: %zu\n", sizeof(int)); |
static | 声明静态变量,其生命周期贯穿整个程序 | static int staticVar = 0; |
struct | 结构体类型,允许将多个不同类型的数据组合成一个单一的数据类型 | struct { int x, y; } point; |
switch | 根据变量值选择不同的代码块执行 | switch(x) { case 1: ... } |
typedef | 为类型创建一个新的名称 | typedef unsigned int uint; |
union | 联合体类型,允许在相同的内存位置存储不同的数据类型 | union { int i; char c; } data; |
unsigned | 声明无符号类型变量或函数返回类型 | unsigned int ui = 10; |
void | 声明没有返回值的函数或无类型指针 | void function() { ... } |
volatile | 声明变量为易失性,编译器将避免优化其访问 | volatile int v = 0; |
while | 根据条件创建循环,条件为真时重复执行循环体 | while(x < 10) { x++; } |
| 关键字 | 详细描述 | 代码示例 | ||
|---|---|---|---|---|
| alignas | 指定变量的对齐方式,与编译器的对齐方式一致 | alignas(16) int x; | ||
| alignof | 获取类型或变量的对齐要求 | alignof(int) | ||
| andb | 位与操作符(C++20之前) | int result = a & b; | ||
| and_eqb | 位与赋值操作符(C++20之前) | int a; a &= b; | ||
| asm | 内联汇编指令 | asm("mov al, 1"); | ||
| bitand | 位与操作符 | int result = a & b; | ||
| bitor | 位或操作符 | `int result = a | b;` | |
| bool | 布尔类型 | bool flag = true; | ||
| char8_t | UTF-8字符类型 | char8_t ch = u8'a'; | ||
| char16_t | UTF-16字符类型 | char16_t ch = u'a'; | ||
| char32_t | UTF-32字符类型 | char32_t ch = U'a'; | ||
| class | 定义类或结构体 | class MyClass {}; | ||
| compl | 按位取反操作符 | int result = ~a; | ||
| const_cast | 移除const属性 | int* p = const_cast<int*>(const_ptr); | ||
| consteval | 用于定义编译时计算的常量表达式 | consteval int add(int a, int b) { return a + b; } | ||
| constexpr | 表示编译时常量表达式 | constexpr int add(int a, int b) { return a + b; } | ||
| constinit | 用于初始化静态局部变量 | static constinit int x = 10; | ||
| co_await | 协程中的等待操作 | co_await some_coroutine; | ||
| co_return | 协程中的返回操作 | co_return result; | ||
| co_yield | 协程中的产生操作 | co_yield value; | ||
| decltype | 根据表达式推断类型 | auto x = 5; decltype(x) y = x; | ||
| delete | 删除函数定义 | void func() = delete; | ||
| dynamic_cast | 运行时类型转换 | dynamic_cast<Derived*>(base_ptr); | ||
| explicit | 显式构造函数 | explicit MyClass(int value); | ||
| export | 导出类或函数 | export MyClass; | ||
| false | 布尔假值 | bool flag = false; | ||
| friend | 友元函数或类 | friend void func(MyClass); | ||
| mutable | 允许在const上下文中修改的成员变量 | mutable int x; | ||
| namespace | 定义命名空间 | namespace MySpace { int x; } | ||
| new | 分配内存并构造对象 | MyClass* p = new MyClass; | ||
| noexcept | 指定函数不会抛出异常 | void func() noexcept; | ||
| not | 逻辑非操作符 | bool result = !condition; | ||
| not_eq | 不等操作符 | bool result = a != b; | ||
| nullptr | 空指针常量 | int* p = nullptr; | ||
| operator | 重载运算符 | MyClass operator+(const MyClass& other); | ||
| or | 逻辑或操作符 | `bool result = a | b;` | |
| or_eq | 逻辑或赋值操作符 | `bool a; a | = b;` | |
| private | 私有访问修饰符 | private: int x; | ||
| protected | 受保护访问修饰符 | protected: int x; | ||
| public | 公共访问修饰符 | public: int x; | ||
| reinterpret_cast | 强制类型转换 | int* p = reinterpret_cast<int*>(ptr); | ||
| requires | 概念约束 | void func() requires (sizeof(int) == 4); | ||
| static_assert | 编译时断言 | static_assert(sizeof(int) == 4); | ||
| static_cast | 静态类型转换 | double d = static_cast<double>(x); | ||
| size_t | ||||
| template | 模板定义 | template <typename T> class Container; | ||
| this | 指向当前对象的指针 | int MyClass::func() { return this->x; } | ||
| thread_local | 线程局部变量 | thread_local int x; | ||
| throw | 抛出异常 | throw std::runtime_error("error"); | ||
| try | 尝试块,用于异常处理 | try { } catch (...) { } | ||
| typeid | 运行时类型识别 | typeid(MyClass).name(); | ||
| typename | 模板中类型名称 | template <typename T> typename T::type x; | ||
| using | 引入命名空间或类型定义 | using namespace std; | ||
| virtual | 虚函数声明 | virtual void func(); | ||
| wchar_t | 宽字符类型 | `wchar_t ch = L’a’; | ||
| xorb | 位异或操作符(C++20之前) | int result = a ^ b; | ||
| xor_eqb | 位异或赋值操作符(C++20之前) | int a; a ^= b; |
(1)static & const
跳转到“(1)static & const”作用:
- 隐藏,当同时编译多个文件时,所有未加 static 前缀的全局变量和函数都具有全局可见性。
- static 的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和 static 变量,只不过和全局变量比起来,static 可以控制变量的可见范围,说到底 static 还是用来隐藏的。
- 其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是 0x00,某些时候这一特点可以减少程序员的工作量。
1)全局变量和全局静态变量的区别
- 1)全局变量是不显式用 static 修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过 extern 全局变量名的声明,就可以使用全局变量。
- 2)全局静态变量是显式用 static 修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用 extern 声明也不能使用。
静态局部变量有以下特点:
- (1)该变量在全局数据区分配内存;
- (2)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
- (3)静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为 0;
- (4)它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。
static 关键字用途
1)静态局部变量:
- 变量在函数内声明,并且在函数的所有调用之间保留其值。
- 生命周期贯穿程序的整个运行期间。
void function() { static int count = 0; count++; std::cout << count << std::endl;}2)类的静态成员变量:
- 属于类而不是类的对象的变量。
- 所有类的对象共享这个变量。
- 必须在类外进行定义和初始化。
class MyClass {public: static int value;};
int MyClass::value = 0;3)静态成员函数:
- 属于类而不是类的对象的函数。
- 无法访问类的非静态成员变量和成员函数。
class MyClass {public: static void staticFunction() { // 只能访问静态成员变量和成员函数 }};4)静态全局变量和函数:
- 只在定义它们的文件中可见,具有文件作用域。
static int fileStaticVariable = 0;
static void fileStaticFunction() { // 只能在当前文件中访问}const 关键字用途
1)常量变量:
- 声明为不可修改的变量。
- 可以用于任何基本数据类型、对象或指针。
const int constantValue = 10;2)常量成员函数:
- 表示函数不会修改类的成员变量。
- 可以在函数签名后添加
const关键字。
class MyClass {public: int getValue() const { return value; }private: int value;};3)指针和常量:
- 常量指针:指针的值(即它指向的地址)不可改变。
- 指向常量的指针:指针所指向的数据不可改变。
- 指向常量的常量指针:指针和值都不可改变。
int value = 10;int *const constPtr = &value; // 常量指针const int *ptrToConst = &value; // 指向常量的指针const int *const constPtrToConst = &value; // 指向常量的常量指针4)常量参数:
- 防止函数修改传递给它的参数。
void printValue(const int value) { std::cout << value << std::endl;}例子
#include <iostream>
class Example {public: static int staticVar; const int constVar;
Example(int v) : constVar(v) {}
static void staticFunction() { std::cout << "Static function called. StaticVar: " << staticVar << std::endl; }
void constFunction() const { std::cout << "Const function called. constVar: " << constVar << std::endl; }};
int Example::staticVar = 10;
int main() { Example ex(5);
// 调用静态成员函数 Example::staticFunction();
// 调用常量成员函数 ex.constFunction();
// 静态变量 Example::staticVar = 20; Example::staticFunction();
return 0;}(2)auto
跳转到“(2)auto”auto 关键字在不同的上下文中有不同的用途,主要用于让编译器自动推断变量或函数返回类型的类型。
| 用法类型 | 语法结构 | 说明 | 代码示例 |
|---|---|---|---|
| 基础类型推断 | auto var = value; | 推断变量 var 的类型为 value 的类型。 | auto x = 5; // x 的类型为 int |
| 范围for循环 | for (auto var : container) | 在循环中自动推断序列中每个元素的类型。 | for (auto num : vector) { /* 使用 num */ } |
| Lambda类型 | auto func = []() { /* ... */ }; | Lambda 表达式的结果是一个可调用的匿名类型,auto 用于声明。 | auto lambda = [](int x) { return x * x; }; |
| 函数返回类型 | auto function() { return value; } | 函数的返回类型由返回语句 return value; 的类型推断。 | auto add(int a, int b) { return a + b; } // 返回类型为 int |
| decltype特化 | auto var = some_expression; | 与 decltype(some_expression) 相同,可以绑定到几乎任何类型。 | int x = 10; auto copy_of_x = x; // copy_of_x 类型为 int |
| 模板参数类型 | template <typename T> void func(auto value) { /* ... */ } | 模板参数 T 由 value 的类型推断。 | template <typename T> void process(T value) { /* ... */ } |
(3)new
跳转到“(3)new”new 操作符用于动态内存分配。使用 new 分配的内存需要手动释放,否则会导致内存泄漏。使用 delete 来释放单个对象,使用 delete[] 来释放对象数组。
| 用法 | 描述 | 语法 | 示例 |
|---|---|---|---|
| 单个对象 | 分配内存并构造一个对象 | new 类型 | int* p = new int; |
| 数组 | 分配内存并构造对象数组 | new 类型[大小] | int* arr = new int[10]; |
| 初始值 | 使用构造函数初始化对象 | new 类型(初始化参数) | int* p = new int(5); |
| 初始值数组 | 使用构造函数初始化对象数组 | new 类型[大小](初始化参数) | int* arr = new int[3](); 或 int* arr = new int[3]{1, 2, 3}; |
| 委托构造 | 使用另一个构造函数初始化对象 | new 类型(: 初始化参数列表) | new 类型(: 基类构造函数参数) |
| 委托数组 | 使用另一个构造函数初始化对象数组 | new 类型[大小](: 初始化参数列表) | new 类型[大小](: 基类构造函数参数) |
| 作用域外释放 | 使用 delete 释放单个对象 | delete 指针 | delete p; |
| 作用域外释放数组 | 使用 delete[] 释放对象数组 | delete[] 指针 | delete[] arr; |
(4)typedef
跳转到“(4)typedef”typedef关键字用于为数据类型创建一个新的别名。当你看到typedef,基本上可以认为它是在说“我将给某个数据类型起一个新的名字”。
typedef void (*KeyFunction)(WORD, int);// 你可以这样声明一个KeyFunction类型的指针,并将它指向一个具体的函数KeyFunction myKeyFunction = keyPress; // 假设 keyPress 是一个符合签名的函数// 你可以像调用普通函数一样调用这个函数指针:myKeyFunction(VK_A, 100); // 调用 keyPress 函数分解解释如下:
void:表示函数返回类型,这里指函数不返回任何值。(*KeyFunction):定义了一个名为KeyFunction的指针变量。(WORD, int):这是KeyFunction指针指向的函数的参数列表,表示这个函数接受两个参数,第一个是WORD类型,第二个是int类型。
综合起来,这行代码定义了一个名为KeyFunction的新类型,它是一个指向函数的指针,这个函数没有返回值(void),并且接受两个参数:一个WORD类型的参数和一个int类型的参数。
这种类型定义的用途是,你可以创建一个函数指针数组,数组中的每个元素都是指向符合这种参数和返回值约定的函数的指针。这样,你就可以通过函数指针来调用不同的函数,实现某种形式的多态性或函数回调机制。
(5)try & catch | noexcept
跳转到“(5)try & catch | noexcept”noexcept:
noexcept是用来声明一个函数不会抛出异常的。- 当函数被标记为
noexcept时,编译器会对其进行优化,不会生成额外的异常处理代码,从而提高代码执行效率。 - 如果在
noexcept函数内部抛出了异常,程序会调用std::terminate()来终止程序的执行,而不会调用异常处理器。这样可以避免因异常而导致的资源泄漏等问题。
try:
try是用来处理可能抛出异常的代码块。- 通过
try和catch块,可以捕获和处理可能在try块中抛出的异常。 try块内部的代码在运行时可能会抛出异常,而catch块则用来捕获并处理这些异常,或者将异常传播到更高层的异常处理器。
catch 是 C++ 中用于异常处理的关键字。它通常与 try 关键字一起使用。在 try 块中编写可能引发异常的代码,而在 catch 块中编写处理异常的代码。当 try 块中的代码引发异常时,程序会跳转到与之匹配的 catch 块,并执行其中的代码来处理异常情况。
例如:
try { // 可能引发异常的代码 throw runtime_error("Something went wrong");}catch (const runtime_error& e) { // 处理异常的代码 cout << "Caught exception: " << e.what() << endl;}在这个示例中,如果 try 块中的代码抛出 runtime_error 异常,程序将会执行 catch 块中的代码,输出异常的信息。
(6)extern
跳转到“(6)extern”extern 用于声明一个变量或函数是在其他文件中定义的。它告诉编译器这个变量或函数的定义在别的地方,可以在当前文件中使用。
1)声明全局变量,在头文件中用 extern 声明一个变量,然后在一个源文件中定义它。这通常用于在多个文件中共享全局变量。
// 在头文件 globals.h 中extern int globalVariable;
// 在源文件 globals.cpp 中int globalVariable = 42;2)声明函数,extern 通常省略,因为函数默认是 extern,但在某些情况下,使用 extern 可以提高代码可读性。
// 在头文件 functions.h 中extern void someFunction();
// 在源文件 functions.cpp 中void someFunction() { // 函数实现}3)在多个文件中共享常量
// 在头文件 constants.h 中extern const int CONSTANT_VALUE;
// 在源文件 constants.cpp 中const int CONSTANT_VALUE = 100;在头文件中定义的全局变量在源文件中使用时,必须加上extern关键字来声明。这是因为头文件中的定义会在每个包含它的源文件中生成一个独立的实例,这样会导致重复定义的错误。在源文件中定义全局变量,并在头文件中声明这个全局变量的目的是为了让其他源文件也能访问和使用这个全局变量。
不建议直接在头文件中定义全局变量,在源文件中使用。这会导致每个包含该头文件的源文件都有一个独立的变量副本,导致多重定义错误。为了保证全局变量只有一个定义,正确的方法是:
- 在头文件中使用
extern声明全局变量。- 在一个源文件中定义全局变量。
这样做的好处是确保全局变量在程序中只有一个实例,避免重复定义和链接错误。如果在头文件中定义全局变量,并在每个使用该变量的源文件中使用
extern关键字引用它,仍然会报重复定义错误。
从编译原理角度解释,这种重复定义出现的原因
在C++编程中,头文件中的变量定义会导致多重定义错误,这与编译和链接的工作方式有关。以下是详细解释:
- 预处理(Preprocessing):编译器处理头文件包含指令(#include)、宏定义(#define)等。预处理器会把头文件的内容直接插入到包含它的源文件中。
- 编译(Compilation):每个源文件独立编译成目标文件(.obj或.o文件)。编译过程生成的目标文件包含了源文件的机器代码,但此时还没有进行跨文件的引用解析。
- 链接(Linking):链接器将所有目标文件和库文件组合在一起,解决跨文件的符号引用(函数和变量),生成最终的可执行文件或库文件。
多重定义错误的原因 当在头文件中定义全局变量时,例如:
yolowindow.h #ifndef YOLOWINDOW_H#define YOLOWINDOW_H#include <opencv2/opencv.hpp>cv::Size YOLO_SIZE = cv::Size(640, 640); // 定义全局变量#endif // YOLOWINDOW_H并且在多个源文件中包含这个头文件时,例如:
file1.cpp #include "yolowindow.h"// file2.cpp#include "yolowindow.h"编译器的预处理阶段会将
yolowindow.h文件的内容插入到file1.cpp和file2.cpp中。这意味着YOLO_SIZE变量在每个包含该头文件的源文件中都会有一个定义。具体步骤如下:
- 预处理阶段:
file1.cpp和file2.cpp在预处理后都包含了cv::Size YOLO_SIZE = cv::Size(640, 640);的定义。- 编译阶段:
file1.cpp和file2.cpp分别被编译成目标文件file1.obj和file2.obj。每个目标文件中都包含YOLO_SIZE变量的定义。- 链接阶段:链接器尝试将所有目标文件组合在一起生成可执行文件。此时,链接器发现有多个
YOLO_SIZE变量的定义,导致多重定义错误(multiple definition error)。
1)在头文件yolowindow.h中声明变量:
extern cv::Size YOLO_SIZE;2)在一个源文件(例如yolowindow.cpp)中定义变量:
cv::Size YOLO_SIZE = cv::Size(640, 640);这样,YOLO_SIZE变量的定义和声明就会分别在源文件和头文件中,避免重复定义的问题。
#ifndef YOLOWINDOW_H#define YOLOWINDOW_H
#include <opencv2/opencv.hpp>
extern cv::Size YOLO_SIZE;
#endif // YOLOWINDOW_H#include "yolowindow.h"
cv::Size YOLO_SIZE = cv::Size(640, 640);这样就可以在任何包含yolowindow.h头文件的源文件中(例如 main.cpp)使用YOLO_SIZE变量了:
#include "yolowindow.h"
void someFunction(){ // 使用 YOLO_SIZE 变量 cv::Size size = YOLO_SIZE; // ... 其他代码 ...}