目录
1.static的作用
(1)限制了修饰对象的作用域为本程序文件。外部程序文件不能访问该对象,本文件中的静态或非静态函数都可以调用静态变量和函数。
(2)使修饰的对象具备记忆功能和全局生存期,static定义的对象存储在静态存储区。
(3)类中定义的静态成员直接用类作用域调用,即 A::func(),A::var。类的静态成员只能够调用该类的静态成员,不能调用非静态成员,这是因为类内非静态成员是通过指向类对象实例的this指针调用的,而静态成员没有this指针。
(4)类内声明静态成员时,不占用内存,需要在类以及main函数外进行定义,若没有显示初始化,则在第一次调用时执行初始化,默认初始化为0。
2.引用和指针的区别
(1)指针是一个存放地址的变量,而引用是变量的别名。从汇编实现的角度看,引用和指针都是占用内存的,引用的实现是一个指针常量,因此当引用绑定了一个对象后,不能转而绑定另外一个变量。
(2)指针在声明时可以不初始化(建议初始化),而引用在声明时必须初始化,绑定一个对象。
(3)指针是对象,而引用不是对象。因此可以定义指针的指针,不能定义引用的引用。
(4)sizeof得到的值不同 。sizeof指针得到指针所占的内存大小(32位系统为4B,64位系统为8B),sizeof引用得到的是绑定的对象所占的内存大小。
3.malloc/free与new/delete的区别
(1)属性不同。malloc/free是库函数,需要头文件支持;new/delete是关键字,需要编译器支持。
(2)参数不同。malloc在申请内存时需要具体指出申请的字节长度;new会自动根据类型申请相应大小的内存。
(3)返回值不同。malloc申请成功时返回void*,因此需要强制类型转换,失败则返回NULL;new申请成功时返回对象类型的指针,无需进行类型转换,失败则抛出bad_alloc异常。
(4)申请方式不同。new先调用operate new申请内存(底层使用malloc),然后调用对象类型的构造函数初始化,delete先调用对象类型的析构函数销毁对象,再调用operate delete释放内存(底层用free);而malloc/free只申请内存空间,没有初始化或是销毁对象,因此需要另外定义函数执行这部操作。
(5)重载问题。malloc/free不支持重载;new/delete支持重载。
(6)布局new不申请内存空间,仅将已分配的内存空间指定为起始内存地址。
(new申请的内存在“自由存储区”上,这个“自由存储区”是个针对new提出的抽象概念,一般是堆,但也可以改在其他内存存储区域)
4.内存泄漏问题及检测方式
(1)内存泄漏的情况:
- 在定义指针时没有进行初始化,在接下来又使用了该指针;
- new了一个指针,当不再需要时没有及时进行delete,造成内存空间浪费;
- delete一个指针后,没有让该指针指向nullptr,当再次使用时出现野指针的情况;
- 多个指针指向一个内存区域,当delete掉一个指针时,内存已被释放,再使用其他指针导致出错;
(2)检测方法:
- VS中导入CRT库进行内存的检测;
- 尽量使用STL,指针都用C++11的智能指针;
5.预处理指令
6.宏定义#define与全局const的区别
(1)编译器处理的方式不同。宏在预处理阶段进行展开,因此无法直接进行调试;const常量时在编译运行阶段处理。
(2)类型安全检查不同。宏没有类型,因此不进行类型安全检查;const修饰的对象在编译阶段执行类型安全检查。
(3)存储方式不同。宏在预处理中展开,也即是在编译前就将所有用到宏的地方进行替换,不分配内存;const修饰的对象需要分配内存。
(4)宏具有边缘效应,在展开时只是字符串的替换,可能会出现与原始语义不同的问题。
(5)宏不能作为参数传值给函数,const修饰的对象可以。
(6)宏在定义之后可以通过#undef进行销毁。
7.简述宏函数
(1)宏函数的格式,这里以交换两数为例,#define swap(a,b) a = a + b; b = a - b; a = a - b;
(2)宏函数的参数没有类型,因此可以传入任意类型的实参。
(3)宏函数不适用于代码量较大且频繁使用的函数,否则在进行宏展开时会造成大量的代码替换,导致生成的目标文件太大。
8.宏函数与普通函数的区别
(1)类型安全检查。宏函数的形参没有规定类型,不进行类型安全检查;普通函数的传参需要进行类型安全检查。
(2)时空间的开销。宏函数在预处理阶段进行宏展开,即简单的字符替换,因此会生成的目标文件较大;普通函数则在调用的时候需要申请栈空间进行现场的保护恢复,参数的压栈等操作。
(3)在执行比较复杂的功能时,宏函数的效率会比较低,因此比较适用于安全要求低,简单且频繁使用的函数;普通函数更适用于安全性有要求且更为复杂的场景。
9.遍历时用++i还是i++
用++i,因为i++会创建一个临时变量保存i的值,之后又进行销毁,而i只是类似计数的作用,不需要保存先前的值,因此在这种情况下++i的效率更高。
10.如何判断一个程序是由C编译还是C++编译的
#ifdef __cplusplus
printf("C++\n");
#else
printf("C\n");
以上内容均是个人理解总结,若有错误欢迎指出!