类与对象(中),咕咕咕
1.类的默认成员函数
默认成员函数没有显示实现,编译器会自动生成的成员函数为默认成员函数。
一个类,不写的情况下,编译器默认生成6个默认成员函数,C++11后增加2个成员函数,移动构造和移动赋值。(构造函数初始化工作,析构函数清理工作,拷贝构造用同类对象初始化创建对象,复制重载把一个对象赋值给另一个对象,普通对象取地址,const对象取地址)。
2.构造函数
构造函数是特殊的成员函数,主要任务并不是开空间创造对象(常使用的局部对象是栈帧创建时,空间就开好了),而是对象实例化时初始化对象,本质是替代Satck,Date等类中的init函数的功能,构造函数自动调用的特点可以替代Init。
构造函数的特点;
1.函数名与类名相同。
2.无返回值(不写void)
3.对象实例化时系统自动调用对应的构造函数
4.构造函数可以重载
5.如果类中没有显式敬意构造构造函数则C++编译器会自动生成一个无参的默认构造函数,一旦定义了,编译器不再生成。
6.无参构造函数,全缺省构造函数,编译器生成的默认构造函数,都叫做默认构造函数(不传实参),但是这3个函数有且只有一个存在,不能同时存在
7.编译器生成的默认构造函数对内置类型诚园变量的初始化没有要求(是否初始化不确定)。对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化,如果这个成员变量没有默认构造函数,要用初始化列表解决。
class Date
{无参构造函数
Date()
{
year=1;
month=1;
day=1;
}带参构造函数
Date(int year,int month,int day)
{
year=year;
month=month;
day=day;
}全缺省构造函数
/*
Date(int year=1,int month=1,int day=1)
{
year=year;
month=month;
day=day;
}
*/};
class myqueue
{
public:
系统默认生成的myqueue构造函数调用stack的构造函数(stack的构造要自己写)
private:
stack s1;
stack s2;
};
3.析构函数
析构函数不是完成对对象本身的销毁,局部对象是存在于栈帧的,函数结束栈帧销毁会自动释放,对象在销毁时会自动调用析构函数,完成对对象的清理工作。
析构函数的特点:
1.析构函数名在前面加~
2.无参数,无返回值(不要void)
3.一个类只能有一个析构函数,若未显式定义,系统自动生成
4.对象周期结束,系统自动调用析构函数
5.编译器自动生成的析构函数对内置类型成员不做处理,自定义类型成员函数会调用它的析构函数
6.显式写了析构函数,自定义类型成员也会调用它的析构
7.类中没有申请资源时,析构函数可以不写,有资源申请时必须写,不然会资源泄漏
8.后定义的对象先析构
class stack
{
private:
int *a;
size_t capacity;
size_t top;
public:
~stack()
{
free(a);
a=nullptr;
top=capacity=0;
}
};
4.拷贝构造函数
一个构造函数的第一个参数是自身类型的引用,且任何额外的参数都有默认值,则此构造函数叫拷贝构造函数
拷贝构造函数的特点:
1.拷贝构造函数是构造函数的一个重载
2.拷贝构造函数的第一个参数必须是类类型对象的引用·,使用传值方式编译器报错,因为语法上会无限递归,可以有多个参数,但是其他的参数必须有缺省值。
3.自定义类型对象进行拷贝行为必须调用拷贝构造,自定义类型传值传参,传值返回都会调用拷贝构造完成
4。若未显式定义,编译器自动生成,会浅拷贝(值拷贝,一个字节一个字节的拷贝)
5.对于内部申请资源的类对象,要自己写深拷贝(新申请空间,不然析构函数对同一片空间2次析构会崩)。一个类如果显示实现了构造并释放了资源,就要显式拷贝构造,否则没必要
6.传值返回会产生一个临时对象调用拷贝构造,传值引用返回,返回的是对象的别名(引用),没有产生拷贝。但是如果返回对象是一个当前函数局部域的局部对象,函数结束就销毁了,就不能用引用返回,野引用类似野指针,引用返回要确定返回对象在函数结束后仍然存在。
class date
{
public:
date(const date& d)
{
year=d.year;
month=d.month;
day=d.day;
}
};date d2(&d1); 只是普通的构造函数
date d2(d1); 拷贝构造
date d2=d1; 拷贝构造
5.运算符重载
类类型对象使用运算符时,必须调用对应运算符重载。
运算符重载是特殊名字的函数,operator和后面的运算符组成,有返回类型和参数,参数至少有一个类类型参数
它的参数和该运算符原来的参数个数一致(类外实现),在类内实现时,第一个参数是隐含的this,参数会少一个。
运算符重载后,优先级和结合性与对应的内置类型运算符一致。
不能用语法中没有的符号来重载重载哪些运算符要看他重载后有没有意义
.* :: sizeof ?: . 这5个符号不能重载
前置运算符和后置运算符,重载函数名都是operator++,后置运算符++重载是写作operator++(int),
用于区分。
重载<<和>>时,要重载为全局函数若是重载为成员函数,this指针默认占了第一个形参的位置,第一个形参位置是左侧运算对象,要写做<<cout,,重载为全局函数把istream/ostream放在第一形参位,另一个放类类型对象。
全局重载无法访问私有成员:
1.成员放公有
2.类提供getXXXXX函数
3.友元函数声明
4.重载为成员函数
赋值运算符重载用于完成2个已存在的对象直接的拷贝赋值,特点是:
1.必须重载为成员函数,参数建议写成const类类型引用,不会传参拷贝
2.有返回值,建议写成当前类类引用,提高效率,有返回值可以支持连续赋值
3.没有显式实现,系统会自动生成,只是值的赋值(类似浅拷贝的效果)
4.如果一个类显示实现了析构并释放了资源,就要显式的写赋值运算符重载,否则不用
举个例子,日期类的实现:
class date
{private:int day;int month;int year;public:int getmonthday(int year,int month){static int gugu[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};if((month==2)&&((year%400==0)||((year%4==0)&&(year%100!=0)))){return 29;}else{return gugu[month];}}//全缺省的构造函数date(int year=1,int month=1,int day=1){year=year;month=month;day=day;}//拷贝构造函数date(const date& d){year=d.year;month=d.month;day=d.day;}//赋值运算符重载date& operator=(const date& d){if(this!=&d){year=d.year;month=d.month;day=d.day;}return *this;}//析构函数~date(){year=0;month=0;day=0;}//日期+=天数date& operator+=(int day){this->day+=day;while(this->day>getmonthday(year,month)){this->day-=getmonthday(year,month);month++;if(month==13){year++;month=1;}}return *this;}//日期+天数date operator+(int day){date tmp(*this);tmp+=day;return tmp;}//日期-=天数date& operator-=(int day){this->day-=day;while(this->day<1){month--;if(month==0){year--;month=12;}this->day+=getmonthday(year,month);}return *this;}//日期-天数date operator-(int day){date tmp(*this);tmp-=day;return tmp;}//前置++date& operator++(){*this+=1;return *this;}//后置++date operator++(int){date tmp(*this);*this+=1;return tmp;}//前置--date& operator--(){*this-=1;return *this;}//后置--date operator--(int){date tmp(*this);*this-=1;return tmp;}//>重载bool operator>(const date& d){if(year>d.year){return true;}else if(year==d.year){if(month>d.month){return true;}else if(month==d.month){return day>d.day;}}else{return false;}}//==重载bool operator==(const date& d){return (year==d.year)&&(month==d.month)&&(day==d.day);}//>=重载bool operator>=(const date& d){return (*this>d)||(*this==d);}//<重载bool operator<(const date& d){return !(*this>=d);}//!=重载bool operator!=(const date& d){return !(*this==d);}//<=重载bool operator<=(const date& d){return !(*this>d);}//日期-日期int operator-(const date& d){// 复制两个日期,避免修改原对象date minDate = *this;date maxDate = d;int days = 0;bool isNegative = false;// 判断哪个日期更早,确定计数方向if (*this < d) {minDate = *this;maxDate = d;isNegative = true; // 当前日期更早,结果为负数} else {minDate = d;maxDate = *this;}// 从较早日期开始,一天天累加直到等于较晚日期while (minDate != maxDate) {++minDate; // 调用前置++运算符(假设已实现)++days;}// 根据日期先后返回正负号的天数return isNegative ? -days : days;}
};