利用构造函数对类对象进行初始化

9.1利用构造函数对类对象进行初始化
9.1.1对象的初始化
在程序中常常需要对变量赋初值,即对其初始化。
那么,怎样使他们得到初值呢?有人试图在声明类时对数据成员初始化。如
class Time
{
hour=0;
minute=0;
sec=0;
};
这是错误的。因为类并不是一个实体,而是一种抽象类型,并不占存储空间,显然无处容纳数据。
如果一个类中所有成员都是公用的,则可以在定义对象时对数据成员进行初始化。如:
class Time
{
pubic:
hour;
minute;
sec;
};
Time t1={14,56,30};
这种情况和结构体变量的初始化是类似的,在一个花括号内顺序列出各公用数据成员的值,两个值之间用逗号分隔。但是,如果数据成员


是私有的,或者类中有private或protected的数据成员,就不能用这种方法初始化。
9.1.2用构造函数实现数据成员的初始化
C++提供了构造函数(constructor)来处理对象的初始化。构造函数时一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它


,而是在建立对象时自动执行。构造函数是在声明类的时候由类的设计者定义的,程序用户只须在定义对象的同时指定数据成员的初值即


可。
构造函数的名字必须与类名同名,而不能任意命名,以便编译系统能识别它并把它作为构造函数处理。它不具有任何类型,不返回任何值



例9.1 在例8.3的基础上,用构造函数为对象的数据成员赋初值。




#include<iostream>
using namespace std;
class Time
{
public:
Time()  //定义构造成员函数,函数名和类名相同
{
hour=0;  //利用构造函数对对象中的数据成员赋初值
minute=0;
sec=0;
}
void set_time();  //公用成员函数
void show_time();
private:             //数据成员为私有的
int hour;
int minute;
int sec;
};
void Time::set_time()  //在类外定义set_time函数
{
cin>>hour;
cin>>minute;
cin>>sec;
}
void Time::show_time()
{
cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
int main()
{
Time t1;              //建立对象t1,同时调用构造函数t1.Time()
t1.set_time();    //对t1的数据成员赋值
t1.show_time();//显示t1的数据成员的值
Time t2;     //建立对象t2,同时调用构造函数t2.Time()
t2.set_time();        
t2.show_time();
return 0;
}




/*在类中定义了构造函数Time,它和所在的类同名。在建立对象时自动执行构造函数,根据构造函数Time的定义,其作用是对该对象中的数


据成员赋予初值0。
请不要误认为是在声明类时直接对程序数据成员赋初值(那是不允许的),赋值语句是写在构造函数的函数体中的,只有在调用构造函数


时才执行这些赋值语句,对当前对象中的数据成员赋值。
    程序在运行时首先建立对象t1,在执行构造函数过程中对t1中的数据成员赋予初值0。然后再执行主函数中的t1.set_time函数,从键盘输入


新值赋给对象t1的数据成员,再输出t1的数据成员的值。
   上面是在类内定义构造函数的,也可以只在类内对构造函数进行声明而在类外定义构造函数。将程序中第5~9行改为下面一行:
Time();//对构造函数进行声明
在类外定义构造函数:
Time::Time()                   //在类外定义构造函数,要加上类名Time和域限定符"::"。
{
hour=0;
minute=0;
sec=0;
}
有关构造函数的使用,有以下说明:
1.什么时候调用构造函数呢?在建立类对象时会自动调用构造函数。在建立对象时系统为该对象分配内存单元,此时执行构造函数,就把指


定的初值送到有关
数据成员的存储单元中。每建立一个对象,就调用一次构造函数。在上面的程序中,在主函数中定义了一个对象t1,在此时,就会自动调


用t1对象中的构造函数Time,使各数据成员的值为0。
2.构造函数没有返回值,因此也没有类型,它的作用只是对对象进行初始化。因此也不需要在定义构造函数时声明类型,这是它和一般函


数的一个重要的不同之点。不能写成;
int Time()
{...}

void ?time()
{...}
3.构造函数不需要用户调用,也不能被用户调用。下面用法是错误的:
t1.Time();    //试图用调用一般成员函数的方法调用构造函数
构造函数是在定义对象时由系统自动执行的,而且只能执行一次。构造函数一般声明为public。
4.可以用一个类对象初始化另一个类对象,如
Time t1;         //建立对象t1,同时调用构造函数t1.Time()
Time t2=t1;         //建立对象t2,并用一个t1初始化t2
此时,把对象t1的各数据成员的值复制到t2相应各成员,而不调用构造函数t2.Time()。
5.在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含其他语句,例如cout语句。但是一般不提倡在构造函数中加入与初始化


无关的内容,以保持程序的清晰。
6.如果用户自己没有定义构造函数,则C++系统会自动生成一个构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化


操作。
9.1.3 带参数的构造函数
有时用户希望对不同的对象赋予不同的初值,怎么办?
可以采用带参数的构造函数,在调用不同对象的构造函数时,从外面将不同的数据传递给构造函数,以实现不同的初始化。构造函数首部


的一般形式为:
构造函数名(类型1 形参1,类型2 形参2,...)
前面已说明:用户是不能调用构造函数的,因此无法采用常规的调用函数的方法给出实参(如fun(a,b);)。实参是在定义对象时给出的。定


义对象的一般形式为
类名 对象名(实参1,实参2,...);
在建立对象时把实参的值传递给构造函数相应的形参,把他们作为数据成员的初值。
例9.2 有两个长方柱,其高、宽、长分别为(1)12,20,25;(2)10,14,20。求他们的体积。编写一个基于对象的程序,在类中用带参数的


构造函数对数据成员初始化。
#include<iostream>
using namespace std;
class Box  //声明Box类
{
public:
Box(int,int,int);//声明带参数的构造函数
int volume();//声明计算体积的函数
private:
int height;
int width;
int length;
};
Box::Box(int h,int w,int len) //在类外定义带参数的构造函数
{
height=h;
width=w;
length=len;
}
int Box::volume()  //定义计算体积的函数
{
return(height*width*length);
}
int main()
{
Box box1(12,25,30);  //建立对象box1,并指定box1的高、宽、长的值
cout<<"The volume of box is"<<box1.volume()<<endl;
Box box2(15,30,21);
cout<<"The volume of box2 is"<<box2.volume()<<endl;
return 0;
}
构造函数Box有3个参数(h,w,l),分别代表高、宽、长。在主函数中定义对象box1时,同时给出函数的实参12,25,30。然后在cout语句中调用


函数box1.volume(),并输出box1的体积。对box2也类似。
注意:定义对象的语句形式是
Box box1(12,25,30);
可以知道;
1.带参数的构造函数中的形参,其对应的实参是在建立对象时给定的。即在建立对象时同时指定数据成员的初值。
2.定义不同对象时用的实参是不同的,他们反映不同对象的属性。用这种方法可以方便地实现对不同对象进行不同的初始化。


9.1.4  用参数初始化表对数据成员初始化
       在9.1.3节中介绍的是在构造函数的函数体内通过赋值语句对数据成员实现初始化。C++还提供另一种初始化数据成员的方法——参数初试化表来实现对数据成员的初始化。这种方法不在函数体内对数据成员初始化,而是在函数首部实现。如例9.2中定义构造函数可以改用以下形式:
Box::Box(int h,int w,int len):height(h),width(w),length(len){}
即在原来函数首部的末尾加一个冒号,然后列出参数的初始化表。上面的初始化表表示:用形参h的值初始化数据成员height,用形参w的


值初始化数据成员width,用形参len的值初始化数据成员length。后面的花括号是空的,即函数体是空的,没有任何执行语句。这种形式的


构造函数的作用和例9.2中在类外定义的Box构造函数相同。用参数的初始化表法可以减少函数体的长度,使结构函数显得精炼简单。这样


就可以直接在类体中(而不是在类外)定义构造函数。尤其当需要初始化的数据成员较多时更显其优越性。许多C++程序人员喜欢用这种


方法初始化所有数据成员。
带有参数初始化表的构造函数的一般形式如下:
类名::构造函数名([参数表])[:成员初始化表]
{
[构造函数体]
}
其中方括号内为可选项(可有可无)。
说明:如果数据成员是数组,则应当在构造函数的函数体中用语句对其赋值,而不能在参数初始化表中对其初始化。如:
class Student
{
public:
Student(int n,char  s,nam[]):num(n),sex(s)  //定义构造函数
{
strcpy(name,mam);
}
private:  //函数体
int num;
char sex;
char name[20];
};
可以这样定义对象stud1:
Student stud1(10101,'m',"Wang_li");
利用初始化表,把形参n得到的值10101赋给私有数据成员num,把形参s得到的值'm'赋给sex,把形参数组nam的各元素的值通过strcpy函数


复制到name数组中。这样对象stud1中所有的数据成员都初始化了,此对象是有确定内容的。
9.1.5 构造函数的重载
     在一个类中可以定义多个构造函数,以便为对象提供不同的初始化方法,供用户选用。这些构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。在第4.6节中所介绍的函数重载的知识也适用于构造函数。
通过下面的例子可以了解怎样应用构造函数的重载。
例9.3  在例9.2的基础上,定义两个构造函数,其中一个无参数,一个有参数。
#include<iostream>
using namespace std;
class Box
{
public:
Box();  //声明一个无参构造函数Box
Box(int h,int w,int len):height(h),width(w),length(len){}//定义一个有参的构造函数,用参数的初始化表对数据成员初始化
int volume();  //声明成员函数volume
private:
int height;
int width;
int length;
};
Box::Box()  //在类外定义无参构造函数Box
{
height=10;
width=10;
length=10;
}
int Box::volume() //在类外定义声明成员函数volume
{
return(height*width*length);
}
int main()
{
Box box1;//建立对象box1,不指定实参
cout<<"The volume of box1 is"<<box1.volume()<<endl;
Box box2(15,30,25);
cout<<"The volume of box2 is"<<box2.volume()<<endl;
return 0;
}


      在类中声明了一个无参数构造函数Box(),在类外定义的函数体中对私有数据成员赋值。第2个构造函数是直接在类体中定义的,用参数初始化表对数据成员初始化,函数有3个参数,需要3个实参与之对应。这两个构造函数同名(都是Box),那么系统怎么辨别调用的是哪一个构造函数呢?编译系统是根据函数调用的形式去确定对应哪一个构造函数。
      在主函数中,建立对象box1时没有给出参数,系统找到与之对应的无参构造函数Box,执行此构造函数的结果是使3个数据成员的值均为10。然后输出box1的体积。建立对象box2时给出3个实参,系统找到有3个形参的构造函数Box与之对应,执行此构造函数的结果是使3个数据成员的值为15,30,25。然后输出box2的体积。
      在本程序中定义了两个同名的构造函数,其实还可以定义更多的重载构造函数。例如还可以有以下的构造函数原型:
Box::Box(int h);             //有一个参数的构造函数
Box::Box(int h,int w);       //有两个参数的构造函数
在建立对象时可以给出一个参数和两个参数,系统会分别调用相应的构造函数。
说明:
1.  在建立对象时不必给出实参的构造函数,称为默认构造函数(default constructor)。显然,无参构造函数属于默认构造函数。一个类只能有一个默认构造函数。如果用户未定义构造函数,则系统会自动提供一个默认构造函数,但它的函数体是空的,不起初始化作用。如果用户希a员有初值,就必须自己定义构造函数。
2.如果在建立对象时选用的是无参构造函数,应注意正确书写定义对象的语句。如本程序中有以下定义对象的语句:
Box box1;//建立对象的正确形式
注意不要写成Box box1();//建立对象的错误形式,不应该有括号
上面的语句并不是定义Box类的对象box1,而是声明一个普通函数box1,此函数的返回值为Box类型。在程序中不应出现调用无参构造函数


(如Box()),请记住:构造函数时不能被用户显式调用的。
3.尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数,并非每个构造函数都被执行



9.1.6 使用默认参数的构造函数
构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值。


/*例9.4 将例9.3程序中的构造函数改用含默认值的参数,宽、高、长的默认值均为10。
*/
#include<iostream>
using namespace std;
class Box
{
public:
Box(int h=10,int w=10,int len=10);  //在声明构造函数时指定默认参数,形参名可以省略,即写成Box(int =10;int =10;int =10);
int volume();  //声明成员函数volume
private:
int height;
int width;
int length;
};
Box::Box(int h,int w,int len)//在定义函数时可以不指定默认参数
{
height=h;
width=w;
length=len;
}
/*对构造函数的定义(第12~16行)也可以改写成参数初始化表的形式:
Box::Box(int h,int w,int len):height(h),width(w),length(len){}
*/
int Box::volume() //在类外定义声明成员函数volume
{
return(height*width*length);
}
int main()
{
Box box1;//建立对象box1,不指定实参
/* cout<<"The volume of box1 is"<<box1.volume()<<endl;
Box box2(15,30,25);
cout<<"The volume of box2 is"<<box2.volume()<<endl;*/
cout<<"The volume of box1 is"<<box1.volume()<<endl;
Box box2(15);
cout<<"The volume of box2 is"<<box2.volume()<<endl;
Box box3(15,30);
cout<<"The volume of box3 is"<<box3.volume()<<endl;
Box box4(15,30,20);
cout<<"The volume of box4 is"<<box4.volume()<<endl;
return 0;
}
/*由于在定义对象box1时没有给实参,系统就调用默认构造函数,各形参的值均取默认值10,即
box1.height=10;box1.width=10;box1.length=10
在定义对象box2时只给定一个实参15,它传给形参h(长方柱的高),形参w和len未得到实参过来的值,就取默认值10。即
box2.height=15;box2.width=10;box2.length=10;
说明:
1.应该在什么地方指定构造函数的默认参数?应在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。因为类声明是放在


头文件中的,它是
类的对外接口,用户是可以看到的,而函数的定义是类的实现细节,用户往往看不到的。在声明构造函数时指定默认参数值,使用户知道


在建立对象时怎样使用
默认参数。
2.程序第5行在声明构造函数时,形参名可以省略.
3.如果构造函数的全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。由于不需要实参也可以调用构


造函数,因此全部参数
都指定了默认值的构造函数也属于默认构造函数。前面曽提到过:一个类只能有一个默认构造函数,也就是说,可以不用参数而调用的构


造函数,一个类只能有一个。其道理是显然的,是为了避免调用时的歧义性。如果同时定义了下面两个构造函数,是错误的。
Box();//声明一个无参构造函数
Box(int =10,int =10,int=10);声明一个全部参数都指定了默认值的构造函数
在建立对象时,如果写成
Box box1;
编译系统无法识别应该调用哪个构造函数,出现歧义性,编译时报错。应该避免这种情况。
4.在一个类中定义了全部是默认参数的构造函数后,不能再定义重载的构造函数。例如在一个类中有以下构造函数声明:
Box(int=10,int=10,int=10);//指定全部为默认参数
Box();//声明无参的构造函数
Box(int,int);//声明有两个参数的构造函数
若有以下定义语句:
Box box1;//是调用上面第1个构造函数,还是调用第二个构造函数
Box box2(15,30)//是调用上面第1个构造函数,还是调用第3个构造函数
应该执行哪一个构造函数呢?出现歧义性。但如果构造函数中的参数并非全部为默认值时,就要分析具体情况。如有以下3个原型声明:
Box();//无参构造函数
Box(int,int=10,int=10);//有1个参数不是默认参数
Box(int,int);//有两个参数的构造函数
若有以下定义对象的语句:
Box box1;//正确,不出现歧义性,调用第1个构造函数
Box box2(15);//调用第2个构造函数
Box box3(15,30);//错误,出现歧义性
很容易出错,要十分仔细。因此,一般不应同时使用构造函数的重载和有默认参数的构造函数。






































 

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值