目录
一.流插入,流提取运算符
二.const成员函数
三.取地址重载
四.构造函数(列表初始化)
小测试:
五.全部代码
前排提醒:本文所参考代码仍是取用上篇文章关于日期类相关功能的实现~末尾有全部代码~
一.流插入,流提取运算符
为什么流插入cout不能对类使用呢?
因为d1是自定义类型,需要有相关的流提取运算符重载函数,而函数Print中cout是对内置类型作处理,有默认的成员函数。
如果运算符重载函数这样写,那么图中第一种写法会报错,第二种反而不会~
小细节:ostream不用加const,因为是它是流插入,肯定是会变化的。
因为双操作数的运算符是有规定顺序的,第一参数为左操作数,第二参数为右操作数。这里第一操作数给了this指针,这样肯定是不行的。
要想避免这种情况,我们只能把该函数写到全局中去,这样才能设置两个参数。
这样还有两个问题:一是关于私有成员的访问(_year等),另一个是诸如(cout<<d1<<d2)这种连续赋值得有返回值。
ostream& operator<<(ostream& out,const Date& d) { out << d._year << "年" <<d._month << "月" << d._day << "日" << endl; return out; }
在声明的类里面设置友元函数就可以去访问了,类外在额外放一份声明。
流提取也一样,唯一不同的是流提取是把内容放在Date里,所以这里不加const。
istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
二.const成员函数
如果我们在定义时给d1加了const,那么d1的类型就会发生改变,由于实参是&d1那么类型就变为const Date* ,而在Print函数中的隐藏this指针的类型是Date*。所以实参与形参之间存在权限放大问题,造成报错。
要想解决该问题这里只能让Print处的形参权限缩小,在C++规定中我们是这样处理的:使得形参与实参之间实现权限平移。
我们再重新定义对象d2可以看出,实参类型为Date* ,形参类型为const Date*,二者是一个权限缩小的过程,也可以正常进行。
const成员函数中const对象与非const对象都可以调用const成员函数,当然const成员函数也并不是能够全部应用,遇到需要修改this指针指向的函数就无法使用const成员函数(例如+=,-=),还有流插入与流提取(因为它们在全局中,没有隐藏的this指针)。
三.取地址重载
Date* Date::operator&() { cout << "Date* operator&()" << endl; return this; } const Date* Date::operator&()const { cout << "const Date* operator&()const" << endl; return this; }
二者都是默认成员函数,写不写编译器都会默认生成的。
同理d2其实两个重载都可以调用,只不过会优先调用最匹配的~
四.构造函数(列表初始化)
class A { public: A(int a = 0) :_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; } private: // 声明 int _year; int _month; int _day; int& _ref; const int _n; A _aa; }; } int main() { bit::Date d1(2023, 10, 31); return 0; }
列表初始化必须存在的意义是它可以初始化在函数体内无法初始化的变量,例如const,引用,自定义类型。
对于const与引用而言,它们是必须要在定义的时候初始化。(必须得先有一个实体空间,后面才能初始化)
这里只是声明,空间只能在对象定义时才开
怎么说呢~bit::Date d1(2023, 10, 31);这里是对象整体定义(房子的轮廓修建,例如房子各个设施修缮的时间,程度,布局等等都没有涉及)。
而能让每个成员都知道在什么地方定义(知道修缮时间,布局)只有靠初始化列表(之所以是每个是因为有三种成员无法在函数体内初始化),除这3个成员之外的其实可以在函数体内那定义。
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。Date(int year, int month, int day) //列表初始化 :_year(year) ,_month(month) ,_day(day) , _ref(year) ,_n(1) ,_aa(10) { // 函数体内初始化 /*_year = year; _month = month; _day = day;*/ //_ref = year; //_n = 1; }
列表初始化与函数体内初始化也可以混用
默认构造函数(全缺省,无实参,默认生成).
在列表初始化那对内置类型年月日定义并且给了随机值(因为没有写),对自定义成员_aa则是去调用它的构造函数(如果无默认构造呢?让int a = 0变为int a——报错),不过年月日随机值在后面的函数体内初始化还是会初始化为我们所给的值。
所以针对无默认拷贝构造的自定义类型,const与引用这三者必须放在列表初始化里。
只不过混用并不是指这种特地把年月日放在函数体内初始化而不是列表初始化。
函数体内初始化还是有一些好处的,比如可以检查开辟的空间是否出错或是把内容拷贝给数组
因此我们尽量使用列表初始化,毕竟函数体内初始化处理不了自定义,const与引用。
反正说来说去就一点,对自定义类型而言如果不用或没有默认构造,那就在列表初始化那里老老实实给值。对内置类型而言,你可以选择在声明那给缺省值,也可以在列表初始化那给值,否则最终就是随机值。
小测试:
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(); }
最终输出结果为1 ,随机值。
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关所以是先初始化_a2然后才是_a1。
五.全部代码
//test.cpp
//test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include "Date.h"
class A
{
public:
A(int a=0)
:_a(a)
{
cout << "A(int a = 0)" << endl;
}
private:
int _a;
};
namespace bit
{
class Date
{
public:
Date(int year, int month, int day)
//列表初始化
: _ref(year)
,_n(1)
,_year(year)
{
// 函数体内初始化
_year = year;
_month = month;
_day = day;
//_ref = year;
//_n = 1;
}
private:
// 声明
int _year;
int _month;
int _day;
int& _ref;
const int _n;
A _aa;
};
}
//int main()
//{
//
// bit::Date d1(2023, 10, 31);
//
// return 0;
//}
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();
}
//Date.cpp
//Date.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"
//构造初始化
Date::Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
if (_year < 1 || _month < 1 || _month>12 || _day<1 || _day>GetMonthDay(_year, _month))
{
Print();
cout << "日期违法" << endl;
}
}
//打印
void Date::Print() const
{
cout << _year << "--" << _month << "--" << _day << endl;
}
//类与类拷贝构造
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
31 };
int day = days[month];
if (month == 2
&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
day += 1;
}
return day;
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= (-day);
}
//复用+
/**this = *this + day;
return *this;*/
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day)const
{
//复用+=
Date tmp(*this);
tmp += day;
return tmp;
/*Date tmp(*this);
tmp._day += day;
_day += day;
while (_day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day -= GetMonthDay(tmp._year, tmp._month);
tmp._month++;
if (tmp._month > 12)
{
tmp._year++;
tmp._month = 1;
}
}
return tmp;*/
}
// >运算符重载
bool Date:: 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 Date::operator==(const Date& d)const
{
if (_year == d._year && _month == d._month && _day == d._day)
{
return true;
}
return false;
}
// !=运算符重载
bool Date::operator != (const Date& d)const
{
return !(*this == d);
}
// >=运算符重载
bool Date::operator >= (const Date& d)const
{
return (*this == d) || (*this > d);
}
// <运算符重载
bool Date::operator<(const Date& d)const
{
return !(*this >= d);
}
// <=运算符重载
bool Date::operator <= (const Date& d)const
{
return !(*this > d);
}
// 日期-=天数
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++()
{
*this += 1;
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
// 后置--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
// 前置--
Date& Date:: operator--()
{
*this -= 1;
return *this;
}
// 日期-天数
Date Date::operator-(int day)const
{
Date tmp(*this);
tmp -= day;
return tmp;
}
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
int flag = 1;
int n = 0;
Date max = *this;
Date min = d;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
while (max != min)
{
min++;
n++;
}
return n * flag;
}
Date& Date::operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
}
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;
}
Date* Date::operator&()
{
cout << "Date* operator&()" << endl;
return this;
}
const Date* Date::operator&()const
{
cout << "const Date* operator&()const" << endl;
return this;
}
void Date::operator<<(ostream& out)
{
out << _year << "年" << _month << "月" << _day << "日" << endl;
}
//Date.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
//构造初始化
Date(int year, int month, int day);
//打印
void Print()const;
//类与类拷贝构造
Date(const Date& d);
Date& operator=(const Date& d);
//获取某年某月的天数
int GetMonthDay(int year, int month);
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day)const;
// 日期-天数
Date operator-(int day)const;
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
bool operator>(const Date& d)const;
// ==运算符重载
bool operator==(const Date& d)const;
// >=运算符重载
bool operator >= (const Date& d)const;
// <运算符重载
bool operator <(const Date& d)const;
// <=运算符重载
bool operator <= (const Date& d)const;
// !=运算符重载
bool operator != (const Date& d)const;
// 日期-日期 返回天数
int operator-(const Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
const Date* operator&()const;
Date* operator&();
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out,const Date& d);
istream& operator>>(istream& in, Date& d);