【重学C++】01| C++ 如何进行内存资源管理?

这篇具有很好参考价值的文章主要介绍了【重学C++】01| C++ 如何进行内存资源管理?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章首发

【重学C++】01| C++ 如何进行内存资源管理?

前言

大家好,我是只讲技术干货的会玩code,今天是【重学C++】的第一讲,我们来学习下C++的内存管理。

与java、golang等自带垃圾回收机制的语言不同,C++并不会自动回收内存。我们必须手动管理堆上内存分配和释放,这往往会导致内存泄漏和内存溢出等问题。而且,这些问题可能不会立即出现,而是运行一段时间后,才会暴露出现,排查也很困难。因此,了解和掌握C++中的内存管理技巧和工具是非常重要的,可以提高程序性能、减少错误和增加安全性。

内存分区

在C++中,将操作系统分配给程序的内存空间按照用途划分了代码段、数据段、栈、堆几个不同的区域,每个区域都有其独特的内存管理机制。

代码区

代码区是用于存储程序代码的区域,代码段在程序真正执行前就被加载到内存中,在程序执行期间,代码区内存不会被修改和释放。

由于代码区是只读的,所以会被多个进程共享。在多个进程同时执行同一个程序时,操作系统只需要将代码段加载到内存中一次,然后让多个进程共享这个内存区域即可。

数据段

数据段用于存储静态全局变量、静态局部变量和静态常量等静态数据。在程序运行期间,数据段的大小固定不变,但其内容可以被修改。按照变量是否被初始化。数据段可分为已初始化数据段和未初始化数据段。

C++中函数调用以及函数内的局部变量的使用,都是通过栈这个内存分区实现的。栈分区由操作系统自动分配和释放,是一种"后进先出"的一种内存分区。每个栈的大小是固定的,一般只有几MB,所以如果栈变量太大,或者函数调用嵌套太深,容易发生栈溢出(stack overflow)。

先来一段示例代码,看看C++是如何使用栈进行使用栈来进行函数调用的。

#include <iostream>

void inner(int a) {
    std::cout << a << std::endl;
}
void outer(int n) {
	int a = n + 1;
    inner(a);
}

int main() {
    outer(4);
}

上面这段代码运行过程中的栈变化如下图

每当程序调用一个函数时,该函数的参数、局部变量和返回地址等信息会被压入栈中。当函数执行完毕,再将这些信息从栈中弹出。根据之前压入的外层调用者压入栈的返回地址,返回到外层调用者未执行的代码继续执行。

本地变量是直接存储在栈上的,当函数执行完成后,这些变量占用的内存就会被释放掉了。前面例子中的本地变量是简单类型,在C++中称为POD类型。对于带有构造和析构函数的非POD类型变量,栈上的内存分配同样有效。编译器会在合适的时机,插入对构造函数和析构函数的调用。

这里有个问题,当函数执行发生异常时,析构函数还会被调用吗?
答案是会的,C++对于发生异常时对析构函数的调用称为"栈展开"。通过下面这段代码演示栈展开。

#include <iostream>
#include <string>

class Obj {
public:
    std::string name_;
    Obj(const std::string& name):name_(name){std::cout << "Obj() " << name_ << std::endl;};
    ~Obj() {std::cout << "~Obj() " << name_ << std::endl;};
};


void bar() {
    auto o = Obj{"bar"};
    throw "bar exception";
}

int main() {
    try {
        bar();
    } catch (const char* e) {
        std::cout << "catch Exception: " << e << std::endl;
    }
}

执行代码的结果是:

Obj() bar
~Obj() bar
catch Exception: bar exception

可以发现,发生异常时,bar函数中的本地变量o还是能被正常析构。

栈展开的过程实际上是异常发生时,匹配catch子句的过程。

  1. 程序抛出异常,停止当前执行的调用链,开始寻找与异常匹配的catch子句。
  2. 如果异常发生在try中,则会首先检查与该try块匹配的catch子句。如果异常所在函数体没有try捕获异常。则会直接进入下一步。
  3. 如果第二步未找到匹配的catch,则会在外层的try块中查找,直到找到为止。
  4. 如果到了最外层还没有找到匹配的catch,也就是说异常得不到处理,程序会调用标准库函数terminate终止函数的执行。

在这期间,栈上所有的对象都会被自动析构。

堆是C++中用来存储动态分配内存的内存分区,堆内存的分配和释放需要手动管理,可以通过new/delete或malloc/free等函数进行分配和释放。堆内存的大小通常是不固定的,当我们需要动态分配内存时,就可以使用堆内存。

堆内存由程序员手动分配和释放,因此使用堆内存需要注意内存泄漏和内存溢出等问题。当程序员忘记释放已分配的内存时,会导致内存泄漏问题。而当申请的堆内存超过了操作系统所分配给进程的内存限制时,会导致内存溢出问题。

C++程序绝大多数的内存泄露,都是由于忘记调用delete/free来释放堆上的资源。

还是上代码

#include <iostream>
#include <string>

class Obj {
public:
    std::string name_;
    Obj(const std::string& name):name_(name){std::cout << "Obj() " << name_ << std::endl;};
    ~Obj() {std::cout << "~Obj() " << name_ << std::endl;};
};

Obj* makeObj() {
	Obj* obj = nullptr;
	try {
		obj = new Obj{"makeObj"};
		...
	} catch(...) {
		delete obj;
		throw;
	}
	return obj;
}

Obj* foo() {
	Obj* obj = nullptr;
	try {
		obj = makeObj();
		...
	} catch(...) {
		delete obj;
	}
	return obj;
}
int main() {
    Obj* obj = foo();
    ...
    delete obj;
}

可以看到,由makeObj函数创建的堆变量obj, 在每个获取该变量的上层调用中,都需要关心对该变量的处理。这无疑极大得增加了开发者的心智负担。

RAII

想在堆上创建对象,又不想处理这么复杂的内存释放操作。C++没有像java、golang其他语言创建一套垃圾回收机制,而是采用了一种特有的资源管理方式 --- RAII(Resource Acquisition Is Initialization,资源获取即初始化)。

RAII利用栈对象在作用域结束后会自动调用析构函数的特点,通过创建栈对象来管理资源。在栈对象构造函数中获取资源,在栈对象析构函数中负责释放资源,以此保证资源的获取和释放。

下面给出一个通过RAII来自动释放堆内存的例子

#include <iostream>

class AutoIntPtr {
public:
    AutoIntPtr(int* p = nullptr) : ptr(p) {}
    ~AutoIntPtr() { delete ptr; }

    int& operator*() const { return *ptr; }
    int* operator->() const { return ptr; }

private:
    int* ptr;
};

void foo() {
	AutoIntPtr p(new int(5));
    std::cout << *p << std::endl; // 5
}

int main() {
    foo();
}

上面例子中,AutoIntPtr类封装了一个动态分配的int类型的指针,它的构造函数用于获取资源(ptr = p),析构函数用于释放资源(delete ptr)。当AutoIntPtr超出作用域时,自动调用析构函数来释放所包含的资源。

基于RAII,C++11引入了std::unique_ptrstd::shared_ptr等智能指针用于内存管理类,使得内存管理变得更加方便和安全。这些内存管理类可以自动进行内存释放,避免了手动释放内存的繁琐工作。值得一提的是,上面的AutoIntPtr就是一个简化版的智能指针了。

在实际开发中,RAII的应用很广。不仅仅用于自动释放内存。还可以用来关闭文件、释放数据库连接、释放同步锁等。

总结

本文介绍了C++中的内存管理机制,包括内存分区、栈、堆和RAII技术等内容。通过学习本文,我们可以更好地掌握C++的内存管理技巧,避免内存泄漏和内存溢出等问题。文章来源地址https://www.toymoban.com/news/detail-451833.html

到了这里,关于【重学C++】01| C++ 如何进行内存资源管理?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Docker网络(网络通信),资源控制(CPU优化,内存优化,磁盘优化),数据管理(数据卷,端口映射,容器互联)

    目录 docker网络 网络实现原理 网络实现实例 网络模式 查看Docker中的网络列表: 指定容器网络模式 模式详解 Host模式(主机模式): Container模式(容器模式): None模式(无网络模式): Bridge模式(桥接模式): 自定义网络: cpu优化概述 1. 资源限制: 2. CPU 实时调度策略:

    2024年01月16日
    浏览(80)
  • 【粉丝福利社】Excel高效办公:人力资源管理(AI版)(文末送书-进行中)

    🏆 作者简介,愚公搬代码 🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,51CTO博客专家等。 🏆《近期荣誉》:

    2024年04月08日
    浏览(94)
  • 如何使用Go中的Weighted实现资源管理

    本文将介绍 Go 语言中的 Weighted 并发原语,包括 Weighted 的基本使用方法、实现原理、使用注意事项等内容。能够更好地理解和应用 Weighted 来实现资源的管理,从而提高程序的稳定性。 在微服务架构中,我们的服务节点负责接收其他节点的请求,并提供相应的功能和数据。比

    2024年02月07日
    浏览(90)
  • Unity 如何基于addressable来做资源管理

    一、什么是Addressable系统 Addressable系统是Unity 2018.3版本中推出的一种资源管理方案,它可以帮助我们更好地管理游戏中的资源。在以往的游戏开发中,我们通常使用Resources来进行资源加载,但是这种方式存在很多问题,比如加载速度慢、内存占用高等。Addressable系统可以解决这

    2024年02月16日
    浏览(33)
  • Effective C++条款17——以独立语句将newed 对象置入智能指针(资源管理)

    假设我们有个函数用来揭示处理程序的优先权,另一个函数用来在某动态分配所得的widget上进行某些带有优先权的处理: 由于谨记“以对象管理资源”(条款13)的智慧铭言,processwidget决定对其动态分配得来的widget运用智能指针(这里采用trl::shared_ptr)。 现在考虑调用proces

    2024年02月11日
    浏览(33)
  • 人力资源三支柱是什么?人力资源管理如何运用HR三支柱模型

           随着人力资源管理变革的加速,目前人力资源管理的模式已经升级到了战略性管理阶段。其工作核心聚焦在企业组织变革和战略部署两个方面。人力资源管理的意义在于能够为企业创造多大价值,为客户、投资人和员工等利益相关者提供多大价值?因此,人力资源的

    2024年02月07日
    浏览(54)
  • 如何使用命令提示符重新启动Windows 资源管理器?

    电脑资源管理器出现问题,导致电脑黑屏,如何使用命令提示符重新启动Windows 资源管理器呢?出现这个问题的时候,不要慌,按照下面的操作步骤,大概率是可以复原的,当然你觉得这样比较麻烦,也可以直接重启电脑解决这个问题,以下步骤方法是针对不重启电脑的解决

    2024年02月11日
    浏览(46)
  • C++如何进行内存管理 (new、delete)

    我成功是因为我有决心,从不踌躇。——拿破仑  本章是关于c++内存管理的文章,字数不多,内容简单,希望对你有所帮助!! 话不多说安全带系好,发车啦 (建议电脑观看) 。 附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或

    2024年02月08日
    浏览(99)
  • 大数据计算引擎 EasyMR 如何简单高效管理 Yarn 资源队列

    设想一下,作为一个开发人员,你现在所在的公司有一套线上的 Hadoop 集群。A部门经常做一些定时的 BI 报表,B部门则经常使用软件做一些临时需求。那么他们肯定会遇到同时提交任务的场景,这个时候到底应该如何分配资源满足这两个任务呢?是先执行A的任务,再执行B的任

    2024年02月10日
    浏览(41)
  • 人工智能与客户关系管理:如何实现高效的客户资源利用

    随着人工智能技术的不断发展和进步,越来越多的企业开始将其应用于客户关系管理(CRM)领域,以提高客户资源的利用效率。人工智能在客户关系管理中的应用主要体现在以下几个方面: 客户行为分析:通过收集和分析客户的行为数据,人工智能可以帮助企业更好地了解客户

    2024年02月22日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包