引言
在C++编程中,内存管理一直是个重要且复杂的话题。传统的裸指针虽然灵活,但也容易导致内存泄漏、悬空指针等问题。为了解决这些问题,C++11引入了智能指针的概念,它们能够自动管理内存生命周期,大大提高了代码的安全性和可维护性。
本文将深入探讨C++中四种主要的智能指针:unique_ptr、shared_ptr、weak_ptr以及已被废弃的auto_ptr,通过丰富的代码示例展示它们的用法和最佳实践。
一、为什么需要智能指针?
1.1 传统指针的问题
让我们先看一个使用裸指针可能导致问题的例子:
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int val) : value(val) {
cout << "构造函数被调用,value = " << value << endl;
}
~MyClass() {
cout << "析构函数被调用,value = " << value << endl;
}
void print() {
cout << "Value: " << value << endl;
}
private:
int value;
};
void problematicFunction() {
MyClass* ptr = new MyClass(10);
ptr->print();
// 如果这里发生异常或者提前返回
// delete将不会被执行,导致内存泄漏
// throw runtime_error("发生异常!");
delete ptr; // 可能会被跳过
}
int main() {
problematicFunction();
return 0;
}
1.2 智能指针的优势
智能指针通过RAII(Resource Acquisition Is Initialization)技术,将资源管理封装在对象生命周期中,确保资源在不再需要时被正确释放。
二、unique_ptr:独占所有权的智能指针
2.1 基本用法
unique_ptr实现独占式所有权,同一时间只能有一个unique_ptr指向特定对象。
#include <iostream>
#include <memory>
using namespace std;
void uniquePtrDemo() {
cout << "=== unique_ptr 示例 ===" << endl;
// 创建unique_ptr
unique_ptr<MyClass> ptr1(new MyClass(100));
// 使用make_unique(C++14推荐)
auto ptr2 = make_unique<MyClass>(200);
// 访问对象成员
ptr1->print();
ptr2->print();
// 转移所有权
unique_ptr<MyClass> ptr3 = move(ptr1);
if (!ptr1) {
cout << "ptr1 现在为空" << endl;
}
if (ptr3) {
cout << "ptr3 拥有对象: ";
ptr3->print();
}
// ptr2和ptr3在离开作用域时会自动释放内存
}
2.2 自定义删除器
#include <cstdio>
void customDeleterDemo() {
cout << "\n=== 自定义删除器 ===" << endl;
// 文件指针的自定义删除器
auto fileDeleter = [](FILE* file) {
if (file) {
fclose(file);
cout << "文件已关闭" << endl;
}
};
unique_ptr<FILE, decltype(fileDeleter)> filePtr(fopen("test.txt", "w"), fileDeleter);
if (filePtr) {
fputs("Hello, World!", filePtr.get());
}
// 数组的自定义删除器
auto arrayDeleter = [](int* arr) {
delete[] arr;
cout << "数组内存已释放" << endl;
};
unique_ptr<int[], decltype(arrayDeleter)> arrPtr(new int[10], arrayDeleter);
for (int i = 0; i < 10; ++i) {
arrPtr[i] = i * i;
}
}
三、shared_ptr:共享所有权的智能指针
3.1 基本用法
shared_ptr通过引用计数实现共享所有权,当最后一个shared_ptr离开作用域时,对象才会被销毁。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Resource {
public:
Resource(string name) : name(name) {
cout << "Resource '" << name << "' 被创建" << endl;
}
~Resource() {
cout << "Resource '" << name << "' 被销毁" << endl;
}
void use() {
cout << "使用 Resource '" << name << "'" << endl;
}
private:
string name;
};
void sharedPtrDemo() {
cout << "\n=== shared_ptr 示例 ===" << endl;
// 创建shared_ptr
shared_ptr<Resource> ptr1 = make_shared<Resource>("资源A");
{
shared_ptr<Resource> ptr2 = ptr1; // 共享所有权
cout << "引用计数: " << ptr1.use_count() << endl; // 输出2
ptr1->use();
ptr2->use();
} // ptr2离开作用域,引用计数减1
cout << "引用计数: " << ptr1.use_count() << endl; // 输出1
ptr1->use();
}
3.2 循环引用问题
class Node {
public:
string name;
shared_ptr<Node> next;
Node(string name) : name(name) {
cout << "Node '" << name << "' 被创建" << endl;
}
~Node() {
cout << "Node '" << name << "' 被销毁" << endl;
}
};
void cyclicReferenceDemo() {
cout << "\n=== 循环引用问题 ===" << endl;
auto node1 = make_shared<Node>("节点A");
auto node2 = make_shared<Node>("节点B");
node1->next = node2;
node2->next = node1; // 循环引用!
cout << "node1 引用计数: " << node1.use_count() << endl; // 输出2
cout << "node2 引用计数: " << node2.use_count() << endl; // 输出2
// 离开作用域时,由于循环引用,内存不会被释放!
}
四、weak_ptr:解决循环引用
4.1 基本用法
weak_ptr是对shared_ptr所管理对象的非拥有式引用,不会增加引用计数。
class SafeNode {
public:
string name;
weak_ptr<SafeNode> next; // 使用weak_ptr避免循环引用
SafeNode(string name) : name(name) {
cout << "SafeNode '" << name << "' 被创建" << endl;
}
~SafeNode() {
cout << "SafeNode '" << name << "' 被销毁" << endl;
}
void setNext(shared_ptr<SafeNode> nextNode) {
next = nextNode;
}
void printNext() {
if (auto nextPtr = next.lock()) { // 转换为shared_ptr
cout << "下一个节点是: " << nextPtr->name << endl;
} else {
cout << "下一个节点已被销毁" << endl;
}
}
};
void weakPtrDemo() {
cout << "\n=== weak_ptr 解决方案 ===" << endl;
auto node1 = make_shared<SafeNode>("安全节点A");
auto node2 = make_shared<SafeNode>("安全节点B");
node1->setNext(node2);
node2->setNext(node1); // 使用weak_ptr,不会造成循环引用
cout << "node1 引用计数: " << node1.use_count() << endl; // 输出1
cout << "node2 引用计数: " << node2.use_count() << endl; // 输出1
node1->printNext();
node2->printNext();
// 离开作用域时,内存会被正确释放
}
4.2 weak_ptr的典型应用场景
class Observer;
class Subject {
public:
void addObserver(shared_ptr<Observer> observer) {
observers.push_back(observer);
}
void notifyObservers();
private:
vector<weak_ptr<Observer>> observers; // 使用weak_ptr避免影响Observer生命周期
};
class Observer {
public:
virtual void update() = 0;
virtual ~Observer() = default;
};
五、智能指针实战应用
5.1 工厂模式与智能指针
#include <iostream>
#include <memory>
#include <map>
using namespace std;
// 产品接口
class Product {
public:
virtual void operation() = 0;
virtual ~Product() = default;
};
// 具体产品
class ConcreteProductA : public Product {
public:
void operation() override {
cout << "ConcreteProductA 操作" << endl;
}
};
class ConcreteProductB : public Product {
public:
void operation() override {
cout << "ConcreteProductB 操作" << endl;
}
};
// 工厂类
class Factory {
public:
static unique_ptr<Product> createProduct(const string& type) {
if (type == "A") {
return make_unique<ConcreteProductA>();
} else if (type == "B") {
return make_unique<ConcreteProductB>();
}
return nullptr;
}
};
void factoryDemo() {
cout << "\n=== 工厂模式示例 ===" << endl;
auto productA = Factory::createProduct("A");
auto productB = Factory::createProduct("B");
if (productA) productA->operation();
if (productB) productB->operation();
}
5.2 实现简单的对象池
#include <queue>
#include <functional>
template<typename T>
class ObjectPool {
public:
ObjectPool(size_t initialSize = 10) {
for (size_t i = 0; i < initialSize; ++i) {
pool.push(make_shared<T>());
}
}
shared_ptr<T> acquire() {
if (pool.empty()) {
return make_shared<T>();
}
auto obj = pool.front();
pool.pop();
return obj;
}
void release(shared_ptr<T> obj) {
// 重置对象状态(如果需要)
pool.push(obj);
}
size_t available() const {
return pool.size();
}
private:
queue<shared_ptr<T>> pool;
};
void objectPoolDemo() {
cout << "\n=== 对象池示例 ===" << endl;
ObjectPool<string> stringPool(3);
cout << "可用对象数: " << stringPool.available() << endl;
auto str1 = stringPool.acquire();
auto str2 = stringPool.acquire();
*str1 = "第一个字符串";
*str2 = "第二个字符串";
cout << "使用后可用对象数: " << stringPool.available() << endl;
stringPool.release(str1);
stringPool.release(str2);
cout << "归还后可用对象数: " << stringPool.available() << endl;
}
六、智能指针的最佳实践
6.1 选择正确的智能指针
void bestPractices() {
cout << "\n=== 最佳实践 ===" << endl;
// 1. 优先使用make_unique和make_shared
auto ptr1 = make_unique<int>(42);
auto ptr2 = make_shared<string>("Hello");
// 2. 明确所有权语义
unique_ptr<int> createUnique() {
return make_unique<int>(100);
}
shared_ptr<int> createShared() {
return make_shared<int>(200);
}
// 3. 在接口中明确指针的语义
void processObject(unique_ptr<int> ptr) { // 转移所有权
cout << "处理对象: " << *ptr << endl;
}
void observeObject(const shared_ptr<int>& ptr) { // 不转移所有权,只是观察
if (ptr) {
cout << "观察对象: " << *ptr << endl;
}
}
// 4. 避免混用裸指针和智能指针
int* rawPtr = new int(50);
shared_ptr<int> smartPtr(rawPtr);
// 错误:不要再用rawPtr操作内存
}
6.2 性能考虑
#include <chrono>
void performanceTest() {
cout << "\n=== 性能测试 ===" << endl;
const int iterations = 1000000;
// 测试裸指针
auto start1 = chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
int* ptr = new int(i);
delete ptr;
}
auto end1 = chrono::high_resolution_clock::now();
// 测试unique_ptr
auto start2 = chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
auto ptr = make_unique<int>(i);
}
auto end2 = chrono::high_resolution_clock::now();
auto duration1 = chrono::duration_cast<chrono::microseconds>(end1 - start1);
auto duration2 = chrono::duration_cast<chrono::microseconds>(end2 - start2);
cout << "裸指针耗时: " << duration1.count() << " 微秒" << endl;
cout << "unique_ptr耗时: " << duration2.count() << " 微秒" << endl;
}
七、总结
C++智能指针是现代C++编程中不可或缺的工具,它们通过自动内存管理大大提高了代码的安全性和可维护性。让我们回顾一下关键点:
-
unique_ptr:用于独占所有权场景,性能接近裸指针
-
shared_ptr:用于共享所有权场景,通过引用计数管理生命周期
-
weak_ptr:配合shared_ptr使用,解决循环引用问题
-
最佳实践:优先使用make函数、明确所有权语义、避免混用指针类型
智能指针虽然不能解决所有内存管理问题,但在大多数场景下,它们能够显著减少内存相关的bug。掌握智能指针的正确用法,是成为优秀C++程序员的重要一步。
int main() {
uniquePtrDemo();
customDeleterDemo();
sharedPtrDemo();
cyclicReferenceDemo();
weakPtrDemo();
factoryDemo();
objectPoolDemo();
bestPractices();
performanceTest();
return 0;
}
1286

被折叠的 条评论
为什么被折叠?



