C++笔试题汇总

news/2024/9/22 14:52:54/

C++笔试题汇总记录

  • 一、概述
  • 二、概念分类
    • 1. 结构体
      • 1. C 和 C++ 中 struct 有什么区别?
      • 2. C++中的 struct 和 class 有什么区别?
    • 2. 类相关
      • 1. 类的大小
        • 1. 空类的大小
        • 2. 一般非空类大小
        • 3. 有虚函数类
        • 4. 有虚函数类的继承
        • 5. 只有虚函数
        • 6. 静态数据成员
      • 2. C++的三大特性(封装、继承、多态)
      • 3. 多态是怎么实现的?
      • 4. 什么是虚函数和纯虚函数?以及区别?
      • 5. 虚函数是怎么实现的?虚函数在那个阶段生成?虚函数是怎么实现的?虚函数在那个阶段生成?
      • 6. 构造函数和析构函数的作用?
      • 7. 构造函数和析构函数那个可以被声明为虚函数,为什么?
      • 8. 构造函数和析构函数那个可以被重载,为什么?
      • 9. this 指针,为什么会存在this指针?
      • 10. 类继承中构造函数和析构函数的调用顺序、为什么析构函数要写成虚函数
  • 三、内存管理
    • 1. 指针
      • 1. “引用”与指针的区别是什么?
      • 2. malloc/free 和 new/delete 的区别
      • 3. 如果在申请动态内存时找不到足够大的内存块,malloc 和 new 将返回 NULL 指针,宣告内存申请失败。你是怎么处理内存耗尽的?
      • 4. 一个指针占几个字节
      • 5. 什么是指针数组和数组指针
      • 6. 什么是函数指针和指针函数以及区别
      • 7. 常量指针和指针常量以及区别
      • 8. C++程序在内存的分布图
      • 9.
    • 2. 引用
  • 四、
  • 五、
  • 六、

一、概述

这里记录一下收集的常见的面试题,一些概念题,方便查看,后面会更新

二、概念分类

1. 结构体

1. C 和 C++ 中 struct 有什么区别?

Protection行为能否定义函数
C否,但可以有函数指针
C++可以,默认是public

C 本身是面向过程的,但C++是面向对象的,所以,struct基本表现类似class,也是可以有构造的,

struct Person
{Person() {m_name = "";m_age = 0;}Person(std::string &name, int &age) {m_name = name;m_age = age;}std::string m_name;int m_age;
};

2. C++中的 struct 和 class 有什么区别?

从语法上讲,class和struct做类型定义时只有两点区别:

  • 1.默认继承权限。如果不明确指定,来自class的继承按照private继承处理,而struct的继承按照public继承处理;
  • 2.成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。 除了这两点,class和struct基本就是一个东西。语法上没有任何其它区别。

就像下面的代码就是手动指定是private

struct Person
{Person() {m_name = "";m_age = 0;}Person(std::string &name, int &age) {m_name = name;m_age = age;}
private:    std::string m_name;int m_age;
};
  1. pass
  2. pass
  3. pass

2. 类相关

1. 类的大小

这里设操作系统位数是32位,则指针大小为4字节,若64位则8字节

1. 空类的大小

为了给实例化的空类提供唯一地址,所以编译器会给空类隐含的添加一个字节,其大小为1

class CBase
{
};
std::cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;	// sizeof(CBase)=1

就算是有函数,也是不会占用空间的

class CBase
{void FuncA();
};
std::cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;	// sizeof(CBase)=1
2. 一般非空类大小
class CBase
{int  a;char *p;
};	// sizeof(CBase)=8

这里因为指针的大小取决于操作系统的位数,大小为4

3. 有虚函数类
class CBase
{
public:CBase(void);virtual ~CBase(void);
private:int   a;char *p;
}; 			//sizeof(CBase)=12

C++ 类中有虚函数的时候有一个指向虚函数的指针(vptr),在32位系统分配指针大小为4字节

4. 有虚函数类的继承
class CChild :public CBase
{
public:CChild(void);~CChild(void);
private:int b;
};			//sizeof(CChild)=16;

子类的大小是本身成员变量的大小加上父类的大小。

5. 只有虚函数
class A
{virtual void FuncA();virtual void FuncB(); 
};		//sizeof(A)=4;

当C++ 类中有虚函数的时候,会有一个指向虚函数表的指针(vptr),在32位系统分配指针大小为4字节。所以size为4.

6. 静态数据成员
class A
{int a;static int b;virtual void FuncA();
};				//sizeof(A)=8;

静态数据成员被编译器放在程序的一个global data members中,它是类的一个数据成员.但是它不影响类的大小,不管这个类实际产生了多少实例,还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在。

而类的非静态数据成员只有被实例化的时候,他们才存在.但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在.可以这么说,类的静态数据成员是一种特殊的全局变量.
所以该类的size为:int a型4字节加上虚函数表指针4字节,等于8字节。

2. C++的三大特性(封装、继承、多态)

封装:将数据(成员变量)和对数据的操作(成员函数)捆绑在一起,并对外部隐藏数据的具体实现。

继承:通过继承,子类可以继承父类的属性和行为,从而实现代码的重用和扩展。

多态:允许不同的对象以相同的接口进行操作,即同一操作可以作用于不同的对象上,表现出不同的行为。多态有运行时多态(重写),有编译时多态(重载)

3. 多态是怎么实现的?

多态的实现主要依赖于虚函数和动态绑定。多态允许程序在运行时决定调用哪个具体的函数实现。

运行时多态:通过使用虚函数实现,允许基类指针或引用调用派生类中的重写函数。这是通过虚函数表(vtable)和虚指针(vptr)机制实现的。

4. 什么是虚函数和纯虚函数?以及区别?

虚函数 :在基类中声明为 virtual 的成员函数,允许派生类重写(override)该函数。虚函数实现运行时多态。

作用:通过基类指针或引用调用虚函数时,实际调用的是派生类中的重写函数。

纯虚函数:在基类中声明为 virtual 并且等于 0 的虚函数,例如 virtual void func() = 0;。

作用:定义接口,要求所有派生类必须实现纯虚函数。使得基类成为抽象类,不能实例化。

5. 虚函数是怎么实现的?虚函数在那个阶段生成?虚函数是怎么实现的?虚函数在那个阶段生成?

虚函数表:每个类(如果包含虚函数)会有一个虚函数表,它是一个指向虚函数实现的指针数组。每个对象会有一个指向虚函数表的指针(vptr)。

动态绑定:通过 vptr 指针,运行时可以查找虚函数表中的具体函数地址,决定调用哪个函数实现。

编译阶段编译器生成虚函数表和虚指针。虚函数表在类定义时生成,虚指针在对象实例化时设置。
运行阶段:在程序运行时,当通过基类指针或引用调用虚函数时,程序会根据虚函数表的地址来找到实际应该调用的函数。这种机制实现了多态性,即允许不同的类对象对同一消息做出不同的响应。

6. 构造函数和析构函数的作用?

构造函数:在创建对象时初始化对象的状态。构造函数可以用于分配资源、初始化数据成员等。
特性:构造函数与类名相同,无返回值,可以被重载。

析构函数:在对象生命周期结束时清理对象的资源。析构函数负责释放构造函数分配的资源,如内存、文件句柄等。
特性:析构函数的名字前加 ~,无返回值,不能被重载。

7. 构造函数和析构函数那个可以被声明为虚函数,为什么?

析构函数可以被声明为虚函数:如果基类的析构函数是虚函数,确保派生类的析构函数在基类指针或引用被销毁时正确调用。防止资源泄漏和不完全析构。

构造函数不能被声明为虚函数:构造函数在对象创建时调用,此时对象还未完全构造,无法设置虚函数表,因此构造函数不能是虚函数。

8. 构造函数和析构函数那个可以被重载,为什么?

析构函数不可以被重载:每个类只能有一个析构函数,负责对象生命周期结束时的清理操作。如果允许重载,会导致析构时不确定性。

构造函数可以被重载:允许创建对象时通过不同的参数进行初始化。通过重载构造函数,可以提供多种初始化方式。

9. this 指针,为什么会存在this指针?

定义:this 指针是指向当前对象的指针。在类的成员函数内部,this 指针指向调用该函数的对象。this指针是在成员函数的开始前构造,并在成员函数的结束后清除

访问对象成员:允许成员函数访问对象的属性和其他成员函数。

区分成员变量和参数:在成员函数中,this 指针可以用来区分同名的成员变量和函数参数。

10. 类继承中构造函数和析构函数的调用顺序、为什么析构函数要写成虚函数

构造函数的调用顺序:自上而下

  • 当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达最底层目标派生类的构造函数

析构函数的调用顺序:自下而上

  • 当删除一个对象时,首先调用该派生类的析构函数,然后调用上一层基类的析构函数,依次类推,直到到达最顶层的基类析构函数

虚析构函数的作用:通过基类指针来删除派生类对象时,基类的析构函数应该是虚函数

在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。如果要用基类对继承成员进行操作,则要把基类的这个成员函数定义为虚函数,析构函数同样需要如此。

如果要用基类指针来删除派生类的对象,而这个基类有一个非虚的析构函数。后果是对象的派生部分不会被销毁。然而基类部分被销毁了,将导致内存泄露

  • 基类析构函数不是虚函数,则析构的时候子类对象没有析构
  • 基类析构函数是虚函数,子类对象和父类对象都被析构

三、内存管理

1. 指针

1. “引用”与指针的区别是什么?

指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;
而引用本身就是目标变量的别名,对引用的操作就是对目标变量的直接操作
1.引用必须被初始化,指针不必。
2.引用初始化以后不能被改变,指针可以改变所指的对象。
3.不存在指向空值的引用,但是存在指向空值的指针

2. malloc/free 和 new/delete 的区别

malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。因为对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。
因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。注意 new/delete 不是库函数

3. 如果在申请动态内存时找不到足够大的内存块,malloc 和 new 将返回 NULL 指针,宣告内存申请失败。你是怎么处理内存耗尽的?

1.判断指针是否为 NULL,如果是则马上用 return 语句终止本函数。
2.判断指针是否为 NULL,如果是则马上用 exit(1) 终止整个程序的运行
3.为 new 和 malloc 设置异常处理函数。例如 Visual C++可以用_set_new_hander 函数为 new 设置用户自己定义的异常处理函数,也可以让 malloc 享用与 new 相同的异常处理函数。

4. 一个指针占几个字节

一个指针在32位的计算机上,占4个字节;
一个指针在64位的计算机上,占8个字节。
这个是与计算机的寻找空间能力有关,也就是地址的总线宽度决定

5. 什么是指针数组和数组指针

指针数组是一个数组,其中的每个元素都是指针。这些指针可以指向不同的内存地址,通常用于存储一组相同类型的指针。

就像下面的,ptrArray[] 是一个数组,数组中的类型是 int * 指针类型。

int *ptrArray[5];

数组指针是一个指针,它指向数组的首地址,这个变量保存的值是数组的地址。它本身是一个指针,但指向的内容是一个数组对象,那我们就需要对这个数组指针接引之后才得到值。

下面这种其实是和 int *arrPtr 功能一致。

// arrPtr 是一个指针,指向一个包含 5 个整数的数组
int (*arrPtr)[5];

去获取值的话就是

int a1[3] = {12, 15, 75};int (*p_a1)[3] = &a1;
int *p_a2 = &a1[0];std::cout<<*(*p_a1 + 1) <<*(p_a2+1);	//输出 15 15

指针数组常用于需要动态管理一组指针的场景,而数组指针则用于处理数组的整体,特别是在函数参数传递和多维数组的处理中比较常见。

6. 什么是函数指针和指针函数以及区别

函数指针是指指向函数的指针变量,而指针函数是一个返回类型为函数指针的函数

函数指针 是指 指向一个函数的指针变量,用于保存函数地址。它可以指向一个特定类型和签名(参数类型和返回类型)的函数。函数指针的声明形式类似于指向其他类型的指针,但其类型是指向函数的指针类型。

// 声明一个指向返回类型为 void,参数为 int 的函数指针
typedef void (*FuncPtr)(int);// 定义一个函数
void myFunction(int x) {// 函数体
}int main() {// 声明一个函数指针变量并初始化FuncPtr ptr = &myFunction;// 通过函数指针调用函数ptr(10);  // 相当于调用 myFunction(10);return 0;
}

指针函数 指的是 返回类型 为 指向函数的指针 的函数。换句话说,指针函数是一个返回类型为函数指针的函数。


// 声明一个返回类型为 int*,参数为两个 int指针函数
int (*funcPtr)(int, int);// 定义一个函数
int add(int a, int b) {return a + b;
}// 另一个函数,返回一个函数指针
int (*getAddFunctionPointer())(int, int) {return &add;
}int main() {// 获取 add 函数的函数指针funcPtr = getAddFunctionPointer();// 通过函数指针调用函数int result = funcPtr(3, 4);  // 相当于调用 add(3, 4),result 等于 7return 0;
}

函数指针在声明时需要指定其指向的函数的签名(参数类型和返回类型),而指针函数的返回类型是一个函数指针类型。

函数指针直接指向一个已存在的函数,可以通过该指针调用该函数;而指针函数返回一个函数指针,需要通过该函数指针再调用相应的函数。

7. 常量指针和指针常量以及区别

常量指针: 是指一旦指向了某个对象,就无法再指向其他对象的指针。

int x = 10;
int* const ptr = &x;  // ptr 是一个常量指针,指向 int 类型的对象// 无法再修改 ptr 指向的对象,但可以修改对象本身的值
*ptr = 20;  // 合法,修改了 x 的值为 20// 以下操作不合法,因为 ptr 是常量指针,不能改变指向
// ptr = &y;  // 错误,无法改变 ptr 的指向

指针常量:是指指向常量对象的指针,一旦指向了某个对象,不能通过该指针修改所指向对象的值。

int x = 10;
const int* ptr = &x;  // ptr 是一个指向常量 int 的指针// 以下操作合法,可以修改 ptr 所指向对象的值
x = 20;  // 修改了 x 的值为 20// 以下操作不合法,因为 ptr 所指向的对象是常量,不能修改其值
// *ptr = 30;  // 错误,不能通过 ptr 修改其指向的对象的值// 以下操作合法,因为 ptr 本身不是常量,可以改变其指向
int y = 50;
ptr = &y;  // 合法,修改了 ptr 的指向为变量 y

常量指针: 强调指针本身是常量但指向对象的值可以改变

指针常量: 强调指针所指向的对象是常量,也就是对象值不可变,但指针的值可以变;

简单来说:const 修饰的右边最近的值是常量,不能被修改。

8. C++程序在内存的分布图

9.

2. 引用

四、

五、

六、


http://www.ppmy.cn/news/1509858.html

相关文章

Java 多线程练习2 (抽奖比较Runnable写法)

MultiProcessingExercise2 package MultiProcessingExercise120240814;import java.util.ArrayList; import java.util.Collections;public class MultiProcessingExercise1 {public static void main(String[] args) {// 需求&#xff1a;// 在此次抽奖过程中&#xff0c;抽奖…

花10亿裁6300人,这家网络巨头不好过

导语 一年两度裁员&#xff0c;涉及超万人。网络业务巨头思科为何作出如此大手笔裁员&#xff1f; 继英特尔之后&#xff0c;大洋彼岸的另一家IT巨头思科&#xff0c;也爆出了裁员计划。 彭博社的消息指出&#xff0c;思科将会进行今年的第二次裁员&#xff0c;涉及的范围为7%的…

Delphi中的魔法注入:依赖注入的神秘面纱

标题&#xff1a;Delphi中的魔法注入&#xff1a;依赖注入的神秘面纱 在软件开发的复杂世界里&#xff0c;Delphi作为一种历史悠久的编程语言&#xff0c;一直在企业级应用开发中占据着一席之地。随着软件工程实践的不断进步&#xff0c;依赖注入&#xff08;Dependency Injec…

开源大模型套壳解决方案

大家好&#xff0c;我是小书童。 本篇给大家介绍一个开源的全套 AI 助手解决方案&#xff0c;GeekAI&#xff0c;它基于 AI 大语言模型 API 实现&#xff0c;自带运营管理后台&#xff0c;开箱即用。集成了 OpenAI&#xff0c;Azure&#xff0c;ChatGLM&#xff0c;讯飞星火&am…

【网络】应用层协议-http协议

应用层协议-http协议 文章目录 1.Http协议1.1什么是http协议1.2认识URL1.3urlencode和urldecode1.4HTTP请求协议格式1.5HTTP响应协议格式1.6HTTP常见的Header1.7HTTP常见状态码1.8HTTP的方法1.8根据url调取对应的服务 2.cookie和session2.1cookie2.2session 3.HTTPS协议3.1对称…

8.13 Day19 Windows服务器(Windows service 2008 R2)上域的搭建 (1)

域服务器&#xff08;DC&#xff09;&#xff1a;安装了活动目录服务的服务器就称为DC。 将三台设备配置在同一网络中&#xff0c;此处将外部网络隔离开&#xff0c;只将他们放在局域网中 服务端网络配置&#xff0c;此时与外部网络彻底隔绝开&#xff0c;且已无法和主机通信&…

无信号区的输电线路监拍装置:从深山到戈壁

无信号区的输电线路监拍装置&#xff1a;从深山到戈壁 随着电网数字化转型的加速推进&#xff0c;输电线路的可视化监控成为确保电力供应稳定和安全的重要手段。然而&#xff0c;由于输电线路往往跨越复杂多样的地理环境&#xff0c;包括人迹罕至的深山老林、高原平原以及远郊…

优化系统性能:解析Web层缓存与Redis应用的挑战与应对策略

在当今高速发展的互联网环境中&#xff0c;系统性能的优化直接关系到用户体验和系统稳定性。Web层缓存和Redis作为常用的技术手段&#xff0c;在提高系统响应速度和减轻数据库压力方面扮演着重要角色。然而&#xff0c;它们的应用也伴随着一系列挑战。本文将深入探讨Web层缓存与…