跳转到内容
新建笔记

关键字

关键字 (C++) | Microsoft Learn

关键字说明代码示例
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); }
defaultswitch语句中的默认分支,如果没有其他case匹配switch(x) { default: printf("No match found"); }
dodo-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 = ab;`
bool布尔类型bool flag = true;
char8_tUTF-8字符类型char8_t ch = u8'a';
char16_tUTF-16字符类型char16_t ch = u'a';
char32_tUTF-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 = ab;`
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;

作用:

  • 隐藏,当同时编译多个文件时,所有未加 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;
}

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) { /* ... */ }

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;

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类型的参数。

这种类型定义的用途是,你可以创建一个函数指针数组,数组中的每个元素都是指向符合这种参数和返回值约定的函数的指针。这样,你就可以通过函数指针来调用不同的函数,实现某种形式的多态性或函数回调机制。

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 块中的代码,输出异常的信息。

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关键字来声明。这是因为头文件中的定义会在每个包含它的源文件中生成一个独立的实例,这样会导致重复定义的错误。在源文件中定义全局变量,并在头文件中声明这个全局变量的目的是为了让其他源文件也能访问和使用这个全局变量。

不建议直接在头文件中定义全局变量,在源文件中使用。这会导致每个包含该头文件的源文件都有一个独立的变量副本,导致多重定义错误。为了保证全局变量只有一个定义,正确的方法是:

  1. 在头文件中使用 extern 声明全局变量。
  2. 在一个源文件中定义全局变量。

这样做的好处是确保全局变量在程序中只有一个实例,避免重复定义和链接错误。如果在头文件中定义全局变量,并在每个使用该变量的源文件中使用 extern 关键字引用它,仍然会报重复定义错误。

从编译原理角度解释,这种重复定义出现的原因

在C++编程中,头文件中的变量定义会导致多重定义错误,这与编译和链接的工作方式有关。以下是详细解释:

  1. 预处理(Preprocessing):编译器处理头文件包含指令(#include)、宏定义(#define)等。预处理器会把头文件的内容直接插入到包含它的源文件中。
  2. 编译(Compilation):每个源文件独立编译成目标文件(.obj或.o文件)。编译过程生成的目标文件包含了源文件的机器代码,但此时还没有进行跨文件的引用解析。
  3. 链接(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 变量在每个包含该头文件的源文件中都会有一个定义。具体步骤如下:

  1. 预处理阶段:file1.cpp 和 file2.cpp 在预处理后都包含了 cv::Size YOLO_SIZE = cv::Size(640, 640); 的定义。
  2. 编译阶段:file1.cpp 和 file2.cpp 分别被编译成目标文件 file1.obj 和 file2.obj。每个目标文件中都包含 YOLO_SIZE 变量的定义。
  3. 链接阶段:链接器尝试将所有目标文件组合在一起生成可执行文件。此时,链接器发现有多个 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变量的定义和声明就会分别在源文件和头文件中,避免重复定义的问题。

yolowindow.h
#ifndef YOLOWINDOW_H
#define YOLOWINDOW_H
#include <opencv2/opencv.hpp>
extern cv::Size YOLO_SIZE;
#endif // YOLOWINDOW_H
yolowindow.cpp
#include "yolowindow.h"
cv::Size YOLO_SIZE = cv::Size(640, 640);

这样就可以在任何包含yolowindow.h头文件的源文件中(例如 main.cpp)使用YOLO_SIZE变量了:

main.cpp
#include "yolowindow.h"
void someFunction()
{
// 使用 YOLO_SIZE 变量
cv::Size size = YOLO_SIZE;
// ... 其他代码 ...
}