📪1.const成员
📪将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
🎈首先我们来想一想为什么在C++中我们的输出流能自动识别类型呢?
🎈这里给大家分享一个文档以及我给大家查到的知识
🎈点击查看官方C++库
🎈C++委员会已经帮我们写好的C++库 我们只需要写很短小的一截就可以自动识别类型 这是因为函数重载 类型匹配 函数名修饰规则的原因
所以我们标准的cout重载应该按照以下的语法规则书写
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
📟输入的语法规则则为
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
📮见下图能否帮助你理解一下
🎓下面给大家总结一下
🎓运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)
🎓注意:
不能通过连接其他符号来创建新的操作符:比如operator
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现
🎓*注意:.不会涉及 在后面的笔试题中可能会出现 在实际的工作中基本不会遇见
🔫2.取地址及const取地址操作运算符重载
🎾下面我们正式进入今天的主题
来看下面两段代码
// const对象和非const对象都可以调用const成员函数
// const Date d1(2023, 10, 31);
// d1.Print();
//
// Date d2(2023, 1, 1);
// d2.Print();
📢首先要记住一点的就是我们在前面已经讲过了就是权限可以平移和缩小但是不能放大
🔫d1里面传的本质是d1的地址也就是const Date*
而函数调用的参数里面却又是Date* this 所以这里是权限的放大 我们需要添加一个const 那么问题又来了 const加到哪里合适呢?看上面图片和代码
📲只要是不修改成员的日期类对象都可以加上const 这样让我们的const和非const成员都可以传过来 看下面代码
#include "Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (_year < 1 ||
_month < 1 || _month > 12 ||
_day < 1 || _day > GetMonthDay(_year, _month))
{
//assert(false);
Print();
cout << "日期非法" << endl;
}
}
void Date::Print() const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
bool Date::operator==(const Date& y) const
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
// d1 != d2
bool Date::operator!=(const Date& y) const
{
return !(*this == y);
}
bool Date::operator>(const Date& y) const
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
bool Date::operator>=(const Date& y) const
{
return *this > y || *this == y;
}
bool Date::operator<(const Date& y) const
{
return !(*this >= y);
}
bool Date::operator<=(const Date& y) const
{
return !(*this > y);
}
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12);
int monthArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
return 29;
return monthArray[month];
}
// d1 += 100
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= (-day);
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day) const
{
Date tmp(*this);
tmp += day;
return tmp;
}
//
// d1 += 100
//Date& Date::operator+=(int day)
//{
// //Date d = *this + day;
// //*this = d;
//
// *this = *this + day;
// return *this;
//}
//
//Date Date::operator+(int day)
//{
// Date tmp(*this);
//
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._month))
// {
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
//
// ++tmp._month;
//
// if (tmp._month == 13)
// {
// tmp._year++;
// tmp._month = 1;
// }
// }
//
// return tmp;
//}
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date tmp(*this);
tmp -= day;
return tmp;
}
// ++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
// d1 - d2
int Date::operator-(const Date& d) const
{
// 假设左大右小
int flag = 1;
Date max = *this;
Date min = d;
// 假设错了,左小右大
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
//void Date::operator<<(ostream& out)
//{
// out << _year << "年" << _month << "月" << _day << "日" << endl;
//}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
🚿思考下面问题
📲1. const对象可以调用非const成员函数吗?
📲2. 非const对象可以调用const成员函数吗?
📲解析:能定义成const的成员函数都应该定义成为const 这样对const对象和非const成员来说都可以调用 要修改成员变量的成员函数不能定义成const
📲上述问题归根结底还是我们的权限的放大缩小和平移的问题
//class A
//{
//public:
// A(int a)
// :_a(a)
// {
// cout << "A(int a = 0)" << endl;
// }
//private:
// int _a;
//};
//
//namespace bit
//{
// class Date
// {
// public:
// //Date(int year, int month, int day)
// //{
// // // 函数体内初始化
// // _year = year;
// // _month = month;
// // _day = day;
//
// // //_ref = year;
// // //_n = 1;
// //}
//
// //Date(int year, int month, int day)
// // :_year(year)
// // ,_month(month)
// // ,_day(day)
// // ,_ref(year)
// // ,_n(1)
// //{
// // // 初始化列表
// // //
// //}
//
// Date(int year, int month, int day)
// :_year(2)
// ,_ref(year)
// ,_n(1)
// ,_aa(10)
// {
// // 剩下3个成员没有在初始化列表显示写出来定义
// // 但是他也会定义,只是内置类型默认给的随机值
// // 如果是自定义类型成员会去调用它的默认构造函数
//
// // 函数体内初始化
// _year = year;
// _month = month;
// _day = day;
// }
//
// private:
// // 声明
// int _year = 1; // 缺省值
// int _month = 1;
// int _day;
//
// A _aa; // 定义类型成员(且该类没有默认构造函数时)
//
// int& _ref; // 引用 : 必须在定义的时候初始化
// const int _n; // const : 必须在定义的时候初始化
// };
//}
// 初始化列表解决的问题:
// 1、必须在定义的地方显示初始化 引用 const 没有默认构造自定义成员
// 2、有些自定义成员想要显示初始化,自己控制
// 尽量使用初始化列表初始化
// 构造函数能不能只要初始化列表,不要函数体初始化
// 不能,因为有些初始化或者检查的工作,初始化列表也不能全部搞定
// 80-100%初始化列表搞定,还有需要用函数体,他们可以混着用
class Stack
{
public:
Stack(int n = 2)
:_a((int*)malloc(sizeof(int)*n))
,_top(0)
,_capacity(n)
{
//...
//cout << "Stack(int n = 2)" << endl;
if (_a == nullptr)
{
perror("malloc fail");
exit(-1);
}
memset(_a, 0, sizeof(int) * n);
}
//...
int* _a;
int _top;
int _capacity;
};
📲上述代码给大家写了一下初始化列表和函数体的运用规则 对于const成员和引用成员必须在初始化列表初始化 不能再函数体内部初始化 初始化列表也可以运用函数 比如malloc 然后在函数体内判断是否为NULL 在进行memset操作
📲来看下面的一道面试题
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2;
int _a1;
};
int main()
{
A aa(1);
aa.Print();
}
🎒根据声明的先后顺序 因此我们这里的_a1=1 _a2是随机值
🎒下篇给大家讲解类和对象下篇 到那是我们就正式结束了我们的类和对象 我们就要正式进入内存管理和模板以及STL的学习了