动机
帮助程序员自动释放申请的动态内存。智能指针也是模板类
shared_ptr
make_shared函数
一般通过次函数创建指向的对象,该函数还返回一个智能指针。注意参数一定要和该类型的某个构造函数一致。
这样定义的话我感觉以后可以告别new了。
shared_ptr<int> p3 = make_shared<int>(42);
shared_ptr<string> p4 = make_shared<string>(10, '9');
通过类型的调用析构函数销毁
1.当所指向对象的引用计数为0时,会自动调用指向对象的析构函数进行销毁,这是不是说还得会写析构函数。
2.局部对象离开作用域会递减引用计数
shared_ptr<Foo> factory(T arg)
{
return make_shared<Foo>(arg);
}
情况解析:离开作用域减1。
void use_factory(T arg)
{
shared_ptr<Foo> p = factory(arg);
} //p离开作用域,递减引用计数,然后为0就会调用指向对象的析构函数。
情况解析:返回给其他对象就加1
void use_factory(T arg)
{
shared_ptr<Foo> p = factory(arg);
return p;
} //p离开作用域,递减引用计数,然后为0就会调用指向对象的析构函数。
shared_ptr成员变量
当含有shared_ptr成员的对象销毁时,该成员变量会自动销毁,所以可以用默认析构函数销毁,不用再另外写。
直接管理内存大法
用法
直接管理内存是指自己用new分配内存,用delete释放内存。
缺陷
1.不能使用默认的拷贝,赋值,析构函数,因为默认的不会帮你分配和释放内存。
2.造成多次delete的问题。
new分配
1.分为默认初始化和值初始化。
int *p1 = new int; //默认初始化。未定义值
int *p2 = new int(); //值初始化。默认为0
注意:
对于内置基本类型,默认初始化和值初始化结果不同;对于有构造函数的类型,上面两者都会调用默认构造函数。
2.创建const对象
const int *p3 = new const int(1024); //
3.nothow选项
如果分配不了内存,常规会抛出异常。
int *p4 = new(nothrow) int(1024); //
delete释放
1.必须记着释放分配的动态内存。
Foo * factory(T arg)
{
return new Foo(arg); //分配内存了
}
错误的用函数写法
void useFactory(T arg)
{
Foo *p = factory(arg);
} //离开函数作用域不会自动释放的
正确的用函数写法
void useFactory(T arg)
{
Foo *p = factory(arg);
delete p; //手动释放
}
void useFactory(T arg)
{
Foo *p = factory(arg);
return p; //以后记着释放
}
2.置为nullptr
一般delete后的p为空悬指针,造成指向无效问题,最好指向nullptr。
int *p = new int(42);
auto q =p;
delete p;
p = nullptr; //q也无效,但是会忘记置为null,也可能再次delete q
shared_ptr和new
1.explicit的构造函数
构造方式变成如下:
shared_ptr<int> p1 = new int(42); //xxx
shared_ptr<int> p2(new int(42));
也不能向形参传递普通指针
void process(shared_ptr<int> p)
{
}
int *x(new int(1024));
process(x); //xxx,不支持构造一个临时对象
2. 通过旧式指针初始化另一个智能指针,俩智能指针独立 (重点)
shared_ptr<int> p(new int(42));
int *q = p.get();
{
shared_ptr<int>(q); //建立临时对象
} //离开作用域销毁临时变量,还会释放指向对象的内存
int foo = *p; //xxx,内存已被释放
智能指针和异常
1.智能指针确保即使发生异常也能释放内存
如果被f自己捕获,智能指针会释放吗?
void f()
{
shared_ptr<int> p1 (new int(42));
int *p2 = new int(42);
//抛出异常了且没被f捕获
delete p2; //到不了p2这句,然后就不会释放了
}
2.智能指针与哑类
哑类指没有析构函数的类,资源不会释放。
connection connect(destination *); //这里应该是不完全类啊,居然也能用
void disconnect(connection);
void f(destination &d)
{
connection c = connect(&d); //xxx,这里说不完全类了
}//函数退出不会调用析构释放资源
可以通过智能指针传个删除器
//删除器函数
void end_connection(connection *p)
{
disconnect(*p); //xxx,这里说不完全类了
}
void f1(destination &d)
{
connection c = connect(&d); //xxx,这里说不完全类了
shared_ptr<connection> p(&c, end_connection);
}
unique_ptr
特性
独占对象
构造方式
只能通过new绑定了
unique_ptr<double> p1;
unique_ptr<int> p2(new int(42));
无拷贝和赋值操作
由于独占,所以拷贝和赋值也没必要支持
unique_ptr<int> p2(new int(42));
unique_ptr<int> p3(p2); //xxx,直接报错,定义为delete
p3 = p2; //xxx,运行才报错,也是delete的
release和reset区别
u.release(); //只是切断联系,不会释放内存
u.reset(); //会释放内存
可以拷贝将要销毁的unique_ptr
此规则允许我们返回一个unique_ptr局部对象。
unique_ptr<int> clone(int p)
{
return unique_ptr<int>(new int(p));
}
weak_ptr
特性
拥有对象但不更改引用计数,也不负责释放对象
构造方式
通过shared_ptr构造,是它的小弟
auto p = make_shared<int>(42);
weak_ptr<int> wp(p);
检查指向对象是否有效
一般通过lock()函数取得shared_ptr,然后判断是否为空
if(shared_ptr<int> np = wp.lock())
{
}