C++ common knowledge notes (2)

条款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;
}

你可能感兴趣的:(C++)