构造函数
构造函数的特点:
1.是一个特殊的成员函数;
2.名字与类名相同;
3.有初始化列表;
4.创建类类型对象时,由编译器自动调用,在对象的声明周期内只调用一次;
5.构造函数可以重载,实参决定了调用哪个函数;
6.无参构造函数和带有缺省值得构造函数都认为是缺省构造函数,并且缺省构造函数只能有一个;
7.如果没有显示定义构造函数时,编译器会提供一个默认的构造函数;(不一定,有一定的条件)
例如:#include <iostream>
using namespace std;
class Time
{
public:
Time(int hour = 11,int minute = 11,int second = 11)//构造函数
:_hour(hour)
,_minute(minute)
,_second(second)
{
cout<<"构1造ì函ˉ数簓"<<endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date//没有显示定义的构造函数
{
public:
private:
int _year;
int _month;
int _day;
Time t;
};
void Test()
{
Date d;
}
当Time类中有一个显示定义的缺省的构造函数,而Date类没有并且Date类中包含一个Time类类型的对象t,此时,系统在创建Date类类型对象d 时,自动生成一个默认构造函数。可通过反汇编代码来查看:
8.构造函数不能用const来修饰,构造函数一般要修改成员变量,而一个对象在构造完成前是没有const,值等属性的,所以不存在const构造函数
构造函数的作用:
1.初始化成员数据
2.类型转化(当函数只有一个参数时)
3.构造对象(1.初始化阶段2.普通计算阶段)
初始化列表:
1.以一个冒号开始,接着是以一个逗号分隔的数据成员列表,每个数据成员后面跟一个放在()中的初始化式;
2.每个成员在初始化类表中只能出现一次;
3.初始化列表仅用于初始化数据成员,并不指定这些数据成员的初始化的顺序,最好不要用成员数据去初始化成员数据(有些成员数据可能还未初始化),初始化顺序最好和成员的定义顺序保持一致;
4.引用数据成员,const数据成员和类类型成员(该类没有缺省的构造函数),以上成员必须要放在初始化列表中初始化。
默认构造函数:
类中如果没有显示定义构造函数时,编译器自动合成的默认构造函数使用与变量初始化相同的规则来初始化成员,具有类类型的成员通过运行各自的默认构造函数来进行初始化。内置和复合类型的成员如指针,数组,只对定义在全局作用域中的对象初始化,当对象定义在局部作用域时,内置和复合类型的成员不进行初始化。
explcit:
在某些情况下,默认构造函数是由编译器隐式使用的,用explcit修饰构造函数,抑制由构造函数定义的隐式转换,explcit关键字类内部的构建声明上,在类的定义体外部的定义上不再重复。
拷贝构造函数
1.只有单个形参,而且该形参是对本类型对象的引用(常用const修饰);
2.是构造函数的重载;
3.拷贝构造函数是特殊的构造函数,创建对象时使用已存在的同类对象来进行初始化,由编译器自动调用。也就是说,当用一个本类类型对象来初始化一个变量时,编译器会自动调用拷贝构造函数。
class Time
{
public:
Time(int hour = 11,int minute = 11,int second = 11)
:_hour(hour)
,_minute(minute)
,_second(second)
{
cout<<"构1造ì函ˉ数簓"<<endl;
}
Time(const Time& t)//拷贝构造函数
:_hour(t._hour)
,_minute(t._minute)
,_second(t._second)
{}
private:
int _hour;
int _minute;
int _second;
};
4.如果没有显示定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化。
例:Time类中显示定义了拷贝构造函数,而Date类中没有,并且Date类中包含一个Time类类型的对象t,此时用已存在的同类对象d去初始化d1,编译器会自动合成一个默认拷贝构造函数。
#include <iostream>
using namespace std;
class Time
{
public:
Time(int hour = 11,int minute = 11,int second = 11)//构造函数
:_hour(hour)
,_minute(minute)
,_second(second)
{
cout<<"构造函数"<<endl;
}
Time(const Time& t)//显示定义拷贝构造函数
:_hour(t._hour)
,_minute(t._minute)
,_second(t._second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date//无显示定义的拷贝构造函数,并且包含一个Time类型对象t
{
public:
Date(int year = 2016,int month = 10,int day = 16)//构造函数
:_year(year)
,_month(month)
,_day(day)
{
cout<<"构造函数"<<endl;
}
private:
int _year;
int _month;
int _day;
Time t;
};
void Test()
{
Date d;//创建对象时,编译器自动调用构造函数
Date d1(d);//用已存在的同类型对象初始化对象时,编译器自动调用拷贝构造函数
}
可通过反汇编代码查看编译器是否合成默认拷贝构造函数
拷贝构造函数的使用场景
1.对象实例化对象
2.传值方式作为函数的参数
3.传值方式作为函数返回值
但有一个例外:返回值是无名对象
析构函数
在对象被销毁时,由编译器自动调用,完成类的一些清理和善尾工作
也可以通过反汇编代码查看
Date d(2016,10,16);
00B414FD push 10h
00B414FF push 0Ah
00B41501 push 7E0h
00B41506 lea ecx,[ebp-28h]
00B41509 call Date::Date (0B4100Fh)
00B4150E mov dword ptr [ebp-4],0
Date d1(d);
00B41515 lea eax,[ebp-28h]
00B41518 push eax
00B41519 lea ecx,[ebp-48h]
00B4151C call Date::Date (0B411BDh)
}
00B41521 lea ecx,[ebp-48h]
00B41524 call Date::~Date (0B411E5h) //销毁d1
00B41529 mov dword ptr [ebp-4],0FFFFFFFFh
00B41530 lea ecx,[ebp-28h]
00B41533 call Date::~Date (0B411E5h) //销毁d
00B41538 push edx
析构函数的特性:
1.析构函数在类名上加上字符~ ~Date()
2.析构函数无参数无返回值;
3.一个类有且只有一个析构函数。若未显示定义,编译器会自动生成缺省的析构函数。(Time类中显示定义析构函数,Date类中无显示定义的析构函数且包含Time类类型对象t,此时,系统自动合成析构函数)
4.对象生命周期结束时,编译器自动调用析构函数;
5.析构函数并不是删除对象,而是做一些清理工作。
操作符重载:是一个具有特殊函数名的函数,关键字operator后面接需要定义的操作符符号,具有返回值和形参表,形参数与操作符的操作数目相同,使用运算符重载可以提高代码的可读性。
可以被重载的操作符:
不可以被重载的操作符:
1.不能通过连接其他符号来创建新的操作符;比如operator@(){};
2.重载操作符必须有一个类类型或者枚举类型的操作数;
3.用于内置类型的操作符,其含义不能改变;
4.作为类成员的重载函数,其形参看起来比操作数数目少一个,成员函数的操作符有一个默认的形参this,限定为第一个形参;
赋值运算符重载
class Complex
{
public:
Complex(double r = 1.0,double i = 1.0)//构造函数
:_real(r)
,_image(i)
{
cout<<"Complex(double r ,double i )"<<endl;
}
Complex( const Complex& c)//拷贝构造函数
:_real(c._real)
,_image(c._image)
{
cout<<"Complex( const Complex& c)"<<endl;
}
Complex& operator=(const Complex& c)//赋值运算符重载
{
cout<<"operator"<<this<<endl;
if(this != &c)//防止对象给自己赋值
{
_real = c._real;
_image = c._image;
}
return *this;
}
private:
double _real ;
double _image;
};
private:
double _real ;
double _image;
};