网络班级网站建设,河南省村镇建设处网站,网站关键词几个合适,房地产公司网站建设pptC类和对象进阶#xff1a;运算符重载 前言引入运算符重载定义语法注意事项重载为全局函数重载为成员函数运算符重载的本质 默认赋值运算符重载(默认成员函数)编译器自己生成的赋值运算符重载函数需要自己实现的场景总结默认赋值运算符重载 拷贝构造函数和赋值重载的区分验证 总… C类和对象进阶运算符重载 前言引入运算符重载定义语法注意事项重载为全局函数重载为成员函数运算符重载的本质 默认赋值运算符重载(默认成员函数)编译器自己生成的赋值运算符重载函数需要自己实现的场景总结默认赋值运算符重载 拷贝构造函数和赋值重载的区分验证 总结 前言
在C中运算符重载允许我们为自定义类型赋予与内置类型相似的操作方式极大提升了代码的可读性和灵活性。本文将深入探讨运算符重载的规则与实现并重点分析默认成员函数之一的赋值运算符重载函数。 引入
class Date {
public:Date(int year 2025, int month 2, int day 22) {this-_year year;this-_month month;this-_day day;}
private:int _year;int _month;int _day;
};
int main(){Date d1, d2;//d1 d2; // 若无运算符重载这样的写法未定义。//if(d1 d2){;} // 若无运算符重载这样的写法未定义。
}思考以下场景 如果想
1. 比较两个日期类对象是否相等2. 两个日期相减的运算来计算相差的天数3. 计算一个日期100天后是什么日期
C为了满足自定义类型中以上类似需求并为了增强代码的可读性引入了运算符重载。
运算符重载
定义
运算符重载是具有特殊函数名的函数也具有其返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。
语法
函数名字为关键字operator后面接需要重载的运算符符号。 例如要对进行重载
//假定返回值为bool
bool operator(int x, int y); //声明函数原型返回值类型 operator操作符(参数列表)
注意事项
不能通过连接其他符号来创建新的操作符比如operator。重载操作符必须有一个类类型参数。用于内置类型的运算符其含义不能改变例如内置的整型不 能改变其含义。作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this指针。.* ::, sizeof, ? : , . 注意以上5个运算符不能重载。
总结以上内容
函数格式返回类型 operator运算符(参数列表)关键限制 只能重载C中已有的运算符不能创造新的运算符。重载运算符主要是针对自定义类型的因此operator必须有一个类类型参数.不能改变运算符对内置类型操作的原始含义。要重载的运算符有几个操作数operator中就有几个参数(算上this指针)以下运算符不可重载.* :: sizeof ?: .。
重载为全局函数
bool operator(const Date d1, const Date d2) { if (d1._year d2._year)return true;else if (d1._year d2._year d1._month d2._month)return true;else if (d1._year d2._year d1._month d2._month d1._day d2._day)return true;elsereturn false;
}
class Date {
public:Date(int year 2025, int month 2, int day 22) {this-_year year;this-_month month;this-_day day;}
//private: //暂时设为public,是为了让全局重载的可以访问到类内的成员变量
public:int _year;int _month;int _day;
};
int main(){Date d1(2025, 2, 12);Date d2(2024, 2, 12);cout (d2 d1) endl;//d2 d1 会被编译器转换成 d2.operator(d1),本质上是调用函数return 0;
}注意d1 d2, 重载后的 Date类对象使用号时左操作数是这里的d1右操作数是这里的d2
这里会发现运算符重载成全局函数需要成员变量是公有的那么问题来了封装性如何保证
为解决这一问题我们可以重载成成员函数。
重载为成员函数
//运算符重载
class Date {
public:Date(int year 2025, int month 2, int day 22) {this-_year year;this-_month month;this-_day day;}//操作符是几个操作数operator函数就有几个参数(应该包括上隐含的this指针参数)//也可以全局重载但在类内重载更方便可以直接访问私有成员bool operator(const Date d) { //自定义类型最好传引用类内不能通过形参d修改原变量加上constif (this-_year d._year)return true;else if (this-_year d._year this-_month d._month)return true;else if (this-_year d._year this-_month d._month this-_day d._day)return true;elsereturn false;}
private:int _year;int _month;int _day;
};bool operator(const Date d);这里需要注意的是由于第一个形参是this, 左操作数是*this是调用函数的对象右操作数是传入的另一个对象。是否需要重载运算符要看这些运算符对该类型是否有意义
对比分析
特性成员函数形式全局函数形式访问权限可直接访问私有成员需友元声明左操作数类型必须是类对象任意类型隐式this参数有无对称性操作符不便于处理更适合如流操作符
掌握以上规则我们便学会了如何对运算符进行重载。
接下来来看六大默认成员函数中的赋值运算符重载。
运算符重载的本质 由上图汇编代码可以看到 d1 d2 d1.operator(d2) 本质都是调用了类内的函数。也正因如此运算符重载函数也可以按照函数重载的规则重载。
默认赋值运算符重载(默认成员函数)
我们早已知道赋值运算符重载是类内的一个默认成员函数。 C语言中自定义类型可以完成赋值操作(例如同类型结构体之间的赋值)C中的class同样支持赋值操作只不过C对这一行为进行了优化与升级。 赋值运算符重载格式 参数类型const T传递引用可以提高传参效率返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值检测是否自己给自己赋值允许自己给自己进行赋值但此时不需要拷贝直接返回当前对象本身。返回*this 要复合连续赋值的含义
//运算符重载
class Date {
public:Date(int year 2025, int month 2, int day 22) {this-_year year;this-_month month;this-_day day;}//操作符是几个操作数operator函数就有几个参数(应该包括上隐含的this指针参数)//也可以全局重载但在类内重载更方便可以直接访问私有成员bool operator(const Date d) { //自定义类型最好传引用类内不能修改加上constif (this-_year d._year)return true;else if (this-_year d._year this-_month d._month)return true;else if (this-_year d._year this-_month d._month this-_day d._day)return true;elsereturn false;}//赋值运算符重载是默认成员函数编译器会自己生成 不能写成全局的Date operator(const Date d) {//if (*this ! d) // 这样子比较代价有点大将 ! 重载后是可以实现的但是没必要if (this ! d) { //防止这样的赋值 d1 d1 如果自己给自己赋值可以不复制this-_year d._year;this-_month d._month;this-_day d._day;}return *this; //返回对象的别名出了作用域*this生命周期还在}
private: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;}赋值运算符如果不显式实现编译器会生成一个默认的。此时用户再在类外自己实现 一个全局的赋值运算符重载就和编译器在类中生成的默认赋值运算符重载冲突了故赋值 运算符重载只能是类的成员函数。
编译器自己生成的赋值运算符重载函数 用户没有显式实现时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝。 注意内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符 重载完成赋值。 观察以下程序
class Stack{
typedef int DataType;
public:Stack(size_t capacity 10){_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array){perror(malloc申请空间失败);return;}_size 0;_capacity capacity;}void Push(const DataType data){// CheckCapacity();_array[_size] data;_size;}~Stack(){if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}
private:DataType *_array;size_t _size;size_t _capacity;
};
int main(){Stack s1;s1.Push(1);s1.Push(2);Stack s2;s2 s1;return 0;
}以上程序会报错 报错原因和往期文章中的拷贝构造函数类似。 main函数结束时会调用Stack中的析构函数对s1和s2两个对象进行析构。编译器默认生成的赋值运算符重载进行的是值拷贝。由于进行了s2 s1的赋值操作赋值过后, s2和s1中变量_array存放了同一块空间的地址。main函数结束后会调用st1和st2的析构函数由于地址相同则会对同一块空间析构两次(对同一块空间free两次)。因此会报错。
需要自己实现的场景 总结默认赋值运算符重载
默认成员函数默认赋值运算符重载是默认成员函数重载特性只能重载成类的成员函数不能重载成全局函数,若定义为全局函数会与编译器默认生成的冲突。拷贝方式编译器生成的默认赋值运算符重载函数完成字节序的值拷贝了(浅拷贝)需要手动实现的场景如果类中未涉及到资源管理(在堆区申请空间)赋值运算符是否实现都可以一旦涉及到资源管理则必须要手动实现深拷贝。
拷贝构造函数和赋值重载的区分
思考一下情景会调用拷贝构造还是赋值重载
Date d1(2025, 2, 12);
Date d2(2024, 3, 13);
//思考会分别调用什么函数
Date d3 d1; //拷贝构造 还是 赋值运算符重载 ?
d2 d1; //拷贝构造 还是 赋值运算符重载 ?验证
这是我们的测试代码
class Date {
public:Date(int year 2025, int month 2, int day 22){this-_year year;this-_month month;this-_day day;}Date operator(const Date d) {if (this ! d) {this-_year d._year;this-_month d._month;this-_day d._day;}return *this;}Date(const Date d) {cout Date(const Date d) endl;_year d._year;_month d._month;_day d._day;}
private:int _year;int _month;int _day;
};
int main(){Date d1(2025, 2, 12);Date d2(2024, 3, 13);//思考会分别调用什么函数Date d3 d1; //拷贝构造 还是 赋值运算符重载 ?d2 d1; //拷贝构造 还是 赋值运算符重载 ?
}我们来调试验证 C拷贝构造与赋值重载的区分 总结
//区分拷贝构造和赋值运算符重载
Date d1(2025, 2, 12);
Date d2(2024, 3, 13);
//用一个已经存在的对象初始化另一个对象 ----- 调用拷贝构造函数
Date d3 d1; //等价于 Date d3(d1);//已经存在的两个对象之间赋值拷贝 ----- 赋值运算符重载函数
d2 d1;用一个已经存在的对象初始化另一个对象 ----- 调用拷贝构造函数已经存在的两个对象之间赋值拷贝 ----- 赋值运算符重载函数
总结
特性运算符重载赋值运算符重载必要性增强代码可读性资源管理的必要手段默认行为无浅拷贝典型应用场景算术运算、比较运算对象复制、资源管理实现要点操作数类型、返回值优化深拷贝、自赋值检查
最佳实践建议
优先使用成员函数形式进行运算符重载对于资源管理类必须实现深拷贝赋值流操作符(, )建议采用全局函数友元形式保持运算符的语义一致性例如不应修改操作数 通过合理使用运算符重载可以显著提升代码的表达能力使自定义类型的使用更加直观自然。但需谨记能力越大责任越大不当的运算符重载反而会降低代码可维护性。 文章到此结束啦欢迎各位大佬在评论区讨论交流如果觉得文章写的不错还请留下免费的赞和收藏