(1)构造函数:
一个特殊的类成员函数,函数名与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期中只且只调用一次,以保证每个数据成员都有一个合适的初始值。有初始化阶段与普通计算阶段。
class Date
{
public:
Date()
{}
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
如以上类中,为显式定义的构造函数Date(),两个成员函数可构成重载,都为类的构造函数,具体调用看编程者在实例化时传参情况,若未显式定义,编译器会自动合成默认构造函数。
构造函数的特性:
(1):函数名与类名相同;
(2):没有返回值(真的没有吗?)。
通过观察反汇编代码发现,是会有返回值的。
如以上类。
定义一个普通的函数Funtest():
int Funtest()
{
int temp = 3;
return temp;
}
观察return temp;语句的反汇编代码时,可以看到
mov eax,dword ptr [temp];
而当进入类构造函数的反汇编代码时,可以看到
mov eax,dword ptr [this];
异曲同工,说明构造函数可能会隐式返回一个值,此值为始终指向此类的指针this。
(3):有初始化列表(可以不用)(且只有构造函数才可以有)
初始化列表见词知意,用来初始化,以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后边跟一个放在圆括号中的初始化式。每个成员只能初始化一次。
初始化顺序:
<1>每个成员在初始化列表中只能出现一次。
<2>初始化列表仅用于初始化数据成员,并不指定这些数据成员的初始化顺序,数据成员在类中定义顺序就是在参数列表中的初始化顺序。
<3>尽量避免使用成员初始化成员,成员的初始化顺序最好和成员的定义顺序保持一致。
附:类中包含以下成员必须要放在初始化列表中初始化:
1、引用数据成员
2、const数据成员
3、类类型成员(该类没有缺省的构造函数)
(4):新对象被创建,由编译器自动调用,且在对象的生命期内仅调用一次。
(5):构造函数可以重载,实参决定了调用那个构造函数。
如以上类,Date() Date(int year, int month, int day)两个函数重载,通过判断实参传递判断调用哪个构造函数。
(6):如果没有显式定义时,编译器会提供一个默认的构造函数。((实际操作中,编译器会判断需不需要进行生成构造函数,如下:)
class Date
{
public:
private:
int _year;
int _month;
int _day;
};
int main()
{
Date date;
system("pause");
return 0;
}
如上代码,在Date date处打断点调试时会发现直接跳到下一句执行,查看反汇编时会发现没有生成构造函数。
一般,类中没有定义类成员,编译器会进行优化,不给出默认构造函数。
class Time
{
public:
Time()
{}
private:
int _hours;
int _minute;
int _second;
};
class Date
{
public:
void Print(int year = 2016,int month = 10,int day = 21)
{
_year = year;
_month = month;
_day = day;
cout<<"date"<<endl;
}
private:
int _year;
int _month;
int _day;
Time t;
};
int main()
{
Date date;
date.Print(2016,10,20);
system("pause");
return 0;
}
如上代码,没有显示的给出构造函数,编译器会生成一个默认构造函数:
call Date::Date (0B114A6h)
(7):无参构造函数和带有缺省值得构造函数都认为是缺省构造函数,并且缺省构造函数只能有一个(由于重载的问题)。
(8):构造函数不能用const来修饰。
【默认构造函数】
类如果没有显式定义构造函数时,编译器会合成一个默认的构造函数,该构造函数中什么工作都不做(并不是绝对的,有可能要做一些工作)。
class Time
{
public:
Time()
{}
private:
int _hours;
int _minute;
int _second;
};
class Date
{
public:
void Print(int year = 2016,int month = 10,int day = 21)
{
_year = year;
_month = month;
_day = day;
cout<<"date"<<endl;
}
private:
int _year;
int _month;
int _day;
Time t;
};
进入反汇编后
call Date::Date (0B114A6h) 两次F11进入后会发现
call Time::Time (0B114A1h) 代码,可知在生成的构造函数中为Time类生成了构造函数。
只要显式定义了,即使该构造函数什么也不做,编译器也不会为该类合成默认的构造函数。编译器生成的默认构造函数使用与变量初始化相同的规则来初始化成员,具有
类类型的成员通过运行各自的默认构造函数来进行初始化。内置和复合类型的成员如指针、数组,只对定义在全局作用域中的对象初始化,当对象定义在局部作用域时,内置
和符合类型的成员不进行初始化。在某些情况下,默认构造函数是由编译器隐式使用的。
构造函数作用:
1、构建对象
2、初始化对象
3、类型转换
【explcit】
用explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit关键字类内部的构建声明上,在类的定义体外部的定义上不再重复。
(2)拷贝构造函数
只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建对象时
使用已存在的同类对象来进行初始化,由编译器自动调用。
Date(const Date& date)
【使用场景】
1、对象实例化对象
Date d1(1990, 1, 1);
Date d2(d1);
2、传值方式作为函数的参数
void FunTest (const Date date)
{}
3、传值方式作为函数返回值
Date FunTest()
{
Date date;
return date;
}
特性:
(1):它是构造函数的重载。
(2):它的参数必须用同类型的引用传递。
若直接传值,也就是说
Date(const Date date)
再次形成对象实例化对象的情况,不断递归。
(3):如果没有显式定义,系统会自动合成一个默认的拷贝构造函数。会依次拷贝类的数据成员完成初始化。
(3)析构函数
析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和汕尾工作
class CArray
{
public:
CArray(size_t capacity)
: _capacity(capacity)
{
_pData = ( int*)malloc(capacity*sizeof (int));
_size = 0;
}
~CArray()
{
if (NULL != _pData)
{
free(_pData);
_pData = NULL;
}
_size = 0;
_capacity = 0;
}
private:
int* _pData;
size_t _size;
size_t _capacity;
};
以上为大概工作原理
特性 :
a、析构函数在类名(即构造函数名)加上字符~。
b、析构函数无参数无返回值。
c、一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
d、对象生命周期结束时,C++编译系统系统自动调用析构函数。
e、注意析构函数体内并不是删除对象,而是做一些清理工作。