前言
关键字在编程语言中具有特定的含义和用途,它们被用来表示语言的语法结构、控制流程、数据类型、访问权限等重要概念。
关键字有助于编程语言的解析和编译,使得编程语言的语法规则能够被准确地解释和执行。通过使用关键字,编程语言可以提供一致性的语法规范和编程范式,使得程序员可以按照特定的语义规则编写代码。
例如,在C++中,关键字"if"和"else"用于控制程序的条件执行,关键字"for"和"while"用于循环结构,关键字"class"用于定义类,关键字"int"和"double"用于声明整数和浮点数数据类型等等。
关键字是编程语言的基础构成要素之一,它们定义了语言的语法规则和功能特性。了解和正确使用关键字是编写有效、正确的程序的基础。同时,编程语言的关键字是预留的,不能将其用作标识符或变量名。
C++11关键字图表:
根据当前的 C++ 标准(C++17),以及截至到目前为止的最新 C++ 标准(C++20),export 关键字已经被移除,并不再是 C++ 的一个关键字。在 C++11 标准中,export 关键字被引入,但在后续的标准中,并没有被广泛采纳并实现,最终在 C++17 中被移除。
一、alignas?
在C++中,alignas关键字用于指定变量或类型的对齐方式。对齐方式是指将数据在内存中按照一定规则进行排列的方式,以提高访问效率和内存使用效率。
alignas关键字可以在类型或变量声明时使用,用于指定所需的对齐方式。它接受一个对齐值作为参数,该对齐值必须是2的幂。
示例代码:
#include <iostream> struct alignas(16) MyStruct // 指定该结构体按16字节对齐 { int x; char c; double d; }; int main() { alignas(16) int alignedInt; // 指定该变量按16字节对齐 std::cout << "Size of MyStruct: " << sizeof(MyStruct) << std::endl; std::cout << "Alignment of MyStruct: " << alignof(MyStruct) << std::endl; // 输出的结果可能因不同编译器而有所不同 std::cout << "Alignment of alignedInt: " << alignof(decltype(alignedInt)) << std::endl; return 0; }
二、alignof?
在C++中,alignof关键字用于获取类型或变量的对齐方式。对齐方式是指数据在内存中按照一定规则进行排列的方式,以提高访问效率和内存使用效率。
alignof关键字可以在类型或变量后面使用,用于获取所需的对齐方式。它返回一个整数值,表示类型或变量的对齐要求。
示例代码:
#include <iostream> struct MyStruct { int x; char c; double d; }; int main() { std::cout << "Alignment of MyStruct: " << alignof(MyStruct) << std::endl; double arr[5]; std::cout << "Alignment of arr: " << alignof(decltype(arr)) << std::endl; return 0; }
运行结果:
1. Alignment of MyStruct: 8 2. Alignment of arr: 8
三、asm?
在C++中,asm关键字用于嵌入汇编语言代码到C++程序中。它允许程序员直接在C++代码中插入特定的汇编指令,以便实现对底层硬件和操作系统特性的直接访问。
使用asm关键字时,要根据所使用的编译器和目标平台的不同来编写针对性的汇编代码。因此,示例代码可能因环境而异。在现代的C++标准中,推荐使用__asm__关键字,而不是简单的asm。
示例代码:
#include <iostream> int main() { int x = 10; int y = 20; int result; __asm__ ( "mov %1, %%eax;" // 将y赋值给EAX寄存器 "add %2, %%eax;" // 将x加到EAX寄存器上 "mov %%eax, %0;" // 将EAX寄存器的值存回result变量 : "=r" (result) // 输出操作数(result) : "r" (y), "r" (x) // 输入操作数(y和x) : "%eax" // 受影响的寄存器 ); std::cout << "Result: " << result << std::endl; return 0; }
需要注意的是,嵌入汇编是一种非标准的C++语法扩展,它依赖于特定的编译器和平台。因此,在使用嵌入汇编时要特别小心,确保对应的汇编代码是正确的,并且在目标平台上能够正常工作。
1.使用asm关键字的用途及优势
1. 直接访问底层硬件和操作系统特性:通过嵌入汇编代码,可以直接使用底层硬件和操作系统提供的特性,如处理器指令集、寄存器等。这样可以更灵活地对底层资源进行操作,访问和控制硬件的细节。
2. 提高程序性能:嵌入汇编代码可以精确地控制指令执行的顺序和方式。通过编写高效的汇编代码,可以针对特定的场景和需求进行优化,从而提高程序的性能。
3. 实现特定的算法和功能:有些特定的算法和功能可能需要使用汇编代码来实现,如加密算法、视频编解码器等。通过嵌入汇编代码,可以直接使用汇编级的指令和操作来实现这些功能,而不受高级语言的限制。
4. 调试和研究底层问题:嵌入汇编代码可以用于调试和研究底层问题。通过直接观察和修改底层的寄存器和内存状态,可以更深入地了解程序运行时的细节,帮助排查和解决一些底层问题。
需要注意的是,嵌入汇编是一项高级技巧,需要对汇编语言和底层硬件有一定的了解。同时,使用嵌入汇编代码时要小心处理和维护代码的可移植性和可读性,避免破坏程序的可维护性。在大多数情况下,应当优先使用高级语言提供的特性和库函数来实现功能,只在必要的情况下才考虑使用嵌入汇编。
四、constexpr?
在C++constexpr关键字,用于在编译期计算值或表达式,从而提高程序的性能和可读性。
使用constexpr定义的变量或函数,在编译时就能够计算出值来,并且可以用于初始化其他变量。这个特性可以消除一些不必要的运行时计算,提高程序的效率。
示例代码:
#include <iostream> constexpr int factorial(int n) { return n <= 0 ? 1 : n * factorial(n - 1); } int main() { constexpr int x = 5; std::cout << "Factorial of " << x << " is " << factorial(x) << std::endl; int y; std::cin >> y; constexpr int z = y * 10; // 非法,y是运行时变量,不可以在编译期使用 return 0; }
在上述代码中,定义了一个constexpr函数factorial,用于计算输入参数的阶乘。在main函数中,我们使用constexpr关键字定义了一个常量x,并且使用factorial函数计算了x的阶乘作为输出。同时,还演示了constexpr在编译期进行计算的特性。
运行结果:
Factorial of 5 is 120
需要注意的是,constexpr变量或函数的定义必须满足一定的条件,只有在满足条件的情况下才能进行编译期计算。例如,constexpr函数必须满足输入参数和返回值都是字面量类型(literal type),函数体中只能包含需要在编译期获取的代码等。同时,对于constexpr变量的初始化表达式也必须满足特定的要求。
五、decltype?
decltype 关键字是 C++11 引入的,用于查询表达式的类型,可以用于获取变量、表达式或函数返回值的类型。并用获取到的类型声明变量。
示例代码:
#include <iostream> int main() { int x = 5; const double pi = 3.14159; decltype(x) a = x; // a的类型为int,与x的类型相同 decltype(pi) b = pi; // b的类型为const double,与pi的类型相同 decltype(x + pi) c = x + pi; // c的类型为double,根据表达式x + pi的结果推导 std::cout << "a: " << a << std::endl; std::cout << "b: " << b << std::endl; std::cout << "c: " << c << std::endl; return 0; }
其中,decltype里面的内容可以 是一个变量、表达式或函数调用。
在上述示例中,decltype 关键字用于声明了三个变量 a、b 和 c,分别初始化为 x、pi 和 x + pi 的类型。根据推导规则,a 的类型为 int,与 x 的类型相同;b 的类型为 double,与 pi 的类型相同;c 的类型为 double,根据表达式 x + pi 的结果推导。最后将结果打印输出。
需要注意的是,decltype 推导出的类型通常会保留 const 和引用特性。对于函数调用,decltype 将返回函数的返回值类型。
通过使用 decltype,我们可以在编译时获取表达式的类型,使得代码更加灵活和类型安全。
六、explicit?
explicit 是 C++ 关键字,用于修饰单参数构造函数,它的作用是禁止隐式类型转换。
使用 explicit 关键字可以防止编译器在某些情况下进行隐式的自动类型转换,保证只能使用显式的方式进行类型转换。
示例代码:
#include <iostream> class MyClass { public: explicit MyClass(int x) : value(x) {} int getValue() const { return value; } private: int value; }; void printValue(const MyClass& obj) { std::cout << "Value: " << obj.getValue() << std::endl; } int main() { MyClass obj1(10); printValue(obj1); // MyClass obj2 = 20; // 错误,禁止隐式类型转换 MyClass obj2 = MyClass(20); // 正确,通过显式构造函数进行赋值 printValue(obj2); // MyClass obj3 = obj1; // 错误,禁止隐式类型转换 MyClass obj3(obj1); // 正确,通过显式构造函数进行赋值 printValue(obj3); return 0; }
在上述示例中,MyClass 类定义了一个使用 explicit 关键字修饰的单参数构造函数。该构造函数用于将整数值赋给 value 成员变量。
在 main 函数中,我们创建了 obj1 对象,并通过 printValue 函数打印出它的值。然后尝试使用隐式类型转换进行构造赋值,如 MyClass obj2 = 20; 和 MyClass obj3 = obj1;,这两行代码会在编译时产生错误。
为了通过编译,我们需要使用显式构造函数进行赋值,如 MyClass obj2 = MyClass(20); 和 MyClass obj3(obj1);。这样就确保只能通过显式的方式进行类型转换,增加了代码的可读性和类型安全性。
使用 explicit 关键字能够避免不必要的隐式类型转换,减少代码行为的模糊性。它通常用于构造函数,确保只有在明确的意图下进行类型转换。
七、mutable?
mutable 是 C++ 中的一个关键字,它的作用是修改类中被声明为 mutable 的成员变量,即使在 const 成员函数中,这些变量也可以被修改。
使用 mutable 关键字,可以在 const 成员函数中修改被声明为 mutable 的成员变量的值,即使按理说在 const 函数中是不允许修改成员变量的。
示例代码:
#include <iostream> class Counter { public: int getCount() const; void increment() const; private: mutable int count = 0; }; int Counter::getCount() const { // count 可以在 const 函数中被访问,但不可以直接修改 // 但是因为被声明为 mutable,仍然可以修改它的值 count += 1; return count; } void Counter::increment() const { count += 1; } int main() { Counter c; std::cout << "Count: " << c.getCount() << std::endl; // 输出 1 std::cout << "Count: " << c.getCount() << std::endl; // 输出 2 c.increment(); std::cout << "Count: " << c.getCount() << std::endl; // 输出 3 return 0; }
在上述示例中,我们有一个名为 Counter 的类。它有一个私有的 count 成员变量,被声明为 mutable,初始化为 0。
类中的 getCount() 和 increment() 函数都被声明为 const 成员函数。然而,getCount() 函数尝试通过 count += 1 来修改成员变量的值。
因为 count 被声明为 mutable,即使在 const 函数中,也可以修改它的值。
在 main() 函数中,我们使用 Counter 类的实例 c 来测试。我们首先通过 getCount() 函数获取初始的计数值,并对计数值递增操作进行多次操作,以验证 count 变量在const成员函数中的修改。
通过使用 mutable 关键字,我们可以在 const 函数中修改特定的成员变量,提供更多的灵活性和功能,并且不违反 const 成员函数的约定。
八、noexcept?
noexcept 是 C++ 中的一个关键字,它用于指示函数是否可能引发异常。使用 noexcept 关键字可以明确指定一个函数不会引发异常,或是可能引发异常。
作为一种函数修饰符,noexcept 在函数声明或定义时使用,并紧跟在函数参数列表和函数的返回类型之后。
示例代码:
#include <iostream> void func1() noexcept { std::cout << "This function will not throw any exceptions." << std::endl; } void func2() { throw std::runtime_error("Exception thrown."); } int main() { try { func1(); func2(); } catch (const std::exception& e) { std::cout << "Exception caught: " << e.what() << std::endl; } return 0; }
在 main() 函数中,我们尝试调用 func1() 和 func2()。由于 func1() 是 noexcept 的,不会引发异常,所以它可以正常执行。而 func2() 引发了异常,但没有被 try-catch 块捕获,所以程序会终止执行,并输出异常信息。
九、thread_local?
thread_local 是 C++ 中的一个关键字,它用于声明线程局部存储的变量。使用 thread_local 关键字声明的变量,在不同的线程中拥有各自独立的副本,每个线程都可以独立地访问和修改自己的副本。
示例代码:
#include <iostream> #include <thread> // 全局变量 thread_local int threadId; void threadFunc(int id) { threadId = id; std::cout << "Thread " << id << " | threadId: " << threadId << std::endl; } int main() { std::thread t1(threadFunc, 1); std::thread t2(threadFunc, 2); t1.join(); t2.join(); std::cout << "Main thread | threadId: " << threadId << std::endl; return 0; }
示例结果:
Thread 1 | threadId: 1 Thread 2 | threadId: 2 Main thread | threadId: 0