1.构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员有一个合适的初始值,并且在对象的生命周期内只调用一次
构造函数并不是开空间创建对象,而是初始化对象
特征:
①.函数名与类名相同
②.无返回值
③.对象实例化时编译器自动调用的构造函数
④.构造函数可以重载,可以写多个构造函数,提供多种初始化方式
无参构造函数
Date(){
_year=1;
_month=1;
_day=1;
}
带参构造函数
Date(int year,int month,int day){
_year=year;
_month=month;
_day=day;
}
带参缺省值构造函数
Date(int year=2023,int month=7,int day=30){
_year=year;
_month=month;
_day=day;
}
Date d1;//调用无参构造函数
Date d2(2015,1,1)//调用带参的构造函数
如果通过无参构造函数创建对象时,对象后面不用跟括号,不然就成了函数声明
Date d3();
这段代码的意思为:声明一个d3函数,该函数没有参数,返回值为Date类型
⑤.如果类中没有显式定义构造函数,则c++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义编译器将不再生成
默认构造函数
<1> 无参构造函数
<2>全缺省值构造函数
<3>编译器默认生成的构造函数
构造函数,也是默认成员函数,我们不写,编译器会自动生成,编译器生成的默认构造函数的特点:
<1>我们不写才会生成,我们写了就不会生成
<2>内置类型的成员不会处理(c++把类型分成内置类型(基本类型)和自定义类型,内置类型就是语法已经定义好的类型,如int,char等,自定义类型就是自己用class,struct,union定义的类型。编译器生成的默认构造函数对内置类型不做处理,对于自定义类型将调用它们的构造函数)
<3>自定义类型的成员才会处理,会去调用这个成员的默认构造函数
(该自定义类型成员如果没有默认构造函数则会报错,如果不提供默认构造,则需要初始化列表才能解决)
2.析构函数
析构函数,与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成,而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特征:①.析构函数名是在类名前加字符~
②.无参数无返回值
③.一个类有且只有一个析构函数,析构函数不能重载,若显式未定义,系统会自动生成默认的析构函数
(系统默认生成的析构函数与系统默认生成的构造函数类似,对于内置类型不会处理,对于自定义类型会自动调用该自定义类型的析构函数,像int*p,不会去释放p所指向的空间,因为指针类型都是内置类型)
④.对象生命周期结束时,c++编译系统会自动调用析构函数
Date d1;
Date d2;
stack st1;
stack st2;
//先调用st2的析构,再是st1,再是d2,再是d1
3.拷贝构造函数
为什么要有拷贝构造函数?
如果采用浅拷贝,将对象浅拷贝,对象中如果有动态开辟的内存,拷贝的对象析构后,动态开辟的内存被释放,而原对象也要析构,所以会造成double free,两次释放的问题
①.拷贝构造函数是构造函数的一个重载形式
②.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用
(因为拷贝构造函数的参数如果是传值传参,先传参,传参要形成一个拷贝构造,拷贝构造又要继续传参
stack(const stack&s){
_array=(int*)malloc(sizeof(int)*capacity;
memcpy(_array,s._array,sizeof(int)*s._size);
_size=s._size;
_capacity=s._capacity;
}
我们不写,编译器默认生成的拷贝构造函数跟之前拷贝构造函数特性不一样
①.内置类型进行值拷贝
②.自定义类型,调用它的拷贝构造
4.赋值运算符重载
①.参数类型
②.返回值
③.检测是否自己给自己赋值
④.返回*this
⑤.一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的拷贝
运算符重载
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型operator操作符(参数列表)
①.不能通过连接其他符号来创建新的操作符,比如operator@
②.重载操作符必须有一个类类型或者枚举类型的操作数
③.用于内置类型的操作符,其含义不能改变,例如内置的整型+不能改变其含义
④.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
操作符有一个默认形参this,限定为第一个形参
⑤.不能改变操作符的操作数个数,一个操作符是几个操作数,那么重载的时候就有几个操作数
⑥.五个运算符不能重载,分别是 .* :: sizeof ?: 丶不能重载
日期类的实现
class Date
{
friend void operator<<(ostream& out, const Date& d);//友**元函数是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字**
friend void operator>>(istream& out, Date& d);
public:
// 获取某年某月的天数
int GetMonthDay(int year, int month){
/* if (month <= 0 || month > 12){ cout << "月份不对" << endl; return 0; }*/
static int monthDatearry[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if ((month==2)&&((year % 4 == 0 && year % 100 != 0) || (year % 100 == 0 && year % 400 == 0))){
return 29;
}
else{
return monthDatearry[month];
}
}
// 全缺省的构造函数
Date(int year = 1900, int month = 1 , int day = 1){
cout << "构造" << endl;
_year = year;
_month = month;
_day = day;//如果声明给了缺省值,没有次语句将使用缺省值;
}
//不传参数就可以调用就叫默认构造;(系统默认生成,全缺省,无参数)
//Date(){
// _year = 1900;
// _month = 1;
// _day = 2;
//}
// 拷贝构造函数(自定义类型);
// d2(d1)
Date(const Date& d ){//const引用不能修改防止写反
cout << "拷贝构造" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d){
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 析构函数
~Date(){
_year = 0;
_month = 0;
_day = 0;
}
//运算符重载运算符有几个操作数就有几个参数
// 日期+=天数
Date& operator+=(int day){
if (day < 0)return *this -= (-day);
_day += day;
while (_day>GetMonthDay(_year, _month)){
_day -= GetMonthDay(_year, _month);
_month++;
while (_month == 13){
_month = 1;
_year++;
}
}
return *this;
}
// 日期+天数
Date operator+(int day)const{
Date ret(*this);
ret += day;
return ret;
}
// 日期-=天数
Date& operator-=(int day){
if (day < 0)return *this += (-day);
_day -= day;
while (_day<=0){
--_month;
if (_month <= 0){
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// 日期-天数
Date operator-(int day)const{
Date ret = *this;
ret -= day;
return ret;
}
// 前置++
Date& operator++(){
*this += 1;
return *this;
}
// 后置++,后置++增加了一个形参,加了一个int参数进行占位,跟前置++构成重载进行区分,本质后置++调用,编译器进行特殊处理)
Date operator++(int){
Date ret = *this;//Date ret(*this);
ret += 1;
return ret;
}
// 后置--
Date operator--(int){
Date tmp = *this;
*this -= 1;
return tmp;
}
// 前置--
Date& operator--(){
*this -= 1;
return *this;
}
// >运算符重载
bool operator>(const Date& d)const{
if (_year > d._year){
return true;
}
else if (_year == d._year&&_month > d._month){
return true;
}
else if (_year == d._year&&_month == d._month&&_day > d._day){
return true;
}
return false;
}
// ==运算符重载
bool operator==(const Date& d)const{
return _year == d._year
&&_month == d._month
&&_day == d._day;
}
// >=运算符重载
bool operator >= (const Date& d)const{
return *this > d || *this == d;
}
// <运算符重载
bool operator < (const Date& d)const{
return !(*this >= d);
}
// <=运算符重载
bool operator <= (const Date& d)const{
return *this < d || *this == d;
}
// !=运算符重载
bool operator != (const Date& d)const{
return !(*this == d);
}
// 日期-日期 返回天数
int operator-(const Date& d)const{
Date max = *this;
Date min = d;
int flag = 1;
int count = 0;
if (*this < d){
flag = -1;
max = d;
min = *this;
}
while (max != min){
++count;
++min;
}
return (flag*count);
}
private:
int _year;
int _month;
int _day;//可以给缺省值,不是初始化,没有空间;
};
//成员函数this指针隐藏,默认第一个;流提取和流插入重载一般不写为成员函数;
void operator<<(ostream& out,const Date& d){
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}
void operator>>(istream& in, Date& d){
in >> d._year;
in >> d._month;
in >> d._day;
}
5.cont成员
const修饰类的成员函数
将const修饰的类的成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
void print()const;
void print();
可以同时存在,构成重载
认为const Datethis,Datethis是两种不同的类型,去找最匹配的
//读
const int&operator[](Date*this) const {
return _a[i];
}
//写
int&operator[](
return _a[i];
}
const对象不能调用非const成员函数-权限的放大
非const对象可以调用const成员函数-权限的缩小
const成员函数不能调用非const成员函数-权限的放大
非const成员函数可以调用const成员函数-权限的缩小
取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义,编译器默认生成
除非不想要取到有效地址
const Date*operator&()const{
return nullptr;
}
声明与定义分离,两边都需要加const