C++——拷贝构造和运算符重载

时间:2022-12-22 16:04:09

1. 拷贝构造函数

1.值传递

#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(date d)//值传递 date d 会报错
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2022,12,21);
date d2(d1);//拷贝构造
return 0;
}
  • 这里为什么会报错?
  • 存在递归拷贝

C++——拷贝构造和运算符重载


d对象初始化时需要自动调用构造函数,

调用函数之前需先传参,

传参过程中又是一个拷贝构造

  • 上面是一个严谨的分析过程,没看懂就继续往下看

C++——拷贝构造和运算符重载

d2 (d1) ——(用d1拷贝构造d2) 对象初始化之前自动调用构造函数
调用函数之前先传参 将参数d1传给构造函数 的参数d 的过程中,

由于是传值调用,相当于将d1的拷贝传给d

即拷贝构造 —— date d (d1) (用d1构造d)
再次进行拷贝构造
参数 d1传给 参数d 的过程中,又进行拷贝构造
无休止递归下去

2. 引用传递

C++——拷贝构造和运算符重载


由于d为d1的别名,所以 参数d1传给 d的过程中, 不会发生拷贝构造

#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;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2022,12,21);
date d2(d1);//拷贝构造
return 0;
}

加入const,是为了防止由于操作失误导改变d本身 如:假设 d._year =_year , _year代表d2._year ,将d2中的年赋值给d1的年,就会导致报错

3. 浅拷贝问题

#include<iostream>
using namespace std;
class stack
{
public:
stack(int n)//构造函数
{
_a = (int*)malloc(sizeof(int) * n);
_size = 0;
_capity = n;
}
~stack()//析构函数
{
free(_a);
_a = nullptr;
_size = _capity = 0;
}

private:
int * _a;
int _size;
int _capity;
};
int main()
{
stack s1(10);
stack s2(s1);//拷贝构造
return 0;//空间会被释放两次,程序崩溃
}
  • 以上代码为什么一运行就会报错?
    -
    s1._a 指针指向 开辟的10个字节的空间,由于是拷贝构造,所以将s1._a指针的值 赋值给了 s2._a指针, 使s2._a指针同样指向与s1._a相同的位置,
    又由于是先构造的后析构,所以理应先析构 s2 ,s2free这块空间后,由于s1._a与s2._a指向同一个位置,s1还会对这块空间再次free
    同一块空间释放两次,会导致崩溃

2. 运算符重载

1. 为什么不可以直接比较?

#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
return 0;
}
  • date 实例化出的对象 d1 与d2 的大小可以直接比较嘛?<font 不可以,自定义类型是自己定义的,怎么比较大小由自己规定
    内置类型 是编译器自己定义的类型,它知道要怎么比

C++为了增强代码的可读性引入运算符重载,运算符重载是具有特殊函数名的函数

2. 操作符 ==

1. 错误写法
#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//private://需要将_year等改为public
int _year;
int _month;
int _day;
};
bool operator ==(const date& d1, const date& d2)//由几个参数,就接收几个
{
//判断年月日是否都相等
return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
d1 == d2;
cout << (d1 == d2) << endl;// 0 代表假
return 0;
}

但是这种方法会 把date类中prviate的里面的成员变量变成共有的 否则就会报错

2. 正确写法

写入类中作为成员函数

#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
bool operator ==( const date& d2)// 由于隐藏的this指针的存在,&d1传过来由this指针接收
{
return _year == d2._year && _month == d2._month && _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};

int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
d1 == d2;// d1.operator==(d2) 等价于 d1.operator==(&d1,d2)
cout << (d1 == d2) << endl;
return 0;
}

传入类中,由于隐藏的this指针的存在,所以&d1传过去被this接收了,_year代表 d1._year 但是因为它是隐藏的,所以&d1也不需要表现出来直接传入d2即可<

3. 操作符 >

#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
bool operator >( const date& d2)// 由于隐藏的this指针的存在,&d1传过来由this指针接收
{
if (_year > d2._year)//年大为大
{
return true;
}
else if (_year == d2._year && _month > d2._month) //年相,月大为大
{
return true;
}
else if (_year == d2._year && _month == d2._month && _day > d2._day)//年 月相等,天大为大
{
return true;
}
else
{
return false;
}
}
private:
int _year;
int _month;
int _day;
};

int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
d1 > d2;//等价于 operator>(&d1,d2)
cout << (d1 > d2) << endl;
return 0;
}

判断大于时 ,同样存在一个隐藏的this指针,我们只需要判断 年大的就为大 ,年相等 月大的就为大,年月相等,天大的就为大,其他情况都为假

4. 注意事项

1. 不能通过连接其他符号来创建新的操作符 (如 operator@)

2.重载操作符必须有一个类类型或者枚举类型的操作数

C++——拷贝构造和运算符重载


date 就属于是自定义类型</font><font color=blue> 3.用于内置类型的操作符,其含义不能改变(如 int 加法 不能改变)

4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参

C++——拷贝构造和运算符重载


this指针是隐藏的,所以也不用传过来,this指针默认为第一个参数,并且代表d1

5. ( . * ) (:: ) (sizeof ) (? : 三目运算符) ( . ) 以上5个运算符不能重载

5.操作符 !=

#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
bool operator==(const date& d2)//d1==d2
{
return _year == d2._year && _month == d2._month && _day == d2._day;
}
bool operator!=(const date& d2)
{
return !(*this == d2);//借助上面的d1==d2的相反即 d1!=d2
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
d1 != d2;
cout << (d1 != d2) << endl;
return 0;
}

借助上面已经写好的d1==d2 ,取其相反 即为 d1!=d2

6. 操作符 <

#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
bool operator >(const date& d2)// 由于隐藏的this指针的存在,&d1传过来由this指针接收
{
if (_year > d2._year)//年大为大
{
return true;
}
else if (_year == d2._year && _month > d2._month) //年相,月大为大
{
return true;
}
else if (_year == d2._year && _month == d2._month && _day > d2._day)//年 月相等,天大为大
{
return true;
}
else
{
return false;
}
}
bool operator<(const date& d2)// d1 < d2
{
return !(*this > d2);
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
d1 < d2;
cout << (d1 < d2) << endl;
return 0;
}

若直接写d1<d2 的条件太复杂,直接采用 d1 >d2 的相反,*this 代表 d1

7 .赋值操作符

1. 正常使用
#include<iostream>
using namespace std;
class date
{
public:
date(int year = 1, int month = 1, int day = 1)//构造
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void operator=(const date& d2)//为了防止自己给自己赋值的事情发生,如:d1=d1
{
if ( this == &d2)
{
_year = d2._year;
_month = d2._month;
_day = d2._day;
}
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2022,12,21);
date d2(2022,12,22);
d1 =d2;
return 0;
}
2. 连续赋值情况的考虑

C++——拷贝构造和运算符重载

如果为内置类型,如int 则可以进行连续赋值 对于 int 来说,j=z ,返回值为z ,i=z,生成最终结果 但是如果为自定义类型,连续赋值就不可以,从右开始 d3=d1 同样也应该有一个返回值,用来与d2进行赋值

传值返回

C++——拷贝构造和运算符重载


由于*this为date 类型,属于传值返回,即返回一个临时变量,所以需进行拷贝构造

同时也会多开辟一块空间存储</font>,<font color=red> 就会导致当return ( * this) 返回时,传入 拷贝构造中创建临时变量 ,再次从中返回时,才能返回到 主函数中

传引用返回

C++——拷贝构造和运算符重载


传 * this的引用作为变量的别名,相当于 * this本身,不会消耗一块空间,自然在return*this 返回时,不会进入拷贝构造中,而是直接返回