目录
1、define、const、typedef 的区别
define | const | typedef |
发生在编译的预处理阶段,只是简单的字符串替换,使用多少次就多少次替换,没有类型检查,不安全;可以用来防止头文件重复引用 | 用于定义常量生效于编译阶段,有类型,define也可以定义常量不带类型,生效于预处理阶段直接使用不放入内存 | 有对应的数据类型,在编译、运行阶段是要进行类型判断的;在静态存储区中分配空间,在程序运行过程中内存中只有⼀个拷贝 |
2、constexpr
constexpr 表示“常量”的语义,只能定义编译期常量,你将⼀个成员函数标记为constexpr,则顺带也将它标记为了const。如果你将⼀个变量标记为constexpr,则同样它是const的。但相反并不成立,⼀个const的变量或函数,并不是constexpr的。
constexpr变量:
复杂系统中很难分辨⼀个初始值是不是常量表达式,可以将变量声明为constexpr类型,由编译器来验证变量的值是否是⼀个常量表达式。初始化 constexpr int n = 1; constexpr int m = n + 1;
constexpr函数:
constexpr函数是指能用常量表达式的函数,函数的返回类型和所有形参类型都是字面值类型,函数体有且只有⼀条return语句。为了可以在编译过程展开,constexpr函数被隐式转换成了内联函数
constexpr和内联函数可以在程序中多次定义,⼀般定义在头文件。
constexpr 构造函数:构造函数不能是const,但字面值常量类的构造函数可以是constexpr。
constexpr构造函数必须有⼀个空的函数体,即所有成员变量的初始化都放到初始化列表中。对象调用的成员函数必须使用 constexpr 修饰。
3、内存泄漏及避免
释义:内存泄漏(memory leak)是程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误失去了对该段内存的控制,而造成了内存的浪费,常见于指针指向改变,未释放动态分配的内存。可以用Valgrind, mtrace进行内存泄漏检查。
常见内存泄漏:1)通过malloc,new等从堆中分配的⼀块内存,完成后须通过调用对应的 free或者 delete释放掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,产生Heap Leak。2)主要指程序使用系统分配的资源如 Bitmap,handle ,SOCKET 等没有用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。3)当基类指针指向子类对象时,如果基类的析构函数不是 virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露;
避免:将内存的分配封装在类中,构造函数分配内存,析构函数释放内存;使用智能指针。
4、new和malloc,delete和free
new | malloc |
C++的运算符,可以为对象分配内存并调用相应的构造函数 | malloc 是C语言库函数,只分配指定大小的内存块,不会调用构造函数 |
new 返回的是具体类型的指针,而且不需要进行类型转换 | malloc 返回的是 void* ,需要进行类型转换,因为它不知道所分配内存的⽤途 |
new 在内存分配失败时会抛出 std::bad_alloc 异常 | malloc 在内存分配失败时返回 NULL |
new 可以⽤于动态分配数组,并知道数组大小 | malloc 只是分配指定大小的内存块,不了解所分配内存块的具体⽤途 |
delete | free |
delete 会调用对象的析构函数,然后释放内存,确保资源正确释放 | free 只是简单地释放内存块,不会调用对象的析构函数 |
delete 释放的内存块的指针值会被设置为 nullptr ,以避免野指针 | free 不会修改指针的值,可能导致野指针问题 |
delete 可以正确释放通过 new[] 分配的数组 | free 不了解数组的大小,不适用于释放通过 malloc 分配的数组 |
一般new[]和delete[]要配对使用,因为new[]会创建一个数组,一个对象数组需要一定的空间大小,一个对象需要N个字节,k个对象需要k*N个空间来构造对象,但是delete[]如何知道数组的长度?new[]在k*N的基础上头部多申请4个字节用于存储数组长度,这样delete时候知道对象数组的大小,才会相应调用k次析构函数释放kN+4大小的内存。
如果是内置类型,可以不匹配使用,因为内置类型在申请时知道是内置类型不需要析构函数,也不需要多4个字节存储数组长度只需要直接操作内存即可。
C++ new操作符在内存分配失败时会抛出std::bad_alloc异常而不是返回nullptr,所以理论上不用判空,因为失败进入异处理,如果使用int* p=new(std::nothrow) int;这是失败会返回nullptr需要判空
5、面向对象三大特性
封装:将数据和代码捆绑在⼀起,避免外界干扰和不确定性访问