欢迎光临
我们一直在努力

C++之类和对象(中)

1.类的默认成员函数

⼀个类,我 们不写的情况下编译器会默认⽣成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,,现阶段我们先学前四个。构造函数,析构函数,拷贝构造函数,赋值重载。

2.构造函数

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并 不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化 对象。

构造函数的特点:

1. 函数名与类名相同。

2. ⽆返回值。 (返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)

3. 对象实例化时系统会⾃动调⽤对应的构造函数。

4. 构造函数可以重载。

5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显 式定义编译器将不再⽣成。

#include"maomao.h"
class Date {
public:
//函数完成初始化
void Init(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
//构造函数完成初始化
Date() {
_year=2014;
_month=3;
_day=19;
}
void Print() {
cout << this->_year << "-"
<< this->_month << "-"
<< this->_day<< endl;
}
private:
//私有实现代码
int _year;
int _month;
int _day;
};
int main() {
Date d1;
Date d2;
d1.Init(2012, 12, 8);
d1.Print();
d2.Print();

return 0;
}

#include"maomao.h"
class Date {
public:
//函数完成初始化
void Init(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
//构造函数完成初始化
Date() {
_year=2014;
_month=3;
_day=19;
}
Date(int x) {
_year = 2026;
_month = 3;
_day = 19;
}
void Print() {
cout << this->_year << "-"
<< this->_month << "-"
<< this->_day<< endl;
}
private:
//私有实现代码
int _year;
int _month;
int _day;
};
int main() {
Date d1;
Date d2;
Date d3(3);
d1.Init(2012, 12, 8);
d1.Print();
d2.Print();
d3.Print();

return 0;
}

⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函数。⼀旦⽤⼾显式定义构造函数编译器将不再⽣成构造函数。

1.不写构造

#include"maomao.h"
class Date {
public:
//函数完成初始化
void Init(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << this->_year << "-"
<< this->_month << "-"
<< this->_day<< endl;
}
private:
//私有实现代码
int _year;
int _month;
int _day;
};
int main() {
Date d1;
Date d2;
d1.Init(2012, 12, 8);
d1.Print();
d2.Print();

return 0;
}

2.⽆参构造函数、全缺省构造函数

还有要注意的一点是

Date d1(); 并不是在调用默认构造函数创建对象,而是被编译器解析成了:声明一个名为 d1 的函数,该函数无参数,返回值类型是 Date。应该要写出Date d1;

多数情况下构造函数要我们自己写,编译器自己生成的往往不符合我们的需求。

3.析构函数

析构函数与构造函数功能相反,析构函数不是完成对对象本⾝的销毁,⽐如局部对象是存在栈帧的, 函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会⾃动调⽤析构函数,完成对 象中资源的清理释放⼯作。

析构函数的特点:

1. 析构函数名是在类名前加上字符 ~。

2. ⽆参数⽆返回值。 (这⾥跟构造类似,也不需要加void)

3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。

4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。

5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会调⽤他的析构函数。

6. 还需要注意的是我们显⽰写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数

7. 如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,,但默认析构只做 “空操作”,不会调用free释放堆内存;如Date;如果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,

根据上面的说法,我们很容易就可以理解析构函数需要我们自定义的往往都是我们之前自己申请过空间,然后函数结束要释放的那些。

#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}

_capacity = n;
_top = 0;
}

~Stack() {
//为了便于观察我们将这个过程打印出来
cout << "调用了~Stack()" << endl;
free(_a);
_a = nullptr;
_capacity= _top=0;
}

private:
STDataType * _a;
size_t _capacity;
size_t _top;
};

// 两个Stack实现队列
class MyQueue
{
public:
//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源

// 显⽰写析构,也会⾃动调⽤Stack的析构
/*~MyQueue()
{}*/
private:
Stack pushst;
Stack popst;
};

int main()
{
Stack st;

MyQueue mq;

return 0;
}

可以看出我们调用了三次这个析构函数,MyQueue两次,Stack一次。

4.拷贝构造

拷贝构造函数是类的特殊构造函数,核心作用是用一个已存在的对象,创建出一个新的对象。

Date(Date& d1);

  • 必须传引用:如果传值,会触发 “值传递时的拷贝构造”,导致无限递归调用
  • 加 const:避免误修改源对象,是 C++ 的最佳实践;
  • 调用场景:用已有对象初始化新对象(如Stack s2 = s1;、Stack s3(s1);)、函数传值参数、函数返回值(部分编译器优化后可能不调用)。
  • 如果不写自定义拷贝构造,编译器会生成默认拷贝构造函数,但它做的是浅拷贝(按字节拷贝所有成员变量,两个对象会用同一块指针)
  • 有时候浅拷贝会存在问题,要解决浅拷贝的问题,必须写自定义拷贝构造函数,实现 “深拷贝”(为新对象重新申请堆内存,拷贝数据,而非共用指针):

#include"maomao.h"
class Date {
public:
Date() {
_year=2014;
_month=3;
_day=19;
}

Date(Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print() {
cout << this->_year << "-"
<< this->_month << "-"
<< this->_day<< endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1;
Date d2(d1);
d1.Print();
d2.Print();
return 0;
}

拷贝构造的写法主要有下面两种

Date d2(d1);
Date d3 = d1;

如果我们不写拷贝构造,编译器会生成默认拷贝构造函数。但是这个函数有时候也不符合我们的需求。

#include"maomao.h"
class Date {
public:
Date() {
_year=2014;
_month=3;
_day=19;
}

void Print() {
cout << this->_year << "-"
<< this->_month << "-"
<< this->_day<< endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1;
Date d2(d1);
Date d3 = d1;
d1.Print();
d2.Print();
d3.Print();
return 0;
}

对于Date来说是没有问题的。

#include<stdlib.h>
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}

_capacity = n;
_top = 0;
}

void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType * tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}

_a = tmp;
_capacity = newcapacity;
}

_a[_top++] = x;
}

~Stack() {
//为了便于观察我们将这个过程打印出来
cout << "调用了~Stack()" << endl;
free(_a);
_a = nullptr;
_capacity = _top = 0;
}

private:
STDataType* _a;
size_t _capacity;
size_t _top;
};

// 两个Stack实现队列
class MyQueue
{
public:
//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源

// 显⽰写析构,也会⾃动调⽤Stack的析构
/*~MyQueue()
{}*/
private:
Stack pushst;
Stack popst;
};

int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
// Stack不显⽰实现拷⻉构造,⽤⾃动⽣成的拷⻉构造完成浅拷⻉
// 会导致st1和st2⾥⾯的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃
Stack st2 = st1;
MyQueue mq1;
// MyQueue⾃动⽣成的拷⻉构造,会⾃动调⽤Stack拷⻉构造完成pushst/popst
// 的拷⻉,只要Stack拷⻉构造⾃⼰实现了深拷⻉,他就没问题
MyQueue mq2 = mq1;
return 0;
}

析构函数执行free(_a)的逻辑,但因为st1和st2的_a指向同一块内存,第二次free时:

操作系统的内存管理模块会判定这是 “非法释放”。所以说不写拷贝构造时对于复杂的类,比如栈是行不通的。

需要写析构的往往也要写拷贝构造。

接下来我们接着来看

#include<stdlib.h>
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}

_capacity = n;
_top = 0;
}

void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType * tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}

_a = tmp;
_capacity = newcapacity;
}

_a[_top++] = x;
}

~Stack() {
//为了便于观察我们将这个过程打印出来
cout << "调用了~Stack()" << endl;
free(_a);
_a = nullptr;
_capacity = _top = 0;
}

private:
STDataType* _a;
size_t _capacity;
size_t _top;
};

// 两个Stack实现队列
class MyQueue
{
public:
//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源

// 显⽰写析构,也会⾃动调⽤Stack的析构
/*~MyQueue()
{}*/
private:
Stack pushst;
Stack popst;
};

int& func1() {
int x = 10;
return x;
}
Stack& func2() {
Stack st;
st.Push(1);
st.Push(2);
st.Push(3);
st.Push(4);
return st;
}
int main()
{
cout << func1() << endl;
cout << func2() << endl;
return 0;
}

你觉得这个的运行结果是什么?程序是奔溃还是正常运行呢?

在讲引用的那篇文章中说过先分清:什么是左值?什么是右值?
·左值(lvalue):有持久内存地址、能放在赋值号左边的值(不一定非要赋值)。
例int a = 10; 中的 a、数组 arr[0]、函数返回的左值引用。
·右值(rvalue):无持久内存地址、只能放在赋值号右边的值(临时创建,用完即销毁)。
例:字面量 10、表达式 a+3、函数返回的普通值(int func(){return 5;})。

普通函数(返回值)

引用(返回引用)

不创建临时对象,直接返回x本身的引用。

func1()返回的是x的引用但x是局部变量,函数结束时就销毁了。但引用还在指向那块已经释放的内存。对于那个内存的访问就会危险。若是没有被覆盖会巧合的输出正确答案。运气不好程序就会奔溃。

func2也是同样的原因func2() 执行结束后,局部对象 st 会被销毁,自动调用析构函数 ~Stack()。析构函数中执行了 free(_a) 和 _a = nullptr,释放了栈的底层数组内存。

main 函数中调用 func2().top() 时,返回的引用指向一个已经被销毁的对象,其 _a 指针要么是 nullptr,要么指向已释放的内存,访问时就会触发权限异常。

5.赋值运算符重载

假设我们现在要实现两个复数的相加减操作往往是不能直接使用。

#include"maomao.h"
#include<iostream>
using namespace std;
class Complex
{
double real, image;
public:
Complex(double r = 0, double i = 0)
{
real = r;
image = i;
}
void show()
{
if (image > 0)
{
if (image == 1)
cout << real << "+" << "i" << endl;
else
cout << real << "+" << image << "i" << endl;
}
else if (image < 0)
{
if (image == -1)
cout << real << "-" << "i" << endl;
else
cout << real << image << "i" << endl;
}
else
{
cout << real << endl;
}
}

};

int main()
{
Complex c1(1, 1), c2(2, -1);
c1.show();
c2.show();
Complex c3 = c1 + c2;

return 0;
}

这段代码运行后,

这时需要赋值运算符重载

函数原型:赋值运算符必须以成员函数形式重载(不能是全局函数),原型为:类名& operator赋值运算符(const 类名& 源对象);

#include"maomao.h"
#include<iostream>
using namespace std;
class Complex
{
friend Complex operator+(const Complex& c1, const Complex& c2);

public:
Complex(double r = 0, double i = 0)
{
real = r;
image = i;
}
void show()
{
if (image > 0)
{
if (image == 1)
cout << real << "+" << "i" << endl;
else
cout << real << "+" << image << "i" << endl;
}
else if (image < 0)
{
if (image == -1)
cout << real << "-" << "i" << endl;
else
cout << real << image << "i" << endl;
}
else
{
cout << real << endl;
}
}
public:
double real, image;

};

Complex operator+(const Complex& c1, const Complex& c2) {
Complex temp;
temp.real = c1.real + c2.real;
temp.image = c1.image + c2.image;
return temp;
}
int main()
{
Complex c1(1, 9), c2(2, 12);
c1.show();
c2.show();
Complex c3 = c1 + c2;//隐式调用
Complex c4 = operator+(c3,c2); //显式调用
c3.show();
c4.show();
return 0;
}

在这个过程中会遇到一些问题

1.对于real,image访问权限的限制

 1、成员放公有

 2、complex提供getxxx函数

 3、友元函数

 4、重载为成员函数

我们这里选择友元,友元函数声明写在类内部(任意位置,private/public 均可)。

2.如何解决连续操作的问题像是cout<<"a"<<b<<endl;这个样子

关键在于在返回值上是引用

接下来我们进一步来理解运算符重载

1.重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元 运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。

2.如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算 符重载作为成员函数时,参数⽐运算对象少⼀个。 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。

#include"maomao.h"
#include<iostream>
using namespace std;
class Complex
{

double real, image;
public:
Complex(double r = 0, double i = 0)
{
real = r;
image = i;
}
void show()
{
if (image > 0)
{
if (image == 1)
cout << real << "+" << "i" << endl;
else
cout << real << "+" << image << "i" << endl;
}
else if (image < 0)
{
if (image == -1)
cout << real << "-" << "i" << endl;
else
cout << real << image << "i" << endl;
}
else
{
cout << real << endl;
}
}
Complex operator+( const Complex& c2) {
Complex temp;
temp.real = this->real + c2.real;
temp.image = this->image + c2.image;
return temp;
}
};

int main()
{
Complex c1(1, 9), c2(2, 12);
c1.show();
c2.show();
Complex c3 = c1 + c2;//隐式调用
Complex c4 = c3.operator+(c2); //显式调用
c3.show();
c4.show();
return 0;
}

在前面的文章中讲过类的this指针

this指针是隐藏在类的非静态成员函数中的一个隐含参数,它指向当前调用该成员函数的对象

Complex operator+( Complex& const this, Complex& c2);

所以当函数在类里面时只需要传入(总数-1)个的参数。在调用时要和成员函数一样。

3.不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@。

4..* :: sizeof ?: . 注意以上5个运算符不能重载。

5.操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int

operator+(int x, int y)

6.重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。

#include"maomao.h"
#include<iostream>
using namespace std;
class Complex
{

double real, image;
public:
Complex(double r = 0, double i = 0)
{
real = r;
image = i;
}
void show()
{
if (image > 0)
{
if (image == 1)
cout << real << "+" << "i" << endl;
else
cout << real << "+" << image << "i" << endl;
}
else if (image < 0)
{
if (image == -1)
cout << real << "-" << "i" << endl;
else
cout << real << image << "i" << endl;
}
else
{
cout << real << endl;
}
}
Complex operator+(const Complex& c2) {
Complex temp;
temp.real = this->real + c2.real;
temp.image = this->image + c2.image;
return temp;
}
Complex& operator++() {//前置
this->real++;
this->image++;
return *this;
}
Complex operator++(int) {//后置

Complex temp = *this;
this->real++;
this->image++;
return temp;
}
};

int main()
{
Complex c1(1, 9), c2(2, 12);
c1.show();
c2.show();
Complex c3 = ++c1;
c1.show();
c3.show();
Complex c4 = c3++;
c3.show();
c4.show();

return 0;
}

7.重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位 置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使⽤习惯和可读性。 重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对

在类外面的函数

在类里面放友元

8.区分拷贝构造和运算符重载。先创建,后赋值是运算符重载,在创建时就赋值是拷贝。

9.没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认拷 ⻉构造函数类似,对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义 类型成员变量会调⽤他的赋值重载函数。

6.综合运用

我们进行日期类的操作,类似于日期时间计算器,来对构造函数,析构函数,拷贝构造函数,赋值重载更加系统的输入的理解。

//maomao.h
#pragma once
#include<iostream>
using namespace std;
class Date {
public:
Date(int year = 1, int month = 1, int day = 1);
Date(const Date& d);
Date::~Date() {
//本函数并没有new,或者申请空间不需要自己写析构函数
}
Date operator+(int day);
Date operator+=(int day);

private:
int _year;
int _month;
int _day;
};

//maomao.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"maomao.h"
Date::Date(int year, int month, int day ) {
_year = year;
_month = month;
_day = day;
}
Date::Date(const Date& d) {
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;

}
int GetMonthDay(int year, int month) {
//静态,不用每次操作都创建
static int Monthday[] = { -1,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;
}
else {
return Monthday[month];
}
}

Date Date::operator+(int day) {
Date temp = *this;
temp += day;
return temp;
}
Date& Date::operator+=(int day) {

this->_day += day;//不算这个月的天数,还要过多少天
while (this->_day >= GetMonthDay(this->_year, this->_month)) {//减去每个月的天数
this->_day -= GetMonthDay(this->_year, this->_month);
++this->_month;
if (this->_month > 12) {
this->_month = 1;
this->_year++;
}

}
return *this;

}


构造函数的格式是

// 声明
类名(参数);

// 定义
类名::类名(参数)
{

}


析构函数


拷贝函数的格式是

// 声明
类名(参数);

// 定义
类名::类名(const参数的引用)
{

}


运算符重载函数的格式

// 声明
返回值(&) operator运算符(参数);

// 定义
返回值(&) 类名::operator运算符(参数)
{

}
//看情况要不要用引用和const


接下来我们来思考一下既然可以复用,那+=,+是否可以调换呢?

关键在于效率,拷贝次数越少,效率越高。

很显然让“+”复用“+=”的效率更高。

下面我们进一步完善这个类。完成-=和-

Date Date::operator-(int day) {
Date temp = *this;
temp -= day;
return temp;
}
Date& Date::operator-=(int day) {
this->_day -= day;//先看一下当月天数够不够
while (this->_day < 1) {//当月不够然后依次减前一个月
–this->_month;

if (this->_month < 1) {
this->_month = 12;
this->_year–;
}

this->_day += GetMonthDay(this->_year, this->_month);

}
return *this;
}

实现逻辑

左边:2025.4.19 减去 100 天(减法逻辑)

核心思路:当当前天数不够减时,向上一个月 “借” 该月的总天数。

  • 初值计算

    • 日期:2025 年 4 月 19 日,减去 100 天。
    • 运算:19 – 100 = -81。
    • 结果:2025 年 4 月,剩余 – 81 天(说明 4 月不够减,需借 3 月的天数)。
  • 借 3 月(借位)

    • 操作:向上一个月(3 月)借天数。3 月有 31 天。
    • 运算:-81 + 31 = -50。
    • 结果:2025 年 3 月,剩余 – 50 天(3 月也不够减,需借 2 月的天数)
  • …………..

    右边:2025.4.19 加上 100 天(加法逻辑)

    核心思路:当当前天数超过当月总天数时,向高一位的月 “进” 位。

  • 初值计算

    • 日期:2025 年 4 月 19 日,加上 100 天。
    • 运算:19 + 100 = 119。
    • 结果:2025 年 4 月,剩余 119 天(超过 4 月总天数 30 天,需进位)。
  • 进 5 月(进位)

    • 操作:减去 4 月已过的天数(4 月共 30 天),剩余天数往 5 月算。
    • 运算:119 – 30 = 89。
    • 结果:2025 年 5 月,剩余 89 天(5 月有 31 天,剩余,需继续进位)。
  • …………………..


    接下来继续实现前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。

    //前置++
    Date& Date::operator++() {
    *this += 1;
    return *this;
    }
    //后置++
    Date Date::operator++(int) {
    Date temp = *this;
    *this += 1;
    return temp;
    }

    注意显示调用的区别,后置要传参,前置不用。


    接下来是比较运算符(==、!=、>、>=、<、<=)巧妙的运用了复用,

    bool Date::operator==(const Date& d) {
    if (this->_year == d._year &&
    this->_month == d._month &&
    this->_day == d._day)
    return true;
    else return false;
    }

    bool Date::operator != (const Date& d) {
    return !(*this == d);
    }
    bool Date::operator>(const Date& d) {
    if (this->_year > d._year)
    return true;
    else if (this->_year == d._year && this->_month > d._month) {
    return true;
    }
    else if (this->_year == d._year && this->_month == d._month && this->_day > d._day)
    return true;
    else
    return false;
    }

    bool Date::operator >= (const Date& d) {
    return !(*this < d);
    }
    bool Date::operator < (const Date& d) {
    if (this->_year < d._year)
    return true;
    else if (this->_year == d._year && this->_month < d._month) {
    return true;
    }
    else if (this->_year == d._year && this->_month == d._month && this->_day < d._day)
    return true;
    else
    return false;
    }
    bool Date::operator <= (const Date& d) {
    return !(*this > d);
    }


    然后是日期和日期之间的天数差计算。

    int Date::operator-(const Date& d) {
    Date max = *this;
    Date min = d;
    int flag = 1;
    if (*this < d)
    {
    max = d;
    min = *this;
    flag = -1;
    }

    int n = 0;
    while (min != max)
    {
    ++min;
    ++n;
    }

    return n * flag;
    }


    对于重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位 置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使⽤习惯和可读性。 重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对

    全部代码集合

    //maomao.h
    #pragma once
    #include<iostream>
    #include<assert.h>
    using namespace std;
    class Date {
    public:
    Date(int year = 1, int month = 1, int day = 1);
    Date(const Date& d);
    Date& operator=(const Date& d);
    ~Date();
    Date operator+(int day);
    Date& operator+=(int day);
    Date operator-(int day);
    Date& operator-=(int day);
    Date& operator++();
    Date operator++(int);
    Date operator–(int);
    Date& operator–();
    bool operator>(const Date& d);
    bool operator==(const Date& d);
    bool operator >= (const Date& d);
    bool operator < (const Date& d);
    bool operator <= (const Date& d);
    bool operator != (const Date& d);
    int operator-(const Date& d);
    friend ostream& operator<<(ostream& out, const Date& d);
    friend istream& operator>>(istream& in, Date& d);
    private:
    int _year;
    int _month;
    int _day;
    };

    //maomao.cpp
    #define _CRT_SECURE_NO_WARNINGS
    #include"maomao.h"
    //全缺省构造
    Date::Date(int year, int month, int day ) {
    _year = year;
    _month = month;
    _day = day;
    }
    //拷贝
    Date::Date(const Date& d) {
    this->_year = d._year;
    this->_month = d._month;
    this->_day = d._day;

    }

    Date& Date::operator=(const Date& d) {
    this->_year = d._year;
    this->_month = d._month;
    this->_day = d._day;
    return *this;
    }

    // 析构函数

    Date::~Date() {
    //本函数并没有new,或者申请空间不需要自己写析构函数
    }
    int GetMonthDay(int year, int month) {
    //静态,不用每次操作都创建
    assert(month >= 1 && month <= 12);
    static int Monthday[] = { -1,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;
    }
    else {
    return Monthday[month];
    }
    }

    Date Date::operator+(int day) {
    Date temp = *this;
    temp += day;
    return temp;
    }
    Date& Date::operator+=(int day) {
    if (day < 0)
    return *this -= -day;
    this->_day += day;//不算这个月的天数,还要过多少天
    while (this->_day >= GetMonthDay(this->_year, this->_month)) {//减去每个月的天数
    this->_day -= GetMonthDay(this->_year, this->_month);
    ++this->_month;
    if (this->_month > 12) {
    this->_month = 1;
    this->_year++;
    }

    }
    return *this;

    }
    Date Date::operator-(int day) {
    Date temp = *this;
    temp -= day;
    return temp;
    }
    Date& Date::operator-=(int day) {
    if (day < 0)
    return *this += -day;
    this->_day -= day;//先看一下当月天数够不够
    while (this->_day < 1) {//当月不够然后依次减前一个月
    –this->_month;

    if (this->_month < 1) {
    this->_month = 12;
    this->_year–;
    }

    this->_day += GetMonthDay(this->_year, this->_month);

    }
    return *this;
    }
    //前置++
    Date& Date::operator++() {
    *this += 1;
    return *this;
    }
    //后置++
    Date Date::operator++(int) {
    Date temp = *this;
    *this += 1;
    return temp;
    }
    // 前置–

    Date& Date::operator–() {
    *this -= 1;
    return *this;
    }
    // 后置–

    Date Date::operator–(int) {
    Date temp = *this;
    –*this;
    return temp;
    }
    bool Date::operator==(const Date& d) {
    if (this->_year == d._year &&
    this->_month == d._month &&
    this->_day == d._day)
    return true;
    else return false;
    }

    bool Date::operator != (const Date& d) {
    return !(*this == d);
    }
    bool Date::operator>(const Date& d) {
    if (this->_year > d._year)
    return true;
    else if (this->_year == d._year && this->_month > d._month) {
    return true;
    }
    else if (this->_year == d._year && this->_month == d._month && this->_day > d._day)
    return true;
    else
    return false;
    }

    bool Date::operator >= (const Date& d) {
    return !(*this < d);
    }
    bool Date::operator < (const Date& d) {
    if (this->_year < d._year)
    return true;
    else if (this->_year == d._year && this->_month < d._month) {
    return true;
    }
    else if (this->_year == d._year && this->_month == d._month && this->_day < d._day)
    return true;
    else
    return false;
    }
    bool Date::operator <= (const Date& d) {
    return !(*this > d);
    }

    int Date::operator-(const Date& d) {
    Date max = *this;
    Date min = d;
    int flag = 1;
    if (*this < d)
    {
    max = d;
    min = *this;
    flag = -1;
    }

    int n = 0;
    while (min != max)
    {
    ++min;
    ++n;
    }

    return n * flag;
    }

    ostream& operator<<(ostream& out, const Date& d) {
    out << d._year << "-" << d._month << "-" << d._day << endl;
    return out;
    }
    istream& operator>>(istream& in, Date& d) {
    cout << "请输入年月日,用空格分隔" << endl;

    in >> d._year>> d._month >> d._day ;

    return in;
    }

    //test.cpp

    #include"maomao.h"
    void text01(){
    Date d1(2025,3,19) ;//默认构造
    Date d2 = d1;//拷贝构造
    cout << d1<<" "<<d2 << endl;
    Date d3 = d1 +100;//加法运算
    d2 = d3;//运算符重载
    cout << d2<<endl;
    cout << "d1往后100天是"<<d3<<endl;
    d3 += 500;
    cout <<"d3往后500天后是"<< d3<<endl;
    d3 = d1 – 100;
    cout << "d3往前100天是" <<d3<<endl;
    d3 -= 1000;
    cout << "d3往前1000天是" << d3 << endl;
    Date d4=d3++;
    cout << "d3=" << d3 << " d4=" << d4 << endl;
    d4 = ++d3;
    cout << "d3=" << d3 << " d4=" << d4 << endl;
    d4 = d3–;
    cout << "d3=" << d3 << " d4=" << d4 << endl;
    d4 = –d3;
    cout << "d3=" << d3 << " d4=" << d4 << endl;
    d4 += 500;
    cout << (d3 > d4) << endl;
    cout << (d3 < d4) << endl;
    cout << (d3 == d4) << endl;
    cout << (d3 <= d4) << endl;
    cout << (d3 >= d4) << endl;
    cout << (d3 != d4) << endl;
    d3 += 100;
    cout << d4 – d3 << endl;

    int day = d1 – d2;
    cin >> d1;
    cout << d1;
    }
    int main() {
    text01();
    return 0;
    }

    赞(0)
    未经允许不得转载:171主机测评 » C++之类和对象(中)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址