您的位置:首页 > 编程语言 > C语言/C++

重读C++ Primer,记录一些之前不易觉察的知识点(13. 拷贝控制)

2013-09-04 13:09 344 查看
1.复制构造函数

如果没有定义类的复制构造函数,编译器就会自动生成一个。即使定义了其他构造函数,虽然不会再生成默认构造函数,但是也会生成一个默认的复制构造函数(合成复制构造函数)。默认复制构造函数的行为是,逐个执行成员初始化,将新对象初始化为原对象的副本。

对许多类而言,默认复制构造函数只完成必要的工作。对于只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数。

然而,有些类必须对复制对象时发生的事情加以控制。这样的类通常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。还有一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义复制构造函数。

有些类需要完全禁止复制,例如,iostream 类就不允许复制。如果想要禁止复制,类必须显式声明其复制构造函数为 private。如果复制构造函数是私有的,将不允许用户代码复制该类类型的对象,编译器将拒绝任何进行复制的尝试。然而,类的友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个(private)复制构造函数但不对其定义。声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。通过声明(但不定义)private 复制构造函数,可以禁止任何复制类类型对象的尝试:用户代码中复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在链接时导致错误。

一般来说,最好显式或隐式定义默认构造函数和复制构造函数。只有不存在其他构造函数时才生成默认构造函数。如果定义了复制构造函数,也必须定义默认构造函数。

不定义复制构造函数和/或默认构造函数,会严重局限类的使用。不允许复制的类对象只能作为引用传递给函数或从函数返回,它们也不能用作容器的元素。

复制构造函数可用于初始化顺序容器中的元素。例如,可以用表示容量的单个形参来初始化容器。容器的这种构造方式使用默认构造函数和复制构造函数:

// default string constructor and five string copy constructors invoked
vector<string> svec(5);


编译器首先使用 string 默认构造函数创建一个临时值来初始化 svec,然后使用复制构造函数将临时值复制到 svec 的每个元素。除非你想使用容器元素的默认初始值,否则,更有效的办法是,分配一个空容器并将已知元素的值加入容器。

如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。然而,如果使用常规的花括号括的数组初始化列表来提供显式元素初始化式,则使用复制构造函数来初始化每个元素。根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素:

Sales_item primer_eds[] = { string("0-201-16487-6"),
string("0-201-54848-8"),
string("0-201-82470-1"),
Sales_item()
};


2.赋值操作符

默认生成的赋值操作符根据成员类型使用适合的内置或类定义的赋值操作符,依次给每个成员赋值,该操作符返回 *this,它是对左操作数对象的引用。

Sales_item& Sales_item::operator=(const Sales_item &rhs)
{
......
return *this;
}


3.析构函数

析构函数并不仅限于用来释放资源。一般而言,析构函数可以执行任意操作,该操作是类设计者希望在该类对象的使用完毕之后执行的。

与复制构造函数或赋值操作符不同的是,编译器总是会为我们生成一个默认的析构函数。默认析构函数按对象创建的逆序撤销每个非 static 成员,因此,它按成员在类中声明次序的逆序撤销成员。对于类类型的每个成员,合成析构函数调用该成员的析构函数来撤销对象。

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

如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: