码迷,mamicode.com
首页 > 其他好文 > 详细

复制控制

时间:2015-08-04 17:22:44      阅读:96      评论:0      收藏:0      [点我收藏+]

标签:

当定义一个新类型的时候,需要显式或隐式地指定复制、赋值和撤销该类型的对象时会发生什么——这是通过定义特殊成员:复制构造函数、赋值操作符和析构函数来达到的。如果没有显式定义复制构造函数或赋值操作符,编译器(通常)会为我们定义。

    复制构造函数是一种特殊构造函数, 具有单个形参, 该形参 (常用 const & 修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。当将该类型的对象传递给函数形参或函数返回该类型的对象时,将隐式使用复制构造函数。

    析构函数是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。析构函数可用于释放对象时构造或在对象的生命期中所获取的资源。不管类是否定义了自己的析构函数,编译器都自动执行类中static 数据成员的析构。

复制构造函数、赋值操作符和析构函数总称为 复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。 有一种特别常见的情况需要类定义自己的复制控制成员的:类具有指针成员。(三法则??)

?  复制构造函数

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

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

stringnull_book = "9-999-99999-9"; // copy-initialization

stringdots(10, ‘.‘); // direct-initialization

stringempty_copy = string(); // copy-initialization

stringempty_direct; // direct-initialization

ps:合成的复制构造函数,只执行逐个成员初始化,即将新对象初始化为原对象的副本。有些类必须对复制对象时发生的事情加以控制。这样的类经常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义复制构造函数。

pps:有些类要禁止复制。如iostream类就不允许复制。为了防止复制,类必须显式声明其复制构造函数为private。

 

?  赋值操作符

与复制构造函数一样,如果类没有定义自己的赋值操作符, 则编译器会合成一个。

合成赋值操作符与合成复制构造函数的操作类似。它会执行逐个成员赋值:右操作数对象的每个成员赋值给左操作数对象的对应成员。除数组之外,每个成员用所属类型的常规方式进行赋值。对于数组,给每个数组元素赋值。(正常数组是不能赋值的!!)

自定义:

Sales_item& Sales_item::operator=(const Sales_item &rhs)

{

isbn = rhs.isbn; // calls string::operator=

units_sold = rhs.units_sold; // uses built-in int assignment

revenue = rhs.revenue; // uses built-in double assignment

return *this;

}

 

ps:复制和赋值常一起使用,实际上,就将这两个操作符看作一个单元。如果需要其中一个,

我们几乎也肯定需要另一个。复制构造函数相当于初始化,而赋值是已经初始化好了,区别如下:

Sales_item s1,s2=s1;  //复制构造函数

 

Sales_item s1,s2;

s2=s1;            //赋值

 

 

?  析构函数

何时调用析构函数,撤销类对象时会自动调用析构函数:

Sales_item*p = new Sales_item;

{

Sales_item item(*p); // copy constructor copies *p into item

delete p; // destructor called on object pointed to

}

变量(如 item)在超出作用域时应该自动撤销。因此,当遇到右花括号时,将运行 item 的析构函数。动态分配的对象只有在指向该对象的指针被删除时才撤销。如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就一直存在,从而导致内存泄漏,而且,对象内部使用的任何资源也不会释放。

 

何时编写显式析构函数

仅在有些工作需要析构函数完成时,才需要析构函数。析构函数通常用于释放在构造函数或在对象生命期内获取的资源。

 

三法则:如果类需要析构函数, 则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。

 

合成析构函数

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

 

 

如何编写析构函数

析构函数是个成员函数,它的名字是在类名字之前加上一个代字号(~),它没有返回值,没有形参。因为不能指定任何形参,所以不能重载析构函数。虽然可以为一个类定义多个构

造函数,但只能提供一个析构函数,应用于类的所有对象。析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。

如果类定义了析构函数,则在类定义的析构函数结束之后运行合成析构函数。

如: ~Sales_item(){}

 

******定义复制控制函数最为困难的部分通常在于认识到它们的必要性。******

分配内存或其他资源的类几乎总是需要定义复制控制成员来管理所分配的资源。如果一个类需要析构函数,则它几乎也总是需要定义复制构造函数和赋值操作符。

版权声明:本文为博主原创文章,未经博主允许不得转载。

复制控制

标签:

原文地址:http://blog.csdn.net/qhairen/article/details/47277987

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!