c++中智能指针的原理、分类和实现

本文深入解析C++智能指针的概念,包括auto_ptr、unique_ptr、shared_ptr和weak_ptr的原理与应用,阐述如何通过智能指针有效管理堆内存,防止内存泄漏。

智能指针的介绍

原理

智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。

作用

C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。程序员自己管理堆内存可以提高了程序的效率,但是整体来说堆内存的管理是麻烦的,C++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存。

智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。

智能指针的分类

auto_ptr

在 C++ 语言中,要使用 STL 中的 auto_ptr 对象,必须包含头文件 memory,该文件包括 auto_ptr 模板。使用通常的模板句法来实例化所需类型的指针。auto_ptr 构造函数是显式的,不存在从指针到 auto_ptr 对象的隐式类型转换。

auto_ptr(c++98的方案,c++11已经抛弃),采用的是所有权模式。

	string p = new string("hello world");
	auto_ptr<string> p1 (p); // 允许
	auto_ptr<string> p2;
	p2 = p1; // 不允许 p2剥夺了p1的所有权,当程序运行访问p1将报错
	p2 = auto_ptr <string> (p); //允许
	auto_ptr <string> panto = p; //不允许

模板可以通过构造函数将 auto_ptr 对象初始化为一个常规指针。auto_ptr 是一个智能指针,但其特性远比指针要多。值得注意的是,在使用 auto_ptr 时,只能配对使用 new 和 delete。
提示,只能对 new 分配的内存使用 auto_ptr 对象,不要对由 new() 分配的或通过声明变量分配的内存使用它。

autp_ptr链接

unique_ptr

unique_ptr实现独占式拥有或者严格拥有概念,保证同一时间只有一个智能指针可以指向该对象。它对于避免资源泄露特别有用。

	unique_ptr<string> p1 (new string("auto"));
	unique_ptr<string> p2;
	p2 = p1 ; // #1此时报错
	unique_ptr<string> p3;
	p3 = unique_ptr<string>(new string("you")); // #2正确

#1留下悬挂的unique_ptr p1,非法,避免了p1不在指向有效数据;#2它调用unique_ptr的构造函数,该构造函数创建的临时对象在其所有权让给p3后销毁。

如果确实想执行类似与#1的操作,使用函数move(),让你有一个unique_ptr赋值给另一个。例如:

	#include<iostream>
	#include<memory>
	using namespace std;
	int main()
	{
	    unique_ptr<string> ps1, ps2;
	    ps1 = unique_ptr<string> (new string("world"));
	    ps2 = move(ps1);
	    ps1 = unique_ptr<string> (new string("hello "));
	    cout << *ps1 << *ps2 << endl;
	    return 0;
	}

在这里插入图片描述

shared_ptr

shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在最后一个引用被销毁时候释放。通过use_count()来查看资源被几个指针共享。除了可以通过new来构造,还可以通过传入auto_ptr,unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0,资源会被释放。

成员函数:

  1. use_count()返回引用计数的个数;
  2. unique()返回是否独占所有权;
  3. swap()交换两个shared_pt6;
  4. reset()放弃内部对象的所有权或者拥有对象的变更,会引起原来对象的引用计数的减少;
  5. get()返回内部对象的地址。
	#include<iostream>
	#include<memory>
	using namespace std;
	int main()
	{
	    shared_ptr<string> ptest(new string("123"));
	    shared_ptr<string> ptest2(new string("456"));
	    cout<<*ptest2.get()<<endl;
	    cout<<ptest2.use_count()<<endl;
	    ptest = ptest2;//"456"引用次数加1,“123”销毁
	    cout << *ptest.get() << endl;
	    cout<<ptest2.use_count()<<endl;//2
	    cout<<ptest.use_count()<<endl;//2
	    ptest.reset();
	    ptest2.reset();//此时“456”销毁
	    cout<<"done !\n";
	    return 0;
	}

在这里插入图片描述

weak_ptr

weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

成员函数比shared_ptr多了两个,但是少了get():

  1. expired() 为use_count()为0,返回true,否则返回false;
  2. lock()如果expired为空,返回空的shared_ptr;否则返回一个指向对象的shared_ptr;

weak_ptr代码链接

智能指针的总结

  1. 不要把一个原生指针给多个智能指针对象管理, 对所有的智能指针对象都成立。
  2. 不要把 this 指针给智能指针对象,对所有的智能指针对象(包括 auto_ptr)都成立。
  3. 不要在函数实参里创建智能指针对象。
  4. 处理不是 new 创建的对象要小心. 如果确实需要这样做, 需要智能指针传递一个删除器, 自定义删除行为。
  5. 不要使用 new 创建一个智能指针对象.如 new shared_ptr 。
  6. 使用 dynamic_pointer_cast 进行转换。
  7. 不要 memcpy 智能指针对象。
  8. 智能指针对象数组的使用, 需要自定义释放器。
  9. 将智能指针对象作为函数参数传递时要小心, 如下面的代码, 当调用所在的表达式结束(即函数调用返回)时, 这个临时对象就被销毁了, 它所指向的内存也被释放.。
  10. 当将一个智能指针对象(如 shared_ptr)绑定到一个普通指针时, 就将内存管理的责任交给了这个 shared_ptr. 此后就不应该使用内置指针来访问 shared_ptr 所指向的内存了。
  11. 不能使用 delete 释放 get 返回的普通指针. get 函数的设计是为了向不能使用智能指针的代码传递一个普通指针, 应该减少 get 函数的调用。
  12. 不要使用 get 返回的普通指针来初始化另一个智能指针, 或为另一个智能指针赋值. 显然如果这样做, 将导致两次释放相同的内存, 或者其中一个已经将内存释放, 但另一个还在使用。

总结参考链接

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值