构造函数和析构函数是类的两个特殊的成员函数
1.构造函数
构造函数(constructor)是类的一个特殊的成员函数,它与类名同名。当定义该类的对象时,构造函数将被系统自动调用用以实现对该对象的初始化。
构造函数不能有返回值,因而不能指定包括void在内的任何返回值类型。
构造函数的定义与其他成员函数的定义一样可以放在类内或类外。
构造函数的定义格式为:
类名(形参说明)
{函数体}
构造函数既可以定义成有参函数,也可以定义成无参函数,要根据问题的需要来定。
注意:程序中不能直接调用构造函数,构造函数是在创建对象时由系统直接调用的,因此,在构造函数中一般完成初始化类成员变量的操作。
2.构造函数的重载
一个类中出现了两个以上的同名的成员函数时,称为类的成员函数的重载。
在类的成员函数的重载中,比较常见形式是构造函数的重载,当类中出现了重载构造函数时,C++语言将根据构造函数中的参数个数和类型选择合适的构造函数来完成对象的构造。
3.默认构造函数与缺省参数的构造函数
如果在类中没有显示定义构造函数,则编译系统会为该类提供一个默认的构造函数,该默认构造函数是一个无参函数,函数体为空,它仅仅负责创建对象,而不做任何初始化工作(即不给相应的数据成员赋初值),所以在该类的对象创建时不能保证有一个确定的初始状态。
良好的编程习惯应该是给类提供合适的完成初始化工作的构造函数。
但是,只要一个类定义了一个构造函数(不一定是无参构造函数),编译系统就不再提供默认的构造函数。
当构造函数具有缺省参数时,称为具有缺省参数的构造函数,在使用具有缺省参数的构造函数时,要防止二义性。
4.拷贝构造函数
拷贝构造函数是一种特殊的构造函数。定义拷贝构造函数的一般格式为:
类名::类名(const 类名 &形式参数)
{ 函数体 }
拷贝构造函数的函数名与类名同名。该函数也没有返回值。
拷贝构造函数的功能是通过将一个同类对象的值拷贝给一个新对象,来完成对新对象的初始化,即用一个对象去构造另外一个对象。
如果在类的定义中没有定义拷贝构造函数,则编译系统将自动生成一个具有上述形式的默认的拷贝构造函数,作为该类的公有成员。
5.析构函数
与构造函数对应的是析构函数。当一个对象被定义时,系统会自动调用构造函数为该对象分配相应的资源,当对象使用完毕后且在对象消失前,系统会自动调用类的析构函数来释放这些系统资源。
析构函数也是类的一个特殊的成员函数,其函数名称是在类名的前面加上“~”;它没有返回值,也没有参数。一个类中只能拥有一个析构函数,所以析构函数不能重载。
析构函数的定义方式为:
~类名()
{ 函数体 }
如果程序员在定义类时没有为类提供析构函数,则系统会自动创建一个默认的析构函数,其形式为:
~类名()
{ }
对象被析构的顺序与其创建时的顺序正好相反,即最后构造的对象最先被析构。
如果一个对象是被new运算符动态创建的,当使用delete运算符释放它时,delete将会自动调用析构函数。
6.一个类的对象作为另一个类的数据成员
当一个类中的数据成员是某一个类的对象时,可称这种成员是新建类的子对象或对象成员,则新类的定义格式可表示为:
calss X
{
类名1 成员名1;
类名2 成员名2;
类名3 成员名3;
……………
类名n 成员名n;
…。………… //其他成员
};
其中,X为新建类的类名,类名1、类名2、……、类名n必须是已定义过的类。如:
class A {};
class B
{
A a;
};
则在创建类B的对象(调用类B的构造函数)时,会自动调用类A的构造函数。如果类A的构造函数为有参函数时,通常采用初始化表的方式来调用构造函数。
新类的构造函数的一般定义格式为:
新类(参数表0):成员1(参数表1),成员2(参数表2),…,成员n(参数表n)
{ ………… }
其中,成员1、成员2、……、成员n是新类中的对象成员;参数表1提供初始化成员1所需的参数,参数表2提供初始化成员2所需的参数,依此类推,并且这几个参数表中的参数均来自参数表0.另外,初始化新类的非对象成员所需的参数,也由参数表0提供。
7.常对象与常对象成员
(1)常对象
常对象是指对象常量,其定义格式为:
const 类名 对象名;
从格式中可以看出,常对象的定义与一般对象的定义相比,在类名前必须加const关键字。
常对象具有以下特点:
l 常对象在定义时必须进行初始化,而且在程序中不能再对其进行更新。
l 通过常对象只能调用类中的常成员函数,而不能调用类中的其他成员函数。
(2)常对象成员
常对象成员分为常成员函数和常数据成员。
1)常成员函数
在类中,使用关键字const说明的成员函数成为常成员函数,常成员函数的说明格式为:
类型 函数名(形参表) const;
类中的常成员函数与普通成员函数相比,具有以下特点:
l 常成员函数为类的只读函数,这种成员函数可以读取数据成员的值,但不可以更新数据成员的值,它也不能调用该类中没有const修饰的其他成员函数。
l 常成员函数定义中的const关键字是函数类型的一部分,因此在其实现部分中也要带上const关键字。
l 常成员函数定义中的const关键字可以参与区分重载函数。
例如:
#include <iostream.h> class Test_const{ void Test_const::setvalue(int newvalue) void main() |
2)常数据成员
类中定义的数据成员,除了可以为一般变量外,还可以为const常量,这种数据成员称为常数据成员。
构造函数可以对对象的数据成员进行初始化,但如果数据成员为常量成员或引用成员时,则不能在构造函数中直接用赋值语句为其进行赋值。需要利用构造函数所附带的初始化表进行初始化,即在构造函数的括号后面加上“:”和初始化表,其格式为:
类名::类名(形参表):常数据成员名1(值1),常数据成员名2(值2),……
{
//构造函数的函数体
}
可以看出,当有多个数据成员时,初始化表中的初始化项有多个,且需要用逗号隔开。
8.类作用域
类作用域又可称为类域,它是指在类定义中用一对大括号开括起来的范围。
不同的类的成员函数可以具有相同的名字,因此,需要用作用域运算符“::”来指明该成员函数所属的类。
在类的成员函数中可以直接引用类的数据成员。但是,如果在成员函数中定义了同名的局部变量时,则必须用作用域运算符“::”来指定,以免混乱。例如:
#include <iostream.h> class Region{ private: int x; int y; public: Region(int x,int y) { Region::x=x; Region::y=y; } void print() { cout<<"x="<<x<<",y="<<y<<endl; } }; void main() { Region region(5,10); Region *p; p=®ion; region.print(); p->print(); } |