Skip to content

Latest commit

 

History

History
436 lines (329 loc) · 8.38 KB

File metadata and controls

436 lines (329 loc) · 8.38 KB

2.2 C++ 核心特性

📍 导航返回目录 | 上一节:Go核心 | 下一节:Python实战


智能指针

unique_ptr - 独占所有权

#include <memory>

// 创建 unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>(42);

// 访问
std::cout << *ptr << std::endl;

// 转移所有权
std::unique_ptr<int> ptr2 = std::move(ptr);
// 此时 ptr 为 nullptr

// 自定义删除器
auto deleter = [](FILE* fp) { fclose(fp); };
std::unique_ptr<FILE, decltype(deleter)> file(fopen("file.txt", "r"), deleter);

特点

  • 不可拷贝,只能移动
  • 零开销抽象
  • 适用于独占资源管理

shared_ptr - 共享所有权

// 创建 shared_ptr
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);

// 拷贝(引用计数+1)
std::shared_ptr<int> ptr2 = ptr1;

// 查看引用计数
std::cout << ptr1.use_count() << std::endl; // 输出 2

// 自定义删除器
std::shared_ptr<FILE> file(fopen("file.txt", "r"), [](FILE* fp) { fclose(fp); });

原理:引用计数,最后一个引用销毁时释放资源

weak_ptr - 弱引用

// 解决循环引用问题
struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // 使用 weak_ptr 打破循环
};

// 使用
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp = sp;

// 访问(需要先提升为 shared_ptr)
if (auto sp2 = wp.lock()) {
    std::cout << *sp2 << std::endl;
} else {
    std::cout << "对象已销毁" << std::endl;
}

RAII 资源管理

Resource Acquisition Is Initialization:资源获取即初始化

// 文件资源管理
class FileGuard {
public:
    FileGuard(const char* filename) {
        fp = fopen(filename, "r");
        if (!fp) throw std::runtime_error("Failed to open file");
    }
    
    ~FileGuard() {
        if (fp) fclose(fp);
    }
    
    // 禁止拷贝
    FileGuard(const FileGuard&) = delete;
    FileGuard& operator=(const FileGuard&) = delete;
    
    FILE* get() { return fp; }
    
private:
    FILE* fp;
};

// 使用
void readFile() {
    FileGuard file("data.txt");
    // 使用 file.get()
    // 离开作用域自动关闭文件
}

RAII 的优势

  • 异常安全
  • 自动资源释放
  • 避免资源泄漏

移动语义

右值引用

// 左值和右值
int a = 10;        // a 是左值
int b = a + 5;     // a+5 是右值

// 右值引用
int&& rvalue_ref = 20;

// std::move 将左值转为右值
std::string s1 = "hello";
std::string s2 = std::move(s1); // s1 被移动,资源转移到 s2

移动构造与移动赋值

class String {
public:
    // 拷贝构造(深拷贝)
    String(const String& other) {
        size_ = other.size_;
        data_ = new char[size_ + 1];
        std::strcpy(data_, other.data_);
    }
    
    // 移动构造(资源转移)
    String(String&& other) noexcept {
        data_ = other.data_;
        size_ = other.size_;
        other.data_ = nullptr;  // 防止析构时释放资源
        other.size_ = 0;
    }
    
    // 移动赋值
    String& operator=(String&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }
    
    ~String() {
        delete[] data_;
    }
    
private:
    char* data_;
    size_t size_;
};

性能优势:避免不必要的拷贝,提升性能


并发编程

std::thread - 线程

#include <thread>
#include <iostream>

void worker(int id) {
    std::cout << "Worker " << id << " is running\n";
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    
    t1.join();
    t2.join();
    
    return 0;
}

std::mutex - 互斥锁

#include <mutex>

std::mutex mtx;
int counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx); // RAII 自动加锁/解锁
    ++counter;
}

// 或使用 unique_lock(更灵活)
void increment2() {
    std::unique_lock<std::mutex> lock(mtx);
    ++counter;
    lock.unlock(); // 可以手动解锁
    // 其他操作
}

std::condition_variable - 条件变量

#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;

// 生产者
void producer() {
    for (int i = 0; i < 10; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            data_queue.push(i);
        }
        cv.notify_one(); // 通知消费者
    }
}

// 消费者
void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return !data_queue.empty(); }); // 等待数据
        
        int value = data_queue.front();
        data_queue.pop();
        lock.unlock();
        
        std::cout << "Consumed: " << value << std::endl;
    }
}

std::atomic - 原子操作

#include <atomic>

std::atomic<int> counter(0);

void increment() {
    counter.fetch_add(1, std::memory_order_relaxed);
    // 或简写为:counter++;
}

// 原子比较交换(CAS)
int expected = 10;
int desired = 20;
if (counter.compare_exchange_strong(expected, desired)) {
    // 成功:counter 从 10 变为 20
} else {
    // 失败:expected 被更新为 counter 的当前值
}

性能优化技巧

对象池(Object Pool)

template<typename T>
class ObjectPool {
public:
    ObjectPool(size_t size) {
        for (size_t i = 0; i < size; ++i) {
            pool_.push_back(std::make_unique<T>());
        }
    }
    
    T* acquire() {
        std::lock_guard<std::mutex> lock(mtx_);
        if (pool_.empty()) {
            return new T();
        }
        T* obj = pool_.back().release();
        pool_.pop_back();
        return obj;
    }
    
    void release(T* obj) {
        std::lock_guard<std::mutex> lock(mtx_);
        pool_.push_back(std::unique_ptr<T>(obj));
    }
    
private:
    std::vector<std::unique_ptr<T>> pool_;
    std::mutex mtx_;
};

CRTP(奇异递归模板模式)

// 静态多态,避免虚函数开销
template<typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

class Derived : public Base<Derived> {
public:
    void implementation() {
        std::cout << "Derived implementation\n";
    }
};

constexpr 编译期计算

// 编译期计算阶乘
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

// 编译期使用
constexpr int result = factorial(5); // 编译时计算

常见问题与陷阱

内存泄漏

工具检测

# Valgrind
valgrind --leak-check=full ./program

# AddressSanitizer(推荐)
g++ -fsanitize=address -g program.cpp
./a.out

常见原因

  • 忘记 delete
  • 异常导致资源未释放
  • 循环引用(shared_ptr)

解决方案:使用智能指针和 RAII

数据竞态

检测工具

# ThreadSanitizer
g++ -fsanitize=thread -g program.cpp
./a.out

常见原因

  • 多线程访问共享数据未加锁
  • 锁的粒度不当

解决方案

  • 使用 mutex 保护共享数据
  • 使用 atomic 原子操作
  • 避免共享可变状态

野指针

// ❌ 错误示例
int* ptr = new int(42);
delete ptr;
*ptr = 10; // 野指针访问!

// ✅ 正确方式
std::unique_ptr<int> ptr = std::make_unique<int>(42);
// 自动管理生命周期

本章小结

C++ 是系统级编程的首选语言,掌握智能指针、RAII、移动语义对于编写高性能、安全的代码至关重要。

关键要点

  • ✅ 智能指针自动管理内存,避免泄漏
  • ✅ RAII 确保资源异常安全
  • ✅ 移动语义显著提升性能
  • ✅ 使用 Sanitizer 工具检测内存和并发问题

扩展阅读


💡 思考题

  1. unique_ptr 和 shared_ptr 的区别?何时用哪个?
  2. 什么是移动语义?为什么能提升性能?
  3. 如何避免 shared_ptr 循环引用?

⏮️ 上一节:Go核心 | ⏭️ 下一节:Python实战