C/C++——构造函数和析构函数

来源:互联网 发布:淘宝手机单怎么刷步骤 编辑:程序博客网 时间:2024/04/30 12:53

本文对类的构造函数和析构函数进行总结,主要包括了构造函数的初始化、重载、使用参数和默认参数,拷贝构造函数和析构函数,希望能帮助读者在程序开发中更好的理解类,属C/C++基础。

 

1、 构造函数

构造函数与类名相同,它是一种特殊的成员函数,没有返回值类型和返回值。在对象建立时自动调用,不需要也不能由用户调用。构造函数主要为对象开辟内存空间,完成对象数据成员的初始化。

(1)、构造函数的初始化

构造函数的主要功能是给对象初始化,初始化有两种方式,一种是用参数初始化列表,这种方式简练;另一种是在构造函数的函数体内对数据成员进行赋值实现初始化,这种方式方便直观。

参数初始化列表初始化

参数初始化列表初始化的形式是在函数的首部的末尾加冒号,然后列出用逗号分隔的数据成员列表初始化表。如:

T::T(inta , int b) :hour(a),minute(b) {}

表示用a初始化hour,用b初始化minute,即 hour=a; minute=b;

虽然后面的花括号是空的,但已经给对象进行了初始化。

注意:

对于const或引用类型的成员必须在构造函数初始化列表中进行初始化,这是初始化它们的唯一机会,原因分析在赋值语句初始化段。

不能用参数初始化列表对静态数据成员初始化,而且只能在类外初始化,可以像普通数据成员那样通过对象访问,也可以通过类名访问。如:

int T::hour=0;   //这里在类中定义的hour是static变量

这是因为,对于static指定的静态成员不属于具体的对象,而是属于类的,它在对象之外开辟内存空间,不随对象的撤销而释放内存。如果可以在初始化列表里对它初始化,那么试想,每次建立一个对象,都对它初始化,那它还能叫static变量吗,它怎么保存在上次使用后的变量值?

如果读者对这两点不甚了解,请看我前面写的关于const和static的两篇文章。

赋值语句初始化

对象的初始化也可以省略初始化参数列表,而在函数体里对数据成员进行赋值,这个普通变量的赋值操作没有什么区别。虽然这和使用参数列表初始化达到了同样的效果,但初始化的过程却是不一样的。构造函数的执行分两个阶段,一是初始化阶段,二是函数体内的执行语句。所以在没有参数列表的情况下使用赋值进行初始化,同样会先进行初始化列表访问,只不过这个初始化列表什么都不做,然后在函数体内用赋值语句对数据成员赋值,所以两者效果一样,但这种方式,严格意义上讲,应该叫赋值操作,不叫初始化。所以这也理解了,为什么const或引用数据成员不能通过赋值方式初始化。

(2)、默认构造函数

当用户没有显式的去定义构造函数时,编译器才会为类生成一个默认的构造函数,称为“默认构造函数”。或者说无参的构造函数是默认构造函数。如果定义了构造函数,则编译器不再生成默认的构造函数。一个类只能有一个默认构造函数,默认构造函数的函数体是空的,不能完成对象数据成员的初始化,只能给对象创建标识符,并为对象中的数据成员开辟一定的内存空间对于这个内存中存的数据是随机的。虽然默认构造函数不对数据成员进行初始化,但在类设计中,如果定义了其他构造函数,再提供一个默认构造函数几乎总是对的。

(3)、带参数的构造函数

① 在定义不同对象时,有时希望对象有不同的初值,这时可以用带参数的构造函数,通过参数传递,把实参的值传递给形参,作为数据成员的初值,产生希望的初始对象。

② 对其进行进一步讨论,如果带参数的构造函数赋有默认参数,这个构造函数为带默认参数的构造函数。它既具有默认构造函数的特点有具有带参数的构造函数的优点。如:设计一个类T

class T

{

public:

       T(int,int);

       void disp();

private:

       int hour;

       int minute;

};

它的构造函数的实现是:

T::T(inta = 0, int b = 0):hour(a),minute(b)

{

}

则,定义下面三个对象T t(2);T t1;T t2(1, 2);都可以用这一个构造函数实现。

(4)、构造函数的重载

C++提供了函数的重载机制,重载函数具有相同的函数名,通过参数的个数和参数的类型加以区别。定义对象时,对象所带的实参决定使用哪一个构造函数。因此,在类中可以有多个构造函数,这样可以为对象提供丰富的初始化方式。构造函数重载和上面带参数的构造函数有一定的交集,所以重载的构造函数依然可以采用默认参数,这样进一步丰富了构造函数的初始化方式。对于带默认参数的构造函数,应该在声明构造函数的时候指定默认值,不能只在定义构造函数时指定默认值,因为函数定义是细节,一般看不到,而声明是接口,用户可以看到。

 

2、拷贝构造函数

复制构造函数也叫拷贝构造函数,是构造函数的一种,拷贝构造函数的参数个数可以是1个或者多个,但是第一个参数必须是本类对象的引用参数,这个参数的形式是“类名 &对象名”,因为参数传递的是同类的引用,所以一般参数加const约束,用来防止实参被改变。如:T::T(constT&); 其中T是一个类名。执行拷贝构造函数,将实参的各个数据成员赋给新对象的数据成员。构造函数的作用是使类的成员变量有合适的初值,调用是时机是在创建该类的对象的时候,由编译器自动调用。拷贝构造函数是指类的对象在创建的时候,能使用已有的对象初始化它。

关于拷贝构造函数的几点说明:

① 区分对象的赋值和对象的复制(拷贝):

赋值:同普通变量赋值操作一样,两个对象都存在,然后用一个赋值给另一个;

拷贝:首先只有一个对象,然后用它去产生另一个相同性质的对象,另一个对象是从无到有,而赋值是一开始在内存中就存在两个对象。

② 在C++中,下面三种对象需要调用拷贝构造函数

1、对象需要通过另外一个对象进行初始化;

2、函数参数为类的对象

3、函数返回值为类的对象

③ 这里推荐一篇文章,给读者解析了一下拷贝构造函数的浅拷贝和深拷贝:

http://blog.csdn.net/lwbeyond/article/details/6202256

 

3、析构函数

在类名前加波浪号,如:~Point() 形成析构函数。析构函数也是一个特殊的成员函数,没有返回值类型和返回值,也没有参数列表,所以不能被重载,即一个类只有一个析构函数。与构造函数相反,析构函数是在对象被撤销时被自动调用,用于对成员撤销时的一些清理工作和用户希望在最后一次使用对象后所执行的操作。如果用户没有定义析构函数,编译系统自动生成什么操作都不进行的默认的析构函数。

 

4、构造函数和析构函数的调用顺序

一般情况下,同一存储类别(生存周期和作用域相同)的对象,调用析构函数的顺序和其构造函数的顺序相反,即先构造的后析构,后构造的先析构。如函数A里定义对象a,函数B里定义对象b,在一个函数A里调用另一个函数B。这时,构造函数和析构函数调用顺序是:a构造→b构造→b析构→a析构。当在不同存储类别定义的对象,构造函数和析构函数的调用顺序就要看具体情况了。

(1)如果在全局范围定义对象,那么它的构造函数在本文件的所有函数(包括main函数)执行前调用,而在多个源文件的程序中,调用顺序不确定。当程序终止时,调用析构函数。函数体内的对象只在进入这个函数的时候进行构造。

(2)如果定义的是局部自动对象,但存储类别不同,则建立对象时调用构造函数,在函数执行结束调用析构函数。

(3)如果定义静态对象,在定义对象时调用构造函数,析构函数不随定义此对象的函数执行结束而调用,而是在程序执行结束时,调用析构函数。

至于具体的调用顺序,还是得看具体的对象定义方式。

 

 

参考文献:

http://www.cnblogs.com/mr-wid/archive/2013/02/19/2917911.html

http://blog.chinaunix.net/uid-20773165-id-1847729.html

 

 

 

1 0