new,delete是关键字(运算符),不是函数,new和delete会调用构造函数和析构函数
new做了两件事,1、operator new 分配内存 2、调用构造函数来初始内存
delete做了两件事 1、先调用析构函数 2、释放内存operator delete
class A{};
// 我们认为new了一个对象为2的数组应该占用2个字节,但是实际占用了6个字节
// 2个字节表示类对象数组占用的内存,4个字节是用来记录数组的个数
A* a = new A[2];
delete[] a;
// delete a; // 会出现异常
// 内置类型,使用delete和delete[]效果一样,不需要调用析构函数所以没有额外空间
智能指针能自动释放指向的内存
auto_ptr (c++98)
unique_ptr (c++11)
shared_ptr (c++11)
weak_ptr (c++11)
目前auto_ptr已经被unique_ptr取代,因为auto_ptr不安全
这三种指针都是使用类模板
1、shared_ptr 共享指针(多个指针指向同一块对象,最后一个指针被销毁时,这个对象会被释放)
2、weak_ptr 是辅助 shared_ptr工作
3、unique_ptr 独占式指针,同一个时间,只有一个指针指向该对象
共享所有权,不是被一个shared_ptr拥有,而是被多个shared_ptr直接相互协助,有额外开销。
工作原理:引用计数,每个shared_ptr的拷贝都是相同的内存,只有最后一个指向
该内存(对象) 的shared_ptr指针不需要再指向该对象时,才会析构所指向的内存。
1、释放时机
a)这个shared_ptr 被析构的时候
b)这个shared_ptr 指向别的对象时
格式:
shared_ptr<指向类型名> 智能指针名
shared_ptr<int> p1(new int(100));
2 、使用make_shared函数
make_shared_ptr函数:标准库里的函数模板,安全,高效的分配和使用,shared_ptr,它能够在动态分配内存中分配并初始化一个对象,然后返回shared_ptr
shared_ptr<int> p1 = make_shared_ptr<int>(100);
3、在如下情况下,所引用的智能指针计数会增加
auto p1 = make_shared(100);
auto p2(p1);
1、这种情况用p1初始化和p2
2、把智能指针做实参传递 (如果函数形参使用的是引用,则智能指针引用计数不会增加)
3、作为函数返回值
4、引用计数减少
给shared_ptr赋予新值,让他指向新的对象,会造成引用计数减少,减少到0,就会释放这个指针
use_count() 返回多少个智能指针指向某个对象
示例:
shared_ptr<int> my(new int(100);
int count = my.use_count();
unique():是否该智能指针独自某个对象,如果是返回true,否则返回false
shared_ptr<int> my(new int(100);
my.unique();
若pi是指向该对象的唯一指针,那么会释放该指针,并将其置为空
若pi不是指向该对象的唯一指针,指针计数会减-1,然后pi会被置空
// 情况1:
shared_ptr<int> pi(new int(100);
pi.reset(); // pi置为空,并且释放该内存
if(pi==nullptr)
cout<<"pi为空"<<endl;
// 情况2:
shared_ptr<int> pi(new int(100);
auto pi2(pi);
pi.reset(); // pi为空,引用计数-1
reset() 带参数一般是new出来的指针时
若pi是指向该对象的唯一指针,那么会释放该指针,并将pi指向该指针
若pi不是指向该对象的唯一指针,指针计数会减-1,然后pi指向该指针
// 情况1:
shared_ptr<int> pi(new int(100);
pi.reset(new int(1)); // pi指向新的指针,释放原内存
// 情况2:
shared_ptr<int> pi(new int(100);
auto pi2(pi);
pi.reset(new int(1)); // 引用计数-1,pi指向新的指针
shared_ptr<int> pi;
pi.reset(new int(10));
*获得pi指向的对象
shared_ptr<int> pi(new int(100);
cout<<*pi<<endl; // 输出结果100
考虑到一些函数需要时裸指针,所以由此而生
pi.get返回pi中保存的裸指针,不小心释放了,会造成严重后果
shared_ptr<int> pi(new int(100);
int* p = pi.get();
swap交换两个智能指针的指向
shared_ptr<string> pi(new string("123"));
shared_ptr<string> pi1(new string("1234"));
pi.swap(pi1);
a)所指向的对象,引用计数减1,若引用计数为0,则释放
shared_ptr<string> pi(new string("123"));
p1 = nullptr;
shared_ptr<string> pi(new string("123"));
if(pi)
{
cout<<"指向一个对象"<<endl;
}
else
{
cout<<"指向null"<<endl;
}
a)指定删除器
1、定时机帮我们删除所指向的对象,delete:将delete运算符作为默认的析构方式,我们可以指定自己的删除器,可以取代系统的默认删除器。
void myDelete(int* p) // 我们的删除器,当智能指针计数为零,默认调用这个函数
{
if(p!=NULL)
{
delete p;
p = NULL;
}
}
// 指定删除器
shared_ptr<int> p(new int(113),myDelete));
// 删除器可以不是函数,可以时lambda表达式
shared_ptr<int> p(new int(113),[](int* p)
{
delete p;
});
默认删除器有些情况处理不了,比如动态数组(因为默认的时delete p,不是delete[] p)
shared_ptr<int> p(new int[10],[](int* p)
{
delete[] p;
});
// 可以用default_delete来做删除器,是标准的模板类
shared_ptr<int> p(new int(113),std::default_delete<int[]>[]);
写个函数模板来封装shared_ptr数组
template<typedef T>
shared_ptr<T> make_shared_array(size_t size)
{
return shared_ptr<T>(new T[size],default_ptr<T[]>());
}
// 调用
shared_ptr<int> p = make_shared_array<int>(5);
删除器额外说明
两个shared_ptr指定不同的删除器,只要他们所指向的对象类型相同,那么这两个shared_ptr也属于同一个类型
auto lambda1 = [](int* p)
{
// 日志
delete p;
}
auto lambda2 = [](int* p)
{
// 日志
delete p;
}
shared_ptr<int> p(new int(100),lambda1);
shared_ptr<int> p1(new int(200),lambda2);
p1 = p; // 先释放p1所指向的对象释放掉,然后p所指向的引用计数+1
shared_ptr<int> func(int value)
{
return make_shared<int>(value);
}
shared_ptr<int> func2()
{
auto tmp = func(10);
return tmp;
}
auto pi = func2();
智能指针注意事项:
1、慎用裸指针
不要使用裸指针初始化多个shared_ptr,会造成多次释放
void func(shared_ptr<int> p)
{
}
int* p = new p(10);
func(shared_ptr<int>(p)); //参数是一个临时的shared_ptr,用一个裸指针显示构建
*p = 45; // 会出现意外错误
// p指针已经在func函数内部释放了,因为引用计数是1,func函数的p是一个临时变量,结束时候会释放
// 改善写法1
int* p = new p(10);
shared_ptr<int> p2(p);
func(p2);
*p = 11;
// 改善写法2
shared_ptr<int> p2(new int(100));
func(p2);
*p = 21;
2、慎用get返回的裸指针
因为一些函数接口需要的是传入一个裸指针,所以就引入了get这个函数接口,get返回的指针不能delete,会导致异常,他是智能指针进行管理的。
3、不要把类对象this指针作为shared_ptr的返回值,改用enable_shared_from_this
class A:public enable_shared_from_this<A>
{
public:
shared_ptr<A> getself()
{
//return shared_ptr(this); // 用裸指针初始化多个shared_ptr
return shared_ptr_this(); // 通过这种 方法返回智能指针
}
};
shared_ptr<A> pctl(new A);
shared_ptr<A> pct2 = pctl->getself(); // 问题出现
// 导致pctl和pct2两个智能指针没有关联
// enable_shared_from_this中有一个弱指针weak_ptr,能够监视this,通过调用了lock函数,
使强引用计数+1,并且返回shared_ptr指针
4、避免循环引用
weak_ptr是用来辅助shared_ptr使用,用来指向一个shared_ptr管理的对象,但是weak_ptr不能改变引用shared_ptr引用计数,控制不了,所指向对象的生存期,也叫弱引用,监视shared_ptr的生命周期,某种意义上说是对shared_ptr的扩充,
创建weak_ptr的时候,一般使用shared_ptr来初始化,因为weak_ptr是需要依赖shared_ptr
auto pi = shared_ptr<int>(100);
weak_ptr<int> piw(pi); // 弱共享,pi引用计数不改变
检查weak_ptr所指向的对象是否存在,如果存在则返回shared_ptr指针,强引用计数也会加1,如果不存在,lock返回一个空shared_ptr
auto pi = shared_ptr<int>(100);
weak_ptr<int> piw(pi); // 弱共享,pi引用计数不改变
auto pi2 = piw.lock(); // pi2是一个shared_ptr指针
use_count()函数得到的是强引用的数量
auto pi = shared_ptr<int>(100);
weak_ptr<int> piw(pi); // 弱共享,pi引用计数不改变
piw.use_count();
expired()是否过期,判断所观察的资源是否已经过期
auto pi = shared_ptr<int>(100);
weak_ptr<int> piw(pi); // 弱共享,pi引用计数不改变
pi.reset();
if(piw.expired())
{
cout<<"对象过期"<<endl;
}
else
{
cout<<"未过期"<<endl;
}
reset()将弱引用指针设置为空,不影响强引用指针数量,弱引用计数会减少1
auto pi = shared_ptr<int>(100);
weak_ptr<int> piw(pi); // 弱共享,pi引用计数不改变
piw.reset();
auto pi = weak_ptr<int>(100);
int* p;
int i1 = sizeof(pi); // 8,两个裸指针
int i2 = sizeof(p); // 4
// 第一个指针指向,智能指针指向的对象
// 第二个指针指向,控制块

独占式的智能指针,同一时刻只能有一个unique_ptr指向这个对象(这块内存)
// 初始化
unique<int> pi(new int(100));
// c++14引入了 make_unique
auto p2 = make_unique<int>(100);
1、release函数,放弃对指针的控制器(切断了和智能指针的联系,返回裸指针,将智能指针置空,裸指针可以delete手动释放,也可以来初始化其他智能指针)
unique<int> pi(new int(100));
unique<int> p2(pi.release()); // 利用pi返回的裸指针来初始化p2智能指针
2、reset不带参数,释放智能指针(并且置空)
带参数,释放智能指针指向的对象,并让该指针指针指向新对象
unique<int> pi(new int(100));
pi.reset();
// 带参数
pi.reset(pi.release());
3、nullptr
unique<int> pi(new int(100));
pi = nullptr; // 释放pi指向的对象,并将他指向为空
4、指向数组
不要忘记int[],否则释放就会出问题
unique<int[]> pi(new int[10]);
5、get
返回智能指针的裸指针
unique<int> pi(new int(100));
int* p = pi.get();
6、*解引用
获取智能指针指向的对象,可以指针操作
unique<int> pi(new int(100));
*pi = 13;
7、swap
交换两个智能指针所指向的对象
unique<int> p1(new int(1001));
unique<int> p2(new int(100));
p1.swap(p2);
8、转换shared_ptr
如果unique_ptr为右值的时候就可以赋值给shared_ptr,因为shared_ptr显示构造函数,可将右值unique_ptr转换为shared_ptr,从此接管原对象
auto func()
{
return unique_ptr<int>(new int(100));
}
shared_ptr<int> pi = func();
9、从函数返回一个局部unique_ptr指针
unique_ptr<int> func()
{
return unique_ptr<int>(new int(100));
}
// 返回局部对象,这种情况会生产一个临时unique_ptr,调用unique_ptr移动构造函数
auto pi = func();
10、指定删除器
void mydelete(int* p)
{
if(p!=NULL)
{
delete p;
p = NULL;
}
}
unique<int> pi(new int(100),mydelete);
11 、尺寸问题
unique<int> pi(new int(100));
int *p;
int r1 = sizeof(pi);
int r2 = sizeof(p);
// unique_ptr和裸指针一样都是4字节
帮助我们释放内存,防止我们忘记释放,内存泄漏。
auto_ptr为什么被废弃:具有一部分unique_ptr,不能在容器中保存,也不能在函数中返回