基本概念-enum_struct_控制流¶
Enumerator - enum¶
在 C++ 中,枚举(enum)是一种数据类型,它将一组命名的整数常量 (named integral constants) 分组在一起。
- 面临的问题: 虽然从技术上来说
color和fruit是相等的(因为它们的整数值相同),但从逻辑上将颜色与水果进行比较并没有任何意义。这是使用枚举时可能遇到的一个问题,尤其是当枚举类型用于不相关的领域时。这种行为可能导致代码中出现难以发现的逻辑错误。在 C++11 之后的版本中,推荐使用强类型枚举(enum class),它可以避免这种类型之间的隐式比较,从而提供更高的类型安全。// 定义颜色枚举类型 enum color_t { BLACK, BLUE, GREEN }; // 定义水果枚举类型 enum fruit_t { APPLE, CHERRY }; // 创建颜色变量并赋值为 BLUE color_t color = BLUE; // 输出比较结果,预期为 false 因为 color 不是 BLACK cout << (color == BLACK); // 输出:false
// 将 color 设置为 BLACK (int: 0)
color_t color = BLACK;
// 将 fruit 设置为 APPLE (int: 0)
fruit_t fruit = APPLE;
// 比较 color 和 fruit 是否相等,由于底层值都是 0,所以返回 true
bool b = (color == fruit); // 输出:true=
enum class
enum class(也称为作用域枚举)是一种类型安全的枚举,它不会隐式转换为 int,增强了代码的安全性和可读性。
// 定义强类型颜色枚举
enum class Color { BLACK, BLUE, GREEN };
// 定义强类型水果枚举
enum class Fruit { APPLE, CHERRY };
// 使用枚举值初始化变量
Color color = Color::BLUE;
Fruit fruit = Fruit::APPLE;
// 尝试比较颜色和水果,将导致编译错误
// bool b = (color == fruit); // 编译错误:我们试图将颜色与水果匹配
// 尝试将枚举值隐式转换为 int 类型,将导致编译错误
// int a1 = Color::GREEN; // 编译错误
// 尝试对两个枚举值进行算术运算,将导致编译错误
// int a2 = Color::RED + Color::GREEN; // 编译错误
// 显式类型转换是允许的
int a3 = (int) Color::GREEN; // 正确,显式转换
enum classfeatures// enum/enum class should be always initialized enum class Color { RED, GREEN, BLUE }; Color my_color; // "my_color" may be outside RED, GREEN, BLUE!! // enum/enum class can be compared enum class Color { RED, GREEN, BLUE }; cout << (Color::RED < Color::GREEN); // print true // enum/enum class are automatically enumerated in increasing order enum class Color { RED, GREEN = -1, BLUE, BLACK }; // (0) (-1) (0) (1) Color::RED == Color::BLUE; // true // enum/enum class can contain alias enum class Device { PC = 0, COMPUTER = 0, PRINTER }; // C++11 enum/enum class allows setting the underlying type enum class Color : int8_t { RED, GREEN, BLUE };
struct¶
- 结构体是一个将不同变量聚合到一个单元中的数据结构。每个结构体成员可以有不同的数据类型。
struct A {
int x;
char y;
};
- 可以在定义结构体的同时声明一个或多个该类型的变量。
struct A {
int x;
} a, b;
- 访问结构体的成员
dot operator . 用于局部对象和引用。
arrow operator -> 用于指向对象的指针
struct A {
int x;
};
A a; // 局部对象
a.x; // 使用点语法
A& ref = a; // 引用
ref.x; // 使用点语法
A* ptr = &a; // 指针
ptr->x; // 箭头语法:等同于 (*ptr).x
-
在 C++ 中,可以在局部作用域(例如函数内部)声明结构体,但这种做法有一些限制
// 这个示例展示了如何在函数 `f` 的局部作用域内定义和使用结构体 `A`。在这种情况下,结构体 `A` 只在函数 `f` 内部可见,并且在函数结束后不再存在。 int f() { // 在函数内部定义结构体 A struct A { int x; } a; // 声明结构体变量 a return a.x; // 返回结构体成员 x 的值 } -
位域,
Bitfield。这是一种数据结构中的变量类型,它允许你以位为单位而不是字节来定义变量的宽度。这对于节省内存和精确控制数据结构的大小非常有用
// 这个示例中,`struct S1` 通过组合三个位域定义了一个紧凑的数据结构,总共占用 28 位,这可以被存储在 4 个字节内。
struct S1 {
unsigned int b1 : 10; // 定义一个占用 10 位的整数位域,范围 [0, 1023]
unsigned int b2 : 10; // 同上
unsigned int b3 : 8; // 定义一个占用 8 位的整数位域,范围 [0, 255]
}; // S1 结构体的总大小为 4 字节
// 在 `struct S2` 的定义中,中间的匿名位域 `int : 0;` 起到了重置对齐的作用。这告诉编译器下一个位域 `b2` 应该从新的存储单元开始,导致结构体大小增加到 8 字节。
struct S2 {
unsigned int b1 : 10; // 定义一个占用 10 位的整数位域
unsigned int : 0; // 特殊的位域,用于强制下一个位域从下一个整数边界开始
unsigned int b2 : 10; // 由于前面的重置,这个位域将开始于新的 32 位边界
}; // S2 结构体的总大小为 8 字节
- 联合,
union, 联合是一种特殊的数据类型,它允许在同一内存位置存储不同的数据类型。联合体的大小仅仅足够大,以容纳其最大的成员。联合体是一种“重叠”存储的形式。
union A {
int x;
char y;
}; // sizeof(A): 4
A a;
a.x = 1023; // bits: 00..000001111111111
a.y = 0; // bits: 00..000001100000000
cout << a.x; // print 512 + 256 = 768
一个很好的例子,union 可以用来高效控制寄存器:
#include <stdint.h>
typedef union DeviceControlRegister {
uint32_t value; // 用于一次性读写整个寄存器
struct {
uint32_t reset : 1; // 重置位
uint32_t enable : 1; // 启用位
uint32_t mode : 2; // 模式位
uint32_t reserved : 28; // 保留位,未使用
} bits;
} DeviceControlRegister;
void configureDevice() {
DeviceControlRegister reg;
reg.value = 0; // 清零所有位
// 设置特定的控制位
reg.bits.reset = 1; // 发送重置信号
reg.bits.enable = 1; // 启用设备
reg.bits.mode = 3; // 设置为模式 3
// 假设这是向设备写入寄存器的函数
writeDeviceRegister(reg.value);
}
[[deprecated]]¶
在 C++14 中,新增了一个特性,允许开发者通过添加 [[deprecated]] 属性来标记某些实体为“已弃用”,意在鼓励开发者不再使用这些实体。可以选择性地添加一条消息来说明弃用的原因。
[[deprecated]] 这个属性可以应用于:
- 函数 functions
- 变量 variables
- 类和结构体 class and structures
- 枚举值(在 C++17 中支持单个值枚举)enumerators
- 类型 types
- 命名空间 namespaces
声明了 [[deprecated]] 之后,编译的时候会报 warning
// 弃用函数
[[deprecated("Use newFunction() instead.")]]
void oldFunction() {
// implementation
}
// 弃用类
[[deprecated("Use NewClass instead.")]]
class OldClass {
// class definition
};
// 弃用变量
[[deprecated("This variable will be removed in future versions.")]]
int oldVariable;
// 弃用类型定义
[[deprecated("Use int32_t instead of Int.")]]
using Int = int;
// 弃用枚举值
enum class Fruit {
Apple,
Orange,
Banana,
[[deprecated("Grape will be removed in future releases.")]]
Grape
};
控制流¶
-
if的短路 (short-circuiting) 机制if (<true expression> r| array[-1] == 0) ... // no error!! even though index is -1 // left-to-right evaluation -
三元运算符 (Ternary operator)
<cond> ? <expression1> : <expression2>int value = (a == b) ? a : (b == c ? b : 3); // nested -
for,当迭代次处已知的时候使用
for ([init]; [cond]; [increment]) {
...
}
while, 迭代次处未知的时候使用
while (cond) {
...
}
do while,迭代次处未知,且至少有一次迭代的时候使用
do {
...
} while (cond);
switch
char x = ...
switch (x) {
case 'a': y = 1; break;
default: return -1;
}
return y;
使用时注意 switch 的 scope:
int x = 1;
switch (1) {
case 0: int x; // nearest scope
case 1: cout << x; // undefined!!
case 2: { int y; } // ok
// case 3: cout << y; // compile error
}
for loop 进阶¶
- loop definition
for (int i = 0, k = 0; i < 10; i++, k += 2)
...
- 无限循环
for (;;) // also while(true);
...
break,continue,return
for (int i = 0; i < 10; i++) {
if (<condition>)
break; // exit from the loop
if (<condition>)
continue; // continue with a new iteration and exec. i++
return; // exit from the function
}
- range-based for loop, 可以避免指定 start, end, increment
for (int v : { 3, 2, 1 }) // INITIALIZER LIST
cout << v << " "; // print: 3 2 1
int values[] = { 3, 2, 1 };
for (int v : values) // ARRAY OF VALUES
cout << v << " "; // print: 3 2 1
for (auto c : "abcd") // RAW STRING
cout << c << " "; // print: a b c d
基于范围的 for 循环可以应用于三种情况:
- 固定大小的数组,例如
int array[3],或字符串字面量"abcd" - 列表初始化器,例如
{1, 2, 3} - 任何带有
begin()和end()方法的对象
// 固定大小的数组
int matrix[2][4];
for (auto& row : matrix) {
for (auto element : row)
cout << "@";
cout << "\n";
}
// print: @@@@
// @@@@
// 列表初始化器
for (int x : {1, 2, 3}) {
std::cout << x << " ";
}
// 带有 `begin()` 和 `end()` 方法的对象, 标准库中的容器如 `std::vector`、`std::list`、`std::map` 等都满足这一条件
std::vector<int> v = {4, 5, 6};
for (int value : v) {
std::cout << value << " ";
}