在 C++ 中,当你定义一个类(class
或 struct
)时,编译器会自动为你生成一些你没有显式(手动)定义的成员函数。这些函数被称为默认成员函数(Default Member Functions)。
一个类我们不写的情况下,编译器会默认生成6个成员函数,前4个最重要。C++11以后还增加了2个默认成员函数,分别是移动构造和移动赋值,这两个本篇文章不做讲解,主要讲解前4个。
构造函数是特殊的成员函数,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时,空间就开好了),而是对象实例化时初始化对象。构造函数的本质就是要替代之前类中的初始化函数(Init)的功能,构造函数自动调用的特点就完美的替代的了Init。
构造函数的特点:
注:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的原生数据类型, 如:int/char/double/指针等,自定义类型就是我们使用class/struct等关键字自己定义的类型。
#include<iostream>
using namespace std;
class Date
{
public:
// // 无参构造函数
// Date()
// {
// _year = 2025;
// _month = 10;
// _day = 13;
// }
// // 带参构造函数
// Date(int year, int month, int day)
// {
// _year = year;
// _month = month;
// _day = day;
// }
// 全缺省构造函数(不能和无参构造函数同时出现,也不能和带参构造函数同时出现)
// 写全缺省构造函数最好,这样就不用再写无参构造函数了
Date(int year = 2025, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day<< '\n';
}
private:
int _year;
int _month;
int _day;
};
int main()
{
// 调用默认构造函数(无参构造 或 全缺省构造 或 编译器默认生成的构造)
Date d1;
// 注意,代用默认构造函数创建对象时,后面不能加括号,否则编译器无法识别是函数声明还是实例化对象
// Date d1(); // err
// 调用带参构造函数
Date d2(2025, 10, 1);
d1.Print();
d2.Print();
return 0;
}
成员变量为自定义类型,编译器默认生成的构造函数会调用成员变量的构造函数
class Stack
{
public:
Stack(int n = 4)
{
_a = (int*)malloc(sizeof(int) * n);
_top = 0;
_capacity = n;
}
private:
int* _a;
int _top;
int _capacity;
};
// 两个栈实现一个队列
class Queue
{
public:
// Queue类的构造函数调用了Stack类的默认构造函数,实现对自定义类型成员变量的初始化
// 如果Stack类中没有默认构造,那么就必须用初始化列表初始化
private:
// int i; // 内置类型,编译器默认生成的构造函数对它是否初始化不确定,看编译器
Stack st1;
Stack st2;
};
int main()
{
Queue q1;
return 0;
}
析构函数与构造函数功能相反,析构函数不是完成堆对象本身的销毁;局部对象是存在栈帧的,函数结束栈帧销毁,它就释放了,不需要我们管。C++规定,对象销毁时自动调用析构函数,完成对对象中资源的清理释放工作。析构函数的功能可以类比 Stack 中的Destory函数的功能,而Date没有Destory,严格来说,Date是不需要析构函数的,但是析构函数都会默认生成
析构函数的特点:
class Date
{
public:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
// 这个析构函数可以不写,因为并没有资源的申请,这里是为了观察对象生命周期结束时是否会调用构造函数
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
// 析构函数会在对象生命周期结束时自动调用
int main()
{
Date d1(2025, 10, 1);
return 0;
}
如果一个类里面只有自定义类型的成员变量,那么如果我们不显示的写析构函数,对象生命周期结束时,会自动调用成员变量的析构函数;对于内置类型,它们没有析构,会在对象生命周期结束时自动销毁
// 构造函数写成默认构造比较好,这要如果另一个类中只有该类型的成员变量
// 就不用再写构造函数,而是直接调用这个类中的默认构造函数
class Stack
{
public:
Stack(int n = 4)
{
_a = (int*)malloc(sizeof(int) * n);
if (_a == nullptr)
{
exit(1);
perror("malloc fail!");
}
_top = 0;
_capacity = n;
}
~Stack()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
// 两个栈实现一个队列
class Queue
{
public:
// 不需要显式写析构函数,编译器会自动生成一个析构函数,这个析构函数会调用st1和st2的析构函数。
// 如果我们没有在Queue的析构函数中做其他事情,就不需要定义析构函数。
// 如果需要,可以这样写,但不要调用st1和st2的析构函数:
// ~Queue()
// {
// 这里可以处理Queue自己可能拥有的资源(但当前没有)
// 决不能在类的析构函数中显式调用成员变量的析构函数
// // 这里可以做一些其他清理,但不要调用st1和st2的析构函数
// }
private:
Stack st1;
Stack st2;
};
总结
拷贝构造函数是一种特殊的构造函数,它的作用是用一个已经存在的对象来初始化一个新创建的同类对象。
拷贝构造函数的特点:
拷贝构造函数在传值传参,传引用传参互传值返回,传引用返回的作用
class Date
{
public:
// 构造
Date(int year = 2025, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 构造
Date(const Date* d)
{
_year = d->_year;
_month = d->_month;
_day = d->_day;
}
// 拷贝构造(因为Date类中成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完成需要的拷贝,所以不需要我们显示实现拷贝构造,这里是为了观察)
Date(const Date& d) // 加const避免权限的问题,只要不是想要改变被引用的对象,最好就加上const
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 析构
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
// 传值传参会调用拷贝构造
void func1(const Date d)
{}
// 传引用传参不会调用拷贝构造
void func2(const Date& d)
{}
// 上面两个函数不会构成重载
// 传值返回
Date func3()
{
Date d;
return d;
}
// 传引用返回
Date& func4()
{
Date tmp;
return tmp;
}
int main()
{
Date d1;
// 拷贝构造
// Date d2(d1);
// Date d2 = d1;
// 可以利用指针完成拷贝,但是不是拷贝构造,只是⼀个普通的构造
Date d5(&d1);
// 传值传参 C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里传值传参要调用拷贝构造
// func1(d1); // 会调用拷贝构造
// 传引用传参,可以减少拷贝次数
// func2(d2); // 不会调用拷贝构造
// 传值返回
// Date d3 = func3(); // 编译器优化为直接构造
// Date d3(func3());
// 传引用返回
// func4返回了⼀个局部对象tmp的引⽤作为返回值
// func4函数结束,tmp对象就销毁了,相当于了⼀个野引⽤
// Date d4 = func4();
// Date d4(func4());
return 0;
}
如果成员变量申请了资源,要手动实现拷贝构造,实现深拷贝
class Stack
{
public:
// 构造
Stack(int n = 4)
{
_a = (int*)malloc(sizeof(int) * n);
_top = 0;
_capacity = n;
}
// 这是浅拷贝(错误)
// 1、一个对象修改,会影响另一个对象
// 2、析构时,同一块空间会释放两次
//Stack(const Stack& st)
//{
// _a = st._a;
// _top = st._top;
// _capacity = st._capacity;
//}
// 拷贝构造(深拷贝)
// 不仅对成员拷贝,还要对指向的资源空间数据进行处理
Stack(const Stack& st)
{
// 需要对_a指向资源创建同样⼤的资源再拷⻉值
_a = (int*)malloc(sizeof(int) * st._capacity);
if (_a == nullptr)
{
exit(1);
perror("malloc fail!");
}
// 将空间中的值都拷贝到 _a 中
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
// 入栈
void Push(int x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
int* tmp = (int*)realloc(_a, newcapacity *
sizeof(int));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
// 析构
~Stack()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
// 虽然也都是内置类型,但是_a指向了资源,
// 编译器⾃动⽣成的拷⻉构造完成的值拷⻉/浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)
int* _a;
int _top;
int _capacity;
};
// 两个栈实现一个队列
class MyQueue
{
public:
// MyQueue类的构造函数调用了Stack类的默认构造函数,实现对自定义类型成员变量的初始化
// 如果一个类中既有内置类型又有自定义类型,且我们不写拷贝构造函数
// 那么默认的拷贝构造函数会同时处理这两种类型:内置类型进行浅拷贝,自定义类型调用其拷贝构造函数。
private:
// int _i; // 内置类型默认构造函数进行浅拷贝
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;
}
运算符重载是C++中允许程序员为自定义类型重新定义运算符行为的特性。通过运算符重载,我们可以让自定义类型像内置类型一样使用各种运算符。重载之后,我们可以直接使用运算符进行操作,不用再去调用函数,为我们提供便利的同时,还使得代码更加易读,简洁。
运算符重载的特点:
前面不能重载的运算符中,.* 运算法之前没见过,在这里介绍一下(很少用,主要了解一下)
void func1()
{
cout << "void func1()" << endl;
}
class A
{
public:
void func2()
{
cout << "A::void func2()" << endl;
}
};
int main()
{
void(*pf1)() = func1; // 函数指针
(*pf1)(); // 调用函数
// A类型的成员函数指针
void(A::*pf2)() = &A::func2; // 成员函数指针
A a;
(a.*pf2)(); // .* 运算符,调用函数
return 0;
}
不能通过运算符重载改变内置类型对象的含义
// 运算符重载,参数类型中至少有一个是类类型,不能通过运算符重载改变内置类型对象的含义
int operator+(int x, int y)
{
return x - y; // err
}
运算符重载在全局的情况(大多数不建议)
class Date
{
public:
Date(int year = 2025, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
// 如果运算符重载在全局,那么将无法访问内部的成员变量
// 有4种方法可以解决:
// 1、成员放公有
// 2、Date提供getxxx函数(Java中常用)
// 3、友元函数
// 4、重载为成员函数 (C++中常用)
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._day
&& d1._month == d2._month
&& d1._day == d2._day;
}
int main()
{
Date d1(2020,1,1);
Date d2(2025, 1, 1);
bool ret = d1 == d2; // 隐式调用
// 编译器会转换成 operator==(d1, d2);
// 传参时,运算符左边的传给第一个参数,右边的传给第二个参数,要注意顺序
cout << (d1 == d2) << endl; // 要注意运算符的优先级(优先级和内置类型保持一致)
// operator==(d1, d2); // 显示调用(重载在全局)
return 0;
}
运算符重载在类的内部(比较好)
class Date
{
public:
Date(int year = 2025, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少一个
bool operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2025, 10, 1);
Date d2(2025, 10, 12);
bool ret = d1 == d2; // 隐式调用
// 编译器会转换成 d1.operator==(d2);
// d1.operator==(d2); // 显示调用(重载在类内部)
return 0;
}
赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于于一个对象拷贝初始化给另⼀个要创建的对象。
赋值运算符的特点:
// 成员变量不会申请资源
class Date
{
public:
Date(int year = 2025, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 有一个隐含的this指针,所以只要显示的写一个参数就行
Date& operator=(const Date& d)
{
// 不允许自己给自己赋值
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// d1 = d2表达式的返回对象应该为d1,也就是*this
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2025, 10, 1);
Date d3(d1); // 拷贝构造,一个对象去初始化另一个对象
Date d2(2025, 10, 13);
d1 = d2; // 赋值重载,完成两个已经存在的对象直接的拷贝赋值
// d1.operator=(d2); // 显示调用
return 0;
}
成员变量会申请资源的情况
// 成员变量申请资源
class Stack
{
public:
// 构造
Stack(int n = 4)
{
_a = (int*)malloc(sizeof(int) * n);
_top = 0;
_capacity = n;
}
// 拷贝构造
Stack(const Stack& st)
{
_a = (int*)malloc(sizeof(int) * st._capacity);
if (_a == nullptr)
{
exit(1);
perror("malloc fail!");
}
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
// 赋值重载
Stack& operator=(const Stack& st)
{
_a = (int*)malloc(sizeof(int) * st._capacity);
if (_a == nullptr)
{
exit(1);
perror("malloc fail!");
}
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
// 入栈
void Push(int x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
int* tmp = (int*)realloc(_a, newcapacity *
sizeof(int));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
// 析构
~Stack()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
// 两个栈实现一个队列
class MyQueue
{
public:
private:
// 如果一个类中既有内置类型又有自定义类型,且我们不写赋值运算符重载
// 那么默认的赋值运算符重载函数会同时处理这两种类型
// 1、对于内置类型:执行逐成员拷贝(按位复制)
// 2、对于自定义类型:调用该自定义类型的赋值运算符
// int i;
Stack pushst;
Stack popst;
};
int main()
{
//Stack st1(8);
//st1.Push(1);
//st1.Push(2);
//Stack st2(st1); // 拷贝构造
//Stack st3;
//st3 = st1; // 赋值重载
MyQueue mq1;
MyQueue mq2(mq1); // 拷贝构造
MyQueue mq3;
mq3 = mq1; // 赋值重载
return 0;
}
Date.h
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
// 友元函数的声明(放在哪里都可以)
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1);
// 该函数中有一个隐藏的this指针,Date* const this,而传过来的地址类型为const Date*,所以要在后面加一个const
// void Print(const Date* const this) const; // 里面的const是修饰this指针,后面的const修饰的是this指针指向的内容
// this指针有 Date* const this 变为 const Date* const this
void Print() const;
// 直接定义类里面,他默认是inline
// 频繁调用
int GetMonthDay(int year, int month) const
{
assert(month > 0 && month < 13);
// 存储在静态区,要不然每次都要在栈帧中创建数组
static int monthDayArray[13] = { -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 monthDayArray[month];
}
}
bool CheckDate() const
{
if (_month < 1 || _month>12)
return false;
if (_day<1 || _day>GetMonthDay(_year,_month))
return false;
return true;
}
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;
// d1 += 天数
// this指针,会把调用这个函数的对象的地址传过来
Date& operator+=(int day);
Date operator+(int day) const;
// d1 -= 天数
Date& operator-=(int day);
Date operator-(int day) const;
// d1 - d2
int operator-(const Date& d) const;
// ++d1 -> d1.operator++()
Date& operator++();
// d1++ -> d1.operator++(0)
// 为了区分,构成重载,给后置++,强行增加了⼀个int形参
// 这⾥不需要写形参名,因为接收值是多少不重要,也不需要⽤
// 这个参数仅仅是为了跟前置++构成重载区分
Date operator++(int);
// 前置--
Date& operator--();
// 后置--
Date operator--(int);
// d1.operator<<(cout);
// 日期类d1传给了this,out是cout的别名
//void operator<<(ostream& out)
//{
// // 输出多个内置类型的变量,cout是有返回值的,输出多个其实是多次流插入函数的调用
// out << _year << "-" << _month << "-" << _day << '\n';
//}
private:
int _year;
int _month;
int _day;
};
// cout要占据第一个位置,如果重载为成员函数,this指针默认第一个是无法更改的,所以要重载成全局函数
// void operator<<(ostream& out, const Date& d); // 这个不能连续输出
// 把 d 中的值读出来,往out里面写
ostream& operator<<(ostream& out, const Date& d);
// 把 in 中的值读出来往 d 里面写,所以不能加const
istream& operator>>(istream& in, Date& d);
Date.cpp
#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "非法日期:>" << *this << endl;
}
}
void Date::Print() const
{
// this -> _year++; // err 加上const后,this指针指向的内容也不能修改了
cout << _year << "-" << _month << "-" << _day << endl;
}
//// d1 += 100 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; // 不是局部对象,可以传引用返回
//}
//
//// d1 + 100 d1自身不改变
//Date Date::operator+(int day) const
//{
// Date tmp(*this); // 用tmp拷贝d1
//
// 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是局部对象,所以不能用传应用返回
//}
// d1 += 100 d1自身也会改变
// 0次拷贝
Date& Date::operator+=(int day)
{
// 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; // 不是局部对象,可以传引用返回
}
// d1 + 100 复用+=实现+
// 2次拷贝
Date Date::operator+(int day) const
{
Date tmp(*this); // 用tmp拷贝d1
tmp += day; // 直接用上一个函数
return tmp; // tmp是局部对象,所以不能用传应用返回
}
// 这种复用方式更好,总的拷贝次数少(让拷贝多的复用拷贝少的)
//// d1 += 100 复用+实现+=
// 3次拷贝
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
//
// return *this; // 不是局部对象,可以传引用返回
//}
//
//// d1 + 100 d1自身不改变
// 2次拷贝
//Date Date::operator+(int day) const
//{
// Date tmp(*this); // 用tmp拷贝d1
//
// 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是局部对象,所以不能用传引用返回
//}
// d1 -= 100 自身也会改变
Date& Date::operator-=(int day)
{
// 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;
}
// d1 - 100 自身不会改变
// 复用-=实现-
Date Date::operator-(int day) const
{
Date tmp = *this; // 拷贝构造
tmp -= day;
return tmp;
}
// 前置++和后置++
// ++d1 -> d1.operator++();
// 前置++没有拷贝,用前置比较好
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++ -> d1.operator++(0); // 里面的数字只要是整型即可
// 后置++两次拷贝
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;
}
// 日期比较大小
// 多复用,提高代码可维护性
bool Date::operator<(const Date& d) const
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
else if (_month == d._month)
return _day < d._day;
}
return false;
}
// d1 <= d2 (d1是this,d2是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);
}
bool Date::operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d) const
{
return !(*this == d);
}
// d1 - d2
// 法1:
// 先计算两个日期各自距离自己所在年份的1月1日有多少天,设分别为x和y
// 两个年份相减得到z
// z*365 + 跨越的闰年数 + (x-y)
// 法2:
// 比较两日期,找到较小的;小日期不断++,直到与大日期相等,++了多少次,他们就差了多少天
int Date::operator-(const Date& d) const
{
Date maxDate = *this;
Date minDate = d;
int flag = 1;
// 如果第一个日期小,第二个日期大,相减是负数
if (*this < d)
{
maxDate = d;
minDate = *this;
flag = -1;
}
int day = 0;
while (minDate != maxDate)
{
++day;
++minDate;
}
return day * flag;
}
// 流插入和流提取重载
// 如果不是有元函数,那么类的内部要提供Get()函数,才能访问类内部的成员变量
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << '\n';
return out;
}
// 如果不是有元函数,那么类的内部要提供Set()函数,才能访问类内部的成员变量
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (d.CheckDate())
{
break;
}
else {
cout << "输入日期非法,请重新输入!" << endl;
}
}
return in;
}
test.cpp
int main()
{
Date d1(2025, 10, 1);
Date d2(2025, 10, 13);
//cin >> d1;
operator>>(cin, d1);
//cout << d1;
operator<<(cout, d1);
int i = 0;
cin.operator>>(i); // istream类内部重载了内置类型的流提取
cout.operator<<(i); // ostream类内部重载了内置类型的流插入
return 0;
}
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。除非一些很特殊的场景,比如我们不想让别⼈取到当前类对象的地址,就可以自己实现⼀份,胡乱返回一个地址。
class Date
{
public :
Date* operator&()
{
return this;
// return nullptr;
}
const Date* operator&()const
{
return this;
// return nullptr;
}
private :
int _year ; // 年
int _month ; // ⽉
int _day ; // ⽇
};
结语
如有不足或改进之处,欢迎大家在评论区积极讨论,后续我也会持续更新C++相关的知识。文章制作不易,如果文章对你有帮助,就点赞收藏关注支持一下作者吧,让我们一起努力,共同进步!