[读书笔记] C++Primer (第5版) 第19章 特殊工具与技术

1.控制内存分配(new和delete):

new表达式执行的三个步骤:

  • 调用一个名为operator new或operator new[]的标准库函数,该函数分配一块足够大的、原始的、未命名的内存空间,以便存储特定类型的对象或对象数组。
  • 编译器运行相应的构造函数来构造这些对象,并传入初始值。
  • 对象被分配了空间并构造完成,返回一个指向该对象的指针。

delete表达式执行的两个步骤:

  • 对所指的对象或数组中的元素执行对应的析构函数
  • 编译器调用operator delete或operator delete[]的标准库函数释放内存空间

如果应用程序希望控制内存分配的过程,可以定义自己的operator new和operator delete。 编译器会使用自定义的版本替换标准库中的版本。
当编译器发现一条new或delete表达式,如果被分配(释放)的对象是类类型,现在类的作用域中找operator new成员,如果没有,再在全局作用域中查找。全局作用域中没有则执行标准库版本。
::new表示只在全局作用域中查找匹配。

void *operator new(size_t);				//返回类型必须是void*,第一个形参必须是size_t
void *operator delete(void*) noexcrpt;	//返回类型必须是void,第一个形参必须是void*

可以重载new,但是有一个版本只供标准库使用,不能被用户重新定义:
void operator new(size_t, void);
实际运行的operator delete函数版本由对象的动态类型决定。
定位new表达式: new (place_address) type; //place_address是地址
可以实现内存分配和初始化分离,这块地址下进行初始化。该指针不是一定要指向动态内存。

2.运行时类型识别:

运行时类型识别(RTTI)的由两个运算符实现:

  • typeid运算符,用于返回表达式的类型
  • dynamic_cast运算符,用于将基类指针或引用安全地转换成派生类的指针或引用

如果一条dynamic_cast语句的转换目标是指针练行且失败了,则结果为0。如果转换目标是引用类型且失败了,则抛出一个bad_cast异常。
typeid(e); //e可以是任意表达式或类型的名字
当运算对象不属于类类型或不包含任何虚函数的类时,typeid运算符指示的是运算对象的静态类型。当运算定对象是定义了至少一个虚函数的类的左值时,typeid的结果直到运行时才会求得。
当type id作用于指针时(而非指针所指的对象),返回的结果是该指针的静态编译时类型。
创建type_info对象的唯一途径是使用typeid运算符。

3.枚举类型:

枚举类型属于字面值常量类型。
两种枚举类型,限定作用域和不限定作用域。

enum class open_modes{input, output, append};	//限定作用域,也可写成enum struct
enum color{red, blue, green};	//不限定作用域

限定作用域的枚举成员遵循常规的作用域准则。不限定作用域的枚举成员与枚举类型本身作用域相等。
如果没有给枚举成员提供初始值,则值为上一个成员的值+1。
不限定作用域的枚举类型可以隐形转换,限定作用域的不能。
可以指定enum使用的类型 enum color : unsigned long long {a, b};
限定作用域enum成员的默认类型是int,不限定作用域enum成员的潜在类型足够大。
要初始化一个enum对象,必须使用该enum类型的另一个对象或它的一个枚举成员。

4.类成员指针:

成员指针:可以指向类的非静态成员的指针。

//指向ClassA对象的string成员 ClassA:类名  pData:不是成员变量名,是成员指针的名字
string ClassA::*pData;
//初始化一个成员指针时,需要指定它所指的成员
pData = &ClassA::contentA;
//成员指针指定了成员而非该成员所属的对象,只有解引用成员指针时我们才提供对象的信息
ClassA myClass, *pMy = &myClass;
auto s = myClass.*pData;  // 使用解引用符(.*)获取到myClass对象的contentA成员
s = pMy->*pData;   // 使用解引用符(->*)获取到pMy所指对象的contentA成员

上面是成员数据指针,也有成员函数指针。
成员函数指针的解引用运算符要加括号,因为它的优先级小于调用运算符。
可以使用function,mem_fn和bind将成员函数指针用作为可调用对象。

5.嵌套类:

一个类可以定义在另一个类的内部,前者称为嵌套类或嵌套类型。
外层类的对象和嵌套类的对象是互相独立的。彼此不包含任何对方的成员。
外层类的对象只包含外层类定义的成员,在外层对象中不会有任何嵌套类的成员。

class ClassOut{
public:   ClassNested classA;
}
class ClassOut::ClassNested{   
/*内嵌类ClassNested的定义部分*/
}

6.union:一种节省空间的类

联合是一种特殊的类,一个union可以有多个数据成员,但是任意时刻只有一个数据成员有值。
当我们给union的某个成员赋值后,该union的其他成员就变成了未定义的状态。
union不能还有引用类型的成员。

union Token   // Taken类型的对象只有一个成员,该成员的类型可能是下列类型中的任意一种
{
char        cval;  // 默认是public的
int           ival;
double   dval;
}

匿名union是一个未命名的union。在花括号和分号之间没有任何声明。一旦我们定义了一个匿名union,编译器就自动为该union创建一个未命名的对象。
在匿名union定义所在的作用域内,该union的成员都是可以直接访问的。
可以在类中使用一个枚举类型作为判别符,标表示我当前union的类型。

7.局部类:

类可以定义在某个函数的内幕,我们称这样的类为局部类。
局部类的所有成员(包括函数在内),都必须完整定义在类的内部,不允许声明静态数据成员。
局部类只能访问外层作用域定义的类型名,静态变量以及枚举成员。
如果局部类定义在某个函数内部,则该函数的普通局部变量不能被该局部类使用。

8.固有的不可移植的特性:

不可移植特性是指因机器而异的特性。当我们将还有不可移植特性的程序从一台机器转移到另一台机器时,需要重新编写该程序。
那可以将其非静态数据成员定义成位域,占一个位域中还有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。
位域的类型必须是整型或者枚举类型。卫浴在内存的分布是与机器相关的,如果可能的话,在类的内部,连续定义的位域压缩在同一整数的相邻位,从而提供存储压缩。
取地址符(&)不能作用于位域,因此任何指正都无法指向类的位域。

class ClassA {
Bit mode: 2;        //mode占两位
Bit modified: 1;   //modified占一位
}

volatile限定符:volatile的确切含义与机器有关,只能通过阅读编译器文档来理解。当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为volatile,告诉编译器不应该对这样的对象进行优化。
链接指示:extern“C” :C++使用链接指示指出任何非C++函数所用的语言。
要想把C++代码和其他语言编写的代码放在一起,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器兼容。
链接指示不能出现在类定义或函数定义内部,同样的链接指示必须在函数的每个声明中都出现。
当一个include只是被放置在复合链接指示的花括号中,头文件中所有普通函数声明都被认为有链接指示的语言编写的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值