写出更安全、更高效、更优雅的 C++ 代码:10 个核心编程技巧
'写 C++ 不难,难的是写出正确、高效且可维护的 C++。' —— 每一位经历过段错误和内存泄漏的开发者
C++ 是一门'多范式'语言,功能强大却暗藏陷阱。幸运的是,随着 C++11/14/17/20 的演进,许多旧日痛点已被优雅解决。本文总结了在多年开发中反复验证、真正提升生产力的 10 个核心编程技巧,助你写出更现代、更健壮的 C++ 代码。
10 个现代 C++ 编程技巧,包括使用智能指针管理内存、利用 auto 简化类型、RAII 资源管理、emplace 优化容器插入、constexpr 编译期计算、explicit 防止隐式转换、override 明确重写意图、string_view 避免字符串拷贝、noexcept 标注不抛异常函数以及开启编译器警告。这些实践有助于编写更安全、高效且可维护的代码。
写出更安全、更高效、更优雅的 C++ 代码:10 个核心编程技巧
'写 C++ 不难,难的是写出正确、高效且可维护的 C++。' —— 每一位经历过段错误和内存泄漏的开发者
C++ 是一门'多范式'语言,功能强大却暗藏陷阱。幸运的是,随着 C++11/14/17/20 的演进,许多旧日痛点已被优雅解决。本文总结了在多年开发中反复验证、真正提升生产力的 10 个核心编程技巧,助你写出更现代、更健壮的 C++ 代码。
问题:手动 new/delete 极易导致内存泄漏、重复释放或悬空指针。
解决方案:
std::unique_ptr<T>:独占所有权,自动析构(零开销)std::shared_ptr<T>:共享所有权,引用计数管理std::weak_ptr<T>:打破 shared_ptr 循环引用// ❌ 危险 MyClass* obj = new MyClass(); // ... 忘记 delete?异常中断?
// ✅ 安全 auto obj = std::make_unique<MyClass>(); // 推荐使用 make_* 系列
黄金法则:除非与 C 接口交互,否则绝不使用裸
new。
auto 减少冗余,提升可读性与泛化能力auto 不仅简化代码,还能避免类型拼写错误,并天然支持泛型编程。
// 冗长且脆弱 std::unordered_map<std::string, std::vector<int>>::iterator it = data.begin();
// 清晰且健壮 auto it = data.begin();
// 范围 for + auto for (const auto& [key, values] : data) { // C++17 结构化绑定 // ... }
注意:在需要显式类型转换或接口契约明确时(如函数返回值),仍应写明类型。
RAII 是 C++ 的灵魂。所有资源(内存、文件、锁、网络连接)都应通过对象生命周期管理。
class FileGuard {
FILE* fp_;
public:
FileGuard(const char* name) : fp_(fopen(name, "r")) {
if (!fp_) throw std::runtime_error("Open failed");
}
~FileGuard() { if (fp_) fclose(fp_); }
FILE* get() const { return fp_; }
};
// 使用
{
FileGuard file("data.txt");
// 文件自动关闭,即使发生异常
}
STL 中的
std::lock_guard、std::fstream都是 RAII 的典范。
emplace_back / emplace 而非 push_back避免不必要的临时对象构造与拷贝。
std::vector<std::string> v;
v.push_back("hello"); // 构造临时 string,再移动
v.emplace_back("hello"); // 直接在 vector 内存中构造 string
// 对复杂对象更明显 v.emplace_back(100, 'x'); // 调用 string(size_t, char)
同理适用于 map::emplace、queue::emplace 等。
constexpr 将计算移到编译期让程序更快、更小、更安全。
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int arr[factorial(5)]; // 编译期确定大小,无需运行时计算
C++20 更进一步支持 constexpr 虚函数、动态内存(有限制)等。
explicit 保护构造函数防止意外的类型转换引发逻辑错误。
class Buffer {
public:
explicit Buffer(size_t size) : data_(new char[size]) {}
// ...
};
Buffer b1(1024); // OK
Buffer b2 = 1024; // ❌ 编译错误(若未加 explicit 则危险通过!)
建议:所有单参数构造函数默认加
explicit,除非你明确需要隐式转换。
override 和 final 明确你的意图override:确保你确实重写了基类虚函数(拼写错误会报错)final:禁止进一步继承或重写,提升性能与设计清晰度class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle final : public Shape {
public:
double area() const override { return 3.14 * r_ * r_; }
};
编译器会检查
override是否匹配基类签名,极大减少调试时间。
std::string_view 避免不必要的字符串拷贝(C++17)当你只需要'读'字符串而不修改它时,string_view 是零拷贝的完美选择。
void process(const std::string& s); // 可能触发拷贝(如传字面量)
void process(std::string_view sv); // 无拷贝,兼容 string、char*、字面量
process("Hello"); // 高效!
注意:
string_view不拥有数据,确保底层字符串生命周期足够长。
noexcept 标注不会抛异常的函数帮助编译器优化(如移动操作),并提升异常安全性。
class MyVector {
T* data_;
public:
MyVector(MyVector&& other) noexcept // 移动构造不应抛异常
: data_(other.data_) {
other.data_ = nullptr;
}
};
STL 在可能的情况下会优先选择 noexcept 的版本(如 vector::resize)。
在 CMake 或 Makefile 中加入:
target_compile_options(my_target PRIVATE -Wall -Wextra -Wpedantic -Werror -Wshadow -Wnon-virtual-dtor -Wold-style-cast )
这些选项能帮你提前发现:
经验之谈:一个干净的编译输出,是高质量代码的第一道防线。
这些技巧不仅仅是'语法糖',它们代表了现代 C++ 的核心思想:
掌握它们,你写的就不再是'带类的 C',而是真正现代化、工业化、可维护的 C++。
延伸建议:定期阅读《C++ Core Guidelines》,在 Code Review 中强制应用上述规则,用 Clang-Tidy 自动化代码规范检查。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online