一、普通指针的缺点
- 普通指针需要手动释放内存
- 相互引用时内存无法释放
二、智能指针
1、智能指针介绍
- 智能指针本质是对象: 它们封装了原始指针,并提供了自动内存管理功能。
- 构造函数和析构函数: 智能指针具有构造函数和析构函数,这些构造函数和析构函数在智能指针的生命周期内管理资源的分配和释放。例如,
shared_ptr
会在对象被销毁时自动减少引用计数,并在引用计数为零时释放资源。 - 成员函数 : 智能指针提供了一些成员函数,用于访问和管理其封装的指针。例如,
unique_ptr
提供release
、reset
、get
等成员函数,shared_ptr
提供use_count
、unique
、reset
等成员函数。 - 运算符重载 : 智能指针重载了一些运算符,使其使用起来更像原始指针。例如,
operator*
和operator->
允许智能指针像原始指针一样使用。
cpp
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> uniquePtr = std::make_unique<int>(10); // unique_ptr 是一个对象
std::shared_ptr<int> sharedPtr = std::make_shared<int>(20); // shared_ptr 是一个对象
std::weak_ptr<int> weakPtr = sharedPtr; // weak_ptr 是一个对象
std::cout << "uniquePtr: " << *uniquePtr << std::endl;
std::cout << "sharedPtr: " << *sharedPtr << std::endl;
// 调用 shared_ptr 的成员函数 use_count()
std::cout << "sharedPtr use count: " << sharedPtr.use_count() << std::endl;
return 0;
}
2、智能指针分类
std::unique_ptr
: 提供独占所有权的智能指针,适用于单一所有权场景。std::shared_ptr
: 提供共享所有权的智能指针,适用于多个对象共享同一资源的场景。std::weak_ptr
: 用于解决**shared_ptr
**的循环引用问题,不参与引用计数。
3、std::unique_ptr 介绍
- 创建一个
std::unique_ptr
cpp
//使用std::make_unique
auto ptr = std::make_unique<int>(10);
//使用构造函数
std::unique_ptr<int> ptr(new int(10));
std::unique_ptr
不可复制和拷贝构造,只能使用std::move
转移所有权
cpp
#include <iostream>
#include <memory>
int main()
{
auto ptr1 = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
std::unique_ptr<int> ptr2 = std::move(ptr1); // 转移所有权给 ptr2
if (!ptr1) {
std::cout << "ptr1 is null" << std::endl; // ptr1 现在为空
}
std::cout << "Value: " << *ptr2 << std::endl; // 输出 10
return 0; // ptr2 超出作用域时自动释放内存
}
- 使用实例
cpp
#include <iostream>
#include <memory>
int main()
{
// 创建一个 std::unique_ptr,拥有整数对象的唯一所有权
std::unique_ptr<int> uniqueInt = std::make_unique<int>(42);
// 唯一所有权转移
std::unique_ptr<int> anotherUniqueInt = std::move(uniqueInt);
// 访问唯一的对象 int value = *anotherUniqueInt;
std::cout << "anotherUniqueInt: " << value << std::endl;
// 释放 anotherUniqueInt 指向的资源 anotherUniqueInt.reset();
// 检查是否为空
if (!anotherUniqueInt)
{
std::cout << "anotherUniqueInt is null" << std::endl;
}
return 0;
}
4、std::shared_ptr介绍
- 创建
shared_ptr
cpp
#include <iostream>
#include <memory>
int main()
{
// 使用 make_shared 创建 shared_ptr
std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
std::cout << "Value: " << *ptr1 << std::endl; // 输出 10
// 使用构造函数创建 shared_ptr
std::shared_ptr<int> ptr2(new int(20));
std::cout << "Value: " << *ptr2 << std::endl; // 输出 20
return 0;
}
- 共享所有权
多个shared_ptr
可以指向同一个对象,并共享该对象的所有权:
cpp
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int> ptr1 = std::make_shared<int>(30);
// ptr2 共享 ptr1 所指向的对象
std::shared_ptr<int> ptr2 = ptr1;
std::cout << "Value from ptr1: " << *ptr1 << std::endl; // 输出 30
std::cout << "Value from ptr2: " << *ptr2 << std::endl; // 输出 30
// 引用计数
std::cout << "Use count: " << ptr1.use_count() << std::endl; // 输出 2
return 0;
}
- 避免循环引用
shared_ptr
可能导致循环引用,从而造成内存泄漏。使用std::weak_ptr
可以打破循环引用:
cpp
#include <iostream>
#include <memory>
struct Node
{
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 使用 weak_ptr 打破循环引用
~Node() {
std::cout << "Node destroyed" << std::endl;
}
};
int main()
{
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->prev = node1;
std::cout << "Use count of node1: " << node1.use_count() << std::endl; // 输出 1
std::cout << "Use count of node2: " << node2.use_count() << std::endl; // 输出 1
return 0; // node1 和 node2 超出作用域,内存被释放
}
- 使用实例
cpp
#include <iostream>
#include <memory>
int main() {
// 创建一个 std::shared_ptr,共享整数对象
std::shared_ptr<int> sharedInt = std::make_shared<int>(42);
// 共享相同的对象 std::shared_ptr<int> anotherSharedInt = sharedInt;
// 访问共享的对象 int value = *sharedInt;
int anotherValue = *anotherSharedInt;
std::cout << "sharedInt: " << value << std::endl; std::cout << "anotherSharedInt: " << anotherValue << std::endl;
// 检查引用计数 int count = sharedInt.use_count();
std::cout << "Reference count: " << count << std::endl;
// 释放 sharedInt 指向的资源 sharedInt.reset();
// 再次检查引用计数 count = anotherSharedInt.use_count();
std::cout << "Reference count after reset: " << count << std::endl;
return 0;
}
5、std::weak_ptr介绍
- weak_ptr创建
weak_ptr
只能从shared_ptr
或另一个weak_ptr
构造,可以使用lock
方法提升为shared_ptr
,如果所指向的对象已经被销毁,则lock()
方法返回空指针。
cpp
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sharedPtr = std::make_shared<int>(100);
std::weak_ptr<int> weakPtr = sharedPtr;
if (auto lockedPtr = weakPtr.lock()) { // 尝试提升为 shared_ptr
std::cout << "Value: " << *lockedPtr << std::endl; // 输出 100
} else {
std::cout << "Pointer is expired" << std::endl;
}
sharedPtr.reset(); // 手动释放 shared_ptr 所管理的对象
if (weakPtr.expired()) {
std::cout << "Pointer is expired" << std::endl; // 输出 Pointer is expired
}
return 0;
}
- 使用实例
cpp
#include <iostream>
include <memory>
int main()
{
// 创建一个 std::shared_ptr 来管理整数对象
std::shared_ptr<int> sharedInt = std::make_shared<int>(42);
// 创建一个 std::weak_ptr 来观察 sharedInt
std::weak_ptr<int> weakInt = sharedInt;
// 使用 std::weak_ptr 来访问对象
if (auto shared = weakInt.lock())
{
int value = *shared;
std::cout << "Weak pointer is valid: " << value << std::endl;
}
else
{
std::cout << "Weak pointer is expired" << std::endl;
}
// 释放 sharedInt 的资源
sharedInt.reset();
// 再次使用 std::weak_ptr
if (auto shared = weakInt.lock())
{
std::cout << "Weak pointer is still valid: " << *shared << std::endl;
}
else
{
std::cout << "Weak pointer is expired" << std::endl;
}
return 0;
}
6、使用场景
在什么情况下选择使用智能指针而不是原始指针?
- 智能指针适用于需要自动管理生命周期的场景,可以避免内存泄漏和悬挂指针问题。原始指针适用于简单的指针运算和需要精细控制生命周期的场景。