智能管理动态分配的内存,自动释放程序员new出来的内存,从而避免内存泄漏。
动态分配的内存交给有生命周期的对象处理,在对象过期时,内存的释放交给对象来处理。
- #include
- auto_ptr<类型> 变量名称(new 类型);
-
- auto_ptr
str(new string("string型智能指针测试")) ; - auto_ptr
int>> v(new vecotr<int>); - auto_ptr<int> 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<int[]> 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<int> p1(ptr);
- shared_ptr<int> p2(ptr); // 出错
-
- /// 可能顺序:先new一个int,然后去调用g()函数,shared_ptr指针则不能实现对int的托管,造成了内存泄露
- func(shared_ptr<int>(new int), g()); // 可能会出问题
weak_ptr特性
1. waek_ptr 设计的目的是为了协助shared_ptr工作
2. weak_ptr 可以从一个shared_ptr或weak_ptr对象构造
3. weak_ptr 构造和析构不会引起计数的增加或减少
- // use_count()函数引用计数问题
- shared_ptr<int> sp(new int(10));
- weak_ptr<int> wp(sp); // 定义weak_ptr,并用sp进行初始化
- cout << wp.use_count() << endl; // 输出结果为 1
-
- // expired()指针有效性问题
- shared_ptr<int> sp(new int(10));
- weak_ptr<int> wp(sp);
- if (wp.expired()) {
- cout << "weak_ptr无效,内存已经释放" << endl;
- }
- else {
- cout << "weak_ptr有效,内存未释放" << endl;
- }
-
- // lock()获取托管内存
- auto sp = make_shared<int>(42);
- weak_ptr<int> 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;
- }