c++ primer 笔记[20190406]

130 阅读4分钟

P406 复制控制

不管类是否定义了自己的析构函数,编译器都自动执行类中非static数据成员的析构函数。

复制构造函数

C++支持两种初始化形式:直接初始化和复制初始化。
复制初始化使用=符号,直接初始化将初始化式放在圆括号中。

当初始化用于类类型时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定的构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。

以非引用类型作返回值时,将返回return语句中的值的副本。
当形参或返回值为类类型时,由复制构造函数进行复制。

合成的复制构造函数的行为是:执行逐个成员初始化(即编译器将现有对象的每个非static成员,依次复制到正创建的对象)。合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。

禁止复制 P410

有些类需要完全禁止复制,比如iostream类。如果想要禁止复制,似乎可以省略复制构造函数,然而,如果不定义复制构造函数,编译器将合成一个。
为了防止复制,类必须显式声明其复制构造函数为private。但这样,类的友元和成员仍可以进行复制。如果想连友元和成员中的复制也禁止,就可以声明一个private复制构造函数但不对其定义。声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。

析构函数 P413

构造函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。

撤销一个容器(不过是标准库容器还是数组)时,也会运行容器中的类类型元素的析构函数。

{
    Sales_item *p = new Sales_item[10];
    vector<Sales_item> vec(p, p+10);
    delete [] p;  // array is freed, destructor run on each element
} // vector goes out of scope; destructor run on each element

容器中的元素总是按逆序撤销:首先撤销下标为size()-1的元素,最后是下标为0的元素。

合成析构函数按照对象创建时的逆序撤销每个非static成员,因此,它按照成员在类中声明次序的逆序撤销成员。对于类类型的每个成员,合成析构函数调用该成员的析构函数来撤销对象。

重载赋值操作符 operator=

将运算符重载函数作为类的成员函数时,二元运算符的参数只有一个,一元运算符不需要参数;之所以少一个参数,是因为这个参数是隐含的*this;如:

// 假设 complex 类中重载了加法运算符:
complex& operator+(const complex & A);

// c1 c2 c3都是类对象
c3 = c1 + c2;

// 会被转换为:
c3 = c1.operator+(c2); // 通过 this 指针隐式的访问 c1 的成员变量。

将运算符重载函数作为全局函数时,二元操作符就需要两个参数,一元操作符需要一个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符,防止程序员修改用于内置类型的运算符的性质; 如果有两个参数,这两个参数可以都是对象,也可以一个是对象,一个是C++内置类型的数据;如:

// 例如,下面这样是不对的:
int operator + (int a,int b){
    return (a-b);
}
// +号原来是对两个数相加,现在企图通过重载使它的作用改为两个数相减, 如果允许这样重载的话,
// 那么表达式4+3的结果是 7 还是 1 呢?显然,这是绝对禁止的。

// 如果有两个参数,这两个参数可以都是对象,也可以一个是对象,一个是C ++内置类型的数据,例如:
complex operator+(int a, complex &c)
{
    return complex(a+c.real, c.imag);
}
// 它的作用是使一个整数和一个复数相加。