Skip to content

Ch 16: 最佳实践

  • 遵循 C++ 代码规范
  • 编写有效的单元测试
  • 使用 Doxygen 生成文档
  • 实现跨平台开发
  • 了解常用工具链
类型风格示例
变量snake_case 或 camelCaseuser_name, userName
常量kConstantName 或 CONSTANT_NAMEkMaxRetries, MAX_RETRIES
函数snake_case 或 camelCasecalculate_total(), calculateTotal()
类PascalCaseBankAccount, HttpClient
命名空间snake_casemath_utils, http_server
模板参数PascalCase 或 snake_casetypename T, typename ValueType
成员变量member_ 或 memberNamecount_, count

Google C++ 风格指南的命名规则:

#include <string>
#include <vector>
// 类名:PascalCase
class BankAccount {
public:
// 构造函数
BankAccount(const std::string& name, double balance)
: account_name_(name), balance_(balance) {}
// 公开方法:camelCase
std::string GetAccountName() const { return account_name_; }
double GetBalance() const { return balance_; }
// 私有成员:_suffix
private:
std::string account_name_;
double balance_;
};
// 常量:k 前缀
const int kMaxConnections = 100;
const double kPi = 3.14159265358979;
// 全局变量:g 前缀(尽量避免)
int g_global_counter = 0;
// 枚举值:k 前缀或全部大写
enum class ErrorCode {
kSuccess = 0,
kNotFound = 404,
kInternalError = 500
};
// 命名空间:全小写
namespace math_utils {
double calculate_area(double radius) {
return kPi * radius * radius;
}
}

使用 .clang-format 保持代码风格一致:

.clang-format
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 100
PointerAlignment: Left
ReferenceAlignment: Left
foo.h
#ifndef PROJECT_FOO_H // 或 #pragma once
#define PROJECT_FOO_H
// 1. 先包含项目头文件
#include "project/bar.h"
// 2. 再包含依赖库
#include <vector>
#include <string>
// 3. 前向声明(减少编译依赖)
class Baz; // 前向声明
// 4. 避免 using 声明在头文件
// using std::string; // 不好
#endif
// foo.cpp
#include "foo.h"
#include "project/baz.h" // 实际需要的才包含
框架特点适用场景
Google Test功能丰富,跨平台大型项目
doctest头文件only,轻量快速开发
Catch2BDD 风格,语法简洁中小项目
Boost.Test功能全面Boost 项目
math_test.cpp
#include <gtest/gtest.h>
#include "math.h"
// 测试夹具(可选)
class MathTest : public ::testing::Test {
protected:
void SetUp() override {
// 每个测试前调用
}
void TearDown() override {
// 每个测试后调用
}
};
// 简单测试
TEST(MathTest, Add) {
EXPECT_EQ(add(1, 2), 3);
EXPECT_EQ(add(-1, 1), 0);
}
// 浮点比较(使用近似)
TEST(MathTest, Divide) {
EXPECT_DOUBLE_EQ(divide(10.0, 3.0), 3.333333);
EXPECT_NEAR(divide(10.0, 3.0), 3.333, 0.001);
}
// 测试异常
TEST(MathTest, DivideByZero) {
EXPECT_THROW(divide(1, 0), std::invalid_argument);
}
// 测试布尔条件
TEST(MathTest, IsEven) {
EXPECT_TRUE(is_even(4));
EXPECT_FALSE(is_even(5));
}
// 参数化测试
class AddTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};
TEST_P(AddTest, HandlesParameters) {
auto [a, b, expected] = GetParam();
EXPECT_EQ(add(a, b), expected);
}
INSTANTIATE_TEST_CASE_P(
PositiveNumbers,
AddTest,
::testing::Values(
std::make_tuple(1, 2, 3),
std::make_tuple(0, 0, 0),
std::make_tuple(-1, 1, 0)
)
);
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
#include "math.h"
int add(int a, int b) { return a + b; }
TEST_CASE("addition") {
CHECK(add(1, 2) == 3);
CHECK(add(-1, 1) == 0);
}
TEST_CASE("division") {
CHECK(divide(10, 2) == 5);
CHECK_THROWS(divide(1, 0)); // 测试异常
}
Terminal window
# 使用 gcov (GCC) 或 lcov 生成覆盖率报告
g++ -fprofile-arcs -ftest-coverage -o test test.cpp
./test
gcov test.cpp
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage
┌─────────┐
│ E2E │ 少量:端到端测试
│ Tests │
┌┴─────────┴┐
│ Integration│ 中等:模块集成测试
│ Tests │
┌┴───────────┴┐
│ Unit │ 大量:单元测试
│ Tests │
└─────────────┘
Terminal window
# Doxyfile 基础配置
PROJECT_NAME = "My Project"
INPUT = src/
OUTPUT_DIRECTORY = docs/
RECURSIVE = YES
HAVE_DOT = YES
GENERATE_HTML = YES
GENERATE_LATEX = YES
/**
* @file math.h
* @brief Mathematical utility functions
*
* This file provides basic mathematical operations
* including basic arithmetic and advanced functions.
*/
#include <cmath>
namespace math {
/**
* @brief Calculate the greatest common divisor
* @param a First integer
* @param b Second integer
* @return The GCD of a and b
* @pre a and b must be non-negative
*
* This function uses the Euclidean algorithm to compute
* the greatest common divisor efficiently.
*
* @code
* int result = gcd(48, 18); // returns 6
* @endcode
*/
int gcd(int a, int b);
/**
* @brief A class representing a 2D point
*/
class Point {
public:
/**
* @brief Constructor
* @param x X coordinate
* @param y Y coordinate
*/
Point(double x, double y) : x_(x), y_(y) {}
/**
* @brief Get X coordinate
* @return X coordinate value
*/
double x() const { return x_; }
/**
* @brief Get Y coordinate
* @return Y coordinate value
*/
double y() const { return y_; }
private:
double x_;
double y_;
};
} // namespace math
Terminal window
# 生成 HTML 和 LaTeX 文档
doxygen Doxyfile
# 查看 HTML 文档
open docs/html/index.html
#include <iostream>
// 检测操作系统
#ifdef _WIN32
#include <windows.h>
const char* PATH_SEP = "\\";
#define SLEEP(ms) Sleep(ms)
#elif __linux__
#include <unistd.h>
const char* PATH_SEP = "/";
#define SLEEP(ms) usleep((ms) * 1000)
#elif __APPLE__
#include <mach/mach.h>
const char* PATH_SEP = "/";
#define SLEEP(ms) usleep((ms) * 1000)
#endif
// 检测编译器
#ifdef __GNUC__
#define COMPILER "GCC"
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#elif _MSC_VER
#define COMPILER "MSVC"
#define MSVC_VERSION _MSC_VER
#endif
// 检测 C++ 标准
#ifdef __cplusplus
#if __cplusplus >= 202002L
#define CXX_STANDARD "C++20"
#elif __cplusplus >= 201703L
#define CXX_STANDARD "C++17"
#elif __cplusplus >= 201402L
#define CXX_STANDARD "C++14"
#endif
#endif
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# C++ 标准
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 平台特定配置
if(WIN32)
add_definitions(-D_WIN32_WINNT=0x0601)
add_compile_options(/W4)
elseif(UNIX AND NOT APPLE)
add_compile_options(-Wall -Wextra -pedantic)
elseif(APPLE)
add_compile_options(-Wall -Wextra)
endif()
# 查找依赖
find_package(Threads REQUIRED)
# 源文件
set(SOURCES
src/main.cpp
src/utils.cpp
)
# 创建可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})
# 链接库
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads)
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
void file_operations() {
// 创建目录(跨平台)
fs::create_directories("data/output");
// 路径拼接
fs::path config_path = fs::current_path() / "config" / "settings.json";
// 检查存在
if (fs::exists(config_path)) {
// 读取文件
}
// 遍历目录
for (const auto& entry : fs::directory_iterator("data")) {
std::cout << entry.path() << std::endl;
}
// 获取用户目录
fs::path home = fs::path(getenv("HOME") ?: "");
#ifdef _WIN32
home = fs::path(getenv("USERPROFILE") ?: "");
#endif
}
#include <thread>
#include <mutex>
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
// 线程优先级设置
void set_thread_priority(std::thread& t, int priority) {
#ifdef _WIN32
SetThreadPriority(t.native_handle(), priority);
#else
// POSIX: 0-99,值越大优先级越高
struct sched_param param;
param.sched_priority = priority;
pthread_setschedparam(t.native_handle(), SCHED_FIFO, &param);
#endif
}
project/
# 目录结构
# ├── CMakeLists.txt
# ├── cmake/
# │ └── FindPackage.cmake
# ├── include/
# │ └── project/
# │ └── header.h
# ├── src/
# │ └── source.cpp
# └── tests/
# └── test.cpp
# 主 CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 导出编译_commands
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 包含 cmake 目录
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# 库
add_library(my_lib STATIC
src/lib.cpp
)
target_include_directories(my_lib PUBLIC
${PROJECT_SOURCE_DIR}/include
)
# 可执行文件
add_executable(my_app
src/main.cpp
)
target_link_libraries(my_app PRIVATE
my_lib
)
# 测试
enable_testing()
add_subdirectory(tests)
Terminal window
# 安装 vcpkg
git clone https://github.com/Microsoft/vcpkg.git
./vcpkg/bootstrap-vcpkg.sh
# 安装依赖
vcpkg install nlohmann-json fmt spdlog
# CMake 集成
# CMakeLists.txt
find_package(nlohmann_json REQUIRED)
find_package(fmt REQUIRED)
target_link_libraries(my_app PRIVATE
nlohmann_json::nlohmann_json
fmt::fmt
)
Terminal window
# 安装
brew install clang-format clang-tidy
# 运行
clang-tidy src/*.cpp
# 使用配置文件 .clang-tidy
Checks: '-*,modernize-*,readability-*'
WarningsAsErrors: ''
HeaderFilterRegex: '.*\\.(h|hpp)$'
FormatStyle: google
cmake-format.yaml
indentation: 4
command_case: lowercase
keyword_case: upper
# CMake 设置优化级别
if(CMAKE_BUILD_TYPE STREQUAL "Release")
target_compile_options(my_app PRIVATE
-O3
-march=native # 针对本地 CPU 优化
-mtune=native
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(my_app PRIVATE
-g
-O0
-fsanitize=address,undefined # 调试工具
)
endif()
// 1. 预分配容量
std::vector<int> v;
v.reserve(10000); // 避免多次重分配
// 2. 使用移动语义
void process(std::vector<int> data) { // 按值传递,调用方应 std::move
// 处理数据
}
// 3. 避免不必要的虚函数
// 虚函数有 vptr 和 vtable 跳转开销
// 4. 使用 constexpr
constexpr int fib(int n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
// 5. 使用 inline 优化小函数
inline int square(int x) { return x * x; }
// 6. 避免 dynamic_cast(运行时类型检查开销大)
  • 遵循统一的代码风格指南(推荐 Google 风格)
  • 单元测试是代码质量的基础
  • Doxygen 生成专业文档
  • CMake 是跨平台构建的事实标准
  • 条件编译处理平台差异
  • 静态分析工具提升代码质量
  • 性能优化需基于实际 profiling 结果

下章预告:ch17 参考资源。