目录
C++入门 第一篇(C++关键字, 命名空间,C++输入&输出)-CSDN博客
C++入门 第二篇( 引用、内联函数、auto关键字、指针空值nullptr)-CSDN博客
【C++】类与对象 第一篇(class,this)-CSDN博客
类的6个默认成员函数
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
初始化和清理
1.构造函数
特征: 构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。 其特征如下:
-
函数名与类名相同。
-
无返回值。
-
对象实例化时编译器自动调用对应的构造函数。
-
构造函数可以重载。
class Stack
{
public:
Stack()
{
_a=nullptr;
_size=_capacity=0;
}
Stack(int n)
{
_a=(int*)malloc(sizeof(int)*n);
_size=_capacity=0;
}
void Init(int n=4)
{
_a=(int*)malloc(sizeof(int)*n);
if (nullptr==_a)
{
perror("malloc is fail");
return;
}
_capacity=n;
_size=0;
}
void Push(int x)
{
//...
a[_size++]=x;
}
//...
void Dstory()
{
//...
}
private:
int _a;
int _size;
int _capacity;
};
int main()
{
Stack st;//无参
//Stack st();//有参
st.Push(1);
st.Push(2);
st.Push(3);
st.Push(4);
st.Dstory();
return 0;
}
以上述代码为例:
自动调用初始化
注意:调用无参时如:Data d;此处在后面不能➕(),否则编译器会调用有参的。
使用缺省值:
class Date
{
public:
Date(int year=1,int month=1,int day=1)
{
_year=1;
_month=1;
_day=1;
}
void print()
{
cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
}
private:
//成员变量
int _year;
int _month;
int _day;
};
int main()
{
//Date d1;
Date d2(2077,2,3);
d2.print();
return 0;
}
2.析构函数
概念: 通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的? 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
特性 析构函数是特殊的成员函数,其特征如下:
-
析构函数名是在类名前加上字符 ~。
-
无参数无返回值类型。
-
一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载
-
对象生命周期结束时,C++编译系统系统自动调用析构函数。
例子:
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_array = (DataType *)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
// 其他方法...
~Stack()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType *_array;
int _capacity;
int _size;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
}
3.共同点
如果编译过程不写,那编译器会自动生成一个默认的,但是如果我们实现了任意一个,编译器就不会生成了。
若是自动初始化,那为什么这个地方会生成随机值呢?
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类 型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看 下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员 函数。
内置类型(基本类型):int/char/double... /任意类型指针 自定义类型:class/structd定义的
默认生成构造函数:
1.内置类型成员不做处理。
2.自定义类型的成员,会去调用它的默认构造(不用传参数的构造)
private:
// 基本类型/内置类型 - 不进初始化
int _year;
int _month;
int _day;
实用场景:
class Date
{
public:
//内置类型成员不做处理
void print()
{
cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
}
private:
// 基本类型/内置类型 - 不进初始化
int _year;
int _month;
int _day;
};
class MyQueue
{
// 默认生成构造函数,对自定义类型,会调用它的默认构造函数
void push(int x);
{
}
//...
Stack _pushST();
Stack _popST();
};
析构:
默认生成构造函数:
1.内置类型成员不做处理。
2.自定义类型的成员,会去调用它的析构函数
class MyQueue
{
// 默认生成析构函数,对自定义类型,会调用它的析构函数
void push(int x);
{
}
Stack _pushST();
Stack _popST();
};
int main()
{
//Date d1;
Date d1;
d1.print();
MyQueue q;
return 0;
}
内置类型并不会主动初始化,但可以通过给缺省值进行初始化:
private:
//声明位置给缺省值
int _year=1;
int _month=1;
int _day=1;
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。
拷贝复制
1.拷贝构造
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。
拷贝构造:内置类型,编译器直接拷贝,自定义类型拷贝需要调用拷贝构造
特征
拷贝构造函数也是特殊的成员函数,其特征如下:
-
拷贝构造函数是构造函数的一个重载形式。
(
为什么自定义类型要用拷贝构造,而内置类型编译器却可以直接拷贝(按字节拷贝)? 因为自定义类型进行拷贝容易出问题:若要拷贝栈,栈的成员变量都是指向相同的空间,若进行拷贝,则会导致两个Stack指向同一个空间,导致原本Stack执行析构函数时被销毁,而复制的那个则指向空,正确的应该是各有各的空间。
)
-
拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
class Date
{
public:
Date(int year=2077, int month=10, int day=12)
{
_year = year;
_month = month;
_day = day;
}
Date(Date d)//这样拷贝会造成无限递归
{
_year = d.year;
_month = d.month;
_day = d.day;
}
private:
int _year;
int _month;
int _day;
};
在拷贝构造函数 Date(Date d)
中,参数 d
是按值传递的,这意味着每次调用拷贝构造函数时都会创建一个新的 Date
对象,并将原始对象 d
复制到新的对象中。然而,在拷贝构造函数内部,对于拷贝构造函数的调用又会传递同样的参数 d
,导致不断地递归调用拷贝构造函数,从而产生无限递归。
则可以使用 & ,使其不在重新创建空间并使用原Date对象
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
这个拷贝构造函数将创建一个新的 Date
对象,并将原始对象的 _year
、_month
和 _day
成员变量的值分别拷贝到新对象的相应成员变量中。通过使用对象引用作为参数,我们可以避免无限递归调用拷贝构造函数的问题,并且确保在构造新对象时不会复制整个对象
class Date
{
public:
Date(int year = 2077, int month = 10, int day = 12)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)//const是为了防止写反->d._year = _year;加const缩小权限
{
cout<<"Date(Date& d);"<<endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 2, 3);
//两种拷贝方式
Date d2(d1); // 使用拷贝构造函数创建对象 d2,并将 d1 的值拷贝到 d2 中
Date d3=d1;
return 0;
}
RunCode:
若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
深拷贝是指在对象拷贝时,复制所有的数据和资源,使得新对象和原对象完全独立,互不影响。与之相对的是浅拷贝,浅拷贝只复制指针或引用,导致新旧对象共享同一份数据,修改一个可能会影响另一个。深拷贝能够保证对象之间的独立性和数据完整性。
分清楚是不是拷贝构造:
class Date
{
public:
Date(int year = 2077, int month = 10, int day = 12)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date(const Date& d)
{
cout<<"Date(Date& d);"<<endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
//构造函数 不是拷贝构造
Date(const Date* d)
{
cout<<"Date(Date& d);"<<endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 2, 3);
//Date(const Date* d)
Date d2(&d1);
Date d3=&d1;
return 0;
}
拷贝构造函数典型调用场景:
-
使用已存在对象创建新对象
-
函数参数类型为类类型对象
-
函数返回值类型为类类型对象
使用细节
1使用拷贝构造时最好加上const,以防权限的放大(例如拷贝构造的值:_year = d._year + 1;)
// 拷贝构造
Date(const Date &d)
{
_year = d.year;
_month = d.month;
_year = d.year;
}
2编译器可自动生成但存在问题
本文所用的日期类可以使用自动生成拷贝,但进行拷贝栈时就会出现问题
当进行拷贝时,用值拷贝,导致两个栈指向同一个空间,造成空间互相覆盖 指向同一块空间的问题:1.插入删除数据会互相影响 2.析构两次,程序崩溃
析构是也跟栈一样是后进先出,后定义的先析构,故st2先析构,对st2滞空是,并不影响st1,导致野指针。此时浅拷贝行不通,需要进行深拷贝:
代码实现:
class Stack
{
public:
void Push(const int &data)
{
_array[_size] = data;
_size++;
}
Stack(const Stack &st)
{
_array = (int *)malloc(sizeof(int) * st._capacity);
if (nullptr == _array)
{
perror("malloc is fail");
exit(-1);
}
memcpy(_array, st._array, sizeof(int) * st._size);
_size = st._size;
_capacity = st._capacity;
}
~Stack()
{
if (_array)
{
delete[] _array;
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
int *_array;
int _size;
int _capacity;
};
int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
st1.Push(3);
Stack st2(st1);
return 0;
}
什么情况下需要事项拷贝构造呢? 自己实现了析构释放空间,就需要实现拷贝构造
class Stack
{
public:
//构造函数
Stack(size_t _capacity = 10)
{
cout << "Stack(size_t _capacity)" << endl;
_array = (int *)malloc(sizeof(int) * _capacity);
if (nullptr == _array)
{
perror("malloc is fail");
exit(-1);
}
_size = 0;
_capacity = _capacity;
}
void Push(const int &data)
{
_array[_size] = data;
_size++;
}
//拷贝构造
Stack(const Stack &st)
{
_array = (int *)malloc(sizeof(int) * st._capacity);
if (nullptr == _array)
{
perror("malloc is fail");
exit(-1);
}
memcpy(_array, st._array, sizeof(int) * st._size);
_size = st._size;
_capacity = st._capacity;
}
~Stack()
{
if (_array)
{
delete[] _array;
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
int *_array;
int _size;
int _capacity;
};
class MyQueue
{
//默认生成构造 ->生成定义在Stack中的默认构造函数
//默认生成析构
//默认生成的拷贝构造
private:
Stack _pushST;
Stack _popST;
int _size = 0;
};
int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
st1.Push(3);
Stack st2(st1);
cout<<"====="<<endl;
MyQueue q;
return 0;
}
默认生成拷贝构造和赋值重载:
a.内置类型完成 浅/值 拷贝--按byte一个一个拷贝
b.自定义类型,去调用这个成员 拷贝构造/赋值重载
2.赋值重载
运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。 函数原型:返回值类型 operator操作符(参数列表)
自定义类型不能直接使用运算操作符 内置类型是语法定义,而自定义类型为人为定义,编译器并不知道该如何进行比较,这便出现了operator用函数完成
运算符重载:自定义类型对象可以使用运算符
函数重载:支持函数名相同,参数不同的函数
#include <iostream>
using namespace std;
class Date
{
public:
// 构造函数
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造
Date(const Date &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
// d1==d2 -> d1.operator==(d2)
bool operator==(const Date &d)
{
return _year == d._year && _month == d._month && _day == d._day;
// this->_year==d.year
}
private:
// 成员变量
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2077, 2, 4);
Date d2(2077, 2, 4);
// d1==d2;
cout << d1.operator==(d2) << endl;
cout << (d1 == d2) << endl;
return 0;
}
注意:
-
不能通过连接其他符号来创建新的操作符:比如operator
-
重载操作符必须有一个类类型参数
-
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
-
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this现。
-
注意以下5个运算符不能重载
.* :: sizeof ?: .
== <= < >= > !=
// d1==d2 -> d1.operator==(d2)
bool operator==(const Date &d)
{
return _year == d._year && _month == d._month && _day == d._day;
// this->_year==d.year
}
// bool operator<(const Date &d)
// {
// 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;
// }
// else
// return false;
// }
// d1<d2
bool operator<(const Date &d)
{
return _year < d._year && (_year == d._year &&
_month < d._month) && (_year == d._year &&
_month == d._month && _day < d._day);
}
任何一个类都适用于,当写了一个>=或<=那其他的就可以进行复用
// d1<=d2
bool operator<=(const Date &d)
{
return *this < d || *this == d;//*this就是d1
}
这个地方对operator<(const Date &d)
进行了复用。operator<=(const Date &d)
中的表达式*this < d
调用了operator<(const Date &d)
来判断两个日期对象的大小关系。同时,operator<=(const Date &d)
还利用operator==(const Date &d)
来判断两个日期对象是否相等。通过这样的复用方式,可以简化代码并提高代码的可读性
// d1>d2
bool operator>(const Date &d)
{
return !(*this<= d);
}
//d1>=d2
bool operator>=(const Date& d)
{
return !(*this<d);
}
//d1!=d2
bool operator!=(const Date& d)
{
return !(*this ==d);
}
当然赋值运算符不使用&并不会无限循环
// d1<=d2
bool operator<=(const Date d)
{
return *this < d || *this == d;
}
当然可以不用,但是最好还是加上
连续赋值
d3 = d1 = d2
// 因为Date operator=(const Date &d) 出了该函数的作用域*this/d1 还在此时返回的*this是临时拷贝,需要进行拷贝重新开空间造成浪费,不如直接进行&
Date operator=(const Date &d)//&d 引用
{
if (this != &d)//防止d1=d1进行赋值 此处的&为去地址 ,若this与d的地址一样则无需赋值
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this; //*this就是d1
} // 返回值为了支持连续赋值,保持运算符特性 d3=d1=d2;
当然要注意,赋值重载是针对已经创造的对象
Date d5=d1;//拷贝构造
像这样就是拷贝构造
同样作为默认成员函数,可以不用手动写
源码:
#include <iostream>
using namespace std;
class Date
{
public:
// 构造函数
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造
Date(const Date &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
// d1==d2 -> d1.operator==(d2)
bool operator==(const Date &d)
{
return _year == d._year && _month == d._month && _day == d._day;
// this->_year==d.year
}
// d1<d2
bool operator<(const Date &d)
{
return _year < d._year && (_year == d._year && _month < d._month) && (_year == d._year && _month == d._month && _day < d._day);
}
// d1<=d2
bool operator<=(const Date &d)
{
return *this < d || *this == d; //*this就是d1
}
// d1>d2
bool operator>(const Date &d)
{
return !(*this <= d);
}
// d1>=d2
bool operator>=(const Date &d)
{
return !(*this < d);
}
// d1!=d2
bool operator!=(const Date &d)
{
return !(*this == d);
}
// // 因为Date operator=(const Date &d) 出了该函数的作用域*this/d1 还在此时返回的*this是临时拷贝,需要进行拷贝重新开空间造成浪费,不如直接进行&
// Date operator=(const Date &d)//&d 引用
// {
// if (this != &d)//防止d1=d1进行赋值 此处的&为去地址 ,若this与d的地址一样则无需赋值
// {
// _year = d._year;
// _month = d._month;
// _day = d._day;
// }
// return *this; //*this就是d1
// } // 返回值为了支持连续赋值,保持运算符特性 d3=d1=d2;
private:
// 成员变量
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2077, 2, 4);
Date d2(2222, 3, 2);
Date d3(2078, 2, 4);
cout << (d1 < d2) << endl;
// d3 = d1 = d2;
d1 = d2;
d1.Print();
return 0;
}
日期类
TestDate.h
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
class Date
{
// 友元函数
// friend void operator<<(ostream &out, const Date &d);
friend ostream &operator<<(ostream &out, const Date &d);
friend istream &operator>>(istream &in, Date &d); // 不能加const,为输出型参数
public:
int GetMonthDay(int year, int month);
// 构造函数
Date(int year = 1, int month = 1, int day = 1);
void Print();
// d1==d2 -> d1.operator==(d2)
bool operator==(const Date &d);
// d1<d2
bool operator<(const Date &d);
// d1<=d2
bool operator<=(const Date &d);
// d1>d2
bool operator>(const Date &d);
// d1>=d2
bool operator>=(const Date &d);
// d1!=d2
bool operator!=(const Date &d);
// d1+=100
Date &operator+=(int day);
// d1+100
Date operator+(int day);
// d1-=100;
Date &operator-=(int day);
// d1-100
Date operator-(int day);
//++d1
Date operator++();
// d2++
Date operator++(int); //()内参数(int)无实际意义
//--d1
Date operator--();
// d2--
Date operator--(int); //()内参数(int)无实际意义
// d1-d2
int operator-(const Date &d);
private:
// 成员变量
int _year;
int _month;
int _day;
};
// void operator<<(ostream &out, const Date &d);
// 为了支持连续调用➕返回值
ostream &operator<<(ostream &out, const Date &d); // 加const是为了减少拷贝
// 流提取
istream &operator>>(istream &in, Date &d); // 不能加const,为输出型参数
直接用内联函数进行定义,无需在对其他.cpp进行链接: 类里面短小函数,适合做内联的函数,直接是在类里面定义
inline ostream &operator<<(ostream &out, const Date &d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
inline istream &operator>>(istream &in, Date &d)
{
in >> d._year >> d._month >> d._day;
return in;
}
TestDate.cpp
#include "TestDate.h"
// 判断该月份天数
int Date::GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
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;
}
else
return monthArray[month];
}
Date::Date(int year, int month, int day)
{
// 判断日期是否合法
if (month > 0 && month < 13 && (day > 0 && day <= GetMonthDay(year, month)))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
// d1==d2 -> d1.operator==(d2)
bool Date::operator==(const Date &d)
{
return _year == d._year && _month == d._month && _day == d._day;
// this->_year==d.year
}
// d1<d2
bool Date::operator<(const Date &d)
{
return _year < d._year && (_year == d._year && _month < d._month) && (_year == d._year && _month == d._month && _day < d._day);
}
// d1<=d2
bool Date::operator<=(const Date &d)
{
return *this < d || *this == d; //*this就是d1
}
// d1>d2
bool Date::operator>(const Date &d)
{
return !(*this <= d);
}
// d1>=d2
bool Date::operator>=(const Date &d)
{
return !(*this < d);
}
// d1!=d2
bool Date::operator!=(const Date &d)
{
return !(*this == d);
}
// d1+=d2
Date &Date::operator+=(int day) // 出作用域*this还在,故用引用返回Date*
{
//+= --50
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
// d1+d2
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;
tmp += day; // 对Date& operator+=(int day)进行复用
return tmp;
}
/*
对于第一种实现方式:*this += day,它使用了+=运算符重载,避免了拷贝构造函数的调用,只对当前对象进行了原地修改,因此效率更高。
而对于第二种实现方式:*this = *this + day,它在+运算符重载中使用了拷贝构造函数创建了临时对象tmp,之后又将临时对象赋值给*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)
// {
// *this=*this+day;
// return *this;
// }
//++d1 返回的是++后的值
Date Date::operator++()
{
*this += 1;
return *this;
}
// d2++ 返回的是++前的值 ,由此可得:前置优于后置
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp; // 出作用域销毁无需销毁
}
// d1-=100;
Date &Date::operator-=(int day)
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// d1-100
Date Date::operator-(int day)
{
Date tmp(*this);
*this -= day;
return tmp;
}
//--d1
Date Date::operator--()
{
*this -= 1;
return *this;
}
// d2--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
// d1-d2
int Date::operator-(const Date &d)
{
Date max = *this; // d1
Date min = d; // d2
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
// void operator<<(ostream &out,const Date& d)
// {
// out<<d._year<<"年"<<d._month<<"月"<<d._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;
// }
//在TestDate中改为了内联函数
test.cpp
#include "TestDate.h"
void TestDate1()
{
Date d1(2023, 2, 4);
d1.Print();
cout << "===" << endl;
Date d2 = d1 + 150;
d1.Print();
d2.Print();
cout << "===" << endl;
Date d3 = d1;
d3 += 100;
// d1+=d3+=100;这样会报错,d1+=d3为 日期类+日期类,
d1.Print();
d3.Print();
cout << "===" << endl;
}
void TestDate2()
{
Date d1(2023, 2, 4);
d1.Print();
++d1; // d1.operator++();
d1.Print();
d1++; // d1.operator++(1);//括号内传整形(不一定是1)只是为了占位,跟前置区分
d1.Print();
cout << "===" << endl;
}
void TestDate3()
{
Date d1(2023, 2, 4);
d1.Print();
d1 -= 100;
d1.Print();
cout << "=====" << endl;
Date d2(2023, 2, 4);
d2.Print();
d2 += -100;
d2.Print();
cout << "=====" << endl;
Date d3(2023, 2, 4);
d3.Print();
d3 -= -100;
d3.Print();
cout << "===" << endl;
Date d4(2023, 2, 4);
d4.Print();
d4--;
d4.Print();
cout << "===" << endl;
--d4;
d4.Print();
}
void TestDate4()
{
Date d1(2023, 11, 1);
d1.Print();
Date d2(2022, 11, 3);
d2.Print();
cout << d2 - d1 << endl;
cout << (d1 - d2) << endl;
}
void TestDate5()
{
// 流插入
int i = 1;
double d=1.11;
cout << i; // cout.operator(i) //int
cout << d; // cout.operator(d) //double
cout<<endl;
cout<<"===="<<endl;
//实现流插入
Date d1(2023, 11, 2);
// operator<<(cout);
// d1<<cout;
operator<<(cout,d1);
cout<<d1;
cout<<endl;
Date d2(2023, 11, 2);
//此处为连续调用 每次 <<执行完应该return值以便继续调用
cout<<d1<<d2<<endl;
}
void TestDate6()
{
//流提取
Date d1;
cin>>d1;
cout<<d1;
}
int main()
{
TestDate6();
return 0;
}
const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数 隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
流插入和提取 都不是成员函数,不能用const修饰
int GetMonthDay(int year, int month) const;
// 构造函数
Date(int year = 1, int month = 1, int day = 1);
void Print() const;
// d1==d2 -> d1.operator==(d2)
bool operator==(const Date &d) const;
// d1<d2
bool operator<(const Date &d) const;
// d1<=d2
bool operator<=(const Date &d) const;
// d1>d2
bool operator>(const Date &d) const;
// d1>=d2
bool operator>=(const Date &d) const;
// d1!=d2
bool operator!=(const Date &d) const;
// d1+=100
Date &operator+=(int day);
// d1+100
Date operator+(int day) const;
// d1-=100;
Date &operator-=(int day);
// d1-100
Date operator-(int day) const;
//++d1
Date operator++();
// d2++
Date operator++(int); //()内参数(int)无实际意义
//--d1
Date operator--();
// d2--
Date operator--(int); //()内参数(int)无实际意义
// d1-d2
int operator-(const Date &d) const;
int Date::GetMonthDay(int year, int month)const
{
assert(month > 0 && month < 13);
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;
}
else
return monthArray[month];
}
Date::Date(int year, int month, int day)
{
// 判断日期是否合法
if (month > 0 && month < 13 && (day > 0 && day <= GetMonthDay(year, month)))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
void Date::Print()const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
// d1==d2 -> d1.operator==(d2)
bool Date::operator==(const Date &d)const
{
return _year == d._year && _month == d._month && _day == d._day;
// this->_year==d.year
}
// d1<d2
bool Date::operator<(const Date &d)const
{
return _year < d._year && (_year == d._year && _month < d._month) && (_year == d._year && _month == d._month && _day < d._day);
}
// d1<=d2
bool Date::operator<=(const Date &d)const
{
return *this < d || *this == d; //*this就是d1
}
// d1>d2
bool Date::operator>(const Date &d)const
{
return !(*this <= d);
}
// d1>=d2
bool Date::operator>=(const Date &d)const
{
return !(*this < d);
}
// d1!=d2
bool Date::operator!=(const Date &d)const
{
return !(*this == d);
}
// d1+=d2
Date &Date::operator+=(int day) // 出作用域*this还在,故用引用返回Date*
{
//+= --50
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
// d1+d2
Date Date::operator+(int day)const
{
Date tmp(*this); // 拷贝构造函数的定义,拷贝临时对象
tmp += day; // 对Date& operator+=(int day)进行复用
return tmp;
}
//++d1 返回的是++后的值
Date Date::operator++()
{
*this += 1;
return *this;
}
// d2++ 返回的是++前的值 ,由此可得:前置优于后置
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp; // 出作用域销毁无需销毁
}
// d1-=100;
Date &Date::operator-=(int day)
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// d1-100
Date Date::operator-(int day) //const
{
Date tmp(*this);
*this -= day;
return tmp;
}
//--d1
Date Date::operator--()
{
*this -= 1;
return *this;
}
// d2--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
// d1-d2
int Date::operator-(const Date &d)const
{
Date max = *this; // d1
Date min = d; // d2
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月 int _day ; // 日
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需 要重载,比如想让别人获取到指定的内容!
#include <iostream>
#include <assert.h>
using namespace std;
class Array
{
public:
int &operator[](int i)
{
assert(i < 10);
return _a[i];
}
const int &operator[](int i) const
{
assert(i < 10);
return _a[i];
}
private:
int _a[10];
int _size;
};
void Func(const Array &aa)
{
for (int i = 0; i < 10; i++)
{
cout << aa[i] << " ";
}
}
int main()
{
Array a;
for (int i = 0; i < 10; i++)
{
a[i] = i;
}
for (int i = 0; i < 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
Func(a);
cout << endl;
return 0;
}