目录
前言:面试的时候被问到智能指针了,答的很糟糕,这几天重新学习了智能指针。下面是我对c++智能指针的理解与总结,希望能帮到大家,同时提升自己。
一,什么是智能指针
在C++中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针(smart pointer)。
智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。
- c++中用的最多的是下面三种智能指针
C++11中提供了三种智能指针,使用这些智能指针时需要引用头文件
<memory>
std::shared_ptr
:共享的智能指针std::unique_ptr
:独占的智能指针std::weak_ptr
:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的。
- 下面对他们分别进行分析
二,共享的智能指针shared_ptr
- 首先了解一下基本概念,再看代码,会学的很快
1. shared_ptr的初始化
共享智能指针是指多个智能指针可以同时管理同一块有效的内存,共享智能指针shared_ptr 是一个模板类,如果要进行初始化有三种方式:通过构造函数、std::make_shared辅助函数以及reset方法。共享智能指针对象初始化完毕之后就指向了要管理的那块堆内存,如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数
use_count
2.获取原始指针
对应基础数据类型来说,通过操作智能指针和操作智能指针管理的内存效果是一样的,可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象,那么就需要取出原始内存的地址再操作,可以调用共享智能指针类提供的get()方法得到原始地址
3. 指定删除器
当智能指针管理的内存对应的引用计数变为0的时候,这块内存就会被智能指针析构掉了。另外,我们在初始化智能指针的时候也可以自己指定删除动作,这个删除操作对应的函数被称之为删除器,这个删除器函数本质是一个回调函数,我们只需要进行实现,其调用是由智能指针完成的。
- 上面三个基本知识点了解以后就可以轻松的阅读代码了(代码注释都非常的详细哦!)
#include <iostream>
using namespace std;
#include <string>
#include <memory>
class Test
{
public:
Test() : m_num(0)
{
cout << "construct Test..." << endl;
}
Test(int x) : m_num(0)
{
cout << "construct Test, x = " << x << endl;
}
Test(string str) : m_num(0)
{
cout << "construct Test, str = " << str << endl;
}
~Test()
{
cout << "destruct Test..." << endl;
}
void setValue(int v)
{
this->m_num = v;
}
void print()
{
cout << "m_num: " << this->m_num << endl;
}
private:
int m_num;
};
int main()
{
/*-------------------------- 一,初始化智能指针shared_ptr ------------------------------*/
//1.通过构造函数初始化
shared_ptr<int> ptr1(new int(3));
cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
//2.通过移动和拷贝构造函数初始化
shared_ptr<int> ptr2 = move(ptr1);
cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
shared_ptr<int> ptr3 = ptr2;
cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
//3.通过 std::make_shared初始化
shared_ptr<int> ptr4 = make_shared<int>(8);
shared_ptr<Test> ptr5 = make_shared<Test>(7);
shared_ptr<Test> ptr6 = make_shared<Test>("GOOD LUCKLY!");
//4.通过reset初始化
ptr6.reset(); //重置ptr6, ptr6的引用基数为0
cout << "ptr6管理的内存引用计数: " << ptr6.use_count() << endl;
ptr5.reset(new Test("hello"));
cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl;
cout << endl;
cout << endl;
/*-------------------------- 二,共享智能指针shared_ptr的使用 ------------------------------*/
//1.方法一
Test* t = ptr5.get();
t->setValue(1000);
t->print();
//2.方法二
ptr5->setValue(7777);
ptr5->print();
printf("\n\n");
/*------------------------------------ 三,指定删除器 -----------------------------------*/
//1.简单举例
shared_ptr<Test> ppp(new Test(100), [](Test* t) {
//释放内存
cout << "Test对象的内存被释放了......." << endl;
delete t;
});
printf("----------------------------------------------------------------------\n");
2.如果是数组类型的地址,就需要自己写指定删除器,否则内存无法全部释放
//shared_ptr<Test> p1(new Test[5], [](Test* t) {
// delete[]t;
// });
//3.也可以使用c++给我们提供的 默认删除器函数(函数模板)
shared_ptr<Test> p2(new Test[3], default_delete<Test[]>());
//4.c++11以后可以这样写 也可以自动释放内存
shared_ptr<Test[]> p3(new Test[3]);
return 0;
}
另外,我们还可以自己封装一个函数模板make_shared_array方法来让shared_ptr支持数组,代码如下:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
//有了这个函数模板,我们就不用自己去释放数组类型的地址了
template <typename T>
shared_ptr<T> make_share_array(size_t size)
{
//返回匿名对象
return shared_ptr<T>(new T[size], default_delete<T[]>());
}
int main()
{
shared_ptr<int> ptr1 = make_share_array<int>(10);
cout << ptr1.use_count() << endl;
shared_ptr<string> ptr2 = make_share_array<string>(7);
cout << ptr2.use_count() << endl;
return 0;
}
三,独占的智能指针unique_ptr
- 首先了解一下基本概念,再看代码,会学的很快
1. 初始化
std::unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。
2. 删除器
unique_ptr指定删除器和shared_ptr指定删除器是有区别的,unique_ptr指定删除器的时候需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器
#include <iostream>
using namespace std;
#include <string>
#include <memory>
#include <functional>
class Test
{
public:
Test() : m_num(0)
{
cout << "construct Test..." << endl;
}
Test(int x) : m_num(1)
{
cout << "construct Test, x = " << x << endl;
}
Test(string str) : m_num(2)
{
cout << "construct Test, str = " << str << endl;
}
~Test()
{
cout << "destruct Test..." << endl;
}
void setValue(int v)
{
this->m_num = v;
}
void print()
{
cout << "m_num: " << this->m_num << endl;
}
private:
int m_num;
};
int main()
{
/*-------------------------- 一,初始化智能指针unique_ptr ------------------------------*/
//1.通过构造函数初始化
unique_ptr<int> ptr1(new int(3));
//2.通过移动函数初始化
unique_ptr<int> ptr2 = move(ptr1);
//.通过reset初始化
ptr2.reset(new int(7));
/*-------------------------- 二,unique_ptr的使用 ------------------------------*/
//1.方法一
unique_ptr<Test> ptr3(new Test(666));
Test* pt = ptr3.get();
pt->setValue(6);
pt->print();
//2.方法二
ptr3->setValue(777);
ptr3->print();
/*------------------------------------ 三,指定删除器 -----------------------------------*/
//1.函数指针类型
//using ptrFunc = void(*)(Test*);
//unique_ptr<Test, ptrFunc> ptr4(new Test("hello"), [](Test* t) {
// cout << "-----------------------" << endl;
// delete t;
// });
//2.仿函数类型(利用可调用对象包装器)
unique_ptr<Test, function<void(Test*)>> ptr4(new Test("hello"), [](Test* t) {
cout << "-----------------------" << endl;
delete t;
});
/*---------- 四,独占(共享)的智能指针可以管理数组类型的地址,能够自动释放 ---------*/
unique_ptr<Test[]> ptr5(new Test[3]);
//在c++11中shared_ptr不支持下面的写法,c++11以后才支持的
shared_ptr<Test[]> ptr6(new Test[3]);
return 0;
}
四, 弱引用的智能指针weak_ptr
弱引用智能指针
std::weak_ptr
可以看做是shared_ptr
的助手,它不管理shared_ptr
内部的指针。std::weak_ptr
没有重载操作符*
和->
,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视shared_ptr
中管理的资源是否存在。
1 初始化
#include <iostream>
#include <memory>
using namespace std;
int main()
{
shared_ptr<int> sp(new int);
weak_ptr<int> wp1;
weak_ptr<int> wp2(wp1);
weak_ptr<int> wp3(sp);
weak_ptr<int> wp4;
wp4 = sp;
weak_ptr<int> wp5;
wp5 = wp3;
return 0;
}
weak_ptr<int> wp1;
构造了一个空weak_ptr
对象weak_ptr<int> wp2(wp1);
通过一个空weak_ptr
对象构造了另一个空weak_ptr
对象weak_ptr<int> wp3(sp);
通过一个shared_ptr
对象构造了一个可用的weak_ptr
实例对象wp4 = sp;
通过一个shared_ptr
对象构造了一个可用的weak_ptr
实例对象(这是一个隐式类型转换)wp5 = wp3;
通过一个weak_ptr
对象构造了一个可用的weak_ptr
实例对象
- 通过调用
std::weak_ptr
类提供的use_count()
方法可以获得当前所观测资源的引用计数
2.常用函数
- 通过调用
std::weak_ptr
类提供的expired()
方法来判断观测的资源是否已经被释放- 通过调用
std::weak_ptr
类提供的lock()
方法来获取管理所监测资源的shared_ptr
对象- 通过调用
std::weak_ptr
类提供的reset()
方法来清空对象,使其不监测任何资源
- 利用
weak_ptr可以解决shared_ptr的一些问题
- 返回管理this的shared_ptr
- 解决循环引用问题