首先写一个Date类:
class Date
{
public:
void show();
private:
int _year;
int _month;
int _day;
};
void Date::show()
{
//cout << _year << "-" << _month << "-" << _day << endl;
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
int main()
{
Date date;
date._year = 2018;
date._month = 3;
date._day = 19;
date.show();
system("pause");
}
隐含的this指针
- 每个成员函数都有一个指针形参,它的名字是固定的,称为this指针,this指针是隐式的。
- 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
- this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。
构造函数
定义:成员变量为私有的,要对它们进行初始化,必须用一个公有成员函数来进行。同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数。
特点:
- 函数名与类名相同;
- 无返回值;
- 对象构造(对象实例化)时系统自动调用对应的构造函数;
- 构造函数可以重载;
- 构造函数可以在类中定义,也可以在类外定义;
- 如果类定义中没有给出构造函数,则C + + 编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数。
- 无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个。
无隐含this
class Date
{
public:
Date()//无参构造函数
{}
//带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//全缺省构造函数
Date(int year=2018, int month=3, int day=19)
{
_year = year;
_month = month;
_day = day;
}
//半缺省
Date(int year, int month = 2)
{
_year = year;
_month = month;
_day = 6;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2018,3,19);
Date d3(d2);//拷贝构造
system("pause");
}
拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数,是特殊的构造函数。
特点:
- 拷贝构造函数其实是一个构造函数的重载。
- 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
- 若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。
class Date
{
public:
Date( const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date()//无参构造函数
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
Date d3=d1;//拷贝构造
}
这里解释一下特点2:
析构函数
当一个对象的生命周期结束时,C + + 编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数。
特殊的成员函数。
特点:
- 析构函数在类名加上字符~ 。
- 析构函数无参数无返回值。(不可重载)
- 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
- 对象生命周期结束时,C + + 编译系统系统自动调用析构函数。
- 注意析构函数体内并不是删除对象,而是做一些清理工作。
运算符重载
特点:
- operator + 合法的运算符 构成函数名(重载< 运算符的函数名:operator < )。
- 重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。
class Date
{
public:
//全缺省构造函数
Date(int year=2018, int month=3, int day=19)
{
_year = year;
_month = month;
_day = day;
}
////半缺省
//Date(int year, int month = 2)
//{
// _year = year;
// _month = month;
// _day = 6;
//}
//Date( const Date& d)
//{
// _year = d._year;
// _month = d._month;
// _day = d._day;
//}
Date()//无参构造函数
{}
//带参构造函数
//Date(int year, int month, int day)
//{
// _year = year;
// _month = month;
// _day = day;
//}
bool operator==( const Date& d2)
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018,2,10);
Date d2(2018,9,10);
Date d3=d1;
cout << (d1 == d2) << endl;//不相等返回0,相等返回1
cout << (d1.operator==(d2)) << endl;//同上
}
【笔试考点】
5 个C + + 不能重载的运算符:
.*/::/sizeof/?:/.
注:(以/隔开)
赋值运算符重载
拷贝构造函数是创建的对象,使用一个已有对象来初始化这个准备创建的对象。
赋值运算符的重载是对一个已存在的对象进行拷贝赋值。
#pragma once
class Date
{
public:
Date()
{}
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date(const Date& d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
void show()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//赋值运算符重载(对已存在的对象拷贝赋值)
Date& operator=(const Date& d)//传引用
{
if (this != &d)//避免自身赋值
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1(2018,3,28);
d1.show();
Date d2 = d1;//拷贝构造函数
d2.show();
Date d3(2018,3,29);
d3 = d1;//赋值运算符重载
d3.show();
}
类的成员变量初始化方式
- 初始化列表:以一个冒号开始,接着一个逗号分隔数据列表,每个数据成员都放在一个括号中进行初始化.
- 构造函数体内进行赋值。
class Time
{
public:
Time()
{
cout << "Time()" << endl;
int _hour = 0;
int _minute = 0;
int _second = 0;
}
Time(const Time& t)
{
cout << "Time(const Time& t)" << endl;
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
//1
Date(int year, int month, int day, const Time& t)
{
cout << "Date ()非初始化列表" << endl;
_year = year;
_month = month;
_day = day;
_t = t;
}
//2
//Date(int year, int month, int day, const Time& t)
// : _year(year)
// , _month(month)
// , _day(day)
// , _t(t)
//{
// cout << "Date ()- 初始化列表 " << endl;
//}
private:
int _year;
int _month;
int _day;
int _testConst=2; // 1.测试const 成员变量的初始化
int& _testReference=_testConst; // 2.测试引用成员变量的初始化
Time _t; // 3.测试无缺省构造函数的成员变量的初始化
};
void TestDate()
{
Time t;
Date d1(2018, 3, 28, t);
}
结果为:
非初始化列表
初始化列表
通过比较可发现:初始化列表较为高效,仅调用了一次Time()与Time(const Time& t)。而非初始化列表调用了两次Time().
注意:
a. 哪些成员变量必须放在初始化列表里面?
- 常量成员变量。(常量创建时必须初始化)
- 引用类型成员变量。(引用创建时必须初始化)
- 没有缺省构造函数的类成员变量.
b.成员变量按声明顺序依次初始化,而非初始化列表出现的顺序.
c.初始化列表不管是否使用都会被调用。