🎁个人主页:我们的五年
🔍系列专栏:C++课程学习
🎉欢迎大家点赞👍评论📝收藏⭐文章
目录
✍拷贝构造:
🍉特点一:
🍉特点二:
🍉特点三:
🍉特点四:
拷贝构造函数三种调用场景:
✍运算符重载:
🍭赋值运算符重载:
🍭前置++和后置++的重载:
前言:
前面已经学过默认成员函数,但是只讲了构造函数和析构函数。这篇文章就来讲一讲剩下的两个默认成员函数:拷贝构造函数和赋值运算符重载。
拷贝构造函数也是属于构造函数的,只是拷贝构造是用另外一个对象里的数据给新创建的对象赋值。
✍拷贝构造:
拷贝构造:
拷贝构造只有一个和要创建的对象同类型的参数的引用,而且一般用const修饰,因为这个参数只要读取权限就可以了。因为他属于构造函数,所以我们传参时满足这一特点,编译器就会调用这个构造函数。
🍉特点一:
拷贝构造是构造函数的一种重载形式。
🍉特点二:
拷贝构造的参数只有一个,并且一定要是同类型的对象的引用。
比如:
Data (const Data& d){
……
}
🍉特点三:
如果是传值拷贝构造,那么就会发生无穷递归。
看看下面的错误示范:
#include<iostream>
using namespace std;
class Data {
public:
Data(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
Data(const Data d) {
//……
}
private:
int _year=1;
int _month=1;
int _day=1;
};
int main()
{
Data a(2024,6,11);
Data b(a);
return 0;
}
●b实例化的时候,就会去调用拷贝构造,然后把实参a传给形参d.
●但是传值要进行拷贝,调用d拷贝构造,把a拷贝给d。
●差不多每次传给另外一个参数,就要调用一次拷贝构造,这样就会一直重复下去。
🍉特点四:
如果我们不显式实现,因为也是属于默认成员函数,所以编译器会自己生成。编译器会自己生成,编译器自己生成的拷贝构造函数是按内存存储按字节序列进行拷贝的。这种拷贝叫浅拷贝,或者值拷贝。因为如果类的成员有的是指针,那也是拷贝同样的指针,这样是不行的。
自定义类型就是调用它的拷贝构造函数。
总结:
如果一个类有空间申请,那么一定要显式自己写拷贝构造函数。如果没有资源的清理,那么编译器生成的也是可以的。比如日期类就不要显式实现。
拷贝构造函数三种调用场景:
1.在创建新的对象的时候,参数是同类型对象的引用。
2.在传值传参的时候,也是要调用拷贝构造函数的。
3.当类类型作为返回值的时候,也是要调用拷贝构造函数的。
从上面的三种场景来看,日常我们可以不用传值就不要用传值。也就是尽可能的使用传引用,除非特殊情况一定要用传值。
✍运算符重载:
概念:
在类中,为了增强代码的可读性,C++中引入了运算符重载这一概念。因为是重载,所以我们也可以推断出,我们用一样的运算符,但是是不同的类型的时候,就会调用不同的函数。
运算符重载是特殊函数名的函数,有返回类型和参数,返回类型和参数与普通的函数差不多。
●函数名:operator+要重载的运算符。
●原型:返回类型 函数名(参数列表)。
●必须是已存在的运算符才能重载,比如>,<。@这个符号就不能进行运算符重载。
●重载操作符必须有一个类类型的参数。
●虽然看起来形参比操作对象少一个,其实是隐藏了一个this指针,this指针是指向最前面那个操作对象的。
● .* sizeof :: . ?: 这五个运算符是不能进行重载的。
🍭赋值运算符重载:
赋值运算符重载的格式:
● 参数类型:const 类型&,传递引用可以提高效率。
●返回类型:类型&(这个类类型的引用),返回引用是为了可以支持连续赋值,比如c=b=a,顺序是,先是a给b赋值,然后返回b,然后在b给c赋值。
●赋值运算符只能重载成类的成员函数,不能重载为全局函数。
因为如果重载为全局函数,这时候在类里面没有显示实现,所以编译器会自己生成一个赋值运算符。这样我们调用赋值的时候,编译器就不知道去调用哪个赋值函数。
●编译器自己生成的赋值运算符,对于内置类型,也是去按内存的字节序进行赋值。对应自定义类型,那么就去调用它的赋值运算符函数。
下面是日期类的显式赋值运算符重载:
#pragma once
#include<iostream>
#include<stdbool.h>
#include<assert.h>
#include<math.h>
using namespace std;
public:
//全缺省的构造函数
Data(int year = 1, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Data(const Data& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//赋值运算符重载
Data& operator = (const Data& d) {
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
//析构函数
~Data() {
_year = -1;
_month = -1;
_day = -1;
}
private:
int _year;
int _month;
int _day;
};
🍭前置++和后置++的重载:
先来说这两个的区别:
1.前置++:调用++函数以后,返回++之后的值。
2.前置++:调佣++函数以后,返回++之前的值。
因为这两个++的函数名都是operator++,为了构成区分,我们的C++祖师爷在后置++里面放了一个int参数,这样与前置++就可以构成函数重载;
因为前置++可以返回的++以后的值,所以我们++以后,就直接可以用引用返回。
但是后置++,返回的++之前的值,这样我们传值返回。
日期类的前置++和后置++:
#pragma once
#include<iostream>
#include<stdbool.h>
#include<assert.h>
#include<math.h>
using namespace std;
class Data {
friend ostream& operator<<(ostream& out, Data& d);
friend istream& operator>>(istream& in, Data& d);
public:
//全缺省的构造函数
Data(int year = 1, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Data(const Data& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//+=运算符重载
Data& operator+=(int day) {
if (day < 0)
{
return *this -= -day;
}
_day += day;
while (_day > GetDay(_year,_month))
{
_day -= GetDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
//日期的前置++
Data& operator++() {
return *this += 1;
}
//日期的后置++
Data operator++(int) {
Data a = *this;
*this += 1;
return a;
}
Data operator++(int);
//析构函数
~Data() {
_year = -1;
_month = -1;
_day = -1;
}
private:
int _year;
int _month;
int _day;
};