您的位置:首页 > 其它

第十二章-----第十三章 构造函数 复制构造函数 赋值操作符析构函数 static类成员

2012-05-11 17:30 141 查看
构造函数

1.构造函数可以包含一个构造函数初始化列表:以一个冒号(:)开始,接着是一个亿逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。构造函数初始化式只在构造函数的定义中而不是在声明中指定。

2.从概念上讲,可以认为构造函数分两个阶段执行:a 初始化阶段;b 普通的计算阶段。计算阶段由构造函数体中的所有语句组成。不管成员是否在构造函数初始化列表中显示初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前。

3.有些成员必须在构造函数初始化列表中进行初始化。对于这样的成员,在构造函数体中对它们赋值不起作用。没有默认构造函数的类类型的成员,以及const或引用里类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中。

4.成员被初始化的次序就是定义成员的次序。第一个成员首先被初始化,然后是第二个,依此类推。初始化的次序常常无关紧要。然而,如果一个成员是根据其他成员而初始化,则成员初始化的次序是至关重要的。

5.初始化类类型的成员是,要指定实参并传递给成员类型的一个构造函数。可以使用该类型的任意构造函数。

6.只要定义了一个对象时没有提供初始化时,就使用默认构造函数。为所有形参提供默认实参的构造函数也定义了默认构造函数。

7.只要当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。

合成的默认构造函数 使用与变量初始化相同的规则来初始化成员。具有类类型的成员通过运行各自的默认构造函数来进行初始化。内置和复合类型的成员,如指针和数组,只对定义在全局作用域中的对象才初始化。当对象定义在局部作用域中时,内置或复合类型的成员不进行初始化。

每个构造函数应该为每个内置或复合类型的成员提供初始化时。没有初始化内置或复合类型成员的构造函数,将使那些成员处于未定义的状态。除了作为赋值的目标之外,以任何方式使用的一个未定义的成员都是错误的。

8.类通常应定义一个默认构造函数。实际上,如果定义了其他构造函数,则提供一个默认构造函数几乎总是对的。通常,在默认构造函数中给成员提供的初始值应该指出该对象是“空”的。

9.仰制由构造函数定义的隐式转换。可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数。

复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。

复制构造函数

1.只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为复制构造函数。复制构造函数可用于:

根据另一个同类型的对象显示或隐式初始化一个对象。

复制一个对象,将它作为实参传送给一个函数。

从函数返回时复制一个对象。

初始化顺序容器中的元素。

根据元素初始化式列表初始化数组元素。

2.如果我们没有定义复制构造函数,编译器就会为我们合成一个。与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。所谓“逐个成员”,指的是编译器将现有对象的每个非static成员,依次复制到正创建的对象。数组成员的复制是个例外,虽然一般不能复制数组,但如果一个类具有数组成员,则合成复制构造函数将复制数组。复制数组时合成复制构造函数将复制数组的每一个元素。
3.因为用于向函数传递对象和从函数返回对象,该构造函数一般不应设置为explicit。
对许多类而言,合成复制构造函数只完成必要的工作,只包含类类型成员的内置(但不是指针类型)成员的类,无须显示地定义复制构造函数,也可以复制。
然而,有些类必须显示定义复制构造函数。这样的类经常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义复制构造函数。
4.禁止复制
为了防止复制,类必须显示声明其复制构造函数为private。如果复制构造函数是私有的,将不允许用户代码复制该类类型的对象,编译器将拒绝任何进行复制的尝试。然而,类的友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个private复制构造函数但不对其定义。

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

赋值操作符
1.当操作符成为成员函数是,它的第一个操作数隐式绑定到this指针。
合成赋值操作符与合成复制构造函数的操作类似,它会执行逐个成员赋值:右操作数对象的每个成员赋值给左操作数的对应成员。对于数组,给每个数组元素赋值。该操作符返回*this,它是对左操作数对象的引用。
2.一般而言,如果类需要复制构函数,它也会需要赋值操作符。

析构函数
1.动态分配的对象只有在指向该对象的指针被删除时才撤销。如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就一直存在,从而导致内存泄漏,而且,对象内部使用的任何资源也不会释放。
当对象的引用或指针超出作业域是,不会运行析构函数。只有删除指向动态分配对象的指针或实际对象(不是对象的引用)超出作业域是,才会运行析构函数。
Sales_item *p=new Sales_item; //p points to default constructed object
{ //new scope
Sales_item item(*p); //copy constructor copies *p into item
delete p; //destructor called on object pointed to by p
} //exit local scope; destructor called on item
2.如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。

3.合成析构函数按对象创建时得逆序撤销每个非static成员,因此,它按成员在类中声明次序的逆序撤销成员。

分配了资源的类一般需要定义析构函数以释放那些资源。

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

12.6 static类成员

1.非static数据成员存在于类类型的每个对象中。不想普通的数据成员,static数据成员独立于该类的任何对象而存在;每个static数据成员与类关联的对象,并不与该类的相关联。static成员函数没有this形参,它可以直接访问所属类的static成员,但不能直接使用非static成员。

2.使用类的static成员的优点:

static成员的名字是在类的作业域中,因此可以避免与其他类的成员或全局对象名字冲突。

可以实施封装。static成员可以是私有成员,而全局对象不可以。

通过阅读程序容易看出static成员是与特定类关联的。这种可见性可清晰地显示程序员的意图。

3.可以通过作用域操作符从类直接调用static成员,或者通过对象、引用或指向该类类型对象的指针间接调用。

4.static成员函数:

a. 当在类的外部定义static成员时,无须重复指定static保留字,该保留字只出现定义体内部的声明处

b. static成员是类的组成部分但不是任何对象的组成部分,因此,static成员函数没有this指针。因为static成员不是任何对象的组成部分,所以static成员函数不能被声明为const。

5.static数据成员

a.static数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义是进行初始化。

像使用任意的类成员一样,在类定义体外部引用类的static成员时,必须指定成员是在哪个类中定义的。然而,static关键字只能用于类定义体内部的声明中,定义不能标示为static。

b. 一般而言,类的static成员,像普通数据成员一样,不能在类的定义体中初始化。相反,static数据成员通常在定义时才初始化。这个规则的一个例外是,只要初始化式是一个常量表达式(例如const static int number=10;)它可以用在任何需要常量表达式的地方。

const static数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义。

c.因为static数据成员不是任何对象的组成部分,所以它们的使用方式对于非static数据成员而言是不合法的。例如:static数据成员的类型可以是该成员所属的类类型。非static成员被限定声明为其自身类对象的指针或引用:

class Bar{

public:

//...

private:

static Bar mem1; //OK
Bar *mem2; //OK
Bar mem3; //error

};

类似地,static数据成员可以用作默认实参:

class Screen{

public:

//bkground refers to the static member

//declared later in the class definition

Screen& clear(char =bkground);

private:

static const char bkground='#';

};

非static数据成员不能用作默认实参,因为它的值不能独立于所属的对象而使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐