f 物联网-嵌入式八股-C++面试35题(20240820) - Powered by qidao123.com技术社区

嵌入式八股-C++面试35题(20240820)

打印 上一主题 下一主题

主题 2043|帖子 2043|积分 6129

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. C++基础与语言特性

1.1 构造函数是否能声明为虚函数?为什么?什么情况下为错误?

答复:
构造函数不能声明为虚函数。构造函数用于初始化对象,而虚函数机制依赖于对象的虚函数表(vtable)。在构造对象时,vtable尚未完全建立,因此无法调用虚函数。如果构造函数可以声明为虚函数,那么在基类构造函数中调用虚函数大概会导致调用派生类未初始化的虚函数,从而导致未定义举动。
1.2 类中static函数是否能声明为虚函数?

答复:
类中的static函数不能声明为虚函数。静态函数属于类本身而不是某个对象,因此它们不依赖于具体的对象实例,自然也就没有虚函数机制。
1.3 哪些函数不能被声明为虚函数?

答复:
以下函数不能声明为虚函数:


  • 构造函数
  • 静态成员函数
  • 内联函数(虽然内联函数可以是虚函数,但其内联效果大概会因虚函数的性子而丧失)
1.4 explicit用在哪里?有什么作用?

答复:
explicit关键字用于构造函数,防止构造函数在单参数的情况下被隐式调用。它的作用是制止隐式转换大概导致的编译器错误或不测举动。通常用于防止单参数构造函数被错误地用于隐式范例转换。
2. 内存管理与指针

2.1 指针占用的大小是多少?

答复:
指针的大小取决于编译器和处理器的架构。在32位体系上,指针大小为4字节(32位);在64位体系上,指针大小为8字节(64位)。
2.2 野指针和内存泄漏是什么?如何制止?

答复:
野指针是指向已释放或未初始化内存的指针,使用它大概导致未定义举动。内存泄漏是指分配的内存没有被正确释放,从而造成内存的浪费。
制止方法


  • 初始化指针,在使用完后将其置为nullptr。
  • 使用智能指针(如std::unique_ptr或std::shared_ptr)来自动管理内存。
  • 制止手动管理内存,尽量使用STL容器
2.3 malloc和new的区别是什么?

答复:


  • malloc是C语言的内存分配函数,仅分配内存,不会调用构造函数。
  • new是C++中的操纵符,分配内存并调用构造函数。
  • 使用malloc分配的内存需要使用free释放,而使用new分配的内存需要使用delete释放。
2.4 C++中空类的大小是多少?

答复:
在C++中,空类的大小为1字节。C++标准要求空类也应占用肯定的内存空间,以确保两个不同对象的地址不同。
2.5 空类中有什么函数?

答复:
空类中有以下默认生成的函数:


  • 默认构造函数
  • 默认析构函数
  • 拷贝构造函数
  • 拷贝赋值运算符
  • 移动构造函数(C++11及以后)
  • 移动赋值运算符(C++11及以后)
2.6 成员变量初始化的顺序是什么?

答复:
成员变量的初始化顺序按照它们在类中声明的顺序举行,而不是按照初始化列表中的顺序。这点很紧张,特别是在成员变量之间存在依赖关系时。
3. STL与容器

3.1 什么是STL?

答复:
STL(Standard Template Library,标准模板库)是C++标准库的一部分,包罗了一组通用算法、容器、迭代器和函数对象,帮助开发者更方便地举行数据操纵和管理。
3.2 解释阐明一下map和unordered_map

答复:


  • map是基于红黑树实现的有序关联容器,支持按键值顺序遍历,查找、插入、删除操纵的时间复杂度为O(log n)。
  • unordered_map是基于哈希表实现的无序关联容器,查找、插入、删除操纵的平均时间复杂度为O(1),但最坏情况下大概退化为O(n)。
3.3 哈希碰撞的处理方法

答复:
哈希碰撞是指两个不同的键值颠末哈希函数盘算后得到雷同的哈希值。处理方法包括:


  • 开放地址法:寻找下一个空闲位置存放辩论的键值。
  • 链地址法:将辩论的键值存在同一个链表中。
  • 再哈希法:使用第二个哈希函数重新盘算辩论的键值的存放位置。
3.4 unordered_map的扩容过程

答复:
unordered_map在元素数量超过当前容量时会自动扩容,通常通过增长桶的数量来实现。扩容后,所有元素将重新盘算哈希值并移动到新的桶中。这是一个相对昂贵的操纵。
3.5 vector中的push_back()和emplace_back()的区别、以及使用场景

答复:
push_back()将一个元素的副本添加到vector的末尾,需要调用拷贝构造函数或移动构造函数;emplace_back()在vector的末尾直接构造元素,制止了不必要的拷贝或移动操纵,实用于需要直接转达构造参数的情况。
3.6 vector扩容,resize和reserve的区别

答复:


  • resize用于改变vector的大小,如果新大小大于当前大小,会默认初始化新元素。
  • reserve仅预留存储空间,改变capacity但不改变size,实用于提前分配空间以减少多次分配的开销。
3.7 vector扩容为了制止重复扩容做了哪些机制?

答复:
vector在需要扩容时会按照肯定的比例(通常是2倍)增长容量,以减少频繁分配内存的开销。这种几何增长策略使得扩容操纵的摊销成本较低。
3.8 vector如何判断应该扩容?(size和capacity)

答复:
vector的扩容是在size达到capacity时触发的。当添加新元素时,如果size等于capacity,则vector会自动扩容。
3.9 对比迭代器和指针的区别

答复:


  • 迭代器是STL容器用于遍历元素的抽象工具,可以是智能的(如random access iterator),而指针是一个直接指向内存地址的简朴工具。
  • 迭代器可以具备更高级的功能(如范围检查、容器一致性),而指针更轻量级、没有额外的开销。
4. 当代C++特性

4.1 push_back()左值和右值的区别是什么?

答复:
push_back()左值会调用拷贝构造函数,而右值会调用移动构造函数,从而制止不必要的深拷贝,提高性能。
4.2 move底层是怎么实现的?

答复:
std::move是一个范例转换,它将其参数强制转换为右值引用(T&&),从而答应移动语义的实现。move本质上不会移动数据,而是答应调用者触发移动构造函数或移动赋值操纵符。
4.3 完美转发的原理是什么?

答复:
完美转发使用模板和std::forward实现,将参数原封不动地转达给另一个函数。std::forward会根据转达的值种别(左值或右值)举行正确的转发,确保参数在转达时保持原有的范例特性。
4.4 移动构造和拷贝构造的区别是什么?

答复:
移动构造函数通过“盗取”资源来构造新对象,而拷贝构造函数则是通过深拷贝复制资源。移动构造通常比拷贝构造更高效,因为它制止了不必要的资源分
配和释放。
4.5 lamda表达式捕获列表捕获的方式有哪些?如果是引用捕获要注意什么?

答复:
捕获方式包括值捕获、引用捕获、隐式捕获(值或引用)、按值捕获(移动语义)。使用引用捕获时,需要注意生命周期问题,确保被捕获的变量在Lambda表达式执行时仍然有效。
5. 多线程与并发

5.1 线程有哪些状态,线程锁有哪些?

答复:
线程的状态包括:新建(new)、就绪(ready)、运行(running)、等待(waiting)、终止(terminated)。常用的线程锁包括:互斥锁(std::mutex)、读写锁、递归锁、定时锁。
5.2 多线程会发生什么问题?线程同步有哪些手段?

答复:
多线程常见问题包括竞争条件、死锁、资源饥饿。同步手段包括:互斥锁、条件变量、原子操纵、信号量。
5.3 如何实现线程安全,除了加锁另有没有其他的方式?

答复:
实现线程安全的方式包括:


  • 加锁(如mutex)
  • 使用无锁编程技术(如使用std::atomic范例)
  • 使用线程本地存储(thread_local关键字)
  • 不可变对象设计(线程间不共享可变状态)
6. 高级C++与体系编程

6.1 动态链接和静态链接的区别,动态链接的原理是什么?

答复:


  • 静态链接:在编译时将所有依赖的库链接到可执行文件中,生成的文件较大,但运行时不依赖外部库。
  • 动态链接:可执行文件在运行时动态加载依赖的库,生成的文件较小,但需要依赖外部的共享库(如.dll或.so)。
    动态链接原理:动态链接库在运行时由操纵体系加载到内存中,动态链接器负责将库中的符号分析到步伐的地址空间中。
6.2 讲讲你理解的虚基类

答复:
虚基类用于解决多重继承中的“菱形继承”问题。通过使用虚继承,基类中的数据成员在派生类中只会存在一份,从而制止了重复继承带来的数据冗余和二义性问题。
6.3 C++哪些运算符不能被重载?

答复:
以下运算符不能被重载:


  • ::(作用域分析运算符)
  • .(成员访问运算符)
  • .*(成员指针访问运算符)
  • sizeof(大小运算符)
  • ?:(条件运算符)
  • typeid(范例识别运算符)
  • alignof(对齐运算符)
6.4 C++中怎么编译C语言代码?

答复:
在C++代码中编译C语言代码需要使用extern "C"来指示编译器使用C语言的链接规范,以制止C++名称修饰(name mangling)导致的链接问题。比方:
  1. extern "C" {
  2.     #include "c_code.h"
  3. }
复制代码
7. 高级内存管理与优化

7.1 weak_ptr是怎么实现的?

答复:
std::weak_ptr是一种不控制资源生命周期的智能指针。它通过内部引用计数器(与std::shared_ptr共享)实现。weak_ptr不会增长资源的引用计数,但可以检查资源是否仍然有效,并通过lock()函数获取一个shared_ptr以安全地访问资源。
7.2 虚函数的底层原理是什么?

答复:
虚函数通过虚函数表(vtable)实现。当类中有虚函数时,编译器会为每个类生成一张虚函数表,表中存放指向虚函数的指针。对象实例有一个隐藏的指向vtable的指针,调用虚函数时通过vtable动态分析出对应的函数地址,从而实现多态。
7.3 一个函数f(int a,int b),其中a和b的地址关系是什么?

答复:
在函数参数转达中,a和b通常会依次压入栈中。因此,在大多数体系中,后转达的参数b的地址会比前一个参数a的地址低。不外这也取决于体系的栈增长方向(通常是从高地址向低地址增长)。
7.4 如何包管类的对象只能被开发在堆上?(将构造函数声明为私有、单例)

答复:
为了包管类的对象只能被创建在堆上,可以:


  • 将构造函数设为private或protected,并提供一个静态成员函数(如工厂方法)来在堆上创建对象,并返回指向该对象的指针。
  • 通过删除栈上分配的运算符(如重载operator new和operator delete)。
7.5 动态链接和静态链接的区别,动态链接的原理是什么?

答复:
见6.1。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表