4.2 构造函数和析构函数在OOP中,凡是实用程序创建的对象都需要作某种形式的初始化。为此,在C++的类说明中,引进了构造函数(constructor function),供创建类的实例时调用,并自动完成对象的初始化。析构函数(destructor function)则用于释放对象定义时通过构造函数向系统所申请的存储空间以及有关的系统资源。它是在对象离开其有效范围时自动调用。
[例4.1a] 使用构造函数改写例4.1。EX4-1a.CPP
4.2.1 构造函数的特性构造函数具有以下特性:
(1)、构造函数的名称与它所属的类名相同,且是无返回值类型(任何返回类型,包括void,都是非法的)。
(2)、一个类可以有一个以上的构造函数,重载构造函数参数个数或类型不一样。如果编程时在类中没有显式定义构造函数,慢编译器会为类自动生成一个缺省的构造函数(不带任何参数)。
(3)、构造函数是在以类去定义所属实例时,由编译器自动调用的。在C++中,执行某对象的说明时,调用了构造函数,创建了该对象。它不同于一般的变量说明语句。
(4)、构造函数与一般成员函数性质相同,同样要受到访问限制。一个定义于非public区的构造函数,则说明该类为私有类。
[例4.2] 私有类实例。EX4_2.CPP
(5)、控制成员变量内存分配,为定义对象向系统申请内存。
(6)、构造函数不能用常规的调用方法,不可取它们的地址,不能被继承。
4.2.2 构造函数的设计和使用首先讨论构造函数设计面临的问题是什么?解决问题的思路和原则是什么?
(1)、类中私有成员一般不作初始值设置,需要在定义对象时正确初始化,以确保对象的有效性。
(2)、采取显式调用成员函数进行对象初始化容易遗漏,造成对象无意义。因此使用构造函数进行对象初始化为最佳选择。
(3)一个类的不同对象需要有不同的初始化,因此采取两种方式解决此问题:参数化的构造函数和多构造函数。
下面分别进行具体的介绍。
1、带参数的构造函数
[例4.3] 具有带参数的构造函数的类的说明及使用。EX4_3.CPP
一般情况下,构造函数往往使用缺省值,因为构造函数定义了缺省值,在函数调用时,若无特别指定参数,便可以缺省值作初始化。而且也可以防止遗漏初始赋值。
缺省参数的构造函数用法与一般函数用法相同。
[例4.3a] 将上例函数改为带缺省参数的构造函数如EX4_3a.CPP
注意:编译器一般是严格按函数定义时的顺序来确定实参的位置。如果参数调用时的实参个数不足,编译器将优先匹配前面的形参,而不管它们是否有缺省值(参见函数一节)。
此外,当构造函数只有一个函数时,可采用以下形式来定义对象:
class_name obj_name=value;
2、多构造函数
对于一个类的不同对象,当其需要不同类型和不同个数的初始化参数时,可以在一个类中具有几个构造函数,以适应不同的情况。
[例4.3b] 多构造函数的示例EX4_3b.CPP。
注意避免错误:所定义的多个构造函数之前,在参数个数或类型上必须有差异;在定义实例对象,调用多构造函数时,若使用缺省参数,特别要防止二义性。
2,复制构造函数(copy constructor)
复制构造函数是一种特殊的构造函数:
(1)、具有一般构造函数的功能,创建新对象时系统自动调用它。
(2)、具有将参数传输对象按位拷贝的特殊功能。例如,当函数返回对象时,当用一个对象初始化另一个对象时,都调用该复制构造函数。
(3)编译系统具有为类产生一个缺省拷贝构造函数的功能。例如:
在main()中的语句
pointer ob1(30,40);
pointer ob2(ob1);
这里定义ob2时调用系统缺省拷贝构造函数,实现了对象拷贝(属于按位拷贝),ob1与ob2完全相等,但是两个实体对象。
(4)、用户可以自定义拷贝构造函数,定义的格式如下:
class_name(const class_name &obj){…}
其中,obj是一个对象引用,可用于初始化其他对象。如果有其他参数则在此后列出,如果缺省,则这个引用对象不能省。Const是理论上的限定词,也可以不是const型。
[例4.3c] 加入拷贝构造函数实例EX4_3C.CPP。
注意:如果类中包含有指针成员,那么就要慎用缺省的拷贝构造函数。因为拷贝会使两个对象的指针指向同一片内存区域,而非复制指针指向的内容。当释放时,有可能导致严重的后果(见[例4.10])。
4.2.3 析构函数的特性和用法
(1)、析构函数也是成员函数,与构造函数相对应,命名是在构造函数名前加~(波浪线)。
(2)、析构函数只能是无返回值型。
(3)、析构函数不能有任何参数。
(4)、析构函数不能重载,一个只允许有一个析构函数。
当对象离开其有效范围,或被取消时,析构函数都将起作用。即释放对象所占用的内存。析构函数的定义,一般是由一系列的delete组成。
[例4.4] 析构函数使用实例EX4_4.CPP
[例4.1a] 使用构造函数改写例4.1。EX4-1a.CPP
4.2.1 构造函数的特性构造函数具有以下特性:
(1)、构造函数的名称与它所属的类名相同,且是无返回值类型(任何返回类型,包括void,都是非法的)。
(2)、一个类可以有一个以上的构造函数,重载构造函数参数个数或类型不一样。如果编程时在类中没有显式定义构造函数,慢编译器会为类自动生成一个缺省的构造函数(不带任何参数)。
(3)、构造函数是在以类去定义所属实例时,由编译器自动调用的。在C++中,执行某对象的说明时,调用了构造函数,创建了该对象。它不同于一般的变量说明语句。
(4)、构造函数与一般成员函数性质相同,同样要受到访问限制。一个定义于非public区的构造函数,则说明该类为私有类。
[例4.2] 私有类实例。EX4_2.CPP
(5)、控制成员变量内存分配,为定义对象向系统申请内存。
(6)、构造函数不能用常规的调用方法,不可取它们的地址,不能被继承。
4.2.2 构造函数的设计和使用首先讨论构造函数设计面临的问题是什么?解决问题的思路和原则是什么?
(1)、类中私有成员一般不作初始值设置,需要在定义对象时正确初始化,以确保对象的有效性。
(2)、采取显式调用成员函数进行对象初始化容易遗漏,造成对象无意义。因此使用构造函数进行对象初始化为最佳选择。
(3)一个类的不同对象需要有不同的初始化,因此采取两种方式解决此问题:参数化的构造函数和多构造函数。
下面分别进行具体的介绍。
1、带参数的构造函数
[例4.3] 具有带参数的构造函数的类的说明及使用。EX4_3.CPP
一般情况下,构造函数往往使用缺省值,因为构造函数定义了缺省值,在函数调用时,若无特别指定参数,便可以缺省值作初始化。而且也可以防止遗漏初始赋值。
缺省参数的构造函数用法与一般函数用法相同。
[例4.3a] 将上例函数改为带缺省参数的构造函数如EX4_3a.CPP
注意:编译器一般是严格按函数定义时的顺序来确定实参的位置。如果参数调用时的实参个数不足,编译器将优先匹配前面的形参,而不管它们是否有缺省值(参见函数一节)。
此外,当构造函数只有一个函数时,可采用以下形式来定义对象:
class_name obj_name=value;
2、多构造函数
对于一个类的不同对象,当其需要不同类型和不同个数的初始化参数时,可以在一个类中具有几个构造函数,以适应不同的情况。
[例4.3b] 多构造函数的示例EX4_3b.CPP。
注意避免错误:所定义的多个构造函数之前,在参数个数或类型上必须有差异;在定义实例对象,调用多构造函数时,若使用缺省参数,特别要防止二义性。
2,复制构造函数(copy constructor)
复制构造函数是一种特殊的构造函数:
(1)、具有一般构造函数的功能,创建新对象时系统自动调用它。
(2)、具有将参数传输对象按位拷贝的特殊功能。例如,当函数返回对象时,当用一个对象初始化另一个对象时,都调用该复制构造函数。
(3)编译系统具有为类产生一个缺省拷贝构造函数的功能。例如:
在main()中的语句
pointer ob1(30,40);
pointer ob2(ob1);
这里定义ob2时调用系统缺省拷贝构造函数,实现了对象拷贝(属于按位拷贝),ob1与ob2完全相等,但是两个实体对象。
(4)、用户可以自定义拷贝构造函数,定义的格式如下:
class_name(const class_name &obj){…}
其中,obj是一个对象引用,可用于初始化其他对象。如果有其他参数则在此后列出,如果缺省,则这个引用对象不能省。Const是理论上的限定词,也可以不是const型。
[例4.3c] 加入拷贝构造函数实例EX4_3C.CPP。
注意:如果类中包含有指针成员,那么就要慎用缺省的拷贝构造函数。因为拷贝会使两个对象的指针指向同一片内存区域,而非复制指针指向的内容。当释放时,有可能导致严重的后果(见[例4.10])。
4.2.3 析构函数的特性和用法
(1)、析构函数也是成员函数,与构造函数相对应,命名是在构造函数名前加~(波浪线)。
(2)、析构函数只能是无返回值型。
(3)、析构函数不能有任何参数。
(4)、析构函数不能重载,一个只允许有一个析构函数。
当对象离开其有效范围,或被取消时,析构函数都将起作用。即释放对象所占用的内存。析构函数的定义,一般是由一系列的delete组成。
[例4.4] 析构函数使用实例EX4_4.CPP