P28 C++ 对象的生存周期(栈的作用域生存周期)

前言

本期的主题是栈作用域中对象的生存期,通俗来讲,就是讨论对象是如何在栈上生存的。

这章内容整体分为两部分。

  1. 第一部分是,你必须理解栈上的东西是如何存在的,这样你才能真正写出能正常工作的代码。
  2. 第二部分是,一旦你知道了它是如何运作的,那要如何利用好它,做你想做的事情,能想出一些聪明的方法来做一些事情。

首先我们先要了解栈的概念。

栈是一种数据结构,你可以在上面堆叠一些东西。

假设你的桌子上有一堆书,为了访问中间的一个,你得先把上面的前几个拿掉,然后找到中间那本书,(当然现实世界中,你可以直接把书抽出来,但这不是栈在编程中的工作方式)。

所以每次我们在 C++ 中进入一个作用域时,我们是在 push 栈帧,但它不一定非得是将数据推进(push)一个栈帧,你可以把它想成是把一本书放在书堆上,在此作用域下(这本书内)声明的变量,就像是在书中写东西,一旦作用域结束,将这本书从书堆中拿出来,然后扔掉,那么在书中申明的每一个基于栈的变量,以及你在书中栈里创造的所有对象就都消失了。

这样有好处也有坏处,但是如果你知道你自己在做什么,百分百是有好处的。接下来一起来看一些例子,了解一下这一切是如何结合在一起的以及这一切是如何运作的。

01 什么是作用域。

作用域有很多使用场景。比如函数作用域,类作用域,比如 if 语句作用域,或者是 for 循环、while 循环作用域,甚至是空作用域。

我们来完善一个类的例子来说明。

02 在栈中创建对象

#include <iostream>class Demo
{
public:Demo(){std::cout << "构造函数" << std::endl;}~Demo(){std::cout << "析构函数" << std::endl;}};int main()
{Demo demo;return 0;
}

 这是一个简单的 Demo 类,包括构造函数和析构函数。在下面的空作用域中声明了一个 Demo ,这样写,就不是将它创建在堆上,而是创建在栈上,这将调用默认构造函数,

在main函数设置断点调试一下。

运行一步后,可以看到 "构造函数" 被打印到控制台,到了作用域的最后 },再运行一步,可以看到 "析构函数" ,意味着正在摧毁 Demo类,这部分内存已经被释放了。

03 在堆中创建对象 

怎么在堆中创建对象呢?

很简单,加*和加new关键字 

而如果改为堆分配,则是这样的效果。

断点调试一下

可以看到 "构造函数!" 后程序直接跳过了作用域的 } 这一行,并没有输出 "析构函数" ,Demp永远不会被销毁(当然,当应用程序终止时,操作系统会清除这些内存)。现在你应该已经明白了基于栈的变量和基于堆的变量在对象生存期上的区别,基于栈的变量,一出作用域就被释放了,这就是本章的重点。

04 堆中创建变量的案例

现在有了这个基础后,我们来看看一些你可能会经常做的事情。

我们可能在函数中创建数组,然后返回一个 int 类型指针,我们可能会在函数中写下面这些代码。

这看起来非常合理,但是,这完全是不对的。

这样的数组并没有在堆上分配,因为没有使用 new,我们只是在栈上声明它,当返回一个指向它的指针时,它返回一个指向栈内存的指针,这个栈内存,一旦离开作用域,内存就会被清除,在使用的时候就会失败。

如果你想实现你想要的功能,有两个选择。

  • 第一,你可以在堆上分配数组,从而确保它的生存期会一直存在。
  • 第二,你可以将这里创建的数据赋值给一个在栈作用域之外存在的变量,采取传参或者其他的方式来操作数组。在这个意义上,我们只是传递一个指针,所以不会做分配这个操作。

这个错误很常见,那有没有一种方法,可以将它这个特性用于好的方面呢?

答案是肯定的,它在很多方面都非常有用,比如可以帮助我们自动化代码。

其中一件事就是作用在类的作用域,例如智能指针 smart_ptr,或是 unique_ptr,作用域指针,或者像作用域锁(scoped_lock),有很多例子,本期先不涉及。但最简单的例子可能是作用域指针。

作用域指针基本上是一个类,一个指针的包装器,在构造时用堆分配指针,然后在析构时删除指针。所以我们可以自动化这个 new 和 delete。让我们看看如何编写这样的类。

上面是一个 DemoPtr 的类,有一个 Entity 指针成员,有一个参数为 Demo指针类型的构造函数,并完成初始化,在析构函数中,调用 delete 释放成员指向的内存。

这是一个基本的作用域指针,让我们看看如何使用它。

可以看到,这里还有一个隐式转换的过程。

设置断点调试一下。

可以看到,即使我们用 new 来做堆分配,还是可以做到内存释放。

这里不好看出来,你们还是拿代码测试一下

这是一个很好的例子,其实这是 smart_ptr、unique_ptr 做的最基本的事情。

这种自动构造,自动析构这种基于栈的变量,离开作用域后会自动销毁,是非常有用的。

#include <iostream>class Demo
{
public:Demo(){std::cout << "构造函数" << std::endl;}~Demo(){std::cout << "析构函数" << std::endl;}};class DemoPrt
{
private:Demo* m_demo;
public:DemoPrt(Demo* demo) : m_demo(demo){}~DemoPrt(){delete m_demo;}
};int * demoarr()
{return NULL;
}int main()
{DemoPrt d = new Demo();return 0;
}

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

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

相关文章

防火墙补充NAT

目录 1.iptables保存规则 2.自定义链 3.NAT NAT的实现分为下面类型&#xff1a; SNAT实验操作 DNAT实验操作 1.iptables保存规则 永久保存方法一&#xff1a; iptables -save > /data/iptables_rule //输出重定向备份 iptables -restore < /data/iptables_r…

修复尺寸显示操作说明

有时我们在绘制草图/工程图中标注尺寸时会显示<MOD-DIAM>符号&#xff0c;如下图所示&#xff1a; 此符号通常在SolidWorks软件更新后会显示出来&#xff0c;Windows更新后可能也会出现此符号。 解决方法&#xff1a; 1. 打开软件-系统选项-文件位置-符号图库文件 2. 选…

MybtisPlus快速开发(从controller到mapper)

创建新项目 写好配置文件 server:port: 8905#配置MP控制台打印日志 mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplspring:datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:m…

行情分析——加密货币市场大盘走势(11.30)

大饼已经形成了底背离&#xff0c;即MACD往下走&#xff0c;而价格还在往上走&#xff0c;这种往往后续会大跌。我们继续把空单拿好&#xff0c;已经持仓的无需加仓。多次上涨都一直不能突破&#xff0c;也说明多空和空军力量都很强&#xff0c;等待后续出方向&#xff0c;而笔…

四大排序方法(java版)

四大排序方法 &#x1f4d1;前言 本文主要是【算法】——常用的排序方法的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f3…

Vue快速实践总结 · 下篇

文章目录 组件间通信方式父 --> 子通信props插槽 子 --> 父通信&#xff08;自定义事件&#xff09;任意组件通信全局事件总线消息订阅与发布 Vuex工作原理运行环境简单使用GettersmapState与mapGettersmapActions与mapMutations模块化 命名空间 VueRouter路由的作用与分…

编写算法,对n个关键字取整数值的记录序列进行整理。以使所有关键字为负值的记录排在关键字为非负值的记录之前

编写算法&#xff0c;对n个关键字取整数值的记录序列进行整理。以使所有关键字为负值的记录排在关键字为非负值的记录之前&#xff0c;要求&#xff1a; &#xff08;1&#xff09;采用顺序存储结构&#xff0c;至少使用一个记录的辅助存储空间 &#xff08;2&#xff09;算法的…

自动驾驶DCLC 功能规范

目录 1 概述Summary....................................................................................................... 4 1.1 目的Purpose....................................................................................................... 4 1.2 范围Ran…

mysql迁移到sqlserver数据库,真的超级简单

这是微软官方步骤 需要注意的地方在连接mysql时&#xff0c;如果没有驱动&#xff0c; 那记得更换SSMAforMySQL X86的版本 工具下载地址&#xff08;点击download后会弹出选项的&#xff09;&#xff1a;Download Microsoft SQL Server Migration Assistant for MySQL from Of…

数字孪生3D场景开发工具:弥补不足,开拓全新可能

随着数字化时代的来临&#xff0c;越来越多的企业和行业开始探索数字孪生技术的应用。数字孪生是指通过数字技术将现实世界中的物体、场景等复制到虚拟世界中&#xff0c;以实现实时监测、预测和优化。然而&#xff0c;在数字孪生的发展过程中&#xff0c;一些不足也逐渐浮现。…

web前端tips:js继承——寄生组合式继承

上篇文章给大家分享了 js继承中的 寄生式继承 web前端tips&#xff1a;js继承——寄生式继承 今天给大家分享一下 js 继承中的 寄生组合式继承 寄生组合式继承 寄生组合式继承是一种结合了寄生式继承和组合式继承的方式&#xff0c;它的目标是减少组合式继承中多余的调用父…

【ArcGIS Pro二次开发】(78):批量合并GDB数据库

有些GDB数据库会按分幅或行政区划进行分开储存&#xff0c;尤其是一些地形测绘或国情地理数据。 如下图所示&#xff1a; 数据是完整的&#xff0c;但使用的时候要一个一个拖进地图中&#xff0c;进行分析的时候也需要将其合并后使用。 因此就做了这个合库工具。 一、要实现的…