目录
std::auto_ptr:C++98中的智能指针,存在问题,不推荐使用。
std::unique_ptr:C++11中引入,不允许拷贝,只能移动。
10--智能指针
在C++编程中,内存管理一直是一个复杂而关键的话题。传统的new
和delete
操作虽然直接,但很容易导致内存泄漏和其他资源管理问题。随着C++的发展,利用RAII的智能指针作为现代C++内存管理的解决方案应运而生。智能指针是C++内存管理的一大进步,它们提供了一种安全、高效的方式来自动管理动态分配的内存减少内存泄漏的风险,同时简化代码的复杂性。
1、内存管理的挑战和智能指针的必要性:
在传统C++编程中,内存管理是一项至关重要的任务。传统的手动内存管理方式虽然灵活,但容易出错。例如,例如,当new
操作抛出异常时,程序可能在运行中途终止,如果没有适当的异常处理和内存释放机制,就可能导致内存泄漏。为了解决这一问题,C++11标准引入了智能指针,它们提供了自动内存管理的机制,极大地简化了资源管理的复杂性。
2、内存泄漏:
内存泄漏发生在程序未能释放不再使用的内存时。长期运行的程序如果出现内存泄漏,会逐渐变慢,甚至崩溃。智能指针通过自动管理内存,有效避免了内存泄漏的问题。发生内存泄露的场景主要集中在异常安全问题。
#include<iostream>
using namespace std;
double Division(double a, double b)
{
if (b == 0)
throw "number b == 0";
return a / b;
}
int main()
{
int* a = new int(10);
int* b = new int(0);
Division(*a, *b);
// 异常抛出未处理,a、b将无法释放,导致内存泄露
delete a;
delete b;
return 0;
}
避免内存泄漏的方法:
- 良好的设计规范和编码习惯(也不好弄)
- 使用RAII(Resource Acquisition Is Initialization)技术和智能指针。
3、智能指针和原理:
- RAII(Resource Acquisition Is Initialization):资源在对象构造时获取,在析构时释放。
RAII是一种利用对象的生命周期来管理资源的技术。它的核心思想是在对象构造时获取资源,并在对象析构时释放资源。这样,资源的管理和对象的生命周期紧密相连,确保了资源的正确管理。
4、C++标准库中的智能指针:
std::auto_ptr
:C++98中的智能指针,存在问题,不推荐使用。
auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存
头文件:<menory>
get()
: 返回指向auto_ptr
管理的对象的指针。这个函数不会转移所有权,只是返回指针。release()
: 返回指向auto_ptr
管理的对象的指针,并将auto_ptr
置为空(不再管理对象)。这个函数会转移所有权,所以在调用后,你需要自己负责释放对象的内存。reset()
:用于将auto_ptr
置为空,不再管理对象。它的作用类似于release()
,但是不返回指向对象的指针。相反,它只是将auto_ptr
置为空,释放对对象的所有权。
Example:
#include<iostream>
#include<memory>
using namespace std;
int main()
{
auto_ptr<int> autoPtr(new int);
cout<<*autoPtr<<endl; // 325344784
*autoPtr = -1;
cout<<*autoPtr<<endl; // -1
cout<<autoPtr.get()<<endl; // 0x25513641ab0
int* p = autoPtr.release();
cout<<*p<<endl; // -1
autoPtr.reset(new int(100));
cout<< *autoPtr<<endl; // 100
return 0;
}
auto_ptr
没有完全解决拷贝时绑定资源的问题(主要是资源的管理和释放),而是使用管理权转移的手段
!!! 自从 C++11 之后,auto_ptr
已被 unique_ptr
取代,C++11后不建议使用auto_ptr
std::unique_ptr
:C++11中引入,不允许拷贝,只能移动。
unique_ptr
直接禁止拷贝构造、赋值运算符重载等可能涉及资源转移的操作
注意:unique_ptr构造的时候不能使用等号!!!
No viable conversion from 'Date *' to 'unique_ptr<Date>'
#include<iostream>
using namespace std;
int main()
{
//unique_ptr<Date> p = new Date; // error(unique_ptr不支持赋值)
unique_ptr<Date> p(new Date); // 成功
return 0;
}
源码实现
#pragma once
namespace my_stl
{
template<class T>
class unique_ptr
{
private:
T* _ptr;
public:
unique_ptr() {}
unique_ptr(T* ptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if (_ptr) // 检查内部指针情况
{
delete _ptr;
_ptr = nullptr;
}
}
unique_ptr<T>(const unique_ptr<T>& p) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& p) = delete;
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
};
}
std::shared_ptr
:通过引用计数管理资源,支持多个智能指针共享同一资源。
源码实现:在unique_ptr的基础上添加拷贝构造和赋值运算符重载即可
[warring]此代码没有考虑线程安全!
#pragma once
namespace my_stl
{
template<class T>
class shared_ptr
{
private:
int* _count;
T* _ptr;
public:
shared_ptr()
:_count(new int(0)),
_ptr(nullptr)
{}
shared_ptr(T* ptr)
:_count(new int(1)),
_ptr(ptr)
{}
shared_ptr(const shared_ptr& sp)
:_count(sp._count),
_ptr(sp._ptr)
{
++(*_count);
}
shared_ptr& operator=(const shared_ptr& sp)
{
if (this != &sp)
{
this->~shared_ptr();
_ptr = sp._ptr;
_count = sp._count;
++(*sp._count);
}
return *this;
}
~shared_ptr()
{
if (--(*_count) == 0 && _ptr)
{
delete _ptr;
delete _count;
_ptr = _count = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
};
}
weak_ptr:解决循环引用问题
weak_ptr
是一种不控制对象生命周期的智能指针,它可以用来解决std::shared_ptr
循环引用导致内存泄漏的问题。但由于实际项目中不经常使用,在此基础篇不过多介绍。
5、智能指针的高级特性:
std::shared_ptr
的线程安全问题:引用计数的修改是线程安全的,但资源本身不是。- 循环引用问题:使用
weak_ptr
解决shared_ptr
的循环引用问题。 - 删除器:
shared_ptr
可以与删除器一起使用,以管理非堆对象。
6、C++11和Boost库中智能指针的关系:
C++的Boost库是一个广泛使用的C++库集合,它提供了对标准C++库的补充。Boost库由一群C++开发者创建,旨在为C++程序员提供高质量的、经过同行评审的库,这些库最终可能成为C++标准的一部分。(可以理解为boost是c++标准的beta测试版)
- C++98引入了
auto_ptr
。 - Boost库提供了
scoped_ptr
、shared_ptr
和weak_ptr
。 - C++11标准从Boost库中借鉴并引入了
unique_ptr
、shared_ptr
和weak_ptr
。
7、题目
(1)下面关于unique_ptr说法错误的是(B)
A.unique_ptr是C++11才正式提出的
B.unique_ptr可以管理一段连续空间
C.unique_ptr不能使用其拷贝构造函数
D.unique_ptr的对象之间不能相互赋值
解释:B错误,C++11中提供的智能指针都只能管理单个对象的资源,没有提供管理一段空间资源的智能指针.
(2)关于RAII下面说法错误的是(C)
A.RAII的实现方式就是在构造函数中将资源初始化,在析构函数中将资源清理掉
B.RAII方式管理资源,可以有效避免资源泄漏问题
C.所有智能指针都借助RAII的思想管理资源
D.RAII方式管理锁,有些场景下可以有效避免死锁问题
解释:C错误,weak_ptr不能单独管理资源,必须配合shared_ptr一块使用,解决shared_ptr中存在的循环引用问题