当前位置: 首页 > article >正文

《C++运算符重载深度解析:从加法、流操作到仿函数与类型转换》

目录

1、加法运算符的重载

类的初定义:

实现对象与对象之间的加法:

实现对象与变量的加法:

实现变量与对象的加法:

2、将对象转化为所需的内置类型(以int为例)

3、输出流和输入流的重载

输出流的重载

输入流的重载

4、单目运算符“++”和“--”的重置

5、重载函数调用(仿函数)

6、当常对象需要修改(以仿函数为例)

存在问题分析

1. const成员函数中非法修改成员变量

解决方案(mutable)

修复const成员函数的未定义行为

关键修改说明

总结


1、加法运算符的重载

类的初定义:
 class Int
 {
 private:
     int value;
 public:
     Int(int x = 0) :value(x)
     {
         cout << "create Int : " << value << " " << this << endl;
     }
     Int(const Int& it) :value(it.value)
     {
         cout << "copy create Int: " << value << " " << this << endl;
     }
     ~Int()
     {
         cout << "destroy Int: " << value << " " << this << endl;
     }
     Int& operator=(const Int& it)  //用const引用右值
     {
     if (this == &it) //防止自己给自己赋值
     {
         return *this;
     }
     value = it.value;
     cout << "operator=" << endl;
     return *this;   
     }
 }
实现对象与对象之间的加法:
 Int operator+(const Int &it)const
 {
     return Int(this->value + it.value);
 }
例:c = a + b--------> c = a.operator+(b) ----> c = operator+(&a,b)
实现对象与变量的加法:
 Int operator+(const int x)const
 //用内置类型在形参时一般不用引用,引用需要访问内存两次
 {
     return Int(this->value + x);
 }
例:c = a+10------>c = a.operator+(10) ----> c = operator+(&a,110)

思考:为什么用内置类型做形参时不使用引用?

因为引用在系统的底部是以指针来实现,相当于把实参的地址给形参,当在函数体内部使用该形参时,首先是将该形参变量的值找到,即找到变量的地址,这是第一次访问内存,接着再在该地址将变量的值找到,这是第二次访问内存;

如果不使用引用,以值进行传递时,只需要访问一次内存。

实现变量与对象的加法:

因为加法是一个双目运算符,如果在类里面实现加法运算符重载实现变量与对象的加法,将出现三个参数,所以应该在全局进行定义,但是因为该函数为全局函数,无法直接访问对象的私有属性,所以有三种方法解决:

第一种方法:

 Int operator+(const int x, const Int& it)
 {
     return Int(x + it.Value());
     //调用类中可以返回所需值的方法获取value值
 }
例:c = 10 + a------> c = operator+(10,a)

第二种方法:

 class Int
 {
     ......
     //类内将该方法设置为友元
     friend Int operator+(const int x, const Int& it);
 }
 Int operator+(const int x, const Int& it)
 {
     return Int(x + it.value);
     //调用类中可以返回所需值的方法获取value值
 }

第三种方法:

将变量与对象相加转变为对象与常量相加:

 Int operator+(const int x, const Int& it)
 {
     return it + x;
 }

2、将对象转化为所需的内置类型(以int为例)

 operator int() const{return value}

上述代码的功能是将对象强转为int类型,当需要将对象转化为int类型时,便会发生隐式转换,如果用explic关键字声明,隐式转换将不会发生

此处可以参考学习关于拷贝构造函数的类型转换

C++构造函数详解:从基础到类型转换机制_c++構造函數-CSDN博客

3、输出流和输入流的重载

输出流的重载
 class Int
 {
 private:
     int value;
 public:
     ostream& operator<<(ostream& out) const
     {
         out << value;
         return out;
     }
 };
 int main()
 {
     Int a(10);
     a << cout;
     //相当于a.operator<<(cout)
 }

因为a << cout不符合常用习惯,改进如下:

 class Int
 {
 private:
     int value;
 public:
     //friend ostream& operator<<(ostream& out, const Int& it);
     ostream& operator<<(ostream& out) const
     {
         out << value;
         return out;
     }
 };
 ostream& operator<<(ostream& out, const Int& it)
 {
     //out << it.Value();//使用成员方法访问类的属性
     //out << it.value;//将全局函数定义为友元
     it << out;//使用类内的输出流重载函数
     return out;
 }

在类外定义一个运算符重载函数的原因:

  • 需要符合输出流在前变量在后的书写习惯

  • 输出流运算符为双目运算符,需要两个参数,设计为成员方法会天然的多出一个this指针

输入流的重载
 #define _CRT_SECURE_NO_WARNINGS
 #include <iostream>
 using namespace std;
 class Int
 {
 private:
     int value;
 public:
     //friend istream& operator>>(ostream& in, const Int& it);
     istream& operator>>(istream& in)
     {
         in >> value;
         return in;
     }
 };
 istream& operator>>(istream& in,Int& it)
 {
     //in >> it.Value();//使用成员方法访问类的属性
     //in >> it.value;//将全局函数定义为友元
     it >> in;//使用类内的输出流重载函数
     return in;
 }

4、单目运算符“++”和“--”的重置

前置++和后置++的区别

 #define _CRT_SECURE_NO_WARNINGS
 #include <iostream>
 using namespace std;
 class Int
 {
 private:
     int value;
 public:
     Int(int x):value(x)
     {
         return;
     }
     Int& operator++()//前置++的重载
     {
         value += 1;
         return *this;
     }
     Int operator++(int)//后置++的重载
     {
         //第一种方法
         //Int tmp(*this);
         //this->value += 1;
         //return tmp;
         //第二种方法
         //int x = this->value;
         //++ *this; //调用前置++的重置函数
         //return Int(x);
         //第三种方法
         return Int(this->value++);
     }
 };

在前置++的方案中,不需要重新创建对象;在后置++的方案中,每一次运算都需要创建一个亡值对象用于值的传递。

减号运算符重载与加号运算符相当

5、重载函数调用(仿函数)

两个重载的operator(),允许对象像函数一样被调用(仿函数),替代c语言中的函数指针

 class Add
 {
 private:
     int value;
 public:
     Add(int x = 0):value(x){}
     ~Add() {}
     int operator()(int x)
     {
         value = ++x;
         return value;
     }
     int operator()(int x, int y)//仿函数
     {
         value = x + y;
         return value;
     }
 };
 int main()
 {
     Add x(0);
     int a = 10, b = 20;
     int c = x(10,20);
     cout << c <<endl;//运算结果为30
     int x = Add()(10, 20);//先构建一个不具名对象,然后调用双参数的函数调用重载
     int y = Add{}(10);//先构建一个不具名对象,然后调用单参数的函数调用重载
     return 0;
 }
  1. 类定义

    • 私有成员int value用于存储计算结果。

    • 构造函数Add(int x = 0)初始化value,默认值为0。

    • 析构函数:默认析构函数,无特殊操作。

  2. 成员函数

    • 单参数operator()

    • 双参数operator()

6、当常对象需要修改(以仿函数为例)

 class Add
 {
 private:
     int value;
 public:
     Add(int x = 0):value(x){}
     ~Add() {}
     int operator()(int x)
     {
         value = ++x;
         return value;
     }
     int operator()(int x, int y) const
     {
         *(int*)&value = x + y;  //强转,首先用强转为int*去掉值的常性,然后再解引用就是value值的本身
         //还有一种做法,如下:
         //((Add*)this)->value = x +y;
         return value;
     }
 };
 int main()
 {
     const Add add(0);
     int z = 0;
     z = add(12,23);
     return 0;
 }
存在问题分析
1. const成员函数中非法修改成员变量
 int operator()(int x, int y) const {
     *(int*)&value = x + y; // 未定义行为
     return value;
 }
  • 直接问题const成员函数承诺不修改对象状态,但此处通过*(int*)&value强制去掉const属性并修改value,违反const语义,属于未定义行为 (UB)

  • 后果: 可能引发程序崩溃、数据不一致或编译器优化导致的意外行为。

2. 类设计const正确性缺失

  • 双参数operator()标记为const,但实际修改了成员变量,破坏了const契约。


解决方案(mutable)
修复const成员函数的未定义行为

若需允许const对象修改value,应声明valuemutable

 class Add {
 private:
     mutable int value; // 允许const成员函数修改
 public:
     int operator()(int x, int y) const {
         value = x + y; // 合法操作
         return value;
     }
 };

关键修改说明
原代码问题修改方案作用
const函数内非法修改成员声明valuemutable允许const函数修改特定成员
强制类型转换去const直接赋值(依赖mutable合法性)消除未定义行为,确保代码安全

总结
  • 避免强制类型转换去const:优先使用mutable或调整设计保证const正确性。

  • 严格区分const/非const成员函数:非const函数用于修改状态,const函数用于只读操作。


http://www.kler.cn/a/569085.html

相关文章:

  • 江协科技/江科大-51单片机入门教程——P[1-3] 单片机及开发板介绍
  • 【容器化】低版本docker拉取ubuntn 22.04镜像启动容器执行apt update提示 NO_PUBKEY 871920D1991BC93C
  • 国产AI新秀:DeepSeek的前生今世
  • 如何调试Linux内核?
  • VS Code Python调试执行代码时出现“ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接”的问题解决
  • Llama 2中的Margin Loss:为何更高的Margin导致更大的Loss和梯度?
  • 三七互娱,蓝禾,顺丰,oppo,游卡,汤臣倍健,康冠科技,作业帮,高途教育25届春招内推
  • 深入浅出理解编译器:前端视角
  • Kotlin 扩展函数
  • 【无人机】无人机飞行日志下载及分析,飞行日志分析软件的使用
  • 蓝桥杯(握手问题)
  • Express + MongoDB 实现用户登录
  • 蓝桥杯好题推荐-----高精度减法
  • PyQt5入门教程和简单使用
  • 阿里云 Qwen2.5-Max:超大规模 MoE 模型架构和性能评估
  • [AIGC]Agent的ReAct原理基于LangChain框架的Agent构建详解
  • Windows系统安装GPU驱动/CUDA/cuDNN/PyTorch
  • PHP环境安装达梦数据库驱动实操
  • 迷你世界脚本玩家接口:Player
  • Excel 豆知识 - XLOOKUP 为啥会出 #N/A 错误