如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是一个特殊的构造函数。
拷贝构造的特点:
无穷递归:

//拷贝构造
#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;
}
private:
int _year;
int _month;
int _day;
};
//自定义类型,传值传参要调用拷贝构造(这里要用引用传参不能直接传值传参)
//我们调用会先完成d1拷贝构造给d,然后进入函数
//建议引用传参加上const(只要被引用对象不改变)
// void func(const Date &d)
void func(Date& d)//这里不能写成void func(Date d)
{
//...
}
int main()
{
Date d1(2025, 8, 1);
//拷贝构造 -- 拷贝同类型的对象来初始化
Date d2(d1);
//权限可以平移或缩小
const Date d3(2025, 8, 1);
Date d4(d3);
//这也是拷贝构造
Date d5 = d3;
//我们调用函数先完成传参再进入函数
func(d1);
return 0;
}但是如果是栈的话,需要自己实现拷贝构造,因为他需要的是深拷贝 对于日期类我们就是单纯的拷贝即可(浅拷贝/值拷贝) 浅拷贝的特点:1)一个对象修改,会影响另一个对象 2)析构时,释放俩次空间(同一块空间) 对于栈来说他不能浅拷贝,我们修改一个栈不能影响第二个 深拷贝的特点:1)不仅仅对成员拷贝,还要对指向资源空间数据进行处理(各自有各自的空间) 2)析构时,各自释放各自的
.、.*、::、sizeof、?:,注意以上 5 个运算符不能重载。(选择题里面常考,大家要记一下)
int operator+(int x, int y)
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
//这里最好写成(我们这里不用更改参数)
//bool operator==(const Date& x1,const Date& x2)
bool operator==(Date x1, Date x2)
{
//这里只是一个简单演示不是实现内容的写法
return true;
}
//这里最好写成(我们这里不用更改参数)
//bool operator-(const Date& x1,const Date& x2)
int operator - (Date x1, Date x2)
{
return 0;
}
int main()
{
Date d1(2025, 11, 29);
Date d2(2025, 11, 30);
//⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数
//顺序是不能换的
d1 == d2;
//等价于
operator==(d1, d2);
d1 - d2;
//等价于
operator-(d1, d2);
return 0;
}这里仅为简单展示一下,下面我们去详细实现:
//运行符顺序是对应的不能换
//但是我们成员函数是私有的,这里不能直接用该怎么解决?
bool operator==(const Date& x1, const Date& x2)
{
return x1._year == x2._year
&& x1._month == x2._month
&& x1._day == x2._day;
}#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//plan 1
//我们去调用公有的成员函数
int GetYear()
{
return _year;
}
int GetMonth()
{
return _month;
}
int GetDay()
{
return _day;
}
//我们将下面的_year、_month、_day均改成调用成员函数即可(java中常用)
private:
int _year;
int _month;
int _day;
};
//运行符顺序是对应的不能换
//但是我们成员函数是私有的,这里不能直接用该怎么解决?
bool operator==(const Date& x1, const Date& x2)
{
return x1._year == x2._year
&& x1._month == x2._month
&& x1._day == x2._day;
}
int operator-(const Date& x1, const Date& x2)
{
//这里我们先不实现了,比较复杂
return 0;
}
int main()
{
Date d1(2025, 8, 1);
Date d2(2025, 10, 1);
cout << (d1 == d2) << endl;
//可以写成
operator==(d1, d2);
//同上
d1 - d2;
operator-(d1, d2);
return 0;
}#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//plan 2
//我们在类外面不能访问,在类里面可以访问
//运行符顺序是对应的不能换
//但是我们成员函数是私有的,这里不能直接用该怎么解决?
//成员函数有一个隐含的this指针
//参数个数要和运算符的运算对象数量一样多,这样我们将operator函数变为成员函数多了一个隐含的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, 8, 1);
Date d2(2025, 10, 1);
cout << (d1 == d2) << endl;
//可以写成
d1.operator==(d2);
return 0;
}//介绍一个新的运算符 -- .*
//.*主要用来访问成员函数的指针
#include<iostream>
using namespace std;
void func1()
{
cout << "void func()" << endl;
}
class A
{
public:
void func2()
{
cout << "A::func()" << endl;
}
};
int main()
{
//函数指针的调用
void(*pf1)() = func1;
(*pf1)();
//成员函数指针也要指定类域
//A类型成员函数的指针(成员函数有个隐含的this指针)
//成员函数的指针前还要加&(语法规定)
void(A::*pf2)() = &A::func2;
A aa;
//this指针在形参和实参的位置均不能显示传递
//不能直接调用
(aa.*pf2)();
return 0;
}赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于一个对象拷贝初始化给另一个要创建的对象。
赋值运算符重载的特点:
//重载操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义
//如: int operator+(int x, int y)
//⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义
//⽐如Date类重载operator - 就有意义,但是重载operator + 就没有意义。
//日期相加是没有意义,但是日期加天数是有意义的
#include<iostream>
using namespace std;
Date operator+(const Date& d, int x);
int main()
{
return 0;
}//赋值运算符重载(赋值拷贝)
//拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象。
//赋值运算符重载是用于完成俩个已经存在的对象直接的拷贝赋值
#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;
}
//赋值运算符重载
//d3 = d5
//我们将d5赋值给了this指针,但是我们拿不到d3
//这里实际前面还有一个Date* const this
//this就是d3的地址,*this就是d3
//this指针在形参和实参的位置不能显示定义,但是在类里可以使用
//如果这里使用传值传参(特点就是不返回*this,返回的是*this的拷贝)
Date& operator =(const Date& d)
{
//防止自己赋值给自己
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
void Pirnt()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2025, 11, 25);
//拷贝构造 -- 一个已经存在的对象初始化另一个对象
Date d2(d1);
//一定注意,这是拷贝构造
Date d4 = d1;
Date d3(2025, 11, 26);
//赋值运算符重载(俩个已经存在的对象)
d1 = d3;
Date d5(2025, 11, 27);
d1 = d3 = d5;
//赋值支持连续赋值,从右往左
//首先是d3 = d5,d3作为表达式的返回值再赋值给d1
//所以这里是俩次函数调用
return 0;
}我们需要注意这段代码中的Date& operator =(const Date& d)这里为什么不用Date operator =(const Date &d):
原因:避免拷贝开销 + 支持连续赋值 使用
Date& operator=(返回引用),而不是Date operator=(返回值),主要有两个核心原因: 1. 避免不必要的拷贝,提升效率 如果返回Date(值),函数会在返回时拷贝当前对象(调用拷贝构造函数),产生额外的性能开销;而返回Date&(引用),直接返回当前对象本身,无拷贝操作,更高效。 2. 支持连续赋值(如d1 = d3 = d5) C++ 中连续赋值(a = b = c)的执行逻辑是从右到左:先计算b = c,再将结果赋值给a。
operator=返回Date&(引用),b = c的结果是b的引用,可直接参与后续赋值(a = (b = c));Date(值),b = c的结果是一个临时对象,虽然也能完成赋值,但临时对象会被销毁,且存在拷贝开销。这是 C++ 中赋值运算符重载的标准写法,既高效又符合语法习惯。(AI生成)
这里我们只是简单实现一些日期类的基本功能,下一篇博客我会为大家讲解取地址运算符重载,当我们学完之后我会为大家带来更详细的日期类的实现。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1);
void Print();
//给我一个年份和月份我们要获取这个月的天数
//高频调用的小函数最好使用内联,类里面的函数本身就内联/
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//闰年与平年2月天数
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
/*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);*/
// d1 += 天数
Date& operator+=(int day);
Date operator+(int day);
// d1 -= 天数
Date& operator-=(int day);
Date operator-(int day);
//// d1 - d2
//int operator-(const Date& d);
// ++d1 -> d1.operator++()
Date& operator++();
// d1++ -> d1.operator++(0)
// 为了区分,构成重载,给后置++,强⾏增加了⼀个int形参
// 这⾥不需要写形参名,因为接收值是多少不重要,也不需要⽤
// 这个参数仅仅是为了跟前置++构成重载区分
Date operator++(int);
Date& operator--();
Date operator--(int);
private:
int _year;
int _month;
int _day;
};#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Date::Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//日期+天数
//d1 += 100
//+=改变自己返回自己,传引用返回
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day = _day - GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
//不能改变自己
//d1 + 100
Date Date:: operator+(int day)
{
//拷贝构造
Date tmp(*this);
////我们去改变拷贝构造不去改变自己
//tmp._day += day;
//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//{
// tmp._day = tmp._day - GetMonthDay(tmp._year, tmp._month);
// ++tmp._month;
// if (tmp._month == 13)
// {
// ++tmp._year;
// tmp._month = 1;
// }
//}
tmp += day;
return tmp;
}
// d1 -= 天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
// 处理负天数(等价于 += 绝对值)
return *this += -day;
}
_day -= day;
// 当日期≤0时,向前借月/年
while (_day <= 0)
{
--_month; // 月份减1
if (_month == 0)
{
// 月份减到0,切换到上一年的12月
--_year;
_month = 12;
}
// 日期 += 当前月份的天数(向前借月,用当月天数补)
_day += GetMonthDay(_year, _month);
}
return *this; // 返回自身引用,支持链式操作(如 d1 -= 5 -= 3)
}
//d1 - 天数
Date Date::operator-(int day)
{
Date tmp(*this); // 拷贝原对象(不修改自身)
tmp -= day; // 调用 -= 完成计算(复用逻辑,避免冗余)
return tmp; // 返回新对象
}
//前置++
//++d1 -> d1.operator++( );
//调用完成后d1还在,所以用引用返回
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;
}#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
//d1+=天数/d1+天数/d1-=天数/d1-天数
int main()
{
//+=
Date d1(2025, 11, 29);
Date d2 = d1 += 100;
d1.Print();
d2.Print();
//+
Date d3(2025, 11, 29);
Date d4 = d3 + 100;
d3.Print();
d4.Print();
//-=
Date d5(2025, 11, 29);
Date d6 = d5 - 100;
d5.Print();
d6.Print();
//-
Date d7(2025, 11, 29);
Date d8 = d7 - 100;
d7.Print();
d8.Print();
return 0;
}
////前置++与后置++
////前置--与后置--
//int main()
//{
// Date d1(2025, 11, 29);
// Date ret1 = d1++;
// ret1.Print();
// d1.Print();
//
// Date d2(2025, 11, 29);
// Date ret2 = ++d2;
// ret2.Print();
// d2.Print();
//
// Date d3(2025, 11, 29);
// Date ret3 = d3--;
// ret3.Print();
// d3.Print();
//
// Date d4(2025, 11, 29);
// Date ret4 = --d4;
// ret4.Print();
// d4.Print();
//}