cpp

C++ 常见问答题

Continuous update :D

Posted by kevin on October 14, 2019

0x01 C和C++的区别

  1. C 是面向过程的编程,C++ 是面向对象的编程,主要包括:
    • 封装:隐藏了代码细节,只留接口给用户调用
    • 继承:继承父类的数据和方法,扩展已经存在的模块,实现代码重用
    • 多态:通过派生类重写父类的虚函数,实现了接口的重用,也就是 “一个接口,多种实现”
  2. C++ 支持函数重载, C 不支持
  3. C++ 有引用,C 中不存在引用的概念
  4. 动态管理内存方式不同,C 使用 malloc/free,C++ 除此之外还有 new/delete 关键字
  5. C++ 支持重载的概念, C 不支持

0x02 #include 和 #include "file.h" 的区别

#include 先从标准库的路径中寻找文件,找不到再去工作目录

#include”file.h” 是先从工作目录中寻找文件,找不到再去标准库寻找

0x03 C++ 文件编译与执行的四个阶段

  1. 预处理:根据文件中的预处理指令来修改源文件的内容
  2. 编译:把源代码编译成汇编代码
  3. 汇编:把汇编代码翻译成目标二进制机器指令
  4. 链接:链接目标代码生成可执行程序,可执行程序包括源程序的机器码和相关的描述信息,如程序多大,占用多少内存空间等

0x04 #define 和 const 有什么区别

  1. 处理阶段不同:#define 定义的宏变量在预编译时就展开,在有宏变量的地方进行替换;const 定义的变量在编译时确定值
  2. #define 定义的常量没有类型,所给出的是一个立即数;const 定义的常量有类型名字,存放在静态(全局)区域
  3. #define 可以定义简单的函数,const 不可以定义函数
  4. #define 定义的常量不可以用指针去指向,因为不会给这个常量分配内存,const 定义的常量可以用指针去指向它的地址
  5. 能用 const 最好不要用 #define

0x05 C++ 深拷贝和浅拷贝的区别

  1. 对拥有动态成员的对象进行浅拷贝后,一个对象改变,另一个对象也会跟着改变,也就是说,这样的话,两个对象中在的指针成员指向的是同一片内存空间,为避免这种情况,就得用深拷贝,深拷贝后,两个对象的指针成员指向不同的地址,但是地址中的值是一样的
  2. 如果一个类拥有资源,当这个类的对象发生复制过程的时候,如果资源重新分配了就是深拷贝;反之没有重新分配资源,就是浅拷贝。

0x06 C++构造函数参数列表初始化与直接在函数内部初始化有何区别

这个问题之前也有人问过我,但是我回答不上来,后来在知乎上找到了答案:

当实例化一个类的对象时,实质上会发生下面 5 步:

  1. 分配 memory 给对象
  2. 调用类相应的构造函数
  3. 先进行初始化列表的初始化
  4. 再进入构造函数体内,进行一些赋值什么的
  5. 跳出函数体,控制权还给调用者

因此可以看到,如果写在函数内部的话,它是先用默认构造初始化了一遍,然后又去函数内部赋值,就很影响效率,所以最好优先使用初始化列表。

基类的构造函数只能通过列表初始化来完成! 上次有人问我一般在什么情况下用列表初始化,原来就是这个场景!

0x07 引用与指针有什么区别

  1. 引用必须被初始化,指针不必初始化
  2. 引用初始化之后就不能被改变,指针可以改变指向的对象
  3. 引用不能指向空值,指针可以

0x08 堆和栈有什么区别

  1. 堆是由 new 分配的内存块,需要程序员手动释放,否则程序结束后会被操作系统自动回收; 栈存放函数的参数值,局部变量,由编译器自动分配释放(保护现场就是用栈)
  2. 堆的分配需要使用频繁的 new/delete ,造成内存空间的不连续,会有大量的碎片(操作系统是用链表来储存空闲的内存地址
  3. 堆的生长空间向上,地址越来越大,栈的生长空间向下,地址越来越小
  4. 申请栈的空间大小有限制(类比汇编,栈段的大小不能超过 64 kB),申请空间超过栈的剩余空间时,会提示 overflow ,而堆的大小受限于系统中的有效虚拟内存,因此灵活度高,空间也比较大
int a = 0; //全局初始化区 
char *p1; //全局未初始化区 
main() 
{ 
    int b; //栈 
    char s[] = "abc"; //栈 
    char *p2; //栈 
    char *p3 = "123456"; //123456\0在常量区,p3在栈上。 
    static int c =0 //全局(静态)初始化区 
    p1 = (char *)malloc(10); //堆 
    p2 = (char *)malloc(20);  //堆 
}

0x09 struct 和 union 有什么区别

  1. struct 中的每个成员都有自己独立的地址,它们是同时存在的,union 中的所有成员占用同一段内存,它们不能同时存在,union 里面的数据存放在同一个地址开始的内存单元
  2. struct 的大小是字节对齐之后所有成员长度的总和,union 的大小是内存对齐后最长的数据成员的长度,union 就是为了节省空间才设计出来的
  3. union 的好处是可以用来测试 CPU 是大端模式还是小端模式

0x0A 多态,虚函数,纯虚函数

参考我写的这篇文章

0x0B 关键字static有什么作用

0x0C 哪些函数不能声明成虚函数

C++ 有五种函数不能被声明成虚函数:构造函数,友元函数,非成员函数,静态成员函数,内联成员函数

  1. 内联函数在编译时被展开,而虚函数在运行时才能动态绑定函数
  2. 非成员函数和友元函数不支持继承,没有继承属性的函数不能作为虚函数
  3. 静态成员函数对于每个类只有一个,所有的对象共享一份代码,这是属于类的而不是属于对象的,相反,虚函数必须要知道指针或引用对象的类型才能判断调用哪一个虚函数,因此虚函数是与对象相关的,而静态成员函数是与类相关的

0x0D 哪些成员函数不能被继承

  1. 构造函数(含拷贝构造函数)
  2. 析构函数
  3. 赋值运算符重载函数

0x0E 内存有哪五个区

  1. 堆:存储局部变量和函数参数
  2. 栈:由 new 申请的内存区,需要程序员手动 delete 释放,否则可能会被操作系统回收
  3. 全局存储区(静态存储区):存储全局变量和静态变量(初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放)
  4. 常量存储区:里面存放常量,不允许修改
  5. 自由存储区:由 malloc 申请的内存区,需要程序员手动 free 释放,否则就被操作系统回收
int a = 0;                //静态存储区 
char *p1;                 //静态存储区 
void main()
{
   int b;                //栈 
   char s[] = "abc";     //栈 
   char *p2;             //栈 
   char *p3 = "123456";  //123456:常量存储区,p3:栈 
   static int c = 0;     //静态存储区 
   p1 = new char[10];    //堆
   p1 = "123456";        //123456:常量存储区,编译器将p1与p3所指向的"123456"优化成同一个地方
}

0x0F 如何初始化 const 型数据成员

const 型的数据成员只能通过类构造函数初始化列表来初始化,不能在类里直接初始化