C++面试题和笔试题(五)
一、
#include <iostream>
using namespace std; class Base {
public: Base(int j) : i(j) {} virtual ~Base() {} void func1() { i *= 10; func2(); } int getvalue() { return i; }
protected: virtual void func2() { i++; }
protected: int i;
}; class Child : public Base {
public: Child(int j) : Base(j) {} void func1() { i *= 100; func2(); }
protected: void func2() { i += 2; }
}; int main() { Base* pb = new Child(1); pb->func1(); cout << pb->getvalue() << endl; delete pb; return 0;
}
输出的结果是(102)
官方解释:
-
基类
Base
:定义了一个整数成员i
和两个虚函数func1
和func2
。func1
函数首先将i
乘以10,然后调用func2
。func2
在基类中的实现是将i
加1。 -
派生类
Child
:从Base
类公开继承。它重写了func1
和func2
函数。在Child
中,func1
首先将i
乘以100,然后调用func2
。func2
在派生类中的实现是将i
加2。 -
main
函数:创建了一个指向Child
对象的Base
指针pb
。通过pb
调用func1
函数。由于多态性,将调用Child
类中的func1
。最后,输出i
的值并删除动态分配的对象。
程序的输出将取决于func1
和func2
的调用顺序以及它们在基类和派生类中的实现。在这个例子中,由于Child
类重写了func1
和func2
,所以最终i
的值将会是1 * 100 + 2 = 102
(Child
类的func1
乘以100,然后Child
类的func2
加2)。输出将是102
。
自己的理解:
假设现在我们有两个这样的魔法盒子,一个是爸爸的,一个是孩子的。爸爸的盒子只有“变大10倍并加1”这个按钮,而孩子的盒子既有“变大10倍并加1”按钮,又有“变大100倍并加2”按钮。
但是,孩子很调皮,他把他的盒子给了爸爸,并且让爸爸以为这就是他的盒子。当爸爸按下“变大10倍并加1”这个按钮时,其实他按的是孩子盒子的按钮,所以盒子的东西会按照孩子的规则来变大。
假设一开始盒子里有1个苹果,当爸爸按下按钮后,这个苹果会先变成100个(因为是按照孩子的盒子的“变大100倍”规则),然后再加2个,最后盒子里就有102个苹果了。
最后,爸爸会告诉我们盒子里有多少个苹果,我们就知道了。
在这个例子中,爸爸的盒子就像是基类Base
,孩子的盒子就像是派生类Child
。爸爸按下的按钮就像是基类的函数func1
,而孩子盒子的特殊功能就像是派生类重写的函数。因为爸爸实际上拿的是孩子的盒子,所以按下按钮后,会按照孩子的规则来变化
考点:
-
多态性:通过基类指针调用派生类对象的虚函数时,实际调用的是派生类中定义的版本。这里
pb->func1();
和pb->func2();
展示了多态性的使用。 -
虚函数:
func2
被声明为protected
和virtual
,这意味着它可以在派生类中被重写,同时保证多态性的行为。 -
构造函数和析构函数:构造函数用于初始化对象,析构函数用于清理对象。这里
Base
和Child
类的构造函数展示了如何初始化基类成员变量,而析构函数则展示了如何安全地销毁对象。 -
继承:
Child
类公开继承自Base
类,这意味着Child
对象可以使用Base
类的公有和保护成员。 -
访问控制:
Base
类中的func2
和i
被声明为protected
,这意味着它们可以在派生类中被访问,但不能从Base
类的对象直接访问。 -
内存管理:使用
new
和delete
进行动态内存分配和释放是 C++ 中的一个重要概念,这里展示了如何正确地使用它们。 -
错误处理:虽然代码中没有显式展示错误处理,但在实际编程中,处理动态内存分配失败(虽然在现代系统上很少见)或其他异常情况也是重要的考点。
二、改错题
1)
void GetMemory(char*p)
{
p=(char*)malloc(100);
}
void Test(void)
{
char *str=NULL;
GetMemory(str);
strcpy(str,"hello world");
printf(str);
}
问题一:GetMemory
函数中的指针修改不反映到 Test
函数中。
在 GetMemory
函数中,p
是一个指向 char
的指针的本地副本。当你将 p
指向 malloc
分配的内存时,Test
函数中的 str
并没有改变,因为它传递的是 str
的一个副本,而不是 str
本身的地址。因此,Test
函数中的 str
仍然是 NULL
。
问题二:内存泄漏。
由于 GetMemory
函数中的 malloc
分配的内存没有被释放,这会导致内存泄漏。
问题三:strcpy
可能导致未定义行为。
由于 str
是 NULL
,尝试使用 strcpy
将字符串复制到它指向的位置会导致未定义行为,通常是程序崩溃。
#include <stdio.h>
#include <stdlib.h>
#include <string.h> void GetMemory(char **p) { *p = (char*)malloc(100); if (*p == NULL) { // 错误处理,分配内存失败 exit(EXIT_FAILURE); }
} void Test(void) { char *str = NULL; GetMemory(&str); // 传递str的地址 if (str != NULL) { strcpy(str, "hello world"); printf("%s\n", str); free(str); // 释放分配的内存 }
} int main() { Test(); return 0;
}
GetMemory
函数现在接受一个指向char
指针的指针(即char **
),这样它就可以修改Test
函数中的str
。- 使用
malloc
分配内存后,我们检查是否成功分配了内存。 - 在
Test
函数中,我们检查str
是否为NULL
,然后才尝试使用strcpy
。 - 使用完分配的内存后,我们在
Test
函数中释放它,以避免内存泄漏。 malloc
分配的内存应该用free
来释放,而且应该在所有使用完这块内存的地方都这么做
自己的理解:
想象你有一个存钱罐,你想要放更多的钱进去,但是你还没有足够的硬币。于是,你决定去银行取一些钱。
GetMemory
函数就像是你去银行取钱的过程。你告诉银行你想要100个硬币(这就像是分配100个字节的内存)。银行给你一个装满硬币的袋子(这就像是malloc
返回的内存地址)。
Test
函数就像是你回到家,想要把取回来的钱放进存钱罐里。但是,如果你忘记从银行带钱回来(也就是说,str
仍然是NULL
),你尝试往存钱罐里放钱时,就会出问题,因为没有钱可以放(这会导致程序崩溃)。
为了避免这个问题,你在GetMemory
函数里不是直接告诉银行你要取钱,而是告诉银行你存钱罐的地址(这就像是传递str
的地址,即&str
)。这样,银行就能直接把装满硬币的袋子放到你的存钱罐里。
当你把钱放进存钱罐后,记得以后不用了要把钱放回银行,不然你的家就会堆满钱,没地方放了(这就像是内存泄漏)。所以,在Test
函数最后,我们使用free(str)
来释放我们之前分配的内存。
这样,你就学会了如何正确地取钱(分配内存)和存钱(释放内存),而不会让家里变得乱七八糟。
2)
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"new A"<<endl;
m=(char*)malloc(10);
}
~A()
{
cout<<"del A"<<endl;
}
protected:
char *m;
}
class B:public A
{
public:
B()
{
cout<<"new B"<<endl;
m=(char*)malloc(100);
}
~B()
{
cout<<"del B"<<endl;
}
protected:
char*m;
};int main()
{A*c=new B;
delete c;
}
错误和需要注意的地方:
-
class B
中重定义了m
成员变量。在继承关系中,如果基类A
和派生类B
中都有名为m
的成员变量,那么它们将被视为两个不同的变量。这可能会导致混淆和错误。 -
在
main
函数中,你使用new B
创建了一个B
类的对象,但是将其赋值给了一个A
类型的指针c
。虽然这是合法的(因为B
是从A
公有继承的),但需要注意,当你通过A
类型的指针访问m
成员时,你会访问到基类A
中的m
,而不是派生类B
中的m
。 -
在
A
和B
的构造函数中,你使用malloc
为m
分配了内存,但在析构函数中并没有使用free
来释放这些内存。在C++中,更推荐使用new
和delete
来管理动态内存,因为new
会自动调用对象的构造函数,而delete
会自动调用析构函数。
#include<iostream>
using namespace std; class A {
public: A() { cout << "new A" << endl; m = new char[10]; // 使用 new 分配内存 } virtual ~A() { // 声明为虚析构函数,确保派生类的析构函数也会被调用 cout << "del A" << endl; delete[] m; // 使用 delete[] 释放内存 }
protected: char *m;
}; class B : public A {
public: B() { cout << "new B" << endl; // 注意:这里不应该再次分配 m,因为 m 已经在 A 的构造函数中被分配了。 // 如果你确实需要在 B 中分配新的内存,你应该使用一个不同的变量名。 } ~B() { cout << "del B" << endl; // 在这里,我们不需要释放 m,因为 A 的析构函数会负责释放它。 // 如果 B 有自己的内存需要释放,应该在这里做。 }
}; int main() { A* c = new B; // 创建 B 的对象,但用 A 的指针指向它 delete c; // 使用 delete 释放 c 指向的对象,这会先调用 B 的析构函数,然后调用 A 的析构函数 return 0;
}
在这个修改后的版本中,我将 A
的析构函数声明为虚函数(virtual ~A()
)。这是为了确保当通过基类指针删除派生类对象时,派生类的析构函数也会被调用。此外,我还使用了 new
和 delete[]
来分配和释放内存,这是C++中更推荐的做法。
3)
#include<iostrem>
using namespace std;class Test
{
public:long a;long b;virtual void fun(){}Test(long temp1=0,long temp2=0)
{a=temp1;b=temp2;
}long getA(){return a;}
long getB(){return b;}
};int maint()
{Test obj(5,10);
long * pint =(long*)&obj;
*(pint)=100;
*(pint+1)=200;
cout<<"a="<<obj.getA()<<endl;
cout<<"b="<<obj.getB()<<endl;
return 0;
}
程序在64位机器上打印输出结果是(a=100,b=200)
官方解释:
在 main
函数中,你创建了一个 Test
类的对象 obj
,并初始化了 a
为5,b
为10。然后,你创建了一个指向 long
的指针 pint
,并将其设置为指向 obj
的地址。接下来,你通过 pint
修改了 obj
的内存内容。
long * pint =(long*)&obj;
*(pint)=100;
*(pint+1)=200;
这里,*(pint)=100;
将 obj
的起始地址(即 a
的值)设置为100。*(pint+1)=200;
将 obj
起始地址后8字节的位置(即 b
的值)设置为200。
因此,obj
的成员变量 a
和 b
现在分别被设置为100和200。
输出将是:a=100 b=200。
自己的理解:
当然可以,让我们用小学生能懂的方式和生活中的例子来解释这段代码。
首先,想象一下你有一个魔法盒子,这个盒子里面可以放两个魔法球,每个魔法球都有一个数字。这个魔法盒子就是我们代码中的Test
类,而两个魔法球就是a
和b
这两个变量。
现在,我们有一个特别的咒语,可以让这个魔法盒子里的魔法球变出数字来。这个咒语就是Test
类的构造函数,它可以在我们创建魔法盒子的时候,给两个魔法球分别赋予数字。
Test(long temp1=0,long temp2=0)
{ a=temp1; b=temp2;
}
比如说,我们念这个咒语:“魔法盒子,给我一个5和一个10!”然后,魔法盒子里的两个魔法球就分别变成了5和10。
在main
函数中,你创建了一个这样的魔法盒子,并给它念了咒语,让里面的两个魔法球变成了5和10。
接下来,你有一个魔法棒,这个魔法棒可以让你直接看到魔法盒子里面的东西。但是,你这次没有用魔法棒去看,而是直接用手去摸魔法盒子,并试图改变魔法球里的数字。
long * pint =(long*)&obj;
*(pint)=100;
*(pint+1)=200;
你用手伸进了魔法盒子,摸到了第一个魔法球,然后把它变成了100。接着,你又摸到了第二个魔法球,把它变成了200。
最后,你用魔法棒去看魔法盒子里的魔法球,发现它们已经变成了100和200,而不是原来的5和10了。
cout<<"a="<<obj.getA()<<endl;
cout<<"b="<<obj.getB()<<endl;
所以,这个程序在64位机器上打印的输出结果是:a=100 b=200;
相关文章:
C++面试题和笔试题(五)
一、 #include <iostream> using namespace std; class Base { public: Base(int j) : i(j) {} virtual ~Base() {} void func1() { i * 10; func2(); } int getvalue() { return i; } protected: virtual void func2() { i; } protected: int i;…...

Mysql:行锁,间隙锁,next-key锁?
注:以下讨论基于InnoDB引擎。 文章目录 问题引入猜想1:只加了一行写锁,锁住要修改的这一行。语义问题数据一致性问题 猜想2:要修改的这一行加写锁,扫描过程中遇到其它行加读锁猜想3:要修改的这一行加写锁&…...

Grass推出Layer 2 Data Rollup
Grass推出Layer 2 Data Rollup Grass邀请链接最新资讯 Grass邀请链接 欢迎使用我的邀请码进行注册: 邀请链接 如果你还不知道注册流程:详见Grass: 出售闲置带宽实现被动收入 最新资讯 简讯:2024年3月13日,Grass宣布正在建立基于Solana的La…...
[Java、Android面试]_04_进程、线程、协程
本人今年参加了很多面试,也有幸拿到了一些大厂的offer,整理了众多面试资料,后续还会分享众多面试资料。 整理成了面试系列,由于时间有限,每天整理一点,后续会陆续分享出来,感兴趣的朋友可收藏 文…...
MyLisp项目日志:解析用户输入与波兰表达式
文章目录 编程语言模拟自然语言定义名词和形容词定义短语定义句子 简化模拟过程正则表达式 波兰表达式及其解析波兰表达式语法描述波兰表达式语法解析解析用户输入 v0.0.2 编程语言 编程语言是类似于自然语言的,虽然我们是自然而然就学会了自己的母语,但…...

torch.backends.cudnn.benchmark 作用
相关参数 torch.backends.cudnn.enabled torch.backends.cudnn.benchmark torch.backends.cudnn.deterministictorch.backends.cudnn.benchmark True:将会让程序在开始时花费一点额外时间,为整个网络的每个卷积层搜索最适合它的卷积实现算法,…...
vue的$nextTick应用场景
文章目录 $nextTick有什么作用?一、NextTick是什么二、为什么要有nextTick? $nextTick有什么作用? 一、NextTick是什么 官方对其的定义 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的…...
springboot RestTemplate 发送xml、接收xml、pojo中的属性转为属性
背景 调用第三方接口时,它们的系统比较老,只支持接收xml而不支持json,默认的springboot RestTemplate不支持发送xml,添加依赖就可以解决这个问题。 添加jackson-dataformat-xml依赖 FasterXML/jackson-dataformat-xml是一个xml…...
Lua-Lua与C++的交互2
Lua与C的交互是指在C程序中使用Lua本语言,或者在Lua脚本中调用C代码的过程。这种交互可以实现C与Lua之间的数据传递和函数调用。 在C中与Lua交互的主要步骤如下: 引入Lua库:首先需要在C程序中引入Lua的头文件和库文件,以便能够使…...

学python新手如何安装pycharm;python小白如何安装pycharm
首先找到官网: Download PyCharm: The Python IDE for data science and web development by JetBrains 打开后选择下载,下图标红部分 点击exe程序,点击下一步! 选择安装路径,下一步 弹出界面全选 选择默认 然后直接…...

Oracle Primavera P6 数据库升级
前言 为了模拟各种P6测试,我常常会安装各种不同版本的p6系统,无论是P6服务,亦或是P6客户端工具Professional,在今天操作p6使用时,无意识到安装在本地的P6 数据库(21.12)出现了与Professional软…...
共享库的创建gcc选项“-shared -fPIC -WI”
共享库的创建非常简单,最关键的是gcc的几个参数: “-shared”: 表示输出结果是共享库类型。编译选项告诉编译器生成一个共享库(也称为动态链接库或 DLL)。共享库是一种包含可重用代码和数据的二进制文件,…...

微服务:Bot代码执行
每次要多传一个bot_id 判网关的时候判127.0.0.1所以最好改localhost 创建SpringCloud的子项目 BotRunningSystem 在BotRunningSystem项目中添加依赖: joor-java-8 可动态编译Java代码 2. 修改前端,传入对Bot的选择操作 package com.kob.botrunningsy…...

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 3、线条平滑曲面但有条纹
环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata imp…...

Vue.js+SpringBoot开发数字化社区网格管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、开发背景四、系统展示五、核心源码5.1 查询企事业单位5.2 查询流动人口5.3 查询精准扶贫5.4 查询案件5.5 查询人口 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数字化社区网格管理系统…...

java SSM农产品订购网站系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计
一、源码特点 java SSM农产品订购网站系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采…...
vsto快速在excel中查找某个字符串
是的,使用foreach循环遍历 Excel.Range 可能会较慢,特别是在大型数据集上。为了提高效率,你可以考虑使用 Value 属性一次性获取整个范围的值,然后在内存中搜索文本。这样可以减少与 Excel 之间的交互次数,提高性能。 …...

Unity类银河恶魔城学习记录10-1 10-2 P89,90 Character stats - Stat script源代码
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Stat.cs using System.Collections; using System.Collections.Generic; us…...

西门子TIA中配置Anybus PROFINET IO Slave 模块
1、所需产品 Siemens S7 PLC CPU 315-2 PN/DP 6ES7 315-2EH-0AB0 Siemens PLC 编程电缆 n.a. n.a. PC ,并安装Siemens PLC编程软件 TIA Portal V11 X-gateway Slave 接口的GSDML文件 根据网关的软件版本而定 Anybus Communicator GSD文件 GSDML-V1.0-HMS-ABCPRT-20050317.xl…...

在 Rust 中使用 Serde 处理json
在 Rust 中使用 Serde 处理json 在本文中,我们将讨论 Serde、如何在 Rust 应用程序中使用它以及一些更高级的提示和技巧。 什么是serde? Rust中的serde crate用于高效地序列化和反序列化多种格式的数据。它通过提供两个可以使用的traits来实现这一点&a…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...
Spring事务传播机制有哪些?
导语: Spring事务传播机制是后端面试中的必考知识点,特别容易出现在“项目细节挖掘”阶段。面试官通过它来判断你是否真正理解事务控制的本质与异常传播机制。本文将从实战与源码角度出发,全面剖析Spring事务传播机制,帮助你答得有…...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战
🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...
Nginx 事件驱动理解
在做埋点采集服务的过程中,主要依靠openresty加lua脚本来实现采集。高并发还是主要依靠nginx来实现。而其核心就是事件驱动/多路io复用(epoll机制),不同的linux服务器都有对应的实现方式。 而epoll机制就是,应用启动的…...