一、构造函数
构造函数是特殊的成员函数,需要注意的是,构造函数名称虽然叫构造函数,但是需要注意的是它并不是开辟空间创建对象,而是对对象进行初始化。如果类中没有显示定义构造函数,C++编译器会自动生成一个无参的构造函数,一旦用户显示定义编译器将不再生成。
class Date{
public:
//无参的构造函数
Date(){
}
//带参的构造函数
Date(int year,int mouth,int day){
_year = year;
_mouth = mouth;
_day = day;
}
private:
int _year;
int _mouth;
int _day;
};
构造函数的特征:
- 函数名与类名相同;
- 无返回值;
- 对象实例化时编译器自动调用对应的构造函数;
- 构造函数可以重载;
无参的构造函数和全缺省的构造函数都被称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、编译器默认生成的默认构造函数都可以被认为是默认构造函数。
例如下面的代码,有两个默认的构造函数,再编译过程中,编译器会报错。
class Date {
public:
Date() {
_year = 1900;
_mouth = 1;
_day = 1;
}
Date(int year = 1900,int mouth = 1,int day = 1) {
_year = year;
_mouth = mouth;
_day = day;
}
private:
int _year;
int _mouth;
int _day;
};
int main() {
Date d1;
return 0;
}
那么问题来了,编译器默认生成的构造函数到底有什么用?
答案是:编译器生成默认的构造函数会对自定类型(是用class、struct、union自己定义的类型)成员调用;
class Time{
public:
Time(){
cout << "Time()" << endl;
_hout = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date{
private:
//基本类型(内置类型)
int _year;
int _mouth;
int _day;
//自定义类型
Time _t;
};
二、析构函数
析构函数:与构造函数的功能相反,构造函数不是完成对象的销毁,局部对象的销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成类的一些清理工作。
typedef int DataType;
class SeqList{
public :
SeqList (int capacity = 10){
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList(){
if (_pData){
free(_pData ); // 释放堆上的空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
析构函数的特性:
- 析构函数名是在类名前面加上字符 ~ ;
- 无参数、无返回值;
- 一个类有且只有一个析构函数。若未显示定义,系统会自动生成默认的析构函数;
- 对象生命周期结束时,C++编译系统自动调用析构函数;
对于编译器自动生成的默认析构函数,类似于构造函数,会对自定义类型成员调用它的析构函数;
三、拷贝构造函数
拷贝构造函数:只有单个形参,该形参时对被类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
注意:拷贝构造函数只是构造函数的一种重载形式!拷贝构造函数的参数有且只有一个!!必须使用引用传参!!,使用传值的方式会引发无穷递归引用。
class Date {
public:
//构造函数,初始化列表
Date(int year, int mouth, int day)
:_year(year)
, _mouth(mouth)
, _day(day)
{
cout << "Date()" << endl;
}
//拷贝构造函数
Date(const Date& d) {
_year = d._year;
_mouth = d._mouth;
_day = d._day;
}
//析构函数
~Date() {
cout << "~Date()" << endl;
}
private:
int _year;
int _mouth;
int _day;
};
同样的,如果没有显示定义,系统将会生成默认的拷贝构造函数,但是默认的构造拷贝函数时按照内存存储的字节序完成拷贝,这种拷贝我们称为浅拷贝(值拷贝)。
四、赋值操作符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似;
函数名字:关键字operator+需要重载的运算符符号;
函数原型:返回值类型operator操作符(参数列表);
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的 操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载!!!
class Date {
public:
/*
Date(int year,int mouth,int day){
_year = year;
_mouth = mouth;
_day = day;
}
*/
//构造函数,初始化列表
Date(int year, int mouth, int day)
:_year(year)
, _mouth(mouth)
, _day(day)
{
cout << "Date()" << endl;
}
//拷贝构造函数
Date(const Date& d) {
_year = d._year;
_mouth = d._mouth;
_day = d._day;
}
//赋值运算符重载
bool operator=(const Date& d) {
if (this != &d) {
_year = d._year;
_mouth = d._mouth;
_day = d._day;
}
}
//运算符重载
bool operator==(const Date& d) {
return _year == d._year
&& _mouth == d._mouth
&& _day == d._day;
}
//析构函数
~Date() {
cout << "~Date()" << endl;
}
private:
int _year;
int _mouth;
int _day;
};
五、const成员
const修饰的类成员函数称之为 const 成员函数,const修饰类成员函数,实际修饰该成员函数隐含的 this 指针,表明在该成员函数中不能对类的任何成员进行修改;
class Date{
public :
void Display (){
cout<<"Display ()" <<endl;
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl<<endl ;
}
void Display () const{
cout<<"Display () const" <<endl;
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl<<endl;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
void Test (){
Date d1 ;
d1.Display ();
const Date d2;
d2.Display ();
}
六、取地址及const取地址操作符重载
这两个函数一般不用重新定义,编译器会默认生成;
class Date{
public :
Date* operator&(){
return this ;
}
const Date* operator&()const{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
}