C++如何用智能指针管理内存资源

 更新时间:2020年08月31日 07:00:37   作者:Dabelv  
这篇文章主要介绍了C++如何用智能指针管理内存资源,帮助大家更好的理解和使用c++开发,感兴趣的朋友可以了解下

1.简介

C++作为一门应用广泛的高级编程语言,却没有像Java、C#等语言拥有垃圾回收(Garbage Collection )机制来自动进行内存管理,这也是C++一直被诟病的一点。C++在发展的过程中,一直致力于解决内存泄漏,C++虽然基于效率的考虑,没有采用垃圾回收机制,但从C++98开始,推出了智能指针(Smart Pointer)来管理内存资源,以弥补C++在内存管理上的技术空白。

智能指针是C++程序员们一件管理内存的利器,使用智能指针管理内存资源,实际上就是将申请的内存资源交由智能指针来管理,是RAII技术的一种实现。RAII是C++的之父Bjarne Stroustrup教授提出的概念,RAII全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”,也就是说在构造函数中获取资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。

“资源获取即初始化”,在使用智能指针管理内存资源时,“资源”指的是通过new或malloc申请的内存资源,“初始化”指的是使用申请的内存资源来初始化栈上的智能指针类对象。使用智能指针管理内存资源的好处显而易见,通过智能指针对象在声明周期结束时,自动调用析构函数,在析构函数中完成对内存资源的释放,即自动的调用内存资源的释放代码,避免因忘记对内存资源的释放导致内存泄漏。

2.实例

下面看一个使用由C++11引入的智能指针unique_ptr来管理内存资源的例子。

#include <memory>
#include <iostream>
using namespace std;

class A
{
public:
	A() {}
	~A() 
	{
		cout<<"A's destructor"<<endl;
	}
	void Hello() 
	{
		cout<<"use smart pointer to manage memory resources as far as possible"<<endl;
	}
};

int main()
{
	unique_ptr<A> pA(new A);
	pA->Hello();
	return 0;
}

程序输出:

use smart pointer to manage memory resources as far as possible
A's destructor

可见在main()函数结束后,类A的析构函数被自动调用,完成了内存资源了释放。

在创建智能指针对象时,也可以暂时不指定内存资源,先创建一个空的智能指针对象。空智能指针对象不可以进行任何操作,但可以使用 get() 成员函数来判断是否存在内存资源,如果为空则可以指定内存资源。类似于如下操作:

unique_ptr<int> pInt;
if (pInt.get()==nullptr)
{
	pInt.reset(new int(8));
	cout<<*pInt<<endl;
}

使用 unique_ptr 智能指针来管理内存资源时,是对内存资源的独占式管理,即内存资源的所有权不能进行共享,同一时刻只能有一个 unique_ptr 对象占有某个内存资源。如果发生赋值或拷贝构造,则会在编译期报错,因为unique_ptr禁止了拷贝语义,提高了代码的安全性。

unique_ptr<int> pInt(new int(8));
unique_ptr<int> pInt1=pInt;	//编译报错
unique_ptr<int> pInt2(pInt);	//编译报错

当然,可以通过移动语义完成内存资源的所有权转移,转移之后,原智能指针对象变为空智能指针对象,不能再对内存资源进行任何操作,否则会发生运行时错误,但我们也可以使用get()成员函数进行判空处理。

unique_ptr<int> pInt(new int(8));
unique_ptr<int> pInt1=std::move(pInt);		//转移所有权
*pInt=6;																//对空智能指针进行赋值操作将报运行时错误
if(!pInt.get())														//判空处理更安全
{
	*pInt=6;
}

独占式的内存资源管理可以使用 unique_ptr 来完成,但是如果想对内存资源进行共享式管理,那么 unique_ptr 就无能为力了。shared_prt 使用引用计数来实现对内存资源的共享式管理,当对内存资源的引用计数变为0时,由最后一个对内存资源拥有管理权的智能指针对象完成对内存资源的释放。

#include <memory>
#include <iostream>
using namespace std;

class A
{
public:
	A() {}
	~A()
	{
		cout << "A's destructor" << endl;
	}
	void Hello()
	{
		cout << "use smart pointer to manage memory resources as far as possible" << endl;
	}
};

int main()
{
	shared_ptr<A> spInt(new A);		//接管内存资源
	cout << "reference count "<<spInt.use_count() << endl;
	shared_ptr<A> spInt1 = spInt;	//spInt1获取内存资源的管理权
	spInt1->Hello();
	cout << "reference count " << spInt.use_count() << endl;
	spInt1.reset();						//spInt1放弃对内存资源的管理权
	cout << "reference count " << spInt.use_count() << endl;
}

程序编译运行结果:

reference count 1
use smart pointer to manage memory resources as far as possible
reference count 2
reference count 1
A's destructor

3.智能指针使用注意事项

智能指针虽然增强了安全性,避免了潜在的内存泄漏,但是我们在使用时还是应该遵守一定的规则,以保证代码的健壮性。
(1)smart_ptr<T> 不等于 T*,使用时不能完全按照T*来使用。因为smart_ptr<T>本质上是类对象,一个用于管理内存资源的智能指针类对象,而T*是一个指向类型T的指针,二者不能随意地转换和赋值;

(2)使用独立的语句将newed对象置入智能指针,因为使用临时智能指针对象可能会引发内存泄漏。比如下面的语句:

process(shared_ptr<A>(new A),foo());

实际上对 process() 函数调用时编译器需要完成如下三步为process()准备好实参。

(1)调用函数foo();
(2)执行new A表达式;
(3)调用shared_ptr<A>构造函数,初始化智能指针对象。

实际上,不同的编译器在执行上述三个语句时可能会有不同的顺序,如果编译器将(2.2)放在(2.1)之前执行,执行顺序如下:

(1)执行new A表达式;
(2)调用函数foo();
(3)调用shared_ptr<A>构造函数,初始化智能指针对象。

如果在调用函数foo()时抛出异常,那么new A表达式产生的指向堆对象指针将会丢失,于是产生了内存泄漏。解决办法就是使用独立的语句将newed对象置入智能指针,做法如下:

shared_ptr<A> spA(new A);
process(spA,foo());

以上就是C++如何用智能指针管理内存资源的详细内容,更多关于c++ 智能指针管理内存的资料请关注脚本之家其它相关文章!

相关文章

  • C++实现简单插件机制原理解析

    C++实现简单插件机制原理解析

    这篇文章主要介绍了C++实现简单插件机制原理解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • C语言中回调函数的使用详情

    C语言中回调函数的使用详情

    这篇文章主要介绍了C语言中回调函数的使用详情,阅读下文我们将学习到架构的核心理念和需、回调函数的作用、回调函数的程序编写等内容,需要的小伙伴可以参考一下
    2022-03-03
  • C语言实现打印九九乘法表的四种方式小结

    C语言实现打印九九乘法表的四种方式小结

    这篇文章主要为大家介绍了C语言实现打印九九乘法表的四种方式,文中的示例代码讲解详细,简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-07-07
  • C语言每日练习之二叉堆

    C语言每日练习之二叉堆

    这篇文章主要为大家介绍了C语言二叉堆,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • C语言实现查询自动售货机中的商品价格【实例分享】

    C语言实现查询自动售货机中的商品价格【实例分享】

    本文主要介绍了C语言实现查询自动售货机中的商品价格的相关资料。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • C++基础学生管理系统

    C++基础学生管理系统

    这篇文章主要介绍了C++基础学生管理系统的相关资料,包括了文件处理代码,链表处理代码,以及自定义代码,主函数自定义,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • 详解C/C++ QT QChart 绘制组件应用

    详解C/C++ QT QChart 绘制组件应用

    Qtcharts 组件基于GraphicsView模式实现,其核心是QChartView和QChart的二次封装版。本文重点给大家介绍C/C++ QT QChart 绘制组件应用的相关知识,感兴趣的朋友一起看看吧
    2021-11-11
  • 使用C语言中的time函数获取系统时间

    使用C语言中的time函数获取系统时间

    在C语言中可以使用time函数来获取系统时间,以下对time函数进行了介绍,需要的朋友可以过来参考下
    2013-07-07
  • 餐馆点菜系统C语言源代码

    餐馆点菜系统C语言源代码

    这篇文章主要为大家详细介绍了餐馆点菜系统C语言源代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • 浅析C语言中printf(),sprintf(),scanf(),sscanf()的用法和区别

    浅析C语言中printf(),sprintf(),scanf(),sscanf()的用法和区别

    以下是对C语言中printf(),sprintf(),scanf(),sscanf()的用法以及区别进行了详细的分析介绍,需要的朋友可以参考下
    2013-07-07

最新评论