智能指针深度解析
1. 智能指针的必要性
在传统 C++ 编程中,手动管理内存容易导致内存泄漏、悬空指针等问题。C++11 引入的智能指针通过 RAII(Resource Acquisition Is Initialization)机制,自动管理动态分配的内存,极大地提高了代码的安全性和可维护性。
2. unique_ptr - 独占所有权
unique_ptr 是一种独占式智能指针,确保同一时刻只有一个指针拥有对象的所有权。当 unique_ptr 离开作用域时,它会自动释放所管理的对象。
#include <memory>
#include <iostream>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
void doSomething() { std::cout << "Doing something\n"; }
};
void uniquePtrExample() {
// 创建 unique_ptr
std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();
ptr1->doSomething();
// 转移所有权
std::unique_ptr<Resource> ptr2 = std::move(ptr1);
// ptr1 现在为空
if (!ptr1) {
std::cout << "ptr1 is null\n";
}
// 使用数组
std::unique_ptr<int[]> arr = std::make_unique<int[]>(10);
arr[0] = 42;
}
// Resource 会在函数结束时自动释放
注意:
unique_ptr 不可复制,只能移动。使用 std::move 转移所有权时,原指针会变为空指针。
3. shared_ptr - 共享所有权
shared_ptr 允许多个指针共享同一个对象的所有权,通过引用计数机制管理对象的生命周期。当最后一个 shared_ptr 被销毁时,对象才会被释放。
#include <memory>
#include <vector>
class Node {
public:
int value;
std::shared_ptr<Node> next;
Node(int v) : value(v) {}
~Node() { std::cout << "Node " << value << " destroyed\n"; }
};
void sharedPtrExample() {
std::shared_ptr<Node> node1 = std::make_shared<Node>(1);
std::shared_ptr<Node> node2 = std::make_shared<Node>(2);
// 共享所有权
std::shared_ptr<Node> node1_copy = node1;
std::cout << "node1 引用计数: " << node1.use_count() << "\n"; // 输出 2
// 创建链表
node1->next = node2;
// 可以在容器中使用
std::vector<std::shared_ptr<Node>> nodes;
nodes.push_back(node1);
nodes.push_back(node2);
}
4. weak_ptr - 打破循环引用
weak_ptr 是一种不控制对象生命周期的智能指针,主要用于解决 shared_ptr 的循环引用问题。它可以观察对象但不增加引用计数。
class TreeNode {
public:
int value;
std::shared_ptr<TreeNode> left;
std::shared_ptr<TreeNode> right;
std::weak_ptr<TreeNode> parent; // 使用 weak_ptr 避免循环引用
TreeNode(int v) : value(v) {}
};
void weakPtrExample() {
auto root = std::make_shared<TreeNode>(1);
auto leftChild = std::make_shared<TreeNode>(2);
root->left = leftChild;
leftChild->parent = root; // weak_ptr 不增加引用计数
// 使用 weak_ptr
if (auto parent = leftChild->parent.lock()) {
std::cout << "Parent value: " << parent->value << "\n";
}
}
提示:使用
weak_ptr::lock() 方法可以安全地访问对象,如果对象已被销毁,会返回空指针。
5. 自定义删除器
智能指针支持自定义删除器,用于处理特殊的资源释放逻辑。
void customDeleterExample() {
// 自定义删除器用于文件句柄
auto fileDeleter = [](FILE* fp) {
if (fp) {
std::cout << "Closing file\n";
fclose(fp);
}
};
std::unique_ptr<FILE, decltype(fileDeleter)> file(
fopen("data.txt", "r"),
fileDeleter
);
// shared_ptr 的自定义删除器
std::shared_ptr<int> ptr(new int[10], [](int* p) {
std::cout << "Deleting array\n";
delete[] p;
});
}
模板编程技术
1. 函数模板
函数模板允许我们编写类型无关的通用代码,编译器会根据实际使用的类型生成具体的函数实例。
// 基础函数模板
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// 使用多个模板参数
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
// 模板特化
template<>
const char* max<const char*>(const char* a, const char* b) {
return (strcmp(a, b) > 0) ? a : b;
}
void templateExample() {
int i = max(10, 20); // T = int
double d = max(3.14, 2.71); // T = double
auto result = add(10, 3.14); // 混合类型运算
}
2. 类模板
类模板用于创建通用的数据结构和容器类。
template<typename T, size_t Size>
class Array {
private:
T data[Size];
public:
size_t size() const { return Size; }
T& operator[](size_t index) {
if (index >= Size) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
const T& operator[](size_t index) const {
if (index >= Size) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
// 迭代器支持
T* begin() { return data; }
T* end() { return data + Size; }
};
void arrayTemplateExample() {
Array<int, 5> intArray;
intArray[0] = 10;
Array<std::string, 3> strArray;
strArray[0] = "Hello";
}
3. 变参模板
C++11 引入的变参模板允许接受任意数量的模板参数。
// 递归终止函数
void print() {
std::cout << "\n";
}
// 变参模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first << " ";
print(args...); // 递归调用
}
// 使用折叠表达式(C++17)
template<typename... Args>
auto sum(Args... args) {
return (args + ...);
}
void variadicExample() {
print(1, 2.5, "hello", 'c'); // 输出: 1 2.5 hello c
auto total = sum(1, 2, 3, 4, 5); // 输出: 15
}
4. SFINAE 和类型萃取
SFINAE(Substitution Failure Is Not An Error)是 C++ 模板元编程的重要技术。
#include <type_traits>
// 使用 enable_if 进行条件编译
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
processInteger(T value) {
return value * 2;
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
processFloat(T value) {
return value * 1.5;
}
// C++17 if constexpr
template<typename T>
auto process(T value) {
if constexpr (std::is_integral_v<T>) {
return value * 2;
} else if constexpr (std::is_floating_point_v<T>) {
return value * 1.5;
} else {
return value;
}
}
并发编程
1. 线程基础
C++11 引入了标准的线程库,提供了跨平台的并发编程支持。
#include <thread>
#include <iostream>
#include <vector>
void threadFunction(int id) {
std::cout << "Thread " << id << " is running\n";
}
void threadExample() {
std::vector<std::thread> threads;
// 创建多个线程
for (int i = 0; i < 5; ++i) {
threads.emplace_back(threadFunction, i);
}
// 等待所有线程完成
for (auto& t : threads) {
if (t.joinable()) {
t.join();
}
}
// 使用 lambda
std::thread t([](int x, int y) {
std::cout << "Result: " << x + y << "\n";
}, 10, 20);
t.join();
}
2. 互斥量和锁
使用互斥量保护共享数据,防止数据竞争。
#include <mutex>
class Counter {
private:
int count = 0;
mutable std::mutex mtx;
public:
void increment() {
std::lock_guard<std::mutex> lock(mtx);
++count;
}
int getCount() const {
std::lock_guard<std::mutex> lock(mtx);
return count;
}
// 使用 unique_lock 支持更灵活的锁定
void complexOperation() {
std::unique_lock<std::mutex> lock(mtx);
// 执行一些操作
count += 10;
lock.unlock(); // 提前释放锁
// 执行不需要锁保护的操作
std::this_thread::sleep_for(std::chrono::milliseconds(100));
lock.lock(); // 重新获取锁
count += 5;
}
};
3. 条件变量
条件变量用于线程间的同步,实现生产者-消费者模式。
#include <condition_variable>
#include <queue>
template<typename T>
class ThreadSafeQueue {
private:
mutable std::mutex mtx;
std::queue<T> queue;
std::condition_variable cv;
public:
void push(T value) {
std::lock_guard<std::mutex> lock(mtx);
queue.push(std::move(value));
cv.notify_one();
}
bool tryPop(T& value) {
std::lock_guard<std::mutex> lock(mtx);
if (queue.empty()) {
return false;
}
value = std::move(queue.front());
queue.pop();
return true;
}
void waitAndPop(T& value) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return !queue.empty(); });
value = std::move(queue.front());
queue.pop();
}
};
4. 原子操作
对于简单的数据类型,可以使用原子操作避免使用锁。
#include <atomic>
class AtomicCounter {
private:
std::atomic<int> count{0};
public:
void increment() {
count.fetch_add(1, std::memory_order_relaxed);
}
int getCount() const {
return count.load(std::memory_order_relaxed);
}
bool compareAndSwap(int expected, int desired) {
return count.compare_exchange_strong(
expected,
desired,
std::memory_order_release,
std::memory_order_acquire
);
}
};
警告:并发编程容易出现死锁、数据竞争等问题,必须仔细设计锁的获取顺序,并充分测试多线程代码。
移动语义与右值引用
1. 左值与右值
左值(lvalue)是持久存在的对象,右值(rvalue)是临时对象。C++11 引入右值引用(&&)支持移动语义,避免不必要的拷贝。
class Buffer {
private:
size_t size;
char* data;
public:
// 构造函数
Buffer(size_t s) : size(s), data(new char[s]) {
std::cout << "Constructor\n";
}
// 拷贝构造函数
Buffer(const Buffer& other) : size(other.size), data(new char[other.size]) {
std::cout << "Copy constructor\n";
std::memcpy(data, other.data, size);
}
// 移动构造函数
Buffer(Buffer&& other) noexcept : size(other.size), data(other.data) {
std::cout << "Move constructor\n";
other.data = nullptr;
other.size = 0;
}
// 拷贝赋值运算符
Buffer& operator=(const Buffer& other) {
std::cout << "Copy assignment\n";
if (this != &other) {
delete[] data;
size = other.size;
data = new char[size];
std::memcpy(data, other.data, size);
}
return *this;
}
// 移动赋值运算符
Buffer& operator=(Buffer&& other) noexcept {
std::cout << "Move assignment\n";
if (this != &other) {
delete[] data;
size = other.size;
data = other.data;
other.data = nullptr;
other.size = 0;
}
return *this;
}
~Buffer() {
delete[] data;
}
};
void moveExample() {
Buffer b1(1024);
Buffer b2 = std::move(b1); // 调用移动构造函数
Buffer b3(512);
b3 = std::move(b2); // 调用移动赋值运算符
}
2. 完美转发
使用 std::forward 实现参数的完美转发,保持参数的值类别。
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
class Widget {
public:
Widget(int x, const std::string& s) {
std::cout << "Constructor: " << x << ", " << s << "\n";
}
};
void forwardExample() {
auto ptr = make_unique<Widget>(42, "hello");
}