c++智能指针学习和遇到的问题

零、总览

|类型|策略|能否用于数组|备注|
|auto_ptr| 所有权模型| 否| -
unique_ptr 所有权模型 能 策略比auto_ptr更严格
shared_ptr 引用计数模型 否 -

一、auto_ptr的使用以及问题

auto_ptr是一个控制权转换的指针,两个auto_ptr赋值赋值的话,会释放赋值运算符右侧变量的所有权。c++11 之后不推荐使用,问题较多。
比如下面的代码:

void testAuto_ptr()
{
	int *p = new int(10);
	auto_ptr<int> ap(p);
	auto_ptr<int> ap2 = ap;
	cout << *ap << endl;
}

运行之后会直接崩溃,因为在auto_ptr ap2 = ap这句话结束之后,ap这个指针已经没有p所指向的内存的权限了。

二、unique_ptr和auto_ptr对比

unique_ptr也是所有权模型,但是他比auto_ptr要严格得多,不允许使用拷贝构造和赋值运算符,所以在一定程度生能避免auto_ptr的问题
和第一部分相似的代码,如果改成unique_ptr,会直接报错
在这里插入图片描述

三、unique_ptr的问题

虽然不能直接进行拷贝构造和赋值运算,但是如果两个unique_ptr都指向一个内存块的话,释放会释放两次,程序结束会直接崩溃,比如下面的代码:

void testUnique_ptr()
{
	int *p = new int(10);
	unique_ptr<int> up(p);
	unique_ptr<int> up2(p);
	cout << *up << endl;
}

运行结果:
在这里插入图片描述
原因是虽然unique_ptr都独享p指针所指向的内存区域,但是函数结束自动释放的时候,会重复释放,这种情况下unique_ptr就有问题了。

四、shared_ptr的好处

shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

  • 初始化。智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如std::shared_ptr p4 = new int(1);的写法是错误的
  • 拷贝和赋值。拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象。
  • get函数获取原始指针
  • 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存
  • 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用在weak_ptr中介绍。
void testShared_ptr()
{
	shared_ptr<int> up = make_shared<int>(10);
	{
		shared_ptr<int> up2 = make_shared<int>(20);
		cout << up.use_count() << endl;
		cout << up2.use_count() << endl;
		up2 = up;
		cout << up.use_count() << endl; //输出2,两个shared_ptr指针指向同一块内存区域
		cout << up2.use_count() << endl; //输出2,两个shared_ptr指针指向同一块内存区域
	}
	cout << up.use_count() << endl; // 输出1,因为up2生命周期结束后释放掉了,这时候up的引用计数-1
	cout << *up << endl;

}

输出:![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419185040267.png

再举一个经常遇到的情况:容器中存储指针,但是只释放了容器而没有释放容器内的指针。
如果不使用智能指针的话,比如说vector中存储的是指针类型:

#include <stdio.h>
#include <vector>
#include <iostream>
#include <memory>
using namespace std;
class myClass
{
    public:
    int my_a;
    myClass(int a):my_a(a){}
    ~myClass() {
        cout << "~myClass" << endl;
    }
};

void test()
{
    myClass *p = new myClass(10);
    vector<myClass*>vtp;
    vtp.push_back(p);
    myClass *q = p;
    cout << "p:" << p << " q:" << q << endl;
    cout << "*p:" << p->my_a << " *q:" << (*q).my_a << endl;
    // for (auto it = vtp.begin(); it != vtp.end(); ++it) {
    //     delete *it;
    // }
    // cout << "*p:" << p->my_a << " *q:" << (*q).my_a << endl;
}

void test_shareptr()
{
    shared_ptr<myClass> p = make_shared<myClass>(10);
    vector<shared_ptr<myClass> >vtp;
    vtp.push_back(p);
    shared_ptr<myClass> q = p;
    cout << "p:" << p << " q:" << q << endl;
    cout << "*p:" << p->my_a << " *q:" << (*q).my_a << endl;
}

int main()
{
    test();
    cout << "---------" << endl;
    test_shareptr();
    return 0;
}

输出结果:
在这里插入图片描述
可以发现普通的指针在函数结束之后也没有释放内存,但是智能指针自己就释放了,所以推荐都使用智能指针

五、指针智能使用注意事项

智能指针不要和普通指针同时使用,当代码量较大的时候,有可能有的地方会使用普通指针并下意识的使用完delete了,这种情况下shared_ptr在生命周期结束后会再次释放对应内存导致崩溃

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章