2023届C/C++软件开发工程师校招面试常问知识点复盘Part 7

news/2024/5/20 12:31:40/文章来源:https://blog.csdn.net/qq_40459977/article/details/127519181

目录

          • 46、C++类的成员变量初始化顺序及拓展
          • 47、强制转换类型操作符号
          • 48、const 成员函数–常成员函数与常量对象
          • 49、volatile关键字
          • 50、赫夫曼树
          • 51、前缀树

46、C++类的成员变量初始化顺序及拓展

注意:

1、const成员或者引用必须在成员变量初始化列表中初始化,不可以在构造函数中初始化

  • 为什么呢?因为在构造函数中的“初始化”,其实不是初始化而是赋值,而const变量不允许被复制
  • 引用本质上又是const 指针,与上述同理

2、static成员变量必须在类的外部初始化

3、没有默认构造函数的自定义类型必须在初始化列表中初始化,原因在本小节最后


初始化顺序要点:

1、类的成员变量的初始化顺序与类定义中的成员变量的顺序有关,与初始化列表中的顺序无关

  • 也即类定义中如果先定义变量int a;后定义变量int b;那么在程序编译阶段就已经决定的成员变量的存放次序了,因此肯定时首先初始化a,然后初始化b

2、如果类的成员变量在构造函数中初始化,那么将会按照在构造函数中的顺序进行初始化


如果带上静态变量的话,其初始化顺序为:

1、基类的静态变量或全局变量

2、派生类的静态变量或全局变量

3、基类的成员变量

4、派生类的成员变量


成员初始化列表与构造函数内进行初始化的区别?

  1. 类的成员变量其实在进入构造函数的函数体内前就已经完成了初始化;如果有成员初始化列表(称为显式的初始化)就会按照列表进行初始化,如果没有显式的初始化也即没有初始化列表,那么编译器将使用默认的构造函数进行初始化。

  2. 因此构造函数内的初始化本质上是一种赋值

  3. 对于内置类型、引用和指针,两种方式在效率和结果上是一样的

  4. 最大的区别在于自定义类型成员变量

    • 如果使用初始化列表来初始化自定义类型,只会发生一次构造,比如拷贝构造,这在进入构造函数前就已经完成
    • 如果没有使用初始化列表,在进入构造函数之前,编译器调用默认的构造函数先初始化一次成员变量,这是第一次构造;然后进入构造函数内部又一次赋值,这就产生了第二次构造,这在效率上是低下的。因为发生了两次构造,因此推荐对自定义类型使用初始化成员列表进行初始化

另一个问题,如果自定义类型的成员变量没有默认构造函数,那么必须在初始化列表中显式的初始化,否则就会因为编译器找不到默认构造函数而报错

47、强制转换类型操作符号
  • 强制转换类型
  1. static_cast<newType>(data)

    • 一般用于良性转换

    • 原有的不会发生什么意外的转换:int–>double、short–>int、const–>非const、向上转换(派生类到基类的转换)

    • void*–>int*int*–>void*类似这种void指针与具体类型之间的转换

    • 存在转换构造函数类型转换函数的类与其他类型之间的转换:double与Complex的转换

      不可以用于无关类型之间的转换,因为无关类型之间的转换是有风险的

      比如:具体类型的之间的转换、整数到指针的转换

  2. const_cast<newType>(data)

    • 一般用于将const/volatile特性抹去,也即将const/volatile类型变成非const/volatile类型

    • 通常情况下是newType是指针类型,也即将将一个指向const类型的指针,转换为一个指向非const类型的指针

      const int n = 15;
      int * p = const_cast<int*>(&n);
      // 注意:&n的类型是const int*   p的类型是int* 
      
  3. reinterpret_cast<newType>(data)

    • 是一种简单粗暴的转换方式,或者说并没有基于转换规则进行转换,而是直接在二进制层面的重新解释

    • 可以做一些正常的转换不被允许的转换:如把一个不同类型的指针的相互转换一个类的指针转换为一个普通类型的指针

  4. dynamic_cast<newType>(data)

    • 是用于在类的继承关系之间进行转换的,既可以向上转换(派生类–>基类),又可以向下转换(基类—>派生类)

      • 向上转换是无条件的,因为一定会成功,等价与static_cast<>()
      • 向下转换是有条件的,必须是安全的,要借助RTTI进行检测,因此只有部分会成功
    • 该转换要求转换双方必须是指针引用

      • 对于指针,转换失败将返回NULL
      • 对于引用,转换失败将返回std::bad_cast异常
    • dynamic_cast的工作过程:

      1. 在程序的运行过程中会遍历继承链,如果途中遇到了要转换的目标类型,那么就能够转换成功
      2. 如果直到继承链的最顶端也即最顶端的基类时依旧没有找到要转换的目标类型,那么就转换失败了
    • 在本质上,dynamic_cast还是只能向上转换。而所谓的基类转换到派生类,只不过是用不同的基类指针指向一个派生类而已

      : class A--->class B--->class C--->class DA* pa = new D();B* pb;C* pc;D* pd;pb = dynamic_cast<B*>(pa);  // pa 向下转换为pb  不过这些都是表现  本质就是不同的基类指针指向派生类而已pc = dynamic_cast<C*>(pa);pd = dynamic_cast<D*>(pa);
      

int 转换为 float 有损失精度的风险, int转double 没有损失精度的风险

int和float一般都是32位存储的,但是存储格式有不同之处

  1. int: 首位是符号位,1~31位都用来表示数据,因此int所能表示数据范围是−231-2^{31}231–>231−12^{31}-12311,也即-2,147,483,648 到 2,147,483,647

  2. float: 从高位到低位依次是符号阶码尾数(1 ,8,23),因此指数最大部分也只是23次方

  • 综合上述的① 和 ②, 我们发现int所能表示的数据的范围要比float更大,因此int转为float时有发生损失精度的风险; 而将int转换成double,64位,高位到低位依次是符号阶码尾数(1,11,52)时不会损失精度,因为double的指数部分最高有52次方。
 cout << "测试 int ---> float的转换\n";int MAX = INT_MAX; // 设置int为最大值float MAXf = (float)MAX;cout << MAX << " " << setprecision(9) << MAXf << endl;cout << (int)MAXf << endl; // -2147483648 与 INT_MAX已经不一样了,说明转换到float后精度损失了double MAXd = (double)MAX;cout << MAX << " " << setprecision(9) << MAXd << endl;cout << (int)MAXd << endl; // 2147483647 == INT_MAX 说明没有发生精度损失
48、const 成员函数–常成员函数与常量对象
  • 常量对象:
const Obj;
  1. 给某个对象加上const进行修饰,表示Obj在初始化之后就不希望对其普通的成员变量进行修改
  2. 常量对象的修饰对静态成员变量起作用吗?答:不起作用,因为const此时修饰的是这个实例化对象,并不是修饰的类,然后静态成员变量属于这个类而不属于这个对象。
  • 常量对象不能调用普通成员函数可以调用常量成员函数静态成员函数
  1. 因为普通成员函数在执行过程中有可能修改对象的成员
  2. 常量成员函数则可以保证不会修改对象的成员,因此可以调用常量成员函数
  3. 静态成员函数独立于类的对象,属于类,因此不能访问非静态的成员变量,自然也就没有修改非静态成员变量的可能,因此可以被常量对象调用
  • 常量成员函数—如下int getAValue() const
class A{int a;
public:A() : a(0) {}int getAValue() const{return a; }
};
  1. 函数后方的const表明这是一个常量成员函数
  2. 常量成员函数不会改变对象的成员变量
  3. 常量成员函数主要被常量对象所调用
  4. 如果编译器发现常量成员函数内部有修改非静态成员变量的行为就会报错,而内部修改静态成员变量是不会报错的
    • 因为类的静态变量并不受const对象约束
#include <iostream>
using namespace std;class Test
{
public:static int a;int b;public:Test() : b(0) {}// 常量成员函数void changeStatic(int _a) const{a = _a; // 此处在const成员函数修改了静态变量,允许。// b++;    // 修改非静态成员变量,不允许}// 普通函数void changeB(){b++;}static void setStatic(){a--;}
};
int Test::a = 100; //静态类成员的类外初始化int main()
{Test t;cout << t.b << " " << t.a << endl;const Test conT;conT.changeStatic(111); // 常量对象调用常量成员函数// conT.changeB();         // 常量对象不允许调用普通成员函数cout << conT.a << endl;conT.setStatic(); // 常量对象可以调用静态函数return 0;
}
  • 关于重载:const修饰可以作为函数重载的区分标志
    • 常量对象调用const版本
    • 非常量对象调用普通版本
  • 如果一个函数不会修改类的成员变量,可以将其定义为const类型,这是一个好习惯
    • 因为普通对象和const对象都可以调用const成员函数
49、volatile关键字

声明语法:int volatile vInt;

  • 当使用该关键字进行声明时,系统总是会重新的从他所在的内存读取数据,即使他前面的指令刚刚从该处读过数据;
  • volatile用于告知编译器该变量是随时可变的,因此禁止了编译器的优化,让程序每次都去读取最新的数据
    • (有时候编译器会根据代码结构进行优化,而不去读取寄存器或内存位置的最新数据)

一般来讲volatile的使用场景为:

  1. 中断服务程序中修改的供其他程序检测的变量需要加volatile
  2. 多任务环境下各个任务间共享的标志或数据应该加volatile
  3. 存储器映射的硬件寄存器通常也需要加volatile说明,因此每次对他的读写都可能意义不同

volatile也可以像const一样修饰指针指向的变量

const int* pInt;   // 表示pInt指向的数据是一个常量
volatile int* pVInt; // 表示pVInt指向的数据是一个易变的量// ---------------------------------------------int* const pInt;		// 表示指针pInt本身是不可变的
int* volatile pVInt; 	// 表示指针pVInt本身是易变的

该关键字指定了某个对象的存储位置在内存中,而不是在寄存器中。因为一般的对象编译器可能会将其拷贝到寄存器中,用以加快指令的执行速度。

主要就是要让编译器每次操作该变量时一定真正从内存中取出,而不是使用已经在寄存器中的值

50、赫夫曼树

赫夫曼树的相关概念:

  1. 路径长度:从一个结点到另一个结点之间的分支构成两个结点之间的路径,路径上分支的数目称为路径长度
  2. 树的路径长度:从树根到每一个结点的路径长度之和
    • 注意是每个结点,而不是每个叶子结点
  3. 结点的带权路径长度:从该结点到树根之间的路径长度结点权重的乘积
  4. 树的带权路径长度:树中所有叶子结点的带权路径长度之和

赫夫曼树的概念:

带权路径长度最小的二叉树称为赫夫曼树

赫夫曼树的构造:———以A(5)、B(15)、C(40)、D(30)、E(10)为例

  1. 先将有权值的叶子结点升序排列
    • {A(5)、E(10)、B(15)、D(30)、C(40)}
  2. 取权值最小的两个结点A(5)、E(10),作为一个新节点N1的子节点
    • 相对较小A(5)的作为左孩子,较大的E(10)作为右孩子
    • 删除序列中两个结点A(5)、E(10)
    • N1(15)添加到序列中,依旧按照升序排列,N1的权值等于左右孩子权值之和
      • {N1(15)、B(15)、D(30)、C(40)}
  3. 再次取权值最小的两个结点N1(15)、B(15),作为一个新节点N2的子节点
    • N1(15)和B(15) 权值一样,因此左右孩子均可
    • 删除序列中的两个结点N1(15)、B(15)
    • 将N2(30)添加到序列中,升序排列,N2的权值等于左右孩子权值之和
      • {N2(30)、D(30)、C(40)}
  4. 重复第2步
    • N3(60)有两个子节点N2和D
      • {C(40)、N3(60)}
  5. 重复第2步
    • N4(100)有两个子节点C(40)N3(60)
      • 此时序列中仅有一个根节点{N4(100)}
  6. 构造完成
			     N4(100)/     \C(40)   N3(60)/     \N2(30)  D(30)/     \N1(15)   B(15)/    \A(5)  E(10)

哈夫曼树的特点

1、满二叉树不一定是哈夫曼树

2、哈夫曼树并不唯一,因为有可能存在带权最短路径一样的树,此时也就有了多个哈夫曼树

3、权重较大的叶子节点更靠近根,权重较小的叶子节点距离根可以稍微远一些

4、哈夫曼树的结点的度要么为0,要么为2,没有度为1的结点

5、包含n个结点的森林需要经过n-1次合并,才能形成一颗赫夫曼树,此时共有2n - 1个结点,因此新增加了n - 1个结点

哈夫曼编码

  • 哈夫曼编码最初的设计主要用于长距离通信中的数据压缩问题
  • 因为不管是中文还是英文,不同的字符的使用频率是不一样的,有些使用非常频繁,有些则使用的次数较少;因此如果在编码过程中如果可以将使用频繁的字符用更短的编码,使用次数少的字符可以使用较长的编码,就可以进行数据的压缩,此时就用到了哈夫曼树,其中字符的使用频率就是其权重
  • 每一个字符的编码结果位于叶子结点

哈夫曼编码定义

一般的,设需要编码的字符集为{d1, d2, ··· , dn},每个字符使用的频率集合为{w1, w2, ···, wn},以d1, d2, ··· , dn作为叶子结点,以w1, w2, ···, wn作为叶子的权重,来构造一棵赫夫曼树。

规定赫夫曼树的左分支为代表0右分支代表1,则从根节点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符编码,这就是赫夫曼编码

1、赫夫曼编码是特殊的前缀码,因为只有叶子结点才代表某个字符

2、赫夫曼编码的每一个字符编码都不是其他编码的前缀

51、前缀树
  • 又称为字典树,是一种有序树,用于保存关联数组,其中的键通常就是字符串
  • 前缀树的键并不是直接存储在结点中,而是由结点在树中的位置所决定的
  • 一个结点的所有子孙都用着相同的前缀,这个前缀就是当前结点对应的字符串
  • 根节点对应空字符串
  • 并不是所有的结点都有对应的值,只有叶子结点和部分内部结点所对应的键才有相关的值

在这里插入图片描述

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

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

相关文章

git的基础指令操作

git的下载地址&#xff1a;https://git-scm.com/download 安装好git后 在桌面上右键即可以看到两个git的快捷方式。 需要先对git进行基本的配置&#xff0c;即需要配置用户名和用户邮箱 1. 打开Git Bash 2. 设置用户信息 git confifig --global user.name “zqy” git confi…

权限项目 1_搭建环境

硅谷通用权限系统&#xff1a;搭建环境 一、项目介绍 1、介绍 权限管理是所有后台系统都会涉及的一个重要组成部分&#xff0c;而权限管理的核心流程是相似的&#xff0c;如果每个后台单独开发一套权限管理系统&#xff0c;就是重复造轮子&#xff0c;是人力的极大浪费&…

第 1 章之:二叉树特性

声明&#xff1a;文章为博主原创&#xff0c;转载请联系博主。文章若有错误和疏漏之处&#xff0c;还望大家不吝赐教&#xff01; 第一章&#xff1a;数据结构与算法基础--------------------------- 本章重点内容为&#xff1…

基于麻雀算法二维oust图像分割算法研究附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

期刊|认知科学领域期刊《Trends in Cognitive Sciences》

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是Ns&#xff5e; 今天我们介绍的是爱思维尔(Elsevier)旗下细胞出版社&#xff08;cell press&#xff09;发行的关于认知科学领域的期刊&#xff1a;Trends in Cognitive Sciences。 1 期刊简介 基本…

mysql之给字符串加索引

文章目录前言长字段加索引前缀索引对覆盖索引的影响合理的使用前缀索引总结前言 之前的文章介绍了主键索引和唯一索引的区别&#xff0c;也介绍了主键索引和唯一索引在不同业务场景下的区别。今天我们继续介绍&#xff0c;普通索引怎么合理的使用。 长字段加索引 这里我们就…

Spring6.0全新发布,快来看看

Spring6.0全新发布&#xff0c;快来看看 Spring Framework 6.0 发布了首个 RC 版本。 翻译后页面(有点好笑)&#xff1a; On behalf of the team and everyone who has contributed, I am pleased to announce that Spring Framework is available now.6.0.0-RC2 Spring Frame…

零信任如何给为企业的数字资源保驾护航?

零信任安全最早由著名研究机构Forrester的首席分析师约翰.金德维格在2010年提出。 零信任安全针对传统边界安全架构思想进行了重新评估和审视&#xff0c;并对安全架构思路给出了新的建议。 零信任模型是什么 零信任是一种基于严格身份验证的网络安全架构。、 在该架构下&am…

【SpringBoot笔记12】SpringBoot框架实现文件上传和文件下载

这篇文章&#xff0c;主要介绍如何使用SpringBoot框架实现文件上传和文件下载。 目录 一、SpringBoot文件上传 1.1、引入依赖 1.2、编写文件上传页面 1.3、编写文件上传代码 &#xff08;1&#xff09;MultipartFile对象 &#xff08;2&#xff09;ResourceUtils工具类 …

音频拼接在一起怎么做?这篇文章来告诉你

随着互联网的发展&#xff0c;很多优质歌曲都纷纷地呈现在大家眼前&#xff0c;而将不同的音乐合并在一起&#xff0c;并且放入视频里&#xff0c;也是别有一番风味&#xff0c;那么许多人会好奇音频如何拼接在一起呢?下面就为大家分享两个好用的方法&#xff0c;只要一点时间…

【C++】使用对象自动管理指针(用到运算符重载)

文章目录1. 首先设计整型类&#xff1a;class Int普通指针2. 设计一个Object类&#xff0c;并设计Int类型的指针。那如何获取Int类型的值呢&#xff1f;1. 首先设计整型类&#xff1a;class Int class Int { private:int value; public:Int(int x 0) :value(x){cout <<…

Springbootg整合validation整合

坚持年年写博客&#xff0c;不能断了&#xff0c;所以粘贴平时写的一份笔记吧 一、简介 校验参数在以前基本都是使用大量的if/else&#xff0c;稍微方便一点的可以使用反射自定义注解的形式&#xff0c;但是复用性不是很好&#xff0c;并且每个人对于的自定义注解有着自己的使…

Java基础-任务执行服务

今天小编带领大家一起来探索Java中的任务执行服务 关于任务执行服务&#xff0c;我们介绍了&#xff1a; 任务执行服务的基本概念。 主要实现方式&#xff1a;线程池。 定时任务。 &#xff08;1&#xff09;基本概念 任务执行服务大大简化了执行异步任务所需的开发&…

算法 - 最少交换次数来组合所有的 1 II

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 2134. 最少交换次数来组合所有的 1 II - 力扣&#xff08;LeetCode&#xff09; 题目描述 交换定义为选中一个数组中的两个 互不相同 的位置并交换二者的值。 环形数组是一个数组&#xff0c;可以认为 第…

第五章:乱序执行

1.概念 指令在执行时常常因为一些限制而等待。例如&#xff0c;MEM单元访问的数据不在cache中,需要从外部存储器中取&#xff0c;这个过程通常需要几十、几百个Cycle&#xff0c;如果是顺序执行的内核,后面的指令都要等待&#xff0c;而如果处理器足够智能&#xff0c;就可以先…

修改数组(秋季每日一题 31)

给定一个长度为 nnn 的正整数数组 a1,a2,…,ana_1,a_2,…,a_na1​,a2​,…,an​。 你可以任意改变其中任意元素的值。 但是&#xff0c;改变后的元素的值仍需是正整数。 将一个元素的值从 aaa 变为 bbb 所需要付出的代价为 ∣a−b∣|a−b|∣a−b∣。 对于一个正整数 ttt&am…

Elasticsearch 查询详解

1 数据准备 PUT student_index {"settings": {"number_of_shards": 1,"number_of_replicas": 0},"mappings": {"properties": {"birthday": {"type": "date","format": "yyy…

AcmHelper -运行在本地的Acm帮手

AcmHelper 详见github 本地环境下的 Polygon , 但不止于 Polygon. 你可以 快速创建具有合理结构的题目文件夹指定 std , checker , validator , interactor使用不同语言完成不同部分 (cpp/py)使用额外的程序来测试数据的质量使用预制的数据生成器快速生成具有某些特征的数据…

Python爬虫|采集开源众包的悬赏任务,自动翻页

前言 现在互联网,有很多网站提供一些接单外派的形式,提供给有能力的人或者团队去接单。比如说,很多人熟悉的猪八戒,程序员客栈,CODING 码市,开源众包等等平台,相信很多同学也都知道。 如果要第一时间了解某个接单平台发布的第一手悬赏任务,选择爬虫也是非常不错的选择…

【路径规划】一种考虑COLREGs人工势场的船舶运动规划算法研究附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …