前情回顾:
类和对象(上篇)
类和对象(中篇)
上两篇已经讲了类和对象的基本使用和最难理解的默认成员函数,接下来将继续深入学习类和对象剩余的内容。
为了自定义类型可以自己使用各种运算符,边引入了运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
#include
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(Date d2)
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 5, 16);
Date d2(d1);
//可以这样写 - 这样写和使用函数一样
//if (d1.operator==(d2))
//更多是下面这样用
if (d1 == d2)//编译器会处理成对应调用if (operator==(d1, d2))
{
cout << "==" << endl;
}
return 0;
}
注意:
#pragma once
#include
#include
using std::cout;
using std::cin;
using std::endl;
class Date
{
//友元函数
friend std::ostream& operator<<(std::ostream& out, const Date& d);
friend std::istream& operator>>(std::istream& in, Date& d);
public:
bool isLeapYear(int year)
{
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int GetMonthDay(int year, int month);
Date(int year = 1, int month = 1, int day = 1);
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;
}
bool operator==(const Date& d) const;
bool operator<(const Date& d) const;
Date operator+(int day) const;
Date& operator+=(int day);
Date operator-(int day) const;
Date& operator-=(int day);
//C++规定有参数的是后置的,无参数的是前置的
Date& operator++() //前置
{
*this += 1;
return *this;
}
Date operator++(int i) //后置 - 不能加缺省值,因为不传参的时候分不清楚,或者不加参数只写类型
{
Date tmp(*this);
*this += 1;
return tmp;
}
//d1 - d2
int operator-(const Date& d) const;
//inline不支持声明和定义分别放到.h 和 .cpp
//所以成员函数中要称为inline最好直接在类里面定义
//类里面定义默认是inline
bool operator>(const Date& d) const
{
//return d < *this;
return !(*this <= d);
}
bool operator>=(const Date& d) const
{
return !(*this < d);
}
bool operator!=(const Date& d) const
{
return !(d == *this);
}
//d1 <= d2
bool operator<=(const Date& d) const
{
return *this < d || *this == d;
}
//建议成员函数中不修改成员变量的成员函数,都可以加上const
//普通对象和const对象都可以调用
void Print() const//等价于void Print(const Date* const this) - 保护指针指向的内容
{
cout << _year << "-" << _month << "-" << _day << endl;
}
Date* operator&()
{
return this;
}
//const取地址重载,返回的是一个const Date*的指针
const Date* operator&() const
{
return this;
}
/*void operator<<(std::ostream& out)
{
out << _year << "-" << _month << "-" << _day << endl;
}*/
/*void operator>>(std::istream& in)
{
}*/
private:
int _year;
int _month;
int _day;
};
#include"Date.h"
//成员函数在类里面声明在类外面定义了 - 也是类里面的函数
//所以是可以访问私有的,因为这也算是类里面的成员函数
//写一个小于等于或者是大于等于其他的都可以复用了
bool Date::operator<(const Date& d) const
{
if (_year < d._year || _year == d._year && _month < d._month
|| _year == d._year && _month == d._month && _day < d._day)
{
return true;
}
else
{
return false;
}
}
//d1 == d2
bool Date::operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
int Date::GetMonthDay(int year, int month)
{
assert(year >= 0 && month > 0 && month < 13);
//加上static的好是避免平凡开辟数组空间
static int monthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && isLeapYear(year))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
Date::Date(int year, int month, int day)
{
if (year >= 1 &&
month <= 12 && month >= 1 &&
day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
d1 + 100 -->d1.operator(day);
d1被改变了,d1是不应该被改变的
//Date Date::operator+(int 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 ret(*this);
/*ret._day += day;
while (ret._day > GetMonthDay(ret._year, ret._month))
{
ret._day -= GetMonthDay(ret._year, ret._month);
ret._month++;
if (ret._month == 13)
{
ret._year++;
ret._month = 1;
}
}*/
ret += day;
//出了作用域这个ret对象就不在了
return ret;
}
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 ret = *this;
ret -= day;
return ret;
}
//用传值返回一定是对的,但是不一定是高效的
Date& 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;
}
//d1 - d2
int Date::operator-(const Date& d) const
{
int flag = 1;
Date max = *this;
Date min = d;
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max)
{
n++;
min++;
}
return n * flag;
}
//流插入
std::ostream& operator<<(std::ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
//流提取
std::istream& operator>>(std::istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
cout 和 cin其实是全局的对象,包含在iostream这个头文件中,库中实现的时候就将其重载了
istream和ostrem的对象,还用到了运算符重载
将cout符号重载写成全局的原因:
能传引用的地方尽量传引用,能返回引用的地方也尽量返回引用,可以提高效率
补充:
int main()
{
Date d1(2022, 5, 18);
//Date d2 = d1 + 15;
Date d3 = d1; //这里调用的是拷贝构造,并不是赋值重载Date d3(d1)
//拷贝构造是用一个对象去初始化同类型的对象
//d1 = d2;//两个已经存在的对象才是赋值
d1 += 15;
d1.Print();
//d2.Print();
return 0;
}
Date d3 = d1; 这里并不是赋值运算符的重载,而是调用拷贝构造
拷贝构造是用一个对象初始化同类型的对象
**d1 = d2;**这才是调用负值重载,对于两个已经存在的对象才是调用赋值重载
正确的做法应该是将this的指针用const修饰,但是又因为this指针是隐式的所以C++新增了一个语法就是在成员函数后面直接加上const,表示给this指针的类型修改成 :const 类类型* const this
补充:
如果声明和定义分离,那么声明和定义都要加上const。
补充:
int a = 10;
int* const pa = &a;
int* pb = pa;
上述写法是允许的,只是赋值。
int a = 10;
const int* pa = &a;
int* pb = pa;
上述写法是不允许的,因为&a是int* 类型的指针,但是const int*类型的指针是不能通过指针修改指针指向的内容的,两者是有区别的。
如果传参的值是不希望被修改的就要考虑加上const加以保护,同时还要考虑连续运算符中返回值产生临时变量的问题,也是const该用到的地方。
1.每一个对象都有自己的空间,成员变量是属于某个对象的,对象是整体定义的,而里面的成员变量是什么时候定义的呢?
2.再次理解构造函数:
3.必须在定义的时候初始化的值:
注意:类中包含以上成员,必须放在初始化列表位置进行初始化
初始化列表可以认为就是对象成员变量定义的地方,并且尽量要在初始化列表初始化。
int value = 1;
class B
{
public:
B(int a = 0)
{
_a = a;
}
private:
int _a;
};
class A
{
public:
A()
:_b(2)
,bb(1)
,_ref(value)
{
_a = 1;
}
private:
//可以定义时初始化
//也可以定义时不初始化,后面再赋值修改
int _a;
//下面的只能在定义的时候初始化
int& _ref;
const int _b;
B bb;
};
int main()
{
A aa;
return 0;
}
如下代码:
如果写了默认构造但是却不对自定义类型处理,但是调试的过程中会发现,编译器还是对内置类型进行了处理。
为什么显式写的构造函数中没有写自定义类型的构造函数,但是还是对自定义类型进行处理了呢?
class Stack
{
public:
Stack(int capacity = 0)
{
_a = (int*)malloc(sizeof(int) * capacity);
assert(_a);
_top = 0;
_capacity = capacity;
}
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue
{
MyQueue()
{}
private:
Stack _st1;
Stack _st2;
int _size = 0; //缺省值,给初始化列表用的
};
int main()
{
MyQueue mq;
return 0;
}
面试题:实现一个类,计算程序中创建出了多少个类对象。
class A
{
public:
A()
{
++_scount;
}
A(const A& t)
{
++_scount;
}
~A()
{
--_scount;
}
static int GetACount()
{
return _scount;
}
private:
//声明
static int _scount;
};
//定义
int A::_scount = 0;
int main()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
return 0;
}
int A::_scount = 0:
只是全局的静态他的作用域是全局的,在全局任何地方都能使用
局部的静态只能在函数里面使用,类的静态,只能在类里面使用,或者说指定类域,去类域里面找可以访问。
总结:
#include
using namespace std;
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d) {
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d) {
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
注意:
友元关系不能传递: