————————————————————————————————————————————————————————
时间是做美好的礼物,之前没有接触C++时候,只是知道C++是面向过程的,
不理解什么是面向过程,现在接触C++有一阵了,有一次在公交车上,就那么一瞬间给明白了。
什么事情都需要时间,时间就像一片土地,需要我们在上面播种,施肥,浇水,时间久了,
自然会有自己想要收获。还有就是无论是看书,还是看一些网络视频,感觉看一遍,就知道了,
但是很难去记住它,下次遇到了可能还会去回忆半天,俗话说的好,眼过千遍,不如手写一遍。
把一些东西给记录下来,方便梳理知识的同时也加深了记忆。
———————————————————————————————————————————————————————————
一.构造函数
我们定义的类并不是一个实体,而是我们抽象的类型,并不占用存储空间,也不能容纳数据。因此,我们不能在定义类的空时初始化数据成员。这个时候就引出构造函数(constructor)。构造函数就是来对对象进行初始化。构造函数是一种特殊的成员函数,不需要用户调用,构造函数必须和类名同名。她不具有任何类型,也不返回任何值。
class Time
{
public:
Time();//声明不带参数的构造函数
.....
private:
int hour;
int mintue;
int sec;
};
注意:
(1)构造函数必须类名一致。
(2)构造函数不具有任何类型,也不返回任何值,不能写成 void Time()。
(3)可以在类中定义构造函数,也可以只在类中声明,在类外定义构造函数。类外定义构造函数的格式:
Time :: Time() //类外定义构造函数,需要添加限定符Time 和域限定符 ::
{
hour = 0;
mintue = 0;
sec = 0;
}
(4)构造函数主要用来初始化对象,不需要被用户调用,也不能被调用,不能写成 t1.Time()。构造函数在建立对象时候由系统直 接执行,而且只执行一次,构造函数一般声明为public。
(5)如果自己没有定义构造函数,则C++编译器会自动生成构造函数,只是这个构造函数是空的,没有参数,一般不提倡在构造函 数中添加和初始化无关的语句。
2.构造函数的类型
(1)带参数的构造函数
对于不带参数的构造函数,在函数体内部进行赋值,这种办法虽然简单,但是无法对不同的对象赋不同的初值。我们可以采用带参数的构造函数。在调用不同对象的构造函数时候,可以从外部把数据传递给构造函数,以实现不同的初始化。
格式:构造函数名(类型 形参1,类型 形参2,.....)
class Time
{
public:
Time(int, int, int );//声明带参数的构造函数
.....
private:
int hour;
int mintue;
int sec;
};
Time::Time(int h, int m, int s) //在类外定义带参数的构造函数
{
hour = h;
mintue = m;
sec = s;
}
构造函数是不能被调用,我们需要在定义对象的时候给出。
格式:类名对象名(实参1,实参2,.....)
int main()
{
Time t1(10,10,10); //带参数的构造函数,对应的实参是在定义对象时候给出
Time t2(0,0,0); //这种方法可以实现不同的对象进行不同的初始化
......
}
(2)参数初始化列表对数据成员初始化
Time::Time(int h, int m, ints):hour(h),mintue(s),sec(s){} //在类外定义带参数的构造函数
如果在类中定义的构造函数,也可以用这种办法,去掉限定符和域限定符。
注意:参数列表中间是“,” 不是“;”。
(3)构造函数重载
在一个类中可以定义多个构造函数,以便对类对象进行提供不同的初始化方法,这些构造函数具有相同的名字,而参数的个数或者参数的类型不一致,这称作函数的重载。
#include <iostream>
using namespace std;
class Time
{
public:
Time(); //声明无参数的构造函数
Time(int h, int m, int s): hour(h),mintue(m),sec(s){} //声明有参数的构造函数,用参数初始化列表对数据成员初始化
......
private:
int hour;
int mintue;
int sec;
};
Time::Time()
{
hour = 0;
mintue = 0;
sec = 0;
}
int main()
{
Time t1; //建立对象t1不指定对象
Time t2(10,10,10); //建立对象t2,指定3个参数
.....
.....
}
说明:
程序中定义了两个重载构造函数,其实可以定义其他构造函数。如下所示:
Time::Time(int h); //有1个参数的构造函数
Time::Time(int h, int m); //有2参数的构造函数
在建立对象的时候分别指定1个参数和两个参数。
注意:
(1)用户没有定义构造函数,则系统就会提供一个构造函数,但是函数体内是空的,不起初始化作用,如果用户希望在创建对象时 候就能使数据成员有初值,就必须自己定义构造函数。
(2)建立对象选用无参构造函数,书写形式:Time t1;
(4)使用默认参数的构造函数
构造函数的值可以通过实参传递,也可以指定为某些默认值。实际生活中常有这样一些初始值,计数器的初值一般默认是0,战士的性别默认是男,如果实际情况和这些不同,可以由用户另行指定,减少工作量。
#include <iostream>
using namespace std;
class Time
{
public:
Time(int h = 0, int m = 0, int s = 0): hour(h),mintue(m),sec(s){} //构造函数指定默认值
display();
private:
int hour;
int mintue;
int sec;
};
Time::display()
{
cout <<"The Time: "
<< hour <<":"
<< mintue <<":"
<< sec <<endl;
}
int main()
{
Time t1; // 没有给定参数
t1.display();
Time t2(10); //只给一个参数
t2.display();
Time t3(10,10); //只给两个参数
t3.display();
Time t4(10,10,10); //给定3个参数
t4.display();
}
在构造函数中使用默认值是方便有效的,相当于多个重载函数。
注意:
(1)应该在声明构造函数的时候指定默认值,而不能在定义构造函数指定默认值。
(2)说明构造函数也可以写成:Time(int = 0, int = 0, int =0): hour(h),mintue(m),sec(s){}
(3)参数全部指定的默认构造构造函数也属于默认构造函数,一个类中只能有一个默认构造函数。
如: Time(); //声明一个无参的构造函数
Time(int =0, int =0, int=0) //声明一个参数都指定了的默认值构造函数
用户在建立对象时候,如果写成:Time t1; 则无法辨识调用哪个构造函数。编译会出错
(4)一个类中定义了全部默认参数的构造函数,不能再定义重载构造函数。会出现类似(3)的歧义。如果构造函数中的参数并非 全部默认值,这就要分情况。
如:Time(); //无参构造函数
Time(int , int =10, int =10); //有1个参数不是默认值
Time(int, int); //有两个参数的构造函数
有以下的定义的语句:
Time t1; //正确,不会产生歧义,调用第一个构造函数
Time t2(15); //正确,调用第二个构造函数
Time t3(14,30);//错误,产生歧义。
二.析构函数
析构函数(destructor)析构函数作用和构造函数作用相反,名称是在类名签名加一个“~”符号。
当对象的生命周期结束的时候,会自动执行析构函数。析构函数并不是删除对象,而是在撤销对象的占用的内存之前完成一些清理工作。析构函数不返回任何值,没有函数类型,也没有函数参数。没有参数,因此不能被重载。一个类可以有多个构造函数,但是只能有一个析构函数。
~Time()
{
cout<<"Destructorcalled"<<endl;
}
三.构造函数和析构函数的调用顺序
先构造的后析构,后构造的先析构。调用顺序如下图: