美文网首页程序员
运算符重载与友元函数

运算符重载与友元函数

作者: 骑猪满天飞 | 来源:发表于2020-12-14 10:51 被阅读0次

运算符重载

C++允许将运算符重载到用户定义的类型,例如,使用+将两个类对象相加。

重载运算符要使用运算符函数:

operatorop(argument-list);
使用方法:
operator+() //重载+运算符

这里给出一个简单的复数类,并重载了运算符,实现复数的加、减、乘运算

假设a=(A,Bi),c=(C,Di) 复数的运算法则:

  • 加法:a+c = (A+C,(B+D)i)
  • 减法:a-c = (A-C,(B-D)i)
  • 乘法:a * c = (A * C-B * D,(A * D + B * C)i)
  • 乘法:a * x = ( A * x,Bi * x) x为实数
class Complex
{
private:
    double real;
    double imag;
public:
    Complex();
    Complex(double r, double i);
    Complex operator+(const Complex &a) const;
    Complex operator-(const Complex &a) const;
    Complex operator*(const Complex &a) const;
    Complex operator*(double x) const;
};
Complex Complex::operator+(const Complex &a) const{ //第二个const是为了确保this所指对象不被修改
    Complex temp;
    temp.real = this->real + a.real;
    temp.imag = this->imag + a.imag;
    return temp;
}

Complex Complex::operator-(const Complex &a) const{
    Complex temp;
    temp.real = this->real - a.real;
    temp.imag = this->imag - a.imag;
    return temp;
}

Complex Complex::operator*(const Complex &a) const{
    Complex temp;
    temp.real = this->real * a.real - this->imag * a.imag;
    temp.imag = this->real * a.imag + this->imag * a.real;
    return temp;
}

Complex Complex::operator*(double x) const {
    Complex temp;
    temp.real = this->real * x;
    temp.imag = this->imag * x;
    return temp;
}

如果result,a,c是Complex类的三个对象,则计算复数a和c的和,可以编写等式:result = a + c

编译器将上述语句转化为result = a.operator+(c),因此如果还有一个d对象,result = a + c + d也是成立的,此语句将被编译器转化为result = a.operator+(c.operator+(d))

重载限制

  1. 重载后的运算符必须有一个操作数为用户定义的数据类型,这是为了防止用户为标准类型重载运算符,如将int类型相加的运算符+重载,将出现问题。
  2. 使用运算符时不能违反运算符原来的语法规则。例如,不能将求模运算符(%)重载成使用一个操作数
  3. 不能创建新的运算符

友元函数

C++严格控制类对象的私有数据访问,外部只能通过公有的类方法来访问私有数据。然而在特定情况下需要外部函数来访问类的私有数据,这种情况下,C++提供了另一种访问形式:友元。

通过让函数成为类的友元,可以使得该函数与类的成员方法有相同的权限。例如,Complex类提供了运算符 * 的重载,实现复数与实数相乘:Complex operator*(double x) const;使用此重载,需要第一个操作数为类对象。

如语句:

result = a * 2

将被转化为下面的成员函数调用:

result = a.operator*(2)

但是当表达式写成result = 2 * a 时将出错,因为2不是Complex类对象,没有operator*()方法。

为了解决此问题,应当编写一个原型如下的非成员函数来重载*:

Complex operator*(const double x, const Complex a);

但是非成员函数无法访问类的私有数据,所以需将此函数申明为Complex的友元函数,操作如下:

class Complex
{
private:
    double real;
    double imag;
public:
    ```
    friend Complex operator*(const double x, const Complex a); //关键字friend表示类的友元函数
    
};

Complex operator*(const double x, const Complex a) { //函数定义时不需要friend关键字
    Complex temp;
    temp.real = a.real * x; //非友元函数无法访问a.real 与 a.imag
    temp.imag = a.imag * x;
    return temp;
}

有了上述声明和定义以后下面语句

result = 2 * a

将被转化为如下语句,从而调用定义的友元函数:

result = operator(2 , a)

常用友元:重载<<、>>运算符

为了输出类对象的内容,我们可以重载<<运算符:

class Complex
{
private:
    double real;
    double imag;
public:
    ```
    void operator<<(std::ostream& os) const;
    
};

void Complex::operator<<(std::ostream& os) const {
    os << "(" << real << "," << imag << "i)";
    
}

但是上述重载存在几个问题:

  • 问题1: 该重载函数第一个参数为调用此方法的类对象,因此需要用以下语句来输出内的内容:

    c << cout;

为解决此问题需要用到友元函数,调换参数顺序修改如下:

class Complex
{
private:
    double real;
    double imag;
public:
    ```
    friend void operator<<(std::ostream& os, const Complex &a);
    
};

void operator<<(std::ostream& os, const Complex& a) {
    os << "(" << a.real << "," << a.imag << "i)";
}

这样输出的表达式就变成cout << c

  • 问题2:该重载函数无法做到cout << c << a连续输出

    因为上述表达式将转化为:

    (operator<<(cout,c)) << a

    (operator<<(cout,c))无返回内容,输出c的内容后,表达式将变成 << a ,出现错误。

    所以对<<的重载函数需要返回第一个参数的引用,此时输出c内容后表达式将变为cout << a,再次输出a的内容。

正确重载<<的代码如下:

class Complex
{
private:
    double real;
    double imag;
public:
    ```
    friend std::ostream& operator<<(std::ostream& os, const Complex &a);
    
};

std::ostream& operator<<(std::ostream& os, const Complex& a) {
    os << "(" << a.real << "," << a.imag << "i)";
    return os;
}

同理运算符>>的重载如下:

class Complex
{
private:
    double real;
    double imag;
public:
    ```
    friend std::ostream& operator<<(std::ostream& os, const Complex &a);
    friend std::istream& operator>>(std::istream& is, Complex &a);
    
};

std::ostream& operator<<(std::ostream& os, const Complex& a) {
    os << "(" << a.real << "," << a.imag << "i)";
    return os;
}

std::istream& operator>>(std::istream& is, Complex& a) {
    std::cout << "Input real part of number :";
    is >> a.real;
    std::cout << "Input imaginary part of number :";
    is >> a.imag;
    return is;
}

完整代码与测试

complex0.h

#include <iostream>
class Complex
{
private:
    double real;
    double imag;
public:
    Complex();
    Complex(double r, double i);
    Complex operator+(const Complex &a) const;
    Complex operator-(const Complex &a) const;
    Complex operator*(const Complex &a) const;
    Complex operator*(double x) const;
    Complex operator~() const;
    
    friend Complex operator*(const double x, const Complex a);
    friend std::ostream& operator<<(std::ostream& os, const Complex &a);
    friend std::istream& operator>>(std::istream& is, Complex &a);


};

complex0.cpp
    
#include <iostream>
#include "complex0.h"


Complex::Complex() {
    real = 0.0;
    imag = 0.0;
}
Complex::Complex(double r, double i) {
    real = r;
    imag = i;
}

Complex Complex::operator+(const Complex &a) const{
    Complex temp;
    temp.real = this->real + a.real;
    temp.imag = this->imag + a.imag;
    return temp;
}

Complex Complex::operator-(const Complex &a) const{
    Complex temp;
    temp.real = this->real - a.real;
    temp.imag = this->imag - a.imag;
    return temp;
}

Complex Complex::operator*(const Complex &a) const{
    Complex temp;
    temp.real = this->real * a.real - this->imag * a.imag;
    temp.imag = this->real * a.imag + this->imag * a.real;
    return temp;
}

Complex Complex::operator*(double x) const {
    Complex temp;
    temp.real = this->real * x;
    temp.imag = this->imag * x;
    return temp;
}

Complex Complex::operator~() const{
    Complex temp;
    temp.real = this->real;
    temp.imag = -this->imag;
    return temp;
}

Complex operator*(const double x, const Complex a) {
    Complex temp;
    temp = a * x;
    return temp;
}

std::ostream& operator<<(std::ostream& os, const Complex& a) {
    os << "(" << a.real << "," << a.imag << "i)";
    return os;
}

std::istream& operator>>(std::istream& is, Complex& a) {
    std::cout << "Input real part of number :";
    is >> a.real;
    std::cout << "Input imaginary part of number :";
    is >> a.imag;
    return is;
}
main.cpp

#include <iostream>
#include "complex0.h" 
using namespace std;

int main() {

    Complex a(3.0, 4.0);
    Complex c;
    cout << "Enter a complex (q to quit):" << endl;
    while (cin >> c) {
        
        cout << "c is" << c << endl;
        cout << "complex conjugate is " << ~c << endl;
        cout << "a is " << a << endl;
        cout << "a + c is " << a.operator+(c) << endl;
        cout << "a - c is " << a - c << endl;
        cout << "a * c is " << a * c << endl;
        cout << "2 * a  is " << 2 * a << endl;
        cout << "a * 2  is " << a * 2 << endl;
        cout << "Enter a complex (q to quit):" << endl;
    }   
}
result.png

相关文章

  • C++ 部分运算符重载

    可重载的运算符 不可重载的运算符和符号 重载运算符为类的成员函数 重载运算符为友元函数 重载赋值运算符 重载流插入...

  • 日记之旅第七天

    上午:重点讲解了函数重载,其中包括运算符重载,函数成员重载。运算符重载结合了昨天所讲的友元函数一起使用 下午:复习...

  • 第十一章 使用类(3)重载运算符

    (三)重载运算符: 1.作为友元非成员函数还是成员函数 两个操作数的运算符,如果运算符重载是成员函数版本,那么第...

  • 9.22总结

    一.知识点总结 1.运算符重载关键字operator 类型为重载运算符的返回类型 2.最常见的是友元函数与重载运算...

  • 15.C++泛型

    模板函数 模板类 函数模板和友元函数 注意,只在重载左移右移运算符的时候使用友元函数,其他都不要用,友元函数容易导...

  • 11. 匿名对象、隐式构造、默认构造函数

    一.匿名对象 二.隐士构造函数 三. 四,友元函数 五.内部类 六.运算符重载

  • c++——函数符号重载2-15

    一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。以下一些双目运算符不能重载为类的...

  • C++运算符重载形式--成员函数or友元函数?

    1.C++操作符重载形式---成员函数or友元函数 1.对运算符重载,需要坚持四项基本原则:不可臆造运算符;运算符...

  • C++派生类的友元函数,派生类的析构函数

    一、赋值兼容 运算符重载函数需要访问父类的私有成员,运算符重载函数是父类的友元函数,子类继承了父类,子类也可以使用...

  • C++第7天: 第112-第126课 友元,成员函数不在堆区

    友元修饰 1. 成员函数 2. 修饰类 3. 全局函数 运算符重载 1.仿生函数

网友评论

    本文标题:运算符重载与友元函数

    本文链接:https://www.haomeiwen.com/subject/czfvgktx.html