面试问答2

1、explicit的作用是什么?如何避  免编译器进行隐式转换?

作用 : 防止编译器对该构造函数或转换函数进行隐式类型转换,避免由于隐式类型转化导致的意外行为或错误

避免 : 在类的构造函数或转换运算符前添加 ‘ explicit ’ 关键字

2、父类析构没写虚函数子类写虚函数的后果?

在C++中,如果父类的析构函数不是虚函数而子类中定义了虚函数,那么在使用多态(通过父类指针或引用指向子类对象)删除对象时,会导致未定义行为。这种情况通常会导致子类的析构函数不会被调用,从而可能引发内存泄漏或其他资源没有被正确释放的问题

3、智能指针的实现原理?

智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放的对象,防止内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源

智能指针的实现原理采用计数器的方法,允许多个智能指针指向同一对象,每当多一个指针指向该对象时,指向该对象的所有智能指针内部引用计数加1,每当减少一个智能指针指向对象时,引用计数会减1,当计数为0时自动释放动态分配的资源

4、智能指针和管理的对象分别在那个区

智能指针是局部变量存储在栈区,是动态分配创建的在堆区。管理的对象存储在堆区

5、虚函数的实现原理

虚函数就是偏移量(没有多态,调用的函数一直都是自身)

当编译器发现我的类中有虚函数的时候,编译器会创建一张虚函数表,把虚函数的函数入口地址放到虚函数表中,并且在类中秘密增加一个指针,这个指针就是vpointer(缩写vptr),这个指针之u想对象的虚函数表,在多态调用的时候,根据vptr指针,找到虚函数表来实现动态绑定

6、封装的作用?如何实现封装?

封装是面向对象的核心思想,因为封装解决了代码独立性的问题,从而提高了代码的复用性,为代码的复用性和扩展性提供了基础,封装将成员属性和成员方法绑定在一起,选择访问权限可以实现对外提供接口,对内开放数据从而达到对数据的保护,内在的构造函数和析构函数,不用去考虑释放,提供一种更安全的机制,提高开发效率

类封装的本质,在于将数据和行为,绑定在一起然后通过对象来完成操作,封装时对私有函数添加set/get方法,全局变量会破坏数据的封装可以采用类的静态成员来解决

7、继承

派生类可以使用基类的属性和方法,实现不同类之间的代码复用。继承使得我们可以基于已有的类创建新的类,无需重新编写以实现的代码,派生类可以添加新的属性和方法,或重写(Override)基类的方法来实现新的功能(通过重写虚函数virtual来实现)。

基类提供属性和方法,派生类继承自基类并扩展或修改其行为

单继承:一个派生类只能继承自一个基类

多继承:一个派生类可以同时继承自多个基类

8、什么是多态?多态的使用场景?那些语法体现多态?实现多态的条件?多态的优缺点?

①多态是使用同一接口,传递不同实例对象,执行不同操作。(C语言中共用体就是一种多态)

②当功能不断变化,考虑用多态实现代码的扩展性

③静多态(编译时绑定)的函数重载和模板 和动多态(运行时绑定)的虚函数(少用)

④基类中要有虚函数;派生类要重写基类中的虚函数(override);通过基类的指针或引用绑定派生类的对象

⑤优点:多态解决代码扩展性,增加程序的灵活性,减轻系统升级,维护,调试的工作量和复杂度

    缺点:多态影响效率,造成空间浪费(引入虚函数表指针)

9、#include<>   与  #include " " 的区别?

尖括号是系统或第三方库提供的头文件,他只在系统头文件目录查找

双引号是实现者自定义的头文件,他先在当前目录查找,再去系统头文件目录查找

10、weak_ptr什么时候用?

std::weak_ptr 是C++11引入的一种智能指针,主要用于解决循环引用和资源管理问题,适用处理复杂的对象关系和避免内存泄漏,在设计具有共享和依赖关系时,合理的使用weak_ptr可以提高程序的安全性和效率

11、TCP与UDP的区别?

12、重载和重写的区别?

重载 : 指在同一作用域内定义多个同名的函数,但他们的参数列表不同

重写 : 指在派生类中重新定义基类的虚函数,具有相同的函数签名,实现多态

13、面向过程和面向对象的区别

面向过程是函数套函数,结构套结构;在处理同样一个问题的时候,面向过程的核心思想是命令,是把一个任拆分成多个步骤,并按一定的顺序执行,这样做的耦合度太高,不利于复杂的程序;缺点耦合度高、关联性强、维护性差、扩展性差

面向对象是用统一的对象来建模,整个过程都是基于对象之间的相互通信协作来完成任务的,在处理同样一个问题的时候,面向对象的核心思想是抽象和封装,是把一个过程分解成高内聚低耦合的抽象,对象之间的相互作用来完成任务i的;他的性能比面向过程低,低耦合度低

14、TCP如何避免粘包?

①固定消息长度

②在消息前面加上固定长度的消息头

③控制发送方发送数据的速度

④在消息之间采用特殊的分隔符

15、TCP的核心API

socket():创建一个客户端的无连接、无绑定的socket连接
serverSocket():创建一个服务端ServerSocket,监听来自客户端的Socket
bind():绑定一个Socket到本地地址和端口上
connect():连接到远程socket
accept():服务器socket接收一个客户端发送的请求连接。当没有客户端请求连接时,服务器将处于阻塞状态(默认时间无限长)
write():把数据写入到Socket输出流
read():从Socket输入流中读取数据

listen():将套接字设置为监听模式,等待连接请求

send()recv() :发送和接收数据

16、const和#define的区别

#define是预处理指令,在编译之前对代码中的标识符进行替换,不会进行内存分配,他的作用域是全局的,只是简单的文本替换,没有类型限制和类型检查

const是C/C++语言中的关键字,用于定义常变量,他是类型安全的,并且遵守作用域规则,避免了全局污染的问题,常量在编译期分配内存并进行类型检查,有明确的类型

17、多线程网络传输需要注意什么?

1、线程同步和资源竞争:多线程可以并发访问共享资源,必须考虑同步问题,否则线程间的竞争可能会导致数据损坏或程序崩溃

2、避免死锁:两个或多个线程相互等待对方释放资源,导致程序卡死

3、网络I/O阻塞问题:网络操作通常是阻塞的,这意味着如果没有数据可读或无法发送方数据,线程会进入阻塞状态,直到操作完成。如果没有适当的处理,阻塞会导致系统响应迟缓

4、线程数量控制 : 线程数量过多会导致系统资源耗尽,上下文切换过于频繁,反而会降低系统性能

5、内存管理 : 多线程程序,多个线程可能会同时访问和修改共享数据,导致内存错误和内存泄漏

18、Qt线程间通信

信号与槽机制、线程池、共享内存、消息传递、信号量

19、进程间通信

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

20、虚函数

虚函数是一种在基类中声明的成员函数,它允许在派生类中重写(override),从而实现多态性

21、虚继承

用于解决多继承中的菱形问题,确保在派生类中只保留一个基类实例,避免重复继承导致数据沉余和二义性问题

22、如何确保线程池中的任务能够按照顺序执行?

线程池不保证任务按照提交顺序执行,因为多个线程可能并发执行任务,如果需要严格的顺序控制,可以通过使用额外的同步机制(队列或条件变量)来管理任务的执行顺序

23、线程池中为什么不直接存储线程对象,而是存储指向线程的指针?

存储指向线程的指针可以避免对象的拷贝和移动操作,保证每个线程对象在创建后都能被正确管理和释放。如果存储线程对象本身,可能会引发不必要的拷贝构造和移动构造操作

24、线程池的作用是什么?他是如何提高多线程应用的性能?

线程池的主要作用是通过复用以创建的线程来执行任务,避免频繁创建和销毁线程开销,从而提高性能。线程池提前创建·一定数量的线程,当有任务时,线程池中的线程可以直接处理任务,避免了创建新线程的延迟

25、结构体和联合体的区别?

结构体联合体
内存分配每个成员占用独立的内存空间,结构体的大小是所有成员大小的总和(加上对齐)所有成员共享同一块内存,联合的大小就是最大成员的大小
访问方式各成员可以同时访问和存储,互不干扰同一时间只能存储前一个成员的值,赋值一个成员会覆盖其他成员的值
使用场景使用于需要同时存储多个不同类型的情况使用于多个数据成员互斥使用,需要节省内存的情况
内存效率内存效率的,因为每个成员都有自己的内存空间内存效率高,因为所有成员共享一块空间

26、如何避免死锁?

资源有序分配:为所有可能的锁或资源分配一个全局顺序(编号)。每个线程按固定顺序请求资源,确保不会产生循环等待

避免占有且等待:在一个线程请求新的资源之前,要求它释放已持有的资源。这样可以避免占有且等待条件。

资源剥夺:允许剥夺线程持有的资源,以避免死锁。具体做法是,如果一个线程请求的资源被占用,可以暂时剥夺其他线程持有的资源,强制释放这些资源供当前线程使用。

27、构造函数不能是虚函数,析构函数可以是虚函数

构造函数的主要作用是初始化,C++虚函数的实现依赖于虚函数表,在构造函数执行后才能初始化。

析构函数的作用是销毁对象,释放它所占用的资源,析构函数的虚拟化是为了支持多态性为,确保通过基类指针或引用删除派生类对象时能够正确调用派生类析构函数

28、构造函数可以是纯虚函数,析构函数不能是纯虚函数

纯虚函数的存在并不意味着没有实现,在一些特殊情况下,基类的构造函数可以被设计为纯虚函数,但这不会组织构造对象。即使是纯虚函数,构造函数仍需要定义以保证派生类的初始化。

析构韩式的主要任务是时释放对象的资源,并在对象生命周期结束时进项清理工作。在类的多态继承体系中,析构函数可以是虚函数,以确保通过基类指针删除派生类的析构函数。但是析构函数如果是纯虚函数且没有定义,那么在对象销毁时会缺少必要的清理逻辑。

29、线程通信的方式及其应用场景。

共享内存:简单的同步或数据共享;注意事项:引入同步机制来避免数据竞争问题

互斥量:互斥资源访问;注意事项:死锁问题

条件变量:生产者消费者模型;注意事项:需要与互斥量结合使用,保证条件变量和资源访问的同步安全

信号量:限量资源访问;注意事项:信号量的计数到达上限时,线程必须等待资源可用

读写锁:高频读,低频写 ;注意事项:防止写线程长期被阻塞

消息队列:异步通信;注意事项:注意消息队列溢出问题

管道:单向数据传输;

信号:异步事件通知;注意事项:避免信号处理中调用不安全的系统函数

事件:等待待定条件;

30、槽函数的定义及注意事项?

槽函数本质上是一个普通的成员函数,它可以被信号触发,槽函数的定义与普通函数类似,但是要放在slots:访问控制符之下,或通过Q_OBJECT宏使其成为槽函数。使用connect()函数将信号和槽连接起来。第一个参数:发送信号的对象;第二个参数:信号的名称;第三个参数:接收信号的对象;第四个参数:槽函数的名称,从QT5开始,可以使用lambda表示式,不需要定义一个单独的槽函数

注意事项:信号和槽默认线程安全;使用Q_OBJECT宏;

31、堆栈溢出

栈(后进先出)溢出指栈的使用超出了栈的容量,从而导致程序崩溃或异常

堆栈溢出通常发生在递归调用过深或局部变量占用内存过多的情境中


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值