条款10 常量成员函数的含义
在类的非常量成员函数中,this指针是T* const类型,而在类的常量成员函数中,this指针是const T* const类型。在常量函数中,类的指针成员变量指向的内容可以修改,但这样做有些不道德。如果需要在常量成员函数中修改某个成员变量,则把该变量声明为mutable,而不是使用const_cast来去掉this指针const属性。
条款11 编译器会在类中放东西
一个类声明了一个或者多个虚函数,则编译器会为该类的每个对象添加一个指向虚函数表的指针,但是不同平台虚函数表指针的位置是不同的。如果使用虚拟继承,即使没有虚拟函数,虚函数表指针依然会被添加。永远不要使用memcpy来复制一个对象,因为这样复制的只是存储区,要使用类的初始化或者赋值操作。
不要对类的内部结构做出假设。
条款12 赋值和初始化并不相同
初始化和赋值是有不同用处不同实现的不同操作。赋值发生于赋值操作时,其他都为初始化,包括声明,函数返回,参数传递,捕获异常。对于复杂的用户自定义类型来说,目标在采用源初始化之前一定要被清理掉。由于正当的赋值操作会有清理操作,所以永远不要对未被初始化的区域进行用户自定义的赋值操作,因为在赋值操作中会有清理操作。
条款13 复制操作
复制构造操作和复制赋值操作是不同的。通常复制构造函数被声明为T(const& T),而复制赋值函数常被声明为T& operator= (const& T)。尽量要使复制构造操作和复制赋值操作的结果没有区别。
在复制赋值操作中要检查自我赋值这种情况。
条款14 函数指针
函数指针的声明方法为 void (*fp)(int n)。*fp周围的括号不能少,如果少了的话,fp就是一个返回值为void的函数。将一个函数的地址初始化或者赋值给一个函数指针的时候,不需要显式的取得函数的地址,编译器会隐式的取得函数的地址。用函数指针来调用函数的时候,也不需要解引用,编译器会自动解引用。
和void*不同,不存在指向任何函数的指针。非静态成员函数的地址不是指针,因此不能用函数指向非静态成员函数。函数指针可以指向内联函数,但是并不会发生内联调用。
条款15 指向类成员的指针并不是指针
在使用指向类成员指针的时候要使用classname::*而不是*,其他用法都和普通指针一样。指向类成员的指针指向的并不是内存的一个地址,而是指向特定类的特定成员,通常编译器把它设置为偏移量。在C++中存在从派生类指针到基类指针的转换,但是在指向成员的指针正好相反:存在从指向基类成员指针向指向派生类成员指针的转换,但是不存在从指向派生类成员指针向指向基类成员指针的转换。
条款16 指向成员函数的指针并非指针
与指向类成员的指针一样,使用指向成员函数的指针时要使用classname::*。在解引用时,要用括号把指向成员函数的指针括起来,因为classname::*的优先级比()低(函数参数周围的括号)。当指向虚拟函数的时候,“虚拟性”指的是函数的虚拟性,而不是指针的虚拟性。在类型转换方面,和指向类成员的指针一样,存在从指向基类成员函数指针向指向派生类成员函数指针的转换。
条款17 处理函数和数组声明
函数和数组修饰符优先级比指针修饰符高,当想要声明一个变量为指针的时候,并且后面紧跟的是函数或者数组修饰符,则应该把该变量和指针修饰符用括号括起来。函数的参数和返回值对函数指针影响很大,参数和返回值类型不同,函数指针类型也不同。
能够明白下面的代码,函数指针也就明白了。
#include "stdio.h"
void (*set_new_handler(void (* new_handler)()))()
{
printf("set new handler\n");
return new_handler;
}
void foo()
{
printf("foo\n");
}
int main()
{
void (* foo_t)();
foo_t = set_new_handler(foo);
foo_t();
return 0;
}