C++面试题个人汇总

有一些是自己总结的,有一些是借鉴前辈们的文章的,希望能给大家带来帮助。

目录
一、 基础

  1. 面向对象的特征
  2. 继承关系
  3. 虚继承与虚基类
  4. 使一个类不能继承的方法
  5. 多态
  6. 虚函数定义以及其作用
  7. 虚函数表
  8. 不能声明为虚函数的函数
  9. 构造函数、析构函数与抛出异常
  10. 纯虚函数
  11. 虚函数与纯虚函数的区别?
  12. C++源文件从文本到可执行文件经历的过程
  13. 编译期间的错误、链接期间的错误
  14. C++11新特性
  15. 智能指针
  16. Static的用法
  17. Const的用法
  18. 强制类型转换
  19. 模板
  20. C++内存管理
  21. 堆与栈的区别
  22. malloc底层原理
  23. new的操作原理
  24. new和malloc的区别
  25. C++ 中 struct 和 class 的区别
  26. struct与union的区别
  27. define与const的区别
  28. strcpy和memcpy的区别
  29. sizeof 和 strlen 的区别
  30. 空类的sizeof大小
  31. 新建空类包含的函数
  32. 指针和引用
  33. inline函数优点、缺点
  34. 内联函数和宏定义的区别
  35. 左值和右值的区别
  36. 移动语义(move)、完美转发(forward)
  37. 浅拷贝、深拷贝
  38. 内存碎片
  39. 内存池
  40. 内存泄漏
  41. linux常用指令(包括GDB调试)
  42. 静态链接与动态链接
  43. 设计模式
  44. OOP五大设计原则
    二、 进程与线程

1.进程和线程的定义、区别

  1. fork和vfork的区别
  2. 孤儿进程和僵尸进程
  3. 进程通信
  4. 操作系统中的页表寻址
  5. 分段和分页的区别
  6. 用户态和内核态的定义、进程由用户态转化为内核态的时刻
  7. 进程调度
  8. Cpu调度算法
  9. 进程切换的过程
  10. 线程间同步方式及系统调用
  11. 线程间通信
  12. 线程池
  13. 死锁发生的条件以及解决死锁的方法
  14. Linux的4种锁机制
    三、 计算机网络

1.四层模型:
2. 局域网常用无类型域间选路(CIDR)
3. IP报
4. TCP 和 UDP 的区别
5. TCP 和 UDP 应用场景
6. 三次握手
7. 四次挥手
8. TCP可靠性
9. socket编程
10. select、poll、epoll
11. 心跳机制
12. HTTP和HTTPS的区别,以及HTTPS的缺点
13. GET和POST的区别
14. 服务器高并发量
四、 数据结构
1 .基本排序
2. 快速排序最坏情况的改进
3. 布隆过滤器
4. 解决hash冲突的方法
五、 STL容器
1.STL六大组件
2. vector
3. list
4. deque
5. map
6. map与multimap的区别
7. 红黑树
8. set/multiset
9. Unorder_map/unordered_set
10. SGI Stl的内存分配方式
11. vector的插入操作可能会导致迭代器失效的原因
12. 不允许有遍历行为的容器(不提供迭代器)
13. STL容器的参数allocate的作用
14. vector中erase与algorithm中remove的区别、size()和capacity()的区别 41
15. map和set的插入删除效率比其他序列容器高的原因
16. map和set每次Insert之后,以前保存的iterator不会失效的原因
17. map不能边遍历边删除的原因

一、基础
1.面向对象的特征
抽象、封装、继承、多态。
2.继承关系
基类中的public成员 public继承 public
基类中的protected成员 public继承 protected
基类中的private成员 public继承 不可访问
基类中的public成员 protected继承 protected
基类中的protected成员 protected继承 protected
基类中的private成员 protected继承 不可访问
基类中的public成员 private继承 private
基类中的protected成员 private继承 private
基类中的private成员 private继承 不可访问

3.虚继承与虚基类
为了避免多继承产生的二义性,在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。虚继承之所以可以实现在多重派生子类中只保存一份共有虚基类拷贝,关键在于虚基类表指针,该指针指向了一个虚基类表,虚基类表中记录了虚基类表指针与本类的偏移地址和虚基类表指针到共有基类元素之间的偏移量,这样就维持了一份基类的拷贝。
4.使一个类不能继承的方法
继承的时候子类在构造的时候要先执行父类的构造函数,所以只要让类的构造函数和析构函数都设置成private,编译器就会告诉我们这个类不能被继承了。
5.多态
C++ 多态有两种:静态多态、动态多态。静态多态是通过函数重载实现的;动态多态是通过虚函数实现的。
多态的条件是:继承、重写、父类引用指向子类对象。
6.虚函数定义以及其作用
虚函数是允许被其子类重新定义的成员函数。可以实现用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员,从而实现多态。注意:构造函数不能为虚函数,但是析构函数可以为虚函数,并且虚析构函数可以防止父类指针销毁子类对象时不正常导致的内存泄漏。

7.虚函数表
虚函数是通过一张虚函数表来实现的。简称为V-Table。如果一个类中包含虚函数(virtual修饰的函数),那么这个类就会包含一张虚函数表,虚函数表存储的每一项是一个虚函数的地址。虚函数表v-table在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别。

8.不能声明为虚函数的函数
普通函数(非成员函数)、构造函数、友元函数、静态成员函数、内联成员函数。
当派生类在创建对象的时候会调用基类的构造函数,但是如果基类的构造函数是虚函数的话,派生类的构造函数又会把基类的构造函数覆盖,所以无法进一步执行而出错。同时,虚函数通过虚函数表来实现,而指向虚函数表的指针也需要在对象实例化后创建,那么就违背了先实例化后调用的准则。
9.构造函数、析构函数与抛出异常
构造函数可以抛出异常。C++标准指明析构函数不能、也不应该抛出异常。如果对象出了异常,现在异常处理模块为了维护系统对象数据的一致性,避免资源泄漏,有责任释放这个对象的资源,调用对象的析构函数,可现在假如析构过程又再出现异常,那么请问由谁来保证这个对象的资源释放呢?而且这新出现的异常又由谁来处理呢?不要忘记前面的一个异常目前都还没有处理结束,因此这就陷入了一个矛盾之中,或者说无限的递归嵌套之中。
10.纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。带有纯虚函数的类为抽象类。析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
11.虚函数与纯虚函数的区别?
定义一个函数为虚函数,不代表函数为不被实现的函数。
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
定义一个函数为纯虚函数,才代表函数没有被实现。
定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
12.C++源文件从文本到可执行文件经历的过程
预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件
编译阶段:将经过预处理后的预编译文件转换成特定汇编代码(编译原理相关,词法分析、语法分析、语义分析等),生成汇编文件
汇编阶段:将汇编文件转化成机器码,生成可重定位目标文件
链接阶段:将多个目标文件及所需要的库打包连接成最终的可执行目标文件(或库文件以供其他程序使用)
13.编译期间的错误、链接期间的错误
编译时期:变量使用前没有声明、书写代码时,括号不匹配、代码行末尾缺少分号、使用保留关键字作为变量名或程序名等。
链接时期:程序中使用某些函数只有声明,没有定义、找不到某些系统文件等。

  1. C++11新特性
    1.auto自动类型推导: auto a = 1.0;
    2.引入了nullptr,相比于NULL,明确得表示为一个空指针。没有“0”的意思。
    3.发明了新的数据类型:tuple,tuple是一个N元组。能够传入多个不同类型的数据。auto t1 = make_tuple(1, 2.0, “C++ 11”);
    4.初始化变得美观int arr[3] = {1, 2, 3} → int arr[3]{1, 2, 3};
    5.范围for循环:遍历给定序列的每个元素并对序列中的每个值执行某种操作。
    6.lambd表达式:用于定义并创建匿名的函数对象,以简化编程工作。
    智能指针
    15.智能指针
    C++内存管理是一个令人很头疼的事情,尽管每次写完new都会写一个delete,但是如果程序还没有执行到delete的时候就跳转了或者函数返回了,那么就会导致内存泄漏,使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当类的实例超出了作用域的时候,就会自动调用其析构函数,析构函数会自动释放资源。
    三种智能指针
    unique_ptr、shared_ptr、weak_ptr。
    shared_ptr维护了一个指向control block的指针对象,来记录引用个数。weak_ptr用于避免shared_ptr相互指向产生的环形结构,造成的内存泄漏。weak_ptr count是弱引用个数;弱引用个数不影响shared count和对象本身,shared count为0时则直接销毁。
    判断weak_ptr的对象是否失效有三种方法:1.expired():检查被引用的对象是否已删除。2.lock()会返回shared指针,判断该指针是否为空。3.use_count()也可以得到shared引用的个数,但速度较慢。
    shared_ptr 和 unique_ptr区别
    unique具有唯一性,对指向的对象值存在唯一的unique_ptr。unique_ptr不可复制(因为拷贝构造函数被禁用了),赋值,但是move()可以转换对象的所有权,局部变量的返回值除外。与shared_ptr相比,若自定义删除器,需要在声明处指定删除器类型,而shared不需要,shared自定义删除器只需要指定删除器对象即可,在赋值时,可以随意赋值,删除器对象也会被赋值给新的对象。unique的实现中,删除器对象是作为unique_ptr的一部分,而shared_ptr,删除器对象保存在control_block中。
    不能通过weak_ptr直接访问对象的方法,要先通过lock()转换为shared_ptr。

16.Static的用法
1)静态局部变量:作用域只在函数内部,但是在全局静态存储区分配内存,也就是说生存周期随着程序运行结束而结束。会在第一次被执行的时候初始化,以后的函数调用不再初始化。
2)全局静态变量/static修饰的函数:隐藏。该变量/函数不能被其他文件引用。
3)静态数据成员:只会被初始化一次,与实例无关,并且只能在类外初始化。存储在全局静态区。
4)静态成员函数:用于修饰类的成员函数。只能访问静态数据成员或静态成员函数。
可以使用static const成员函数吗?
不可以,const修饰成员函数其实修饰的是this指针,代表不可以通过函数来修改对象,但是static修饰的成员函数属于类,压根就不会有this指针。

17.Const的用法
1)const修饰变量:存储在常量区,不允许被修改。修饰指针变量分为指向常量的指针和指针本身是常量。如果修饰的是类的成员变量那么必须在类中定义的时候初始化或者在构造函数的初始化列表中初始化。
2)const修饰函数:如果修饰返回值表示返回值不可被修改。如果修饰成员函数表示不可在函数中修改任何成员变量。
3)const修饰对象:表示这个对象是一个常量,在初始化的时候要对所有成员变量都初始化。共有变量只能读,并且只能调用const成员函数。
如果const成员函数想要改变成员变量怎么办?
1)mutable修饰的成员变量可以突破const的限制,在被const修饰的函数里也能被修改。
2)可在函数内使用const_cast将this指针转化为class*const类型也就是表示指向的对象可以修改。
18. 强制类型转换
static_cast:
1)在基本数据类型之间转换,如把 int 转换为 char,这种带来安全性问题由程序员来保证;
2)在有类型指针与 void * 之间转换;(不能使用 static_cast 在有类型指针内转换。)
3)用于类层次结构中基类和派生类之间指针或引用的转换。上行转换(派生类---->基类)是安全的;下行转换(基类---->派生类)由于没有动态类型检查,所以是不安全的。
dynamic_cast:
用于将一个父类的指针/引用转化为子类的指针/引用(下行转换)。基类必须要有虚函数,因为 dynamic_cast 是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中。
const_cast:
常量指针(或引用)与非常量指针(或引用)之间的转换。
reinterpret_cast:
用在任意指针(或引用)类型之间的转换。能够将整型转换为指针,也可以把指针转换为整型或数组。

  1. 模板
    模板是创建泛型函数或者类的一个工具,可以对类型进行参数化。使用模板的目的就是编写与类型无关的代码。
    什么叫全特化?偏特化呢?
    全特化就是模板中的参数全被指定成了确定的类型。
    偏特化就是模板中的参数没有被全部确定,需要编译器在编译的时候确定。
    一个模板类在不同特化之后,得到的类还是不是同一个类?
    不是同一个类,不论是特化还是泛型只要参数不同就不是同一个类。

20.C++内存管理
  在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
  栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。  
自由存储区,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
  全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。
21.堆与栈的区别
堆空间的内存是动态分配的,一般存放对象,并且需要手动释放内存。栈空间的内存是由系统自动分配,一般存放局部变量,比如对象的地址等值,不需要程序员对这块内存进行管理,栈是运行时的单位,而堆是存储的单位。堆中的共享常量和缓存 。栈解决程序的运行问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值