特殊工具与技术
控制内存分配
- 当需要自定义内存分配细节时,需要使用new和delete运算符以控制内存分配的过程
重载new和delete
- 尽管我们说能够重载new和delete,但实际上重载这两个运算符与重载其他运算符的过程大不相同
//new 表达式
string * sp = new string("a value");
string * arr = new string(10);
- [x] 1:new表达式调用一个名为operator new(或者operator new[])的标准库函数,该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象
- [x] 2:编译器运行相应的构造函数以构造这些对象,并为其传入初始值
- [x] typeid运算符,用于返回表达式的类型
- [x] dynamic_cast运算符,用于将基类的指针或引用安全地转换成派生类地指针或引用
- 当我们将这两个运算符用某种类型地指针或引用,并且该类型含有虚函数时,运算符将使用指针或引用所绑定对象地动态类型
枚举类型
- 枚举类型使我们可以将一组整型常量组织在一起,和类一样,每个枚举类型定义了一种新的类型,枚举属于字面值常量类型
- C++包含两种枚举:限定作用域地和不限定作用域地
- [x] C++11新标准引入了限定作用域地枚举类型,定义限定作用域地枚举类型一般形式是:首先是关键字enum class,随后是枚举类型名字以及用花括号括起来地以逗号分隔的枚举成员列表,最后是一个分号
enum class open_modes{input,output,append};
- 定义不限定作用域的枚举类型时省略关键字class,枚举类型的名字是可选的:
enum color{red,yellow,green};
- 默认情况下,枚举值从0开始,依次加1,不过也可以为一个或几个枚举成员指定专门的值
- 枚举值不一定唯一,如果我们没有显示的提供初始值,则当前枚举成员的值等于之前枚举成员的值加1
- 枚举成员是const,因此在初始化枚举成员时提供的初始值必须时常量表达式,也就是说,每个枚举成员本身就是一条常量表表达式,我们可以在任何需要常量表达式的地方使用枚举成员
- 尽管每个enum都定义了唯一的类型,但实际上enum是由某种整数类型表示的,在C++11的新标准中,我们可以在enum的名字后面加上冒号以及我们想在该enum中使用的类型
enum intValues : unsigned long long {
charTyp = 255,
shortTyp = 65535
};
- 如果我们没有指定enum的潜在类型,则默认情况下限定作用域的enum成员类型是int
- 对于不限定作用域的枚举类型来说,其枚举成员不存在默认类型,我们只知道成员的潜在类型足够大,肯定能容纳枚举值
- 指定enum潜在类型的能力使得我们可以控制不同实现环境中使用的类型,我们将可以确保在一种实现环境中编译通过的程序所生成的代码与其他实现环境中生成的代码一致
类成员指针
- 成员指针是指可以指向类的非静态成员指针
- 一般情况下,指针指向一个对象,但是成员指针指示的是类的成员,而非类的对象
- 类的静态成员不属于任何对象,因此无须特殊的指向静态成员的指针,指向静态成员的指针与普通指针没有什么区别
- 成员指针的类型囊括了类的类型以及成员的类型,当初始化一个这样的指针时,我们令其指向类的某个成员,但是不指定该成员所属的对象,直到使用成员指针时,才提供成员所属的对象
嵌套类
- 一个类可以定义在另一个类的内部,前者称为嵌套类或嵌套类型
- 嵌套类常用于定义作为实现部分的类
- 嵌套类是一个独立的类,与外层类基本上没什么关系,特别是,外层类的对象和嵌套类的对象是相互独立的,在嵌套类的对象中不包含任何外层类定义的成员,类似的,在外层类的对象中也不包含任何嵌套类定义的成员
union:一种节省空间的类
- union是一种特殊的类,一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值
- 当我们给union的某个成员赋值之后,该union的其他成员就变成未定义的状态了
- 分配给一个union对象的存储空间至少要能容纳它的最大的数据成员,和其他类一样,union顶i有了一种新类型
- union不能含有引用类型的成员,除此之外,它的成员可以是绝大多数类型
- 在C++11中,含有构造函数或析构函数䣌类类型也可以作为union的成员类型
- union可以为其成员指定public、protected、private等保护标记
- 默认情况下,union的成员都是公有的,这一点与struct相同
- union可以定义包括构造函数和析构函数在内的成员函数,但是由于union既不能继承自其他类,也不能作为基类使用,所以union中不能含有虚函数
union的定义
union Tocken{
char cval;
int ival;
double dval;
};
- 上述union对象可以保存一个值,这个值的类型可能是char、int、double中的一种
- union的名字是一个类型名,和其他内置类型一样,默认情况下union是未初始化的,我们可以像显式地初始化聚合类一样使用一对花括号内地初始值显式地初始化一个union
Token first_tocken = {'a'}; //初始化cval成员
Token last_tocken; //未初始化地Token对象
Token *pt = new Token; //指向一个未初始化地Token对象的指针
局部类
- 类可以定义在某个函数的内部,这样的类即局部类
- 局部类只在定义它的作用域内可见,和嵌套类不同,局部类的成员受到严格限制
- 局部类的所有成员(包括函数在内)都必须完整定义在类的内部,因此,局部类的作用与嵌套类相比相差很远
- 在实际编程的过程中,因为局部类的成员必须完整定义在类的内部,所以成员函数的复杂性不能太高,局部类的成员函数一般只有几行代码,否则我们就很难读懂它了
- 在局部类中不允许声明静态数据成员,因为我们没法定义这样的成员
局部类不能使用函数作用域中的变量
- 局部类对其外层作用域中名字的访问权限受到很多限制,局部类只能访问外层作用域定义的类型名、静态变量以及枚举成员
- 如果局部类定义在某个函数内部,则该函数的普通局部变量不能被该局部类使用
固有的不可移植特性
- 为了支持底层编成,C++定义了一些固有的不可移植的特性
- 所谓不可移植的特性是指因机器而异的特性,但我们将含有不可移植特性的程序凑够一台机器转移到另一台机器上时,通常需要重新编写该程序
- 算术类型的大小在不同机器上不一样,这是我们使用过的不可移植特性的一个典型事例
位域
- 类可以将器(非静态)数据成员定义成位域,在一个域中含有一定数量的二进制位,当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会使用位域
- 位域在内存中的布局是与机器相关的
- 位域的类型必须是整型或枚举类型
- t通常情况下我们使用一个无符号类型保存一个位域
- 位域的声明形式是在成员名字之后紧跟一个冒号以及一个常量表达式,该表达式用于指定成员所占的二进制位数
Bit mode: 2;
- 取地址运算符不能用于位域,因此任何指针都无法指向类的位域
- 通常情况下最后将位域设为无符号类型,存储在带符号类型中的位域的行为将因其具体实现而定
volatitle限定符
- volatitle的确切含义与机器有关,只能通过阅读编译器文档来理解,要想让使用了volatitle的程序在移植到新机器或新编译器后仍然有效,通常需要对程序进行改变
- 当对象的值可能在程序的控制或检测之外被改变时,应该将对象声明位volatitle,关键字告诉编译器不对这样的对象进行优化
volatitle int display_register; //该int值可能发生改变
链接指示:extern"C"
- C++程序有时需要调用起他语言编写的函数,最常见的时调用C语言编写的函数
- 像所有其他名字一样,其他语言中的函数名字也必须在C++中进行声明,并且该声明必须指定返回类型和形参列表
- 对于其他语言编写的函数来说,编译器检查其调用的方式与处理普通C++函数的方式相同,但是生成的代码有所区别
- C++使用
链接指示
指出任意非C++函数所用的语言
- 要想把C++代码和其他语言编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器是兼容的
//单语句链接指示
extern "C" size_t strlen(const char *);
//复合语句链接指示
extern "C" {
}