什么是智能指针,何时应该使用它?

什么是智能指针,何时应该使用它?

技术背景

在C++编程中,原始指针的使用可能会带来许多问题,例如忘记释放内存导致内存泄漏,以及在异常情况下无法正确释放资源等。为了解决这些问题,智能指针应运而生。智能指针是一种类模板,它封装了原始指针,并提供了自动内存管理的功能,能够在适当的时候自动释放所指向的对象,从而减少因手动管理内存而带来的错误。

实现步骤

1. 包含必要的头文件

在使用标准库中的智能指针时,需要包含<memory>头文件。

1
#include <memory>

2. 使用不同类型的智能指针

2.1 std::unique_ptr

std::unique_ptr 是一种独占所有权的智能指针,同一时间只能有一个 std::unique_ptr 指向同一个对象。当 std::unique_ptr 被销毁时,它所指向的对象也会被自动销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <memory>

class MyObject {
public:
MyObject() { std::cout << "MyObject created" << std::endl; }
~MyObject() { std::cout << "MyObject destroyed" << std::endl; }
void DoSomething() { std::cout << "Doing something..." << std::endl; }
};

void useUniquePtr() {
std::unique_ptr<MyObject> ptr(new MyObject());
ptr->DoSomething();
// 当ptr离开作用域时,MyObject对象会被自动销毁
}

2.2 std::shared_ptr

std::shared_ptr 是一种共享所有权的智能指针,多个 std::shared_ptr 可以指向同一个对象。它使用引用计数来跟踪有多少个 std::shared_ptr 指向同一个对象,当引用计数为0时,对象会被自动销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <memory>

class MyObject {
public:
MyObject() { std::cout << "MyObject created" << std::endl; }
~MyObject() { std::cout << "MyObject destroyed" << std::endl; }
void DoSomething() { std::cout << "Doing something..." << std::endl; }
};

void useSharedPtr() {
std::shared_ptr<MyObject> p1(new MyObject());
std::shared_ptr<MyObject> p2 = p1; // 共享所有权
p1->DoSomething();
p2->DoSomething();
// 当p1和p2都离开作用域时,引用计数变为0,MyObject对象会被自动销毁
}

2.3 std::weak_ptr

std::weak_ptr 是一种弱引用的智能指针,它可以从 std::shared_ptr 或另一个 std::weak_ptr 构造,但不会增加引用计数。它主要用于解决 std::shared_ptr 可能出现的循环引用问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <memory>

class Owner {
public:
std::shared_ptr<Owner> other;
~Owner() { std::cout << "Owner destroyed" << std::endl; }
};

void useWeakPtr() {
std::shared_ptr<Owner> p1 = std::make_shared<Owner>();
std::shared_ptr<Owner> p2 = std::make_shared<Owner>();
std::weak_ptr<Owner> w1 = p1;
std::weak_ptr<Owner> w2 = p2;

p1->other = w2.lock(); // 使用lock()获取shared_ptr
p2->other = w1.lock();

// 当p1和p2离开作用域时,对象会被正确销毁
}

核心代码

std::unique_ptr 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <memory>

class MyClass {
public:
MyClass() {}
~MyClass() {}
void doSomething() {}
};

int main() {
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
ptr->doSomething();
return 0;
}

std::shared_ptr 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <memory>

class MyClass {
public:
MyClass() {}
~MyClass() {}
void doSomething() {}
};

int main() {
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
std::shared_ptr<MyClass> ptr2 = ptr1;
ptr1->doSomething();
ptr2->doSomething();
return 0;
}

std::weak_ptr 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <memory>
#include <iostream>

class MyClass {
public:
MyClass() { std::cout << "MyClass created" << std::endl; }
~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
void doSomething() { std::cout << "Doing something..." << std::endl; }
};

int main() {
std::shared_ptr<MyClass> sharedPtr = std::make_shared<MyClass>();
std::weak_ptr<MyClass> weakPtr = sharedPtr;

if (auto lockedPtr = weakPtr.lock()) {
lockedPtr->doSomething();
}
return 0;
}

最佳实践

  • 优先使用 std::unique_ptr:当对象的所有权是独占的,即同一时间只有一个地方可以访问该对象时,优先使用 std::unique_ptr
  • 使用 std::shared_ptr 进行共享所有权:当需要多个地方共享同一个对象的所有权时,使用 std::shared_ptr
  • 使用 std::weak_ptr 解决循环引用问题:当使用 std::shared_ptr 可能出现循环引用时,使用 std::weak_ptr 来打破循环。
  • 避免直接使用原始指针:尽量避免直接使用原始指针,而是使用智能指针来管理内存,减少内存泄漏的风险。

常见问题

1. 循环引用问题

当使用 std::shared_ptr 时,如果两个或多个对象相互引用,可能会导致循环引用,从而使对象无法被正确销毁。解决方法是使用 std::weak_ptr 来打破循环。

2. std::auto_ptr 已被弃用

std::auto_ptr 是早期的智能指针,它存在一些问题,如在复制时会转移所有权,容易导致空指针异常。在C++11及以后的标准中,std::auto_ptr 已被弃用,建议使用 std::unique_ptr 代替。

3. 性能问题

使用智能指针可能会带来一定的性能开销,尤其是 std::shared_ptr 的引用计数操作。在对性能要求极高的场景中,需要谨慎使用。


什么是智能指针,何时应该使用它?
https://119291.xyz/posts/what-is-a-smart-pointer-and-when-should-i-use-one/
作者
ww
发布于
2025年5月20日
许可协议