const(不可变)
作用
1. 修饰常量
2. 修饰指针 指针常量和常量指针
3. 常量引用 避免拷贝,避免对值的修改,不能使用非const引用指向const对象。
4. 修饰成员函数 函数内部不能修改成员函数
使用
1 | const int a1; |
static()
作用
1. 修饰普通变量,修改变量的存储区域和生命周期,使变量存储在静态区,在 main 函数运行前分配空间,如果有初始值就用初始值初始化,如果没有初始值系统用默认值初始化。
2. 修饰普通函数,该函数仅在定义该函数的文件内才能使用。
3. 修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。
4. 修饰成员函数,修饰成员函数使得不需要生成对象就可以访问该函数,使用类名作用域符号访问,但是在 static 函数内不能访问非静态成员。
this指针
1. 指向非静态成员的特殊指针。
2. 当对一个对象调用成员函数时,编译程序先将对象的地址赋给 this 指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用 this 指针。
3. this 指针被隐含地声明为: ClassName *const this,这意味着不能给 this 指针赋值。在 ClassName 类的 const 成员函数中,this 指针的类型为:const ClassName* const,这说明不能对 this 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作)。
4. this 只是一个右值,不能使用&this.
5. 在以下场景中,经常需要显式引用 this 指针:
1. 为实现对象的链式引用。
2. 为避免对同一对象进行赋值操作。
3. 在实现一些数据结构时,如 list。
inline
特征
1. 相当于直接执行函数体。
2. 相当于宏操作,但是多了类型检查。
3. 编译器一般不内敛包含循环,递归,switch等复杂操作的inline函数。
4. 在类声明中定义的函数,除了虚函数的成员函数会自动隐式地当成inline函数。
使用
1 | // 类外定义需要显示inline |
编译器对inline函数的处理过程
1. 函数体复制到调用点。
2. 为局部变量分配内存空间。
3. 将inline函数中的输入参数和返回值映射到调用方法的局部变量空间。
4. 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 goto)。
优劣
优点
1. 内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
2. 内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
3. 在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。
4. 内联函数在运行时可调试,而宏定义不可以。
缺点
1. 代码膨胀,省去了调用函数的开销。关键在于函数体内执行时间和调用函数开销的平衡。
2. inline函数无法随着函数库升级而升级,inline函数改变需要重新编译,无法直接链接。
3. 是否内联,程序员不可控。内联函数只是对编译器的建议,是否对函数内联,决定权在于编译器。
tips:
- virtual可以inline,但是当virtual表示多态性时,不能内联。
- inline是编译器建议编译器内联,在程序运行之前,但是多态性的表现在程序运行中。
- 当编译器明确知道调用对象是哪一个类时,具有实际对象,而不是对象指针或引用,可以使用 inline virtual
1
2
3
4
5
6
7
8
9
10
11
12class A{
public:
virtual void foo(){};
}
class B:A{
public:
virtual void foo(){};
}
A *a = new B();
a->B::foo();
delete a;
a = nullptr;
volatile
1 | volatile int a = 1; |
1. volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。
2. volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)
3. const 可以是 volatile (如只读的状态寄存器)
4. 指针可以是 volatile
1 | ## assert() |
pragma pack(n)
强制设定struct,union,class成员变量以n 字节对齐方式。
1 |
位域
1 | Bit mode:2; |
类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。
- 位域内存布局和机器有关
- 类型必须是整型或者枚举,signed int根据具体实现而定。
- 取地址和指针不能作用于位域。
extern “C”
按照C语言方式编译和链接。
extern “C” 的作用是让 C++ 编译器将 extern “C” 声明的代码当作 C 语言代码处理,可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
1 |
|
struct
c
1 | typedef struct a{}s; |
cpp
编译器定位符号的规则改变,首先搜索全局标识符表,如果没有将会搜索类标识符表。
没有同名类 A,可省略struct
1 | struct A{}; |
如果有同名类 A,则A 代表类A,struct A 代表结构体A。
- class和struct 本质区别是默认的访问权限,struct默认public,class默认private。
union
联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点:
* 默认权限public
* 可以含有析构函数,构造函数
* 不能含有引用类型的成员
* 不能作为derived class,不能作为base class
* 不能含有virtual
* 匿名union在定义所在的scope可直接访问union member
* 匿名union不能包含protected和private member
* 全局匿名联合必须是static。
explicit
- 修饰构造函数,防止隐式转换和复制初始化。
- 修饰转换函数,防止隐式转换,按语境转换除外。
- 按语境转换
- if、while、for 的控制表达式;
- 内建逻辑运算符 !、&& 和 || 的操作数;
- 条件运算符 ?: 的首个操作数;
- static_assert 声明中的谓词;
- noexcept 说明符中的表达式;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class A{
A(int a){}
operator bool() const {return false;}
};
class B{
B(int b){}
explicit operator bool() const{return false;}
};
void getA(A a){}
void getB(B b){}
int main(){
A a1(1);//直接初始化
A a2 = 1;//复制初始化
A a3{1};//直接列表初始化
A a4 = {1};//复制列表初始化
A a5 = (A)1;//允许static_cast 显示转型
getA(1);//允许int到A的隐式转换
if (a1);//使用转换函数A::operator bool() 的从A到bool的隐式转换
bool a6(a1);//使用转换函数 A::operator bool()的从A到bool的隐式转换
bool a7 = a1;//使用转换函数A::operator bool()的从A到bool的隐式转换
bool a8 = static_cast<bool>(a1);//static_cast 进行直接初始化
B b1(1);//ok
B b2 = 1;//false, explitct修饰的构造函数不可复制初始化,不可复制列表初始化
getB(b1);// 被explicit修饰的构造函数的对象不允许你int到B隐式转换
if(b1);
bool b6(b1);
//被explicit 修饰的转换函数的对象可以按语境转换。
bool b7 = b1; false,被explicit修饰的构造函数的对象不允许B到bool的转型。
bool b8 = static_cast<bool>(b1);
return 0;
}
引用
1) 左值引用声明符:声明 S& D; 将 D 声明为到 声明说明符序列 所确定的类型 S 的左值引用。
2) 右值引用声明符:声明 S&& D; 将 D 声明为到 声明说明符序列 所确定的类型 S 的右值引用。
* 不存在 void 的引用,也不存在引用的引用,引用的数组,指向引用的指针.
* 引用坍缩:容许通过模板或 typedef 中的类型操作构成引用的引用,这种情况下适用引用坍缩(reference coolapsing)规则:右值引用的右值引用 坍缩成右值引用,所有其他组合均 坍缩成左值引用;
* 当函数的返回值是左值引用时,函数调用表达式成为左值表达式:
1 | typedef int& lref; |
左值引用
常规引用,一般表示对象的身份。
右值引用
右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。
右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:
消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
能够更简洁明确地定义泛型函数。
右值引用可用于为临时对象延长生存期(左值引用亦能延长临时对象生存期,但不能通过左值引用修改)
成员初始化列表
有些场合必须要用初始化列表:
1. 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
2. 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化