当前位置: 首页 > article >正文

[Effective C++]条款49-52 内存分配

本文初发于 “天目中云的小站”,同步转载于此。

条款49 : 了解new-handler的行为

条款50 : 了解new和delete的合理替换时机

条款51 : 编写new和delete时需固守常规

条款52 :写了placement new也要写placement delete

条款49-52中详细讲述了定制new和delete的实现, 加上前面所讲述的智能指针及资源管理类, 让我们对内存管理有了比较深刻的认知. 但是这部分有些内容经查证已经相对过时, 所以我将统合这四个条款, 先对内存分配有一个初步的认识, 然后再简单讲解一下定制new和delete的编写, 之后再讨论一下各种内存分配的方式.

内存分配的整体认知

相比于java等编程语言以垃圾回收功能津津乐道, 但其实际也会带来运行效率下降的弊端, C++因此并没有纳入垃圾回收的机制, 将内存分配的任务交给了程序员自己, 所以学习内存分配是成为一个优秀C++程序员的所必须的. 为了实现更好的内存分配, 诸如智能指针, allocator等的各种资源管理类如雨后春笋般产生, 当然在此之外C++本身就可以对new进行定制, 这种做法虽然原始, 但是也有一定的用武之地.

先让我们了解一下内存分配的流程 :

  • C : 内存申请(malloc) -> …(使用) -> 释放内存(free)
  • C++ : 内存申请 -> 构造 -> …(使用) -> 析构 -> 释放内存

上面展示了C/C++内存分配的流程, 其实内存申请和释放在底层都是调用malloc和free, 但是在C++中对其进行了封装, 因为其OOP的特性, 在new中不仅申请了内存, 还进行了对应的构造, 简单来说就是先调用malloc申请了一块内存, 然后在这片内存上调用对应对象的构造函数, delete也是同理不再赘述.


定制new和delete

  • 什么是定制new和delete?

    即对new和delete运算符进行重载, 包括new和delete的使用以及运算符重载学到这里我们应当都已经非常熟悉.

  • 为什么要定制new和delete?

    根本原因就是标准版本的new和delete提供的服务太少, 只有申请和构造, 析构和销毁, 因此定制可以实现更多的操作.

    书中这里用了一个条款来解释, 我这里简单提炼一下定制new和delete主要可以做到的提升 :

    • 提前检测运用new或delete上的错误.
    • 强化分配的效能, 可以引入内存池提升分配和释放的效率.
    • 收集统计数据, 加入日志功能, 记录内存分配情况, 便于信息分析.
  • 如何实现定制new和delete?

    主要就是学会new和delete的运算符重载, 但是其中还是有一些门道, 我们不妨来回顾一下new是如何使用的 :

    MyClass* myclass = new MyClass(42);
    

    假设有一个MyClass类, 其构造需要传入一个int, 这段代码便可以实现申请内存 + 构造的全过程, 但是我们还可以通过下面的代码实现 :

    void* ptr = ::operator new(sizeof(MyClass));    // ::operator new
    MyClass* myclass = new(ptr) MyClass(42);	    // placement new
    

    这和上面代码实现的效果是一样的, 里面的两个语法::operator new(全局分配函数)和placement new(定位new), 都是C++基础中应该学到过的, 其实也就分别对应了申请内存和构造, 于是我们可以对内存分配流程做出如下的对应 :

    • 内存申请(::operator new) -> 构造(placement new) -> …(使用) -> 析构(placement delete) -> 释放内存(::operator delete)

    那么定制new和delete就变成了定制::operator new和placement new(及对应deltete版本)了, 定制方法也很简单 :

    • 重载operator new, 不同的参数对应不用的功能, 只传入一个size对应::operator new, 不仅传入size还传入ptr对应placement new, 也许这样设定你会觉得很怪, 但是事实就是这样, 我们通过代码来认识 :

      // 定制 ::operator new
      void* operator new(std::size_t size) {// 进行参数检测, 日志记录, 调用内存池等// ......return ::operator new(size);  // 可以仍然使用标准的 ::operator new, 当然也可以自己malloc
      }// 定制 placement new 操作符
      void* operator new(std::size_t size, void* ptr) noexcept {// 进行日志记录等操作return ptr;  // 不进行内存分配,只是返回已分配的内存地址
      }
      

      当然这种运算符重载如果放在类外就是作用于全局, 放在类内就是类专属, 我们应当有所认知.


申请内存失败的处理方式

我们知道malloc是可能出错的, 内存不足等问题都有可能发送, malloc出错会返回一个nullptr, 在标准库中new会抛出std::bad_alloc这个异常, 因此我们在自己的operator new中如果使用malloc, 当其返回一个nullptr时也应抛出一个bad_alloc.

当然书中也提出应该有更优秀的处理方式, 不应该只是抛出异常, 在条款49中便提出标准库内置了一个错误处理函数new_handler, 可以通过set_new_handler(handler)这个函数来设置, 参数是一个函数指针, 也就是我们可以自己写回调函数并将其设置为new_handler, 在发生内存失败时可以自动调用该函数. 我们可以在其中记录日志, 使用内存池机制(如果用了内存池的话), 决定是否抛出异常(如果申请失败无所谓的话可以不抛出异常).

但是真的有必要吗?

其实new_handler在实际运用中已经很少使用, 根本原因还是在现代内存大小已经不再是问题, 也就是说内存申请失败几乎不可能出现, 只在极少嵌入式设备中可能有需要了. 而且如果真的出现了申请内存失败那只能说明问题很大, 不是硬件有问题就是代码哪里出现了严重的内存泄露, 这不是写一个new_handler可以解决的了, 因此其实在现代背景下还是老实抛出异常就行了.


浅谈内存分配

关于内存分配我认为学好智能指针的使用是最关键的, 因为其可以很大程度上保证我们的代码不会内存泄漏, 只要不发生内存泄漏, 内存大小一般不会是问题.

我们对于内存分配的关注点应该放在如何避免内存泄漏和提升效率上, 内存泄漏有智能指针避免, 提速可以通过引入内存池来缓解, 内存池这里不再详述, 简单来说就是申请和释放内存其实是有一定花销的, 不断的一小块一小块地申请内存然后构造其实是效率远低于直接申请一大块内存, 然后根据需求分配这块内存的, 而内存池就是做到了后者.

至于如何引入, 可以通过上文的定制new和delete的重载函数中引入, 也可以通过自定义的allocator(分配器)来引入, allocator也是一种相对有用的内存分配技术, 如果感兴趣可以自己搜索学习.


请记住 :

  • 内存分配在现代主要还是依赖智能指针, 可以有效避免内存泄露.
  • 如果想对内存分配做更细致的处理, 例如提速或记录日志等, 可以尝试定制new和delete, 使用allocator, 引入内存池.
  • 内存分配失败的情况在当下非常少见, 可以写简单的失败处理机制, 但没必要投入过多精力.

by 天目中云

相关文章:

[Effective C++]条款49-52 内存分配

本文初发于 “天目中云的小站”,同步转载于此。 条款49 : 了解new-handler的行为 条款50 : 了解new和delete的合理替换时机 条款51 : 编写new和delete时需固守常规 条款52 :写了placement new也要写placement delete 条款49-52中详细讲述了定制new和d…...

HTML一般标签和自闭合标签介绍

在HTML中,标签用于定义网页内容的结构和样式。标签通常分为两类:一般标签(也称为成对标签或开放闭合标签)和自闭合标签(也称为空标签或自结束标签)。 以下是这两类标签的详细说明: 一、一般标…...

Eureka 服务注册和服务发现的使用

1. 父子工程的搭建 首先创建一个 Maven 项目&#xff0c;删除 src &#xff0c;只保留 pom.xml 然后来进行 pom.xml 的相关配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xs…...

白嫖DeepSeek:一分钟完成本地部署AI

1. 必备软件 LM-Studio 大模型客户端DeepSeek-R1 模型文件 LM-Studio 是一个支持众多流行模型的AI客户端&#xff0c;DeepSeek是最新流行的堪比GPT-o1的开源AI大模型。 2. 下载软件和模型文件 2.1 下载LM-Studio 官方网址&#xff1a;https://lmstudio.ai 打开官网&#x…...

《Origin画百图》之同心环图

《Origin画百图》第四集——同心环图 入门操作可查看合集中的《30秒&#xff0c;带你入门Origin》 具体操作&#xff1a; 1.数据准备&#xff1a;需要X和Y两列数据 2. 选择菜单 绘图 > 条形图&#xff0c;饼图&#xff0c;面积图: 同心圆弧图 3. 这是绘制的基础图形&…...

蓝牙技术在物联网中的应用有哪些

蓝牙技术凭借低功耗、低成本和易于部署的特性&#xff0c;在物联网领域广泛应用&#xff0c;推动了智能家居、工业、医疗、农业等多领域发展。 智能家居&#xff1a;在智能家居系统里&#xff0c;蓝牙技术连接各类设备&#xff0c;像智能门锁、智能灯泡、智能插座、智能窗帘等。…...

快速提升网站收录:避免常见SEO误区

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/26.html 在快速提升网站收录的过程中&#xff0c;避免常见的SEO误区是至关重要的。以下是一些常见的SEO误区及相应的避免策略&#xff1a; 一、关键词堆砌误区 误区描述&#xff1a; 很多…...

简易计算器(c++ 实现)

前言 本文将用 c 实现一个终端计算器&#xff1a; 能进行加减乘除、取余乘方运算读取命令行输入&#xff0c;输出计算结果当输入表达式存在语法错误时&#xff0c;报告错误&#xff0c;但程序应能继续运行当输出 ‘q’ 时&#xff0c;退出计算器 【简单演示】 【源码位置】…...

再谈多组学(multi-omics)

再谈多组学&#xff08;multi-omics&#xff09; 李升伟 李昱均 概念 多组学&#xff08;Multi-Omics&#xff09; 是指结合多种“组学”技术&#xff0c;从不同层次和维度全面解析生物系统的复杂性。传统的单一组学研究通常关注基因组、转录组、蛋白质组、代谢组等某一特定…...

自动化运维的未来:从脚本到AIOps的演进

点击进入IT管理资料库 一、自动化运维的起源&#xff1a;脚本时代 &#xff08;一&#xff09;脚本在运维中的应用场景 在自动化运维的发展历程中&#xff0c;脚本扮演着至关重要的角色&#xff0c;它作为最初的操作入口&#xff0c;广泛应用于诸多日常运维工作场景里。 在系统…...

线程池以及在QT中的接口使用

文章目录 前言线程池架构组成**一、任务队列&#xff08;Task Queue&#xff09;****二、工作线程组&#xff08;Worker Threads&#xff09;****三、管理者线程&#xff08;Manager Thread&#xff09;** 系统协作流程图解 一、QRunnable二、QThreadPool三、线程池的应用场景W…...

联想拯救者R720笔记本外接显示屏方法,显示屏是2K屏27英寸

晚上23点10分前下单&#xff0c;第二天上午显示屏送到&#xff0c;检查外包装没拆封过。这个屏幕左下方有几个按键&#xff0c;按一按就开屏幕、按一按就关闭屏幕&#xff0c;按一按方便节省时间&#xff0c;也支持阅读等模式。 显示屏是 &#xff1a;AOC 27英寸 2K高清 100Hz…...

C++ deque(1)

1.deque介绍 deque的扩容不像vector那样麻烦 直接新开一个buffer 不用重新开空间再把数据全部移过去 deque本质上是一个指针数组和vector<vector>不一样&#xff0c;vector<vector>本质上是一个vector对象数组&#xff01;并且vector<vector>的buffer是不一…...

深度剖析 PyTorch框架:从基础概念到高级应用的深度学习之旅!

目录 一、引言 二、PyTorch 简介 &#xff08;一&#xff09;诞生背景与发展历程 &#xff08;二&#xff09;核心特点 三、PyTorch 基础概念 &#xff08;一&#xff09;张量&#xff08;Tensor&#xff09;&#xff1a;数据的基石 &#xff08;二&#xff09;自动微分&…...

【Pandas】pandas Series cumsum

Pandas2.2 Series Computations descriptive stats 方法描述Series.abs()用于计算 Series 中每个元素的绝对值Series.all()用于检查 Series 中的所有元素是否都为 True 或非零值&#xff08;对于数值型数据&#xff09;Series.any()用于检查 Series 中是否至少有一个元素为 T…...

EtherCAT主站IGH-- 23 -- IGH之fsm_slave.h/c文件解析

EtherCAT主站IGH-- 23 -- IGH之fsm_slave.h/c文件解析 0 预览一 该文件功能`fsm_slave.c` 文件功能函数预览二 函数功能介绍`fsm_slave.c` 中主要函数的作用1. `ec_fsm_slave_init`2. `ec_fsm_slave_clear`3. `ec_fsm_slave_exec`4. `ec_fsm_slave_set_ready`5. `ec_fsm_slave_…...

多头潜在注意力(MLA):让大模型“轻装上阵”的技术革新——从DeepSeek看下一代语言模型的高效之路

多头潜在注意力&#xff08;MLA&#xff09;&#xff1a;让大模型“轻装上阵”的技术革新 ——从DeepSeek看下一代语言模型的高效之路 大模型的“内存焦虑” 当ChatGPT等大语言模型&#xff08;LLM&#xff09;惊艳世界时&#xff0c;很少有人意识到它们背后隐藏的“内存焦虑”…...

Brightness Controller-源码记录

Brightness Controller 亮度控制 一、概述二、ddcutil 与 xrandr1. ddcutil2. xrandr 三、部分代码解析1. icons2. ui3. utilinit.py 一、概述 项目&#xff1a;https://github.com/SunStorm2018/Brightness.git 原理&#xff1a;Brightness Controlle 是我在 Ubuntu 发现上调…...

Java8_StreamAPI

Stream 1.创建流 1.1 集合创建流 List<String> list List.of("a", "b", "c"); Stream<String> stream list.stream(); stream.forEach(System.out::println);1.2 数组创建流 String[] array {"a","b",&qu…...

【架构面试】二、消息队列和MySQL和Redis

MQ MQ消息中间件 问题引出与MQ作用 常见面试问题&#xff1a;面试官常针对项目中使用MQ技术的候选人提问&#xff0c;如如何确保消息不丢失&#xff0c;该问题可考察候选人技术能力。MQ应用场景及作用&#xff1a;以京东系统下单扣减京豆为例&#xff0c;MQ用于交易服和京豆服…...

OpenEuler学习笔记(十六):搭建postgresql高可用数据库环境

以下是在OpenEuler系统上搭建PostgreSQL高可用数据环境的一般步骤&#xff0c;通常可以使用流复制&#xff08;Streaming Replication&#xff09;或基于Patroni等工具来实现高可用&#xff0c;以下以流复制为例&#xff1a; 安装PostgreSQL 配置软件源&#xff1a;可以使用O…...

Vue.js路由管理与自定义指令深度剖析

Vue.js 是一个强大的前端框架,提供了丰富的功能来帮助开发者构建复杂的单页应用(SPA)。本文将详细介绍 Vue.js 中的自定义指令和路由管理及导航守卫。通过这些功能,你可以更好地控制视图行为和应用导航,从而提升用户体验和开发效率。 1 自定义指令详解 1.1 什么是自定义…...

skynet 源码阅读 -- 核心概念服务 skynet_context

本文从 Skynet 源码层面深入解读 服务&#xff08;Service&#xff09; 的创建流程。从最基础的概念出发&#xff0c;逐步深入 skynet_context_new 函数、相关数据结构&#xff08;skynet_context, skynet_module, message_queue 等&#xff09;&#xff0c;并通过流程图、结构…...

论文阅读(十一):基因-表型关联贝叶斯网络模型的评分、搜索和评估

1.论文链接&#xff1a;Scoring, Searching and Evaluating Bayesian Network Models of Gene-phenotype Association 摘要&#xff1a; 全基因组关联研究&#xff08;GWAS&#xff09;的到来为识别常见疾病的遗传变异&#xff08;单核苷酸多态性&#xff08;SNP&#xff09;&…...

企业微信远程一直显示正在加载

企业微信远程一直显示正在加载 1.问题描述2.问题解决 系统&#xff1a;Win10 1.问题描述 某天使用企业微信给同事进行远程协助的时候&#xff0c;发现一直卡在正在加载的页面&#xff0c;如下图所示 2.问题解决 经过一番查找资料后&#xff0c;我发现可能是2个地方出了问题…...

人工智能 - 1

深度强化学习&#xff08;Deep Reinforcement Learning&#xff09; 图神经网络&#xff08;Graph Neural Networks, GNNs&#xff09; Transformer 一种深度学习模型 大语言模型&#xff08;Large Language Models, LLMs&#xff09; 人工智能 • Marvin Minsky 将其定义…...

留学生scratch计算机haskell函数ocaml编程ruby语言prolog作业VB

您列出了一系列编程语言和技术&#xff0c;这些可能是您在留学期间需要学习或完成作业的内容。以下是对每个项目的简要说明和它们可能涉及的领域或用途&#xff1a; Scratch&#xff1a; Scratch是一种图形化编程语言&#xff0c;专为儿童和初学者设计&#xff0c;用于教授编程…...

LeetCode题练习与总结:最长和谐子序列--594

一、题目描述 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 给你一个整数数组 nums &#xff0c;请你在所有可能的 子序列 中找到最长的和谐子序列的长度。 数组的 子序列 是一个由数组派生出来的序列&#xff0c;它可以通过删除一些元素或不删除元素…...

Linux_线程同步生产者消费者模型

同步的相关概念 同步&#xff1a;在保证数据安全的前提下&#xff0c;让线程能够按照某种特定的顺序访问临界资源&#xff0c;从而有效避免饥饿问题&#xff0c;叫做同步竞态条件&#xff1a;因为时序问题&#xff0c;而导致程序异常&#xff0c;我们称之为竞态条件。 同步的…...

Github 2025-01-30 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2025-01-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:2724 次关注人…...