前置知识: C++

C++ 异常处理与性能优化

10 minAdvanced

异常机制、错误处理策略、性能分析与优化技巧。

1. 异常处理 (Exceptions)

异常是 C++ 中处理错误的一种机制,允许程序在遇到错误时跳转到相应的处理代码。

1.1 基本异常处理

使用 try-catch 块捕获和处理异常。

 #include <iostream>
 #include <stdexcept>
 int main() {
  try {
  // 可能抛出异常的代码
  int divisor = 0;
  if (divisor == 0) {
  throw std::runtime_error("Division by zero");
  }
  int result = 10 / divisor;
  } catch (const std::exception& e) {
  // 异常处理代码
  std::cerr << "Exception caught: " << e.what() << std::endl;
  }
  return 0;
 }

1.2 异常

C++ 标准库提供了多种异常型,位于 <stdexcept> 头文件中。

异常描述示例
std::exception所有标准异常的基,通常不直接使用
std::runtime_error运行时错误throw std::runtime_error("Runtime error");
std::logic_error逻辑错误throw std::logic_error("Logic error");
std::bad_alloc内存分配失败new 操作符抛出
std::out_of_range越界访问throw std::out_of_range("Out of range");
std::invalid_argument无效参数throw std::invalid_argument("Invalid argument");

1.3 自定义异常

可以通过继承 std::exception 或其子来创建自定义异常。

 #include <iostream>
 #include <stdexcept>
 class MyException : public std::exception {
 private:
  std::string message;
 public:
  explicit MyException(const std::string& msg) : message(msg) {}
  const char* what() const noexcept override {
  return message.c_str();
  }
 }
 void function_that_throws() {
  throw MyException("Custom exception occurred");
 }
 int main() {
  try {
  function_that_throws();
  } catch (const MyException& e) {
  std::cerr << "Custom exception caught: " << e.what() << std::endl;
  } catch (const std::exception& e) {
  std::cerr << "Standard exception caught: " << e.what() << std::endl;
  }
  return 0;
 }

1.4 异常处理最佳实践

  • 只在特殊情况下使用异常: 异常用于处理意外情况,而不是常规控制流。
  • 捕获具体异常类型: 优先捕获具体的异常型,而不是通用的 std::exception
  • 保持异常处理代码简洁: 异常处理代码应该简洁明了,只处理必要的逻辑。
  • 析构函数不抛异常: 析构函数抛出异常会导致程序终止。
  • 使用 RAII 管理资源: 利用 RAII 机制确保资源在异常发生时正确释放。
  • 异常规范 (C++11 前): 使用 throw()throw(type) 声明函数可能抛出的异常型(C++11 后推荐使用 noexcept)。

1.5 noexcept 说明符 (C++11)

noexcept 说明符用于声明函数不会抛出异常,帮助编译器优化。

 // 声明函数不会抛出异常
 void function_noexcept() noexcept {
  // 函数体
 }
 // 条件 noexcept
 void function_conditionally_noexcept() noexcept(noexcept(expression)) {
  // 函数体
 }
 // 检查函数是否会抛出异常
 template <typename T>
 void check_noexcept() {
  static_assert(noexcept(std::declval<T>().some_method()),
  "some_method() must be noexcept");
 }

1.6 异常与构造函数/析构函数

  • 构造函数: 可以抛出异常,但需要确保已分配的资源被正确释放。
  • 析构函数: 严禁抛出异常,否则会导致程序终止。
 class Resource {
 private:
  int* data;
 public:
  Resource(int size) {
  data = new int[size];
  // 如果分配失败,new 会抛出 std::bad_alloc
  }
  ~Resource() noexcept { // 析构函数应标记为 noexcept
  delete[] data;
  // 析构函数中不应抛出异常
  }
 }

2. 性能优化 (Performance)

性能优化是 C++ 编程中的重要环节,涉及代码设计、编译器优化、内存管理等多个方面。

2.1 代码级优化

2.1.1 内联函数

内联函数可以减少函数调用开销,适用于短小频繁调用的函数。

 // 内联函数声明
 inline int max(int a, int b) {
  return a > b ? a : b;
 }
 // 类内定义的成员函数默认内联
 class MyClass {
 public:
  int getValue() const { // 默认内联
  return value;
  }
 private:
  int value;
 }

2.1.2 常量优化

使用 const 可以帮助编译器进行优化,同时提高代码安全性。

 // 常量引用参数,避免拷贝
 void printValue(const std::string& str) {
  std::cout << str << std::endl;
 }
 // 常量成员函数,保证不修改对象状态
 class MyClass {
 public:
  int getValue() const { // 常量成员函数
  return value;
  }
 private:
  int value;
 }
 // 编译期常量
 constexpr int square(int x) {
  return x * x;
 }
 constexpr int SQUARE_OF_5 = square(5); // 编译期计算

2.1.3 移动语义 (C++11)

移动语义可以避免昂贵的深拷贝,提高性能。

 #include <iostream>
 #include <vector>
 #include <string>
 class MyClass {
 private:
  std::string data;
 public:
  // 构造函数
  MyClass(const std::string& d) : data(d) {
  std::cout << "Copy constructor called" << std::endl;
  }
  // 移动构造函数
  MyClass(std::string&& d) : data(std::move(d)) {
  std::cout << "Move constructor called" << std::endl;
  }
  // 移动赋值运算符
  MyClass& operator=(std::string&& d) {
  data = std::move(d);
  std::cout << "Move assignment called" << std::endl;
  return *this;
  }
 }
 int main() {
  // 使用移动语义
  MyClass obj1("Hello"); // 拷贝构造
  MyClass obj2(std::move(std::string("World"))); // 移动构造
  std::string s = "Test";
  MyClass obj3(std::move(s)); // 移动构造,s 现在为空
  return 0;
 }

2.1.4 避免不必要的拷贝

使用引用指针移动语义避免不必要的拷贝操作。

 // 不好的做法:拷贝参数
 std::vector<int> process_vector(std::vector<int> v) {
  // 处理 v
  return v; // 返回时再次拷贝
 }
 // 好的做法:使用引用
 void process_vector(const std::vector<int>& v) {
  // 处理 v(只读)
 }
 // 好的做法:使用移动语义
 std::vector<int> create_vector() {
  std::vector<int> v = {1, 2, 3, 4, 5};
  return v; // 编译器会进行返回值优化 (RVO)
 }

2.1.5 预分配内存

对于容器,预先分配内存可以减少动态内存分配的次数。

 std::vector<int> v;
 v.reserve(1000); // 预先分配 1000 个元素的空间
 for (int i = 0; i < 1000; i++) {
  v.push_back(i); // 不需要频繁重新分配内存
 }

2.2 编译器优化

2.2.1 优化级别

不同的编译器优化级别会对代码性能产生显著影响。

优化级别描述适用场景
-O0无优化调试阶段
-O1基本优化平衡调试和性能
-O2更高级别优化生产环境
-O3最高级别优化对性能要求高的场景
-Os优化代码大小内存受限环境

2.2.2 编译器特定优化

不同编译器有特定的优化选项。

  • GCC: -march=native (使用本地 CPU 架构), -ffast-math (快速数学运算)
  • Clang: -Weverything (开启所有警告), -fsanitize=address (地址 sanitizer)
  • MSVC: /O2 (优化速度), /Oi (内联函数)

2.3 内存管理优化

2.3.1 智能指针

使用智能指针管理内存,避免内存泄漏

 #include <memory>
 // 推荐使用 make_unique 和 make_shared
 std::unique_ptr<int> up = std::make_unique<int>(42);
 std::shared_ptr<int> sp = std::make_shared<int>(100);
 // 避免循环引用
 class A {
 public:
  std::weak_ptr<B> b; // 使用 weak_ptr 打破循环
 }
 class B {
 public:
  std::shared_ptr<A> a;
 }

2.3.2 内存池

对于频繁分配和释放小对象的场景,使用内存池可以提高性能。

 // 简单的内存池实现
 class MemoryPool {
 private:
  std::vector<void*> blocks;
  size_t blockSize;
  size_t currentBlockIndex;
  size_t currentPosition;
 public:
  MemoryPool(size_t blockSize, size_t initialBlocks = 10)
  : blockSize(blockSize), currentBlockIndex(0), currentPosition(0) {
  for (size_t i = 0; i < initialBlocks; i++) {
  blocks.push_back(std::malloc(blockSize));
  }
  }
  ~MemoryPool() {
  for (void* block : blocks) {
  std::free(block);
  }
  }
  void* allocate() {
  if (currentBlockIndex >= blocks.size() || currentPosition >= blockSize) {
  blocks.push_back(std::malloc(blockSize));
  currentBlockIndex = blocks.size() - 1;
  currentPosition = 0;
  }
  void* result = static_cast<char*>(blocks[currentBlockIndex]) + currentPosition;
  currentPosition += sizeof(int); // 假设分配 int 大小的内存
  return result;
  }
  void deallocate(void* ptr) {
  // 简单实现,不做实际释放
  }
 }

2.4 算法与数据结构优化

选择合适的算法和数据结构对性能至关重要。

场景推荐数据结构时间复杂度
随机访问std::vectorO(1)
频繁插入/删除std::listO(1)
查找操作std::unordered_mapO(1) 平均
有序集合std::setO(log n)
优先级队列std::priority_queueO(log n)

2.5 并行计算

利用多核处理器进行并行计算可以显著提高性能。

2.5.1 标准库并行算法 (C++17)

 #include <algorithm>
 #include <execution>
 #include <vector>
 int main() {
  std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
  // 并行排序
  std::sort(std::execution::par, v.begin(), v.end());
  // 并行变换
  std::transform(std::execution::par, v.begin(), v.end(), v.begin(),
  [](int x) { return x * 2; });
  return 0;
 }

2.5.2 线程库 (C++11)

 #include <thread>
 #include <vector>
 #include <iostream>
 void process_chunk(const std::vector<int>& data, size_t start, size_t end) {
  for (size_t i = start; i < end; i++) {
  // 处理数据
  std::cout << data[i] << " ";
  }
 }
 int main() {
  std::vector<int> data(1000);
  for (int i = 0; i < 1000; i++) {
  data[i] = i;
  }
  // 创建线程
  std::vector<std::thread> threads;
  size_t chunk_size = data.size() / 4;
  for (size_t i = 0; i < 4; i++) {
  size_t start = i * chunk_size;
  size_t end = (i == 3) ? data.size() : (i + 1) * chunk_size;
  threads.emplace_back(process_chunk, std::ref(data), start, end);
  }
  // 等待所有线程完成
  for (auto& t : threads) {
  t.join();
  }
  return 0;
 }

3. 性能分析与调试工具

3.1 性能分析工具

3.1.1 Google Benchmark

Google Benchmark 是一个用于基准测试的框架,可以测量代码的执行性能。

 #include <benchmark/benchmark.h>
 static void BM_Square(benchmark::State& state) {
  for (auto _ : state) {
  int result = 0;
  for (int i = 0; i < 1000; i++) {
  result += i * i;
  }
  benchmark::DoNotOptimize(result);
  }
 }
 BENCHMARK(BM_Square);
 BENCHMARK_MAIN();

3.1.2 gprof

gprof 是 GCC 提供的性能分析工具,可以分析函数调用次数和执行时间。

 # 编译时添加 -pg 选项
 g++ -pg -O2 program.cpp -o program
 # 运行程序,生成 gmon.out 文件
 ./program
 # 分析结果
 gprof program gmon.out > analysis.txt

3.1.3 perf

perf 是 Linux 系统下的性能分析工具,可以分析 CPU 使用率、缓存命中率等。

 # 记录性能数据
 perf record ./program
 # 查看分析结果
 perf report
 # 查看热点函数
 perf top -p <pid>

3.2 内存分析工具

3.2.1 Valgrind

Valgrind 是一个内存调试和内存泄漏检测工具。

 # 检测内存泄漏
 valgrind --leak-check=full ./program
 # 检测内存访问错误
 valgrind --tool=memcheck ./program
 # 检测缓存使用情况
 valgrind --tool=cachegrind ./program

3.2.2 AddressSanitizer

AddressSanitizer (ASan) 是一个内存错误检测工具,集成在 GCC 和 Clang 中。

 # 编译时添加 -fsanitize=address 选项
 g++ -fsanitize=address -g program.cpp -o program
 # 运行程序
 ./program

3.3 调试工具

3.3.1 GDB

GDB 是一个强大的命令行调试器。

 # 编译时添加 -g 选项
 g++ -g program.cpp -o program
 # 启动 GDB
 gdb ./program
 # 常用命令
 # break main # 在 main 函数处设置断点
 # run # 运行程序
 # print variable # 打印变量值
 # step # 单步执行
 # continue # 继续执行
 # backtrace # 查看调用栈

3.3.2 LLDB

LLDB 是 LLVM 项目的调试器,功能似于 GDB。

 # 编译时添加 -g 选项
 clang++ -g program.cpp -o program
 # 启动 LLDB
 lldb ./program
 # 常用命令
 # breakpoint set --name main # 在 main 函数处设置断点
 # run # 运行程序
 # print variable # 打印变量值
 # step # 单步执行
 # continue # 继续执行
 # thread backtrace # 查看调用栈

3.3.3 可视化调试器

  • Visual Studio: Windows 平台的集成开发环境,提供强大的可视化调试功能。
  • CLion: JetBrains 开发的跨平台 IDE,集成了 GDB/LLDB 调试器。
  • VS Code: 轻量级编辑器,通过插件支持调试功能。

4. 性能优化最佳实践

4.1 分析先行

  • 使用性能分析工具:在优化前,先使用性能分析工具找出性能瓶颈。
  • 建立基准测试:创建基准测试用例,用于评估优化效果。
  • 测量而不是猜测:基于实际测量结果进行优化,而不是凭感觉。

4.2 代码优化

  • 优先优化热点代码:重点优化执行频率高的代码。
  • 避免过早优化:先确保代码正确,再进行优化。
  • 保持代码可读性:优化不应以牺牲代码可读性为代价。
  • 使用适当的数据结构:根据具体场景选择合适的数据结构。
  • 减少内存分配:避免频繁的动态内存分配和释放。

4.3 编译优化

  • 选择合适的优化级别:根据实际需求选择适当的编译器优化级别。
  • 启用架构特定优化:使用 -march=native 等选项利用 CPU 特性
  • 使用链接时优化:启用 -flto (Link Time Optimization) 进行全局优化。

4.4 内存管理

  • 使用智能指针:避免内存泄漏和悬空指针
  • 合理使用内存池:对于频繁分配的小对象,使用内存池提高性能。
  • 注意内存对齐:合理安排数据结构,提高缓存命中率。

4.5 并行计算

  • 利用多线程:对于计算密集型任务,使用多线程并行处理。
  • 避免线程竞争:使用互斥锁、原子操作等同步机制避免线程竞争。
  • 使用标准库并行算法:优先使用 C++17 提供的并行算法。

5. 代码示例

5.1 异常处理示例

 #include <iostream>
 #include <stdexcept>
 #include <string>
 // 自定义异常类
 class FileException : public std::runtime_error {
 public:
  explicit FileException(const std::string& message)
  : std::runtime_error(message) {}
 }
 // 文件操作类
 class FileHandler {
 private:
  std::string filename;
 public:
  FileHandler(const std::string& name) : filename(name) {
  // 模拟文件打开失败
  if (name.empty()) {
  throw FileException("Empty filename");
  }
  std::cout << "File " << name << " opened" << std::endl;
  }
  ~FileHandler() noexcept {
  // 析构函数不应抛出异常
  std::cout << "File " << filename << " closed" << std::endl;
  }
  void read() {
  // 模拟读取失败
  if (filename == "error.txt") {
  throw FileException("Failed to read file");
  }
  std::cout << "Reading from file " << filename << std::endl;
  }
 }
 int main() {
  try {
  // 测试正常情况
  FileHandler file1("data.txt");
  file1.read();
  // 测试异常情况
  FileHandler file2("");
  } catch (const FileException& e) {
  std::cerr << "File exception: " << e.what() << std::endl;
  } catch (const std::exception& e) {
  std::cerr << "Standard exception: " << e.what() << std::endl;
  } catch (...) {
  std::cerr << "Unknown exception" << std::endl;
  }
  try {
  FileHandler file3("error.txt");
  file3.read();
  } catch (const FileException& e) {
  std::cerr << "File exception: " << e.what() << std::endl;
  }
  return 0;
 }

5.2 性能优化示例

 #include <iostream>
 #include <vector>
 #include <chrono>
 #include <algorithm>
 #include <execution>
 // 测量函数执行时间的模板函数
 template <typename Func>
 double measure_time(Func&& func) {
  auto start = std::chrono::high_resolution_clock::now();
  func();
  auto end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration<double, std::milli>(end - start).count();
 }
 // 普通排序
 void regular_sort(std::vector<int>& v) {
  std::sort(v.begin(), v.end());
 }
 // 并行排序
 void parallel_sort(std::vector<int>& v) {
  std::sort(std::execution::par, v.begin(), v.end());
 }
 // 不使用 reserve
 void without_reserve() {
  std::vector<int> v;
  for (int i = 0; i < 1000000; i++) {
  v.push_back(i);
  }
 }
 // 使用 reserve
 void with_reserve() {
  std::vector<int> v;
  v.reserve(1000000);
  for (int i = 0; i < 1000000; i++) {
  v.push_back(i);
  }
 }
 int main() {
  // 测试排序性能
  std::vector<int> v1(1000000);
  std::generate(v1.begin(), v1.end(), []() { return rand(); });
  std::vector<int> v2 = v1;
  double time_regular = measure_time([&]() { regular_sort(v1); });
  double time_parallel = measure_time([&]() { parallel_sort(v2); });
  std::cout << "Regular sort: " << time_regular << " ms" << std::endl;
  std::cout << "Parallel sort: " << time_parallel << " ms" << std::endl;
  std::cout << "Speedup: " << time_regular / time_parallel << "x" << std::endl;
  // 测试 reserve 性能
  double time_without_reserve = measure_time(without_reserve);
  double time_with_reserve = measure_time(with_reserve);
  std::cout << "Without reserve: " << time_without_reserve << " ms" << std::endl;
  std::cout << "With reserve: " << time_with_reserve << " ms" << std::endl;
  std::cout << "Speedup: " << time_without_reserve / time_with_reserve << "x" << std::endl;
  return 0;
 }

5.3 内存管理示例

 #include <iostream>
 #include <memory>
 #include <vector>
 // 自定义删除器
 struct CustomDeleter {
  void operator()(int* p) {
  std::cout << "Custom deleter called" << std::endl;
  delete p;
  }
 }
 int main() {
  // 智能指针示例
  std::cout << "=== Unique_ptr ===" << std::endl;
  {
  std::unique_ptr<int> up1(new int(42));
  std::cout << "up1 value: " << *up1 << std::endl;
  // 转移所有权
  std::unique_ptr<int> up2 = std::move(up1);
  if (!up1) {
  std::cout << "up1 is null" << std::endl;
  }
  std::cout << "up2 value: " << *up2 << std::endl;
  } // up2 超出作用域,自动释放
  std::cout << "\n=== Shared_ptr ===" << std::endl;
  {
  std::shared_ptr<int> sp1 = std::make_shared<int>(100);
  std::cout << "sp1 use count: " << sp1.use_count() << std::endl;
  {
  std::shared_ptr<int> sp2 = sp1;
  std::cout << "After sp2 creation, use count: " << sp1.use_count() << std::endl;
  } // sp2 超出作用域,引用计数减 1
  std::cout << "After sp2 destruction, use count: " << sp1.use_count() << std::endl;
  } // sp1 超出作用域,引用计数为 0,自动释放
  std::cout << "\n=== Weak_ptr ===" << std::endl;
  {
  std::shared_ptr<int> sp = std::make_shared<int>(200);
  std::weak_ptr<int> wp = sp;
  std::cout << "sp use count: " << sp.use_count() << std::endl;
  std::cout << "wp expired: " << wp.expired() << std::endl;
  if (auto locked = wp.lock()) {
  std::cout << "Locked value: " << *locked << std::endl;
  std::cout << "Locked use count: " << locked.use_count() << std::endl;
  }
  sp.reset(); // 释放 shared_ptr
  std::cout << "After sp.reset(), wp expired: " << wp.expired() << std::endl;
  if (auto locked = wp.lock()) {
  std::cout << "Locked value: " << *locked << std::endl;
  } else {
  std::cout << "wp is expired, cannot lock" << std::endl;
  }
  }
  std::cout << "\n=== Custom deleter ===" << std::endl;
  {
  std::unique_ptr<int, CustomDeleter> up(new int(300));
  std::cout << "up value: " << *up << std::endl;
  } // 自动调用自定义删除器
  return 0;
 }

更新日志 (Changelog)

  • 2026-04-05: 整合 C++ 异常处理与基础优化技巧。
  • 2026-04-05: 扩写内容,增加详细的异常处理机制、性能优化技术、调试工具、最佳实践和代码示例等内容。