从今天起,豆芽有空也尽己所能,帮助一下大家。

面经来源:https://www.nowcoder.com/discuss/724278?source_id=discuss_experience_nctrack&channel=-1


1. 空类的大小

1个字节,C++的规定,因为要保证地址的唯一性。


2. static和extern

static关键字的作用

  1. 定义静态函数或全局变量:当我们同时编译多个文件时,在函数返回类型或全局变量前加上static关键字,函数或全局变量即被定义为静态函数或静态全局变量。静态函数或静态全局变量只能在本源文件中使用。这就是static的隐藏属性

  2. static 的第二个作用是保持变量内容的持久:在变量前面加上static关键字。初始化的静态变量会在数据段分配内存,未初始化的静态变量会在BSS段分配内存。直到程序结束,静态变量始终会维持前值。只不过全局静态变量和局部静态变量的作用域不一样。

  3. static 的第三个作用是默认初始化为 0:全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是 0x00

    最后对 static 的三条基本作用做一句话总结。首先 static 的最主要功能是隐藏,其次因为 static 变量存放在静态存储区,所以它具备持久性和默认值0。

  4. 在c++中,static关键字可以用于定义类中的静态成员变量:使用静态数据成员,它既可以被当成全局变量那样去存储,但又被隐藏在类的内部。类中的static静态数据成员拥有一块单独的存储区,而不管创建了多少个该类的对象。所有这些对象的静态数据成员都共享这一块静态存储空间。

  5. 在c++中,static关键字可以用于定义类中的静态成员函数:与静态成员变量类似,类里面同样可以定义静态成员函数。只需要在函数前加上关键字static即可。如静态成员函数也是类的一部分,而不是对象的一部分。所有这些对象的静态数据成员都共享这一块静态存储空间。

extern关键字的作用

extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。


3. 虚函数表是属于类的还是属于类的对象的

类的。

虚函数表类中只有一份。


4. 判断链表有环的方法

快慢指针。

慢指针每次走一步,而快指针每次都两步。如果链表有环,那么两个指针一定会再次相遇。


5. 三次握手,为什么需要三次,两次行不行

不能两次

假如只进行两次握手,客户端发送连接请求后,会等待服务器端的应答。但是会出现的问题是,假如客户端的SYN迟迟没有到达服务器端,此时客户端超时后,会重新发送一次连接,假如重发的这次服务器端收到了,且应答客户端了,连接建立了。

但是建立后,第一个SYN也到达服务端了,这时服务端会认为这是一个新连接,会再给客户端发送一个ACK,这个ACK当然会被客户端丢弃。但是此时服务器端已经为这个连接分配资源了,而且服务器端会一直维持着这个资源,会造成资源浪费。

两次握手的问题在于服务器端不知道SYN的有效性,所以如果是三次握手,服务器端会等待客户端的第三次握手,如果第三次握手迟迟不来,服务器端就会释放相关资源


6. 多线程,多进程了解不,区别是什么

(1)一个线程从属于一个进程;一个进程可以包含多个线程。

(2)一个线程挂掉,对应的进程挂掉,多线程也挂掉;一个进程挂掉,不会影响其他进程,多进程稳定。

(3)进程系统开销显著大于线程开销;线程需要的系统资源更少。

(4)多个进程在执行时拥有各自独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。

(5)多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。

(6)通信方式不一样。

(7)多进程适应于多核、多机分布;多线程适用于多核


7. 数据库了解不,连接有几种

外联接
内联接(Inner Join):匹配2张表中字段相等的记录。
左联接(Left Outer Join):返回左表,并且返回左表与右表字段相等的记录
右联接(Right Outer Join):返回右表,并且返回右表与左表字段相等的记录
全联接


8. C++回调了解不,如何使用

函数指针应用场景是回调callback)。我们调用别人提供的 API函数(Application Programming Interface,应用程序编程接口),称为Call;如果别人的库里面调用我们的函数,就叫Callback

我们举个例子:

//以库函数qsort排序函数为例,它的原型如下:
void qsort(void *base,//void*类型,代表原始数组
           size_t nmemb, //第二个是size_t类型,代表数据数量
           size_t size, //第三个是size_t类型,代表单个数据占用空间大小
           int(*compar)(const void *,const void *)//第四个参数是函数指针
          );
//第四个参数告诉qsort,应该使用哪个函数来比较元素,即只要我们告诉qsort比较大小的规则,它就可以帮我们对任意数据类型的数组进行排序。在库函数qsort调用我们自定义的比较函数,这就是回调的应用。

//示例
int num[100];
int cmp_int(const void* _a , const void* _b){//参数格式固定
    int* a = (int*)_a;    //强制类型转换
    int* b = (int*)_b;
    return *a - *b;  
}
qsort(num,100,sizeof(num[0]),cmp_int); //回调

9. 内存结构

img

如上图,从低地址到高地址,一个程序由代码段、数据段、BSS段、堆栈段组成。

  1. 代码段:存放程序执行代码的一块内存区域。只读,不允许修改,代码段的头部还会包含一些只读的常量,如字符串常量字面值(注意:const变量虽然属于常量,但是本质还是变量,不存储于代码段)。

  2. 数据段data:存放程序中已初始化全局变量静态变量的一块内存区域。

  3. BSS 段:存放程序中未初始化全局变量静态变量的一块内存区域。

  4. 可执行程序在运行时又会多出两个区域:堆区栈区。

    堆区:动态申请内存用。堆从低地址向高地址增长。

    栈区:存储局部变量函数参数值。栈从高地址向低地址增长。是一块连续的空间。

  5. 最后还有一个文件映射区(共享区),位于堆和栈之间。


10. 全局变量存在哪儿

  1. 数据段data:存放程序中已初始化全局变量静态变量的一块内存区域。

  2. BSS 段:存放程序中未初始化全局变量静态变量的一块内存区域。


11. 虚函数表的结构,如果基类中有虚函数,而派生类里面没有虚函数,对象的指针指向基类的虚函数表还是子类的虚函数表

问题问的就奇怪。回答不了。单一继承,父子类就一张表,不知道面试官想问个啥。


12. const

  1. const修饰普通类型的变量,告诉编译器某值是保持不变的。

  2. const 修饰指针变量,根据const出现的位置和出现的次数分为三种

    1. 指向常量的指针:指针指向一个常量对象,目的是防止使用该指针来修改指向的值。

    2. 常指针:将指针本身声明为常量,这样可以防止改变指针指向的位置。

    3. 指向常量的常指针:一个常量指针指向一个常量对象

  3. const修饰参数传递,可以分为三种情况。

    1. 值传递的 const 修饰传递,一般这种情况不需要 const 修饰
    2. 当 const 参数为指针时,可以防止指针被意外篡改。
    3. 自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间,因此我们采取 const 外加引用传递的方法。
  4. const修饰函数返回值,分三种情况。

    1. const 修饰内置类型的返回值,修饰与不修饰返回值作用一样。
    2. const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
    3. const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。
  5. const修饰成员函数

    const 修饰类成员函数,其目的是防止成员函数修改被调用对象的值,如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为 const 成员函数。


13. C++11的新特性

  1. 右值引用

  2. lambda

  3. 智能指针


14. 强制转换

C++为了将强制类型转换变得更加明确、控制强制转换的过程,主要将强制转换细化为四种cast转换方式:const_caststatic_castdynamic_castreinterpret_cast

  1. const_cast用于强制去掉不能被修改的常数特性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。
  2. static_cast用于将一种数据类型强制转换为另一种数据类型。什么都可以转,最常用。
  3. dynamic_cast只能用于含有虚函数的类转换,用于类向上和向下转换。dynamic_cast通过判断变量运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。
  4. reinterpret_cast主要有三种强制转换用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型。

15. 说说右值引用

C++11引入右值引用主要是为了实现移动语义完美转发

移动语义为了避免临时对象的拷贝,为类增加移动构造函数。

完美转发,就是通过一个函数将参数继续转交给另一个函数进行处理,原参数可能是右值,可能是左值,如果还能继续保持参数的原有特征,那么它就是完美的。


16. 智能指针是线程安全的嘛(不是)

从源码来看,不是,没有锁


17. 指针常量和常量指针

  1. 指向常量的指针:指针指向一个常量对象,目的是防止使用该指针来修改指向的值。

  2. 常指针:将指针本身声明为常量,这样可以防止改变指针指向的位置。



以上所有题的答案其实都来源于我的博客面经,欢迎大家围观:https://blog.nowcoder.net/jiangwenbo