C++ 智能指针
智能指针简介
在C++编程中,内存管理一直是容易出错且棘手的问题。传统的动态内存管理(使用new
和delete
)常常导致内存泄漏和悬挂指针等问题。智能指针是C++标准库提供的一种自动化内存管理工具,它能够在适当的时机自动释放动态分配的内存,大大简化了内存管理流程,提高了代码的安全性和可靠性。
提示
智 能指针的核心优势在于它遵循"RAII"(Resource Acquisition Is Initialization)原则,即资源的获取与对象初始化同时发生,资源的释放与对象销毁同时发生。
为什么需要智能指针?
考虑以下使用普通指针的代码:
void someFunction() {
// 分配内存
int* ptr = new int(10);
// 使用指针
*ptr = 20;
// 如果这里有异常发生或提前返回,可能导致内存泄漏
if (*ptr > 15) {
return; // 内存泄漏!
}
// 释放内存
delete ptr;
}
上面的代码存在潜在的内存泄漏风险。而使用智能指针,我们可以避免这类问题:
#include <memory>
void someFunction() {
// 创建智能指针
std::unique_ptr<int> ptr = std::make_unique<int>(10);
// 使用指针
*ptr = 20;
// 即使这里提前返回,智能指针也会在函数结束时自动释放内存
if (*ptr > 15) {
return; // 安全,不会造成内存泄漏
}
// 不需要手动释放内存
}
C++ 中的智能指针类型
C++11及更新标准提供了三种主要的智能指针类型:
std::unique_ptr
- 独占所有权模型std::shared_ptr
- 共享所有权模型std::weak_ptr
- 弱引用,配合shared_ptr
使用
这三种智能指针都定义在<memory>
头文件中。
1. std::unique_ptr
unique_ptr
是一种独占式智能指针,它保证同一时间只有一个指针可以指向特定的对象。当unique_ptr
被销毁时,它所管理的对象也会被自动销毁。
创建和使用unique_ptr
#include <iostream>
#include <memory>
int main() {
// 方法1:使用make_unique (C++14及以后)
std::unique_ptr<int> p1 = std::make_unique<int>(42);
// 方法2:使用构造函数 (C++11)
std::unique_ptr<int> p2(new int(100));
// 使用智能指针
std::cout << "p1 points to: " << *p1 << std::endl;
std::cout << "p2 points to: " << *p2 << std::endl;
// 修改指向的值
*p1 = 55;
std::cout << "Modified p1: " << *p1 << std::endl;
// 自动内存管理 - 不需要delete
return 0;
}
输出:
p1 points to: 42
p2 points to: 100
Modified p1: 55
unique_ptr的所有权转移
unique_ptr
不能被复制,但可以被移动,这意味着所有权可以从一个unique_ptr
转移到另一个。
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> original = std::make_unique<int>(100);
// 以下代码会编译错误 - 不能复制unique_ptr
// std::unique_ptr<int> copy = original;
// 移动所有权是允许的
std::unique_ptr<int> moved = std::move(original);
// 现在original不再拥有任何对象
if (original == nullptr) {
std::cout << "original不再指向任何对象" << std::endl;
}
std::cout << "moved points to: " << *moved << std::endl;
return 0;
}
输出:
original不再指向任何对象
moved points to: 100
2. std::shared_ptr
shared_ptr
允许多个指针指向同一个对象。它实现了引用计数机制,当最后一个shared_ptr
离开作用域时,才会删除所管理的对象。
创建和使用shared_ptr
#include <iostream>
#include <memory>
int main() {
// 创建shared_ptr
std::shared_ptr<int> p1 = std::make_shared<int>(42);
// 创建指向同一对象的另一个shared_ptr
std::shared_ptr<int> p2 = p1;
// 查看引用计数
std::cout << "引用计数: " << p1.use_count() << std::endl;
// 修改对象值
*p1 = 100;
// 两个指针指向同一个对象,所以p2也会看到修改后的值
std::cout << "p1: " << *p1 << std::endl;
std::cout << "p2: " << *p2 << std::endl;
// p1离开作用域
{
std::shared_ptr<int> p3 = p1;
std::cout << "内部块,引用计数: " << p1.use_count() << std::endl;
} // p3离开作用域,引用计数减1
std::cout << "外部块,引用计数: " << p1.use_count() << std::endl;
return 0;
}
输出:
引用计数: 2
p1: 100
p2: 100
内部块,引用计数: 3
外部块,引用计数: 2
3. std::weak_ptr
weak_ptr
是一种弱引用智能指针,它不会增加引用计数。主要用来解决shared_ptr
的循环引用问题,以及观察shared_ptr
所管理的对象是否仍然存在。
weak_ptr的用法
#include <iostream>
#include <memory>
int main() {
// 创建shared_ptr
std::shared_ptr<int> shared = std::make_shared<int>(42);
// 从shared_ptr创建weak_ptr
std::weak_ptr<int> weak = shared;
std::cout << "shared_ptr引用计数: " << shared.use_count() << std::endl;
// 检查weak_ptr是否已过期
if (!weak.expired()) {
// 从weak_ptr获取shared_ptr
std::shared_ptr<int> shared2 = weak.lock();
if (shared2) {
std::cout << "值: " << *shared2 << std::endl;
std::cout << "引用计数现在是: " << shared.use_count() << std::endl;
}
}
// 重置原始shared_ptr
shared.reset();
std::cout << "重置原始shared_ptr后" << std::endl;
// 再次检查weak_ptr
if (weak.expired()) {
std::cout << "weak_ptr已过期" << std::endl;
} else {
std::cout << "weak_ptr仍然有效" << std::endl;
}
return 0;
}
输出:
shared_ptr引用计数: 1
值: 42
引用计数现在是: 2
重置原始shared_ptr后
weak_ptr已过期