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是地址 可以实现内存分配和初始化分离,这块地址下进行初始化。该指针不是一定要指向动态内存。
运行时类型识别(RTTI)的由两个运算符实现:
typeid运算符,用于返回表达式的类型dynamic_cast运算符,用于将基类指针或引用安全地转换成派生类的指针或引用如果一条dynamic_cast语句的转换目标是指针练行且失败了,则结果为0。如果转换目标是引用类型且失败了,则抛出一个bad_cast异常。 typeid(e); //e可以是任意表达式或类型的名字 当运算对象不属于类类型或不包含任何虚函数的类时,typeid运算符指示的是运算对象的静态类型。当运算定对象是定义了至少一个虚函数的类的左值时,typeid的结果直到运行时才会求得。 当type id作用于指针时(而非指针所指的对象),返回的结果是该指针的静态编译时类型。 创建type_info对象的唯一途径是使用typeid运算符。
枚举类型属于字面值常量类型。 两种枚举类型,限定作用域和不限定作用域。
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类型的另一个对象或它的一个枚举成员。
成员指针:可以指向类的非静态成员的指针。
//指向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将成员函数指针用作为可调用对象。
一个类可以定义在另一个类的内部,前者称为嵌套类或嵌套类型。 外层类的对象和嵌套类的对象是互相独立的。彼此不包含任何对方的成员。 外层类的对象只包含外层类定义的成员,在外层对象中不会有任何嵌套类的成员。
class ClassOut{ public: ClassNested classA; } class ClassOut::ClassNested{ /*内嵌类ClassNested的定义部分*/ }联合是一种特殊的类,一个union可以有多个数据成员,但是任意时刻只有一个数据成员有值。 当我们给union的某个成员赋值后,该union的其他成员就变成了未定义的状态。 union不能还有引用类型的成员。
union Token // Taken类型的对象只有一个成员,该成员的类型可能是下列类型中的任意一种 { char cval; // 默认是public的 int ival; double dval; }匿名union是一个未命名的union。在花括号和分号之间没有任何声明。一旦我们定义了一个匿名union,编译器就自动为该union创建一个未命名的对象。 在匿名union定义所在的作用域内,该union的成员都是可以直接访问的。 可以在类中使用一个枚举类型作为判别符,标表示我当前union的类型。
类可以定义在某个函数的内幕,我们称这样的类为局部类。 局部类的所有成员(包括函数在内),都必须完整定义在类的内部,不允许声明静态数据成员。 局部类只能访问外层作用域定义的类型名,静态变量以及枚举成员。 如果局部类定义在某个函数内部,则该函数的普通局部变量不能被该局部类使用。
不可移植特性是指因机器而异的特性。当我们将还有不可移植特性的程序从一台机器转移到另一台机器时,需要重新编写该程序。 那可以将其非静态数据成员定义成位域,占一个位域中还有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。 位域的类型必须是整型或者枚举类型。卫浴在内存的分布是与机器相关的,如果可能的话,在类的内部,连续定义的位域压缩在同一整数的相邻位,从而提供存储压缩。 取地址符(&)不能作用于位域,因此任何指正都无法指向类的位域。
class ClassA { Bit mode: 2; //mode占两位 Bit modified: 1; //modified占一位 }volatile限定符:volatile的确切含义与机器有关,只能通过阅读编译器文档来理解。当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为volatile,告诉编译器不应该对这样的对象进行优化。 链接指示:extern“C” :C++使用链接指示指出任何非C++函数所用的语言。 要想把C++代码和其他语言编写的代码放在一起,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器兼容。 链接指示不能出现在类定义或函数定义内部,同样的链接指示必须在函数的每个声明中都出现。 当一个include只是被放置在复合链接指示的花括号中,头文件中所有普通函数声明都被认为有链接指示的语言编写的。