C++初学者指南-3.自定义类型(第一部分)-析构函数

C++初学者指南-3.自定义类型(第一部分)-析构函数

文章目录

  • C++初学者指南-3.自定义类型(第一部分)-析构函数
    • 特殊的成员函数
    • 用户定义的构造函数和析构函数
    • RAII
    • 示例:资源处理
    • 示例:RAII记录
    • 零规则

特殊的成员函数

T::T()默认构造函数当创建新的 T 对象时运行。
T::T(param…)特殊构造函数创建带参数的新 T 对象时运行
T::~T()析构函数当现有的 T 对象被销毁时运行

编译器会在我们没有自己定义的情况下生成一个默认构造函数和一个析构函数。
在后面的章节中,我们将了解到四个特殊的成员,可以用来控制类型的复制和移动行为。

  • copy constructor(拷贝构造函数)  T::T(T const&)
  • copy assignment operator(拷贝赋值操作符函数)  T& T::operator = (T const&)
  • move constructor(移动构造函数)  T::T(T &&)
  • move assignment operator (移动赋值操作符函数) T& T::operator = (T &&)

它们通常也是由编译器自动生成的,在许多/大多数情况下不需要用户自定义。

用户定义的构造函数和析构函数

class Point {};
class Test {std::vector<Point> w_;std::vector<int> v_;int i_ = 0;
public:Test() { std::cout << "constructor\n"; }~Test() { std::cout << "destructor\n"; }// more member functions …
};

运行上面代码

if (…) {…Test x;  // prints 'constructor'…
}  // prints 'destructor'

销毁时执行顺序
在析构函数体运行完毕后,所有数据成员的析构函数将按照声明的相反顺序执行。这是自动发生的,不能更改(至少不容易改 - 毕竟这是C++,几乎有可以绕过任何事情的方法)。

x 超出作用域范围→执行 ~Test():

  • std::cout << “destructor\n”;
  • x的数据成员被销毁了:
    • i_ 被销毁了(基本类型没有析构函数)
    • v_ 被销毁 → 执行析构函数 ~vector():
      • vector在其缓冲区中销毁整数元素;(基本类型→没有析构函数)
      • 释放堆上的缓冲区内存
      • v_的剩余数据成员已被销毁
    • w_ 被销毁 → 执行析构函数 ~vector():
      • vector在其缓冲区中销毁Point元素
      • 每个~Point()析构函数都会被执行
      • 释放堆上的缓冲区内存
      • w_的剩余数据成员被销毁

RAII

“资源获取即初始化”

  • 对象构建:获取资源
  • 对象销毁:释放资源

示例:std::vector

  • 每个向量对象都拥有一个独立的堆上缓冲区,在那里存储着实际内容。
  • 该缓冲区是根据需要分配的,并且在向量对象被销毁时被释放。
    在这里插入图片描述

所有权
如果一个对象负责资源的生命周期(初始化/创建、终结/销毁),我们就说它是资源(内存、文件句柄、连接、线程、锁……)的所有者。

提醒:C++ 使用值语义
= 变量指向对象本身,而不仅仅是引用/指针。
这是几乎所有编程语言中基本类型(int、double等)的默认行为,也是C++中用户自定义类型的默认行为:

  • 深拷贝:生成一个新的、独立的对象;对象(成员)的值被复制
  • 深层赋值:使目标的值等于源对象的值
  • 深层所有权:成员变量指向与包含对象具有相同生命周期的对象
  • 基于值的比较:如果它们的数值相等/较小,则变量进行相等/小于/… 的比较。

由于成员的生命周期与其包含的对象绑定在一起,所以不需要垃圾回收器。

示例:资源处理

常见情况
我们需要使用一个外部的 © 库,它具有自己的资源管理。这些资源可以是内存,还可以是设备、网络连接、已打开的文件等。
在这样的库中,资源通常是通过初始化和清理函数来处理的,比如 lib_init() 和 lib_finalize() ,用户需要调用这些函数。
问题:资源泄漏
通常在程序庞大且控制流复杂时,经常会忘记调用最终清理函数。这可能导致设备卡住,内存未被释放等问题。
解决方案:RAII 包装器

  • 在构造函数中调用初始化函数
  • 在析构函数中调用清理函数
  • 额外优势:包装类还可以用来存储上下文信息,如连接详情,设备ID等,这些只在初始化和结束之间有效
  • 这样的包装器大多数情况下应该是不可复制的,因为它处理着独特的资源(在后面的章节中会有更详细的解释)
#include <gpulib.h>class GPUContext {int gpuid_;
public:explicitGPUContext (int gpuid = 0): gpuid_{gpuid} {gpulib_init(gpuid_);}~GPUContext () {gpulib_finalize(gpuid_);}[[nodiscard]] int gpu_id () const noexcept { return gpuid_;}
// make non-copyable:GPUContext (GPUContext const&) = delete;GPUContext& operator = (GPUContext const&) = delete;
};int main () {if () {// 创建和初始化上下文GPUContext gpu;// 在这里处理事情} // 自动清理释放!}

示例:RAII记录

  • Device的构造函数获得一个指向UsageLog对象的指针
  • UsageLog 可以用来记录 Device 对象生命周期中的操作
  • 如果Device不再存在,析构函数会通知UsageLog
  • UsageLog 还可以统计活跃设备的数量等等
class File {};
class DeviceID {};class UsageLog {
public:explicit UsageLog (File const&);void armed (DeviceID);void disarmed (DeviceID);void fired (DeviceID);
};class Device {DeviceID id_;UsageLog* log_;public:explicitDevice (DeviceId id, UsageLog* log = nullptr): id_{id}, log_{log},{ if (log_) log_->armed(id_);}~Device () { if (log_) log_->disarmed(id_); }void fire () {if (log_) log_->fired(id_);}};int main () {File file {"log.txt"}UsageLog log {file};…Device d1 {DeviceID{1}, &log};d1.fire(); {Device d2 {DeviceID{2}, &log};d2.fire(); }d1.fire(); 
}
log.txt
device 1   armed
device 1   fired
device 2   armed
device 2   fired
device 2   disarmed
device 1   fired
device 1   disarmed

零规则

= 尽量不要编写特殊成员函数

除非你需要进行 RAII 风格的资源管理或基于生命周期的跟踪,否则请避免编写特殊成员函数。
大多数情况下,编译器生成的默认构造函数和析构函数已经足够了。

初始化并不总是需要编写构造函数。
大多数数据成员可以使用成员初始化器进行初始化。

不要为类型添加空析构函数!
用户自定义析构函数的存在会阻止许多优化,并严重影响性能!

你几乎不需要写析构函数。
在C++11之前,使用自定义类并进行显式手动内存管理是非常常见的。然而,在现代C++中,内存管理策略大多数情况下(也应该)封装在专用类(容器,智能指针,分配器等)中。

附上原文地址
如果文章对您有用,请随手点个赞,谢谢!^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/366669.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置WLAN 示例

规格 仅AR129CVW、AR129CGVW-L、AR109W、AR109GW-L、AR161W、AR161EW、AR161FGW-L、AR161FW、AR169FVW、AR169JFVW-4B4S、AR169JFVW-2S、AR169EGW-L、AR169EW、AR169FGW-L、AR169W-P-M9、AR1220EVW和AR301W支持WLAN-FAT AP功能。 组网需求 如图1所示&#xff0c;企业使用WLAN…

C++(第一天-----命名空间和引用)

一、C/C的区别 1、与C相比   c语言面向过程&#xff0c;c面向对象。   c能够对函数进行重载&#xff0c;可使同名的函数功能变得更加强大。   c引入了名字空间&#xff0c;可以使定义的变量名更多。   c可以使用引用传参&#xff0c;引用传参比起指针传参更加快&#…

基于YOLOv9+pyside的安检仪x光危险物物品检测(有ui)

安全检查在公共场所确保人身安全的关键环节&#xff0c;不可或缺。X光安检机作为必要工具&#xff0c;在此过程中发挥着重要作用。然而&#xff0c;其依赖人工监控和判断成像的特性限制了其应用效能。本文以此为出发点&#xff0c;探索了基于Torch框架的YOLO算法在安检X光图像中…

Xcode安装Simulator失败问题解决方法

Xcode安装Simulator_Runtime失败&#xff0c;安装包离线安装保姆级教程 Xcode更新之后有时候会提示要安装模拟器运行时环境&#xff0c;但是用Xcode更新会因为网络原因&#xff0c;我觉得基本上就是因为苹果服务器的连接不稳定导致的&#xff0c;更可气的是不支持断点续…

【论文阅读】--Popup-Plots: Warping Temporal Data Visualization

弹出图&#xff1a;扭曲时态数据可视化 摘要1 引言2 相关工作3 弹出图3.1 椭球模型3.1.1 水平轨迹3.1.2 垂直轨迹3.1.3 组合轨迹 3.2 视觉映射与交互 4 实施5 结果6 评估7 讨论8 结论和未来工作致谢参考文献 期刊: IEEE Trans. Vis. Comput. Graph.&#xff08;发表日期: 2019&…

DICOM灰度图像、彩色图像的窗宽、窗位与像素的最大最小值的换算关系?

图像可以调整窗宽、窗位 dicom图像中灰度图像可以调整窗宽、窗位&#xff0c;RGB图像调整亮度或对比度&#xff1f;_灰度 图 调节窗宽-CSDN博客 窗宽、窗位与像素的最大最小值的换算关系? 换算公式 max-minWindowWidth; (maxmin)/2WindowCenter; 详细解释 窗宽&#xff0…

视频太大怎么压缩变小?6款视频压缩软件免费版分享

视频太大怎么压缩得又小又清晰呢&#xff1f;无论是视频文件传输、视频文件存储&#xff0c;还是进行自媒体视频上传&#xff0c;都对视频文件的大小有一定的限制。高质量的视频文件往往伴随着文件占据大量存储空间&#xff0c;导致文件传输速度变慢。今天教大家6种视频压缩软件…

试用笔记之-汇通来电显示软件

首先汇通来电显示软件下载 http://www.htsoft.com.cn/download/httelephone.rar

IP白名单及其作用解析

在网络安全领域&#xff0c;IP白名单是一项至关重要的策略&#xff0c;它允许特定的IP地址或地址范围访问网络资源&#xff0c;从而确保只有受信任的终端能够连接。下面&#xff0c;我们将深入探讨IP白名单的定义、作用以及实施时的关键考虑因素。 一、IP白名单的定义 IP白名单…

深度学习21-30

1.池化层作用&#xff08;筛选、过滤、压缩&#xff09; h和w变为原来的1/2&#xff0c;64是特征图个数保持不变。 每个位置把最大的数字取出来 用滑动窗口把最大的数值拿出来&#xff0c;把44变成22 2.卷积神经网络 &#xff08;1&#xff09;conv&#xff1a;卷积进行特征…

stm32学习笔记---USART串口协议(理论部分)

目录 通信 通信的目的 通信协议 STM32的通信协议 各种协议的通信引脚介绍 通信空间和时间 时钟特性 电平特性 设备特性 串口通信 硬件电路 电平标准 串口参数及时序 时序 串口的参数 串口通信的实际波形 声明&#xff1a;本专栏是本人跟着B站江科大的视频的学习…

Vue 项目部署为 HTTPS 站点

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;爱蹦跶的大A阿 &#x1f525;当前正在更新专栏&#xff1a;《JavaScript保姆级教程》、《VUE》、《Krpano》 ✨ 前言 在将 Vue 项目部署为 HTTPS 站点时&#xff0c;你需要配置 HTTPS 证书和服务器。以下是一个基本的步骤和…

深入解析 androidx.databinding.BaseObservable

在现代 Android 开发中&#xff0c;数据绑定 (Data Binding) 是一个重要的技术&#xff0c;它简化了 UI 和数据之间的交互。在数据绑定框架中&#xff0c;androidx.databinding.BaseObservable 是一个关键类&#xff0c;用于实现可观察的数据模型。本文将详细介绍 BaseObservab…

java 代码块

Java中的代码块主要有三种类型&#xff1a;普通代码块、静态代码块、构造代码块。它们的用途和执行时机各不相同。 普通代码块&#xff1a;在方法内部定义&#xff0c;使用一对大括号{}包围的代码片段。它的作用域限定在大括号内&#xff0c;每当程序执行到该代码块时就会执行其…

Pikachu 不安全的文件下载(Unsafe file download)概述 附漏洞利用案例

目录 获取下载链接 修改链接 重新构造链接 拓展 不安全的文件下载概述 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c;便会向后台发送一个下载请求&#xff0c;一般这个请求会包含一个需要下载的文件名称&#xff0c;后台在收到请求…

初识Java(复习版)

一. 什么是Java Java是一种面向对象的编程语言&#xff0c;和C语言有所不同&#xff0c;C语言是一门面向过程的语言。偏底层实现&#xff0c;比较注重底层的逻辑实现。不能一味的说某一种语言特别好&#xff0c;每一种语言都是在特定的情况下有自己的优势。 二.Java语言发展史…

Docker Compose 一键快速部署 RocketMQ

Apache RocketMQ是一个开源的分布式消息中间件系统&#xff0c;最初由阿里巴巴开发并贡献给Apache软件基金会。RocketMQ提供了高性能、高可靠性、高扩展性和低延迟的消息传递服务&#xff0c;适用于构建大规模分布式系统中的消息通信和数据同步。 RocketMQ支持多种消息模型&am…

武汉星起航:无锡跨境电商加速“出海”,物流升级助品牌全球布局

随着全球化的不断深入&#xff0c;跨境电商作为数字外贸的新业态&#xff0c;正逐渐成为无锡企业拓展海外市场的重要渠道。武汉星起航关注到&#xff0c;近年来&#xff0c;无锡市通过积极推进国际物流枢纽建设&#xff0c;完善海外仓布局&#xff0c;以及各特色产业带的积极参…

JavaMySQL 学习(基础)

目录 Java CMD Java发展 计算机存储规则 Java学习 switch新用法&#xff08;可以当做if来使用&#xff09; 数组定义 随机数 Java内存分配 MySQL MySQL概述 启动和停止 客户端连接 数据模型 关系型数据库 SQL SQL通用语法 SQL分类 DDL--数据定义语言 数据库…

Intellij Idea显示回退和前进按钮的方法

方法1 使用快捷键&#xff1a; 回到上一步 ctrl alt <-&#xff08;左方向键&#xff09;回到下一步 ctrl alt ->&#xff08;右方向键&#xff09; 方法2&#xff1a; Preferences -> Appearance & Behavior -> Menus and Toolbars -> Navigation B…