C语言中如何将智能指针改写为长尾?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1505个文字,预计阅读时间需要7分钟。
目录:一:背景二:关键词解释
1.auto_ptr
2.auto_ptr 与多重引用问题
一:背景
我们知道 C++ 是一种手动管理内存的语言,程序员需要通过 new 和 delete 操作符来分配和释放内存。这给程序员带来了极大的责任,也容易引发内存泄漏等问题。二:关键词解释
1.auto_ptr
auto_ptr 是 C++ 标准库中提供的一种智能指针,它封装了原始指针,并负责在适当的时机自动释放所管理的内存。auto_ptr 使用 RAII(Resource Acquisition Is Initialization)技术,确保资源在对象生命周期结束时自动被释放。2. auto_ptr 与多重引用问题auto_ptr 存在一个被称为双重释放问题的缺点。当多个 auto_ptr 指向同一内存地址时,一旦其中一个 auto_ptr 被销毁,它将尝试释放该内存地址,从而导致程序崩溃。为了避免这个问题,通常使用 shared_ptr 或 unique_ptr 代替 auto_ptr。
目录
- 一:背景
- 二:关键词解析
- 1. auto_ptr
- 2. auto_ptr 多引用问题
一:背景
我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是new/delete和new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛,弄不好就得内存泄露,比如下面的代码:
void test() { int* i = new int(10); *i = 10; } int main() { test(); }
这段代码因为用了 new 而忘了 delete,导致在nt heap上分配的 i 随着栈地址的回收而成了一块孤悬海外的内存占用,所以修正后的代码如下:
void test() { int* i = new int(10); *i = 10; delete i; } int main() { test(); }
但这种写法比较麻烦,智者千虑必有一失,总会有忘记加 delete 的时候,那怎么办呢? 大家应该知道内存自动管理有两种手段。
1.引用计数
代表作有 Python,PHP,还有 windows 的句柄管理。
2.引用跟踪
代表作有 C#,JAVA 等一众工程化语言。
因为引用计数实现比较简单,主要就是记录下对象的引用次数,次数为 0 则释放,所以可完全借助类的构造函数析构函数和栈的自动回收特性弄一个简单的引用计数,对应着如下四个关键词。
- auto_ptr
- shared_ptr
- unique_ptr
- weak_ptr
接下来我们逐个聊一聊。
二:关键词解析
1. auto_ptr
这是 C++ 最早出现一个的简单引用计数法,参考代码如下:
void test() { auto_ptr<int> ptr = auto_ptr<int>(new int(10)); } int main() { test(); }
接下来看下汇编代码:
auto_ptr<int> ptr = auto_ptr<int>(new int(10));
...
00771D26 call std::auto_ptr<int>::auto_ptr<int> (07710FAh)
00771D2B lea ecx,[ebp-0D8h]
00771D31 call std::auto_ptr<int>::~auto_ptr<int> (0771159h)
可以看到,它分别调用了构造函数和析构函数,接下来找下 auto_ptr 这两个函数的源码。
class auto_ptr { private: _Ty* _Myptr; // the wrapped object pointer public: auto_ptr(auto_ptr_ref<_Ty> _Right) noexcept { _Ty* _Ptr = _Right._Ref; _Right._Ref = nullptr; // release old _Myptr = _Ptr; // reset this } ~auto_ptr() noexcept { delete _Myptr; } }
源码一看就明白了,在构造函数中,将 new int 的地址塞给了内部的_Myptr指针,在析构函数中对_Myptr进行delete,真好,这样就不用整天担心有没有加 delete 啦。
值得注意的是,现在 C++ 不推荐这个了,而是建议使用新增的:shared_ptr,unique_ptr,weak_ptr, 怎么说呢? auto_ptr 有一个不好处理的问题,就是现实开发中会出现这么个场景,多个 ptr 指向同一个 引用,如下图:
2. auto_ptr 多引用问题
方式1:
定义三个 ptr,然后包装同一个new int地址,参考代码如下:
void test() { int* i = new int(10); auto_ptr<int> ptr1(i); auto_ptr<int> ptr2(i); auto_ptr<int> ptr3(i); }
这种写法有没有问题呢? 肯定有问题啦,还记得 auto_ptr 的析构是 delete 吗? 对同一块内存多次 delete 会抛异常的,如下图所示:
方式2:
既然定义三个有问题, 那就用赋值运算符=让 ptr1,ptr2,ptr3 指向同一个地址是不是就可以啦? 参考代码如下:
void test() { int* i = new int(10); auto_ptr<int> ptr1(i); auto_ptr<int> ptr2 = ptr1; auto_ptr<int> ptr3 = ptr2; } int main() { test(); }
那这段代码有没有问题呢? 有没有问题得要看=运算符是如何重写的,扒一下源码看看。
template <class _Other> auto_ptr& operator=(auto_ptr<_Other>& _Right) noexcept { reset(_Right.release()); return *this; } _Ty* release() noexcept { _Ty* _Tmp = _Myptr; _Myptr = nullptr; return _Tmp; }
从源码看有一个很恶心的点,他会将_Right下的_Myptr设为 nullptr,也就是说此时的 ptr1 报废了,言外之意就是后续再访问 ptr1 会抛访问违例。
哈哈,C++里面的专业术语叫控制权转移。
以上就是一起聊聊C++中的智能指针的详细内容,更多关于C++智能指针的资料请关注自由互联其它相关文章!
本文共计1505个文字,预计阅读时间需要7分钟。
目录:一:背景二:关键词解释
1.auto_ptr
2.auto_ptr 与多重引用问题
一:背景
我们知道 C++ 是一种手动管理内存的语言,程序员需要通过 new 和 delete 操作符来分配和释放内存。这给程序员带来了极大的责任,也容易引发内存泄漏等问题。二:关键词解释
1.auto_ptr
auto_ptr 是 C++ 标准库中提供的一种智能指针,它封装了原始指针,并负责在适当的时机自动释放所管理的内存。auto_ptr 使用 RAII(Resource Acquisition Is Initialization)技术,确保资源在对象生命周期结束时自动被释放。2. auto_ptr 与多重引用问题auto_ptr 存在一个被称为双重释放问题的缺点。当多个 auto_ptr 指向同一内存地址时,一旦其中一个 auto_ptr 被销毁,它将尝试释放该内存地址,从而导致程序崩溃。为了避免这个问题,通常使用 shared_ptr 或 unique_ptr 代替 auto_ptr。
目录
- 一:背景
- 二:关键词解析
- 1. auto_ptr
- 2. auto_ptr 多引用问题
一:背景
我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是new/delete和new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛,弄不好就得内存泄露,比如下面的代码:
void test() { int* i = new int(10); *i = 10; } int main() { test(); }
这段代码因为用了 new 而忘了 delete,导致在nt heap上分配的 i 随着栈地址的回收而成了一块孤悬海外的内存占用,所以修正后的代码如下:
void test() { int* i = new int(10); *i = 10; delete i; } int main() { test(); }
但这种写法比较麻烦,智者千虑必有一失,总会有忘记加 delete 的时候,那怎么办呢? 大家应该知道内存自动管理有两种手段。
1.引用计数
代表作有 Python,PHP,还有 windows 的句柄管理。
2.引用跟踪
代表作有 C#,JAVA 等一众工程化语言。
因为引用计数实现比较简单,主要就是记录下对象的引用次数,次数为 0 则释放,所以可完全借助类的构造函数析构函数和栈的自动回收特性弄一个简单的引用计数,对应着如下四个关键词。
- auto_ptr
- shared_ptr
- unique_ptr
- weak_ptr
接下来我们逐个聊一聊。
二:关键词解析
1. auto_ptr
这是 C++ 最早出现一个的简单引用计数法,参考代码如下:
void test() { auto_ptr<int> ptr = auto_ptr<int>(new int(10)); } int main() { test(); }
接下来看下汇编代码:
auto_ptr<int> ptr = auto_ptr<int>(new int(10));
...
00771D26 call std::auto_ptr<int>::auto_ptr<int> (07710FAh)
00771D2B lea ecx,[ebp-0D8h]
00771D31 call std::auto_ptr<int>::~auto_ptr<int> (0771159h)
可以看到,它分别调用了构造函数和析构函数,接下来找下 auto_ptr 这两个函数的源码。
class auto_ptr { private: _Ty* _Myptr; // the wrapped object pointer public: auto_ptr(auto_ptr_ref<_Ty> _Right) noexcept { _Ty* _Ptr = _Right._Ref; _Right._Ref = nullptr; // release old _Myptr = _Ptr; // reset this } ~auto_ptr() noexcept { delete _Myptr; } }
源码一看就明白了,在构造函数中,将 new int 的地址塞给了内部的_Myptr指针,在析构函数中对_Myptr进行delete,真好,这样就不用整天担心有没有加 delete 啦。
值得注意的是,现在 C++ 不推荐这个了,而是建议使用新增的:shared_ptr,unique_ptr,weak_ptr, 怎么说呢? auto_ptr 有一个不好处理的问题,就是现实开发中会出现这么个场景,多个 ptr 指向同一个 引用,如下图:
2. auto_ptr 多引用问题
方式1:
定义三个 ptr,然后包装同一个new int地址,参考代码如下:
void test() { int* i = new int(10); auto_ptr<int> ptr1(i); auto_ptr<int> ptr2(i); auto_ptr<int> ptr3(i); }
这种写法有没有问题呢? 肯定有问题啦,还记得 auto_ptr 的析构是 delete 吗? 对同一块内存多次 delete 会抛异常的,如下图所示:
方式2:
既然定义三个有问题, 那就用赋值运算符=让 ptr1,ptr2,ptr3 指向同一个地址是不是就可以啦? 参考代码如下:
void test() { int* i = new int(10); auto_ptr<int> ptr1(i); auto_ptr<int> ptr2 = ptr1; auto_ptr<int> ptr3 = ptr2; } int main() { test(); }
那这段代码有没有问题呢? 有没有问题得要看=运算符是如何重写的,扒一下源码看看。
template <class _Other> auto_ptr& operator=(auto_ptr<_Other>& _Right) noexcept { reset(_Right.release()); return *this; } _Ty* release() noexcept { _Ty* _Tmp = _Myptr; _Myptr = nullptr; return _Tmp; }
从源码看有一个很恶心的点,他会将_Right下的_Myptr设为 nullptr,也就是说此时的 ptr1 报废了,言外之意就是后续再访问 ptr1 会抛访问违例。
哈哈,C++里面的专业术语叫控制权转移。
以上就是一起聊聊C++中的智能指针的详细内容,更多关于C++智能指针的资料请关注自由互联其它相关文章!

