Re: ゼロから学ぶ C++ 入門(六)类和对象·第三篇:运算符重载

◆ 博主名称: 晓此方-CSDN博客

大家好,欢迎来到晓此方的博客。

⭐️C++系列个人专栏:

此方带你玩转C++_晓此方的博客-CSDN博客

⭐️踏破千山志未空,拨开云雾见晴虹。 人生何必叹萧瑟,心在凌霄第一峰


目录

0.1概要&序論

一,运算符重载

1.1运算符重载的意义

1.1.1运算符重载应当有意义

1.2运算符重载的定义

1.3运算符重载的创建

1.3.1以Date类为例

1.3.2创建重载运算符"=="

1.3.3无意义创建避免

1.4运算符重载的调用

1.4.1显式调用

1.4.2隐式调用

1.4.3重载为成员函数的调用

1.4.4调用常见问题:访问限制

1.4.5运算符重载调用优先级

1.5五大不可重载运算符

1.6指向成员变量或成员函数的指针(加餐内容)

1.6.1补充一:成员函数指针

1.6.2补充二:成员函数指针重命名

1.6.3补充三:定义成员函数指针类型变量

1.6.4补充四:用成员函数指针回调


0.1概要&序論

みなさん、久しぶりです! ,这里是此方,好久不见!上一篇我们介绍了类的默认成员函数:构造函数和析构函数 。本期此方将为大家带来运算符重载的系统讲解。每一个成功的人都有一段不为人知的时光,这段时光称为"修炼"。加油!类与对象中相对困难的部分即将告一段落啦!


一,运算符重载

1.1运算符重载的意义

运算符都是针对内置类型的,对自定义类型而言,不能简单使用运算符。这样,对于自定义类型的各种运算将无从入手。于是C++增加了运算符重载的概念。

当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。 C++规定类类型对象使用运算符 时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。

1.1.1运算符重载应当有意义

一个类需要重载哪些运算符,是看哪些运算符重载后有意义 ,比如Date类重载operator-就有意义(了解两个日期之间间隔天数),但是重载operator+就没有意义

1.2运算符重载的定义

  • 运算符重载是具有特殊名字的函数, 他的名字是由operator和后面要定义的运算符 共同构成。和其他函数一样,它也具有其返回类型和参数列表以及函数体。(前面的流插入流提取。就是一种内置的运算符重载)
  • 重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。 一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。

  • 万不可连接运算符中不存在的符号进行重载:如operator@。

运算符重载和函数重载没有任何关系 ,但是两个及以上的运算符重载构成函数重载。

1.3运算符重载的创建

1.3.1以Date类为例

cpp 复制代码
class Date{
pubilc:
Date(int year,int month,int day)
{
     _year=year;
     _month=month;
     _day=day;
}
private:
   int _year;
   int _month;
   int _day;
}

1.3.2创建重载运算符"=="

要让d1==d2,就让d1的所有成员变量==d2所有成员变量。返回值设置为bool类型。传递参数两个Date类类型。

cpp 复制代码
bool opreator==(Date d1 ,Date d2)
{
    if( d1._year==d2._year
       &&d1._month=d2._month
       &&d1._day=d2._day)
       return true;
    else
       return false;
}

1.3.3无意义创建避免

重载操作符至少有一个类类型参数,不能通过运算符重载改变内置类型对象的含义来构建防御性变编程,如:

cpp 复制代码
int operator+ (int x, int y)
{
   return x-y;
}

1.4运算符重载的调用

1.4.1显式调用

直接把重载后的运算符看作是一个函数,传入参数,如下:

cpp 复制代码
int main()
{
    Date d1(2025,2,23);
    Date d2(2025,3,26);
    operator==(d1,d2);
    return 0;
}

1.4.2隐式调用

把重载后的运算符看作兼容该类类型的运算符,采用运算符语法。

  1. 对于二元运算符重载:左侧对象传给第一个参数,右侧传输给第二个参数。
  2. 隐式调用实际上等同于显式调用,会自动转化为显示调用。
cpp 复制代码
d1==d2;

1.4.3重载为成员函数的调用

下文会讲为什么会重载成成员函数,这里介绍重载为成员函数后的特殊调用方法

如果一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的 this 指针,因此运算符重载作为成员函数时,参数比运算对象少一个。

cpp 复制代码
class Date{
pubilc:
bool opreator==(Date d){
    if( _year==d._year
      &&_month=d._month
      &&_day=d._day   )
       return true;
    else
       return false;
}
//........
}

显示与隐式调用:

隐式调用方式不变,他会自动转换成显示调用,d1传递给this指针,d2传递给d

cpp 复制代码
int main()
{
    Date d1(2025,2,23);
    Date d2(2025,3,26);
    d1.operator==(d2);
    d1==d2;
    return 0;
}

1.4.4调用常见问题:访问限制

由于类的成员变量被private修饰,无法在类外直接调用。

解决办法:

  1. 最挫的方法:成员变量公有化
  2. 仿JAVA常用方法:由类提供get()函数
  3. 后面会讲:友元函数
  4. 最好的方法:重载为成员函数

方法一: 去掉private修饰,让public修饰的作用域覆盖成员变量不建议 ,会让类属性被随意修改。

cpp 复制代码
class Date{
pubilc:
Date(int year,int month,int day){
     _year=year;
     _month=month;
     _day=day;
}
//private:
   int _year;
   int _month;
   int _day;
}

**方法二:**由类提供get()函数,在对象外调用getyear就可以通过返回值间接得到_year。

cpp 复制代码
int Getyear()
{
    return _year;
}

方法四: 重载为成员函数,**省去一切麻烦,最推荐使用该方法,**大部分的运算符重载函数都是会设计成成员函数。

cpp 复制代码
class Date{
pubilc:
bool opreator==(Date d2){
    if( _year==d2._year
      &&_month=d2._month
      &&_day=d2._day   )
       return true;
    else
       return false;
}
private:
   int _year;
   int _month;
   int _day;
};

1.4.5运算符重载调用优先级

cpp 复制代码
//..........
bool operator==(const Date& d){
    return _year == d._year
        && _month == d._month
        && _day == d._day;
}
//private:
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;
}

如果在类的内部和外部同时创建一个相同的运算符重载。

  1. operator==(d1,d2)显式调用只会调用全局函数。
  2. d1==d2隐式调用 对调用成员函数还是全局函数会进行重载决议但往往成员函数会获胜

1.5五大不可重载运算符

  1. 域访问限定符。"::"
  2. 计算类型大小。"sizeof"
  3. 三元操作符。"?:"
  4. 类访问操作符。"."
  5. 指向成员变量或成员函数的指针。".*"

原因:

这五个运算符被语言标准明确规定为不可重载 ,原因并不是随意的,而是出于语法完整性、可解析性以及编译期语义安全的系统性考虑。可以从一个统一的原则来理解:

凡是参与"语言语法结构本身"或"编译期语义建模"的运算符,都不可重载。

1.6指向成员变量或成员函数的指针(加餐内容)

上文提到过".*"这个运算符,想必大家都比较陌生,这是C++相比C语言新增的内容。以下给出详细介绍:

1.6.1补充一:成员函数指针

C/C++中一般函数指针举例:

cpp 复制代码
void (*)(int a,int b);

**成员函数指针类型:**加上了域访问限定符,标志该函数指针来自哪里,是什么类的成员函数。

cpp 复制代码
void (A::*)(int a,int b);

1.6.2补充二:成员函数指针重命名

重命名:注意函数指针类型的重命名相比一般重命名不同:这里将void (*)(int a,int b)重命名为PF

cpp 复制代码
typedef void (*PF)(int a,int b);

C/C++中成员函数指针重命名:

cpp 复制代码
typedef void (A::*PF)(int a,int b);

1.6.3补充三:定义成员函数指针类型变量

定义一个成员函数指针类型的变量:

cpp 复制代码
void (A::*pf)(int a,int b)=nullptr;//方法一
PF pf=nullptr;//方法二

1.6.4补充四:用成员函数指针回调

取得现有成员函数的指针, 与普通函数指针不同,成员函数的指针取得,需要加上两个步骤:确定类域和取地址。

cpp 复制代码
pf=&A::func;

**回调函数:**一般函数的回调方式:

cpp 复制代码
pf=func();
//将函数的指针放入函数指针变量中。
*pf()
//取地址函数指针变量进行回调

成员函数由于函数的第一个参数是this指针 ,但是this指针不能显示传参,所以要一个对象进行辅助,用这个对象调用这个函数。

cpp 复制代码
A aa;
(aa.*pf)()

于是,就出现了.*运算符。

成员函数的回调在日常代码中使用的非常非常少 。C语言时期学过的qsort函数就是用回调的,后面C++还有很多更好的解决办法,一般的,我们会尽可能避免使用函数指针


好了,本期内容就到这里,我是此方,我们下期再见。じゃあね〜

相关推荐
2301_789015622 小时前
每日精讲:环形链表、两个数组中的交集、随机链表的复制
c语言·数据结构·c++·算法·leetcode·链表·排序算法
Slow菜鸟2 小时前
Java基础 | JSON 处理手册
java·开发语言·json
喵了几个咪2 小时前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:用 JavaScript/Lua 解锁动态业务扩展能力
javascript·后端·微服务·golang·lua·admin
_OP_CHEN2 小时前
【Python基础】(五)Python 库使用全攻略:从标准库到第三方库,让开发效率翻倍
开发语言·python·pip·项目实战·python标准库·python第三方库
浮尘笔记2 小时前
Go语言条件变量sync.Cond:线程间的协调者
开发语言·后端·golang
自由生长20242 小时前
请求洪峰来了,怎么使用消息队列削峰? 我们来深入的聊一下
后端·架构
Victor3563 小时前
Netty(28)Netty的内存管理和垃圾回收机制是如何工作的?
后端
后端小张3 小时前
【JAVA 进阶】SpringMVC全面解析:从入门到实战的核心知识点梳理
java·开发语言·spring boot·spring·spring cloud·java-ee·springmvc