zoukankan      html  css  js  c++  java
  • C++学习笔记-运算符重载

    运算符重载使得用户自定义的数据以一种更简洁的方式工作

    运算符重载规则

    重载运算符的限制

    可以重载的运算符

    +-*/%^&|~
    !=<>+=-=*=/=%=
    ^=&=|=<<>>>>=<<===!=
    <=>=&&||++--->*' ->
    []()newdeletenew[]delete[]

    不能重载的算符

    .::.*?:sizeof

    重载运算符函数可以对运算符作出新的解释,但原有基本语义不变

    1. 不改变运算符的优先级
    2. 不改变运算符的结合性
    3. 不改变运算符所需要的操作数
    4. 不能创建新的运算符

    运算符重载的语法形式

    运算符函数是一种特殊的成员函数或友员函数
    成员函数的语法形式为

    返回值类型 类名 :: operator op( 参数表 )
    {
        // 相对于该类定义的操作
    }
    

    一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符

    用成员或友员函数重载运算符

    运算符函数可以重载为成员函数或友员函数
    关键区别在于成员函数具有 this 指针,友员函数没有this指针
    不管是成员函数还是友员函数重载,运算符的使用方法相同
    但传递参数的方式不同,实现代码不同,应用场合也不同

    一元运算符

    Object op或op Object

    • 重载为成员函数,解释为
    Object.operator op()
    

    操作数由对象Object通过this指针隐含传递

    • 重载为友员函数,解释为
    operator op (Object)
    

    操作数由参数表的参数Object提供

    二元运算符

    ObjectL op ObjectR

    • 重载为成员函数,解释为:
    ObjectL.operator op( ObjectR )
    

    左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递

    • 重载为友员函数,解释为:
    operator op( ObjectL, ObjectR )
    

    左右操作数都由参数传递

    用成员函数重载

    当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数

    用友员函数重载

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

    友元函数重载存在的问题

    用友员函数重载像“++”这样的运算符时,有时会碰到问题

    TriCoor TriCoor::operator++()
    {
        x++; y++; z++;
        return *this;
    }//ok,修改了this指针所指对象
    

    用成员函数重载一元运算符时,所需要的唯一变元通过 this 指针传递,对 this 所指对象数据的任何改变都会影响到激活运算符函数的对象

    1. 若定义友员函数 friend operator++()版本:
    TriCoor operator++(TriCoor opl)
    {
        opl.x++;
        opl.y++;
        opl.z++;
        return opl;
    }
    

    函数使用传值参数,对 opl 的所有修改都无法传到函数体外,不会影响被调用的对象
    2. 用指向激活对象的指针定义友员函数:

    TriCoor operator ++ (TriCoor * opl)
    {
        opl->x++;
        opl->y++;
        opl->z++;
        return *opl;
    }
    

    C++不知道如何激活该函数,下述代码无法编译:

    TriCoor ob(1, 2, 3);
    &ob++;//error
    
    1. 使用引用参数:
    TriCoor operator ++ (TriCoor & opl)
    {
        opl.x++;
        opl.y++;
        opl.z++;
        return opl;
    }
    

    下述代码是正确的:

    TriCoor ob (1, 2, 3);
        ob++;//ok,传名
    

    如果一个运算符的操作要修改类的对象的状态,要重载为友员函数时,应该使用引用参数

    若一运算符的操作需要修改类对象状态时,应该用成员函数重载;
    需要左值操作数的运算符(如 ++,–),若重载为友员函数时要用引用参数
    C++不能用友员重载的运算符:= () [] ->
    如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则必须用友员函数重载

    几个典型运算符重载

    数学类中常用的几个运算符重载的特点和应用

    重载++与–

    A Aobject;
    运算符 ++ 和 – 有两种方式:

    • 前置方式:++Aobject与–Aobject
      一元成员函数重载 A::A operator++ ();,解释为:Aobject.operator ++();
      友员函数重载 friend A operator++ (A &);,解释为:operator ++(Aobject);
    • 后置方式:Aobject++与Aobject–
      二元成员函数重载 A::A operator++ (int);,解释为:Aobject.operator ++(0);
      友员函数重载:friend A operator++ (A &, int);,解释为:operator++(Aobject, 0);

    前置++重载

    Complex& operator++()
    {
    	this->a++;
    	this->b++;
    	return *this;
    }
    

    后置++重载

    Complex operator++(int)
    {
    	Complex tmp = *this;
    	this->a++;
    	this->b++;
    	return tmp;
    }
    

    重载赋值运算符

    赋值运算符重载用于对象数据的复制
    operator= 必须重载为成员函数
    重载函数原型为:类型 & 类名::operator= (const 类名 &);

    重载运算符[]和()

    运算符 []() 是二元运算符
    []() 只能用成员函数重载,不能用友元函数重载

    • 重载下标运算符 []
      [] 运算符用于访问数据对象的元素
      重载格式:类型 类::operator[] (类型);
      e.g.
      x 是类 X 的一个对象,则表达式x[y]可被解释为x.operator[](y)
    #include <iostream>
    class vector
    { 
    public:
        vector(int n)
        {
            v = new int[n];
            size = n;
        }
        ~vector()
        {
            delete[] v;
            size = 0;
        }
        int & operator[](int i)
        {
            return v[i];
        }
    private :       
        int *v;
        int size;
    };
    void main()
    {
        vector a(5);
        a[2] = 12;	  
        cout << a[2] << endl;
    }
    
    • 重载函数调用符 ()
      () 运算符用于函数调用
      重载格式:类型 类::operator()(表达式表);
      x 是类 X 的一个对象,则表达式x(arg1, arg2, … )可被解释为x.operator()(arg1, arg2, … )
    #include <iostream>
    class F
    { 
    public:  
        double operator()(double x , double y);
    };
    double F::operator()(double x,double y)
    {
        return x * x + y * y;
    }
    void main()			
    {
        F f;
        cout << f(5.2, 2.5) << endl;
    }
    

    重载流插入和流提取运算符

    istreamostream 是 C++ 的预定义流类
    cinistream 的对象,coutostream 的对象
    运算符 <<ostream 重载为插入操作,用于输出基本类型数据
    运算符 >>istream 重载为提取操作,用于输入基本类型数据
    用友员函数重载 <<>> ,输出和输入用户自定义的数据类型

    friend ostream& operator<<(ostream &out, Complex &c1);
    ···
    ostream& operator<<(ostream &out, Complex &c)
    {
    	out<<c.a<<" + "<<c.b<<"i"<<endl;
    	return out;
    }
    

    重载运算符[],=,==,!=项目演示

    Array.h

    #ifndef _ARRAY_H_
    #define _ARRAY_H_
     
    class Array
    {
    private:
        int mLength;
        int* mSpace;
    
    public:
        Array(int length);
        Array(const Array& obj);
        int length();
        void setData(int index, int value);
        int getData(int index);
        ~Array();
    
    public:
    	int& Array::operator[](int i);
    	Array& Array::operator=(Array &a1);
    	bool operator==(Array &a2);
    	bool operator!=(Array &a2);
    };
    
    #endif
    

    Array.cpp

    #include "iostream"
    #include "Array.h"
    using namespace std;
    
    Array::Array(int length)
    {
        if( length < 0 )
        {
            length = 0;
        }
        mLength = length;
        mSpace = new int[mLength];
    }
    
    Array::Array(const Array& obj)
    {
        mLength = obj.mLength;
        mSpace = new int[mLength];
        for(int i=0; i<mLength; i++)
        {
            mSpace[i] = obj.mSpace[i];
        }
    }
    
    int Array::length()
    {
        return mLength;
    }
    
    void Array::setData(int index, int value)
    {
        mSpace[index] = value;
    }
    
    int Array::getData(int index)
    {
        return mSpace[index];
    }
    
    Array::~Array()
    {
        mLength = -1;
        delete[] mSpace;
    }
    
    //以下是运算符重载函数
    //[]重载
    int& Array::operator[](int i)
    {
    	return mSpace[i];
    }
    
    //=重载
    Array& Array::operator=(Array &a)
    {
    	int i = 0;
    	if (this->mSpace != NULL)
    	{
    		delete[] mSpace;
    		this->mLength = 0;
    	}
    	this->mLength = a.mLength;
    	this->mSpace = new int[a.mLength];
    	for (i=0; i<this->mLength; i++)
    	{
    		mSpace[i] = a[i];
    	}
    	return *this;
    }
    
    //==重载
    bool Array::operator==(Array &a2)
    {
    	if (this->mLength != a2.mLength)
    	{
    		return false;
    	}
    	for (int i=0; i<this->mLength; i++)
    	{
    		if (this->mSpace[i] != a2[i])
    		{
    			return false;
    		}
    	}
    	return true;
    }
    
    //!=重载
    bool Array::operator!=(Array &a2)
    {
    	return !(*this == a2);
    }
    

    ArrayTeat.cpp

    #include "iostream"
    #include "Array.h"
    using namespace std;
    
    int main()
    {
        Array a1(10);
        for(int i=0; i<a1.length(); i++)
        {
            //a1.setData(i, i); 
    		a1[i] = i;
        }
        for(int i=0; i<a1.length(); i++)
        {
            //printf("array %d: %d
    ", i, a1.getData(i));
    		printf("array %d: %d
    ", i, a1[i]);
        }
        Array a2 = a1;
        for(int i=0; i<a2.length(); i++)
        {
            //printf("array %d: %d
    ", i, a2.getData(i));
    		printf("array %d: %d
    ", i, a2[i]);
        }
    	Array a3(20);
    	a2 = a3 = a1;
    	if (a1 == a2)
    	{
    		printf("相等
    ");
    	}
    	else
    	{
    		printf("不相等
    ");
    	}
    	if (a1 != a2)
    	{
    		printf("不相等
    ");
    	}
    	else
    	{
    		printf("相等
    ");
    	}
    	
        system("pause");
        return 0;
    }
    

    类类型转换

    数据类型转换在程序编译时或在程序运行实现
    基本类型 ←→ 基本类型
    基本类型 ←→ 类类型
    类类型 ←→ 类类型

    类对象的类型转换可由两种方式说明:
    构造函数
    转换函数

    称为用户定义的类型转换或类类型转换,有隐式调用和显式调用方式

    构造函数进行类类型转换

    当类 ClassX 具有以下形式的构造函数:
    ClassX::ClassX(arg, arg1 = E1, ..., argn = En);
    说明了一种从参数 arg 的类型到该类类型的转换
    e.g.

    class X
    {
    public:
        X(int);
        X(const char *, int = 0);
    };
    void f(X arg);
    ···
        X a = X(1);// a = 1
        X b = "Jessie";// b = X ( "Jessie" , 0 )
        a = 2;// a = X ( 2 )
        f(3);// f ( X ( 3 ) )
        f(10, 20);// error
    

    类型转换函数

    带参数的构造函数不能把一个类类型转换成基本类型
    类类型转换函数是一种特殊的成员函数,提供类对象之间显式类型转换的机制
    语法形式:

    X::operator T()
    {
        ···
        return T 类型的对象
    }
    

    功能:将类型 X 的对象转换为类型 T 的对象

    • T 可以是预定义类型,也可以是用户定义类型
    • 函数没有参数,没有返回类型,但必须有一条 return 语句,返回 T 类型的对象
    • 该函数只能为成员函数,不能为友员
    class X
    {
    ···
    public:
        operator int();
    ···
    };
    void f(X a)
    {
        int i = int(a);
        i = (int)a;
        i = a;
    }
    

    类型转换函数有两种使用方式:

    • 隐式使用 i = a;
    • 显式使用 i = a.operator int();//int(a) (int)a
      使用不同函数作类型转换函数:
        int i = a;//用类型转换函数进行转换
        X i = a;//用构造函数进行转换
    

    运算符重载运用

    全局函数,类成员函数实现运算符重载

    1. 承认操作符重载是一个函数,写出函数名称
    2. 根据操作数写出函数参数
    3. 根据业务,完善函数返回值,实现函数业务

    全局函数:写出函数实现,在类中添加友元函数声明,一般只有拿不到源代码的类参与才会用全局函数实现

    操作符重载的方法

    • 全局函数友元函数法(技术推演)
      private声明使得类的成员不能被外界访问
      但是通过friend关键字可以例外的开放权限
    • 通过成员函数进行操作符的重载
      用成员函数重载的操作符,左操作数,通过this指针隐含传送
      比全局函数友元函数法少一个参数,即左操作数;不需要使用friend关键字
    Complex c3 = c1 + c2;
    Complex c3 = c1.operator+(c2);
    
    • 什么时候用全局函数重载操作符PK什么时候用成员函数重载操作符
    1. 当无法修改左操作数的类时,使用全局函数进行重载
    2. =, [], ()和->操作符只能通过成员函数进行重载

    运算符重载总结

    • 运算符重载可以像基本数据类型一样,用简洁明确的运算符操作自定义的类对象。
    • 重载运算符函数可以对运算符作出新的解释,但原有基本语义不变。
    • 运算符函数既可以重载为成员函数,也可以重载为友员函数或普通函数。
    • 当一元运算符的操作数,或者二元运算符的左操作数是类的一个对象时,以成员函数重载;当一个运算符的操作需要修改类对象- 状态时,应该以成员函数重载。如果以成友员函数重载,则使用引用参数修改对象。
    • 当运算符的操作数(尤其是第一个操作数)希望有隐式转换,则重载算符时必须用友员函数。
    • 构造函数和类型转换函数可以实现基本类型与类类型,以及类类型之间的类型转换
  • 相关阅读:
    修改tomcat访问路径
    HTML img标签属性
    HTML marquee标签属性详解
    HTML input标签
    HTML iframe 标签
    Linux root默认密码问题
    [Linux]查看本机IP
    [Linux]命令行模式切换
    [Linux]命令root与other切换
    [Linux]XAMPP安装
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664730.html
Copyright © 2011-2022 走看看