智能指针
#《C++11新特兴—C++标准库的更新》
特性1:智能指针
解决问题:C++中没有垃圾回收机制,必须自己释放内存,否则就会造成内存泄漏。因此采用智能指针(smart pointer)来解决该问题。动态指针是一个存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁分配的对象,防止内存泄露。该技术的核心在于引用计数,每使用它一次,内部引用技术加1,每析构一次内部的引用计数减一,减到0时,删除所指向的堆内存。
类型
C++11中引入了三种智能指针,使用时需要包含#include <memory>
头文件。
std::shared_ptr
:==共享==的智能指针。可以由多个指针去管理一个内存,每增加引用一个计数器加1,减少一个引用计数器减1.当计数器减为0时需要删除该指针所指向的堆内存。std:unique_ptr
:==独占==的智能指针。只有一个指针指示,如果需要新的指针指示需要move
操作。std:weak_ptr
:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr
的。
使用方法
1.std::shared_ptr
初始化——可以采用四种初始化方法
通过==构造函数==初始化
std::shared_ptr<T> 智能指针名字(创建堆内存);
1
shared_ptr<int> ptr1(new int(10)); // 对ptr1采用构造函数初始化
使用==拷贝==和==移动==构造函数初始化
1
2
3
4
5
6
7
8
9shared_ptr<int> ptr1(new int(10)); // 对ptr1采用构造函数初始化
// 拷贝构造函数
shared_ptr<int> ptr2(ptr1); // 方式1
shared_ptr<int> ptr3=ptr1; // 方式2
// 移动构造函数
shared_ptr<int> ptr2(std::move(ptr1)); // 方式1
shared_ptr<int> ptr3=std::move(ptr1); // 方式2C++中提供
std::make_shared()
可以完成内存对象的创建并将其初始化给智能指针1
2template<class T, class... Args>
shared_ptr<T> make_shared(Args&& ... args);T
:模板参数的数据类型Args&& ... args
:要初始化的数据,如果是通过make_shared
创建对象,需按照构造函数的参数列表指定。
1
2
3
4
5
6
7
8
9
10
11
12
13class Test{
public:
Test(){}
Test(int a){}
Test(string str){}
~Test(){}
};
int main(){
shared_ptr<Test> ptr=make_shared<Test>("dsfds"); // 有参构造函数
shared_ptr<Test> ptr=make_shared<Test>(); // 默认构造函数
return 0;
}通过
reset
方法初始化。1
2
3
4
5
6// reset 两个功能:重置指针 让该指针重新指向一块内存
shared_ptr<Test> ptr=make_shread<Test>();
// 重置
ptr.reset(); // ptr指向的内存的引用计数为0
// 重新指向一块内存
ptr.reset(new Test()); // 注意这里ptr重新指向内存的内省必须和重置之前一样,都是Test
使用方法
获取到原始地址,再对内存进行操作
1
2Test* p=ptr.get();
t->Get();直接使用智能指针进行操作
1
2
3// 将指针指针想象成一个指针
shared_ptr<Test> ptr=make_shared(new Test(10)); // ptr是一个对象
ptr->setValue(5); // 将其想象成一个指针进行操作指定删除器,当内存的引用计数为$0$时,这块内存就会被智能指针析构掉。此外我们也可以在初始化时指定一个删除动作,该删除操作对应的函数称为删除器。删除器函数本质是一个回调函数,我们只需要进行实现,==调用由智能指针完成==。
1
2
3
4
5
6
7void deleteIntPtr(int *p){
delete p;
cout<<<"point p was releaed"<<endl;
}
shared_ptr<int> ptr(new int(23),deleteIntPtr);
注1:一般系统重写删除器主要是用于数据指针,因为==不指定数组类型指针时,只会删除一个对象==。再删除数组时除了自己写删除器,还可以使用系统中自带
str::default_delete<T>()
函数作为删除器。 注2:在程序中多次需要对数组型指针释放时可以对申请智能时进行封装。
1
2
3
4template<Typename T>
shared_ptr<T> make_share_array(size_t size){
return shared_ptr<T>(new T[size],defalut_delete<T[]>()); //采用默认的删除器加上[]告诉系统是一个数组
} 注3:删除器函数也可以是
lambda
表达式。1
shared_ptr<int> ptr(new int(23),[](int*p){delete p;});
注意事项:
不能使用一个原始地址初始化多个共享智能指针
1
2
3
4int * p=new int(10);
shared_ptr<int> ptr1(p);
shared_ptr<int> ptr2(p);
// 此时会出现析构两次,因为每个智能指针中的计数都为1函数不能返回管理
this
的共享智能指针对象,可能会造成多次析构。共享智能指针不能循环引用,否则不能进行释放,因为计数器始终不会为0.
**2.std::unique_ptr
**:和shared_ptr
的主要区别在于==不允许其他的智能指针共享其内部的指针==,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr
复制给另一个unique_pte
。
初始化——可以采用四种初始化方法
通过构造函数初始化。
1
2unique<int> ptr1(new int(10)); // 构造函数初始化
unique<int> ptr2=ptr1; // 直接赋值错误通过
std::move
来m_STRAPs给其他std::unique_ptr
,但是原始指针的所有权就被转移了,这个原始指针还是被独占的。1
2
3
4
5
6
7
8
9
10
11unique_ptr<int> func()
{
return unique_ptr<int> (new int(520));
}
int main(){
unique<int> ptr1(new int(10));
unique<int> ptr2=move(ptr1); // 采用move
unique<int> ptr3=func(); // 将亡值
return 0;
}通过**
reset
方法**,使用该方法可以让unique_pte
解除对原始内存的管理,也可以用来初始化一个独占的智能指针。1
2
3
4
5
6
7int main(){
unique<int> ptr1(new int(10));
unique<int> ptr2=move(ptr1); // 采用move
ptr1.reset(); // 解除对原始内存的管理
ptr2.reset(new int(732)); // 重新指定智能指针管理的原始内存
return 0;
}使用
make_unique()
函数进行初始化。
使用方法——获取智能指针管理的原始地址
使用
get()
方法,函数原型如下:1
2
3
4
5
6
7poniter get() const noexcept;
// 具体使用
int main(){
unique<int> ptr1(new int(10));
cout<<*ptr1.get(); //10
return 0;
}采用指针直接使用。
指定删除器
- 在初始时指定删除器有所区别
1 | using ptrFunc =void(*)(Test*); |
==独占==的智能指针可以管理数组类型的地址,==能够自动释放==。但是==C++11中共享型不可以自动释放数组,在C++11之后才可以支持这种写法==。
1
2unique_ptr<Test[]> ptr(new Test[3]); // right 数组型释放
shared_ptr<Test[]> ptr(new Test[3]); // c++11之后才支持该写法
Tips: lambda
表达式?仿函数?可调用对象包装器?
3.std::weak_ptr
主要功能:用于辅助shared_ptr
的工作。采用shared_ptr
时会存在以下问题:
1、在一些特殊的对象下会将析构函数调用两次。
1 | struct Test |
2、可能会产生循环调用,无法进行释放内存。因此weak_ptr
用于将下面的结构变为链式结构。
.png)
解决方法:让其中一个shared_ptr
变为weak_ptr