首先,了解三种访问限定符:
- public成员可从类外部直接访问,private/protected成员不能从类外部直接访问。
- 每个限定符在类体中可使用多次,它的作用域是从该限定符出现开始到下一个限定符之前或类体结束前。
- 类体中如果没有定义限定符,则默认为私有的。
- 类的访问限定符体现了面向对象的封装性。
类实例化对象:
成员函数的声明和定义
1.类内定义成员函数
class Person
{
public :
void Display ()
{
cout<<_name <<"-"<< _sex<<"-" <<_age<< endl;
}
public :
char* _name; // 名字
char* _sex; // 性别
int _age; // 年龄
2.类外定义成员函数
class Person
{
public :
void Display ();
public :
char* _name; // 名字
char* _sex; // 性别
int _age; // 年龄
};
void Person::Display ()
{
cout<<_name <<"-"<< _sex<<"-" <<_age<< endl;
}
类实例化对象
1.类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
2.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间存储类成员变量。
3.做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑,存在,同样的类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
类对象存储模型
每个对象的大小为类中所有成员变量的大小之和,不包括成员函数,因为每个对象都要调成员函数,成员函数在一个公共代码区。遵循内存对齐原则。
对齐规则和为什么对齐,参考这篇博客:https://blog.csdn.net/sophia__yu/article/details/79485021
空类对象(无成员变量的类)的大小是多少?为什么?
是1,占位表示存在。
日期类:
this指针:
构造函数:
成员变量是私有的,要对他们进行初始化,必须用一个公有函数来进行
同时这个函数有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数
构造函数是特殊的默认成员函数,特征如下:
1.函数名与类名相同
2.无返回值
3.对象实例化时系统自动调用对应的构造函数
4.构造函数可以重载
5.构造函数可以在类中定义也可以在类外定义
6.如果类定义中没有给构造函数,则c++编译器自动产生一个缺省(无参调用)的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数
7.无参的缺省函数和全缺省的构造函数都认为是缺省构造函数,但缺省的构造函数只能有一个。
析构函数
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数
析构函数是特殊的默认成员函数,特征如下:
1.析构函数函数名在类名前加上字符~
2.析构函数无参数无返回值
3.一个类有且仅有一个析构函数。若类内未定义析构函数,系统会自动生成缺省的析构函数
4.对象生命周期结束时,系统会自动调用析构函数
5.析构函数并不是删除对象,而是完成一些清理工作(free、fclose)
拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造的函数为拷贝构造函数,
拷贝构造函数是特殊的默认的成员函数,特征如下:
1.拷贝构造函数其实是一个构造函数的重载
2.拷贝构造函数的参数必须用引用传参,使用传值方式可能会引发无穷递归调用。不用引用做参数:因为实参要拷给形参,就要将对象实参重新拷贝一个对象,就要调用拷贝构造函数,那么就一直在传参,就会无限递归
3.若类内没有拷贝构造函数,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝类成员进行初始化
class Array
{
public:
Array(int size)//构造函数
{
_ptr = (int *)malloc(sizeof(int)*size);
}
//析构函数
~Array()
{
free(_ptr);//释放动态申请空间
}
private:
int *_ptr;
};
int main()
{
Array a1(10);
Array a2(a1);//将a1拷给a2
a2.~Array();
a1.~Array();//会触发断点,即崩溃
//因为将对象a1拷给对象a2,拷贝类成员即将对象a1指针ptr拷给了a2指针ptr,两个指针相同,但析构函数时free了两次,所以崩溃
//尽管当类内没有定义值拷贝构造函数,系统有默认缺省值拷贝值拷贝函数,但我们最好在类内给出值拷贝函数定义
system("pause");
return 0;
}
运算符重载
特征:(和函数重载无关)
1.operator+合法运算符构成函数名 (如:operator< )
2.重载运算符后不能改变运算符的优先级/结合性/操作符个数
3.注:5个不能重载的运算符: 1.(.*) 2.(:? 3.(sizeof) 4.(?.) 5.(.)
赋值运算符重载:默认成员函数
赋值运算符重载和拷贝构造函数区别:
拷贝构造函数是创建的对象,使用一个已有的对象来初始化这个准备创建的对象
赋值运算符重载:对一个已存在的对象进行拷贝赋值
**日期类代码如下:**
#include<iostream>
#include<assert.h>
using namespace std;
class Data
{
public:
void show()
{
cout << _year << '-' << _month << '-' << _day << endl;
}
int GetMonthDay(int year, int month)//返回该年月的最大天数
{ //因为闰年的2月29,平年28天
assert(year > 0 && month > 0 && month<13);
int Month[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//12个月的天数
int day = Month[month];//直接得到天数
if (month == 2 &&
((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))//判断是否是闰年
day++;
return day;
}
void SetData(int year, int month, int day) //设置日期
{
if (year > 0 && month < 13 && month>0 &&
GetMonthDay(year, month) >= day&&day > 0)
{
_year = year;
_month = month;
_day = day;
}
else
cout << "非法日期" << endl;
}
//Data()//无参构造函数
//{
// _year = 1998;
// _month = 1;
// _day = 1;
//}
//无参构造函数和全缺省构造函数只能有一个
Data(int year = 1998, int month = 1, int day = 1)//全缺省构造函数
{
if (year > 0 && month < 13 && month>0 &&
GetMonthDay(year, month) >= day&&day > 0)
{
_year = year;
_month = month;
_day = day;
}
else
cout << "非法日期" << endl;
}
/*Data(int year, int month, int day)//带参构造函数
{
_year = year;
_month = month;
_day = day;
}*/
~Data()//析构函数 栈先进后出,先构造的对象后析构
{
//cout << "析构函数" << endl;
}
Data(const Data& d)//拷贝构造函数,必须引用做参数,尽量用const,不可修改
{ //不用引用做参数:因为实参要拷给形参,就要将对象实参重新拷贝一个对象,就要调用拷贝构造函数,那么就一直在传参,就会无限递归
//有引用传参,形参实参共用一段空间,就不存在实参拷给形参
//两个参数(Data *this,const Data &d)
_year = d._year;//this->_year = d._day;
_month = d._month;//this->_month = d._month;
_day = d._day;//this->_day = d._day;
//cout << "值拷贝" << endl;
}
//=运算符重载
Data& operator=(const Data& d)//不用引用,会调一次拷贝构造,返回对象出来作用域依然存在,最好用引用返回
{ //即Data& operator =(Data* this, const Data& d)
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
//>运算符重载
bool operator>(const Data& d)
{
if (_year > d._year)
return true;
if (_year==d._year && _month > d._month)
return true;
if (_year == d._year&&_month == d._month&&_day > d._day)
return true;
else
return false;
}
//==运算符重载
bool operator==(const Data& d)
{
if (_year == d._year && _month == d._month && _day == d._day)
return true;
else
return false;
}
//>=运算符重载
bool operator>=(const Data& d)
{
if (*this > d || *this == d)//大于或者等于
return true;
else
return false;
}
//<运算符重载
bool operator<(const Data& d)
{
if (!(*this >= d))//大于等于取反即小于
return true;
else
return false;
}
//<=运算符重载
bool operator<=(const Data& d)
{
if (!(*this > d))//大于取反即小于等于
return true;
else
return false;
}
bool operator!=(const Data& d)
{
if (!(*this == d))
return true;
else
return false;
}
//+运算操作符
Data operator+(int day)
{
Data newd = *this;//拷贝构造 必须定义一个新对象,否则会改变*this
//2018 6 8 +(-50)=2018 6 8 -50
if (day < 0)
{
return newd - (-day);//直接调用-运算操作符
}
newd._day += day;
if (GetMonthDay(newd._year, newd._month) >=newd._day)//天数合法
return newd;
else
{
while (GetMonthDay(newd._year, newd._month) < newd._day)
{
newd._day = newd._day - GetMonthDay(newd._year, newd._month);//本月最大天数
newd._month += 1;
if (newd._month == 13)
{
newd._month -= 12;
newd._year += 1;
}
}
return newd;
}
}
//+=运算操作符
Data& operator+=(int day)
{
*this = *this + day;//直接调用+运算操作符
return *this;
}
//-运算操作符
Data operator-(int day)
{
Data newd = *this;//拷贝构造
//2018 6 8 -(-50)=2018 6 8 +50
if (day < 0)
return newd + (-day);//调用+运算操作符
newd._day-= day;
if ( newd._day>0 )
return newd;
else
{
while (newd._day <= 0)
{
newd._month--;//从上月借天数,需要先将month-1
if (newd._month == 0)
{
newd._month += 12;
newd._year -= 1;
assert(newd._year > 0);
}
newd._day += GetMonthDay(newd._year, newd._month);//天数是负值
}
return newd;
}
}
//-=运算操作符
Data& operator-=(int day)
{
*this = *this - day;//直接调用-运算操作符
return *this;
}
//后置++运算操作符
Data operator++(int) //后置++ 编译器自己规定有int 是后置++
{
Data ret = *this;
*this += 1;//直接调用+=
return ret;
}
//前置++运算操作符
Data& operator++()//前置++
{
*this += 1;
return *this;
}
//后置--操作运算符
Data operator--(int)//后置--
{
Data ret = *this;
*this -= 1;//直接调用-=
return ret;
//前置--操作运算符
Data& operator--() //前置--
{
*this -= 1;//直接调用-=
return *this;
}
//日期—日期
//小的日期一直+1直到两者相等,加了多少次即为多少天
int operator-(const Data &d)
{
int flag = 1;
Data max = *this;
Data min =(Data) d;
if (*this < d)
{
max =(Data)d;
min = *this;
flag = -1;
}
int day = 0;
while (min < max)
{
min += 1;//调用+=,+=调用+
day++;
}
return day*flag;
}
private://私有,类外部不能访问
int _year;
int _month;
int _day;
};
int main()
{
Data d1(1999, 1, 1);//调用带参构造函数 d1先构造
Data d2;//调用无参或全缺省构造函数 d2后析构
Data d3(d1);//或者Data d3 = d1; 两者都为值拷贝构造函数 d1拷给d3
Data d4;
d4.operator=(d1);//赋值运算符重载
d4 = d1;//赋值运算符重载
//d1.show();
d1.SetData(2018, 2,4 );
//d1.SetData(2018, 2, 28);
Data d5(2018, 6, 28);
//+ +=
Data d6 = d5 + (12000);
d6.show();
d5 += (62000);
d5.show();
// - -=
Data d7 = d1 - (62000);
d7.show();
d1 -= (62000);
d1.show();
/*d1.show();
d3.show();
d4.show();*/
Data d8;
d8.SetData(2018, 10, 19);
Data d0 = d8++;
d8.show();
d0.show();
Data d9;
d9.SetData(2018, 10, 19);
++d9;
d9.show();// 2018 10 20
d9.SetData(2018, 10, 30);
d8.SetData(2050, 5, 3);
cout << d9 - d8 << endl;
system("pause");
return 0;
}
注: