C++基础知识
函数指针
函数指针作用:
- 提供调用的灵活性,例如qsort中的排序,是选择排大,还是排小
- 实现面向对象编程中的多态性
- 回调函数
- 简化结构和程序通用性
typedef const double* (p_fun)(const double d);//p_fun是一种typename,而不是函数指针
void (*fun(int (*arg)(void)))(void); // 返回值为void()函数指针,形参为int ()函数指针,函数名为fun
C++类型
- 类默认成员为private,结构体默认为public
- 空结构体和类大小为1,空类也可以实例化,类实例化出的每个对象都需要有不同的内存地址,为使每个对象在内存中的地址不同,所以在类中会加入一个隐含字节
- 成员变量在类中的内存存储并不一定是连续的。它是按照编译器的设置,按照内存块来存储的,这个内存块大小的取值,就是内存对齐。
- vs中使用#pragma pack(1)宏可以更改块的大小 位字段无法用sizeof获取内存大小
- 结构体变量中的成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
- 结构体大小必须是所有成员大小的整数倍,也即所有成员大小的整数倍
- 类成员构造顺序:基类静态成员 – 子类静态成员 – (设置v_ptr/基类成员变量 ) –基类构造函数 – (设置v_ptr/子类成员变量) – 子类构造函数
函数返回多个值的方法
- 把返回值打包返回,如返回一个数组名,指针,结构体
- 函数外定义,函数类修改
C11内存持续性
- 自动存储持续性:栈内存中的变量
- 静态存储持续性:使用static定义的变量,程序运行整个过程
- 线程存储持续性:使用Local_thread修饰的变量,线程生命周期
- 动态存储持续性:直到delete或free
智能指针
有3种方案来管理内部指针:
- delete掉复制构造,复制拷贝
- 引用计数
- 底层复制,即复制内部指针指向的内存
Share_ptr:采用方案二,引用计数,但是存在内存泄漏的风险(相互引用)
Unique_ptr:采用方案一,用来替代auto_ptr
Weak_ptr:用来处理share_ptr相互引用的内存泄漏问题,由Share_ptr生成,但不会增加Share_ptr计数,同时也不会管理对象生命
auto_ptr:被摈弃
函数内联
添加inline标识符,编译器不一定会采用内联,编译器需要支持内联,函数过大,递归函数与宏定义不同,宏定义通过文本替换实现,而内联是参数传递
类中内联的4种方式
1、 隐式内联,实现在类中
2、 显示内联,类里声明inline,类中或类外实现
3、 追加内联,类中不声明inline,类外声明并实现inline
4、 非内联,定义和申明都没有inline
返回值类型确定:
1、返回指向const对象的引用
2、返回非const对象引用
常见引用目的提高效率,免去复制构造和析构,非const典型为cout,连续操作对象
3、返回对象
函数体内的临时变量
4、返回const对象
示例:加法运算:
Vector Vector::operator+()
o1 + o2 = o3;//正确运行
应该修改为const Vector Vector:operator+();
o1 + o2 = o3;//会报错
explicit
只有单个参数的构造方法,才会可能隐式转换,explicit必须显示调用,隐式转换的可能:
1、 将Class对象初始化为double值
2、 将double值赋值给Class对象
3、 将double值传递给接受Class对象参数的函数时
4、 返回值为Class对象时,返回double值
5、 在上述任意一种情况下,使用可转换为double类型的内置类型时
函数栈框架
一个进程中可以同时包含多个线程。
我们通常认为线程是操作系统可识别的最小并发执行和调度单位。
同一进程中的多个线程共享代码段(代码和常量)、数据段(静态和全局变量)和扩展段(堆存储),但是每个线程有自己的栈段。栈段又叫运行时栈,用来存放所有局部变量和临时变量(参数、返回值、临时构造的变量等)。这一条对下文中的某些概念来说是非常重要的 。但是请注意,这里提到的各个“段”都是逻辑上的说法,在物理上某些硬件架构或者操作系统可能不使用段式存储。不过没关系,编译器会保证这些逻辑概念和假设的前提条件对每个 C/C++ 程序员来说始终是成立的。
一般来说,编译器会为当前调用栈里的每个函数建立一个栈框架(Stack Frame)。“栈框架”担负着以下重要任务:
传递参数:通常,函数的调用参数总是在这个函数栈框架的最顶端。
传递返回地址:告诉被调用者的 return 语句应该 return 到哪里去,通常指向该函数调用的下一条语句(代码段中的偏移)。
存放调用者的当前栈指针:便于清理被调用者的所有局部变量、并恢复调用者的现场。
存放当前函数内的所有局部变量:记得吗?刚才说过所有局部和临时变量都是存储在栈上的。
由于共享了除栈以外的所有内存地址段,线程不可以有自己的“静态”或“全局”变量,为了弥补这一缺憾,操作系统通常会提供一种称为 TLS(Thread Local Storage,即:“线程本地存储”)的机制。通过该机制可以实现类似的功能。TLS 通常是线程控制块(TCB)中的某个指针所指向的一个指针数组,数组中的每个元素称为一个槽(Slot),每个槽中的指针由使用者定义,可以指向任意位置(但通常是指向堆存储中的某个偏移)。
decltype推导四准则
decltype推导四规则
- 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误。
- 否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
- 否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&。
- 否则,假设e的类型是T,则decltype(e)为T。
标记符指的是除去关键字、字面量等编译器需要使用的标记之外的程序员自己定义的标记,而单个标记符对应的表达式即为标记符表达式。
通用引用
通用引用只在类型推导时会出现:
- 函数模板参数(function template parameters)
- auto声明(auto declaration)
- auto && var = …;
- typedef声明(typedef declaration)
- decltype声明(decltype declaration)
只有在通用应用中才会出现引用合成
类型转换
static_cast 转换失败抛出异常,向下转换不会进行类型检查
dynamic_cast支持交叉转换,转换失败返回空指针,向下转换会进行类型检查判断
系统调用
频繁调用系统内核函数会导致性能降低,因为程序进入内核态时,系统会将程序封装保存,在切入内核态,返回切换会导致性能降低