赞
踩
在C++中,运算符重载是一种强大的特性,它允许我们重新定义已有的运算符,以便用于用户自定义的数据类型。通过运算符重载,我们可以使得我们自定义的类对象像内置类型一样进行运算,这为编写清晰、简洁且易于理解的代码提供了便利。
在学习运算符重载之前:
我们对于内置类型的运算是这样的:int = int + int
;
但是我们对于自定义类型的类无法使用简单的运算符进行运算,我们需要特别得去写一个函数或者一段过程性代码来实现这个功能。
使用重载运算符:
bool operator==(const Date& d2)
{
return _year == d2._year;
&& _month == d2._month
&& _day == d2._day;
}
该代码就是对“”运算符的重载,之后通过使用”“就可以对比日期类的两个对象(根据该运算是否有意义来决定)。
使用时的代码就是如此:d1 == d2
。
函数原型:**返回值类型 operator操作符(参数列表)**
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this
- . * :: sizeof ? : . 注意以上5个运算符不能重载。这个经常在笔试选择题中出
现。
以日期类进行举例:
①非成员函数重载:
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
用
d1.operator(d2)
或者d1 == d2
都可以实现;
由于在类外实现重载,所以没有this指针,所以可以用类内,但是C++的语法会对此进行优化直接使用d1 == d2
也可以完成。
②成员函数重载:
bool operator==(const Date& d2)
{
return _year == d2._year;
&& _month == d2._month
&& _day == d2._day;
}
类内时,左操作数为this,指向调用函数的对象,可以直接用
d1 == d2
进行运算。
当运行到使用重载运算符的时候就会进行调用重载函数:
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
**检测是否自己给自己赋值
返回*this **:要复合连续赋值的含义
//作为成员函数,赋值运算符重载函数:
// 用Date类型引用返回使得可以连续赋值
Date& operator=(const Date& d)
{
if(this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
当尝试将赋值运算符重载函数作为静态函数进行定义时:
在C++中,赋值运算符 = 被设计为类的成员函数,这是因为它需要访问类的内部状态,并且需要能够处理自赋值的情况(即对象赋值给自己)。当你尝试将赋值运算符重载为全局函数时,会出现问题,原因如下:
- 成员访问权限:作为全局函数,赋值运算符将无法访问类的非公共(private或protected)成员变量。成员函数可以直接访问这些成员,因为它们是类的一部分。
- this指针:成员函数有一个隐式的指针 this,它指向调用该成员函数的对象。在成员函数内部,this 指针允许你访问对象的成员变量和其它成员函数。全局函数没有 this 指针,因此无法访问特定对象的状态。
- 自赋值保护:成员函数版本的赋值运算符可以检查自赋值,即对象赋值给自己。这可以通过比较 this 指针和传入的右值的地址来实现。全局函数没有 this 指针,因此无法进行这种检查。
- 语法要求:C++ 语法要求赋值运算符 = 必须是类的成员函数。尝试将其定义为非成员函数会导致编译错误,因为编译器期望赋值运算符是类的成员。
- 语义问题:赋值运算符的语义是将一个对象的值设置为另一个对象的值。作为成员函数,它清楚地表达了这一点,因为它是在对象上直接调用的。作为全局函数,这种语义就不那么明确了。
- 重载规则:C++ 的运算符重载规则限制了某些运算符(包括赋值运算符)只能作为成员函数重载。这是为了保持语言的一致性和防止潜在的错误使用。
因此,当尝试将赋值运算符重载为全局函数时,编译器会报错,因为它违反了C++的规则和赋值运算符的预期行为。正确的做法是将其作为类的成员函数来重载,以确保正确的访问权限、自赋值保护以及符合C++的语法和语义要求。
- 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。、(值拷贝/浅拷贝 类似Date )。
- 这个默认的赋值运算符会进行成员到成员的简单赋值,也就是逐个复制每个非引用、非指针成员变量的值。
- 如果类中包含了其他自定义类型作为其成员变量,并且这些自定义类型重载了赋值运算符 =,那么在进行类实例的赋值操作时,编译器会尝试调用这些成员变量类型的赋值运算符来完成赋值(MyQueue)。
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必
须要实现。
// 由于this指向的是函数外定义的对象,所以不会销毁,使用引用返回提高效率
Date& operator++()
{
_day += 1;
return *this;
}
当++d1
执行的时候将会返回++后的日期。
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
d1++
:后置++的效果是对++的对象进行处理后返回处理前的数值,所以在函数内定义一个对象进行储存++前的数据,不使用引用返回,使得在返回后构造函数,使程序正常进行。
由于前置++和后置++的源代码原本是没区别的,所以为了区别这两个,祖师爷用一种机制来进行区分使用:后置++运算符重载时,需要声明一个额外的int类型的参数,这个参数在实际使用时并不需要由程序员显式传递,而是由编译器在调用后置++时自动传递。
和流输入一样类内用友元函数。
istream& operator>>(istream& in, Date& d) { int year, month, day; in >> year >> month >> day; if (month > 0 && month < 13 && day > 0 && day <= d.GetMonthDay(year, month)) { d._year = year; d._month = month; d._day = day; } else { cout << "非法日期" << endl; assert(false); } return in; }
class Date { public: // 重载 operator&(),返回指向 Date 的常量指针 const Date* operator&() const { // 这里通常应该直接返回 this,但出于示例的目的,我们可能会做一些检查或其他逻辑 // 但实际上,直接返回 this 是最简单和最直接的方式 return this; } // 注意:如果你还想要一个非 const 版本的 operator&(),你可以这样重载它 Date* operator&() { // 同样的,直接返回 this return this; } };
int main() { Date date; // 使用重载的 operator&() const Date* constPtrToDate = date.operator&(); // 使用成员函数调用方式 // 但是通常你会这样写,因为编译器会隐式地使用 & 运算符 const Date* anotherConstPtrToDate = &date; // 注意:如果你重载了非 const 版本的 operator&(),你也可以这样使用 Date* ptrToDate = &date; // 或者 ptrToDate = date.operator&(); // ... 其他代码 ... return 0; }
例如在类中有以下函数:
void Print()
{
cout << _year << _month << _day << endl;
}
当我们想在主函数中调用该函数时,有两个不同的对象,const d1(2021.2.3),d2(2031, 2,1)
,当我们直接调用时会发现const d1无法调用Print,这时候就涉及到了权限的放大。
d1 和 d2是作为this指针传递的,Print中的this指针是Date* this
类型的,而d1是一个const Date*
型的,所以无法传入d2进行调用函数。
这时候就涉及到了const this
当使用const修饰this后,就可以将之前d1的权限放大变为权限平移,d2的权限平移变成权限缩小,这样就都可以实现调用。
实现代码格式如下:
// 将const加在函数后面
void Print() const
{
cout << _year << _month << _day << endl;
}
在使用的过程中要注意:
①要修改对象成员变量的函数不能加;
②只要成员函数内部不修改成员变量,就都应该加const,这样const对象和普通对象都可以调用。
③这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。