C++ 八股文

目录

1. 讲一讲面向对象的三大特性,并展开说说

2. final关键字的作用是什么?

3. C++的虚函数是如何实现的?它被放在内存的什么位置?是什么时候生成的?

4. 智能指针的本质是什么?他的实现原理是什么?

5. 匿名函数的本质是什么?他的优点是什么?

6. 右值引用是什么?为什么要引入右值引用?

7. 左值引用和指针的区别是什么?

8. 指针是什么?

9. weak_ptr真的不计数吗?是否有计数方式,在哪里分配的空间?

10. malloc内存分配方式有什么缺点?

        那为什么不全部都用brk()来分配内存?

        那为什么不全部都用mmap()来分配内存?

11. free传入一个指针,它如何确定具体要清理多少空间呢?

12. #define 和 const 的区别是什么?

13. 程序执行的步骤是什么?

14. 锁的底层原理是什么?

15. struct和class的区别是什么?

16. 什么是内存对齐原则?它的优势是什么?

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

18. 线程之间的通信方式有哪些?

19. 介绍一下socket中的多路复用,及其它们的优缺点

20. 介绍一下epoll的水平和边缘触发模式

21. 类的生命周期

22. 构造函数和析构函数可以为虚函数吗?

23. 多线程为什么会发生死锁?如何解决死锁?

24. 介绍一下面向过程和面向对象

25. C++的左值和右值是什么?++i是左值还是右值?++i和i++哪个效率高?

26. 介绍一下vector、list的底层实现原理和优缺点

27. 静态变量在什么阶段初始化?在哪里初始化(赋初值)?

28. 如何实现多进程

29. 为什么空对象指针nullptr能调用函数

30. shared_ptr线程安全吗?

31. push_back()中的参数是左值和右值的区别是什么?

32. move()的功能是什么?

33. 介绍一下完美转发是什么?

34. 空类中有哪些函数?

35. explicit有什么作用?

36. 成员变量初始化顺序是什么?

37. 指针的大小是多少?

38. 野指针和内存泄漏是什么?如何避免?

39. new / delete和malloc / free有什么区别?

40. 什么是STL?

41. 迭代器是指针吗?

42. 线程有哪些状态?线程锁有哪些?

43. 介绍一下map和unordered_map

44. 介绍一下vector容器的push_back()和emplace_back()

45. 为了保证线程安全,除了“加锁”,还有什么别的方式?

47. vector容器的resize和reserve有什么区别?

48. vector如何避免重复扩容?

49. C++类的大小是多少?

50. C++的weak_ptr是如何实现的?

51. C11引入了哪些新特性?


1. 讲一讲面向对象的三大特性,并展开说说

      面向对象的三大特性为:封装,继承和多态。

封装:将具体的实现过程和数据封装成一个类,使用设置权限的成员函数接口进行访问。这样做可以防止类内部数据被外部直接访问和修改。 

继承:子类继承父类的全部成员,只是private是不可以见的。继承权限分为private、public和protected三种。父类的构造、析构、友元和静态成员是不可以被继承的。这样做可以提高代码的复用率,提高程序开发效率。

多态:不同对象对统一消息做出不同相应。多态分为静态多态和动态多态。

        静态多态通过函数重载和运算符重载来实现,他是编译期多态,在编译期间就可以确定使用哪个函数。

        动态多态通过虚函数来实现,它是运行期多态,系统在运行期间才知道程序具体要调用哪个函数。

2. final关键字的作用是什么?

        final放在后,表示该类无法被继承。

        final放在虚函数后,表示这个函数无法被重写。

3. C++的虚函数是如何实现的?它被放在内存的什么位置?是什么时候生成的?

        C++的虚函数是通过虚函数表虚函数指针实现的。每一个包含虚函数类的都有一个虚函数指针,指向虚函数表。而虚函数表是一个指针数组,每一个指针对应指向每一个虚函数的实现代码。

        当调用虚函数时,编译器会通过虚函数指针找到对应的虚函数表,并根据函数在表中的位置引用正确的虚函数。

        虚函数和普通函数一样,在编译阶段生成,存放在代码区,只是将他的指针保存在虚表中。

4. 智能指针的本质是什么?他的实现原理是什么?

        智能指针auto是一个封装了C++指针的类模板,为了确保动态内存安全性

实现原理:通过一个对象存储需要释放的资源,然后通过对象的析构函数来释放。

5. 匿名函数的本质是什么?他的优点是什么?

        匿名函数本质上是一个重载()运算符对象

优点:在调用时才创建对象,调用结束后立即释放,节约空间

6. 右值引用是什么?为什么要引入右值引用?

        右值引用是为临时变量起一个别名,可以通过右值引用修改右值。

为什么

        1. 移动语义:绑定临时变量,可以节约复制操作的资源。

        2. 完美转发:将右值引用作为形参,可以在函数内部将其转发给其他函数。

7. 左值引用和指针的区别是什么?

        是否初始化:指针不用初始化,引用必须初始化

        性质不同:指针是一个变量,引用是一个别名,本质上引用保存的是引用对象的首地址

        占用内存不同:指针有自己的存储空间,引用与引用的对象占用同一空间。

8. 指针是什么?

        指针是指内存中存储空间的地址,它分为指针常量和指针变量。

9. weak_ptr真的不计数吗?是否有计数方式,在哪里分配的空间?

        控制块有引用计数和引用计数。weak_ptr内部会有一些额外的计数信息,用于帮助管理生命周期,但这并不会引起实际计数的变化

        如果使用make_shared()函数,则是静态分配,空间在上。

        如果使用new,则是动态分配,空间在上。

10. malloc内存分配方式有什么缺点?

        malloc是一个用于动态分配的库函数,在申请空间时候有两种方式:

方式1:当用户分配的内存 < 128KB 时,通过brk()申请空间。堆顶指针向高地址移动,如果使用free释放空间,并不会将内存返还给操作系统,而是会缓存在malloc的内存池中,待下次使用

方式2:当用户分配的内存 > 128KB 时,通过mmap()文件映射区域申请空间。使用私有匿名映射的方式,在文件映射区域分配一块空间,free时释放内存返还给操作系统。

缺点:动态申请的方式容易产生许多内存碎片。操作不当容易内存泄漏

        那为什么不全部都用brk()来分配内存?

        如果频繁调用malloc和free,堆空间会产生许多内存碎片

        那为什么不全部都用mmap()来分配内存?

        因为向操作系统申请空间,系统调用要先从用户态进入内核态,调用后再转回用户态,频繁地系统调用浪费太多时间。此外,因为mmap()每次释放都会将空间返还给操作系统,因此每次mmap分配的虚拟地址都是缺页状态第一次访问次虚拟地址时候会触发缺页中断

11. free传入一个指针,它如何确定具体要清理多少空间呢?

        在动态申请内存时,会在存储空间的前面多申请一些空间存储头部信息,其中包括内存空间的大小。free会对传递的地址偏移出头部信息的大小,之后就可以释放空间了。

12. #define 和 const 的区别是什么?

        阶段不同:#define是在预处理阶段进行替换,const实在编译阶段确定其值。

        安全性不同:#define只是替换,不做安全性检查。const要做类型判断。

        内存占用不同:#define在程序中有多少次替换,在内存中就有多少次备份。

                                 const在静态存储区域只有一份。

        调试不同:#define不可以调试,因为在预处理阶段已经替换过了。

                          const常量可以调试。

13. 程序执行的步骤是什么?

        预编译:将头文件编译,宏替换,生成.i文件

        编译:检查错误,无错后将其转换为汇编语言文件,生成.s文件

        汇编:汇编器将汇编语言文件翻译成机器语言文件,生成.o文件

        链接:将目标文件和库链接到一起,生成.exe文件

14. 锁的底层原理是什么?

        锁的底层是通过CAS机制,atomic机制实现的。

CAS机制:全称为Compare And Swap(比较转换),可以将比较和交换操作转换为原子操作。

CAS操作依赖三个值:内存中的值V,旧的预估值X,要修改的新值B,如果旧的预估值 X等于内存中的值V,就将新的值B保存在内存中。

atomic机制:原子操作指不会被线程调度机制打断的操作,一旦开始,直接运行到结束。

它在X86中的原理是:如果在某一条汇编语言的前面加上LOCK关键字,在执行时CPU会将LOCK引线的电平持续拉低,保证别的CPU无法访问内存。

15. struct和class的区别是什么?

        class的成员默认权限是private,struct的默认权限是public。

        class支持模板参数,struct不支持模板参数。

16. 什么是内存对齐原则?它的优势是什么?

        内存对齐原则指的是将数据结构尽可能地在自然边界上对齐比如在结构体,类的存储上,都会使用内存对其原则,以整体中,最长的那个类型作为最小单位进行存储。

        优点:内存对其原则可以使内存的存取访问更加高效。

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

1.管道:管道分为匿名管道和命名管道,本质上是一个内核的一个缓存。当进程创建管道后会返回两个文件描述符,一个是写入端,一个是输出端。缺点:它是半双工通信,不适合频繁交换数据。

2.消息队列:可以边发边收,但每个消息有最大长度限制,队列所包含的消息体也有上限,并且通信过程中存在用户态和内核态之间的数据拷贝问题。

3.共享给存:解决了消息队列存在的内核态和用户态之间数据拷贝的问题。

4.信号量:通过同步互斥的PV操作实现

5.信号

6.socket套接字

18. 线程之间的通信方式有哪些?

        信号量、条件变量、互斥量

19. 介绍一下socket中的多路复用,及其它们的优缺点

        多路复用指的是允许多个输入或者输出共用一个通道。

        常用的多路复用机制有:select机制poll机制epoll机制,他们可以监视多个文件描述符,一旦某个文件描述符进入就绪状态,就能通知系统进行相应的读写操作。

select优点可移植性好,对于超时时间精度更高:微秒级,poll和epoll都是毫秒级

select缺点:支持监听的最多文件描述符为1024个。此外,select会维护一个保存文件描述符的数据结构,每次调用时都需要把这个集合从用户区拷贝到内核区,调用后又从内核区拷贝到用户区增大了系统开销

poll优点:poll对于文件描述符的数量没有最大限制,用链表存储。

poll缺点:和select机制一样,每次调用需要将文件描述符集合在用户区和内核区之间拷贝,

增大了系统开销

Epoll优点:epoll对于文件描述符的数量没有最大限制。在调用时,只需要将存放文件描述符的集合从用户区向内核区拷贝一次,不需要重复拷贝

Epoll缺点:目前epoll机制只适用于linux系统,不支持跨平台使用。

20. 介绍一下epoll的水平和边缘触发模式

epoll水平触发(LT):对于操作,只要缓冲区不为空,LT模式返回读就绪

                                 对于操作,只要缓冲区不满,LT模式会返回写就绪

epoll边缘出发(ET):ET模式要求应用程序在每次新的就绪事件到来时立即处理,否则epoll不会重复通知同一个事件。

        与水平触发不同,边缘触发只在状态变化时通知应用程序,即只有当文件描述符从未就绪变为就绪时才通知。如果应用程序没有处理完所有就绪的事件,epoll 不会重复通知同一个事件,而是等待下一个状态变化。

21. 类的生命周期

        类从加载到内存中开始,卸载出内存结束,包括加载验证准备解析初始化使用卸载这七个阶段。其中验证+准备+初始化三部分被称为连接

        全局对象在主函数main开始前被创建,main退出后被销毁。

        静态对象第一次进行作用域时被创建,main退出后被销毁。

        局部对象在进入作用域时被创建,在退出作用域后被销毁。

        动态申请的对象从申请时被创建,在delete或者程序执行完毕后被销毁。

22. 构造函数和析构函数可以为虚函数吗?

        构造函数不可以为虚函数。析构函数可以为虚函数。当父类指针指向子类对象时,将父类的析构函数设置为虚函数,可以避免因“子类析构函数未执行”导致的内存泄漏。

23. 多线程为什么会发生死锁?如何解决死锁?

        死锁指:多个线程共享一个区域的资源,某个状态每一个线程都在等待其他线程访问完,才能继续执行下去,互相阻塞无法推进。

        解决死锁可以通过破坏互斥条件、请求保持条件、不可剥夺条件和环路等待条件的任意一个

24. 介绍一下面向过程和面向对象

        面向过程:指的是将问题解析成步骤,一步一步地执行。代码效率高,但复用率低。

        面向对象:指的是将问题分解为对象,来描述事物在解决问题中的行为。虽然代码效率低,但是代码的复用率高。

25. C++的左值和右值是什么?++i是左值还是右值?++i和i++哪个效率高?

        C++的左值指的是赋值语句左边的值不能被赋值的就是右值

        ++i是左值,它的效率更高,因为++i返回的是一个左值,没有发生拷贝。

26. 介绍一下vector、list的底层实现原理和优缺点

        vector底层是由一块连续的存储空间组成,有三个指针分别为:头指针尾指针可用空间尾指针优点:支持通过下标随机访问,因此遍历的效率高。缺点:删除和插入都需要移动多个元素

        list底层一个双向链表优点:不存在空间浪费。用链表存储,插入和删除效率高。缺点:不支持随机访问,因此遍历效率低。

27. 静态变量在什么阶段初始化?在哪里初始化(赋初值)?

        静态变量、全局变量和常量均在编译阶段完成初始化内存分配

        其他变量在编译阶段初始化,在运行阶段内存分配

        静态变量在类内定义,在类外赋初值。

28. 如何实现多进程

        linux系统下采用fork()函数,windows系统下采用createprocess()函数

29. 为什么空对象指针nullptr能调用函数

        类在初始化时,编译器会将成员函数分配在类外,可以节省内存。如果函数没有涉及到成员变量,则不会使用this指针,所以空对象指针也可以调用函数。

30. shared_ptr线程安全吗?

        智能指针中的引用计数是线程安全的,但智能指针所示对象线程不安全。也就是它只保证管理资源的生命周期线程安全,不保证资源本身的线程安全。

31. push_back()中的参数是左值和右值的区别是什么?

        如果是左值,则直接构造新对象加入容器。

        如果是右值,则尝试采用移动语义的方式,避免不必要的复制。

32. move()的功能是什么?

        move可以强制将一个左值转换为右值引用,类似于强制类型转换。

优点:可以避免拷贝构造,节约资源。

33. 介绍一下完美转发是什么?

        完美转发是指,函数模板可以将自己的参数完美地转发给其他函数,转发的左值也就是左值,右值依旧是右值。

34. 空类中有哪些函数?

        无参构造函数、析构函数、拷贝函数、默认赋值运算符 =

35. explicit有什么作用?

        explicit用于修饰只有一个参数的构造函数第一个参数无默认值,后边所有参数都有默认值作用:它表示构造函数是显示的,禁止隐式转换

36. 成员变量初始化顺序是什么?

        顺序:1. 父类静态变量全局变量

                   2. 子类静态变量全局变量

                   3. 父类成员变量

                   4. 子类成员变量

37. 指针的大小是多少?

        这要看操作系统的地址总线位数32位系统指针是4字节64位系统是8字节

38. 野指针和内存泄漏是什么?如何避免?

 野指针:指向一个权限不允许访问已释放的空间的指针。

        避免:初始化赋初值nullptr,释放空间后及时赋值nullptr。

内存泄漏:丢失了动态申请的空间指针的控制,导致空间未及时释放,造成系统资源的浪费。

        避免:使用智能指针管理资源,及时使用delete释放资源。

39. new / delete和malloc / free有什么区别?

        new和delete为运算符,malloc和free为库函数

        new申请空间失败抛异常,malloc申请失败返回空指针NULL

        delete释放会调用析构函数,free不调用析构函数

        new返回有类型的指针,malloc返回无类型的指针。

40. 什么是STL?

        它是可复用的组件库,包括容器算法迭代器仿函数空间配置器配接器六大组件。

41. 迭代器是指针吗?

        迭代器不是指针,迭代器是一个类模板,通过重载了指针的一些操作符模拟了指针的功能。

                                迭代器所返回的是对象引用而不是对象的值。

                                指针可以指向迭代器,迭代器不可以指向指针。

42. 线程有哪些状态?线程锁有哪些?

        线程有五种状态:创建态、就绪态、运行态、阻塞态、死亡

        线程锁种类互斥锁、条件锁、自旋锁、读写锁、递归

43. 介绍一下map和unordered_map

        map底层实现是一个红黑树,内部所有元素是有序的。

优点:内部元素有序

缺点:空间占用率高

        unordered_map内部实现了一个哈希表,内部元素是无序的。

优点:查找效率高

缺点:建立哈希表浪费时间

44. 介绍一下vector容器的push_back()和emplace_back()

        使用push_back()时,会首先调用有参构造函数构造一个对象,再通过拷贝函数移动到容器中

        使用emplace_back()时,会直接在容器尾部构造对象。它比push_back()少执行一次构造函数

 但emplace_back()只能用于构造新对象,如果要将已有对象放入vector,需要使用push_back()

45. 为了保证线程安全,除了“加锁”,还有什么别的方式?

        可以使用互斥量防止多个线程进行数据竞争

                   原子操作:原子操作不可分割,可以保证线程安全

                   条件变量:在线程间传递信号,从而控制线程的执行流程

47. vector容器的resize和reserve有什么区别?

        vector的resize()是改变vector的大小,可能会添加删除元素

        vector的reserve()是改变vector的容量,不改变元素数量,只为了优化

48. vector如何避免重复扩容?

        当vector容量不足时,会以自身1.52倍进行扩容。

        可以提前使用reserve()自定义最大容量,以减少自动扩容的次数。

49. C++类的大小是多少?

        空类的大小为1字节;

        非空类:1.算上成员变量的空间大小。

                      2. 如果有虚函数,则虚函数指针多增加4字节;

       (成员函数不计入大小)(计算方式按照内存对齐原则计算)

50. C++的weak_ptr是如何实现的?

        weak_ptr的实现基于计数器寄存器

        计数器记录弱引用数量寄存器存储shared_ptr

51. C11引入了哪些新特性?

        1. 引入了智能指针,shared_ptr、weak_ptr和unique_ptr以保证线程安全。

        2. 引入了右值引用,实现完美转发移动语义

        3. 引入了类型推导delctype()

        4. 引入了空指针nullptr

        5. 引入了范围for遍历,for(Elem T : 集合名称 ) { /*对当前元素T的操作*/ }

        6. 引入了初始化列表,用于高效初始化类成员

        7. 引入了匿名函数lambda表达式,[](参数列表) -> 返回值类型

        8. 引入了并发机制,thread、lock_guard和unique_lock

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值