C++ Core Guidelines 阅读笔记(4):表达式与语句
发布于:
前言
本文是 C++ Core Guidelines 阅读笔记的第四篇,重点讨论表达式和语句的使用规范。正确的表达式和语句使用可以提高代码的可读性和安全性。
1. 表达式设计
表达式是代码的基本单元,良好的表达式设计提高代码可读性和安全性。
1.1 优先使用标准库算法
标准库算法经过优化,通常比自己手写的循环更高效、更安全:
// 好的:使用标准库算法
auto it = std::find(vec.begin(), vec.end(), value);
if (it != vec.end()) {
process(*it);
}
// 不好:手写循环
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it == value) {
process(*it);
break;
}
}
1.2 避免复杂的表达式
复杂表达式难以理解和维护,应该拆分为多个简单表达式:
// 好的:简单表达式
bool condition1 = check_condition1();
bool condition2 = check_condition2();
bool is_valid = condition1 && condition2;
// 不好:复杂表达式
bool is_valid = (a > b && c < d) || (e == f && g != h) && (i > j);
// 难以理解,容易出错
1.3 使用括号明确优先级
即使运算符优先级明确,使用括号也能提高可读性:
// 好的:使用括号
if ((a > b) && (c < d)) {
// 优先级明确
}
// 不好:依赖运算符优先级
if (a > b && c < d) {
// 虽然正确,但需要记住优先级
}
2. 语句设计
2.1 优先使用范围 for
// 好的:范围 for
for (const auto& item : container) {
process(item);
}
// 不好:传统 for 循环
for (auto it = container.begin(); it != container.end(); ++it) {
process(*it);
}
2.2 避免 goto
// 好的:使用结构化控制流
void process() {
if (error) {
return;
}
// 继续处理
}
// 不好:使用 goto
void process() {
if (error) {
goto cleanup;
}
cleanup:
// 清理
}
2.3 使用 switch 而非长 if-else
// 好的:switch
switch (value) {
case 1: return "one";
case 2: return "two";
default: return "unknown";
}
// 不好:长 if-else
if (value == 1) return "one";
else if (value == 2) return "two";
else return "unknown";
3. 变量声明
3.1 在首次使用前声明
// 好的:在使用前声明
void process() {
// ... 其他代码
int value = compute_value(); // 在使用前声明
use(value);
}
// 不好:在函数开头声明所有变量
void process() {
int value; // 声明太早
// ... 很多代码
value = compute_value();
use(value);
}
3.2 初始化变量
// 好的:初始化变量
int count = 0;
std::string name;
// 不好:未初始化
int count; // 未初始化
std::string name; // 默认构造,但最好明确
3.3 使用 auto
// 好的:使用 auto
auto value = compute_value();
auto it = vec.begin();
// 不好:显式类型(如果类型复杂)
std::vector<int>::iterator it = vec.begin();
4. 条件语句
4.1 使用 if constexpr(C++17)
// 好的:if constexpr
template<typename T>
void process(T value) {
if constexpr (std::is_integral_v<T>) {
// 编译期分支
} else {
// 其他类型
}
}
4.2 避免深层嵌套
// 好的:早期返回
bool is_valid(const Data& data) {
if (data.empty()) return false;
if (data.size() > MAX_SIZE) return false;
if (!data.check()) return false;
return true;
}
// 不好:深层嵌套
bool is_valid(const Data& data) {
if (!data.empty()) {
if (data.size() <= MAX_SIZE) {
if (data.check()) {
return true;
}
}
}
return false;
}
5. 循环语句
5.1 使用范围 for
// 好的:范围 for
for (const auto& item : items) {
process(item);
}
// 不好:传统 for
for (size_t i = 0; i < items.size(); ++i) {
process(items[i]);
}
5.2 避免不必要的循环
// 好的:使用算法
bool found = std::any_of(vec.begin(), vec.end(),
[](int x) { return x > 10; });
// 不好:手写循环
bool found = false;
for (int x : vec) {
if (x > 10) {
found = true;
break;
}
}
6. 异常处理
6.1 使用异常表示错误
// 好的:使用异常
void process() {
if (error) {
throw std::runtime_error("Error occurred");
}
}
// 不好:使用错误码
int process() {
if (error) {
return -1; // 错误码
}
return 0;
}
6.2 捕获具体异常
// 好的:捕获具体异常
try {
process();
} catch (const std::runtime_error& e) {
handle_error(e);
} catch (const std::exception& e) {
handle_generic_error(e);
}
// 不好:捕获所有异常
try {
process();
} catch (...) {
// 不知道是什么错误
}
7. 类型转换
7.1 避免 C 风格转换
// 好的:使用 C++ 转换
int value = static_cast<int>(double_value);
Base* ptr = dynamic_cast<Derived*>(base_ptr);
// 不好:C 风格转换
int value = (int)double_value;
Base* ptr = (Derived*)base_ptr;
7.2 使用 const_cast 谨慎
// 好的:避免 const_cast
void process(const Data& data) {
// 不修改 data
}
// 不好:使用 const_cast
void process(const Data& data) {
const_cast<Data&>(data).modify(); // 危险
}
8. Lambda 表达式
8.1 使用 Lambda 简化代码
// 好的:使用 Lambda
std::sort(vec.begin(), vec.end(),
[](int a, int b) { return a > b; });
// 不好:函数对象
struct Compare {
bool operator()(int a, int b) const {
return a > b;
}
};
std::sort(vec.begin(), vec.end(), Compare());
8.2 避免过度捕获
// 好的:只捕获需要的变量
int threshold = 10;
std::count_if(vec.begin(), vec.end(),
[threshold](int x) { return x > threshold; });
// 不好:捕获所有
std::count_if(vec.begin(), vec.end(),
[&](int x) { return x > threshold; }); // 捕获所有引用
9. 性能考虑
9.1 避免不必要的拷贝
// 好的:使用引用
void process(const std::vector<int>& vec);
// 不好:值传递
void process(std::vector<int> vec); // 拷贝开销
9.2 使用移动语义
// 好的:移动语义
std::vector<int> create_data() {
std::vector<int> data;
// ...
return data; // 移动
}
// 不好:拷贝
std::vector<int> data = create_data(); // 可能拷贝
11. 实际工程案例
11.1 案例:表达式简化
问题:复杂表达式难以理解
// 原始代码:复杂表达式
if ((a > b && c < d) || (e == f && g != h) && (i > j)) {
process();
}
改进:拆分为简单表达式
// 改进代码:简单表达式
bool condition1 = (a > b) && (c < d);
bool condition2 = (e == f) && (g != h);
bool condition3 = (i > j);
bool is_valid = condition1 || (condition2 && condition3);
if (is_valid) {
process();
}
11.2 案例:循环优化
问题:手写循环容易出错
// 原始代码:手写循环
for (size_t i = 0; i < vec.size(); ++i) {
if (vec[i] > threshold) {
result.push_back(vec[i]);
}
}
改进:使用标准库算法
// 改进代码:标准库算法
std::copy_if(vec.begin(), vec.end(), std::back_inserter(result),
[threshold](int x) { return x > threshold; });
12. 设计模式应用
12.1 策略模式在表达式中的应用
13. 性能考虑
13.1 表达式求值的性能
13.2 循环性能对比
// 性能测试
void benchmark() {
std::vector<int> vec(1000000);
// 范围 for
auto start = std::chrono::high_resolution_clock::now();
for (const auto& x : vec) {
process(x);
}
auto end = std::chrono::high_resolution_clock::now();
// 标准库算法
start = std::chrono::high_resolution_clock::now();
std::for_each(vec.begin(), vec.end(), process);
end = std::chrono::high_resolution_clock::now();
// 通常性能相同,但算法更安全
}
14. 最佳实践检查清单
14.1 表达式设计检查清单
- 优先使用标准库算法
- 避免复杂表达式
- 使用括号明确优先级
- 使用范围 for 循环
- 避免 goto
- 在使用前声明变量
- 初始化所有变量
- 使用 auto 简化类型
15. 小结
表达式和语句应该清晰、简洁、安全。
核心要点:
- 表达式设计:优先使用标准库算法、避免复杂表达式、使用括号明确优先级
- 语句设计:使用范围 for、避免 goto、使用 switch 而非长 if-else
- 变量声明:在使用前声明、初始化变量、使用 auto
- 条件语句:使用 if constexpr、避免深层嵌套
- 循环语句:使用范围 for、避免不必要的循环
- 异常处理:使用异常表示错误、捕获具体异常
- 类型转换:避免 C 风格转换、使用 C++ 转换
- Lambda 表达式:简化代码、避免过度捕获
- 性能考虑:避免不必要的拷贝、使用移动语义
遵循这些规范,可以写出更清晰、更安全的 C++ 代码。