类对象的初始化
需要对类对象进行初始化
对类里面的值进行初始化:
class Time
{hour=0;
minute=0;// 不能在类声明中对数据初始化
sec=0;
};
原因:类不是实体,而是一种抽象类型,不占用内存空间,显然无处存放数据。
ps:如果一个类中所有的成员都是共用,则可以在定义对象时对成员进行初始化。
class Time
{public:
hour;
minute;
sec;
};
Time t1={14,56,30};//将t1初始化为14,56,30
用构造函数实现数据成员的初始化
定义:构造函数时一种特殊的函数,不需要用户来调用它,而是在建立对象时自动执行。
使用:构造函数的名字必须与类名同名,而不能随意命名,以便编译系统能识别它,并把它作为构造函数处理。
构造函数不具有任何类型,不返回任何值。
#include <iostream>
using namespace std;
class Time
{public:
Time()//声明Time类
{hour=0;//定义构造函数,函数名与类名相同
minute=0;//利用构造函数对类中的数据成员赋初值
sec=0;
}//如果不赋初值就直接定义函数Time();就行
void set_time();//成员函数声明
void show_time();
private:
int hour;
int minute;
int sec;
};
void Time::set_time()//定义成员函数,向数据成员赋新值
{cin>>hour;
cin>>minute;
cin>>sec;
}
void Time::show_time()//定义成员函数,输出数据成员的值
{
cout<<hour<<":"<<minute<<":"<<sec<<end;
}
int mian()
{
Time t1;//建立对象t1,同时调用构造函数t1.Time()
t1.set_time();//对t1的数据成员赋新值
t2.show_time();//显示t1的数据成员的值
Time t2;//建立对象t2,同时调用构造函数t2.Time()
t2.show_time();//显示t2的数据成员的值
return 0;
}
从键盘输入t1的值,然后程序运行输出输入的t1的值。然后输出t2的在一开始定义的初始值。
注意:只有在调用构造函数Time时才执行这些赋值语句,对当前的对象中的数据成员。
在类外定义构造函数:
Time::Time()
{
hour=0;
mintue=0;
sec=0;
}
1、每建立一个对象,就调用一次构造函数。
2、构造函数没有返回值,它的作用只是对对象进行初始化。因此也不需要在定义构造函数时声明类型,这是它和一般函数的一个不同的点。
int Time
{........}//这是错误的。
void time()
{........}//这也是错误的
t1.Time();//这是错误的
//构造函数不需要用户调用,也不能被用户调用
下面的是正确可以使用的
Time t1;//建立一个对象t1,同时调用构造函数t1.Time()
Time t2=t1;//建立对象t2,并用t1初始化t2
//此时把对象t1的各数据成员的值复制到t2相应各成员,而不调用构造函数t2.Time()
用带参数的构造函数对不同对象初始化
采用带参的构造函数,在调用不同对象的构造函数时,从外面将不同的数据传递给构造函数以实现不同对象的初始化。
构造函数首部的格式一般为:
构造函数名(类型1 形参1,类型2 形参2......)
定义对象的一般格式为:
类名 对象名(实参1,实参2......);
eg:计算长方体的体积
#include<iostream>
using namespace std;
class Box//声明Box类
{
public:
Box(int,int,int);//声明带参数的构造函数:构造函数名(类型1形参1....)
int volume();//上面形参可以省略只写类型
private:
int height;
int weight;
int length;
};
Box::Box (int n,int w,int len)//在类外定义带参数的构造函数
{height=h;
wight=w;
length=len;
}
int Box::volume()
{return(height*width*length);
}
int main()
{
Box box1(12,25,30);//建立对象box1,并指定box1的高,宽,长的值:类名 对象名(实参1,实参2....)
cout<<"the volume of box1 is"<<box1.volume()<<endl;
Box box2(15,30,21);
cout<<"the volume of box2 is"<<box2.volume()<<endl;
return 0;
}
在构造函数中用参数初始化表对数据成员初始化
参数初始化表来实现对数据成员的初始化。
这种方法不在函数体内对数据成员初始化,而是在函数首部实现。
Box::Box(int h,int w,int len):height(h),width(w),length(len){}
带有参数初始化表的构造函数的一般形式
类名::构造函数名([参数表]:[:成员初始化表]
{[构造函数体]}
//方括号内为可选项(可有可无)
如果数据成员时数组,则应当在构造函数的函数体中用语句对其赋值,而不能在参数初始化表中对其初始化。
eg:
class Student
{
public:
Student(int n,char s,nam[]):num(n),sex(s)//定义构造函数
{strcpy(name,nam);}//函数体
//把形参nam的各元素值通过strcpy函数复制到name数组中
private:
int num;
char sex;
char name[20];
};
//可以这样定义对象stud1:
Student studl(10101,'m',"wang_li");
对构造函数进行重载
在一个类中可以对多个构造函数,这些构造函数有相同的名字,而参数的个数或参数类型不相同,这称为函数的重载。
eg:定义两个构造函数,其中一个有参数,一个无参数
#include<iostream>
using namespace std;
calss Box
{
public:
Box();
Box(int h,int w,int len):heigth(h),width(w),length(len){}//定义一个有参构造函数,用参数的初始化对数据成员初始化
int volume;
private:
int heigth;
int width;
int length;
};
Box::Box()//在类外定义无参构造函数
{
heigth=10;
width=10;
length=10;
}
int Box::volume()
{
return(heigth*width*length);
}
int main()
{
Box box1;//建立对象box1,不指定实参
cout<<"the volume of box1 is"<<box1.volume()<<endl;
Box box2(15,30,25);//建立函数对象box2,指定3个对象
cout<<"the volume of box2 is"<<box2.volume()<<endl;
return 0;
}
在建立对象函数时不必给出实参的构造函数,就称为默认构造函数。无参构造函数属于默认构造函数,一个类只有一个默认构造函数。如果用户未定义构造函数,则系统会自动提供一个默认构造函数,但它的函数体是空的。
构造函数不能被用户显示调用。
Box box1;//建立对象的正确方式
Box box2();//建立对象的错误方式
构造函数可以用默认参数
构造函数中的参数的值既可以通过实参传递,也可以指定为某些默认值,如果用户不指定实参值,编译系统就使形参的值为默认值。
eg:使参数长、宽、高的默认值均为10。
#include<iostream>
using namespace std;
calss Box
{
public:
Box(int h=10,int w=10,int len=10);//在声明构造函数Box时指定默认参数
int volume();
private:
int height;
int width;
int length;
};
Box::Box(int h,int w,int len)//在定义Box函数时可以不指定默认参数
{
heigth=h;
width=w;
length=len;
}
int Box::volume()
{
return(heigth*width*length);
}
int main()
{
Box box1;//没有给定参数
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;
}
为了避免调用时的歧义性,如果同时定义了下面两个构造函数,是错误的。
Box();//声明一个无参的构造函数
Box(int=10,int=10,int=10);//声明一个全部参数都指定了默认值的构造函数
//在建立对象时,如果写成
Box box1;
//系统无法识别调用的时哪个构造函数,产生歧义,编译报错
在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。
Box (int=10,int=10,int=10);//指定全部为默认参数
Box();//声明无参的构造函数
Box(int,int);//声明两个有参的构造函数
//以上三个只能出现一个,不然默认参数系统会混乱不知道调用哪个
利用析构函数进行清理工作
析构函数是一个特殊的函数成员。
定义,形式:
在名字前面加一个“~”符号。(在c++中“~”是取反运算符,析构函数是与构造函数作用相反的函数)
当对象的生命周期结束时,会自动执行析构函数。
具体有以下四种情况:
1、在一个函数中定义了一个对象(假设是自动局部对象),当这个函数被调用结束时,对象应该被释放,在对象释放前自动执行析构函数。
2、静态(static)局部对象子啊函数调用结束时并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
3、如果定义了一个全局的对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数)时,调用该全局对象的析构函数。
4、如果用new运算符动态建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
析构函数不返回任何值,没有函数类型,也没有函数参数。由于没有函数参数,因此它不能被重载。一个类可以有多个构造函数,但是只有一个析构函数。
析构函数时在声明类的时候定义的,(在让系统完成任何工作都在定义的时候进行)如果用户没有定义析构函数,c++编译系统会自动生成一个析构函数,但是只是徒有析构函数的名和形式而已,实际上什么也不进行。
#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
Student(int n,string nam,char s)//定义有参数的构造函数
{
num=n;
name=nam;
sex=s;
cout<<"constructor called."<<endl;
}
~Student()//定义析构函数
{cout<<"Destrctor called"<<endl;}//输出指定信息
void display()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
}
private:
int num;
char name[10];
char sex;
};
int main()
{
Student stud1(10010,"Wang_Li",'f');//定义对象stud1
stud1.display();
Student stud2(10011,"Zhang_fan",'m');//定义对象stud2
stud2.display();
return 0;
}
//析构函数后面指定的结果是显示在最后面的。
调用构造函数和析构函数的顺序
上面析构函数后面输出的结果是:
Destructor called. (执行stud2的析构函数)
Destructor called. (执行stud1的析构函数)
其中,最先调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数正好与调用构造函数的顺序相反。
先构造的后析构,后构造的先析构。相当于一个栈。