拷贝构造函数和赋值运算符重载就是C++类默认六个函数之二。
拷贝构造函数:
如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数 也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。
拷贝构造函数的特点:
- 拷贝构造函数是构造函数的⼀个重载。
- 拷贝构造函数的第⼀个参数必须是类类型对象的引⽤,使⽤传值⽅式编译器直接报错,因为语法逻辑上会引发⽆穷递归调⽤。 拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后⾯的参数必须有缺省值。
- C++规定自定义类型对象进⾏拷贝⾏为必须调⽤拷贝构造,所以这⾥自定义类型传值传参和传值返回都会调⽤拷贝构造完成。
- 若未显式定义拷贝构造,编译器会⽣成⾃动⽣成拷贝构造函数。⾃动⽣成的拷贝构造对内置类型成 员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷贝构造。
- 传值返回会产⽣⼀个临时对象调⽤拷贝构造,传引⽤返回,返回的是返回对象的别名(引⽤),没有产⽣拷贝。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使⽤引⽤返回是有问题的,这时的引⽤相当于⼀个野引⽤,类似⼀个野指针⼀样。传引⽤返回可以减少拷贝,但是⼀定要确保返回对象,在当前函数结束后还在,才能⽤引⽤返回。
这是正确的拷贝构造函数,如果参数不是引用的话,首先传参的时候就要进行拷贝,会调用拷贝构造,拷贝构造有会调用拷贝构造,就这样无穷递归下去了,所以编译器会直接报错。

深拷贝与浅拷贝
浅拷贝就是一个字节一个字节的拷贝,一般情况下也没有什么问题,但是当有动态开辟的内存时,如果使用浅拷贝的方式写拷贝构造,那么当拷贝构造了对象时就会导致内存重复释放的问题。
上面的类中就是浅拷贝,会导致内存重复释放,
两个对象,aa,bb中的a指向的都是同一块堆内存,析构的时候,bb先析构,把这块内存释放了,aa析构的时候又释放了一次,这就会报错。
解决办法就是深拷贝:
就只要把a也进行动态开辟内存就行了。
就和前面的析构函数,只要类的成员中有动态开辟的成员,就要自己写析构,写拷贝构造并且是深拷贝。 没有动态开辟的成员,就可以不写析构函数和拷贝构造函数,默认提供的就可以了。
赋值运算符重载:
运算符重载:
- 当运算符被⽤于类类型的对象时,C++语⾔允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。
- 运算符重载是具有特殊名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
- 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
- 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。
- 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
- 不能通过连接语法中没有的符号来创建新的运算符,比如:operator@.
- 有五个运算符是不能重载的:.* , :: ,sizeof , ?: , .
- 重载操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int operator+(int x, int y)
- ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,⽐如Date类重载operator-就有意 义,但是重载operator+就没有意义。
- 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
- 重载 << 和 >> 时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象 << cout ,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。
下面以Date类为例,展示运算符重载
头文件
cpp
#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
//友元函数
friend ostream& operator<<(ostream& cout, const Date& d);
// 获取某年某月的天数
int GetMonthDay(int year, int month)
{
static int d[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = d[month];
if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0))
day++;
return day;
}
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
// d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
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);
private:
int _year;
int _month;
int _day;
};
源文件
cpp
#include"test.h"
ostream& operator<<(ostream& cout, const Date& d)
{
cout << d._year << "/" << d._month << "/" << d._day << endl;
return cout;
}
//全缺省构造函数
Date::Date(int year, int month, int day)//声明和定义只能一个写缺省值
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 日期+=天数
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)
{
Date tmp = *this;
tmp += day; // 复用 += 运算符
return tmp;
}
// 日期-天数
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day; // 复用 -= 运算符
return tmp;
}
// 日期-=天数
Date& Date::operator-=(int 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;
}
// 前置++
Date& Date::operator++()
{
*this += 1; // 复用+=
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;//复用+=
return tmp;
}
// 后置--
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;//复用-=
return tmp;
}
// 前置--
Date& Date::operator--()
{
*this -= 1;//复用-=
return *this;
}
//大量的复用
// >运算符重载
bool Date::operator>(const Date& d)
{
if (_year != d._year)return _year > d._year;
if (_month != d._month)return _month > d._month;
return _day > d._day;
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
return (_year == d._year
&& _month == d._month
&& _day == d._day);
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{
return (*this > d) || (*this == d);
}
// <运算符重载
bool Date::operator < (const Date& d)
{
return !(*this >= d);
}
// <=运算符重载
bool Date::operator <= (const Date& d)
{
return !(*this > d);
}
// !=运算符重载
bool Date::operator != (const Date& d)
{
return !(*this == d);
}
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
Date maxd = *this;
Date mind = d;//防止改变原数据
int n = 0;
int flag = 1;
if (*this < d)
{
maxd = d;
mind = *this;
flag = -1;
}
while (mind != maxd)
{
mind += 1;
n++;
}
return n * flag;
}
补充:
cpp
Date d2(2025, 3, 15);
Date d3 = d2;
上面的d3虽然用的是 = ,但是,实际上是调用拷贝构造函数,不是赋值重载! 只有原本就已经实例化了的对象才能用赋值重载,没有实例化的就是构造函数。