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. =, [], ()和->操作符只能通过成员函数进行重载

    运算符重载总结

    • 运算符重载可以像基本数据类型一样,用简洁明确的运算符操作自定义的类对象。
    • 重载运算符函数可以对运算符作出新的解释,但原有基本语义不变。
    • 运算符函数既可以重载为成员函数,也可以重载为友员函数或普通函数。
    • 当一元运算符的操作数,或者二元运算符的左操作数是类的一个对象时,以成员函数重载;当一个运算符的操作需要修改类对象- 状态时,应该以成员函数重载。如果以成友员函数重载,则使用引用参数修改对象。
    • 当运算符的操作数(尤其是第一个操作数)希望有隐式转换,则重载算符时必须用友员函数。
    • 构造函数和类型转换函数可以实现基本类型与类类型,以及类类型之间的类型转换
  • 相关阅读:
    Python元组、列表、字典
    测试通过Word直接发布博文
    Python环境搭建(windows)
    hdu 4003 Find Metal Mineral 树形DP
    poj 1986 Distance Queries LCA
    poj 1470 Closest Common Ancestors LCA
    poj 1330 Nearest Common Ancestors LCA
    hdu 3046 Pleasant sheep and big big wolf 最小割
    poj 3281 Dining 最大流
    zoj 2760 How Many Shortest Path 最大流
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664730.html
Copyright © 2011-2022 走看看