zoukankan      html  css  js  c++  java
  • 运算符重载(一)

    • 运算符重载允许把标准运算符(如+、—、*、/、<、>等)应用于自定义数据类型的对象。
    • 直观自然,可以提高程序的可读性。
    • 体现了C++的可扩充性。
    • 运算符重载仅仅只是语法上的方便,它是另一种函数调用的方式。
    • 运算符重载,本质上是函数重载。
    • 不要滥用重载、因为它只是语法上的方便,所以只有在涉及的代码更容易写、尤其是更易读时才有必要重载。

    实际上对于运算符的重载在之前的学习中也已经使用到了,只是没系统的提到,下面来用代码进行说明:

    先用普通的方式来实现一下复数的运算:

    Complex.h:

    #ifndef _COMPLEX_H_
    #define _COMPLEX_H_
    
    class Complex
    {
    public:
        Complex(int real, int imag);
        Complex();
        ~Complex();
    
    private:
        int real_;//复数的实部
        int imag_;//复数的虚部
    };
    
    
    #endif    //_COMPLEX_H_

    Complex.cpp:

    #include "Complex.h"
    
    Complex::Complex(int real, int imag) : real_(real), imag_(imag)
    {
    
    }
    
    Complex::Complex() {
    
    }
    
    Complex::~Complex()
    {
    
    }

    如果要实现两个复数相加,则先定义一个方法:

    Complex.h:

    #ifndef _COMPLEX_H_
    #define _COMPLEX_H_
    
    class Complex
    {
    public:
        Complex(int real, int imag);
        Complex();
        ~Complex();
    
        Complex& add(const Complex& otherComplex);//复数加法运算
        void display() const;//打印复数
    private:
        int real_;//复数的实部
        int imag_;//复数的虚部
    };
    
    
    #endif    //_COMPLEX_H_

    Complex.cpp:

    #include "Complex.h"
    #include <iostream>
    using namespace std;
    
    Complex::Complex(int real, int imag) : real_(real), imag_(imag)
    {
    
    }
    
    Complex::Complex() {
    
    }
    
    Complex::~Complex()
    {
    
    }
    
    Complex& Complex::add(const Complex& otherComplex) {
        real_ += otherComplex.real_;
        imag_ += otherComplex.imag_;
        return *this;
    }
    
    void Complex::display() const {
        cout<<real_<<"+"<<imag_<<"i"<<endl;
    }

    编写测试代码:

    #include "Complex.h"
    
    int main(void) {
        Complex c1(3, 5);
        Complex c2(4, 6);
        c1.add(c2);
    
        c1.display();
        
        return 0;
    }

    编译运行,结果显而易见:

    以上就是以成员函数的方式来实现两个复数的加法运算,但是,这种方式不太直观,而且有个毛病就是c1对象发生的改变,如果要实现下面这种运算呢?

    这时运算符的重载就派上了用场了,上面的这种写法现在肯定是编译通不过的:

    其中运算法函数的有两种方式:成员函数重载、非成员函数重载。下面分别来实现:

    •  成员函数原型的格式:
        函数类型 operator 运算符(参数表);
    • 成员函数定义的格式:
        函数类型 类名::operator 运算符(参数表){

          函数体;

         }

     Complex.h:

    #ifndef _COMPLEX_H_
    #define _COMPLEX_H_
    
    class Complex
    {
    public:
        Complex(int real, int imag);
        Complex();
        ~Complex();
    
        Complex& add(const Complex& otherComplex);//复数加法运算
        void display() const;//打印复数
    
        Complex operator+(const Complex& otherComplex);//+号运算符重载
    private:
        int real_;//复数的实部
        int imag_;//复数的虚部
    };
    
    
    #endif    //_COMPLEX_H_

    Complex.cpp:

    编译运行:

     

    实际上:

    Complex c3 = c1 + c2; 等价于: Complex c3 = c1.operator + (c2);

    下面来验证下:

    编译运行:

    可见这个加法运算实际上就是函数调用,但是这种等价写法不是那么直观,所以还是将代码还原。

    • 友元函数原型的格式:
      friend 函数类型 operator 运算符(参数表);
    • 友元函数定义的格式:
      friend 函数类型 类名::operator 运算符(参数表){

        函数体;

        }

    在编译运行之前,先思考一个问题:成员函数重载和非成员函数成载能否共存呢?下面编译一下:

    从结果来看是可以共存的,但是在vS6.0中是不允许的,所以平常最好只要写其中一个就成了,不要共存。

    如果是以友元的方式重载,那下面这句的话等价于:

    •  运算符重载不允许发明新的运算符。
    • 不能改变运算符操作对象的个数。
    • 运算符被重载后,其优先级和结合性不会改变。
    • 不能重载的运算符:
    • 关于选择成员函数重载还是友元函数重载,有下面一些规划:
      ①、一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
      ②、以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
      ③、类型转换运算符只能以成员函数方式重载。
    • ④、流运算符只能以友元的方式重载。

    这里用一个整数类来说明这个运算符的重载,如下:

    Integer.h:

    #ifndef _INTEGER_H
    #define _INTEGER_H
    
    class Integer
    {
    public:
        Integer(int n);
        ~Integer();
        void display() const;
    private:
        int n_;
    };
    
    #endif    //_INTEGER_H

    Integer.cpp:

    #include "Integer.h"
    #include <iostream>
    using namespace std;
    
    Integer::Integer(int n)    : n_(n) {
    
    }
    
    Integer::~Integer()
    {
    }
    
    void Integer::display() const{
        cout<<n_<<endl;
    }

    编写测试代码:

    #include "Integer.h"
    #include <iostream>
    using namespace std;
    
    int main(void) {
        Integer n(100);
        n.display();
    }

    编译运行:

    接下来实现它的++运算,先将其测试代码写出来,当然现在是编译通不过滴:

    下面需要重载++运算符,先学习一下基本概念:

    ①、前置++运算符重载:

    • 成员函数的方式重载,原型为:
      函数类型 & operator++();
    • 友元函数的方式重载,原型为:
      friend 函数类型 & operator++(类类型 &);

    ②、后置自增和后置自减的重载:

    • 成员函数的方式重载,原型为:
      函数类型 & operator++(int);
    • 友元函数的方式重载,原型为:
      friend函数类型 & operator++(类类型 &,int);

    下面来具体实现一下:

    编译运行:

    而如果以友元的方式重载,需要多加一个参数,如其它重载一样:

    编译:

    所以将之前的重载方式注释掉:

    编译运行其结果跟之前的第一种方式是一样的。

    下面来看下后置++运算符的重载:

    编写测试代码:

    根据后置运算符的语法可以推断n++的整个表示式是没有+1的,也就是n3应该等于101,而对于n自身来讲是要+1的,所以n应该等于102,那结果是102、101么?下面运行一下:

    其主要原因还是这样写有问题:

    那如何解决这个问题,那就是用临时对象来解决,具体如下:

    由于函数原形变了,则在头文件中的声明也得变化一下:

    再次编译运行:

    这时就正确了,而它的友远方式实现跟前置++类似,如下:

    其运行结果跟成员函数的重载一样。

    【注意】:能用成员函数重载解决的就尽量用成员函数的方式,否则才用友员的方式。

    关于这个运算符重载用之前我们已经写过的字符串类来说明:

    String.h:

    #ifndef _STRING_H
    #define _STRING_H
    
    class String
    {
    public:
        String(const char* str="");
        ~String();
    private:
        char* str_;
    };
    
    
    #endif // _STRING_H

    String.cpp:

    #include "String.h"
    
    String::String(const char* str)
    {
        int len = sizeof(str) + 1;
        str_ = new char[len];
        memset(str_, 0, len);
        strcpy(str_, str);
    }
    
    String::~String()
    {
        delete[] str_;
    }

    另外还需要实现深拷贝,关于为什么要实现深拷贝,可以参考博文:http://www.cnblogs.com/webor2006/p/5084247.html,具体代码如下:

    编译运行:

    再次编译:

    再次编译:

    下面来实现赋值运算符,因为默认情况下:

    String s1("aaa");

    String s2 = s1;等价于s2.str_=s1.str_实现的也是浅拷贝,而当两个对象释放时,都会释放同一个str_就会出问题,所以需要实现赋值运算符实现深拷贝,具体如下:

    String.cpp:

    #pragma warning(disable:4996)
    #include "String.h"
    #include <string.h>
    #include <iostream>
    using namespace std;
    
    String::String(const char* str)
    {
        str_ = allocAndCpy(str);
    }
    
    String::String(const String& other) {
        str_ = allocAndCpy(other.str_);
    }
    
    String& String::operator = (const String& other) {
        if(this == &other)
            return *this;
    
        delete[] str_;
        str_ = allocAndCpy(other.str_);
        return *this;
    }
    
    char* String::allocAndCpy(const char* str) {
        int len = sizeof(str) + 1;
        char* newStr = new char[len];
        memset(newStr, 0, len);
        strcpy(newStr, str);
        return newStr;
    }
    
    String::~String()
    {
        delete[] str_;
    }
    
    void String::display() const {
        cout<<str_<<endl;
    }

    测试代码:

    编译运行:

    如果是这样编写测试代码,还能编译通过么?

    编译运行:

    这是由于当执行s3="xxx"时会调用转换构造函数:

    如果不让其转换构造,那还能支持这种写法么?

    见证奇迹:

    其实可以通过重载运算赋值运算符来解决,如下:

    再次编译运行:

     先来编写测试代码再来去实现非运算符重载:

    当然目前的代码是无法编译通过的:

    下面来实现非运算符重载:

     

    编译运行:

  • 相关阅读:
    文件的操作
    encode,decode,str,bytes
    字符串操作
    suse12安装mysql8.16
    VMware配置共享磁盘安装RAC
    Linux过滤文本并显示过滤文字的上下文
    Linux服务器卸载mysql指南
    oracle 各版本各日志存放位置
    impdp按用户导入
    数据泵expdp定时备份
  • 原文地址:https://www.cnblogs.com/webor2006/p/5351990.html
Copyright © 2011-2022 走看看