常见的智能指针

智能指针其实是一个类模板,它与普通指针的区别在于他会自己释放内存空间。常见的智能指针有三种:unique_ptr、shared_ptr、weak_ptr。他们都在头文件<memory>中。

注:下面的普通指针就是裸指针。

目录

unique_ptr:

        unique_ptr的创建:

        unique_ptr的常用函数:

        unique_ptr可能犯的错:

shared_ptr:

        shared_ptr的创建:

        shared_ptr的常用函数:

        shared_ptr可能犯的错:

weak_ptr:

        weak_ptr的创建:

        weak_ptr的常用函数:


unique_ptr:

        unique_ptr的创建:

        unique_ptr是一种独占形的指针,即不能有两个独占性指针指向同一内存。它不允许复制,只允许移动(move),常用有三种创建方法,没有直接创建指针数组的,但可以自己自定义数据类型。

	//第一种
	unique_ptr<int> ptr1(new int(2));   //new一个内存空间给他
	cout << ptr1 << "  " << *ptr1 << endl;
	//第二种
	//unique_ptr<int> ptr2(ptr1);  //报错,不可直接拷贝(浅拷贝)
	unique_ptr<int> ptr2(move(ptr1));     //通过move将ptr1的内存给ptr1.
	cout << ptr2 << "  " << *ptr2 << endl;
	cout << ptr1 << endl;       //此时会发现其指向的地址已经被置空了
	//cout << *ptr1 << endl;   //不可再调用*ptr1,因为它指向的内存已经移动给ptr2了,会报异常。
	//第三种
	unique_ptr<int> ptr3;
	ptr3 = make_unique<int>(3);   //调用make_unique函数。
	cout << ptr3 << "  " << *ptr3 << endl;

    //第四种,创建一个指针数组
//自定义数据类型
	class shuzu {
	private:
		int* p;
		int size;
	public:
		shuzu() = default;          //显式构造构造函数
		shuzu(int k) {              //记得重写构造函数创建空间
			this->size = k;
			this->p = new int[k];
		}
		~shuzu() {                  //析构函数也要记得
			this->size = 0;
			delete[] p;
			p = nullptr;             //要置空,防止野指针的出现
		}
		int get_p() {
			if (p) { return 0; }
			else { return p[0]; }
		}
	};
//创建指针数组
	unique_ptr<shuzu> ptr4(new shuzu(4));
	cout << ptr4 << "  " << ptr4->get_p() << endl;

        unique_ptr的常用函数:

        unique_ptr有四个常用函数,一个是release,一个是reset,一个是get,还有一个swap。release可以将该指针置为空,然后返回一个普通指针指向该内存。reset有一个普通指针的参数,默认为空,会先销毁该指针指向的地址,再让该指针指向参数所指向地址。get会返回一个普通指针,可以让该指针与智能指针指向同一个地址。swap的参数是另一个unique_ptr,会交换两个指针所指向的地址。

	unique_ptr<int> ptr3;
	ptr3 = make_unique<int>(3);
	cout << "ptr3 = " << ptr3 << "  *ptr3 = " << *ptr3 << endl;
	unique_ptr<int> ptr1(new int(2));
	cout << "ptr1 = " << ptr1 << "  *ptr1 = " << *ptr1 << endl;

    int* p = ptr1.get();      //获取ptr1的地址
	cout << "p= " << p<< "  *p= " << *p<< endl;
	cout << "ptr1 = " << ptr1 << "  *ptr1 = " << *ptr1 << endl;

	ptr1.swap(ptr3);      //交换两指针指向地址
	cout << "ptr3 = " << ptr3 << "  *ptr3 = " << *ptr3 << endl;
	cout << "ptr1 = " << ptr1 << "  *ptr1 = " << *ptr1 << endl;

	int* ptr5 = ptr3.release();  //因为智能指针是unique_ptr<int>,所以返回一个int*的指针
	cout << "ptr5 = " << ptr5 << "  *ptr5 = " << *ptr5 << endl;

    //ptr3.release();    //将ptr3置空并释放内存

	ptr1.reset(ptr5);     //因为智能指针是unique_ptr<int>,所以参数是int*的指针
	cout << "ptr5 = " << ptr3 << endl;
	cout << "ptr1 = " << ptr1 << "  *ptr1 = " << *ptr1 << endl;

    ptr1.reset();     //释放ptr1指向的内存并置空

        unique_ptr可能犯的错:

我们都知道,unique_ptr是独占类型的指针,但如果我们通过一个指针给两个unique_ptr类型的指针赋初值是可以成功的,而且这两个指针还指向同一地址,这就会导致unique_ptr变为非独占型,间接导致unique_ptr的reset操作异常以及其他依赖于该指针独占性的操作出问题。所以不能做以下操作:

int main() {

	//成功创建指针并指向同一地址
	int* trap = new int(4);
	unique_ptr<int> ptr1(trap);
	unique_ptr<int> ptr2(trap);
	unique_ptr<int> ptr3(trap);
	cout << "trap的地址为:" << trap << endl;
	cout << "ptr1的地址为:" << ptr1 << endl;
	cout << "ptr2的地址为:" << ptr2 << endl;
	cout << "ptr3的地址为:" << ptr3 << endl;
	//调用函数
	ptr1.release();  //可以成功,但只是将ptr1这个指针置空
	//ptr2.reset();  //无法成功,因为该内存不只被ptr2所拥有

	return 0;  //在执行完return 0 后会报错,因为无法销毁非独占的独占型指针。
}

shared_ptr:

        shared_ptr的创建:

        shared_ptr是一种共享性的智能指针(类模板),它允许移动和复制,每复制一次后,它的计数就会加一,当计数为0时就会销毁。常用的创建方法有:①使用new创建(不推荐,因为使用new会导致重复了被创建对象的键入,导致编译次数增加,代码膨胀,最终难以维护。)②使用make_shared,用法同make_unique。③直接复制另一个shared_ptr,会让这两个指针指向同一块内存。④使用move将一个shared_ptr指向的内存移动到另一个shared_ptr,被移动的指针会被置空。

    //用法一,不推荐
	shared_ptr<int> ptr2(new int(10));
	cout << "ptr2的地址为:" << ptr2 << "  ptr2的值为:" << *ptr2 << endl;
	//用法二,使用make_shared,推荐
	shared_ptr<int> ptr1(make_shared<int>(10));
	cout << "ptr1的地址为:" << ptr1 << "  ptr1的值为:" << *ptr1 << endl;
	//直接复制
	shared_ptr<int> ptr3 = ptr1;
	cout << "ptr3的地址为:" << ptr3 << "  ptr3的值为:" << *ptr3 << endl;
	//将ptr1所指向的内存移动给ptr4
	shared_ptr<int> ptr4(move(ptr1));
	cout << "ptr4的地址为:" << ptr4 << "  ptr4的值为:" << *ptr4 << endl;
	//ptr1被移动,被置空了
	cout << "ptr1的地址为:" << ptr1 << endl;
	//ptr3不变
	cout << "ptr3的地址为:" << ptr3 << "  ptr3的值为:" << *ptr3 << endl;

        shared_ptr的常用函数:

        shared_ptr有几个常用函数:use_count、get、reset、unique、swap,其中use_count函数可以查看与该指针指向同一内存的智能指针数(也就是计数)。get同上,可以获取地址。reset也有两个使用方法,若没有参数,则会将该指针置空,然后计数减一,若计数为0,则销毁该内存;若存在参数,则参数是一个新开辟出的内存地址(不能是裸指针),先将计数减一,若计数为0,则销毁该内存,再将指针指向该地址。unique函数返回一个bool值,若该指针计数为1,则返回true,否则返回false。swap函数同unique_ptr的一样,都是交换两个指针指向的地址。

	shared_ptr<int> ptr1(make_shared<int>(10));
	cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:"<< ptr1.use_count() << "  ptr1的值为:" << *ptr1 << endl;
	//此时计数为1,unique返回true
	cout << ptr1.unique() << endl;
	shared_ptr<int> ptr3 = ptr1;
	cout << "ptr3的地址为:" << ptr3 << "ptr3的计数为:" << ptr3.use_count() << "  ptr3的值为:" << *ptr3 << endl;
	//此时计数为2,unique返回fasle
	cout << ptr1.unique() << endl;

	//使用get获取地址
	int* p = ptr1.get();

	//reset无参数,ptr1指向的地址会被置空,计数-1.
	ptr1.reset();
	cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:" << ptr1.use_count() << endl;
	//reset有参数,地址会指向新开辟出来的,值为4.
	ptr1.reset(new int(4));
	cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:" << ptr1.use_count() << "  ptr1的值为:" << *ptr1 << endl;

    //交换两指针
	ptr1.swap(ptr3);
	cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:" << ptr1.use_count() << ptr1 << "  ptr1的值为:" << *ptr1 << endl;
	cout << "ptr3的地址为:" << ptr3 << "ptr3的计数为:" << ptr3.use_count() << "  ptr3的值为:" << *ptr3 << endl;

        shared_ptr可能犯的错:

        一:同unique_ptr一样,shared_ptr也不能用一个裸指针创建两个shared_ptr,因为你在创建的时候机器会用同一个地址创建两个指针,他们因为刚被创建,计数都是一,等到删除的时候,比如删掉第一个,计数为0,机器以为这块内存无人使用了,想将他销毁,可第二个指针还在使用这块内存,造成错误的出现。

int main() {
	int* p = new int(5);

	shared_ptr<int> ptr1(p);
	shared_ptr<int> ptr2(p);
	cout << "ptr1的地址为:" << ptr1 << "  ptr1的计数为:" << ptr1.use_count() << "  ptr1的值为:" << *ptr1 << endl;
    //系统以为ptr2指向的内存刚被创建,所以ptr2的计数也是1,但实际上是2.
	cout << "ptr2的地址为:" << ptr2 << "  ptr1的计数为:" << ptr2.use_count() << "  ptr2的值为:" << *ptr2 << endl;

	return 0;  //在执行完return 0 后会报错,因为无法销毁指针。
}

        二:shared_ptr的循环引用问题,本质是因为使用的shared_ptr的参数类型是一个带有shared_ptr的成员函数,而且这两个指向同一地址。如下方的a与a->a_这两个shared_ptr指向同一地址,当程序结束时,a销毁,a的use_count减一变为1,所以a所指向的内存不销毁,而该内存不销毁,a->a_就不会销毁(不调用析构函数),其use_count不会为0,该内存一直存在。

而使用weak_ptr,并不会让a的计数加一,所以a销毁时,计数为0,a.a_也会被成功销毁。

class A
{
public:
    std::shared_ptr<A> a_;
    //std::weak_ptr<int> a;  //这个改成weak_ptr就没问题了
public:
    A() {
        std::cout << "construct A" << std::endl;
    }
    ~A() {
        std::cout << "destroy A" << std::endl;
    }
};


int main() {

    // *** circular reference *** ///
    std::shared_ptr<A> a(new A()); // a count: 1
    cout <<"a的地址为:" << a << " a的计数为:" << a.use_count() << endl;

    a->a_ = a; // b count: 2
    cout << "a->a_的地址为:" << a->a_ << " a->a_的计数为:" << a->a_.use_count() << endl;
    cout << "a的地址为:" << a << " a的计数为:" << a.use_count() << endl;

	return 0;  
}

weak_ptr:

        weak_ptr的创建:

        weak_ptr一般不单独使用,它并未重载诸如 *、->等指针操作符,而是作为shared_ptr的补充(监视)。它的初始化需要用到shared_ptr的指针,当使用一个shared_ptr初始化weak_ptr时,该shared_ptr的计数并不会加一!!!!!所以他可以用来解决上面shared_ptr可能出现的第二个问题。

	//创建
	weak_ptr<int> wptr1;
	//初始化
	shared_ptr<int> ptr1(new int(3));
	wptr1 = ptr1;
	cout << "ptr1的地址为:" << ptr1 << "  ptr1的计数为:" << ptr1.use_count() << endl;  //计数为1,不是2哦
	//可以通过复制创建
	weak_ptr<int> wptr2(wptr1);

        weak_ptr的常用函数:

        weak_ptr有几个常用函数:lock、reset、use_count、swap、expired。lock函数会返回weak_ptr所绑定的shared_ptr对象,不推荐用来初始化新的shared_ptr,若无绑定对象则返回NULL。reset会给weak_ptr“解绑”,即该指针指向空(不影响它所绑定的shared_ptr)。use_count可以用来获取它绑定对象的计数值。swap用来交换两个指针绑定的对象。expired会返回一个bool值,若weak_ptr为空或者其绑定的shared_ptr为空,则返回true(为空返回true哦)。

	weak_ptr<int> wptr1;
	shared_ptr<int> ptr1(new int(3));
	wptr1 = ptr1;
	cout << "ptr1的地址为:" << ptr1 << endl;  //计数为1,不是2哦
	shared_ptr<int> ptr2(new int(5));
	weak_ptr<int> wptr2(ptr2);
	cout << "ptr2的地址为:" << ptr2 << endl; 

	//交换wptr1与wptr2的绑定对象。
	wptr1.swap(wptr2);
	cout << wptr1.lock() << endl;  //返回ptr2的地址。
	cout << wptr2.lock() << endl;  //返回ptr1的地址。

	//未解绑前
	cout << wptr1.lock() << endl;  //返回ptr2的地址(一个指向ptr2的shared_ptr<int>的指针)。
	cout << wptr1.expired() << endl;  //ptr2不为空,返回false
	cout << wptr1.use_count() << endl;  //计数为1

	//解绑
	wptr1.reset();
	cout << wptr1.lock() << endl;  //被解绑了,返回空
	cout << wptr1.expired() << endl;  //被解绑了,返回true
	cout << wptr1.use_count() << endl;  //计数为0,因为被解绑了

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值