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…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
