C++运算符重载

运算符重载

什么是运算符重载?
C++运算符重载
运算符重载的本质是一个函数

运算符重载的作用

为什么会用运算符重载机制?
用复数类举例
Complex c3 = c1 + c2;
原因 Complex是用户自定义类型,编译器根本不知道如何进行加减
编译器给提供了一种机制,让用户自己去完成,自定义类型的加减操作。。。。。
这个机制就是运算符重载机制

运算符重载入门

#include <iostream>
using namespace std;
class Complax
{
public:
    Complax(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    void printC()
    {
        cout << "a = " << a << "\tb = " << b << endl;
    }
    ~Complax() {}
//private:
public:
    int a;
    int b;
};
// 1定义全局函数
Complax myAdd(Complax &c1, Complax &c2)
{
    Complax temp(c1.a + c2.a, c1.b + c2.b);
    return temp;
}
// 2函数名升级
Complax operator+(Complax &c1, Complax &c2)
{
    Complax temp(c1.a + c2.a, c1.b + c2.b);
    return temp;
}
int main01()
{
    int a = 0, b = 0;
    int c; // 基础性数据类型,编译器已经知道了,如何运算
    c = a + b;
    // a + bi;  // 复数运算规则
    Complax c1(1, 2), c2(3, 4);
    Complax c3;  // 2c3是用户自定义数据类型,c++编译器不知道如何运算
//    c3 = c1 + c2;
    // 3c++编译器应该给我们提供一种机制
    // 让自定义数据类型 有机会进行 运算符操作 ==> 运算符重载
    // 4运算符重载机制
    Complax c4 = myAdd(c1, c2);
    c4.printC();
    // 步骤二:Complax c4 = c1+c2;
    // Complax c5 = operator+(c1, c2);
    // c5.printC();
    // 步骤三:
    Complax c5 = c1 + c2;
    c5.printC();
    // 运算符重载的本质 是  函数调用
    return 0;
}

运算符重载的限制

C++运算符重载
C++运算符重载

运算符重载基础

C++运算符重载
C++运算符重载
运算符重载的方法步骤
全局函数、类成员函数方法实现运算符重载步骤

  1. 要承认操作符重载是一个函数,写出函数名称operator+ ()
  2. 根据操作数,写出函数参数
  3. 根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
#include <iostream>
using namespace std;

class Complax
{
private:
    int a;
    int b;
    // 重载友元函数 全局函数 操作符重载
    friend Complax operator+(Complax &c1, Complax &c2);
public:
    Complax(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    void printC()
    {
        cout << "a = " << a << "\tb = " << b << endl;
    }
    // 2成员函数法 实现 - 运算符重载
    Complax operator-(Complax &c2)
    {
//        this->a -= c2.a;
//        this->b -= c2.b;
//        return *this;  // 这一个会改变c1的值,因为是+=
        Complax temp(this->a - c2.a, this->b -c2.b);
        return temp;

    }

};
// 1全局函数法 实现 + 运算符重载
Complax operator+(Complax &c1, Complax &c2)
{
    Complax temp(c1.a+c2.a, c1.b+c2.b);
    return temp;
}
/*
    全局函数,类成员函数方法实现运算符重载步骤
    1:要承认运算符重载是一个函数, 写出函数名称
    2: 根据操作数,写出函数参数
    3:根据业务,完善函数的返回值(看函数返回引用,元素,指针),及实现函数业务
*/
int main()
{
    Complax c1(1, 2), c2(3, 4);

    // 1全局函数法 实现 - 运算符重载
    // Complax operator+(Complax &c1, Complax &c2)
    Complax c3 = c1 + c2;
    c3.printC();

    // 2成员函数法 实现 - 运算符重载
    // Complax operator-(Complax &c2);
    Complax c4 = c1 - c2;
    c4.printC();

    return 0;
}

重载++ 需要注意(重点)

#include <iostream>
using namespace std;
class Complax
{
private:
    int a;
    int b;
    // 1全局函数法 实现 ++ 运算符重载
    friend Complax& operator++(Complax &c1);  // 这里是返回一个引用注意,前置++
    friend Complax operator++(Complax &c2, int); // 后置++

public:
    Complax(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }

    // 前置--
    // 因为前置返回的是本身,所以返回一个引用
    // Complax& operator--(Complax &c1)  这个是写错的,注意错在哪里
    Complax& operator--()
    {
        this->a --;
        this->b --;
        return *this;
    }

    // 后置--  因为后置返回的是一个副本所以不用 返回引用
    Complax operator--(int)
    {
        Complax tem = *this;
        this->a--;
        this->b--;
        return tem;
    }

    void printC()
    {
        cout << "a = " << a << "\tb = " << b << endl;
    }
};



// 特别注意 只有成员函数才有 this指针
// 1全局函数法 实现 前置++ 运算符重载
Complax& operator++(Complax &c1)
{
    c1.a++;
    c1.b++;
    return c1;
}
// 后置++
Complax operator++(Complax &c2, int) // int防止与前置++重载而加的占位符
{
    // 因为后置++是使用, 在让c2++所以要定义一个临时变量
    Complax tem = c2;
    c2.a++;
    c2.b++;
    return tem;
}


int main()
{
    Complax c1(1, 2), c2(30, 40);

    // 1全局函数法 实现 前置++ 运算符重载
    //   Complax& operator++(Complax &c1);
    ++c1;  // 相当于 operator++(c1);
    c1.printC();

    // 2成员函数 实现 前置-- 运算符重载
    // 相当于c2.operator--();
    --c2;
    c2.printC();

    //  1全局函数法 实现 后置++ 运算符重载
    // operator++(c2);
    c2++;
    //  Complax& operator++(Complax &c1);  前置++
    //  Complax operator++(Complax &c2, int); // int防止与前置++重载而加的占位符
    c2.printC();

    // 2成员函数 实现 后置-- 运算符重载
    // 相当于c2.operator--();
    // Complax operator--(int) 函数原型
    c1--;
    c1.printC();

    return 0;
}

C++运算符重载

友元函数 重载 << 还有链式编程

//a)用全局函数方法实现 << 操作符
ostream& operator<<(ostream &out, Complex &c1)
{
    //out<<"12345,生活真是苦"<<endl;
    out<<c1.a<<" + "<<c1.b<<"i "<<endl;
    return out;
}
//调用方法
cout<<c1;
//链式编程支持
cout<<c1<<"abcc";
//cout.operator<<(c1).operator<<("abcd");
//函数返回值充当左值 需要返回一个引用
b)类成员函数方法无法实现 << 操作符重载
//因拿到cout这个类的源码
//cout.operator<<(c1);

注意点

友员函数重载运算符常用于运算符的左右操作数类型不同的情况
在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择
友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
C++中不能用友员函数重载的运算符有

=    ()    []    ->

C++运算符重载
所以这个可以在写一个operator + 或者写一个友元函数重载

简单版的复数类重载

#include <iostream>

using namespace std;

class Complax
{
private:
    int a;
    int b;
    friend ostream& operator<<(ostream &out, const Complax &c1);
public:
    Complax(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    // +运算符重载
    Complax operator+(Complax &c2)
    {
        Complax tem(this->a + c2.a, this->b + c2.b);
        return tem;
    }
    // -运算符重载
    Complax operator-(Complax &rhl)
    {
        Complax tem(this->a - rhl.a, this->b - rhl.b);
        return tem;
    }
    // 前置--
    Complax& operator--()
    {
        this->a --;
        this->b --;
        return *this;
    }
    // 前置++
    Complax& operator++()
    {
        this->a++;
        this->b++;
        return *this;
    }

    // 后置--  因为后置返回的是一个副本所以不用 返回引用
    Complax operator--(int)
    {
        Complax tem = *this;
        this->a--;
        this->b--;
        return tem;
    }
    // 后置++
    Complax operator++(int)
    {
        Complax tem = *this;
        this->a++;
        this->b++;
        return tem;
    }
    void printC()
    {
        cout << "a = " << a << "\tb = " << b << endl;
    }
};


// 这里的ostream 不能加const
ostream& operator<<(ostream &out, const Complax &c1)
{
    cout <<"重载<<运算符 链式输出";
    out << "c1.a = " << c1.a << "\t c1.b = " << c1.b << endl;
}
// 在<< 和 >> 运算符重载时 只能使用全局函数就是 加个友元声明
int main32()
{
    Complax c1(1, 2), c2(30, 40);
    int a = 10;
    cout << a << endl; // 基本数据类型

    // 自定义数据类型
    cout << c1;
    // 全局函数调用方法
    // operator<<(cout ,c1);
    // 函数类型声明
    // friend void operator<<(const ostream &out, const Complax &c1);

    cout << c2;
    // 成员函数调用方法  在ostream类中添加 成员函数 operator<<
    // 但是ostream类的源码拿不到,所以在这种情况下只能使用友元函数
    // cout.operator<<(c2);


    // 最好都写这一个
    cout <<c1 << "sflkasdf";
    // cout.operator<< (c1).operator<<("slkfdkls");
    // 上式等价于 void.operator<<"slkfdls"; 所以
    // 如果链式输入的话
    // 就要把返回时改为ostroam &;



    return 0;
}
int main031()
{
    Complax c1(1, 2), c2(30, 40);
    // 后置++
    c1++;
    c1.printC();
    // 前置++
    ++c2;
    c2.printC();   // 虽然最后结果显示都是一样的,但是调用的不一样,
                  // 因为printC函数输入的是前置,或后置之后的结果所以会输出相同

    //+运算符重载
    Complax c3 = c1 + c2;
    c3.printC();

    // -运算符重载
    Complax c4 = c3 - c1;
    c4.printC();

    cout << "Hello world!" << endl;
    return 0;
}

简单的name类重载

#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

class Name
{
public:
    Name (const char *mp)
    {
        len = strlen(mp);
        p = (char *)malloc(sizeof(len+1)); // 注意细节
        strcpy(p, mp);
    }

    //Name obj2 = obj1;
    // 解决方案:手工编写拷贝构造函数,使用深拷贝
    Name (const Name &obj1)
    {
        len = obj1.len;
        p  = (char *)malloc(sizeof(len+1));
        strcpy(p, obj1.p);
    }

    // 成员函数 =运算符重载  不能写全局函数
    Name& operator=(const Name &obj)
    {
        // 先释放旧的内存  特别注意
        if(p != NULL)
        {
            delete []p;
            len = 0;
        }
        len = obj.len;
        p = new char[len+1];
        strcpy(p, obj.p);
        return *this;
    }
    ~Name ()
    {
        cout << "\t析构函数" << endl;
        if (p != NULL)
        {
            free(p);
            p = NULL;
            len = 0;
        }
    }
    void printName()
    {
        cout <<"\tp = " << this->p << "\t len = " << this->len << endl;
    }
private:
    char *p;
    int len;
};


// 对象析构的时候会出现,错误
// 调试存在bug
// 析构的时候会出现二次释放
//默认的拷贝构造函数,如果要对指针进行拷贝,则只是浅拷贝,拷贝过后是两个变量指向
//同一段内存,释放拷贝的obj2后,obj1的指针就是垃圾值,在释放就会出错

void objmain()
{
    Name obj1("aaaa");
    Name obj2 = obj1;
    Name obj3("sfs");

    //  obj3 = obj1;  // 等号操作 c++编译器会对=进行默认的重载(同样会发生二次释放的问题)
    // 就需要手动的写 operator= 函数

    cout << "成员函数=重载  ";
    obj3 = obj1;
    obj3.printName();
    // obj3.operator=(const Name & obj1);
    // void operator=(const Name &obj)  // 函数声明
    // 如果要执行 obj3 = obj2 = obj1; 就需要把void 改成 Name&
    {
        obj3 = obj2 = obj1;
        obj3.printName();
        obj2.printName();
        obj1.printName();
    }

}

int main04()
{
    objmain();
}

// 重载 = 运算符注意点
// 1 先把旧的内存释放
// 2 返回一个引用 obj3 = obj1 = obj2;
// 3数据进行拷贝

编写复数类

main.cpp

#include <iostream>
//#include "Complex.h"
#include "Complex.cpp"
using namespace std;
// 需要注意的是 如果是在main中包含complex.cpp的话,就要在complex.cpp的函数
// 写成内联函数, 并且在其中写上#include"Complex.h"  推荐
// 或者在main中包含#include"Complex.h", 然后再complex.h中包含#include"Complex.cpp"

// Complex类
int main() {
    std::cout << "Hello, World!" << std::endl;
    Complex c(2, 3);
    Complex c2(3, 4);
    c += c2;
    c -= c2;
    Complex c3 = c2;  // 调用拷贝构造函数
    c3 = c;    // 运算符重载
    cout << c3 << endl;
    Complex c4 = c + c2;
    c2++;
    cout << c << c2 << endl;
    c == c2;
    cout << sizeof(c)<<endl;   // 16个字节

//    cout << c.real() << "i+"<< c.imge() << endl;

    return 0;
}

Complex.h

//
// Created by lk on 18-5-28.
//

#ifndef HOUJIE_COMPLEX_H    // 防卫式声明
#define HOUJIE_COMPLEX_H

#include <iostream>

class Complex {
public:

    Complex(double re = 0, double im = 0)
            : re(re), im(im) {}

    // 拷贝构造函数
    Complex(const Complex &obj)
            : re(obj.re), im(obj.im) {}

    Complex operator+(const Complex &obj);

    Complex operator-(const Complex &obj);

    Complex &operator+=(const Complex &obj);

    Complex &operator-=(const Complex &obj);

    bool operator==(const Complex &obj);

    Complex &operator--();

    Complex &operator++();

    Complex operator--(int);

    Complex operator++(int);


    double real() { return re; }

    double imge() { return im; }

private:
    double re;
    double im;

    friend std::ostream &operator<<(std::ostream &os, Complex &obj);

};


#endif //HOUJIE_COMPLEX_H

Complex.cpp

// Created by lk on 18-5-28.
//

#include "Complex.h"
#include <iostream>
inline
Complex &Complex::operator+=(const Complex &obj) {
   this->re += obj.re;
   this->im += obj.im;
   return *this;
}
inline
Complex &Complex::operator-=(const Complex &obj) {
   this->im -= obj.im;
   this->re -= obj.re;
   return *this;
}

// 前置--  --c
inline
Complex &Complex::operator--() {
   this->im--;
   this->re--;
   return *this;
}

// 前置++ ++c
inline
Complex &Complex::operator++() {
   this->im++;
   this->re++;
   return *this;
}

// 后置--  因为后置返回的是一个副本所以不用返回引用 c--
inline
Complex Complex::operator--(int) {
   Complex tem = *this;
   this->im--;
   this->re--;
   return tem;
}

// 后置++ c++
inline
Complex Complex::operator++(int) {
   Complex tem = *this;
   this->im++;
   this->re++;
   return tem;
}

// 重载+  调用者 c+c2
// 这是类内函数, 用一个参数, 如果是类外的就要用两个参数
inline
Complex Complex::operator+(const Complex &obj) {
   return Complex(this->im + obj.im, this->re + obj.re);
}


// 重载-  调用者 c-c2
inline
Complex Complex::operator-(const Complex &obj) {
   Complex tem(this->im - obj.im, this->re - obj.re);
   return tem;
}

// 重载 == c == c2
inline
bool Complex::operator==(const Complex &obj) {
   return (this->im == obj.im && this->re == obj.re);
}

// 重载<< cout << c
inline
std::ostream &
operator<<(std::ostream &os, Complex &obj) {
   return os << obj.re << obj.im;
}

编写string类

main.cpp

 #if 0
#include <iostream>
//#include "MyString.h"
#include "MyString.cpp"
using namespace std;

int main()
{
    MyString s("aaa");
    MyString s2(10);
    s2 = s;
    cout << s  << s2 << endl;
    if (s == s2)
    {
        cout << "s == s2" << endl;
    }
    else
        cout << "s != s2 "<< endl;
    if (s == "aab")
        cout << "s == aab" << endl;
    if (s != "aab")
        cout << "s != aab" << endl;
    s2 = "abbb00";
    auto t = s > "abb00";
    cout << t << endl;
    cout << s[2] << endl;
//    cin >> s;
//    cout << s;
    MyString s3 = "sjkf";
    s3 = s2;
    cout << s3 ;
    return 0;
}
#endif

#include <iostream>
#include <cstring>
#include "MyString.cpp"

using namespace std;

int main1() {
    // 这里也是一个功能
    MyString s1(10);
    cout << "请输入s1" << endl;

    // 重载 >>
    cin >> s1;
    cout << s1 << endl;

    return 0;
}

int main2() {
    MyString s1;
    MyString s2("ab");

    MyString s3(s2);
    s3 = "abcd";

    int flag = (s3 < "bbbb");
    {
        if (flag > 0)
            cout << "s3 > bbbb " << endl;

        else if (flag == 0)
            cout << "s3 = bbbb " << endl;

        else
            cout << "s3 < bbbb " << endl;
    }

    MyString s4 = "abcds";
    cout << s4 << endl;

    return 0;
}

int main() {
    MyString s1;
    MyString s2("aaa");
    MyString s3(s1);  // MyString s3 = s1;
    MyString s4("s4444");


    // 运算符重载 =
    // MyString& operator=(MyString & rhs); 函数原型调用s4.operator=(MyString & rhs)
    s4 = s2;

    // 也是调用运算符重载 MyString& operator=(const char *p);
    s4 = "2222";

    s4[0];   // 所以说 []重载时 最后是返回一个引用
    cout << "当右值 s4[0] = " << s4[0] << endl;

    s4[0] = 'a';
    cout << "当左值 s4[0] = " << s4[0] << endl;

    // s4.operator[](int index);
    // char& operator[](int index)  // 当左值,当右值


    // << 重载
    cout << s4 << endl;
    // MyString& operator<<(ostream &out, MyString &rhs)


    // == 重载
    if (s2 == "aaa") {
        cout << "相等" << endl;
    } else {
        cout << "不相等" << endl;
    }

    // if (s2 != "abcd")
    //  s2.operator==(const char *p)
    //  bool operator==(const char *p)

    if (s2 == s4) {
        cout << "相等" << endl;
    } else {
        cout << "不相等" << endl;
    }
    // if (s2 != s4)

    // bool operator==(const MyString &rhs)

    cout << "Hello world!" << endl;
    return 0;
}

MyString.h

//
// Created by lk on 18-6-10.
//

#ifndef CESHI_MYSTRING_H
#define CESHI_MYSTRING_H

#include <cstring>
#include <iostream>

using namespace std;

class MyString {
    friend ostream &operator<<(ostream &out, const MyString &obj);

    friend istream &operator>>(istream &in, MyString &obj);

public:
    // 构造
    MyString();

    MyString(const char *data);

    MyString(const int leng);

    // 拷贝构造
    MyString(const MyString &obj);

    ~MyString();

public:
    MyString &operator=(const MyString &obj);

    MyString &operator=(const char *data);

    bool operator==(const MyString &obj) const;  // const表示this->m_data不能修改
    bool operator!=(const MyString &obj) const;

    bool operator==(const char *data) const;

    bool operator!=(const char *data) const;

public:
    int operator>(const MyString &obj) const;

    int operator<(const MyString &obj) const;

    int operator>(const char *data) const;

    int operator<(const char *data) const;

public:

    char &operator[](const int index) const;

private:
    char *m_data;
    int len;
};

#endif //CESHI_MYSTRING_H

MyString.cpp

//
// Created by lk on 18-6-10.
//
#include "MyString.h"

//#include <cstring>
// 无参构造函数
inline MyString::MyString() {
    len = 0;
    m_data = new char[1];
    strcpy(m_data, "");
}

inline MyString::MyString(const char *data) {
    // 如果有初始值
    if (data) {
        len = strlen(data);
        m_data = new char[len + 1];
        strcpy(m_data, data);
    } else {
        len = 0;
        m_data = new char[1];
        strcmp(m_data, "");
    }

}

inline MyString::  //构造一个空串
MyString(const int leng) {
    /*
        if (len == 0)
        {
            m_len = 0;
            m_p = new char[m_len + 1];
            strcpy(m_p, "");
        }
        else
        {
            m_len = len;
            m_p = new char[m_len + 1];
            // strcpy(m_p, "");  哪一个都行
            memset(m_p, 0, m_len);
        }

    */
    len = leng;
    m_data = new char[len + 1];
    strcpy(m_data, "");  // 构造空串
}


// MyString s1 = s2; 拷贝构造
inline MyString::
MyString(const MyString &obj) {
    len = strlen(obj.m_data);
    m_data = new char[len + 1];
    strcpy(m_data, obj.m_data);

}

// 析构函数
inline MyString::~MyString() {
    if (m_data != NULL) {
        len = 0;
        delete[]m_data;
        this->m_data = NULL;   // 防止出现野指针
    }
}

// s1 = s2 = s3; 拷贝赋值
inline MyString &MyString::operator=(const MyString &obj) {
    // 提高水平的判断, 就是如果是自己赋值给自己的话,就要加上这句,必须加
    // 如果this 等于obj对象的指针,  这里的&是取地址
    if (this == &obj) {
        return *this;
    }
    // 释放旧内存
    if (m_data != NULL) {
        len = 0;
        delete[]m_data;
        m_data = NULL;
    }

    // 分配新内存 并赋值
    len = strlen(obj.m_data);
    m_data = new char[len + 1];
    strcpy(m_data, obj.m_data);

    return *this;
}

// 拷贝赋值 s1 ="aaa"
inline MyString &MyString::operator=(const char *data) {
//    if (data != NULL)
    // 释放旧内存
    if (m_data == NULL) {
        delete[]m_data;
        len = 0;
        m_data = NULL;
    }
    // 分配新内存
    if (data == NULL) {
        len = 0;
        m_data = new char[1];
        strcpy(m_data, "");
    } else {
        len = strlen(data);
        m_data = new char[len + 1];
        strcpy(m_data, data);
    }
    return *this;


}

inline bool
MyString::operator==(const MyString &obj) const {
    if (len != obj.len)
        return false;
    else {
        if (!strcmp(m_data, obj.m_data))
            return true;
        else
            return false;

    }
}

inline bool
MyString::operator!=(const MyString &obj) const {
    return !(*this == obj);
}

inline bool
MyString::operator==(const char *data) const {
    if (strlen(data) != len) {
        return false;
    } else {
        if (!strcmp(data, m_data))
            return true;
        return false;
    }
}

inline bool
MyString::operator!=(const char *data) const {
    return !(*this == data);
}

inline int
MyString::operator<(const MyString &obj) const {
    return strcmp(m_data, obj.m_data);
}

inline int
MyString::operator>(const MyString &obj) const {
    return strcmp(obj.m_data, m_data);
}

inline int
MyString::operator>(const char *data) const {
    return strcmp(m_data, data);
}

inline int
MyString::operator<(const char *data) const {
    return strcmp(data, m_data);
}

// s[2]
inline char &   // 多看
MyString::operator[](const int index) const {
    static char sNull = '\0';
    if (strlen(m_data) > index + 1 && index >= 0)
        return m_data[index];
    else {
        cout << "下标越界" << endl;
        return sNull;
    }
}


// << 重载 应该是友元 注意返回值,
// cout << s4 << endl; 全局的
// friend ostream& operator<<(ostream &out, MyString &obj);
inline ostream &operator<<(ostream &out, const MyString &obj) {
    return out << obj.m_data << endl;   // 如果是*obj.m_data则是 第一个元素
}

inline istream &operator>>(istream &in, MyString &obj) {
    return in >> obj.m_data;
}

MyString.cpp

更多C++相关知识体系,请移步C++知识目录。

上一篇:Salesforce入门教程(中文)-014


下一篇:JAVA 获取jdbc.properties配置信息