智能管理动态分配的内存,自动释放程序员new出来的内存,从而避免内存泄漏。
动态分配的内存交给有生命周期的对象处理,在对象过期时,内存的释放交给对象来处理。
#include
auto_ptr<类型> 变量名称(new 类型);
auto_ptr str(new string("string型智能指针测试"));
auto_ptr> v(new vecotr);
auto_ptr array(new int[10]);
智能指针内部实现了*,->运算符重载,可以像普通指针一样使用。
1) get()函数:获取指针返回
auto_ptr test(new Test);
Test *tmp = test.get(); // 获取指针返回
2) release()函数:取消智能指针对动态内存的托管,会将内存的地址返回
auto_ptr test(new Test);
Test *tmp = test.release(); // 取消智能指针对动态内存的托管
delete tmp; // 手动释放
3) reset()函数:释放智能指针托管的指针内存
autp_ptr test(new Test);
test.reset(); // 释放智能指针托管的指针内存,将其置为NULL
test.reset(new Test()); // 释放智能指针托管的指针内存,并指向形式参数中的内存地址
存在的问题:
1.复制或者赋值都会改变资源的所有权
2.在STL容器中使用auto_ptr存在风险,因为容器中元素必须可复制和可赋值
3.不支持对象数组管理
1)复制和赋值的资源所有权问题
// auto_ptr 被C++11抛弃的主要原因
auto_ptr p1(new string("I'm Li Ming!"));
auto_ptr p2(new string("I'm age 22."));
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;
/*
TODO p2赋值给p1后,首先p1会先将自己原先托管的内存释放掉,然后接收托管p2所托管的内存,
然后p2所托管的指针为NULL,也就是p1托管了p2托管的内存,而p2放弃了托管。
*/
p1 = p2;
cout << "p1 = p2 赋值后:" << endl;
// 结构托管
cout << "p1:" << p1.get() << endl; // 输出p1托管的地址
cout << "p2:" << p2.get() << endl;
// 输出为null空地址
cout << *p2.get() << endl; // 出错——空地址取值运算
2)STL容器中使用auto_ptr的风险,首先需要对auto_ptr进行move修饰
vector> vec;
auto_ptr p3(new string("I'm P3"));
auto_ptr p4(new string("I'm P4"));
// 必须使用std::move修饰成右值,才可以进行插入到容器中
vec.push_back(std::move(p3));
vec.push_back(std::move(p4));
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;
// 出错
// 在这里进行赋值,赋值成功后,vec[1]智能指针将被释放,输出将会出错
vec[0] = vec[1]; // 如果进行赋值,问题又回到了上面一个问题中。
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;
3)不支持对象数组管理
auto_ptr array(new int[5]); // 出错
1. unique_ptr独占对象所有权,没有使用引用计数,整体性能良好
2. shared_ptr共享对象的所有权,使用引用计数,性能较差
3. weak_ptr配合shared_ptr,解决循环引用问题
unique_ptr指针替换了auto_ptr智能指针,特性:
1.基于排他所有权模式,两个指针不能指向同一个资源。
2.无法进行左值unique_ptr赋值构造,也无法进行左值复制赋值操作,但允许临时右赋值和构造赋值。
3.在容器中保存指针是安全的
4.unique_ptr可以指向一个数组
5.unique_ptr需要指定删除器的类型
shared_ptr特性
使用引用计数,每一个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构时,被托管的内存才会释放。
shared_ptr共享管理的对象,同一时刻可以有多个shared_ptr拥有对象的所有权。
shared_ptr 的小问题:当有两个对象中成员变量都使用了一个shared_ptr指针,并将shared_ptr指针都指向了对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。
/*
1. 不能使用原始指针初始化多个shared_ptr
2. 函数实参中不要创建shared_ptr指针 // 因为函数参数的计算顺序不一定每次相同,先定义后使用
3. 不要通过shared_from_this()返回this指针 // 可能导致重复析构
*/
int *ptr = new int;
shared_ptr p1(ptr);
shared_ptr p2(ptr); // 出错
/// 可能顺序:先new一个int,然后去调用g()函数,shared_ptr指针则不能实现对int的托管,造成了内存泄露
func(shared_ptr(new int), g()); // 可能会出问题
weak_ptr特性
1. waek_ptr 设计的目的是为了协助shared_ptr工作
2. weak_ptr 可以从一个shared_ptr或weak_ptr对象构造
3. weak_ptr 构造和析构不会引起计数的增加或减少
// use_count()函数引用计数问题
shared_ptr sp(new int(10));
weak_ptr wp(sp); // 定义weak_ptr,并用sp进行初始化
cout << wp.use_count() << endl; // 输出结果为 1
// expired()指针有效性问题
shared_ptr sp(new int(10));
weak_ptr wp(sp);
if (wp.expired()) {
cout << "weak_ptr无效,内存已经释放" << endl;
}
else {
cout << "weak_ptr有效,内存未释放" << endl;
}
// lock()获取托管内存
auto sp = make_shared(42);
weak_ptr wp = sp;
auto spt = wp.lock(); // 获取shared_ptr
if (wp.expired()) {
cout << "wp有效" << *spt << endl; // 输出内容
}
weak_ptr解决shared_ptr中的循环引用问题
shared_ptr在两个对象中的同一成员变量同时使用了一个shared_ptr,并将shared_ptr指向对方时,会造成循环引用,使引用计数失效,从而造成内存泄漏。
只需要将两个对象中的其中一个使用的shared_ptr换成weak_ptr就可解决问题
#include
#include
using namespace std;
class A;
class B;
class A {
public:
std::weak_ptr bptr; // 修改为weak_ptr
~A() {
cout << "A is deleted" << endl;
}
};
class B {
public:
std::shared_ptr aptr;
~B() {
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr ap(new A);
std::shared_ptr bp(new B);
ap->bptr = bp;
bp->aptr = ap;
}
cout<< "main leave" << endl;
return 0;
}