目录
一、前言
二、 智能指针的引入 --- 内存泄露
💢什么是内存泄漏?💢
💢内存泄漏有那些危害?💢
💢内存泄漏的原因?💢
💢解决内存泄漏的方法 💢
💢总结 💢
三、智能指针的基本概念
🔥智能指针的案例分析 🔥
🔥智能指针的使用及原理🔥
🍑RAll思想 (智能指针指导思想)
🍍智能指针的原理
四、C++库里的智能指针
💧概述 💧
💧unique_ptr 详解 💧
💧weak_ptr 详解 💧
五、智能指针常考面试题
六、共勉
一、前言
智能指针 这个名词听着挺唬人,其实也没啥,无非就是会自动销毁
new
出来的对象,对于日常使用来说,还是挺方便的,毕竟C/C++
可没有隔壁Java
的垃圾回收机制GC
,得自己清理垃圾, 智能指针 可以自动完成垃圾清理这个工作
二、 智能指针的引入 --- 内存泄露
💢什么是内存泄漏?💢
内存泄漏(memory leak)是指程序在动态分配内存后未能正确释放,导致这些内存无法被回收和重用。
- 内存泄漏 会逐渐耗尽系统内存资源,导致程序性能下降,最终可能导致程序崩溃或系统不稳定。内存泄漏在长期运行的程序(如服务器应用程序)中特别危险。
💢内存泄漏有那些危害?💢
内存泄漏的典型表现包括:
- 程序的内存使用量不断增加,甚至超出可用内存。
- 程序运行速度变慢,响应时间变长。
- 系统资源耗尽,可能导致系统崩溃或冻结。
💢内存泄漏的原因?💢
内存泄漏通常由以下几种原因引起:
1. 忘记释放内存:
- 程序员动态分配了内存但没有调用
delete
或free
释放内存。
void example()
{
int* ptr = new int(42);
// 忘记调用 delete ptr --- 导致内存泄漏
}
2. 丢失指针:
- 动态分配的内存的指针被覆盖或丢失,导致无法释放内存。
void example()
{
int* ptr = new int(42);
ptr = new int(43); // 原来的内存泄漏
delete ptr; // 释放新分配的内存
// 原来的内存无法释放 --- 导致内存泄漏
}
3. 循环引用:
- 循环引用发生在两个或多个对象互相引用对方,形成一个环,导致它们无法被正确释放。
- 假设我们有两个类
A
和B
,它们相互引用对方。我们使用普通指针来演示这种情况:
#include <iostream>
class B; // 前向声明
class A
{
public:
B* b; // A 持有 B 的指针
~A() { std::cout << "A destroyed\n"; }
};
class B
{
public:
A* a; // B 持有 A 的指针
~B() { std::cout << "B destroyed\n"; }
};
void createCycle()
{
A* a = new A();
B* b = new B();
a->b = b; // A 持有 B
b->a = a; // B 持有 A
// 此时 a 和 b 都不会被释放,导致内存泄漏
}
int main()
{
createCycle();
return 0;
}
- 运行结果我不敢看: (*/ω\*),因为像一坨💩
纳尼?A 和 B的析构函数都没有被调用,妥妥的内存泄漏了!
事后,据某位亲身经历这次事件的大牛回忆说:“喔,当时的内存布局是这个样子的!”
- 对于普通指针,我们需要手动调用
delete
来释放内存。如果我们忘记了这一点,内存将无法释放,导致内存泄漏。这种情况在智能指针中也会出现,如果不使用正确的类型,就会导致内存泄漏
💢解决内存泄漏的方法 💢
手动管理内存:
- 严格遵循动态分配内存后必须释放内存的规则。
- 使用工具(如 Valgrind)来检测内存泄漏。
使用智能指针:
std::unique_ptr
:独占所有权,超出作用域时自动释放内存。std::shared_ptr
:共享所有权,引用计数为零时自动释放内存。std::weak_ptr
:与std::shared_ptr
搭配使用,解决循环引用问题。
💢总结 💢
内存泄漏是一个严重的问题,会导致程序资源耗尽、性能下降甚至崩溃。通过使用智能指针、工具检测和良好的编程实践,可以有效地预防和解决内存泄漏问题,确保程序的稳定性和可靠性。
所以【C++11】为了方便大家的代码书写,同时怕大家忘记最后的 内存释放而导致 内存泄漏问题,加入了 智能指针,下面就让我们开启 智能指针 的学习把!!
三、智能指针的基本概念
🔥智能指针的案例分析 🔥
根据上面的学习我们得知内存泄漏是指因为疏忽或者错误,造成程序未能释放已经不再使用的内存的情况,如下就是一个典型的例子:
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
throw invalid_argument("除0错误");
return a / b;
}
void Func()
{
int* p1 = new int[10];
int* p2 = new int[10];
cout << div() << endl;
delete[] p1;
delete[] p2;
}
int main()
{
try
{
Func();
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
代码分析:
div()
函数
- 这个函数读取两个整数
a
和b
。 - 如果
b
等于 0,则抛出一个invalid_argument
异常,信息是 "除0错误"。 - 否则,返回
a / b
的结果。
Func()
函数
- 分配两个整数数组
p1
和p2
,每个包含 10 个元素。 - 调用
div()
函数并输出其返回值。 - 释放之前分配的数组
p1
和p2
。
main()
函数
- 尝试调用
Func()
函数。 - 如果捕获到任何异常(继承自
exception
),输出异常的消息。
内存泄漏分析:
在 Func()
函数中,如果 div()
函数抛出异常(例如输入的 b
为 0),代码将不会继续执行,导致 delete[] p1
和 delete[] p2
不会被调用,从而产生内存泄漏。
为了避免内存泄漏,可以使用 智能指针 来管理内存。例如 std::unique_ptr
:
#include <memory>
void Func()
{
std::unique_ptr<int[]> p1(new int[10]);
std::unique_ptr<int[]> p2(new int[10]);
cout << div() << endl;
}
- 这样,无论是否发生异常,智能指针都会自动释放内存,确保没有内存泄漏。
那么,下文将详细的讲解 ---- 智能指针
🔥智能指针的使用及原理🔥
🍑RAll思想 (智能指针指导思想)
RAII是一种利用对象生命周期来控制程序资源(例如内存、文件句柄、网络连接、互斥量等)的简单技术.
获取到资源以后去初始化一个对象,将资源交给对象管理:资源获取即初始化
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。 借此,我们实际是把管理一份资源的责任托管给了一个对象,这种做法有两大好处:
- 不需要显式的释放资源
- 采用这种方式,对象所需的资源在其生命周期内始终有效
如下我们来使用 RAII的思想 设计一个智能指针SmartPtr
//实现一个最简易的智能指针
template<class T>
class smart_ptr
{
public:
smart_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
//对象析构时自动释放所管理的资源
~smart_ptr()
{
cout << "delete " << _ptr << endl;
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
private:
T* _ptr;
};
- 此时,我们使用智能指针来代替裸指针,并让它发生除0错误:
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
{
throw invalid_argument("除0错误");
}
return a / b;
}
void func()
{
smart_ptr<int> p1(new int);
smart_ptr<int> p2(new int);
cout << div() << endl;
}
int main()
{
try
{
func();
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
- 可以看到,刚才由于抛异常未能释放的资源现在可以正常释放.
🍍智能指针的原理
我们所写的这个简单的智能指针
smart_ptr
还不能称其为智能指针,因为它还不具有指针的行为,指针可以解引用,可以通过->去访问所指向空间内的内容,所以为了让它向指针一样,我们还需重载*
,->
运算符.
template<class T>
class smart_ptr
{
public:
smart_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
~smart_ptr()
{
cout << "delete " << _ptr << endl;
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
T& operator*() const
{
return *_ptr;
}
T* get() const
{
return _ptr;
}
T* operator->() const //T* = const T* _ptr
{
return _ptr;
}
private:
T* _ptr;
};
- 但是这样的智能指针是有问题的,试一下它的拷贝?
int main()
{
smart_ptr<int> sp1(new int);
smart_ptr<int> sp2(sp1);//拷贝构造
smart_ptr<int> sp3(new int);
smart_ptr<int> sp4(new int);
sp3 = sp4;//拷贝赋值
return 0;
}
此时我要拿sp1拷贝给sp2完成拷贝构造,或者是拿sp3赋值给sp3都会存在问题,导致程序崩溃,原因如下:
- 编译器默认生成的拷贝构造函数对内置类型完成值拷贝(浅拷贝),因此sp1拷贝sp2后,相当于sp1和sp2管理了同一块内存空间,所以当sp1和sp2析构时就会导致这块空间被释放两次,程序崩溃。
- 类似的,把sp4赋值给sp3时,相当于sp3和sp4管理的都是原来sp3管理的空间,当sp3和sp4析构时就会导致这块空间被释放两次,并且还会导致sp4原来管理的空间没有得到释放。
那么,怎么去解决这个问题呢?
所以,接下来,我们来介绍几种C++标准库里的智能指针,来探究如何解决此问题
四、C++库里的智能指针
💧概述 💧
C++智能指针-------是一类对象,它们封装了原始指针,并提供了一种机制来自动管理和释放所指向的对象。智能指针通过自动管理内存生命周期,减少了手动内存管理带来的复杂性和错误风险。
unique_ptr
- 这是一种独占所有权的智能指针,它不允许拷贝构造,但支持移动语义 move。
- 这意味着同一时间只能有一个
unique_ptr
指向给定的资源。 - 这避免了多个指针指向同一块内存的情况,减少了内存泄漏的风险。
shared_ptr
- 这是一种引用计数智能指针,它允许多个指针拥有同一个对象。
- 当最后一个拥有该对象的
shared_ptr
被销毁时,它会自动释放所指向的内存。 - 使用
std::make_shared
可以高效地创建一个shared_ptr
实例。
weak_ptr
- 它是一种与shared_ptr配合使用的智能指针,主要用于解决shared_ptr循环引用的问题。
- weak_ptr不会增加引用计数,因此可以用来打破潜在的循环引用,防止内存无法释放。
- 在访问所引用的对象前必须先转换为 std::shared_ptr。
- std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
头文件
#include <memory>
💧unique_ptr 详解 💧
unique_ptr
是 C++11 引入的一种智能指针,提供独占式所有权的内存管理。它确保一个时刻只有一个指针拥有所管理的对象,并在指针超出作用域时自动释放内存。下面详细讲解unique_ptr
的用法和用途。
1. 创建 unique_ptr
可以使用 new
关键字创建动态对象,并将其指针传递给 unique_ptr
:
#include <iostream>
#include <memory> // 包含智能指针的头文件
int main()
{
std::unique_ptr<int> ptr(new int(10)); // 创建一个指向整数 10 的 unique_ptr
std::cout << "Value: " << *ptr << std::endl; // 输出 10
return 0; // ptr 超出作用域时自动释放内存
}
2. 使用 make_unique
(C++14 起)
make_unique
是一种更安全的创建 unique_ptr
的方法,避免了使用 new
关键字:
#include <iostream>
#include <memory>
int main()
{
unique_ptr<int> ptr = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
std::cout << "Value: " << *ptr << std::endl; // 输出 10
return 0; // ptr 超出作用域时自动释放内存
}
3. 使用 std::move
转移所有权
unique_ptr
不允许复制,但可以通过 std::move
转移所有权:
#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 超出作用域时自动释放内存
}
4. 管理动态数组
unique_ptr
可以管理动态分配的数组:
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int[]> arr(new int[5]); // 创建一个指向包含 5 个整数的数组的 unique_ptr
for (int i = 0; i < 5; ++i)
{
arr[i] = i * 10; // 赋值
}
for (int i = 0; i < 5; ++i)
{
std::cout << arr[i] << " "; // 输出数组元素
}
std::cout << std::endl;
return 0; // arr 超出作用域时自动释放内存
}
5. unique_ptr的
单一所有权
下面的示例代码展示了如何使用 unique_ptr
来管理动态分配的内存:
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> ptr1 = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
std::cout << "Value: " << *ptr1 << std::endl; // 输出 10
// 试图复制 unique_ptr 会导致编译错误
// std::unique_ptr<int> ptr2 = ptr1; // 错误,unique_ptr 不可复制
return 0; // ptr1 超出作用域时自动释放内存
}
总结std::unique_ptr
的用途主要有以下几个方面:
- 自动管理动态内存:避免显式调用
delete
,防止内存泄漏。 - 独占所有权语义:确保对象在同一时刻只有一个所有者,防止多个指针同时管理同一对象带来的问题。
- 转移所有权:通过
std::move
可以安全地转移对象的所有权。 - RAII(资源获取即初始化):保证资源在对象创建时被获取,并在对象销毁时被释放。
总的来说,
unique_ptr
是 C++11 提供的一种强大工具,用于安全、简洁地管理动态内存和其他资源。通过理解和正确使用unique_ptr
,可以显著提高代码的健壮性和可维护性。
💧shared_ptr 详解 💧
shared_ptr
是 C++11 引入的一种智能指针,用于共享所有权的内存管理。多个shared_ptr
对象可以指向同一个对象,当最后一个shared_ptr
释放时,该对象的内存才会被释放。下面详细讲解shared_ptr
的用法和用途。
1. 创建 shared_ptr
可以使用 make_shared
或直接使用构造函数创建 shared_ptr
:
#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;
}
2. 共享所有权
多个 shared_ptr
可以指向同一个对象,并共享该对象的所有权:
#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;
}
3. 自动释放内存
当最后一个 shared_ptr
超出作用域时,所管理的对象会被自动释放:
#include <iostream>
#include <memory>
void createAndDestroy()
{
std::shared_ptr<int> ptr = std::make_shared<int>(40);
std::cout << "Value: " << *ptr << std::endl; // 输出 40
std::cout << "Use count inside function: " << ptr.use_count() << std::endl; // 输出 1
// ptr 超出作用域,内存自动释放
}
int main()
{
createAndDestroy();
// 到这里,createAndDestroy 函数中的 shared_ptr 已经超出作用域,内存被释放
return 0;
}
4. 避免循环引用
shared_ptr
可能导致循环引用,从而造成内存泄漏。使用 std::weak_ptr
可以打破循环引用:
#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 超出作用域,内存被释放
}
总结
std::shared_ptr
是 C++ 中一种非常有用的智能指针,它提供共享所有权的内存管理功能,使得多个指针可以安全地共享同一个对象,并在最后一个指针销毁时自动释放资源。通过使用shared_ptr
,可以简化内存管理,减少内存泄漏的风险,并使代码更加安全和易于维护。
💧weak_ptr 详解 💧
std::weak_ptr
是 C++11 引入的智能指针类型,用于解决std::shared_ptr
的循环引用问题。它提供了一种不参与对象引用计数的弱引用,这意味着它不会影响所管理对象的生命周期。weak_ptr
只能从shared_ptr
或另一个weak_ptr
构造,并且只能提升(lock)为shared_ptr
以访问对象。下面详细讲解weak_ptr
的用法和用途。
避免循环引用
- 循环引用是指当一个对象或者一个单元格内的公式直接或间接地引用了自己或者另一个对象。这样会导致内存泄漏或者计算错误。也就是说,如果两个shared_ptr指针互相引用,那么它们的引用计数永远不会为零,也就无法释放内存。
struct Node
{
std::shared_ptr<Node> _next;
std::shared_ptr<Node> _prev;
~Node()
{
cout << "~Node" << endl;
}
};
int main()
{
std::shared_ptr<Node> n1(new Node);
std::shared_ptr<Node> n2(new Node);
// 循环引用
n1->_next = n2;
n2->_prev = n1;
return 0;
}
图例解析如下:
其中,两个赋值语句造成了两个结点对象n1和n2循环引用:
n1
和 n2
两个 Node
对象通过 _next
和 _prev
成员变量相互引用,导致它们的引用计数永远不为零,从而无法被销毁。
死循环:资源只有在引用计数为1时才能被销毁。左边资源只有当右边的_prev释放以后引用计数才为0,而右边资源只有当左边的_next释放以后引用计数才为0。
解决这个问题的一种方法是使用 std::weak_ptr
来代替其中任意一个方向上的 std::shared_ptr
。
std::weak_ptr
是一种智能指针,它用来解决 std::shared_ptr
循环引用的问题。它不会增加所指向对象的引用计数,因此不会影响对象的销毁。
对于上面的例子,造成问题的本质是引用计数永不为0,那么只要将其中一个智能指针改为weak_ptr即可:
struct Node
{
std::shared_ptr<Node> _next;
std::weak_ptr<Node> _prev; // 将_prev用weak_ptr管理
~Node()
{
cout << "~Node" << endl;
}
};
与shared_ptr的关系
- weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期。也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
- 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造,它的构造和析构不会引起引用记数的增加或减少。弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。
也就是说,weak_ptr是为了弥补shared_ptr循环引用而生的,它没有RAII的特性,不直接管理资源,只是shared_ptr的跟班,这也是weak_ptr支持使用shared_ptr构造的原因。
在使用上,weak_ptr支持指针所有的操作。它不是一个功能型的智能指针,而是辅助型,它的使命是解决shared_ptr造成的循环引用问题。
与 shared_ptr
不同,weak_ptr
不能直接访问所指向的对象。要访问对象,需要先调用 lock()
方法将其转换为 shared_ptr
。如果所指向的对象已经被销毁,则 lock()
方法返回空指针。
#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;
}
五、智能指针常考面试题
1. 什么是智能指针?它们的优点是什么?
- 智能指针是对象,它们封装了原始指针,并提供了自动内存管理功能。
- 优点包括:自动释放资源,防止内存泄漏,减少悬挂指针,提高代码的可维护性和安全性。
2. 智能指针为什是 对象?
-
封装性: 智能指针封装了原始指针以及其他与内存管理相关的功能,例如引用计数(对于
shared_ptr
),删除器(对于自定义删除器的智能指针),等等。 -
构造函数和析构函数: 智能指针具有构造函数和析构函数,这些构造函数和析构函数在智能指针的生命周期内管理资源的分配和释放。例如,
shared_ptr
会在对象被销毁时自动减少引用计数,并在引用计数为零时释放资源。 -
成员函数: 智能指针提供了一些成员函数,用于访问和管理其封装的指针。例如,
unique_ptr
提供release
、reset
、get
等成员函数,shared_ptr
提供use_count
、unique
、reset
等成员函数。 -
运算符重载: 智能指针重载了一些运算符,使其使用起来更像原始指针。例如,
operator*
和operator->
允许智能指针像原始指针一样使用。
#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;
}
在这个例子中,uniquePtr
, sharedPtr
, 和 weakPtr
都是智能指针对象,它们封装了原始指针并提供了一些额外的功能用于资源管理。
3. C++中有哪些智能指针?它们的用途是什么?
std::unique_ptr
: 提供独占所有权的智能指针,适用于单一所有权场景。std::shared_ptr
: 提供共享所有权的智能指针,适用于多个对象共享同一资源的场景。std::weak_ptr
: 用于解决shared_ptr
的循环引用问题,不参与引用计数。
4. 如何创建一个
std::unique_ptr
?
使用std::make_unique
:
auto ptr = std::make_unique<int>(10);
使用构造函数:
std::unique_ptr<int> ptr(new int(10));
5.std::shared_ptr
如何实现引用计数?
-
std::shared_ptr
内部维护一个引用计数,当一个新的shared_ptr
对象指向同一个对象时,引用计数增加;当shared_ptr
对象被销毁时,引用计数减少。引用计数为零时,释放所管理的对象。
6. 什么是
std::weak_ptr
?它的作用是什么?
std::weak_ptr
是一个不增加引用计数的智能指针,主要用于避免shared_ptr
的循环引用问题。weak_ptr
只能从shared_ptr
或另一个weak_ptr
构造,可以使用lock
方法提升为shared_ptr
。
7. 如何避免
shared_ptr
的循环引用?
- 使用
std::weak_ptr
打破循环引用。例如,在双向链表或树结构中,父节点使用shared_ptr
指向子节点,而子节点使用weak_ptr
指向父节点。
8.
std::make_shared
与直接使用构造函数创建shared_ptr
有何不同?
std::make_shared
会在单个内存块中分配对象和引用计数,具有更好的性能和更少的内存碎片。- 使用构造函数时,引用计数和对象可能分配在不同的内存块中。
9. std::unique_ptr
是否可以复制?如果不能,如何转移所有权?
std::unique_ptr
不支持复制,因为它提供独占所有权。- 可以使用
std::move
转移所有权:
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
std::unique_ptr<int> ptr2 = std::move(ptr1);
10. 在什么情况下选择使用智能指针而不是原始指针?
- 智能指针适用于需要自动管理生命周期的场景,可以避免内存泄漏和悬挂指针问题。原始指针适用于简单的指针运算和需要精细控制生命周期的场景。
六、共勉
以下就是我对 【C++11】智能指针 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 【C++11】 的理解,请持续关注我哦!!!