关键字const
主要作用:
- 修饰变量或对象,说明该变量或对象不能被改变
- 修饰指针,分为指向常量的指针(const在星号左边,不能用指针改值,属于底层const)和常指针(const在*号右边,能用指针改值,属于顶层const)
- 修饰引用,常用于形参,既避免了拷贝,又避免了函数对值的修改
- 修饰成员函数,说明该成员函数内不能修改成员变量。此时可以用于区分重载(常对象调用常成员函数)
- 修饰成员变量,此时只能在构造函数初始化参数列表初始化
拓展:
与define的区别:
- const常量有数据类型,而宏常量没有
- 编译器可以对const进行安全检查,而只对宏常量进行字符替换
- 有些调试工具只可以对const进行调试
如何修改常成员函数中类的成员变量:
- 在类的成员变量中,用mutable修饰成员变量即可
关键字static
主要作用:
- 修饰普通变量,使变量存储在静态区。在main函数前就分配了空间。有初始值就初始化,没有就默认初始化
- 修饰普通函数,防止多人开发时函数名相同
- 修饰成员变量,使所有该类的对象共享这一份复制,在实现文件中初始化。不需要生成对象就能访问该成员。
class::m_val
- 修饰成员函数,此时static成员函数没有this指针,故只能访问static成员变量。不需要生成对象就能使用该函数。
class::m_func
- 函数体内的static变量作用范围仅为该函数体,不同于auto变量,该变量的内存只被分配一次。在下一次调用该函数时依然维持上一次的值。
this指针
- 是一个隐含于每一个非静态成员函数中的指针(全局函数和静态函数没有),指向正被该成员函数操作的那个对象
- 本质是成员函数的第一个参数
T*const this
(常指针)。常成员函数第一个参数为const T *const this
。调用成员函数时,编译器将类的指针作为实参传递进去,成员函数内隐含使用this指针访问数据成员 - this不是一个常规变量,只是一个右值,不能取其地址&this
- this指针并不占用对象的空间,跟对象之间没有包含关系,只是当前调用的对象被它指向而已
- 避免自赋值
if (this == &rhs) return *this;
inline内联函数
inline必须与函数定义放在一起才能使函数成为内联,仅放在函数声明前不起作用
例:
普通函数:声明定义都以inline修饰
成员函数:类内定义则隐式当成内联函数;类外定义则类外定义处使用inline修饰
优点:
- 内联函数同宏函数一样在被调用处进行代码展开,省去了函数调用的开销(参数压栈、栈帧开辟和回收、结果返回等),从而提高程序的运行速度
- 内联函数相较于宏,在代码展开时会做安全检查或自动类型转换,且可以在运行时调试,真正具有函数特性
- 在类的声明中定义的函数,除了虚函数的其他函数都隐式地当成内联函数
缺点:
- 代码膨胀(复制):内联是通过代码复制来消除函数调用的开销,每一处内联函数调用都要复制代码,使总代码量增大
- 不能包含循环、递归、switch等复杂操作
- 是否对函数内联,决定权在编译器
扩展:
虚函数能否是内联函数?
- 可以是,若虚函数是通过对象来访问的,在编译期间就能确定,于是可以内联,但最终是否内联取决于编译器
- 若虚函数是通过基类指针来访问的,呈现多态性,在编译期间不能确定,编译器无法知道运行期调用哪个代码,于是就不能内联。否则在delete基类指针时会先调用派生类的析构函数,再调用基类析构函数
assert断言
- 是一个宏,并非函数
- 原型定义在
<assert.h>(C)
、<cassert>(C++)
中 - 如果它的条件返回false,则输出错误信息,程序终止执行
sizeof关键字(操作符)和字节对齐
sizeof介绍
它并非函数,返回字节数。
括号内的内容在编译过程中是不被编译的,而是被类型替代
字节对齐
字节对齐是在编译时决定的,一旦决定则不会再改变。
- 作用于数据类型
- 作用于数组,则得到数组所占空间的大小
- 作用于指针,则得到指针本身所占空间的大小(一般为4)
- 作用于变量,则得到变量类型所占空间的大小
- 作用于普通结构体和类,结构体长度一定是最长的数据元素的整数倍,结构体内类型相同的连续元素和数组一样,将在连续空间内。
- 作用于带有虚函数或者虚继承的类
- pack预处理指令:禁止对齐调整(不要轻易做这样的调整,会降低程序性能),合法参数是1\2\4\8\16
总结:
- 当作用于结构类型或变量,sizeof返回实际的大小
- 当作用于静态的数组,sizeof返回全部数组的大小
- sizeof不能返回被动态分配的数组或外部的数组大小
- 数组作为参数传递给函数时传递的是指针而不是数组,于是函数中
return sizeof(数组名)
结果是4 - 一般地址总线总是按照对齐后的地址来访问的。假如你想得到0x00000001开始的4字节内容,系统首先需要以0x00000000读4字节,从中取得3字节,然后再用0x00000004作为开始地址,获得下一个4字节
总之记住四条规则:
- 地址一般从0开始
- 元素存放的位置一定会在自己大小的整数倍上开始
- 检查计算出的存储单元是否为所有元素中最宽的元素长度的整数倍。若是,则结束;否则,将其补齐为它的整数倍
- 当结构体内的元素的长度都小于处理器位数时,以结构体中最长数据元素为对其单位。否则以处理器位数作为对齐单位
与strlen的区别
区别:
- strlen的参数只能是char*,而且必须是
‘\0’
结尾的(由于其实现是计算‘\0’
以前的字符个数),而且它是一个函数。sizeof是一个关键字。 - sizeof操作符的结果类型是size_t,在头文件中typedef为unsigned int类型
- sizeof在编译的时候就能计算出来,而strlen要在运行时才能确定,主要用来计算字符串长度