c++2024/9/9

1.链表和数组的区别
  1. 数组是一片连续的空间,声明时要确定大小;使用前要确定使用的长度

  2. 链表是一片不需连续的动态空间,长度可变,由结点组成,每个结点包含数据元素和指向下一个结点指针;

  3. (数据域和指针域)

  4. 数组的线形查找速度快,查找操作直接使用地址偏移;链表需要按顺序检索结点,效率低;

  5. 链表可快速插入和删除结点,数组则可能需要大量的数据移动;

  6. 链表不存在越界问题;数组存在越界问题;

  7. 总的来说,数组便于查询,,链表便于插入,删除。

2.引用和指针的区别
  1. 引用必须被初始化,却不分配存储空间;指针声明时可以不被初始化,在初始化的时候需要分配存储空间。

  2. 引用初始化后不能被改变,指针可以改变所指向的对象。

  3. 不存在指向null的引用,但存在指向null的指针。

  4. 作为参数传递时,指针需要被解引用*才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;

  5. 使用 sizeof 计算一个指针的大小是 4或8,而引用则是被引用对象的大小;

  6. 可以有 const 指针,但是没有 const 引用;

  7. 指针可以有多级指针(**p),而引用止于一级;

3.指针的定义

指针也就是地址,指针变量是存放某个变量内存地址的变量。

指针是某种数据类型的变量,存储着变量的地址。

指针的作用是用来存储内存地址,通过指针可以间接地访问和修改内存中的数据(指向变量的值)。

指针在编程中非常有用,它可以用于动态内存分配、传递函数参数、实现数据结构等。

但是指针也需要谨慎使用,因为如果指针没有正确初始化或者指向了无效的内存地址,就可能导致程序崩溃或者出现未定义的行为。因此,在使用指针时需要注意对指针进行正确的初始化和操作。

4.如何避免野指针

产生原因:

  1. 指针变量声明时没有被初始化,解决办法是声明时初始化,可以是指向具体的地址,可以是指向NULL;

  2. 指针被释放内存后(free或delete之后),没有置为NULL,解决办法是指针指向的内存空间被释放后,指针应该指向NULL;

  3. 指针操作超越了变量的作用范围,解决办法是在变量的作用域结束前,释放掉变量的地址空间并且让指针指向NULL;(一个函数内的指针没delete,置为null, 在main函数才delete和置null)

5.函数的三种传参方式
  1. 值传递,将参数的值复制一份到函数内部的参数,在函数内部,对形参的修改无法修改实参的内容。

  2. 地址/指针传递,通过指针传递参数,函数接收指针作为形参,可以通过指针来访问和修改原始参数的值。

  3. 引用传递,引用给实参取一个别名,通过引用传递参数,使得函数内部可以直接访问和修改原始参数的值。可以节省内存空间,提高操作效率,是危险操作。

6.简述冒泡排序
  1. 冒泡排序通过重复地遍历待排序的列表,比较相邻的元素并将它们按照升序或降序的方式进行交换。

  2. 冒泡排序的基本思想是外层循环控制遍历次数,内层循环用于比较相邻元素并进行交换。通过多次遍历,每一次最大的元素逐渐“冒泡”到列表的末尾。

  3. 冒泡排序的时间复杂度为O(n^2),其中n是待排序列表的长度。

7.简述快速排序

在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。

8.简述选择排序

首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。重复该操作,直到所有元素均排序完毕。

9.简述TCP与UDP协议的区别。
  1. TCP 是面向连接的,UDP 是无连接的,即发送数据前不需要先建立链接。

  2. TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,不保证可靠交付。

  3. TCP 是面向字节流,会进行拥塞控制,UDP 面向报文,并且没有拥塞控制,网络出现拥塞不会使得发送速率降低。

  4. TCP 只能是 1 对 1 。

  5. UDP 支持 1 对 1,   和 1 对多。

10.三次握手
  1. 客户端发送一个同步包给服务器,请求建立连接。

  2. 服务器收到同步包后,回复一个同步和确认包给客户端,表示确认收到请求,并同意建立连接。

  3. 客户端收到服务器的同步和确认包后,再发送一个确认包给服务器,表示收到确认,并告知服务器可以开始传输数据。

11.sizeof 操作符和strlen库函数的区别
  1. sizeof操作符计算的是数据类型或变量所占用的内存空间的大小,包括结尾的空字符。

  2. strlen: 计算字符串的长度,不包括结尾的空字符。

  3. 编译器在编译时期就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。

12.局部变量和全局变量的区别?

保存位置不同

全局变量保存在内存的全局存储区中,占用静态的存储单元;局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。

作用域不同

局部变量的作用域仅限于定义它的函数内,全局变量的作用域是整个程序。

生命周期不同

局部变量在函数调用时分配,函数返回时释放。全局变量在程序开始时分配,程序结束时释放。

可见性不同

局部变量仅在定义它的函数内可见。全局变量在程序的任何位置可见。

13.static的作用
  1. 限制变量和函数的作用域,防止被其他文件的代码访问。

  2. 延长变量的生命周期,在程序刚开始运行时就完成初始化,直到程序运行结束才释放。

  3. 修饰代静态码块:静态代码块只会在程序开始时执行一次。

14.malloc和new的区别
  1. 他们的作用都是申请内存空间;

  2. 使用new申请内存分配时不需要指定内存的大小,编译器会根据类型自行计算;new,delete是操作符,使用范围在c++;

  3. 使用malloc申请内存分配时则需要指出所需内存的大小。malloc,free是函数,使用范围是c和c++;

  4. 使用new分配内存时调用对象的构造函数,delete释放内存调用对象的析构函数;

  5. malloc仅分配内存,free仅释放内存,并不执行构造和析构函数;

  6. new,delete返回的是某种类型的指针;malloc,free返回的是void型指针;

15.strcpy,sprintf,mencpy的区别

这些都用于拷贝,针对不同的对象,选择合适的函数实现拷贝功能

  1. 操作对象的不同,strcpy的两个操作对象都是字符串,sprintf的操作员对象可以是多种数据类型,目的操作对象是字符串,memcpy的2个对象是两个任意可操作的内存地址,并不限于何种数据类型。
  2. 执行效率不同,mencpy最高,strcpy其次,sprintf最低。

16.volatile、inline是什么含义,有什么用?
  1. volatile: 强制编译器每次从内存中取得该变量的值,而不是从被优化后的寄存器/缓存中读取。
  2. inline: inline关键字仅仅是建议编译器做内联展开处理,即是将函数直接嵌入调用程序的主体,省去了调用/返回指令。
17.volatile、inline的使用场合

内联函数使用场合:对于简短的函数并且调用次数比较多的情况,适合使用内联函数。

在以下几种情况下需要使用 volatile 修饰符:

  1. 在多线程环境中,用于确保变量的值在多个线程之间可见。

  2. 用于与外部设备交互的变量,例如 I/O 设备的状态变量。

  3. 用于表示中断或异常发生的变量。

18.typedef和define有什么区别
  1. 用法不同:typedef用来定义一种数据类型的别名,增强程序的可读性。define主要用来定义常量,以及书写复杂,使用频繁的宏。

  2. 执行时间不同:typedef是编译过程的一部分,有类型检查的功能。define是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。

  3. 作用域不同:typedef有作用域限定。define不受作用域约束,只要是在define声明后的引用都是正确的。

  4. 对指针的操作不同:typedef和define定义的指针时有很大的区别。

  5. typedef定义是语句,句尾要加上分号。而define不是语句,千万不能在句尾加分号。

注意:typedef定义是语句,因为句尾要加上分号。而define不是语句,千万不能在句尾加分号。

19.内联函数和宏定义区别:
  1. 内联函数在编译时展开,而宏在预编译时展开。

  2. 在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。

  3. 内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能。

  4. 宏不是函数,而inline是函数。

20.const是什么

const可以用来定义一个只读(不能修改其值)的变量或对象。

主要优点:便于类型检查、同宏定义一样可以方便地进行参数的修改和调整、节省空间,避免不必要的内存分配、可为函数重载提供参考。

说明:const修饰函数参数,是一种编程规范的要求,便于阅读,一看即知这个参数不能被改变,实现时不易出错。

21.static 有什么作用

static 在 C 中主要用于定义全局静态变量、定义局部静态变量、定义静态函数。

在 C++中新增了两种作用:定义静态数据成员、静态函数成员。

注意:因为 static 定义的变量分配在静态区,所以其定义的变量的默认值为 0,普通变量的默认值 为随机数,在定义指针变量时要特别注意。

22.static全局变量与普通的全局变量有什么区别?

static全局变量只初使化一次,限制其作用域,防止在其他文件单元中被引用;

23.extern的作用

extern标识的变量或者函数声明其定义在别的文件中,提示编译器遇到此变量和函数时在其它模块中寻找其定义。

24.C++面向对象的三大特性是封装、继承和多态。
  1. 封装:封装是指将数据(属性)和操作数据的函数(方法)封装在一起,形成一个类。通过封装,可以将数据和方法进行隐藏,只对外提供必要的接口(获取数据)。这样可以保护数据的安全,并且提高代码的可维护性和复用性。

  2. 继承:继承是指一个类可以派生出子类,子类可以继承父类的属性和方法。通过继承,子类可以重用父类的代码,同时还可以添加新的属性和方法,实现代码的扩展和复用。

  3. 多态:多态是指同一个方法可以在不同的对象上具有不同的行为。多态可以通过继承和接口实现。在C++中,多态可以通过虚函数实现。通过多态,可以实现同一个方法在不同的对象上表现出不同的行为。这样可以提高代码的灵活性和可扩展性。

c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。

多态的实现原理是通过虚函数(virtual function)和动态绑定(dynamic binding)来实现的。在基类中将某个成员函数声明为虚函数,派生类可以对其进行重写。当通过基类指针或引用调用虚函数时,会根据实际对象的类型来动态绑定到相应的函数实现,而不是根据指针或引用的类型来确定调用哪个函数。

动态绑定使得程序在运行时根据对象的实际类型来确定调用的函数,实现了多态的效果。这样可以提高代码的灵活性和可扩展性,使得程序更加易于维护和扩展。

菱形继承(Diamond Inheritance)是指一个派生类同时继承自两个或多个基类,而这些基类又继承自同一个基类。这种继承关系形成了一个菱形的结构。菱形继承可能引发菱形继承问题,即多次继承同一个基类可能导致成员变量和成员函数的二义性。为了解决这个问题,C++使用虚继承(virtual inheritance)来避免多次继承同一个基类,确保只有一份基类的实例被派生类继承。虚继承可以通过在继承关系中使用关键字virtual来实现。

从什么引出什么!!!!!!
25.成员函数的覆盖,重载和隐藏的区别

范围:覆盖/被覆盖的函数在两个类中;重载/被重载的函数都在一个类中。

参数:覆盖/被覆盖的函数的参数列表一定相同;重载/被重载的函数参数列表一定不同。

virtual 的区别:覆盖的基类中被覆盖的函数必须要有 virtual 修饰,而重载函数和被重载函数可以被virtual 修饰,也可以没有。

26.列出你知道的Linux和Windows线程同步方法。

在Linux中线程同步的常见方法: 在WIN32中,同步机制主要有以下几种:

(1)互斥锁;    (1)事件(Event);

(2)条件变量;(2)信号量(semaphore);

(3)读写锁;    (3)互斥量(mutex);

(4)信号量;    (4)临界区(Critical section);

27.进程间通信的方式有哪些?

管道、消息队列 、共享内存、信号、信号量、套接字

28.简述一下交叉编译器的作用。

在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,这个编译过程就叫交叉编译.简单地说,就是在一个平台上生成另一个平台上的可执行代码.

29.Qt的信号和槽机制

信号和槽是Qt框架中用于对象间通信的机制。信号是对象发出的事件,槽是对象对信号做出的响应。通过连接信号和槽,可以实现对象之间的通信和交互。

Qt的信号和槽机制是通过元对象系统实现的。在编译时,Qt会为每个QObject派生类生成一个元对象,其中包含了类的信号和槽的信息。通过元对象系统,可以在运行时动态连接信号和槽。

30.简述指针常量与常量指针区别

指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。(引用)

常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来修改这个对象的值。

注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。

31.数组名和指针的区别

注意:一定要记得数组名并不是真正意义上的指针,它的内涵要比指针丰富的多。但是当数组名当 做参数传递给函数后,其失去原来的含义,变作普通的指针。

另外要注意 sizeof 不是函数,只是操作符。

32.常引用

常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。

常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。

说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象,不能在函数中改变实参的值,这样的好处是有较高的易读性和较小的出错率。

35.虚函数

在基类中定义一个函数,并在派生类中进行覆盖。通过使用虚函数,可以实现多态性,即在运行时根据对象的实际类型来调用相应的函数。

在函数声明前面加上关键字"virtual"。

36.函数重载

作用:一个同名函数完成不同的功能,多用。

实现方法:

  1. 函数名相同

  2. 函数的参数不同 (个数不同,类型不同,位置不同)

  3. 返回值类型不同--无法实现函数重载

37.构造函数

作用:初始化类中的数据成员

特点:

  1. 函数没有返回值
  2. 函数名与类名相同
  3. 函数无需调用,在创建类的对象时自动调用
  4. 构造函数必须要设计在公共区 (PS:放入私有区,将无法创建对象)或静态函数调用
  5. 构造函数可以重载
38.析构函数

作用:删除所创建的对象

特点:

  1. 析构函数的名称与类名相同,但是前面要加上一个波浪号(~)作为标识符,例如 ~MyClass()。
  2. 析构函数没有返回值,也不需要参数。
  3. 它会在对象销毁时自动调用,无法手动调用。(用于释放对象占用的内存和资源)
  4. 可以设置为虚函数。
  5. 只有一个,不能被重载。

注意:析构函数名与类名不同不是析构函数的特征,而是析构函数的命名规则(~)。为了便于区分。

39.重载-覆盖

重载是在同一个作用域中通过函数名和参数列表的不同来定义多个函数,

覆盖是子类重新定义父类中的虚函数,使得子类对象在调用该函数时执行子类中的实现。

40.头文件中的 ifndef/define/endif 干什么用?

确保头文件只被包含一次,防止该头文件被重复引用。

41.#include <filename.h> 和 #include “filename.h” 有什么区别?

对于#include <filename.h> ,编译器从标准库路径开始搜索 filename.h ;

对于#include “filename.h” ,编译器从用户的工作路径开始搜索 filename.h ,找不到再从标准库路径找;

42.大端存储和小端存储

小端存储是指将数据的低位字节     存储在内存的低地址处,高位字节存储在高地址处;

而大端存储则是将数据的高位字节 存储在内存的低地址处,低位字节存储在高地址处。


43.宏定义与函数的区别

宏是一种预处理器指令,它在编译时会被替换为相应的代码,而函数是一段独立的可执行代码,需要在运行时调用才能执行。

运行速度上宏要比函数的运行速度快, 函数需要-调用并且返回值这个过程,而宏却不需要。

44.全局变量和局部变量分别保存在哪个位置,函数内static定义的变量是存放在哪里的?栈堆有什么区别?

全局变量在静态存储区/数据段

局部变量在栈区

static定义的变量是静态存储区

new申请内存分配的变量存放在堆区

45.队列和栈的异同
  1. 都是线形存储结构
  2. 插入和删除数据的操作不同;队列先进先出;栈先进后出
46.栈堆Heap与stack的差别。

1、空间分配区别;2、缓存方式区别;3、数据结构区别

Heap是堆,stack是栈。 Stack的空间由操作系统自动分配/释放,Heap上的空间由程序员手动分配/释放。 Stack空间有限,Heap是很大的自由存储区 C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。 程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行

47.class 和 struct 的区别?

在C++中,struct与class唯一的区别就是在于 默认的访问权限不同。

区别:

struct 默认权限为公共权限-定义一种简单的数据结构

class 默认权限为私有权限-定义一种包含私有成员的复杂数据类型

除了默认的成员访问权限之外,class 和 struct 在其他方面基本上是相同的。它们都可以包含成员变量和成员函数,并且都可以使用继承、多态等面向对象的特性。


48.运算符重载的三种方式 ?

答:普通函数,友元函数,类成员函数。

49.对象间是怎样实现数据的共享的 ?

答:通过类的静态成员变量来实现对象间的数据共享。
静态成员变量占有自己独立的空间不为某个对象所私有。

50.静态函数存在的意义 ?

答:静态私有成员在类外不能被访问,可通过类的静态成员函数来访问;
当类的构造函数是私有的时,不像普通类那样实例化自己,只能通过静态成员函数来调用构造函数。

51.函数重载是什么意思 ? 它与虚函数的概念有什么区别 ?

函数重载是一个同名函数完成不同的功能,编译系统在编译阶段通过函数参数个数、参数类型不同,函数的返回值来区分该调用哪一个函数,即实现的是静态的多态性。
但是记住:不能仅仅通过函数返回值不同来实现函数重载。

虚函数实现的是在基类中通过使用关键字virtual来申明一个函数为虚函数,含义就是该函数的功能可能在将来的派生类中定义或者在基类的基础之上进行扩展,系统只能在运行阶段才能动态决定该调用哪一个函数,所以实现的是动态的多态性。它体现的是一个纵向的概念,也即在基类和派生类间实现。

52.虚函数是怎么实现的 ? 

虚函数是一种实现多态的机制,它可以让基类的指针或引用根据实际指向的对象类型来调用相应的成员函数。虚函数的实现原理是通过虚函数表和虚函数表指针来实现的。
每个含有虚函数的类都有一个虚函数表,存储了该类的所有虚函数的地址。
每个含有虚函数的对象都有一个虚函数表指针,指向该对象所属类的虚函数表。当通过基类的指针或引用调用虚函数时,程序会先通过虚函数表指针找到虚函数表,然后再通过虚函数表找到对应的虚函数地址,最后调用该地址所指向的函数。这样就实现了动态绑定,即在运行时根据对象类型确定调用哪个函数。

53.在什么时候使用常引用 ?

答:如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

54.const有什么用途?
  1. 可以定义 const 常量,只读
  2. const 可以修饰函数的参数、返回值,甚至函数的定义体。被 const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
  3. 可以节省空间,避免不必要的内存分配
55.有一个单向链表,如何找到其中的中间项(描述思路即可)。
  1. 定义两个指针,快慢指针;判断链表是否为空?
  2. 快慢指针都指向头结点;
  3. 使用循环,结束条件是快指针指向空或 快指针的下一个节点为空;
  4. 循环体内,当慢指针移动一个节点时,快指针移动2个节点,当循环结束时,返回慢指针指向的节点;
56.二分搜索算法

二分查找算法是一种高效的查找算法,用于在(不重复)有序数组或有序列表中查找指定元素的位置。二分查找的基本思想是将查找区间不断缩小为原来的一半,直到找到目标元素或确定目标元素不存在为止。具体步骤如下:(左闭右开)

初始化左边界left为0,右边界right为数组长度减1。

当左边界小于等于右边界时,执行以下步骤:

a. 计算中间位置mid,即mid =left + (left + right) / 2。

b. 如果中间位置的元素等于目标元素,则返回mid。

c. 如果中间位置的元素大于目标元素,则将右边界更新为mid-1。

d. 如果中间位置的元素小于目标元素,则将左边界更新为mid+1。

如果循环结束时仍未找到目标元素,则返回-1表示目标元素不存在。

二分查找的时间复杂度为O(log n),其中n为数组或列表的长度。由于每次查找都将查找区间缩小为原来的一半,因此二分查找的效率非常高。然而,二分查找的前提是数组或列表必须是有序的,否则无法使用该算法进行查找。

57.stl的vector、map、list、hashMap,vector底层
  1. vector:vector是一个动态数组,底层使用连续的内存空间存储元素,支持随机访问和动态扩容。当元素数量超过当前容量时,vector会重新分配更大的内存空间,并将原有元素拷贝到新的内存空间中。

  2. map:map是一个关联容器,存储键值对,底层使用红黑树(Red-Black Tree)实现。红黑树是一种自平衡的二叉搜索树,保持了插入、删除和查找操作的对数时间复杂度。

  3. list:list是一个双向链表,底层使用指针连接节点,支持高效的插入和删除操作,但不支持随机访问。

  4. hashMap:hashMap是一个哈希表,底层使用哈希函数将键映射到对应的存储位置。哈希表通过哈希函数将键映射到桶(bucket),每个桶中存储一个链表或红黑树,用于解决哈希冲突。


在C语言中有哪些标准库函数,列出函数名?并描述Abs函数。

<stdef.h>标准定义

<ctype.h>字符处理函数

<stdlib.h>功能函数

<errno.h>错误函数

<stdio.h>输入输出函数

<stdlib.h>字符串函数

<math.h>数学函数

Abs函数是一个数学函数,用于计算一个数的绝对值。

SPI

即串行外设接口,是一种用于芯片通信的同步串行通信接口规范,主要应用于单片机系统中。SPI接口是一种高速、全双工、同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片管脚,同时为PCB的布局上节省空间,提供方便。

IIC

设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。

I2C使用两根信号线进行通信:一根时钟线SCL,一根数据线SDA

调用动态/静态链接库的两种方式。

第一种是静态链接方式,在这种方式下,静态链接库中的所有数据代码都将拷贝到调用程序的代码空间中去,此时它和调用程序本身的函数没有什么区别;

第二种是动态链接方式,在这种方式下,动态链接库中的数据代码是在需要的时候才拷贝到内存中去的;

基于 TCP-服务器

  1. 创建一个 socket,用函数 socket()

  2. 绑定 IP 地址、端口等信息到 socket 上,用函 数 bind()

  3. 设置允许的最大连接数,用函数 listen()

  4. 接收客户端上来的连接,用函数 accept()

  5. 收发数据,用函数 send()和 recv(),或者 read()和 write()

  6. 关闭网络连接

静态变量和动态变量有什么区别?

区别:

属性静态变量,全局变量动态变量,形参
存储位置静态存储区动态存储区
生命周期程序结束时释放函数调用时分配,函数返回时释放
初始化方式可以不初始化,也可以初始化必须初始化

1.重载:一个同名函数完成不同的功能。

2.引用:为一个变量起别名。

3.内置函数:不执行调用,而是在编译时直接把代码接入到调用点处,节省运行时间,但增加了目标程序长度。

4.1封装:封装是指将数据和操作数据的函数封装在一起,形成一个类。

4.2继承:继承是指一个类可以派生出子类,子类可以继承父类的属性和方法。

4.3多态性:多态是指同一个方法可以在不同的对象上具有不同的行为。

5.struct 定义类和 class 定义类的区别:默认的访问权限不同。

6.构造函数:用来处理对象的初始化。初始化类中的数据成员

7.虚基类:他们在继承间接共同基类时只保留一份成员,虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式声明的。

8.虚函数:在基类声明函数是虚拟的,并不是实际存在的函数,然后在派生类中才正式定义此函数。

9.纯虚函数有以下特点:纯虚函数仅仅是用来为基类的派生类中的函数保留位置;纯虚函数 在基类中没有定义,它们被初始化为 0;当虚函数变成纯虚函数时,任何派生类都必须给出 它自己的定义。否则编译出错。

10.函数模板:建立一个通用的函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代替

11.面向对象程序设计有四个主要特点:抽象,封装,继承和多态性。

12.静态成员函数与非静态成员函数的区别:前者没有 this 指针,后者有 this 指针。 静态成员函数只能用来访问静态数据成员,而不访问非静态成员。

13.转换构造函数:讲一个其他类型的数据转换成一个类的对象,只有一个形参。

14.类型转换函数:将一个类的对象转换成另一个类型的数据,只能作为成员函数。有 operator 关键字,无形参

提问

一、基本问题

1、const、static作用。

2、c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。

3、虚析构的必要性,引出内存泄漏,虚函数和普通成员函数的储存位置,虚函数表、虚函数表指针。

4、malloc、free和new、delete区别,引出malloc申请大内存、malloc申请空间失败怎么办。

5、stl熟悉吗,vector、map、list、hashMap,vector底层,map引出红黑树。优先队列用过吗,使用的场景。无锁队列听说过吗,原理是什么(比较并交换)

6、实现擅长的排序,说出原理(快排、堆排)

7、四种cast,智能指针

8、tcp和udp区别

9、进程和线程区别。

10、指针和引用作用以及区别。

11、c++11用过哪些特性,auto作为返回值和模板一起怎么用,函数指针能和auto混用吗。

12、boost用过哪些类,thread、asio、signal、bind、function

13、单例、工厂模式、代理、适配器、模板,使用场景。

14、QT信号槽实现机制,QT内存管理,MFC消息机制。

15、进程间通信。会选一个详细问。

16、多线程,锁和信号量,互斥和同步。

17、动态库和静态库的区别。

二、回答基本问题

1、const、static作用。

2、c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。

多态的实现原理是通过虚函数(virtual function)和动态绑定(dynamic binding)来实现的。在基类中将某个成员函数声明为虚函数,派生类可以对其进行重写。当通过基类指针或引用调用虚函数时,会根据实际对象的类型来动态绑定到相应的函数实现,而不是根据指针或引用的类型来确定调用哪个函数。

动态绑定使得程序在运行时根据对象的实际类型来确定调用的函数,实现了多态的效果。这样可以提高代码的灵活性和可扩展性,使得程序更加易于维护和扩展。

菱形继承(Diamond Inheritance)是指一个派生类同时继承自两个或多个基类,而这些基类又继承自同一个基类。这种继承关系形成了一个菱形的结构。菱形继承可能引发菱形继承问题,即多次继承同一个基类可能导致成员变量和成员函数的二义性。为了解决这个问题,C++使用虚继承(virtual inheritance)来避免多次继承同一个基类,确保只有一份基类的实例被派生类继承。虚继承可以通过在继承关系中使用关键字virtual来实现。

3、虚析构的必要性,引出内存泄漏,虚函数和普通成员函数的储存位置,虚函数表、虚函数表指针。

4、malloc、free和new、delete区别,引出malloc申请大内存、malloc申请空间失败怎么办。

当使用malloc函数申请大内存时,有可能会出现内存不足的情况,导致malloc申请空间失败。在这种情况下,可以采取以下几种处理方式:

  1. 检查返回值:在调用malloc函数后,需要检查其返回值是否为NULL。如果返回值为NULL,则表示malloc申请空间失败。可以通过判断返回值是否为NULL来确定是否申请成功。

  2. 释放已分配的内存:如果malloc申请空间失败,可以先释放已经成功申请的内存,以释放一部分内存空间,然后重新尝试申请大内存。可以使用free函数来释放已分配的内存。

  3. 使用realloc函数:如果malloc申请空间失败,可以尝试使用realloc函数来重新分配内存。realloc函数可以重新分配已分配内存的大小,如果传入NULL指针,则相当于malloc函数。可以通过realloc函数来尝试重新申请更大的内存空间。

  4. 错误处理:在malloc申请空间失败时,可以根据具体情况进行错误处理。可以输出错误信息,记录日志,或者采取其他适当的措施来处理内存申请失败的情况。

5、stl熟悉吗,vector、map、list、hashMap,vector底层,map引出红黑树。优先队列用过吗,使用的场景。无锁队列听说过吗,原理是什么(比较并交换)

我熟悉STL(Standard Template Library),它是C++标准库中的一个重要组成部分,提供了一系列的容器、算法和函数模板,用于简化和加速C++程序的开发。

  1. vector:vector是一个动态数组,底层使用连续的内存空间存储元素,支持随机访问和动态扩容。当元素数量超过当前容量时,vector会重新分配更大的内存空间,并将原有元素拷贝到新的内存空间中。

  2. map:map是一个关联容器,存储键值对,底层使用红黑树(Red-Black Tree)实现。红黑树是一种自平衡的二叉搜索树,保持了插入、删除和查找操作的对数时间复杂度。

  3. list:list是一个双向链表,底层使用指针连接节点,支持高效的插入和删除操作,但不支持随机访问。

  4. hashMap:hashMap是一个哈希表,底层使用哈希函数将键映射到对应的存储位置。哈希表通过哈希函数将键映射到桶(bucket),每个桶中存储一个链表或红黑树,用于解决哈希冲突。

优先队列(Priority Queue)是一个特殊的队列,每个元素都有一个优先级,按照优先级大小进行排列。优先队列通常使用堆(Heap)数据结构来实现,堆是一种完全二叉树,满足堆序性质。优先队列常用于解决一些和优先级相关的问题,如任务调度、最小/最大值查找等。

无锁队列(Lock-Free Queue)是一种并发数据结构,用于解决多线程环境下的数据共享和同步问题。无锁队列的原理是使用原子操作(比较并交换,CAS)来实现对队列的操作,避免了使用锁导致的线程等待和阻塞。无锁队列常用于高并发场景,提高了程序的并发性能和响应速度。

6、实现擅长的排序,说出原理(快排、堆排)

7、四种cast,智能指针

在C++中,有四种类型转换(cast)操作符可以用来进行类型转换,它们分别是:

  1. 静态转换(static_cast):用于非多态类型之间的转换,如基本类型之间的转换、父类指针/引用与子类指针/引用之间的转换等。静态转换在编译时进行类型检查,但不提供运行时类型检查。

  2. 动态转换(dynamic_cast):用于多态类型之间的转换,即基类指针/引用与派生类指针/引用之间的转换。动态转换在运行时进行类型检查,如果转换失败(即指针指向的对象不是目标类型的派生类),则返回空指针(对于指针)或抛出std::bad_cast异常(对于引用)。

  3. 重新解释转换(reinterpret_cast):用于任意类型之间的转换,如指针之间的转换、指针与整数类型之间的转换等。重新解释转换不会进行任何类型检查,它只是将一个指针或引用的位模式重新解释为另一种类型的位模式。

  4. 常量转换(const_cast):用于去除const属性,即将const修饰的指针或引用转换为非const类型。常量转换可以用于修改非const对象的常量属性,但是修改const对象是未定义行为。

智能指针是C++中用于管理动态分配内存的一种机制。它们提供了自动释放内存的功能,避免了手动调用delete来释放内存,从而减少了内存泄漏的风险。常见的智能指针包括std::shared_ptr、std::unique_ptr和std::weak_ptr。

  • std::shared_ptr是一种共享所有权的智能指针,多个shared_ptr可以指向同一个对象,当最后一个shared_ptr销毁时,会自动释放对象的内存。

  • std::unique_ptr是一种独占所有权的智能指针,同一时间只能有一个unique_ptr指向一个对象,当unique_ptr销毁时,会自动释放对象的内存。

  • std::weak_ptr是一种弱引用的智能指针,它可以指向一个由shared_ptr管理的对象,但不会增加对象的引用计数。weak_ptr可以用于解决shared_ptr之间的循环引用问题。

智能指针的使用可以提高程序的安全性和可维护性,避免内存泄漏和悬空指针等问题。

8、tcp和udp区别

9、进程和线程区别。

进程和线程是操作系统中的两个重要概念,它们之间有以下几点区别:

  1. 资源分配:进程是操作系统中资源分配的基本单位,每个进程都有独立的地址空间、文件描述符、运行时环境等。而线程是进程内的执行单元,多个线程共享同一个进程的资源,包括地址空间、文件描述符等。

  2. 调度和切换:进程之间的切换需要保存和恢复整个进程的上下文,包括程序计数器、寄存器等。而线程之间的切换只需要保存和恢复线程的上下文,开销较小。

  3. 并发性:进程是独立执行的,多个进程之间可以并发执行,彼此之间相互独立。而线程是共享进程资源的,多个线程可以在同一个进程中并发执行,共享进程的地址空间和资源。

  4. 通信和同步:进程之间通信需要使用特定的机制,如管道、消息队列、共享内存等。而线程之间通信更加方便,可以直接读写共享变量。线程之间还可以使用同步机制,如互斥锁、条件变量等来实现线程的同步和互斥操作。

  5. 创建和销毁:创建和销毁进程的开销较大,需要分配和释放资源。而创建和销毁线程的开销较小,只需要分配和释放线程的上下文即可。

总的来说,进程是操作系统中资源分配的基本单位,拥有独立的地址空间和资源,进程之间相互独立;而线程是进程内的执行单元,共享进程的资源,可以实现并发执行和共享数据。线程的切换开销较小,通信和同步更加方便,但需要注意线程之间的同步和互斥问题。

10、指针和引用作用以及区别。 11、c++11用过哪些特性,auto作为返回值和模板一起怎么用,函数指针能和auto混用吗。 12、boost用过哪些类,thread、asio、signal、bind、function 13、单例、工厂模式、代理、适配器、模板,使用场景。 14、QT信号槽实现机制,QT内存管理,MFC消息机制。 15、进程间通信。会选一个详细问。 16、多线程,锁和信号量,互斥和同步。 17、动态库和静态库的区别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值