C++常见概念问题(3)

C++常见概念问题(3)

1. 构造函数的初始化顺序

基类构造函数:在派生类的构造函数中,基类的构造函数在派生类构造函数体执行之前调用。
成员变量初始化:类中的成员变量会按照其在类中声明的顺序进行初始化,而不是按照构造函数初始化列表中的顺序。

2. C++内存分区模型

C++分区模型 程序运行前,编译的时候:

代码区:存放函数体的二进制代码,由操作系统进行管理,特点(共享,可读)
全局区:存放全局变量静态变量以及常量,该区域的数据在程序结束后由操作系统释放

程序运行后:

栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

3. 宏定义和全局变量的区别

  1. 数据类型
    宏定义:没有特定的数据类型,因为它仅仅是文本替换,编译器在预处理阶段进行替换,不占用内存。
    全局变量:有具体的数据类型(如整型、浮点型、字符串等),并占用相应的内存空间。
  2. 定义方式
    宏定义:使用预处理指令 #define 来定义,通常用来替代常量或表达式。例子:#define PI 3.14
    全局变量:定义在函数外部
  3. 作用域
    都可以在本文件使用。
    如果跨文件,可以加入extern 关键字在其他文件中使用。

4. 全局变量,静态变量,常量

4.1 全局变量

全局变量是定义在函数外面的变量,本文件内都可以访问。如果想跨文件访问,也可以使用extern关键字实现。

int globalVar = 42;

另一个文件

extern int globalVar;

全局变量在程序运行前的编译阶段就分配了内存,直到程序完全结束,由操作系统释放。

4.2 静态变量

  • 局部静态变量:给函数内部的局部变量加上关键字static,变成函数内部的静态变量,原本局部变量的生命周期随着函数结束。变成静态变量后,生命周期改变了,作用域没有改变。

    生命周期变得和全局变量一样,持续到程序结束。由操作系统释放。
    作用域没有改变,意味着别的函数还是无法访问这个局部静态变量,只有自己函数能访问。上次执行完这个函数,局部静态变量的值会被保存下来,下次再调用这个函数时,接着使用

  • 全局静态变量:那么给全局变量加上关键字static,相比于全局变量,全局静态变量有什么改变:首先生命周期没变,作用域变了,全局静态变量只能在本文件访问到,不能通过extern关键字跨文件访问。

为什么需要静态变量:

  1. 保持状态
    静态变量能够保存函数调用之间的状态。这意味着即使函数执行结束,该变量的值仍然可以被保留并在后续调用中继续使用。

  2. 共享数据
    在类或模块内部,静态变量可以被所有实例或调用共享。这样可以避免使用全局变量,同时又能跨多个实例共享数据,减少了命名冲突的风险。

  3. 内存管理
    静态变量通常在程序的整个生命周期内存在,不需要频繁地分配和释放内存。这对于性能敏感的应用尤为重要,因为它可以减少内存分配的开销。

  4. 初始化控制
    静态变量只会在第一次使用时进行初始化,这样可以确保其初始状态一致,并且在多次调用时不会重复初始化。

  5. 封装性
    使用静态变量可以隐藏实现细节,提供更好的封装性。从外部看,静态变量可以限制访问权限,使得数据的管理更加严谨。

4.3 常量

使用 const 关键字定义的不可修改的变量。
生命周期同样是编译时分配空间,程序结束时操作系统释放资源。
常量的作用域依赖于定义的位置,可以是全局的、局部的,或在类中定义。

函数重载的底层本质

核心:C++之所以有重载,其原因是函数名修饰规则不同

对于C语言,相同的函数名,其修饰名必然相同,与参数无关,因此不同参数的同名函数的修饰名是重复的,因此无法同时存在,更无法实现重载;

C++之所以能够实现重载,是因为编译器能够区分同名不同参的函数,而其区分的底层原理就是对原函数名进行修饰,或者叫重命名

编译前的重载函数(同名不同参的函数),编译后就是不同名的函数,这个新的函数名中不仅包含了原名,还包含了参数信息;

C++和C怎么协同开发(c++项目怎么引用c的文件)

假如在c++文件中使用C的源文件example.c

// example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H#ifdef __cplusplus
extern "C" {
#endifvoid c_function();#ifdef __cplusplus
}
#endif#endif // EXAMPLE_H#ifndef EXAMPLE_H
头文件保护(Include Guard): #ifndef 是 "if not defined" 的缩写。这条指令检查宏 EXAMPLE_H 是否未被定义。如果没有被定义,则继续执行后面的代码;如果已经定义,编译器将跳过这个头文件的内容,以避免重复包含同一头文件造成的潜在错误。
#define EXAMPLE_H
定义宏: 如果上面的条件成立,这里会定义宏 EXAMPLE_H。这意味着在后续的编译过程中,如果再出现 #ifndef EXAMPLE_H,条件将不再成立,从而避免了重复包含该头文件。
#ifdef __cplusplus
检查 C++ 编译器: #ifdef 指令用于检查是否在 C++ 编译环境下编译。如果 __cplusplus 宏已定义,这表示当前正在使用 C++ 编译器。
extern "C" {
防止名称修饰: extern "C" 告诉 C++ 编译器以 C 的方式链接函数。在 C++ 中,为了支持函数重载,编译器会对函数名进行“名称修饰”。使用 extern "C" 可以避免这种情况,使得 C 函数可以在 C++ 中被正确调用。
void c_function();
函数声明: 这里声明了一个名为 c_function 的函数,其返回类型为 void,表示此函数不返回任何值。同时,没有参数列表,表示该函数接受任何数量和类型的参数。
#ifdef __cplusplus
结束 C++ 检查: 这一行与之前的 #ifdef __cplusplus 相对应,用于结束对 C++ 编译环境的检查。
}
结束 extern "C": 这个括号标志着 extern "C" 块的结束,表示在这个块内的所有函数声明都应按照 C 的方式处理,而不是 C++ 的标准方式。
#endif // EXAMPLE_H
结束头文件保护: 这一行对应于前面开始的 #ifndef EXAMPLE_H,它结束了头文件保护的定义。任何在这个保护区块内的代码都会被保证只会被编译一次。

c++迭代器,和迭代器失效的原因

定义: 迭代器类似于指针,它们指向容器中的某个元素,并且能够在容器中前后移动。通过迭代器,可以读取和修改容器中的数据。

迭代器类似于指针,它们指向容器中的某个元素,并且能够在容器中前后移动。通过迭代器,可以读取和修改容器中的数据。

迭代器失效的原因

容器大小变化:

插入(Insert): 如果在迭代器当前位置插入元素,可能会导致所有指向该位置及之后元素的迭代器失效。
删除(Erase): 删除迭代器指向的元素会使该迭代器失效,通常还会影响其他迭代器(例如,删除某个位置之前的元素会使之后的迭代器失效)。
调整容器大小: 对于动态大小的容器(如 std::vector),如果超过了其容量,可能会重新分配内存,这会使所有指向旧内存的迭代器失效。

容器清空:
调用 clear() 函数清空容器时,所有与该容器相关的迭代器都会失效。
直接赋值或重新初始化:

例如,将一个容器赋值给另一个新容器后,之前的迭代器指向的内容可能会失效。

resize和reserve方法

resize()
resize 会 改变容器的大小。它的作用是调整容器的当前大小,即容器内元素的个数。
如果新大小大于当前大小:会增加容器的元素数量,并且这些新增的元素会根据容器的类型进行默认初始化。例如,对std::vector,新增加的元素将会是 0。
如果新大小小于当前大小:会删除容器中的多余元素,容器的大小会减少。

reserve()
reserve 不会改变容器的大小,它只是调整容器的 容量(capacity),即指定一个容器最大容量。
如果目前内存能够放下这个最大容量的数据结构,没变化。
如果目前内存放不下最大容量的数据结构,扩容为指定容量的内存大小。

使用reserve 主要是为了优化性能,避免在插入新元素时频繁重新分配内存
假设 std::vector 中的每个元素占据 4 字节(在大多数平台和编译器中,int 通常是 4 字节),那么在调用 reserve(100) 后,std::vector 会为 100 个 int 元素分配内存空间,大小大约是 100 * sizeof(int)

重新设置容器大小

  • reserve() 用于请求容器在内部分配至少 n个元素的存储空间,以减少动态分配的次数。它不会改变容器的大小,只是调整内部容量(capacity)。
  • resize() 用于改变容器的大小到 n。如果新大小大于当前大小,容器会增加元素,新增的元素会被初始化为零或默认值;如果新大小小于当前大小,超出部分的元素会被删除

内存方面

  • reserve(): 只影响容量(capacity),不会导致元素的复制或移动,不会改变当前存储的元素。
  • resize(): 会改变大小(size),可能会导致元素的移动和复制,特别是在增加元素时。

lambda表达式

在这里插入图片描述
捕获列表:决定 lambda 可以访问哪些外部变量,以及以何种方式访问。
参数列表:与普通函数一致,定义 lambda 接受的输入参数。
返回类型:可选,如果不写,编译器会自动推导。
函数体:包含具体的逻辑实现。

  • capture(捕获列表): 用于指定lambda表达式可以访问的外部变量。捕获列表的常见方式包括:
    值捕获:[x]:将变量 x 的值复制到 lambda 内部。
    引用捕获:[&x]:将变量 x 的引用传入 lambda,允许在 lambda 内部修改原始变量。
    捕获所有(值):[=]:将所有外部变量按值捕获。
    捕获所有(引用):[&]:将所有外部变量按引用捕获。
    混合捕获:[=, &x]:将所有外部变量按值捕获,但 x 按引用捕获。
  • parameters(参数列表): 与普通函数的参数列表相同,定义lambda接受的输入参数,可以为空。
  • return_type(返回类型,可选): 用于指定返回值的类型。如果不写,编译器会根据函数体自动推导返回类型。
  • function body(函数体):包含实际的代码逻辑。

在C++中,lambda表达式(或称为匿名函数)是一种用于定义可调用对象(如函数对象)的语法糖。它的本质是提供一种方便的方式来创建和使用小的、临时的函数对象。lambda表达式可以被用作参数传递给函数,或者作为返回值。

当你在代码中使用 lambda 表达式时,编译器会将其转换为一个具体的类型,通常是一个匿名的、唯一的类。这使得 lambda 表达式不仅可以被调用,还可以携带状态(即捕获的变量)。

即使它们的结构相同,但由于捕获的变量不同,编译器会生成不同的类。
成员函数 operator():编译器会为这个类定义一个 operator() 方法,使得你可以像调用普通函数一样调用这个 lambda。

指针和引用的区别

指针(Pointer):指针是一个变量,它存储另一个变量的内存地址。通过指针可以间接访问或修改指针指向的变量。
引用(Reference):引用是某个已有变量的别名,引用在创建时必须初始化,并且不能再改变指向其他变量。一旦绑定到某个变量,引用就无法指向其他变量。

解释性语言和编译性语言区别

解释性语言:
Python,JavaScript,Ruby
编译性语言:
C,C++,Rust

解释性语言:
即时执行代码是逐行解释执行的,通常没有生成独立的可执行文件。解释器在运行时读取源代码,逐行解析并执行。
平台无关性:只要有合适的解释器,可以在任何平台上运行同样的源代码。

编译性语言:
先编译后执行代码首先通过编译器转换为机器语言(或中间代码),生成一个独立的可执行文件,之后运行时直接执行该可执行文件。
性能较高:编译后的代码是机器可直接执行的,通常比解释型语言更高效。

内存使用
解释性语言:由于代码是在执行时逐行解释,通常需要更多的内存来加载解释器和源代码。
编译性语言:编译后的程序是机器代码,执行时不需要加载解释器,所以内存使用通常更高效。
修改后的行为
解释性语言:修改代码后可以直接运行,修改和测试的周期较短。
编译性语言:修改后需要重新编译才能运行,因此调试周期较长。
调试与错误处理
解释性语言:错误通常在运行时发生,调试过程比较灵活,但可能遇到更难以追踪的错误(如运行时错误)。
编译性语言:错误通常在编译阶段被发现,错误信息相对较为清晰,有助于尽早发现问题。

Java
Java 既不是单纯的解释性语言,也不是单纯的编译性语言,它是两者的结合,采用了 编译+解释 的执行方式。具体来说,Java 采用了一种编译和解释相结合的机制,这种机制被称为 “编译-解释混合模式”。以下是更详细的说明:

Java 的执行流程
源代码编译:

当你编写 Java 程序时,首先会使用 Java 编译器(如 javac)将 .java 源代码编译成 字节码(即 .class 文件)。这个过程就是编译。
字节码是一种中间语言,它与平台无关,不是机器码,而是Java虚拟机(JVM)可以理解的指令。
字节码的执行:

生成的字节码不会直接运行在硬件上,而是由 Java 虚拟机(JVM) 来解释执行。
JVM 会将字节码 解释 成具体平台的机器码,然后在该平台上执行。这个过程类似于解释性语言的运行机制。
在现代 JVM 中,还引入了 即时编译(JIT,Just-In-Time compilation) 技术。JIT 会在运行时将热点字节码(即被频繁执行的部分)动态编译成机器码,以提高程序的执行效率。

C++内存泄漏可能的原因

内存泄漏是指程序在申请内存后,未能正确释放,导致系统可用内存逐渐减少,最终可能引发程序崩溃或系统性能下降。

  1. 程序员在动态分配内存后,忘记使用 delete 或 delete[](对于数组)来释放内存。

  2. 构造函数中使用裸指针(int*, char* 等)来分配内存,但没有适当的内存管理机制(如智能指针),那么如果在析构函数没有释放内存的话,会造成内存泄漏。

  3. 异常导致的提前返回
    当程序出现异常时,如果在栈展开过程中有动态分配的内存没有被释放,就会发生内存泄漏。特别是在没有使用 try-catch 块的情况下,异常会导致提前退出函数,而此时未释放的内存会被遗弃。

  4. 智能指针循环引用

  5. 内存管理不当的多线程问题
    在多线程程序中,如果一个线程分配了内存,而另一个线程错误地释放了它,或者某个线程没有适当地释放内存,就会出现内存泄漏。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Tofu识别跟踪产品视频输入接口汇总

网络视频输入 视频输入默认支持网络RTSP协议视频,分辨率支持480P,720P,1080P,1440P等格式。 目前仅Tofu3支持1440P格式的400万像素视频,可通过升级包支持,400万像素分辨率目前仅支持25601440。 并行数字视…

【WRF工具】WRF 模型输出可视化工具 RIP4

【WRF工具】WRF 模型输出可视化工具 RIP4 1 可视化工具 RIP4 概述1.1 RIP4 的典型工作流程 2 安装 RIP42.1 下载 RIP42.2 编译 RIP4 3 运行 RIP43.1 准备输入文件3.2 运行 RIP 数据预处理程序(ripdp_wrfarw)3.3 运行 RIP 绘图程序(rip&#x…

在Pybullet中加载Cinema4D创建的物体

首先明确我们的目标,是希望在cinema4D中创建自己想要的模型,并生成.obj文件,然后在pybullet中加载.obj文件作为静态物体,可以用于抓取物体,避障物体。(本文提到的方法只能实现静态物体的建模,如…

学习方法——看差的书籍

CSAPP证明从1中午2点到下午3:40,但是还是只是推理证明而已,但是想起来了课上老师讲的东西了 还可以知道补码运算的大前提知识!!!

微积分复习笔记 Calculus Volume 1 - 5.2 The Definite Integral

5.2 The Definite Integral - Calculus Volume 1 | OpenStax

SNH48 GROUP燃动杭州 第五届偶像运动会落下帷幕

2024年11月9日,“我们能赢”SNH48 GROUP第五届偶像运动会在杭州运河体育公园盛大开赛,本次运动会由高榕及SNH48郭爽、GNZ48梁娇作为赛事特邀主持人,并于小红书、微信视频号、SNH48 GROUP官方直播APP口袋48及MEET48全程直播。SNH48&#xff08…

35.安卓逆向-壳-Frida脚本脱壳

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于:图灵Python学院 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要盲目相信。第一…

未来已来!量子计算能否让你成为智商最高的人?

内容概要 在当今时代,量子计算不仅是科技领域的创新标志,更是一个重新定义智能边界的革命性力量。它的运算能力远超传统计算机,这使得复杂的问题在瞬息之间迎刃而解。通过量子比特(qubits),信息呈现出一种…

Java爬虫:京东商品SKU信息的“偷心盗贼”

在这个信息爆炸的时代,数据就像是藏在深山里的宝藏,等待着我们这些“数据探险家”去发掘。今天,我们要化身为一名“偷心盗贼”,用Java这把锋利的“剑”,精准地从京东的海洋中窃取商品的SKU信息。别担心,我们…

RWKV 社区 10 月动态速览!

欢迎大家收看《RWKV 社区最新动态》,本期内容收录了 RWKV 社区 2024 年 10 月的最新动态。 10 月动态省流版(TL;DR) RWKV 社区活动 10 月 13 日,RWKV 团队在北京大学做了《RWKV 技术产品化与生态及模型架构》主题分享 RWKV 学术…

C++:模板之全

目录 前言: 一、函数模板 1.函数模板的原理: 2.函数模板的实例化 2.1.隐式实例化 2.2.显示实例化 2.3.模板参数匹配原则 二、类模板 1.类模板的实例化 三、非类型模板参数 四、模板的特化 五、模板的分离编译 前言: 在C语言中是…

嵌入式linux系统中I2C控制实现AP3216C传感器方法

大家好,今天主要给大家分享一下,如何使用linux系统里面的I2C进行控制实现。 第一:Linux系统中I2C简介 Linux 内核开发者为了让驱动开发工程师在内核中方便的添加自己的 I2C 设备驱动程序,更容易的在 linux 下驱动自己的 I2C 接口硬件,进而引入了 I2C 总线框架。与 Linux 下…

PyQt5超详细教程终篇

PyQt5超详细教程 前言 接: [【Python篇】PyQt5 超详细教程——由入门到精通(序篇)](【Python篇】PyQt5 超详细教程——由入门到精通(序篇)-CSDN博客) 建议把代码复制到pycahrm等IDE上面看实际效果,方便理…

YOLOv11(Ultralytics)可视化界面ui设计,基于pyqt5,单文件即插即用,支持文件夹检测及云摄像头检测并保存

本文的可视化界面对于YOLOv11/Ultralytics/YOLOv8的检测、分割、分类、姿势估算(detection, segmentation, obb, classification, and pose estimation)等均可正常显示。本次新增了图片及视频的保存,可以选择传入文件夹进行检测并显示&#x…

colmap软件用法

文档地址:Tutorial — COLMAP 3.11.0.dev0 documentation background: Structure-from-Motion 分为三个阶段(colmao软件也是按这个阶段进行划分解耦的): Feature detection and extraction Feature matching and geometric verification …

uniapp使用里image标签图片无法撑满全屏问题,uniapp image填充不满父容器解决方案

问题效果 底部有一个白条,查看元素之后也没有padding也没有margin 解决方案 vertical-align: bottom;解决后效果图

嵌入式开发系列----入门保姆级必看博客

嵌入式开发是指为特定的硬件平台编写软件的过程,通常涉及硬件资源有限、实时性要求高的应用。嵌入式系统广泛应用于消费电子、工业自动化、汽车、医疗设备等领域。本文将介绍嵌入式开发的基础内容,包括硬件和软件的构成、开发工具链、常用的编程语言以及…

计算机网络(4)

同轴电缆 由一根空心的外圆柱导体和一根位于中心轴线的内导线组成,内导线和圆柱 导体及外界之间用绝缘材料隔开,按直径的不同,同轴电缆分为粗缆和细缆 两种 与双绞线相比,同轴电缆的抗干扰能力强,屏蔽性好,…

Cesium基础-(Entity)-(label )

里边包含Vue、React框架代码详细步骤、以及代码详细解释 Label 在 Cesium 中表示一个可以在三维地球上显示的文本标签。它通常用于在特定位置显示信息,比如地名、地标名称或其他注释。Label 可以自定义样式、颜色、大小,并能根据距离视角动态调整显示效果。 以下是 Label 的…