C++ 常用编程技巧总结

0 阅读10分钟

C++ 常用编程技巧总结

涵盖 C++11/14/17/20 现代特性与最佳实践


一、现代 C++ 核心特性

1.1 auto 关键字

// 自动类型推导
auto i = 42;              // int
auto d = 3.14;            // double
auto s = "hello";         // const char*
auto v = std::vector<int>{1, 2, 3};

// 迭代器简化
std::map<std::string, int> m;
for (auto it = m.begin(); it != m.end(); ++it) { }

// 范围for循环
for (auto& item : container) { }

1.2 智能指针

#include <memory>

// unique_ptr - 独占所有权
std::unique_ptr<int> p1 = std::make_unique<int>(42);
auto p2 = std::make_unique<int>(100);

// shared_ptr - 共享所有权
auto sp1 = std::make_shared<int>(42);
auto sp2 = sp1;  // 引用计数+1

// weak_ptr - 不增加引用计数
std::weak_ptr<int> wp = sp1;
if (auto locked = wp.lock()) {  // 检查是否有效
    std::cout << *locked << std::endl;
}

// 自定义删除器
std::unique_ptr<FILE, decltype(&fclose)> file(fopen("test.txt", "r"), &fclose);

1.3 右值引用与移动语义

// 移动构造函数
class MyClass {
public:
    MyClass(MyClass&& other) noexcept 
        : data_(std::move(other.data_)) {}
    
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            data_ = std::move(other.data_);
        }
        return *this;
    }
private:
    std::vector<int> data_;
};

// 完美转发
template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));  // 保持原始类型
}

二、RAII 资源管理

2.1 RAII 模式

class ResourceGuard {
public:
    ResourceGuard() { acquire(); }
    ~ResourceGuard() { release(); }
    
    // 禁止拷贝
    ResourceGuard(const ResourceGuard&) = delete;
    ResourceGuard& operator=(const ResourceGuard&) = delete;
    
    // 允许移动
    ResourceGuard(ResourceGuard&&) = default;
    ResourceGuard& operator=(ResourceGuard&&) = default;
};

// 使用示例
void processFile(const std::string& filename) {
    std::ifstream file(filename);  // RAII: 自动关闭
    if (!file) throw std::runtime_error("Cannot open file");
    // 使用file...
}  // 自动调用析构函数

2.2 lock_guard vs unique_lock

#include <mutex>

std::mutex mtx;

// lock_guard - 最简单,不能手动解锁
{
    std::lock_guard<std::mutex> lock(mtx);
    // 临界区代码
}

// unique_lock - 更灵活,可以手动解锁
{
    std::unique_lock<std::mutex> lock(mtx);
    // 临界区代码
    lock.unlock();  // 提前解锁
    
    // 非临界区代码
    lock.lock();    // 再次加锁
}

// scoped_lock - 多锁,避免死锁
std::mutex mtx1, mtx2;
{
    std::scoped_lock lock(mtx1, mtx2);  // 原子性获取多个锁
}

三、容器与算法

3.1 容器选择指南

容器适用场景时间复杂度
vector随机访问、尾部插入O(1) 访问,O(1) 尾插
deque双端操作O(1) 首尾插入
list频繁中间插入删除O(1) 插入删除
map/set有序查找O(log n)
unordered_map/set快速查找O(1) 平均

3.2 vector 优化

// 预分配容量
std::vector<int> v;
v.reserve(1000);  // 避免多次重新分配

// 避免
for (int i = 0; i < 1000; ++i) {
    v.push_back(i);  // 可能多次分配
}

// 更好的方式
v.insert(v.end(), 
    boost::counting_iterator<int>(0),
    boost::counting_iterator<int>(1000));

// 批量添加
v.insert(v.end(), {1, 2, 3, 4, 5});

// 移动语义
std::vector<std::string> vec;
vec.push_back(std::move(str));  // 移动而非拷贝
vec.emplace_back("hello");      // 原地构造

3.3 map 优化

std::map<std::string, int> m;

// 使用 emplace 代替 insert
m.emplace("key", 42);  // 更高效

// 使用 try_emplace (C++17)
m.try_emplace("key", 42);  // 如果key存在,不会构造value

// 使用 insert_or_assign (C++17)
m.insert_or_assign("key", 42);

// 批量插入
m.insert({{"a", 1}, {"b", 2}, {"c", 3}});

四、Lambda 表达式

4.1 Lambda 基础

// 基本语法
auto f = [](int x) { return x * 2; };

// 捕获方式
int a = 10;
int b = 20;

auto f1 = [a, b]() { return a + b; };        // 值捕获
auto f2 = [&a, b]() { a++; return a + b; };  // 混合捕获
auto f3 = [=]() { return a + b; };           // 全部值捕获
auto f4 = [&]() { a++; b++; };               // 全部引用捕获
auto f5 = [=, &a]() { a++; return a + b; };  // 默认值+指定引用

// C++14 泛型 lambda
auto add = [](auto x, auto y) { return x + y; };
add(1, 2);      // int
add(1.5, 2.5);  // double

// C++14 初始化捕获
auto f6 = [p = std::make_unique<int>(42)]() { return *p; };

4.2 Lambda 高级用法

// 立即调用
int result = [](int x, int y) {
    return x + y;
}(1, 2);

// 递归 lambda (C++14)
auto factorial = [](auto self, int n) -> int {
    return n <= 1 ? 1 : n * self(self, n - 1);
};
int fact5 = factorial(factorial, 5);

// C++23 递归 lambda 简化
auto factorial2 = [](this auto&& self, int n) -> int {
    return n <= 1 ? 1 : n * self(n - 1);
};

// 存储 lambda
std::function<int(int, int)> op = [](int a, int b) { return a + b; };
auto op2 = std::make_unique<int(int, int)>([](int a, int b) { return a + b; });

五、模板编程

5.1 模板基础

// 函数模板
template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

// 类模板
template<typename T, size_t N>
class Array {
    T data[N];
public:
    T& operator[](size_t i) { return data[i]; }
    constexpr size_t size() const { return N; }
};

// 变量模板 (C++14)
template<typename T>
constexpr T pi = T(3.14159265358979);

// 别名模板
template<typename T>
using Vec = std::vector<T, std::allocator<T>>;
Vec<int> v;

5.2 可变参数模板

// 递归展开
template<typename T>
void print(T t) {
    std::cout << t << std::endl;
}

template<typename T, typename... Args>
void print(T t, Args... args) {
    std::cout << t << ", ";
    print(args...);
}

// C++17 折叠表达式
template<typename... Args>
auto sum(Args... args) {
    return (... + args);  // 一元右折叠
}

template<typename... Args>
void print_all(Args... args) {
    (std::cout << ... << args) << std::endl;  // 左折叠
}

5.3 SFINAE 与 Concepts

// SFINAE (C++11/14)
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
process(T t) {
    return t * 2;
}

// Concepts (C++20)
template<typename T>
requires std::integral<T>
T process(T t) {
    return t * 2;
}

// 更简洁的写法
template<std::integral T>
T process(T t) {
    return t * 2;
}

// 缩写语法
auto process(std::integral auto t) {
    return t * 2;
}

// 定义概念
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

template<Numeric T>
T add(T a, T b) { return a + b; }

六、编译期计算

6.1 constexpr

// constexpr 变量
constexpr int MAX_SIZE = 100;

// constexpr 函数
constexpr int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
constexpr int fact5 = factorial(5);  // 编译期计算

// constexpr 类 (C++14)
class Point {
    int x_, y_;
public:
    constexpr Point(int x, int y) : x_(x), y_(y) {}
    constexpr int x() const { return x_; }
    constexpr int y() const { return y_; }
    constexpr int distance() const { return x_ * x_ + y_ * y_; }
};

constexpr Point p(3, 4);
static_assert(p.distance() == 25);

6.2 if constexpr

// 编译期条件分支
template<typename T>
auto getValue(T t) {
    if constexpr (std::is_pointer_v<T>) {
        return *t;  // 指针类型
    } else if constexpr (std::is_integral_v<T>) {
        return t * 2;  // 整数类型
    } else {
        return t;  // 其他类型
    }
}

// 类型判断
template<typename T>
void process(T t) {
    if constexpr (std::is_same_v<T, int>) {
        std::cout << "int: " << t << std::endl;
    } else if constexpr (std::is_same_v<T, std::string>) {
        std::cout << "string: " << t << std::endl;
    }
}

6.3 编译期字符串

// C++20 consteval
consteval int sqr(int n) {
    return n * n;
}
int x = sqr(5);  // 必须编译期计算
// sqr(x);       // 编译错误

// C++20 编译期字符串
constexpr size_t str_length(const char* str) {
    size_t len = 0;
    while (str[len]) ++len;
    return len;
}
static_assert(str_length("hello") == 5);

七、性能优化

7.1 避免不必要拷贝

// 使用引用
void process(const std::string& s);  // 好
void process(std::string s);          // 拷贝开销

// 使用移动
std::vector<std::string> getData() {
    std::vector<std::string> result;
    // ...
    return result;  // NRVO 或移动
}

// 返回值优化
std::string createString() {
    return std::string("hello");  // 可能RVO
}

// 参数传递指南
// 1. 只读小对象(基本类型):传值
void f(int x);

// 2. 只读大对象:传const引用
void f(const std::string& s);

// 3. 需要修改:传引用
void f(std::string& s);

// 4. 需要移动进对象:传值然后移动
void f(std::string s) { member_ = std::move(s); }

7.2 emplace_back vs push_back

std::vector<std::pair<int, std::string>> v;

// push_back - 创建临时对象然后移动
v.push_back(std::make_pair(1, "hello"));

// emplace_back - 原地构造,更高效
v.emplace_back(1, "hello");

// 复杂对象差异更大
struct Person {
    std::string name;
    int age;
    Person(std::string n, int a) : name(std::move(n)), age(a) {}
};

std::vector<Person> people;
people.emplace_back("Alice", 30);  // 直接构造

7.3 避免频繁内存分配

// 字符串拼接
std::string result;
result.reserve(1000);  // 预分配
for (int i = 0; i < 100; ++i) {
    result += std::to_string(i);
}

// 使用 string_view (C++17)
void process(std::string_view sv);  // 避免分配

// stringstream 优化
std::ostringstream oss;
oss.str().reserve(1024);  // 预分配

// 使用 std::format (C++20)
std::string s = std::format("Value: {}, {}", 1, 2);

7.4 算法优化

// 使用 STL 算法而非手写循环
std::vector<int> v = {3, 1, 4, 1, 5};

// 排序
std::sort(v.begin(), v.end());

// 查找
auto it = std::find(v.begin(), v.end(), 4);

// 二分查找(已排序)
bool found = std::binary_search(v.begin(), v.end(), 4);

// 统计
int count = std::count(v.begin(), v.end(), 1);

// 条件计数
int cnt = std::count_if(v.begin(), v.end(), [](int x) { return x > 2; });

// 删除特定元素
v.erase(std::remove(v.begin(), v.end(), 1), v.end());  // erase-remove 惯用法

// 条件删除
v.erase(std::remove_if(v.begin(), v.end(), 
    [](int x) { return x % 2 == 0; }), v.end());

八、并发编程

8.1 线程基础

#include <thread>
#include <future>
#include <mutex>
#include <condition_variable>

// 创建线程
std::thread t1([]() { /* task */ });
t1.join();  // 等待完成

// C++20 jthread - 自动 join
std::jthread t2([]() { /* task */ });  // 析构时自动join

// async 异步任务
auto future = std::async(std::launch::async, []() {
    return expensiveComputation();
});
auto result = future.get();  // 阻塞等待结果

// 线程池
std::vector<std::future<int>> futures;
for (int i = 0; i < 10; ++i) {
    futures.push_back(std::async(std::launch::async, [i]() {
        return compute(i);
    }));
}

8.2 同步原语

// 互斥锁
std::mutex mtx;
int counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);
    ++counter;
}

// 读写锁 (C++14)
std::shared_mutex rw_mtx;
void reader() {
    std::shared_lock<std::shared_mutex> lock(rw_mtx);
    // 读操作
}
void writer() {
    std::unique_lock<std::shared_mutex> lock(rw_mtx);
    // 写操作
}

// 条件变量
std::condition_variable cv;
std::mutex cv_mtx;
bool ready = false;

void waitForReady() {
    std::unique_lock<std::mutex> lock(cv_mtx);
    cv.wait(lock, []{ return ready; });
    // 执行
}

void signalReady() {
    {
        std::lock_guard<std::mutex> lock(cv_mtx);
        ready = true;
    }
    cv.notify_one();
}

8.3 原子操作

#include <atomic>

std::atomic<int> counter(0);
std::atomic<bool> flag(false);

// 原子操作
counter.fetch_add(1);
counter.fetch_sub(1);
counter.exchange(10);
int expected = 10;
bool success = counter.compare_exchange_strong(expected, 20);

// 自旋锁
class SpinLock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
    void lock() { while (flag.test_and_set(std::memory_order_acquire)); }
    void unlock() { flag.clear(std::memory_order_release); }
};

九、异常处理

9.1 异常最佳实践

// 抛出异常
throw std::runtime_error("Something went wrong");

// 捕获异常
try {
    riskyOperation();
} catch (const std::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl;
} catch (...) {
    std::cerr << "Unknown error" << std::endl;
}

// 异常安全级别
// 1. 无异常保证: 函数可能泄漏资源
// 2. 基本保证: 异常时对象处于有效状态,资源不泄漏
// 3. 强保证: 异常时操作回滚,状态不变
// 4. 无异常保证: 函数永不抛出异常

// noexcept 说明
void safeFunction() noexcept {
    // 保证不抛出异常
}

// 检查是否可能抛出
static_assert(noexcept(safeFunction()));

9.2 RAII 异常安全

// 强异常安全的实现
class Container {
    std::vector<int> data_;
public:
    void append(const std::vector<int>& items) {
        std::vector<int> temp = data_;  // 拷贝
        temp.insert(temp.end(), items.begin(), items.end());
        data_.swap(temp);  // 原子交换
    }
};

// 使用 scope_guard (GSL)
#include <gsl/gsl>
void process() {
    auto guard = gsl::finally([]() { cleanup(); });
    // 即使异常也会调用 cleanup
}

十、现代 C++ 特性速查

10.1 C++11

特性说明
auto自动类型推导
nullptr空指针字面量
constexpr编译期计算
range-for范围 for 循环
智能指针unique_ptr, shared_ptr
移动语义右值引用, std::move
Lambda匿名函数
override/final虚函数控制
delete删除函数
enum class强类型枚举

10.2 C++14

特性说明
泛型 lambda[](auto x)
make_unique智能指针工厂
二进制字面量0b1010
数字分隔符1'000'000
std::exchange原子交换
std::quoted引号IO操作

10.3 C++17

特性说明
结构化绑定auto [a, b] = pair;
if constexpr编译期条件
std::optional可选值
std::variant类型安全联合
std::any任意类型
std::string_view字符串视图
并行算法std::execution::par
文件系统std::filesystem

10.4 C++20

特性说明
Concepts概念约束
Ranges范围库
Coroutines协程
Modules模块
std::format格式化
std::span数组视图
std::source_location源码位置
三向比较符<=>

10.5 C++23

特性说明
std::expected错误处理
std::print类型安全输出
Deducing this显式对象参数
std::mdspan多维数组视图
std::flat_map/set扁平容器
if constevalconsteval条件

十一、常用代码片段

11.1 单例模式

// C++11 线程安全单例
class Singleton {
public:
    static Singleton& instance() {
        static Singleton inst;  // 线程安全初始化
        return inst;
    }
    
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
private:
    Singleton() = default;
};

11.2 工厂模式

template<typename Base, typename... Args>
class Factory {
public:
    using Creator = std::function<std::unique_ptr<Base>(Args...)>;
    
    void registerType(const std::string& name, Creator creator) {
        creators_[name] = std::move(creator);
    }
    
    std::unique_ptr<Base> create(const std::string& name, Args... args) {
        auto it = creators_.find(name);
        if (it != creators_.end()) {
            return it->second(std::forward<Args>(args)...);
        }
        return nullptr;
    }
    
private:
    std::unordered_map<std::string, Creator> creators_;
};

11.3 观察者模式

template<typename... Args>
class Signal {
public:
    using Slot = std::function<void(Args...)>;
    
    void connect(Slot slot) {
        slots_.push_back(std::move(slot));
    }
    
    void emit(Args... args) {
        for (auto& slot : slots_) {
            slot(args...);
        }
    }
    
private:
    std::vector<Slot> slots_;
};

// 使用
Signal<int, std::string> signal;
signal.connect([](int id, const std::string& msg) {
    std::cout << id << ": " << msg << std::endl;
});
signal.emit(1, "Hello");

11.4 PIMPL 模式

// Widget.h
class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
private:
    class Impl;
    std::unique_ptr<Impl> pImpl_;
};

// Widget.cpp
class Widget::Impl {
public:
    void doSomething() { /* 实现 */ }
};

Widget::Widget() : pImpl_(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::doSomething() { pImpl_->doSomething(); }

11.5 延迟初始化

template<typename T>
class Lazy {
public:
    template<typename... Args>
    explicit Lazy(Args&&... args) 
        : args_(std::forward<Args>(args)...) {}
    
    T& get() {
        if (!value_) {
            value_ = std::make_unique<T>(std::apply(
                [this](auto&&... args) {
                    return T(std::forward<decltype(args)>(args)...);
                }, args_));
        }
        return *value_;
    }
    
private:
    std::tuple<Args...> args_;
    std::unique_ptr<T> value_;
};

十二、调试与测试

12.1 断言

// 运行时断言
assert(ptr != nullptr);

// 编译期断言
static_assert(sizeof(int) == 4, "int must be 4 bytes");

// 自定义断言宏
#define EXPECT(cond, msg) \
    if (!(cond)) { \
        std::cerr << "Assertion failed: " << #cond \
                  << "\nMessage: " << msg \
                  << "\nFile: " << __FILE__ \
                  << "\nLine: " << __LINE__ << std::endl; \
        std::abort(); \
    }

12.2 日志

// 简单日志宏
#define LOG(level, msg) \
    std::cout << "[" << level << "] " \
              << __FILE__ << ":" << __LINE__ << " " \
              << msg << std::endl

// 使用
LOG("INFO", "Processing started");
LOG("ERROR", "Failed to open file: " << filename);

// C++20 std::source_location
#include <source_location>
void log(const std::string& message, 
         const std::source_location& loc = std::source_location::current()) {
    std::cout << loc.file_name() << ":" << loc.line() 
              << " " << message << std::endl;
}

总结

核心原则

原则说明
RAII资源获取即初始化
零开销不为未使用的特性付费
值语义优先使用值而非引用/指针
const正确尽可能使用 const
异常安全保证基本或强异常安全
现代C++使用智能指针,避免裸指针和new/delete

最佳实践

  1. ✅ 使用 auto 简化类型声明
  2. ✅ 优先使用智能指针管理资源
  3. ✅ 使用 RAII 包装资源
  4. ✅ 使用 STL 算法替代手写循环
  5. ✅ 使用 emplace 系列函数
  6. ✅ 使用 std::move 避免拷贝
  7. ✅ 使用 constconstexpr
  8. ✅ 使用现代 C++ 特性

文档生成时间:2026-03-26