C++ 赋值运算重载,const成员,取地址及const取地址操作符重载
- [1. 赋值运算符重载](#1. 赋值运算符重载)
-
- [1.1 运算符重载](#1.1 运算符重载)
- [1.2 赋值运算符重载](#1.2 赋值运算符重载)
- [1.3 前置++/--和后置++/--重载](#1.3 前置++/--和后置++/--重载)
- [2. const成员](#2. const成员)
- [3. 取地址及const取地址操作符重载](#3. 取地址及const取地址操作符重载)
所属专栏:C"嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️
🚀 >关注我,关注我,关注我,重要的事情说三遍!!!!!!!!❤️
1. 赋值运算符重载
1.1 运算符重载
- 我们知道,两个内置类型之间是可以进行适合的运算的,比如两个整形是可以进行加减乘除的,也可以进行比较两个数的大小关系,以此相对应char,double类型的也是可以进行类似的操作的。但是这些都是仅仅有限于内置类型,如果变成了自定义类型呢?自定义类型也可以做到像自定义类型一样的操作吗?所以C++中为了解决这个方法,有了运算符重载的概念。
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
-
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
4..* :: sizeof ?: .
注意以上5个运算符不能重载。这个经常在笔试选择题中出
现class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// bool operator==(Date* this, const Date& d2)
// 这里需要注意的是,左操作数是this,指向调用函数的对象
bool operator==(const Date& d2) //操作数opertor
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};int main()
{
Date d1;
Date d2;bool ret = d1 == d2; // ------> 本质是d1.operato==(d2);(operato==整体看作是一个函数名),但是做了优化之后就可以直接使用 if (ret) { cout << "d1 == d2" << endl; } else { cout << "d1 != d2" << endl; } return 0;
}
1.2 赋值运算符重载
1.赋值运算符重载格式
-
参数类型:const T&,传递引用可以提高传参效率
-
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
-
检测是否自己给自己赋值
-
返回*this :要复合连续赋值的含义
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;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};int main()
{
Date d1;
Date d2(2023, 10, 28);
d1 = d2;//本质上是------> d1.operator=(d2)return 0;
}
-
赋值运算符只能重载成类的成员函数不能重载成全局函数
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}int main()
{
Date d1;
Date d2(2023, 10, 28);
d1 = d2;//本质上是------> d1.operator=(d2)return 0;
}
我们在看一段代码:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2023, 10, 28);
d1 = d2;//这里没有进行重载,但是也是可以的
return 0;
}
- 原因:赋值运算符如果不显式实现,
编译器会生成一个默认的
。此时用户再在类外自己实现
一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数
3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注
意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符
重载完成赋值。这一点和前面的拷贝构造是一样的道理。
- 那么有小伙伴就会问了,既然我们不写显示实现运算符重载编译器会帮我们实现重载的效果,那我们为什么还要自己再写一个呢?这个时候我们可以回顾一下我们之前的拷贝构造,在我们没有涉及到资源管理的时候,拷贝构造是正常的,但是一旦我们malloc一块空间的时候就出问题了,而这里也是一样的,起始赋值运算是一种特殊的拷贝构造。
1.3 前置++/--和后置++/--重载
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 前置++:返回+1之后的结果
// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
Date& operator++()
{
_day += 1;
return *this;
}
private:
int _year;
int _month;
int _day;
};
- 上面是前置++ 的运算符重载操作。
现在小伙伴们想一想后置++怎么办呢?
首先就是他的重载格式是什么样的?
返回类型是什么?
因为我们的后置++ 的规则是先使用后++的,也就是说只有在一条语句结束后才会++。
那么我们因该怎么实现呢?
在C++中为了区分前置和后置,C++中用占位参数来区分前置和后置,没有占位参数的是前置,又占位参数的是后置,并规定占位参数的类型是int。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 后置++:
// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1
// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
Date operator++(int)
{
Date temp(*this);//先保存原先的值
_day += 1;//再进行加加
return temp;//最后返回保存的值
}
private:
int _year;
int _month;
int _day;
};
2. const成员
将const修饰的"成员函数"称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 11, 1);
d1.Print();
return 0;
}
我们看这串代码是没什么问题的,但是如果我用const修饰一下d1呢?
const Date d1(2023, 11, 1);
d1.Print();
这个时候还能通过吗?
这个时候就会报错啦,因为存在权限放大:
原本是:
- 但是我们对d1用const修饰的时候,我们传过去的类型是const d1是不可修改的,但是我们函数接受的参数类型却不是const不可修改的类型,所以这里就有权限放大的问题。
解决这个问题的办法也很简单,就是把我我们的形参也改成const修饰的参数:即const Date* const this
但是问题是const加在那里,C++规定成员函数用const修饰加在形参列表之后:
我们在看一下这段代码:
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print() const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
const Date d1(2023, 11, 1);
d1.Print();
Date d2(2023, 11, 1);
d2.Print();
return 0;
}
- 这里的d2会不会出错呢?
分析一下,我们的成员函数是用const修饰的,而d2却没有使用const修饰的。
但是这里是不会报错的,因为这里是属于权限的缩小。
C++规定,权限允许权限的平移和缩小的。
所以既然权限允许缩小的话,这里建议小伙伴们在写成员函数的时候,成员对象不可修改尽量用const修饰,这样跟更能保证代码的容错率,因为即使成员对象没有用const修饰,有权限缩小这特性还是可以通过的。
3. 取地址及const取地址操作符重载
-
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{
public:
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};int main()
{
Date d1;
Date d2;cout << &d1 << endl; cout << &d2 << endl; return 0;
}