类中的默认成员函数
类中默认成员函数一共有六个:(1)构造函数(2)拷贝构造函数(3)析构函数(4)赋值操作符重载(5)取地址操作符重载(6)const修饰的取地址操作符修改。
一.构造函数
构造函数就是随着对象被创建而自动调用的公有成员函数,有且仅在对象被定义时,自动调用一次,主要用于对对对象的初始化,
它的特征:
1. 函数名与类名相同。
2. 无返回值。
3. 对象构造(对象实例化)时系统自动调用对应的构造函数。
4. 构造函数可以重载。
5. 构造函数可以在类中定义,也可以在类外定义。
6. 如果类定义中没有给出构造函数,则C++编译器自动产生一个默认的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成默认的构造函数。
7. 无参的构造函数和全默认值的构造函数都认为是默认构造函数,并且默认的构造函数只能有一个。
class Date
{
public:
Date()//无参的构造函数
{
}
Date(int year, int month, int day)//有参的构造函数
{
_year = year;
_month = month;
_day = day;
}
/*Date(int year = 1999, int month = 12, int day = 31)//出错 无参的构造函数和带全缺省参数的构造函数只能有一个
{
_year = year;
_month = month;
_day = day;
}*/
void print()
{
cout << _year << " " << _month << " " << _day << " " << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;//调用无参的构造函数
Date d2(2018, 10, 31);//调用有参的构造函数
d2.print();// 2018 10 31
system("pause");
return 0;
}
二.拷贝构造函数
创建对象时,用同类型的对象来初始化,这种构造函数就是拷贝构造函数。
它的特性:
1.拷贝构造函数是构造函数的重载;
2.拷贝构造函数的参数只有一个,而且只能传引用。(传值的话会引发无穷的递归调用)
3.若没有显示定义,系统会默认生成一个拷贝构造函数。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << " " << _month << " " << _day << " " << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 20 ,31);
Date d2 = d1;
Date d3(d1);
system("pause");
return 0;
}
理解拷贝构造函数传值引发无穷递归:
如果是传值,在第一次调用就要获取到d的值,就引发了对象的拷贝,再传值,又引发了对象的拷贝,依次就会在引发对象的拷贝和传值当中无限递归。
三.析构函数
当一个对象的生命周期结束的时候,C++编译系统会自动调用一个成员函数,这个成员函数就是析构函数。
它的特性:
1.析构函数在类名前加~
2.无参数无返回值
3.一个类只能有一个析构函数
4.在对象的生命周期结束时,C++编译系统会自动调用
class Date//日期类
{
public:
~Date()//析构函数
{
}
private:
int _year;
int _month;
int _day;
};
析构函数主要完成的任务:不是删除对象,而是在对象删除前做一些清理事项。
class Slist
{
public:
Slist(int size)
{
int* _arr = (int*)malloc(size*sizeof(int));
}
~Slist()
{
if (_arr)
{
free(_arr);//释放空间
_arr = NULL;//指针制空
}
}
private:
int* _arr;
};
四.运算符重载
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date & d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void print()
{
cout << _year << " " << _month << " " << _day << " " << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 20 ,31);
Date d2 = d1;
Date d3(d1);
if (d3 == d2)//错误error C2678: 二进制“==”: 没有找到接受“Date”类型的左操作数的运算符(或没有可接受的转换)
{
printf("相同的日期\n");
}
system("pause");
return 0;
}
我们判断相同的日期的时候,用“==”符号代码可读性非常好。但是这种符号C++编译器是不会识别的,因此我们引入了运算符重载。
运算符重载的特性:
1. operator + 合法的运算符 构成函数名 (重载“>”:operator>)
2. 重载运算符以后,不能改变运算符的优先级/结合性/操作数个数
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date & d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
bool operator==(const Date& d)//运算符的重载
{
return _year == d._year&&_month == d._month&&_day == d._day;
}
void print()
{
cout << _year << " " << _month << " " << _day << " " << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 20 ,31);
Date d2 = d1;
Date d3(d1);
if (d3 == d2)
{
printf("相同的日期\n");
}
system("pause");
return 0;
}
在C++中,大多数运算符都能被运算符重载实现,
5个C++不能重载的运算符:(1).*(2)sizeof(3)::(4)?.(5).
. (成员访问运算符)
.* (成员指针访问运算符)
:: (域运算符)
sizeof (长度运算符)
?: (条件运算符)
五.赋值运算符的重载
1.赋值运算符的重载是对一个已经存在的对象进行拷贝赋值。
2.当程序没有显式地提供一个以本类或本类的引用为参数的赋值运算符重载函数时,编译器会自动生成这样一个赋值运算符重载函数
class Date
{
public:
Date()
{
}
Date(int year, int month, int day)//构造函数
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << " " << _month << " " << _day << " " << endl;
}
Date& operator=(const Date& d)//运算符重载 赋值运算符 =
{
//判断是不是自己给自己赋值?
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 20 ,31);
Date d2, d3, d4;
d2 = d1;
d1.print();
d2.print();
system("pause");
return 0;
}
代码实现如上,那么问题来了?
为什么赋值运算符的重载返回值是一个类类型的引用? void可以吗?
我们操作一下的代码:
class Date
{
public:
Date()
{
}
Date(int year, int month, int day)//构造函数
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << " " << _month << " " << _day << " " << endl;
}
void operator=(const Date& d)//运算符重载 赋值运算符 =
{
//判断是不是自己给自己赋值?
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 20 ,31);
Date d2, d3, d4;
d2 = d1;
d4 = d3 = d2;//连续赋值出错
d1.print();
d2.print();
d3.print();
d4.print();
system("pause");
return 0;
}
d2=d1没有报错,而d4 = d3 = d2连续赋值出错。所以返回值是类类型的引用是为了连续赋值。
六.类的取地址操作符重载 及 const修饰的取地址操作符重载
这两个成员函数一般不需要重新定义,编译器会默认生成
class Date
{
public: Date* operator &()
{
return this;
}
const Date * operator&() const
{
return this;
}
private:
int _year;
int _month;
int _day;
};