C++运算符重载中有些方法为什么需要定义为友元函数

2023-05-16

C++提供运算符重载主要目的:

  1. 希望对象之间的运算看起来可以和编译器内置类型一样丝滑;
  2. 相当于是告知编译器,类对象之间运算应该如何去做处理。

通过实现一个复数类,来阐述本文章的主题:

class Complex{
public:
    Complex(int r = 1,int i = 1):real_(r),image_(i){}
    ~Complex(){}
    void print(){
        std::cout<<"real_= "<<real_<<std::endl;
        std::cout<<"image_= "<<image_<<std::endl;
    }
    Complex operator+(const Complex&right){
        return Complex(this->real_+right.real_,this->image_+right.image_);
    }
private:
    double real_;
    double image_;
};

我们这里,为了满足Complex对象之间进行加法运算的需求,实现了Complex的加法运算符重载函数,看上去好像没有什么问题,我们使用一下该接口,挨个分析下面每个加法case:

int main(){

    Complex com1(100,1000);
    Complex com2(99,999);
    
    Complex com3 = com1 + com2;
    com3.print();

    Complex com4 = com1 + 5;
    com4.print();
 
    //Complex com5 = 6 + com1;
    //com5.print();
    return 0;
}
  1. Complex com3 = com1 + com2;

我们知道,运算符重载就相当于左边的对象调用自己的某个重载方法,将右边的值作为参数传递进去,这里也是一样的,相当于
com1.operator+(com2),这个样子,这样自然没有什么问题,比较经典的对象加法运算;

  1. Complex com4 = com1 + 5;

同样的,这里相当于com1.operator+(5),这里实际上发生了一个类型的强转,将int类型强转成了Complex类型,生成了一个临时的Complex对象初始化了形参,这个临时对象实际是Complex(5,1),因为该类的构造函数有默认值,所以可以只给一个参数生成临时对象,
最终,即com1.operator+(Complex(5,1)),也没什么问题的,目前该运算符重载函数依然可以满足要求。

  1. Complex com5 = 6 + com1;

此时6在左边,编译器没有理由将6强转成Complex对象,因为此时不像上面第二个例子中的5一样,5作为实参传递给了Complex类型的形参,编译器找到了合适的构造函数才将5进行强转,然后初始化形参的。
此时,编译器发现没有能够满足该类加法的类内的运算符重载,所以就会在全局作用域内寻找:

Complex operator+(const Complex&left,const Complex&right){
    return Complex(left.real_+right.real_,left.image_+left.image_);
}

此时, Complex com5 = 6 + com1;相当于left 为Complex(6,1)这样的临时对象,right为com1,由于全局作用域无法访问类内的私有变量,所以需要将此方法在类内生命为firend.

class Complex{
public:
    Complex(int r = 1,int i = 1):real_(r),image_(i){}
    ~Complex(){}
    void print(){
        std::cout<<"real_= "<<real_<<std::endl;
        std::cout<<"image_= "<<image_<<std::endl;
    }
    Complex operator+(const Complex&right){
        return Complex(this->real_+right.real_,this->image_+right.image_);
    }
    friend Complex operator+(const Complex&left,const Complex&right);
    //不想用friend 也可以提供get real 和 get image方法,firend方便一点
private:
    double real_;
    double image_;
};

Complex operator+(const Complex&left,const Complex&right){
    return Complex(left.real_+right.real_,left.image_+left.image_);
}
int main(){
    Complex com1(100,1000);
    Complex com2(99,999);

    Complex com3 = com1 + com2;
    com3.print();
    
    Complex com4 = com1 + 5;
    com4.print();

    Complex com5 = 6 + com1;
    com5.print();
    return 0;
}

这里既提供了类内的运算符重载,也提供了全局的运算重载,编译器编译时,优先选择类内的,类内如果无法满足,就去全局找,如果全局都没法找到,那么就要编译报错了。

支持输入输出运算符重载:

我们知道我们经常使用的std::coutstd::cin实际上分别是ostream coutistream cin对象
我们需要输出一个对象信息时,需要将我们需要输出的信息以一种格式交给ostream对象

std::ostream& operator<<(std::ostream&out,const Complex&src){
	out<<"real = "<<src.real_<<" image = "<<src.image_<<std::endl;
    return out;
}
std::istream& operator>>(std::istream&in,Complex&src){
    in >> src.real_ >> src.image_;
    return in;
}

完整代码:

class Complex{
public:
    Complex(int r = 1,int i = 1):real_(r),image_(i){}
    ~Complex(){}
    void print(){
        std::cout<<"real_= "<<real_<<std::endl;
        std::cout<<"image_= "<<image_<<std::endl;
    }
    //前置++
    Complex& operator++(){
        image_+=1;
        real_+=1;
        return *this;
    }
    //后置++
    Complex operator++(int){
        return Complex(image_++,real_++);
    }
    Complex operator+(const Complex&right){
        return Complex(this->real_+right.real_,this->image_+right.image_);
    }
    friend Complex operator+(const Complex&left,const Complex&right);
    friend std::istream& operator>>(std::istream&in,Complex&src);
    friend std::ostream& operator<<(std::ostream&out,const Complex&src);
    //不想用friend 也可以提供get real 和 get image方法
private:
    double real_;
    double image_;
};

Complex operator+(const Complex&left,const Complex&right){
    return Complex(left.real_+right.real_,left.image_+left.image_);
}

std::ostream& operator<<(std::ostream&out,const Complex&src){
    out<<"real = "<<src.real_<<" image = "<<src.image_<<std::endl;
    return out;
}

std::istream& operator>>(std::istream&in,Complex&src){
    in >> src.real_ >> src.image_;
    return in;
}

int main(){

    Complex com1(100,1000);
    Complex com2(99,999);

    Complex com3 = com1 + com2;
    com3.print();

    Complex com4 = com1 + 5;
    com4.print();

    Complex com5 = 6 + com1;
    com5.print();

    std::cout<<com5;  

    std::cin>>com5;

    std::cout<<com5;  
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++运算符重载中有些方法为什么需要定义为友元函数 的相关文章

随机推荐

  • 玩Raspberrypi走过的坑

    树莓派挺有用 xff0c 基本上linux所有的功能都能用上 xff0c 比如开发个人脸识别 xff0c 搭建一个AVR开发环境 xff0c 都相当的不错 从18年到现在一年多 xff0c 也走过不少的坑 xff0c 希望分享一下自己的经验
  • 小米吉姆尼自动驾驶改造

    最近买了个小米吉姆尼 xff0c 恰好有个wifi图传 xff0c 准备做个无人驾驶 xff0c 通过图像处理实现自动驾驶 图传回到PC处理图像目前已经实现 xff0c 这个比较简单 现在存在的问题如何给吉姆尼发送信号 考虑方式有两种 xf
  • 化骨龙 GPS M80Pro 拆解

    最近玩穿越机 xff0c 用GPS的时候一不小心锡渣把9V和5V短路 xff0c 直接把化骨龙GPS M80Pro烧了血泪教训 这个GPS还是挺精致的 xff0c 于是乎拆解一下分享给大家 拆开来看其实并不复杂 xff0c 核心是一颗Ubl
  • freeRTOS 低功耗模式 和 空闲任务

    低功耗模式 1 芯片原本就支持的硬件低功耗 2 freeRTOS提供的软件低功耗 xff0c Tickless模式 xff01 当用户将宏定义 configUSE TICKLESS IDLE 配置为 1 且系统运行满足以下两个条件时 xff
  • 化骨龙zeus 800mw 图传拆解

    最近比较倒霉 xff0c 飞飞机又炸鸡了 xff0c 这次炸的有点狠 xff0c 炸到水泥路上了 xff0c 化骨龙小锤子天线炸断 xff0c 电池炸坏一个 xff0c 图传炸断天线座 xff0c 电子炸坏一个 xff0c 关键是电机这几天
  • pixhawk 电源电压电流计拆解

    闲来无事拆解了一个pixhawk带电流计的电源 xff0c 挺有意思的 xff0c 如下图 xff1a 模块挺简单的 xff0c 正面是就是一块MPS公司的电源芯片MP1593 xff0c 最高4 75 28V xff0c 3A 5V输出
  • VM中linux和windows主机进行串口通信

    最近在做关于AIS的内容 为了对AIS电文进行解码 xff0c 串口收发 数据有PC机模拟发送 xff0c 为了调试方便 xff0c 不用次次将程序放到开发板上运行 xff0c 所以利用pc主机和虚拟机进行串口通信模拟该过程 首先需要用到一
  • 基于CANoe的SecOC实现

    在今天的车载网络中 xff0c 大部分数据传输是在没有任何特殊安全措施的情况下进行的 因此 xff0c 一旦能够直接访问车辆的总线 xff0c 任何人都可以读取总线上传输的原始数据 xff0c 甚至 在今天的车载网络中 xff0c 大部分数
  • Autosar Configuration(五) Security之Csm配置

    本系列教程是根据实际项目开发中总结的经验所得 如发现有不对的地方 还请指正 目录 Autosar Configuration 一 Davinci Developer 工具介绍 Autosar Configuration 二 Davinci
  • 一文读懂Autosar SecOC通讯

    一 为什么用SecOC xff1f 在车载网络中 xff0c CAN总线作为常用的通讯总线之一 xff0c 其大部分数据是以明文方式广播发送且无认证接收 这种方案具有低成本 高性能的优势 xff0c 但是随着汽车网联化 xff0c 智能化的
  • AutoSar之微控制器抽象层MCAL

    微控制器抽象层位于AUTOSAR BSW的最底层 xff0c 包含内部驱动 xff0c 可直接访问微控制器和外设芯片 从具体应用来看 xff0c MCAL主要包括微控制器驱动 存储器驱动 通信驱动和输入输出驱动四个部分 xff0c 各部分又
  • 【高效】【IDE】VSCode 插件

    Docuemnt This 加注释文档 选中你的函数名字 xff0c 按两次ctrl 43 alt 43 D xff1b Better Comments 注释高亮 Live Server 实时预览页面 Live Server会启动一个本地服
  • 嵌入式软件算法优化

    嵌入式软件算法优化 一 算法优化原则二 算法优化方法1 系统优化2 算法优化 xff08 需要理解算法原理 xff09 3 代码优化4 使用硬件资源 xff08 需要熟悉芯片架构及资源 xff09 5 汇编 一 算法优化原则 xff08 1
  • CAN总线原理简介

    一 xff0e CAN总线简介 xff1a 是一种串行通信协议 xff0c 能有效的支持具有很高安全等级的分布实时控制应用范围十分广泛 xff0c 从高速网络到低价位的多路接线都可以使用CAN主要运用于汽车电子航天等行业 xff0c 使用C
  • freeRTOS 任务切换

    使用PendSV实现任务切换 上下文切换被触发的场合可以是 xff1a 1 执行一个系统调用 2 系统滴答定时器 SysTick 中断 br PendSV中断服务函数 br TaskSelectHighestPrior的两种方法 br br
  • make -j 参数加快编译效率

    对于大型项目 xff0c 在使用cmake控制编译时 xff0c 仅仅执行make指令效率较低 xff0c 使用make j后面跟一个数字 xff0c 比如make j4 make j6 make j14等 含义是 让make最多允许n个编
  • cmake中add_dependencies的基本作用

    假设我们需要生成一个可执行文件 该文件生成需要链接a so b so c so d so四个动态库 正常来讲 我们一把只需要以下两条指令即可 ADD EXECUTABLE span class token punctuation span
  • 命令行给cmake传递参数

    我们期望在编译前将一些信息缓存起来 然后用CMakeLists txt进行构建时 希望可以访问之前缓存给cmake的变量 比如我们希望缓存TARGET CPU 并且他的值为X86 那么我们可以在命令行或者脚本中执行一下操作 cmake DT
  • 在CMakeLists.txt如何执行脚本?execute_process

    execute process span class token punctuation span COMMAND span class token function bash span SCRIPT PATH name sh WORK P
  • C++运算符重载中有些方法为什么需要定义为友元函数

    C 43 43 提供运算符重载主要目的 xff1a 希望对象之间的运算看起来可以和编译器内置类型一样丝滑 xff1b 相当于是告知编译器 xff0c 类对象之间运算应该如何去做处理 通过实现一个复数类 xff0c 来阐述本文章的主题 xff