C++学习笔记04-补充知识点(问题-解答自查版)
前言
以下问题以Q&A形式记录,基本上都是笔者在初学一轮后,掌握不牢或者频繁忘记的点
Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系,也适合做查漏补缺和复盘。
本文对读者可以用作自查,答案在后面,需要时自行对照。
问题集
Q1:C++中是否有相关设计,类似实现“接口”的功能?
Q2:当我们在Class A类中定义了一个纯虚函数的时候,A类还可以被实例化吗?为什么?
Q3:我们如何合规地使用C++中的“接口”?
Q4:虚函数中 virtual 关键字对程序起到的实质性的作用?(注意区分虚继承)
Q5:override的书写,在函数多态中可有可无?
Q6:虚函数有哪些额外的开销?运行时成本?
Q8:release和debug版本的具体区别?
Q9:C++中默认的隐式转换可以做几次?
Q10:explicit关键字是用来干什么的?
Q11:以下4句语句的 Animal a 对象,各自分配在堆还是栈上?
在栈和堆上,各自作用域/生存周期有何不同?
Q12:作用域内变量自动销毁的特性,若想利用,可以有什么好的实践?(看看即可)
Q13:对于unique_ptr,为什么不能通过以下语法实现复制?
Q14:对于智能指针,为什么不建议通过new对象获得指针,而是使用 std::make_xx<> 方法?
Q15:关于强制转换,C风格的方法和C++风格的方法有何不同?
Q16:static_cast、dynamic_cast、const_cast 分别最主要用在什么地方?
Q17:dynamic_cast
比 static_cast
慢,为什么?
参考解答
Q1:C++中是否有相关设计,类似实现“接口”的功能?
A1:纯虚函数(pure virtual function),与java和C#中的接口定义比较接近
定义一个没有实现的函数,强制要求继承所属类的派生类去实现它。
其格式是 virtual ... func() = 0;
class Animal{
public:int age = 10;static const int sex = 1;virtual void speak() = 0; // 声明纯虚函数
};
Q2:当我们在Class A类中定义了一个纯虚函数的时候,A类还可以被实例化吗?为什么?
A2:不可以,A类的纯虚函数被要求必须指定一个子类,去实现其接口。(重写这个当做“蓝图”的纯虚函数)
以上文中的例子,
Animal *a = new Animal; // 是不合法的语法
不能实例化含有纯虚函数的类对象的原因有以下几点:
-
接口不完整:含有纯虚函数的类定义了一个不完整的接口。这意味着基类本身并没有提供足够的实现细节来创建一个完整的对象。
-
强制实现:纯虚函数的存在是为了确保所有派生类都实现了这些函数。如果允许实例化含有纯虚函数的类,那么这种强制实现的约束就被破坏了。
Q3:我们如何合规地使用C++中的“接口”?
A3:谨慎地多重继承。实际上很多其他语言都有关键字interface,但是C++只有class
主要的代码可以是:
class InterFace{ ... 内有一个纯虚函数 }
...
class 需要这个接口的类 :public Base, InterFace{ ... 之后在这里重写纯虚函数 }
Q4:虚函数中 virtual 关键字对程序起到的实质性的作用?(注意区分虚继承)
A4:告诉编译器:“Hey,为我的这个函数创造一个vftable吧”。
如果这个函数被重写且被子类对象调用了,就按照重写(虚函数指针所指)的函数执行。
Q5:override的书写,在函数多态中可有可无?
A5:雀食可以省略,但是还是建议养成好习惯,坚持写上去增加可读性
class Cat : public Animal{void speak() override {cout << "Cat-speak" << endl;}
};
Q6:虚函数有哪些额外的开销?运行时成本?
A6:1)我们需要额外的内存来存储v表,以分配到正确的函数,这会在Base类中多一个 *vfptr 的指针
2)在调用时,我们需要遍历这个表,来确定要映射到哪个函数
一般来说开销不会特别大,所以尽管用
Q7:我们在VS上写一个 "HelloWorld" 的时候,这个"HelloWorld"默认格式是string吗?
A7:不是诶,是 const char [ ] 类型!
在C++中,字符串字面量(如 `"Hello, World!"`)会被编译器识别为 const char[] 类型,但是当使用标准库中的输入输出流时,如`std::cout`,它会隐式地将const char*(即 const char[] 的指针类型)转换为 std::string
Q8:release和debug版本的具体区别?
A8:1)编译器优化力度有所区别 2)调试信息,debug版本通常更丰富一些
3)再者,初始化的变量值可能不同:
在debug版本下,有的未初始化变量可能是 0xcccccccc。
在release版本下,为了考虑性能这个部分内容应该是0x212B421F这样的随机脏数据
4)断言:Debug版本中,断言(assertions)通常是启用的,以在开发过程中捕获潜在的错误。而在Release版本中,断言可能被禁用,以避免影响程序性能。
Q9:C++中默认的隐式转换可以做几次?
我们假设有以下情境:
1)这里的 "Cherno" 是一个 char 数组
2)程序中有 class Entity ,带构造函数,构造函数可以构造属性 string Name = 输入;
A9:我们在调用 PrintEntity 函数的时候,编译器试图在进行两次隐式转换:
即,char[] 到 std::string 再到 Entity
但是隐式转换在C++中默认只能自动进行一次,所以这里产生了错误。
解决方式:选择以下写法中的任意一种
实际上,在这里我们使用了一种 “隐式构造函数” ,也就是经过隐式转化的构造函数,可以简化代码但最好避免使用。
Q10:explicit关键字是用来干什么的?
A10:主要用在构造函数上,要求显式类型调用构造函数,也就是强制要求禁止使用隐式转换。确保输入参数的类型安全
例如要使用构造这个Dog对象,则必须显式调用此构造函数
这个语法主要是影响这种表达:
Dog e = 22; // 调用的是dog的有参构造函数,相当于 e.age = (int)22// 显然,这种方式会产生一次隐式转换, // err:不存在从 "int" 转换到 "Dog" 的适当构造函数C/C++(415)
然而以下方式定义就不会有问题:
Dog e(22); 或者 Dog* e = new Dog(22); 原因是他们不会将构造函数的入参弄成 int(22)
根据 "Cherno" 是一个 char 数组,也要注意尽可能用 std::string 作为输入,而不要用默认
堆上数据和栈上数据的生存周期问题(重要)
Q11:以下4句语句的 Animal a 对象,各自分配在堆还是栈上?
在栈和堆上,各自作用域/生存周期有何不同?
A11:
1) Animal a;分配在栈空间上,他的作用域是整个main函数,因此会打印 create
2) {Animal a;}分配在栈空间上,不过他的作用域是他所在的代码块,因此会打印 create+destory
注意!这里 a 因为结束了代码块的执行,作用域一过,自动被释放!
3){ Animal a = Animal(); } 分配在栈空间上,作用域是他所在的代码块,打印 create+destory
4){Animal* a = new Animal();}分配在堆空间上,作用域是他所在的代码块,但会长期存在,打印 create
栈是一种会自动默认跟随生命周期管理的数据结构,但是堆不会。堆的特点就是只能手动申请和释放
虽然有些时候编译器会帮我们去保留一段时间,但是完全不建议这么做。
尤其是这种代码:
Q12:作用域内变量自动销毁的特性,若想利用,可以有什么好的实践?(看看即可)
A12:
注意:这些应用都只针对作用域!
1)作用域指针(ScopedPtr):大概意思是使用一个类,其对象含有一个private的 Entity *类型,有构造和析构
当我们即使是使用new申请堆空间的时候,也可以利用析构函数,达到自动释放的效果。
这就是智能指针(smart ptr,或是unique_ptr)做的最基本的事情!
2)Timer计时器对象:构造时创建,析构时输出打印。你只需要在函数开头,写一行代码,那么整个作用域会被计时
3)自动mutex:在作用域内锁住,出作用域时解锁
这里简单补充一点智能指针的知识:
1)unique_ptr,作用域指针:
不允许被复制,不会计数,会自动销毁
出作用域时会自动释放。不能被复制,也就不会导致有2个ptr指向同一块内存进而引发释放错误
格式:std::unique_ptr<类名> ptr = std::make_unique<类名>();
效果:最终不会得到一个没有引用的悬空指针 从而造成内存泄露
完整程序:
class Animal{
public:Animal() {cout << "create" << endl;}~Animal() {cout << "destroy" << endl;}
};int main(){{std::unique_ptr<Animal> ptr = std::make_unique<Animal>();}while (1);return 0;
}
2)共享指针 shared_ptr
允许被复制,会计数,会自动销毁
shared ptr的工作方式是通过引用计数,可以跟踪计数有多少个引用。当计数=0时才删除。(有点文件系统里面 inode或者信号量 的意思)
因为shared ptr需要分配另一块内存,叫做控制块,用来存储引用计数
3)弱指针 weak_ptr
允许被复制,不会计数,会自动销毁
Q13:对于unique_ptr,为什么不能通过以下语法实现复制?
std::unique_ptr<Entity> ptr_new = ptr;
A13:编译器会报错,因为 std::unique_ptr
没有拷贝构造函数。
Q14:对于智能指针,为什么不建议通过new对象获得指针,而是使用 std::make_xx<> 方法?
在 unique_ptr 中,不直接调用new的原因是因为异常安全。
当使用 new
创建对象时,如果对象的构造函数抛出异常,而程序员忘记使用智能指针来管理这个对象,那么这个对象将不会被正确地删除,导致资源泄漏。std::make_xx<>()
方法在内部处理了这些异常情况,确保了即使构造函数失败,资源也会被正确释放。
在shared_ptr中,std::make_xx<>()
主要是用于共享指针需要获得控制块变量++,以便计数。
另外,std::make_xx<>()
提供了一致的创建智能指针的方式。增加可读性,避免维护困难。
Q15:关于强制转换,C风格的方法和C++风格的方法有何不同?
A15:
C风格的方法: int a = (int)c
C++风格的方法:
1)静态转换:用于非多态类型的转换,如基本数据类型,编译时而非运行时进行检查
int a = 10;
double b = static_cast<double>(a); // 将int转换为double
2)动态转换:用于处理多态性,只能在含有虚函数的类层次结构中使用
class Base { virtual void dummy() {} }
class Derived : public Base {};
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast <Derived*>(basePtr);
if (derivedPtr) {
// 转换成功
}
3)const_cast
使得不能改写的const类型数据变得可以修改:
const int* a = new int(10);
int* b = const_cast<int*>(a); // 移除const限定符
Q16:static_cast、dynamic_cast、const_cast 分别最主要用在什么地方?
A16:
-
static_cast
:-
可用于基本数据类型之间的转换,如将
int
转换为char
或将float
转换为int
。 -
可用于类层次结构中的向上转型(从派生类向基类转换),因为这是安全的,编译器知道所有相关的信息
-
-
dynamic_cast
:- 主要用于处理多态性,即在运行时确定对象的实际类型。
- 只能用于包含虚函数的类的指针或引用类型的向下转型(从基类向派生类转换)。
- 如果转换失败,指针类型的
dynamic_cast
返回nullptr
,引用类型的转换会抛出std::bad_cast
异常。
-
const_cast
:- 主要用来修改类型的
const
限定符。 - 可以用来将
const
类型的指针或引用转换为非const
类型,或者反之。 - 转换不会改变对象本身是否是常量,只是改变指针或引用的
const
属性。 - 示例代码:
const int* constIntPtr = new int(5); int* modifiableIntPtr = const_cast<int*>(constIntPtr); *modifiableIntPtr = 10; // 现在可以修改值
- 主要用来修改类型的
Q17:dynamic_cast
比 static_cast
慢,为什么?
A17:因为它需要在运行时检查对象的实际类型
相关文章:

C++学习笔记04-补充知识点(问题-解答自查版)
前言 以下问题以Q&A形式记录,基本上都是笔者在初学一轮后,掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系,也适合做查漏补缺和复盘。 本文对读者可以用作自查,答案在后面࿰…...

Vue el-table的自定义排序返回值为null,设置刷新页面保持排序标志,导航时elementui组件不更新
自定义排序使用sort-change"sortChange"监听,表列需设置为sortable“custom”(自定义) <el-table:data"tableData"bordersort-change"sortChange":default-sort"{prop:sortProp,order:sortOrder}&quo…...

一起笨笨的学C ——16链表基础
目录 目录 前言 正文 链表定义: 基本创建链表程序: 链表结点插入: 对角线记忆法: 画图理解法: 链表结点删除: 链表销毁: 后语 前言 链表理解方法分享,愿你的大脑也能建立一个…...

信息学奥赛一本通1917:【01NOIP普及组】装箱问题
1917:【01NOIP普及组】装箱问题 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 4178 通过数: 2473 【题目描述】 有一个箱子容量为VV(正整数,0≤V≤200000≤V≤20000),同时有n个物品(0≤n≤300≤n≤30),…...

android user 版本如何手动触发dump
项目需要在android user版本增加手动触发dump方法,用以确认user版本发生dump后系统是重启还是真正发生dump卡机! 本文以qcom平台项目为例描述所做的修改,留下足迹以备后忘。 闲言少叙,开整上干货: 一、修改bin文件 …...

RedHat Linux 7.5 安装 mssql-server
RedHat Linux 7.5 安装 mssql-server 1、安装部署所需的依赖包 [rootlocalhost ~]# yum -y install libatomic bzip2 gdb cyrus-sasl cyrus-sasl-gssapi Loaded plugins: ulninfo Resolving Dependencies --> Running transaction check ---> Package bzip2.x86_64 0:1…...

Vue的SSR和预渲染:提升首屏加载速度与SEO效果
引言 在现代Web应用开发中,首屏加载速度和搜索引擎优化(SEO)是衡量应用性能的重要指标。Vue.js 作为流行的前端框架,提供了服务器端渲染(SSR)和预渲染(prerendering)两种技术来提升这些指标。本文将深入探讨如何使用 Vue 的 SSR 和预渲染技术,提供详细的代码示例和最…...

若依ruoyi+AI项目二次开发(智能售货机运营管理系统)
(一) 帝可得 - 产品原型 - 腾讯 CoDesign (qq.com)...

【SpringBoot】 4 Thymeleaf
官网 https://www.thymeleaf.org/ 介绍 Thymeleaf 是一个适用于 Web 和独立环境的现代服务器端 Java 模板引擎。 模板引擎:为了使用户界面和业务数据分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎会生成一个标准的 html 文档…...

动静资源的转发操作
目录 Nginx中的location指令 静态资源的转发 动态资源的转发 注意事项 深入研究 如何在Nginx中实现对特定后缀文件的静态资源进行反向代理? Nginx中location指令的优先级是怎样确定的? 为什么在使用proxy_pass时要区分是否带有斜杠? N…...

Windows系统安全加固方案:快速上手系统加固指南(上)
无论是个人用户、小型企业还是大型机构,都需要采取措施保护其计算机系统免受各种威胁、系统加固常见的应用场景有个人用户、 AWD 比赛、公共机构以及企业环境等等 文档目录 一、Windows常用命令二、Windows常见端口三、账户安全3.1 默认账户安全3.2 按照用户分配账户…...

git连接远程仓库
一、本地新建代码,上传到远程仓库 1.git init #初始化本地仓库 2.git remote -v #查看当前仓库的远程地址 3.git remote add origin 远程仓库的URL 4.git branch master / git branch dev 创建 主分支或者 dev 分支 5.git checkout master/dev. 切换到主分支或者dev 分支…...

算法-----递归~~搜索~~回溯(宏观认识)
目录 1.什么是递归 1.1二叉树的遍历 1.2快速排序 1.3归并排序 2.为什么会用到递归 3.如何理解递归 4.如何写好一个递归 5.什么是搜索 5.1深度(dfs)优先遍历&优先搜索 5.2宽度(bfs)优先遍历&优先搜索 6.回溯 1.什…...

【云原生】Docker搭建知识库文档协作平台Confluence
目录 一、前言 二、企业级知识库文档工具部署形式 2.1 开源工具平台 2.1.1 开源工具优点 2.1.2 开源工具缺点 2.2 私有化部署 2.3 混合部署 三、如何选择合适的知识库平台工具 3.1 明确目标和需求 3.2 选择合适的知识库平台工具 四、Confluence介绍 4.2 confluence特…...

序列化与反序列化的本质
1. 将对象存储到本地 假如有一个student类,我们定义了好几个对象,想要把这些对象存储下来,该怎么办呢 from typing import List class Student:name: strage: intphones: List[str] s1 Student("xiaoming",10,["huawei&quo…...

飞牛爬虫FlyBullSpider 一款简单方便强大的爬虫,限时免费 特别适合小白!用它爬下Boss的2024年7月底Java岗位,分析一下程序员就业市场行情
一、下载安装FlyBullSpider 暂时支持Window,现在只在Win11上做过测试 1 百度 点击百度网盘 下载 链接:https://pan.baidu.com/s/1gSLKYuezaZgd8iqrXhk8Kg 提取码:Fly6 2 csdn https://download.csdn.net/download/fencer911/89584687 二、体验初…...

EXCEL 排名(RANK,COUNTIFS)
1.单列排序 需求描述:如有下面表格,需要按笔试成绩整体排名。 解决步骤: 我们使用RANK函数即可实现单列整体排名。 Number 选择第一列。 Ref 选择这一整列(CtrlShift向下箭头、再按F4)。 "确定"即可计算…...

【踩坑系列-JS】iframe中的url参数获取
Author:赵志乾 Date:2024-07-24 Declaration:All Right Reserved!!! 1. 问题描述 系统A的页面中以iframe的方式嵌入了系统B的页面,并需要将A页面url中的参数传递给B页面。 最初的实现方式是&am…...

测试工作中常听到的名词解释 : )
背景 很多名称其实看字面意思都挺抽象的,有时看群里的测试大佬在不停蹦这类术语,感觉很高大上,但其实很多你应该是知道的,只不过没想到别人是这样叫它的。又或者你的主编程语言不是 Java,所以看不懂他们在讲啥&#x…...

Linux内网离线用rsync和inotify-tools实现文件夹文件单向同步和双向同步
lsyncd实现方式可参考:https://www.jianshu.com/p/c075ccf89516 安装文件下载:相关文件下载 rsync默认都有,所以没有提供。 服务端和客户端均操作 服务端:双向同步其实都是服务端,只是单向同步时稍有区别 客户端&am…...

Spring Security学习笔记(二)Spring Security认证和鉴权
前言:本系列博客基于Spring Boot 2.6.x依赖的Spring Security5.6.x版本 上一篇博客介绍了Spring Security的整体架构,本篇博客要讲的是Spring Security的认证和鉴权两个重要的机制。 UsernamePasswordAuthenticationFilter和BasicAuthenticationFilter是…...

产品经理NPDP好考吗?
NPDP是新产品开发专业人员的资格认证,对于希望在产品管理领域取得认可的专业人士来说,NPDP认证是一项重要的资格。 那么,产品经理考取NPDP资格认证究竟难不难呢? 首先,NPDP考试的难易程度取决于考生的背景和准备情况…...

【C++】:红黑树的应用 --- 封装map和set
点击跳转至文章:【C】:红黑树深度剖析 — 手撕红黑树! 目录 前言一,红黑树的改造1. 红黑树的主体框架2. 对红黑树节点结构的改造3. 红黑树的迭代器3.1 迭代器类3.2 Begin() 和 End() 四,红黑树相关接口的改造4.1 Find…...

unity美术资源优化(资源冗余,主界面图集过多)
图片资源冗余: UPR unity的性能优化工具检查资源 1.检查纹理读/写标记 开启纹理资源的读/写标志会导致双倍的内存占用 检查Inspector -> Advanced -> Read/Write Enabled选项 2.检查纹理资源alpha通道 如果纹理的alpha通道全部为0,或者全部为2…...

【git】github中的Pull Request是什么
在 Git 中,"pull request"(简称 PR)是一种在分布式版本控制系统中使用的功能,特别是在使用 GitHub、GitLab、Bitbucket 等基于 Git 的代码托管平台时。Pull Request 允许开发者请求将他们的代码更改合并到另一个分支&am…...

gitlab查询分支API显示不全,只有20个问题
背景 gitlab查询分支API需要查询所有分支,且分支数量大于20,但目前调用接口返回的branch最多就显示了20个 解决方案 根据GitLab的文档,查询分支API默认最多返回20个分支。如果要一次性显示80个分支,可以使用分页参数来获取所有…...

vue3+vite 实现动态引入某个文件夹下的组件 - glob-import的使用
<template><div class"user-content"><HeaderTitle title"用户详情"></HeaderTitle><div class"main-content"><div><UserForm /></div><div><TableList></TableList></d…...

hhhhh
x torch.tensor([1.0,0.],[-1.,1.],requires_gradTrue) z x.pow(2).sum() z.backward() x.grad在这段代码中,我们利用 PyTorch 进行自动求梯度,下面详细解释代码的每一个部分及其在反向传播中的作用。同时,我们也将介绍函数对象和叶子节点的…...

扫雷小游戏纯后端版
package com.wind;import java.util.Random; import java.util.Scanner;public class ResultLei {static Random random new Random();public static void main(String[] args) {boolean end true;while (end) {System.out.println("请输入你选择的难度对应的数字&#…...

RuoYi-Vue-Plus(动态添加移除数据源)
一、添加数据 private final DynamicRoutingDataSource dynamicRoutingDataSource;private final DefaultDataSourceCreator dataSourceCreator;//添加一个dynamic的数据源@GetMapping("createDynamic")public void createDynamic() {DataSourceProperty property =…...