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;         // 是不合法的语法

        不能实例化含有纯虚函数的类对象的原因有以下几点:

  1. 接口不完整:含有纯虚函数的类定义了一个不完整的接口。这意味着基类本身并没有提供足够的实现细节来创建一个完整的对象。

  2. 强制实现:纯虚函数的存在是为了确保所有派生类都实现了这些函数。如果允许实例化含有纯虚函数的类,那么这种强制实现的约束就被破坏了。

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:

  1. static_cast

    1. 可用于基本数据类型之间的转换,如将 int 转换为 char 或将 float 转换为 int

    2. 可用于类层次结构中的向上转型(从派生类向基类转换),因为这是安全的,编译器知道所有相关的信息

  2. dynamic_cast

    • 主要用于处理多态性,即在运行时确定对象的实际类型。
    • 只能用于包含虚函数的类的指针或引用类型的向下转型(从基类向派生类转换)。
    • 如果转换失败,指针类型的 dynamic_cast 返回 nullptr,引用类型的转换会抛出 std::bad_cast 异常。
  3. 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:因为它需要在运行时检查对象的实际类型

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

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

相关文章

Mysql-高级实战案例

文章目录 千万级用户场景下的运营系统SQL调优1. 索引优化2. 查询优化3. 分析查询执行计划4. 存储引擎配置5. 数据库架构优化6. 监控与报警7. 定期维护8. 软件升级 亿级数据量商品系统的SQL调优实战1. 索引优化2. 查询重构3. 分区策略4. 优化查询计划5. 缓存策略6. 数据库架构调…

国内微短剧系统平台抖音微信付费小程序app开发源代码交付

微短剧作为当下热门的内容&#xff0c;结合抖音平台的广泛用户基础&#xff0c;开发微短剧付费小程序APP具有显著的市场潜力&#xff0c;用户对于短剧内容的需求旺盛&#xff0c;特别是在言情、总裁、赘婿等热门题材方面&#xff0c;接下来给大家普及一下微短剧小程序系统。 顺…

rce漏洞-ctfshow(50-70)

Web51 if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } Nl&#xff0c;绕过tac&#xff0c;cat&#xff0c;绕…

Pytest进阶之fixture的使用(超详细)

目录 Fixture定义 Fixture使用方式 作为参数使用 Fixture间相互调用(作为参数调用) 作为conftest.py文件传入 Fixture作用范围Scope function class module session Fixture中params和ids Fixture中autouse Fixture中Name 总结 pytest fixture 是一种用来管理测试…

回溯算法(相关解题):

求子集序列&#xff1a; 解题思路&#xff1a; 已知原集合的数据位数为N&#xff0c;则可以通过二进制比对原来集合&#xff0c;二进制位为1则输出集合上的该位数据&#xff0c;为0则空&#xff0c;二进制的01排序规律与子集的输出一致由集合的位数可以判断出二进制的范围 0 ~…

Direct3D 9的介绍以及Demo演示

文章目录 1、d3d9的介绍1. 概述2. 核心概念3. 初始化和渲染流程4. 常见用法5. 先进特性6. 总结 2、d3d9demo详解1.头文件和全局变量2.IGW 相关全局变量3.函数&#xff1a;CloseIGW4.函数&#xff1a;OpenIGW5.UI 控件和日志处理6.登录和登出相关函数7.登录回调函数8.DXUT 相关回…

P3-AI产品经理-九五小庞

AI产品的数据流向 美团外卖&#xff0c;实时只能调度 美团28分钟送达需求的分析 AI产品常用的算法 常用算法 常见的AI算法解析 自然语言生成NLG语音识别&#xff1a;科大讯飞&#xff0c;通义千问 虚拟现实机器学习平台 决策管理系统生物特征识别技术 RPA(机器人流程自动…

探索 GPT-4o Mini:开发者的新利器

文章目录 探索 GPT-4o Mini&#xff1a;开发者的新利器1. 引言2. GPT-4o Mini 的核心特点3. 使用 GPT-4o Mini 的实际案例3.1 客户支持自动化3.2 内容生成与创作3.3 代码生成与优化 4. 使用体验分享5. 未来展望6. 结论 探索 GPT-4o Mini&#xff1a;开发者的新利器 OpenAI 最新…

UE4-光照重建

当我们拉入新的光源和模型到我们的场景中后&#xff0c;会产生这样的情况&#xff1a; Preview:预览 表示此时由于光照物体所产生的阴影都是预览级别的并不是真正的效果。 方法一&#xff1a; 或者也可以在世界大纲中选中我们的光源&#xff0c;然后将我们的光源改变为可以…

JAVA基本概念(垃圾回收、API)- 10

一、垃圾分代回收机制 1. 垃圾回收针对的是堆内存 2. 对象在堆内存中存储,对象在使用完成之后会在不定的某个时刻被垃圾回收器(GC - Garbage Collector)解析 掉。现阶段回收过程无法手动控制。当调用构造方法的时候,创建好一个对象,因为Java中对每种数据类型都明确 给…

Jdk22新特性

JDK 22 引入了多项新特性,旨在提升 Java 语言的性能、简化开发过程以及增强代码的可读性和可维护性。以下是对 JDK 22 新特性的详细归纳: 核心Java库 外部函数和内存 API (JEP 454):提供了一个纯 Java 应用程序接口,用于替代 JNI(Java Native Interface),以支持直接调…

前端切片下载

要在Vue3前端实现文件切片下载&#xff0c;可以参考以下步骤&#xff1a; 分片函数&#xff1a;将文件分成多个小片段。 生成Blob对象&#xff1a;将片段转换为Blob对象。 创建下载链接&#xff1a;通过Blob对象创建下载链接。 合并下载的片段&#xff1a;下载完成后&#x…

判断字符串,数组方法

判断字符串方法 在JavaScript中&#xff0c;可以使用typeof操作符来判断一个变量是否为字符串。 function isString(value) {return typeof value string; } 判断数组 在JavaScript中&#xff0c;typeof操作符并不足以准确判断一个变量是否为数组&#xff0c;因为typeof会…

深入理解Python中的Pandas库

目录 Pandas简介安装PandasPandas的核心数据结构 SeriesDataFrame 数据加载与存储 从CSV文件读取数据从Excel文件读取数据从SQL数据库读取数据数据存储 数据操作 数据选择数据过滤数据排序数据分组与聚合数据透视表 数据清洗与处理 处理缺失值数据转换数据合并 数据可视化实战…

KingBase 下的 sys_hba.conf 详解

客户端访问KingbaseES数据库,需要建立身份的认证,sys_hba.conf相当于认证的黑白名单,可以通过配置sys_hba.conf允许或拒绝客户端对数据库服务器的访问。 sys_hba.conf原理: 客户端认证是由一个配置文件(通常名为sys_hba.conf并被存放在数据库集簇目录中)控制(HBA表示基…

《昇思25天学习打卡营第25天|第27天》

今天是学习的第二十七天&#xff0c;今天学习的是应用实践篇中计算机视觉中ShuffleNet图像分类。 从对ShuffleNet网络介绍开始学习&#xff0c;模型架构&#xff08;Pointwise Group Convolution&#xff0c;Channel Shuffle&#xff0c;ShuffleNet模块&#xff0c;构建Shuffl…

knowLedge-工具函数返回kb数值转换为KB,MB,GB等单位保留两位小数

1.场景 实现一个工具函数&#xff0c;将后端返回的单位为KB&#xff08;但实际上是kb&#xff0c;即千字节&#xff0c;注意大小写通常表示不同&#xff0c;这里我们按照常见需求处理为KB&#xff09;的数字转换成KB、MB、GB等形式&#xff0c;你可以按照以下步骤进行。这个函数…

【Mybatis整合Oracle】在 xml 文件中 WITH 子句的简单使用

在 Oracle SQL 中&#xff0c;WITH 子句用于定义一个或多个公共表表达式&#xff08;CTE, Common Table Expression&#xff09;&#xff0c;然后可以在主查询中引用这些表达式。WITH 子句通常用于简化复杂查询的结构&#xff0c;增强可读性&#xff0c;并避免重复编写相同的子…

# Redis 入门到精通(九)-- 主从复制(1)

Redis 入门到精通&#xff08;九&#xff09;-- 主从复制&#xff08;1&#xff09; 一、redis 主从复制 – 主从复制简介 1、互联网“三高”架构 高并发高性能高可用 2、你的“Redis”是否高可用&#xff1f; 1&#xff09;单机 redis 的风险与问题 问题1.机器故障  现…

C++ 鼠标轨迹API【神诺科技SDK】

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;使得神诺科技 能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.…