精华内容
下载资源
问答
  • C++单目运算符重载 单目运算符只有一个操作数,如!a,-b,&c,*p,还有最常用的++i和–i等。重载单目运算符的方法与重载双目运算符的方法是类似的。但由于单目运算符只有一个操作数,因此运算符重载函数只有一个参数...
  • 以下是对C++运算符重载 成员函数与友元函数进行了介绍,需要的朋友可以过来参考下
  • 运算符重载

    2014-08-05 09:59:46
    运算符重载,该程序实现加法,减法,乘法,除法,非常简单的运算符重载
  • 【项目-分数类中的运算符重载】 (1)实现分数类中的运算符重载,在分数类中可以完成分数的加减乘除(运算后再化简)、比较(6种关系)的运算。 class CFraction { private: int nume; // 分子 int deno; // ...
  • 多态性和运算符重载
  • 矩阵类的运算符重载

    2019-05-19 19:28:39
    矩阵类的运算符重载,编写一个矩阵类Matrix,并重载运算符“+”、“-”、“*”、“>>”、“”来分别实现矩阵的加法、减法、乘法,以及标准流输入和流输出操作。
  • 需要的题量多的话建议购买付费专栏(包含上百道题目答案,并持续更新中)
  • C++类的运算符重载,包括 = ++ -- -(负号运算符) + - * / % == != < > <= >= << >>
  • C++ 运算符重载

    万次阅读 多人点赞 2018-12-26 21:39:21
    1. C++运算符重载的概念和原理 2. C++重载=(C++重载赋值运算符) 3. C++深拷贝和浅拷贝(C++深复制和浅复制) 4. C++运算符重载为友元函数 5. C++实现可变长度的动态数组 6. C++重载&lt;&lt;和&gt;...

    前言

    本文引用于“C语言中文网”,我整理出来放在博客,方便大家共同学习。所有知识点和代码均已亲测可用,如有疑问,可提出,一起讨论学习。

    本章内容:

    1. C++运算符重载的概念和原理
    2. C++重载=(C++重载赋值运算符)
    3. C++深拷贝和浅拷贝(C++深复制和浅复制)
    4. C++运算符重载为友元函数
    5. C++实现可变长度的动态数组
    6. C++重载<<和>>(C++重载输出运算符和输入运算符)
    7. C++重载()(强制类型转换运算符)
    8. C++重载++和--(自增和自减运算符)
    9. C++运算符重载注意事项以及汇总

    1 C++运算符重载的概念和原理 

    如果不做特殊处理,C++ 的 +、-、*、/ 等运算符只能用于对基本类型的常量或变量进行运算,不能用于对象之间的运算。

    有时希望对象之间也能用这些运算符进行运算,以达到使程序更简洁、易懂的目的。例如,复数是可以进行四则运算的,两个复数对象相加如果能直接用+运算符完成,不是很直观和简洁吗?

    利用 C++ 提供的“运算符重载”机制,赋予运算符新的功能,就能解决用+将两个复数对象相加这样的问题。

    运算符重载,就是对已有的运算符赋予多重含义,使同一运算符作用于不同类型的数据时产生不同的行为。运算符重载的目的是使得 C++ 中的运算符也能够用来操作对象。

    运算符重载的实质是编写以运算符作为名称的函数。不妨把这样的函数称为运算符函数。运算符函数的格式如下:

    返回值类型  operator  运算符(形参表)
    {
        ....
    }

    包含被重载的运算符的表达式会被编译成对运算符函数的调用,运算符的操作数成为函数调用时的实参,运算的结果就是函数的返回值。运算符可以被多次重载。

    运算符可以被重载为全局函数,也可以被重载为成员函数。一般来说,倾向于将运算符重载为成员函数,这样能够较好地体现运算符和类的关系。来看下面的例子:

    #include <iostream>
    using namespace std;
    class Complex
    {
        public:
        double real, imag;
        Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
        Complex operator - (const Complex & c);
    };
    Complex operator + (const Complex & a, const Complex & b)
    {
        return Complex(a.real + b.real, a.imag + b.imag); //返回一个临时对象
    }
    Complex Complex::operator - (const Complex & c)
    {
        return Complex(real - c.real, imag - c.imag); //返回一个临时对象
    }
    int main()
    {
        Complex a(4, 4), b(1, 1), c;
        c = a + b; //等价于 c = operator + (a,b);
        cout << c.real << "," << c.imag << endl;
        cout << (a - b).real << "," << (a - b).imag << endl; //a-b等价于a.operator - (b)
        return 0;
    }

    程序的输出结果是:

    5,5
    3,3

    程序将+重载为一个全局函数(只是为了演示这种做法,否则重载为成员函数更好),将-重载为一个成员函数。

    运算符重载为全局函数时,参数的个数等于运算符的目数(即操作数的个数);运算符重载为成员函数时,参数的个数等于运算符的目数减一。

    如果+没有被重载,第 21 行会编译出错,因为编译器不知道如何对两个 Complex 对象进行+运算。有了对+的重载,编译器就将a+b理解为对运算符函数的调用,即operator+(a,b),因此第 21 行就等价于:

    c = operator+(a, b);

    即以两个操作数 a、b 作为参数调用名为operator+的函数,并将返回值赋值给 c。

    第 12 行,在 C++ 中,“类名(构造函数实参表)”这种写法表示生成一个临时对象。该临时对象没有名字,生存期就到包含它的语句执行完为止。因此,第 12 行实际上生成了一个临时的 Complex 对象作为 return 语句的返回值,该临时对象被初始化为 a、b 之和。第 16 行与第 12 行类似。

    由于-被重载为 Complex 类的成员函数,因此,第 23 行中的a-b就被编译器处理成:

    a.operator-(b);

    由此就能看出,为什么运算符重载为成员函数时,参数个数要比运算符目数少 1 了。

    2 C++重载=(C++重载赋值运算符)

    赋值运算符=要求左右两个操作数的类型是匹配的,或至少是兼容的。有时希望=两边的操作数的类型即使不兼容也能够成立,这就需要对=进行重载。C++ 规定,=只能重载为成员函数。来看下面的例子。

    要编写一个长度可变的字符串类 String,该类有一个 char* 类型的成员变量,用以指向动态分配的存储空间,该存储空间用来存放以\0结尾的字符串。String 类可以如下编写:

    #include <iostream>
    #include <cstring>
    using namespace std;
    class String {
    private:
        char * str;
    public:
        String() :str(NULL) { }
        const char * c_str() const { return str; };
        String & operator = (const char * s);
        ~String();
    };
    String & String::operator = (const char * s)
    //重载"="以使得 obj = "hello"能够成立
    {
        if (str)
            delete[] str;
        if (s) { //s不为NULL才会执行拷贝
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        else
            str = NULL;
        return *this;
    }
    String::~String()
    {
        if (str)
            delete[] str;
    };
    int main()
    {
        String s;
        s = "Good Luck,"; //等价于 s.operator=("Good Luck,");
        cout << s.c_str() << endl;
        // String s2 = "hello!"; //这条语句要是不注释掉就会出错
        s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");
        cout << s.c_str() << endl;
        return 0;
    }

    程序的运行结果:

    Good Luck,
    Shenzhou 8!

    第 8 行的构造函数将 str 初始化为 NULL,仅当执行了 operator= 成员函数后,str 才会指向动态分配的存储空间,并且从此后其值不可能再为 NULL。在 String 对象的生存期内,有可能从未执行过 operator= 成员函数,所以在析构函数中,在执行delete[] str之前,要先判断 str 是否为 NULL。

    第 9 行的函数返回了指向 String 对象内部动态分配的存储空间的指针,但是不希望外部得到这个指针后修改其指向的字符串的内容,因此将返回值设为 const char*。这样,假定 s 是 String 对象,那么下面两条语句编译时都会报错,s 对象内部的字符串就不会轻易地从外部被修改了 :

    char* p = s.c_str ();
    strcpy(s.c_str(), "Tiangong1");

    第一条语句出错是因为=左边是 char* 类型,右边是 const char * 类型,两边类型不匹配;第二条语句出错是因为 strcpy 函数的第一个形参是 char* 类型,而这里实参给出的却是 const char * 类型,同样类型不匹配。

    如果没有第 13 行对=的重载,第 34 行的s = "Good Luck,"肯定会因为类型不匹配而编译出错。经过重载后,第 34 行等价s.operator=("Good Luck,");,就没有问题了。

    在 operator= 函数中,要先判断 str 是否已经指向动态分配的存储空间,如果是,则要先释放那片空间,然后重新分配一片空间,再将参数 s 指向的内容复制过去。这样,对象中存放的字符串就和 s 指向的字符串一样了。分配空间时,要考虑到字符串结尾的\0,因此分配的字节数要比 strlen(s) 多 1。

    需要注意一点,即使对=做了重载,第 36 行的String s2 = "hello!";还是会编译出错,因为这是一条初始化语句,要用到构造函数,而不是赋值运算符=。String 类没有编写参数类型为 char * 的构造函数,因此编译不能通过。

    就上面的程序而言,对 operator= 函数的返回值类型没有什么特别要求,void 也可以。但是在对运算符进行重载时,好的风格是应该尽量保留运算符原本的特性,这样其他人在使用这个运算符时才不容易产生困惑。赋值运算符是可以连用的,这个特性在重载后也应该保持。即下面的写法应该合法:

    a = b = c;

    假定 a、b、c 都是 String 对象,则上面的语句等价于下面的嵌套函数调用:

    a.operator=( b.operator=(c) );

    如果 operator= 函数的返回值类型为 void,显然上面这个嵌套函数调用就不能成立。将返回值类型改为 String 并且返回 *this 可以解决问题,但是还不够好。因为,假设 a、b、c 是基本类型的变量,则

    (a =b) = c;

    这条语句执行的效果会使得 a 的值和 c 相等,即a = b这个表达式的值其实是 a 的引用。为了保持=的这个特性,operator= 函数也应该返回其所作用的对象的引用。因此,返回值类型为 String & 才是风格最好的写法。在 a、b、c 都是 String 对象时,(a=b)=c;等价于

    ( a.operator=(b) ).operator=(c);

    a.operator=(b) 返回对 a 的引用后,通过该引用继续调用 operator=(c),就会改变 a 的值。

    3 C++深拷贝和浅拷贝(C++深复制和浅复制) 

    同类对象之间可以通过赋值运算符=互相赋值。如果没有经过重载,=的作用就是把左边的对象的每个成员变量都变得和右边的对象相等,即执行逐个字节拷贝的工作,这种拷贝叫作“浅拷贝”。

    有的时候,两个对象相等,从实际应用的含义上来讲,指的并不应该是两个对象的每个字节都相同,而是有其他解释,这时就需要对=进行重载。

    上节我们定义了 String 类,并重载了=运算符,使得 char * 类型的字符串可以赋值给 String 类的对象。完整代码如下:

    #include <iostream>
    #include <cstring>
    using namespace std;
    class String {
    private:
        char * str;
    public:
        String() :str(NULL) { }
        const char * c_str() const { return str; };
        String & operator = (const char * s);
        ~String();
    };
    String & String::operator = (const char * s)
    //重载"="以使得 obj = "hello"能够成立
    {
        if (str)
            delete[] str;
        if (s) { //s不为NULL才会执行拷贝
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        else
            str = NULL;
        return *this;
    }
    String::~String()
    {
        if (str)
            delete[] str;
    };
    int main()
    {
        String s;
        s = "Good Luck,"; //等价于 s.operator=("Good Luck,");
        cout << s.c_str() << endl;
        // String s2 = "hello!"; //这条语句要是不注释掉就会出错
        s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");
        cout << s.c_str() << endl;
        return 0;
    }

    对于上面的代码,如果让两个 String 对象相等(把一个对象赋值给另一个对象),其意义到底应该是什么呢?是两个对象的 str 成员变量都指向同一个地方,还是两个对象的 str 成员变量指向的内存空间中存放的内容相同?如果把 String 对象理解为存放字符串的对象,那应该是后者比较合理和符合习惯,而前者不但不符合习惯,还会导致程序漏洞。

    按照上面代码中 String 类的写法,下面的程序片段会引发问题:

    String s1, s2;
    s1 = "this";
    s2 = "that";
    s2 = s1;

    执行完上面的第 3 行后,s1 和 s2 的状态如图 1 (a) 所示,它们的 str 成员变量指向不同的存储空间。
     


                                                                                   图1:浅拷贝导致的错误

    s2=s1;执行的是浅拷贝。执行完s2=s1;后,s2.str s1.str 指向同一个地方, 如图 1 (b) 所示。这导致 s2.str 原来指向的那片动态分配的存储空间再也不会被释放,变成内存垃圾。

    此外,s1 和 s2 消亡时都会执行delete[] str;,这就使得同一片存储空间被释放两次,会导致严重的内存错误,可能引发程序意外中止。

    而且,如果执行完s1=s2;后 又执行s1 = "some";,则会导致 s2.str 也被释放。

    为解决上述问题,需要对做=再次重载。重载后的的逻辑,应该是使得执行s2=s1;后,s2.str 和 s1.str 依然指向不同的地方,但是这两处地方所存储的字符串是一样的。再次重载=的写法如下:

    String & String::operator = (const String & s)
    {
        if(str == s.str)
            return * this;
        if(str)
            delete[] str;
        if(s.str){ //s. str不为NULL才执行复制操作
            str = new char[ strlen(s.str) + 1 ];
            strcpy(str, s.str);
        }
        else
            str = NULL;
        return * this;
    }

    经过重载,赋值号=的功能不再是浅拷贝,而是将一个对象中指针成员变量指向的内容复制到另一个对象中指针成员变量指向的地方。这样的拷贝就叫“深拷贝”。

    程序第 3 行要判断 str==s.str,是因为要应付如下的语句:

    s1 = s1;

    这条语句本该不改变s1的值才对。s1=s1;等价于s.operator=(s1);,如果没有第 3 行和第 4 行,就会导致函数执行中的 str 和 s.str 完全是同一个指针(因为形参 s 引用了实参 s1,因此可以说 s 就是 s1)。第 8 行为 str 新分配一片存储空间,第 9 行从自己复制到自己,那么 str 指向的内容就不知道变成什么了。

    当然,程序员可能不会写s1=s1;这样莫名奇妙的语句,但是可能会写rs1=rs2;,如果 rs1 和 rs2 都是 String 类的引用,而且它们正好引用了同一个 String 对象,那么就等于发生了s1=s1;这样的情况。

    思考题:上面的两个 operator= 函数有什么可以改进以提高执行效率的地方?

    重载了两次=的 String 类依然可能导致问题。因为没有编写复制构造函数,所以一旦出现使用复制构造函数初始化的 String 对象(例如,String 对象作为函数形参,或 String 对象作为函数返回值),就可能导致问题。最简单的可能出现问题的情况如下:

    String s2;
    s2 = "Transformers";
    String s1(s2);

    s1 是以 s2 作为实参,调用默认复制构造函数来初始化的。默认复制构造函数使得 s1.str 和 s2.str 指向同一个地方,即执行的是浅拷贝,这就导致了前面提到的没有对=进行第二次重载时产生的问题。因此还应该为 String 类编写如下复制构造函数,以完成深拷贝:

    String::String(String & s)
    {
        if(s.str){
            str = new char[ strlen(s.str) + 1 ];
            strcpy(str, s.str);
        }
        else
            str = NULL;
    }
    
    最后,给出 String 类的完整代码:
    class String {
    private:
        char * str;
    public:
        String() :str(NULL) { }
        String(String & s);
        const char * c_str() const { return str; };
        String & operator = (const char * s);
        String & operator = (const String & s);
        ~String();
    };
    String::String(String & s)
    {
        if (s.str) {
            str = new char[strlen(s.str) + 1];
            strcpy(str, s.str);
        }
        else
            str = NULL;
    }
    String & String::operator = (const String & s)
    {
        if (str == s.str)
            return *this;
        if (str)
            delete[] str;
        if (s.str) { //s. str不为NULL才执行复制操作
            str = new char[strlen(s.str) + 1];
            strcpy(str, s.str);
        }
        else
            str = NULL;
        return *this;
    }
    String & String::operator = (const char * s)
    //重载"="以使得 obj = "hello"能够成立
    {
        if (str)
            delete[] str;
        if (s) { //s不为NULL才会执行拷贝
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        else
            str = NULL;
        return *this;
    }
    String::~String()
    {
        if (str)
            delete[] str;
    };

    4 C++运算符重载为友元函数

    一般情况下,将运算符重载为类的成员函数是较好的选择。但有时,重载为成员函数不能满足使用要求,重载为全局函数又不能访问类的私有成员,因此需要将运算符重载为友元。

    例如,对于复数类 Complex 的对象,希望它能够和整型以及实数型数据做四则运算,假设 c 是 Complex 对象,希望c+55+c这两个表达式都能解释得通。

    将+重载为 Complex 类的成员函数能解释c+5,但是无法解释5+c。要让5+c有意义,则应对+进行再次重载,将其重载为一个全局函数。为了使该全局函数能访问 Complex 对象的私有成员,就应该将其声明为 Complex 类的友元。具体写法如下:

    class Complex
    {
        double real, imag;
    public:
        Complex(double r, double i):real(r), imag(i){};
        Complex operator + (double r);
        friend Complex operator + (double r, const Complex & c);
    };
    Complex Complex::operator + (double r)
    { //能解释c+5
        return Complex(real+r, imag);
    }
    Complex operator + (double r, const Complex & c)
    { //能解释5+c
        return Complex (c.real+r, c.imag);
    }

     5 C++实现可变长度的动态数组

    实践中经常碰到程序需要定义一个数组,但不知道定义多大合适的问题。按照最大的可能性定义,会造成空间浪费;定义小了则无法满足需要。

    如果用动态内存分配的方式解决,需要多少空间就动态分配多少,固然可以解决这个问题,但是要确保动态分配的内存在每一条执行路径上都能够被释放,也是一件头疼的事情。

    因此需要编写一个长度可变的数组类,该类的对象就能存放一个可变长数组。该数组类应该有以下特点:

    • 数组的元素个数可以在初始化该对象时指定。
    • 可以动态往数组中添加元素。
    • 使用该类时不用担心动态内存分配和释放问题。
    • 能够像使用数组那样使用动态数组类对象,如可以通过下标访问其元素。

    程序代码如下:

    #include <iostream>
    #include <cstring>
    using namespace std;
    class CArray
    {
    	int size; //数组元素的个数
    	int* ptr; //指向动态分配的数组
    public:
    	CArray(int s = 0); //s代表数组元素的个数
    	CArray(CArray & a);
    	~CArray();
    	void push_back(int v); //用于在数组尾部添加一个元素 v
    	CArray & operator = (const CArray & a); //用于数组对象间的赋值
    	int length() const { return size; } //返回数组元素个数
    	int & operator[](int i)
    	{ //用以支持根据下标访问数组元素,如“a[i]=4;”和“n=a[i];”这样的语句
    		return ptr[i];
    	};
    };
    CArray::CArray(int s) : size(s)
    {
    	if (s == 0)
    		ptr = NULL;
    	else
    		ptr = new int[s];
    }
    CArray::CArray(CArray & a)
    {
    	if (!a.ptr) {
    		ptr = NULL;
    		size = 0;
    		return;
    	}
    	ptr = new int[a.size];
    	memcpy(ptr, a.ptr, sizeof(int) * a.size);
    	size = a.size;
    }
    CArray::~CArray()
    {
    	if (ptr) delete[] ptr;
    }
    CArray & CArray::operator=(const CArray & a)
    { //赋值号的作用是使 = 左边对象中存放的数组的大小和内容都与右边的对象一样
    	if (ptr == a.ptr) //防止 a=a 这样的赋值导致出错
    		return *this;
    	if (a.ptr == NULL) { //如果a里面的数组是空的
    		if (ptr)
    		delete[] ptr;
    		ptr = NULL;
    		size = 0;
    		return *this;
    	}
    	if (size < a.size) { //如果原有空间够大,就不用分配新的空间
    		if (ptr)
    			delete[] ptr;
    		ptr = new int[a.size];
    	}
    	memcpy(ptr, a.ptr, sizeof(int)*a.size);
    	size = a.size;
    	return *this;
    }
    void CArray::push_back(int v)
    { //在数组尾部添加一个元素
    	if (ptr) {
    		int* tmpPtr = new int[size + 1]; //重新分配空间
    		memcpy(tmpPtr, ptr, sizeof(int) * size); //复制原数组内容
    		delete[] ptr;
    		ptr = tmpPtr;
    	}
    	else //数组本来是空的
    		ptr = new int[1];
    	ptr[size++] = v; //加入新的数组元素
    }
    int main()
    {
    	CArray a; //开始的数组是空的
    	for (int i = 0; i<5; ++i)
    		a.push_back(i);
    	CArray a2, a3;
    	a2 = a;
    	for (int i = 0; i<a.length(); ++i)
    		cout << a2[i] << " ";
    	a2 = a3; //a2 是空的
    	for (int i = 0; i<a2.length(); ++i) //a2.length()返回 0
    		cout << a2[i] << " ";
    	cout << endl;
    	a[3] = 100;
    	CArray a4(a);
    	for (int i = 0; i<a4.length(); ++i)
    		cout << a4[i] << " ";
    	return 0;
    }

    程序的输出结果为:

    0 1 2 3 4
    0 1 2 100 4

    []是双目运算符,有两个操作数,一个在里面,一个在外面。表达式 a[i] 等价于 a.operator[](i)。按照[]原有的特性,a[i]应该能够作为左值使用,因此 operator[] 函数应该返回引用。

    思考题:每次在数组尾部添加一个元素都要重新分配内存并且复制原有内容,显然效率是低下的。有什么办法能够加快添加元素的速度呢?

    6 C++重载<<和>>(C++重载输出运算符和输入运算符)

    在 C++ 中,左移运算符<<可以和 cout 一起用于输出,因此也常被称为“流插入运算符”或者“输出运算符”。实际上,<<本来没有这样的功能,之所以能和 cout 一起使用,是因为被重载了。

    cout 是 ostream 类的对象。ostream 类和 cout 都是在头文件 <iostream> 中声明的。ostream 类将<<重载为成员函数,而且重载了多次。为了使cout<<"Star War"能够成立,ostream 类需要将<<进行如下重载:

    ostream & ostream::operator << (const char* s)
    {
        //输出s的代码
        return * this;
    }

    为了使cout<<5;能够成立,ostream 类还需要将<<进行如下重载:

    ostream & ostream::operator << (int n)
    {
        //输出n的代码
        return *this;
    }

    重载函数的返回值类型为 ostream 的引用,并且函数返回 *this,就使得cout<<"Star War"<<5能够成立。有了上面的重载,cout<<"Star War"<<5;就等价于:

    ( cout.operator<<("Star War") ).operator<<(5);

    重载函数返回 *this,使得cout<<"Star War"这个表达式的值依然是 cout(说得更准确一点就是 cout 的引用,等价于 cout),所以能够和<<5继续进行运算。

    cin 是 istream 类的对象,是在头文件 <iostream> 中声明的。istream 类将>>重载为成员函数,因此 cin 才能和>>连用以输入数据。一般也将>>称为“流提取运算符”或者“输入运算符”。

    例题:假定 c 是 Complex 复数类的对象,现在希望写cout<<c;就能以 a+bi 的形式输出 c 的值;写cin>>c;就能从键盘接受 a+bi 形式的输入,并且使得 c.real = a, c.imag = b。

    显然,要对<<>>进行重载,程序如下:

    #include <iostream>
    #include <string>
    #include <cstdlib>
    using namespace std;
    class Complex
    {
    	double real,imag;
    public:
    	Complex( double r=0, double i=0):real(r),imag(i){ };
    	friend ostream & operator<<( ostream & os,const Complex & c);
    	friend istream & operator>>( istream & is,Complex & c);
    };
    ostream & operator<<( ostream & os,const Complex & c)
    {
    	os << c.real << "+" << c.imag << "i"; //以"a+bi"的形式输出
    	return os;
    }
    istream & operator>>( istream & is,Complex & c)
    {
    	string s;
    	is >> s; //将"a+bi"作为字符串读入, "a+bi" 中间不能有空格
    	int pos = s.find("+",0);
    	string sTmp = s.substr(0,pos); //分离出代表实部的字符串
    	c.real = atof(sTmp.c_str());//atof库函数能将const char*指针指向的内容转换成 float
    	sTmp = s.substr(pos+1, s.length()-pos-2); //分离出代表虚部的字符串
    	c.imag = atof(sTmp.c_str());
    	return is;
    }
    int main()
    {
    	Complex c;
    	int n;
    	cin >> c >> n;
    	cout << c << "," << n;
    	return 0;
    }

    程序的运行结果:

    13.2+133i 87
    13.2+133i,87

    因为没有办法修改 ostream 类和 istream 类,所以只能将<<>>重载为全局函数的形式。由于这两个函数需要访问 Complex 类的私有成员,因此在 Complex 类定义中将它们声明为友元。

    cout<<c会被解释成operator<<(cout, c),因此编写 operator<< 函数时,它的两个参数就不难确定了。

    第 13 行,参数 os 只能是 ostream 的引用,而不能是 ostream 对象,因为 ostream 的复制构造函数是私有的,没有办法生成 ostream 参数对象。operator<< 函数的返回值类型设为 ostream &,并且返回 os,就能够实现<<的连续使用,如cout<<c<<5。在本程序中,执行第 34 行的cout<<c进入 operator<< 后,os 引用的就是 cout,因此第 34 行就能产生输出。

    用 cin 读入复数时,对应的输入必须是 a+bi 的格式,而且中间不能有空格,如输入 13.2+33.4i。第 21 行的is>>s;读入一个字符串。假定输入的格式没有错误,那么被读入 s  的就是 a+bi 格式的字符串。

    读入后需要将字符串中的实部 a 和虚部 b 分离出来,分离的办法就是找出被+隔开的两个子串,然后将两个字符串转换成浮点数。第 24 行调用了标准库函数 atof 来将字符串转换为浮点数。该函数的原型是float atof(const char *),它在 <cstdlib> 头文件中声明。

    7 C++重载()(强制类型转换运算符)

    在 C++ 中,类型的名字(包括类的名字)本身也是一种运算符,即类型强制转换运算符。

    类型强制转换运算符是单目运算符,也可以被重载,但只能重载为成员函数,不能重载为全局函数。经过适当重载后,(类型名)对象这个对对象进行强制类型转换的表达式就等价于对象.operator 类型名(),即变成对运算符函数的调用。

    下面的程序对 double 类型强制转换运算符进行了重载。

    #include <iostream>
    using namespace std;
    class Complex
    {
        double real, imag;
    public:
        Complex(double r = 0, double i = 0) :real(r), imag(i) {};
        operator double() { return real; } //重载强制类型转换运算符 double
    };
    int main()
    {
        Complex c(1.2, 3.4);
        cout << (double)c << endl; //输出 1.2
        double n = 2 + c; //等价于 double n = 2 + c. operator double()
        cout << n; //输出 3.2
    }

    程序的输出结果是:

    1.2
    3.2

    第 8 行对 double 运算符进行了重载。重载强制类型转换运算符时,不需要指定返回值类型,因为返回值类型是确定的,就是运算符本身代表的类型,在这里就是 double。

    重载后的效果是,第 13 行的(double)c等价于c.operator double()

    有了对 double 运算符的重载,在本该出现 double 类型的变量或常量的地方,如果出现了一个 Complex 类型的对象,那么该对象的 operator double 成员函数就会被调用,然后取其返回值使用。

    例如第 14 行,编译器认为本行中c这个位置如果出现的是 double 类型的数据,就能够解释得通,而 Complex 类正好重载了 double 运算符,因而本行就等价于:

    double n = 2 + c.operator double();

    8 C++重载++和--(自增和自减运算符)

    自增运算符++、自减运算符--都可以被重载,但是它们有前置、后置之分。

    ++为例,假设 obj 是一个 CDemo 类的对象,++objobj++本应该是不一样的,前者的返回值应该是 obj 被修改后的值,而后者的返回值应该是 obj 被修改前的值。如果如下重载++运算符:

    CDemo & CDemo::operator ++ ()
    {
        //...
        return * this;
    }

    那么不论obj++还是++obj,都等价于obj.operator++()无法体现出差别。

    为了解决这个问题,C++ 规定,在重载++--时,允许写一个增加了无用 int 类型形参的版本,编译器处理++--前置的表达式时,调用参数个数正常的重载函数;处理后置表达式时,调用多出一个参数的重载函数。来看下面的例子:

    #include <iostream>
    using namespace std;
    class CDemo {
    private:
    	int n;
    public:
    	CDemo(int i=0):n(i) { }
    	CDemo & operator++(); //用于前置形式
    	CDemo operator++( int ); //用于后置形式
    	operator int ( ) { return n; }
    	friend CDemo & operator--(CDemo & );
    	friend CDemo operator--(CDemo & ,int);
    };
    CDemo & CDemo::operator++()
    {//前置 ++
    	n ++;
    	return * this;
    }
    CDemo CDemo::operator++(int k )
    { //后置 ++
    	CDemo tmp(*this); //记录修改前的对象
    	n++;
    	return tmp; //返回修改前的对象
    }
    CDemo & operator--(CDemo & d)
    {//前置--
    	d.n--;
    	return d;
    }
    CDemo operator--(CDemo & d,int)
    {//后置--
    	CDemo tmp(d);
    	d.n --;
    	return tmp;
    }
    int main()
    {
    	CDemo d(5);
    	cout << (d++ ) << ","; //等价于 d.operator++(0);
    	cout << d << ",";
    	cout << (++d) << ","; //等价于 d.operator++();
    	cout << d << endl;
    	cout << (d-- ) << ","; //等价于 operator-(d,0);
    	cout << d << ",";
    	cout << (--d) << ","; //等价于 operator-(d);
    	cout << d << endl;
    	return 0;
    }

    程序运行结果:

    5,6,7,7
    7,6,5,5

    本程序将++重载为成员函数,将--重载为全局函数。其实都重载为成员函数更好,这里将--重载为全局函数只是为了说明可以这么做而已。

    调用后置形式的重载函数时,对于那个没用的 int 类型形参,编译器自动以 0 作为实参。 如第 39 行,d++等价于d.operator++(0)

    对比前置++和后置++运算符的重载可以发现,后置++运算符的执行效率比前置的低。因为后置方式的重载函数中要多生成一个局部对象 tmp(第21行),而对象的生成会引发构造函数调用,需要耗费时间。同理,后置--运算符的执行效率也比前置的低。

    前置++运算符的返回值类型是 CDemo &,而后置++运算符的返回值类型是 CDemo,这是因为运算符重载最好保持原运算符的用法。C++ 固有的前置++运算符的返回值本来就是操作数的引用,而后置++运算符的返回值则是操作数值修改前的复制品。例如:

    int a = 5;
    (++a) = 2;

    上面两条语句执行后,a 的值是 2,因为 ++a 的返回值是 a 的引用。而

    (a++) = 2;

    这条语句是非法的,因为 a++ 的返回值不是引用,不能作为左值。

    --运算符的返回值类型的设定和++运算符一样。

    在有的编译器(如Visual Studio)中,如果没有后置形式的重载,则后置形式的自增或自减表达式也被当作前置形式处理。而在有的编译器(如Dev C++)中,不进行后置形式的重载,则后置形式的表达式就会编译出错。

    9 C++运算符重载注意事项以及汇总

    在 C++ 中进行运算符重载时,有以下问题需要注意:

    • 重载后运算符的含义应该符合原有用法习惯。例如重载+运算符,完成的功能就应该类似于做加法,在重载的+运算符中做减法是不合适的。此外,重载应尽量保留运算符原有的特性。
    • C++ 规定,运算符重载不改变运算符的优先级。
    • 以下运算符不能被重载:..*::? :sizeof
    • 重载运算符()[]->、或者赋值运算符=时,只能将它们重载为成员函数,不能重载为全局函数。

    运算符重载的实质是将运算符重载为一个函数,使用运算符的表达式就被解释为对重载函数的调用。

    运算符可以重载为全局函数。此时函数的参数个数就是运算符的操作数个数,运算符的操作数就成为函数的实参。

    运算符也可以重载为成员函数。此时函数的参数个数就是运算符的操作数个数减一,运算符的操作数有一个成为函数作用的对象,其余的成为函数的实参。

    必要时需要重载赋值运算符=,以避免两个对象内部的指针指向同一片存储空间。

    运算符可以重载为全局函数,然后声明为类的友元。

    <<和>>是在 iostream 中被重载,才成为所谓的“流插入运算符”和“流提取运算符”的。

    类型的名字可以作为强制类型转换运算符,也可以被重载为类的成员函数。它能使得对象被自动转换为某种类型。

    自增、自减运算符各有两种重载方式,用于区别前置用法和后置用法。

    运算符重载不改变运算符的优先级。重载运算符时,应该尽量保留运算符原本的特性。

    展开全文
  • 运算符重载.py python运算符重载
  • 需要的题量多的话建议购买付费专栏(包含上百道题目答案,并持续更新中)
  • 一个实例,实现运算符重载(成员函数和非成员函数两种方式),友元函数的使用,注意事项等,自己学习时编写的,
  • 定义了MTX类,用于保存矩阵,为了方便计算,用运算符重载来实现矩阵运算。
  • For this task, put your code in the following files: Matrix.cpp, Matrix.h. You are to write a class Matrix which includes appropriate fields associated with 2*2 matrices. A 2*2 matrix takes the ...
  • Vec2D类以及运算符重载文件,内容关于运算符重载。通过定义二维数组类来熟悉运算符重载的写法,用于博客中展示的cpp文件。
  • 本文实例讲述了Python运算符重载用法。分享给大家供大家参考。具体分析如下: python中,我们在定义类的时候,可以通过实现一些函数来实现重载运算符。 例子如下: # -*- coding:utf-8 -*- ''''' Created on 2013-3...
  • 主要介绍了C#运算符重载用法,实例分析了C#中运算符重载的基本实现与使用技巧,需要的朋友可以参考下
  • C++ 重载+运算符重载加号 实现矩阵相加  学习C++ 基础知识,这里实现简单的实例,记录下自己学习生活,很简单,大家一起看看吧! 实例代码: #include #include using namespace std; class Complex { private: ...
  • 运算符重载之成员运算符重载

    千次阅读 2019-01-05 22:30:20
    成员运算符重载函数 成员运算符重载函数,即把运算符重载函数定义为某个类的成员函数。 1、定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式如下: 函数类型 operator 运算符(形参...

    成员运算符重载函数

    成员运算符重载函数,即把运算符重载函数定义为某个类的成员函数。

    1、定义成员运算符重载函数的语法形式

    (1)在类的内部,定义成员运算符重载函数的格式如下:

    函数类型 operator 运算符(形参表){
    	函数体
    }
    

    (2)成员运算符重载函数也可以在类中声明成员函数的原型,在类外定义。

    在类内,声明成员运算符重载函数原型的格式如下:

    class X{
    	函数类型 operator 运算符(形参表);
    }
    

    在类外,定义成员运算符重载函数的格式如下:

    函数类型 X::operator 运算符(形参表){
    	函数体
    }
    

    operator 是定义运算符重载函数的关键字;
    运算符即是要重载的运算符名称,必须是 C++中可重载的运算符;
    由于成员运算符重载函数是该类的成员函数,所以在类外定义时需要缀上类名。

      在成员运算符重载函数的形参表中,若运算符是单目的,则参数表为空;若运算符是双目的,则参数表中有一个操作数。

    2、双目运算符重载

      对双目运算符而言,成员运算符重载函数的形参表中仅有一个参数,它作为运算符的右操作数。另一个操作数(左操作数)是隐含的,是该类的当前对象,它是通过 this 指针隐含地递给函数的。例如,

    class X{
    	```
    	int operator+(X a);
    	```
    }
    

      在类 X 中声明了重载 “+” 的成员运算符重载函数,返回类型为 int ,它具有两个操作数,一个是当前对象,另一个是类 X 的对象 a。

    例 1:用成员运算符重载函数进行复数运算。

    #include<iostream>
    using namespace std;
    class Complex{
    	private:
    		double real;
    		double imag;
    	public:
    		Complex(double r=0.0,double i=0.0);		//构造函数
    		void print();		//显示输出复数
    		Complex operator+(Complex c);		//声明运算符 "+" 重载函数
    		Complex operator-(Complex c);		//声明运算符 "-" 重载函数
    		Complex operator*(Complex c);		//声明运算符 "*" 重载函数
    		Complex operator/(Complex c);		//声明运算符 "/" 重载函数
    };
    Complex::Complex(double r,double i){  //构造函数 
    	real=r;
    	imag=i;
    }
    void Complex::print(){		//显示复数的实数和虚数部分 
    	cout<<real;
    	if(imag>0)
    		cout<<"+";
    	if(imag!=0)
    		cout<<imag<<'i'<<endl;
    }
    Complex Complex::operator+(Complex c){		
    	Complex temp;
    	temp.real=real+c.real;
    	temp.imag=imag+c.imag;
    	return temp;
    }
    Complex Complex::operator-(Complex c){		
    	Complex temp;
    	temp.real=real-c.real;
    	temp.imag=imag-c.imag;
    	return temp;
    }
    Complex Complex::operator*(Complex c){		
    	Complex temp;
    	temp.real=real*c.real-imag*c.imag;
    	temp.imag=real*c.imag+imag*c.real;
    	return temp;
    }
    Complex Complex::operator/(Complex c){		
    	Complex temp;
    	double t;
    	t=1/(c.real*c.real+c.imag*c.imag);
    	temp.real=(real*c.real+imag*c.imag)*t;
    	temp.imag=(c.real*imag-real*c.imag)*t;
    	return temp;
    }
    int main(){
    	Complex A1(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;
    	A3=A1.operator+(A2);
    	A4=A1-A2;
    	A5=A1*A2;
    	A6=A1/A2;
    	A1.print();
    	A2.print();
    	A3.print();
    	A4.print();
    	A5.print();
    	A6.print();
    	return 0;
    }
    

    执行结果:
    在这里插入图片描述

    其中,

    A3=A1+A2;
    

    C++ 将其解释为:

    A3=A1.operator+(A2);
    

       一般而言,如果在类 X 中采用成员函数重载双目运算符@,成员运算符函数 operator@ 所需的一个操作数由对象 aa 通过 this 指针隐含地传递,它的另一个操作数 bb 在参数表中显示,则以下两种函数调用方法是等价的:

    aa@bb;		//隐式调用
    aa.operator@(bb);		//显式调用
    

    3、单目运算符重载

      对单目运算符而言,成员运算符重载函数的参数中没有参数,此时当前对象作为运算符的一个操作数。

    例 2:用成员函数重载单目运算符“++”

    #include<iostream>
    using namespace std;
    class Coord{
    	private:
    		int x,y;
    	public:
    		Coord(int i=0,int j=0);
    		void print();
    		Coord operator++();
    };
    Coord::Coord(int i,int j){
    	x=i;
    	y=j;
    }
    void Coord::print(){
    	cout<<"x:"<<x<<",y="<<y<<endl;
    }
    Coord Coord::operator++(){		//定义运算符++重载函数 operator++ 
    	++x;
    	++y;
    	return *this;	//返回当前对象的值 
    }
    int main(){
    	Coord ob(10,20);
    	ob.print();
    	++ob;
    	ob.print();
    	ob.operator++();
    	ob.print();
    	return 0;
    }
    

    执行结果:
    在这里插入图片描述
      本例中主函数 main 中调用成员运算符重载函数 operator++ 的两种方式,

    ++ob;
    

    ob.operator++();
    

    是等价的,其执行效果相同。

      一般而言,采用成员函数重载单目运算符时,以下两种方法是等价:

    @aa;	//隐式调用
    aa.operator@();		//显式调用
    
    展开全文
  • 运算符重载之友元运算符重载

    千次阅读 2019-01-05 21:08:02
    友元运算符重载函数: 把运算符重载函数定义为某个类的友元函数。 1、定义友元运算符重载函数的语法形式 (1)在类的内部,定义友元运算符重载函数的格式如下: friend 函数类型 operator 运算符(形参表) { 函数体 ...

    友元可以参考:https://blog.csdn.net/aaqian1/article/details/84427884

    友元运算符重载函数: 把运算符重载函数定义为某个类的友元函数。

    1、定义友元运算符重载函数的语法形式

    (1)在类的内部,定义友元运算符重载函数的格式如下:

    friend 函数类型 operator 运算符(形参表)
    {
    	函数体
    }
    

    (2)友元运算符重载函数也可以在类中声明友元函数的原型,在类外定义。
    在类中,声明友元运算符重载函数原型的格式:

    class X{
    	friend 函数类型 operator 运算符(形参表);
    }
    

    在类外,定义友元运算符重载函数的格式如下:

    函数类型 operator 运算符(形参表){
    	函数体 
    }
    

    由于友元运算符重载函数不是该类的成员函数,所以在类外定义时不需要缀上类名。
    若友元运算符重载函数重载的是双目运算符,则参数表中有两个操作数;若重载的是单目运算符,则参数表中只有一个操作数。

    2、双目运算符重载

    例 1:用友元运算符重载函数进行复数运算。

    两个复数 a+bi 和 c+di 进行加、减、乘、除的方法如下:
    加法:(a+bi)+(c+di)=(a+b)+(c+d)i
    减法:(a+bi)-(c+di)=(a-b)+(c-d)i
    乘法:(a+bi) * (c+di)=(ac-bd)+(ad+bc)i
    除法:(a+bi) / (c+di)=(a+bi) * (c-di) / (cc+dd)
      在 C++ 中,不能直接进行复数的加、减、乘、除运算,不过可以定义四个友元运算符重载函数,通过重载“+、-、*、/ ”运算符来实现复数运算。
    代码如下:

    #include<iostream>
    using namespace std;
    class Complex{
    	private:
    		double real;    //复数实部 
    		double imag;	//复数虚部 
    	public:
    		Complex(double r=0.0,double i=0.0); //https://blog.csdn.net/aaqian1/article/details/83894795。带默认参数的构造函数 
    		void print();
    		friend Complex operator+(Complex& a,Complex& b);	//声明运算符,重载函数
    		friend Complex operator-(Complex& a,Complex& b);	//声明运算符,重载函数
    		friend Complex operator*(Complex& a,Complex& b);	//声明运算符,重载函数
    		friend Complex operator/(Complex& a,Complex& b);	//声明运算符,重载函数
    };
    Complex::Complex(double r,double i){	//构造函数 
    	real=r;
    	imag=i;
    } 
    Complex operator+(Complex& a,Complex& b){
    	Complex temp;
    	temp.real=a.real+b.real;
    	temp.imag=a.imag+b.imag;
    	return temp;
    }
    Complex operator-(Complex& a,Complex& b){
    	Complex temp;
    	temp.real=a.real-b.real;
    	temp.imag=a.imag-b.imag;
    	return temp;
    }
    Complex operator*(Complex& a,Complex& b){
    	Complex temp;
    	temp.real=a.real*b.real-a.imag*b.imag;
    	temp.imag=a.real*b.imag+a.imag*b.real;
    	return temp; 
    }
    Complex operator/(Complex& a,Complex& b){
    	Complex temp;
    	double t;
    	t=1/(b.real*b.real+a.imag*b.imag);
    	temp.real=(a.real*b.real+a.imag*b.imag)*t;
    	temp.imag=(b.real*a.imag-a.real*b.imag)*t;
    	return temp;
    }
    void Complex::print(){	//显示输出复数 
    	cout<<real;
    	if(imag>0)
    		cout<<"+";
    	if(imag!=0)
    		cout<<imag<<'i'<<endl;
    }
    int main(){
    	Complex A1(2.3,1),A2(3.6,2.8),A3,A4,A5,A6;
    	A3=A1+A2;
    	A4=A1-A2;
    	A5=A1*A2;
    	A6=A1/A2;
    	A3.print();
    	A4.print();
    	A5.print();
    	A6.print();
    	return 0;
    }
    

    执行结果如下:
    在这里插入图片描述
      一般而言,如果在类 X 中采用友元函数重载双目运算符 @ ,而 aa 和 bb 是类 X 的两个对象,则以下两种函数调用方法是等价的:

    aa@bb;		//隐式调用
    operator@(aa,bb);		//显示调用
    

    说明:
    (1)有时,在函数返回的时候,可直接用类的构造函数来生成一个临时对象,而不对该对象进行命名,如可将例 1 中的重载运算符“ + ”的友元运算符重载函数

    Complex operator+(Complex& a,Complex& b){
    	Complex temp;
    	temp.real=a.real+b.real;
    	temp.imag=a.imag+b.imag;
    	return temp;
    }
    

    改为:

    Complex operator+(Complex& a,Complex& b){
    	return Complex(a.real+b.real,a.imag+b.imag);
    }
    

    其中 return 语句中的

    Complex(a.real+b.real,a.imag+b.imag);
    

    是建立的一个临时对象,它没有对象名,是一个无名对象。在建立临时对象过程中调用构造函数。return 语句将此临时对象作为函数返回值。
    这种方法的执行的效率比较高,但是前一种方法的可读性比较好。
    (2)有的 C++ 系统(如 Visual C++ 6.0)没有完全实现 C++ 标准,它所提供的不带后缀的“ .h ”的头文件不支持友元运算符重载函数,在 Visual C++ 6.0 中编译会出错,这时可采用带后缀的“ .h ”头文件。将程序中的

    #include<iostream>
    using namespace std;
    

    改为

    #include<iostream.h>
    

    即可顺利运行。

    3、单目运算符重载

      单目运算符只有一个操作数,如 -a、&b、!c、++p等。

    例 2:用友元函数重载单目运算符“-”

    #include<iostream>
    using namespace std;
    class Coord{
    	private:
    		int x,y;
    	public:
    		Coord(int x1=0,int y1=0){
    			x=x1;
    			y=y1;
    		}
    		friend Coord operator-(Coord &obj);	//声明为单目运算符 "-" 重载函数
    		void print(); 
    };
    Coord operator-(Coord &obj){	//定义单目运算符 "-" 重载函数
    	obj.x=-obj.x;
    	obj.y=-obj.y;
    	return obj;
    }
    void Coord::print(){
    	cout<<"x="<<x<<"y="<<y<<endl;
    }
    int main(){
    	Coord ob1(50,60),ob2;
    	ob1.print();
    	ob2=-ob1;
    	ob2.print();
    	return 0;
    }
    

    输出结果:
    在这里插入图片描述

    例 3:用友元函数重载单目运算符“++”

    #include<iostream>
    using namespace std;
    class Coord{
    	private:
    		int x,y;
    	public:
    		Coord(int x1=0,int y1=0){
    			x=x1;
    			y=y1;
    		}
    		friend Coord operator++(Coord &obj);	//声明为单目运算符 "-" 重载函数
    		void print(); 
    };
    Coord operator++(Coord &obj){	//定义单目运算符 "-" 重载函数
    	++obj.x;
    	++obj.y;
    	return obj;
    }
    void Coord::print(){
    	cout<<"x="<<x<<";y="<<y<<endl;
    }
    int main(){
    	Coord ob1(50,60);
    	ob1.print();
    	++ob1;
    	ob1.print();
    	operator++(ob1);
    	ob1.print();
    	return 0;
    }
    

    在这里插入图片描述
    如果将友元运算符重载函数定义为以下格式:

    friend Coord operator++(Coord op){	
    	++obj.x;
    	++obj.y;
    	return obj;
    }
    

    在这里插入图片描述
      这个运行结果是错误的,原因是这个函数的形参是对象,是通过传值的方法传递参数的,函数体内对形参 obj 的所有修改都无法传到函数体外。即,实际上 operator++ 函数中 obj.x 和 obj.y 的增加并没有引起实参 ob1.x 和 ob1.y 的增加,因此造成了运行结果的错误。
      而形参是对象的引用时,是通过传址的方式传递参数的,函数形参 obj.x 和 obj.y 的改变将引起实参 ob1 的变化。

      一般而言,如果在类 X 中采用友元函数重载单目运算符 @ ,而 aa 是类 X 的两个对象,则以下两种函数调用方法是等价的:

    @aa;	//隐式调用
    operator@(aa);		//显式调用
    

    说明:
    (1)运算符重载函数 operator@ 可以返回任何类型,甚至可以是 void 类型,但通常返回的类型与它操作的类的类型相同,这样可以使重载运算符用在复杂的表达式中。
    (2)有的运算符不能定义为友元运算符重载函数,如赋值运算符 “=”,下标运算符“[ ]”、函数调用运算符“ () ”等。

    展开全文
  • operator-overloading-js, JS简单运算符重载库 目录Operator-Overloading-JS安装工具node MODULE ( NPM )浏览器( Bower )Sneak示例重载运算符设计考虑/非常重要/必须阅读。Definig使用运算符重载的上下文
  • 运算符重载python_Python运算符重载

    千次阅读 2020-07-14 05:44:52
    运算符重载pythonWelcome to the tutorial on Python Operator Overloading. As we have already learnt about Python Class, we are going to learn another interesting feature of object oriented python today....

    运算符重载python

    Welcome to the tutorial on Python Operator Overloading. As we have already learnt about Python Class, we are going to learn another interesting feature of object oriented python today.

    欢迎使用Python操作符重载教程。 正如我们已经了解Python Class一样 ,今天我们将学习面向对象python的另一个有趣的功能。

    Python运算符重载 (Python Operator Overloading)

    Python Operator overloading enables us to use mathematical, logical and bitwise operators on python objects just like any primitive data type.

    Python运算符重载使我们能够像对待任何原始数据类型一样,在python对象上使用数学,逻辑和按位运算符。

    For example, you can easily add two numbers 3 and 5 with + operator, i.e 3 + 5. And the result is 8.

    例如,您可以使用+运算符轻松地将两个数字3和5相加,即3 +5。结果为8。

    But what if you want to add two circles (object of a Circle class) and make a circle having twice the radius? Or what if you want to add two cartesian grid points to yield another point with the same ‘+’ operator? Python operator overloading allows you to perform operations just like those.

    但是,如果您想添加两个圆(Circle类的对象)并使半径为两倍的圆呢? 或者,如果您想添加两个笛卡尔网格点以使用相同的“ +”运算符产生另一个点,该怎么办? Python运算符重载使您可以像执行那些操作一样执行操作。

    Python运算符重载示例 (Python Operator Overloading Example)

    Now, let’s see an example of overloading mathematical operator.

    现在,让我们看一个重载数学运算符的例子。

    class GridPoint:  #Line: 1, Declaring a class
        def __init__(self, x, y):  
            self.x = x  
            self.y = y  #Line: 4
      
        def __add__(self, other):  # Equivalent of + operator
            return GridPoint(self.x + other.x, self.y + other.y)
      
        def __str__(self):  #Line: 12, returns the attributes when the object is printed
            string = str(self.x)  
            string = string + ", " + str(self.y)  
            return string  #Line: 12
      
    point1 = GridPoint(3, 5)  #Line: 14 Creating a grid point
    point2 = GridPoint(-1, 4)  #Line: 15, Creating another point
    point3 = point1 + point2  #Line: 16, Add two points using __add__() method
    print(point3)  #Line: 17, Print the attributes using __str__() method

    Lines 1 to 4 indicates the declaration of the class GridPoint and definition of constructor method. Let’s have a look at lines 6 and 7.

    第1至4行表示GridPoint类的声明和构造方法的定义。 让我们看一下第6行和第7行。

    def __add__(self, other):  # Equivalent of + operator
            return GridPoint(self.x + other.x, self.y + other.y)

    When we use ‘+’ operator as mathematical addition operation, the __add__() method is implicitly called.

    当我们使用'+'运算符作为数学加法运算时, __add__()方法被隐式调用。

    So, if we are to add two objects of the class GridPoint, we must re-define this method. So, here we need to create another instance of GridPoint class whose value of x is the summation of x in the two GridPoint instances around the ‘+’ operator and value of y is also the summation of y in the two GridPoint instances around the ‘+’ operator.

    因此,如果要添加GridPoint类的两个对象,则必须重新定义此方法。 因此,在这里我们需要创建另一个GridPoint类的实例,其x的值是在'+'运算符附近的两个GridPoint实例中x的总和,而y的值也是在''周围的两个GridPoint实例中y的总和。 +'运算符。

    Lines 9 to 12 defines the __str__() method which is called when we try to print the object. That’s also a built in method. But we are going to overload the method so that it prints in our specified format.

    第9至12行定义了__str__()方法,当我们尝试打印对象时会调用该方法。 这也是一个内置方法。 但是我们将重载该方法,以便它以指定的格式打印。

    Lines 14 and 15, we’ve created two objects of GridPoint, namely point1 and point2. Now, watch Line 16. Two instances of the class GridPoint class are added using ‘+’ operator and assigned as another instance of GridPoint. Python operator overloading helps you do this. So, don’t get surprised when the 17th line shows an output like below image.

    第14和15行,我们创建了GridPoint的两个对象,即point1和point2。 现在,看第16行。使用'+'运算符添加GridPoint类的两个实例,并将它们分配为GridPoint的另一个实例。 Python运算符重载可以帮助您做到这一点。 因此,当第17行显示如下图所示的输出时,请不要感到惊讶。

    数学运算符列表 (List of Mathematical Operators)

    Here is a list of operators which can be overloaded and used with python operator overloading in a similar way.

    这是可以重载的运算符列表,并且可以类似的方式与python运算符重载一起使用。

    OperatorDescriptionMethod
    +Addition__add__(self, other)
    Subtraction__sub__(self, other)
    *Multiplication__mul__(self, other)
    /True Division__truediv__(self, other)
    //Floor Division__floordiv__(self, other)
    %Remainder__mod__(self, other)
    **Power__pow__(self, other)
    &Bitwise AND__and__(self, other)
    |Bitwise OR__or__(self, other)
    ^Bitwise XOR__xor__(self, other)
    操作员 描述 方法
    + 加成 __add __(自己,其他)
    减法 __sub __(自己,其他)
    * 乘法 __mul __(其他)
    / 真师 __truediv __(自己,其他)
    // 楼层部 __floordiv __(自己,其他)
    __mod __(自己,其他)
    ** 功率 __pow __(自己,其他)
    按位与 __和__(自己,其他)
    | 按位或 __或__(自己,其他)
    ^ 按位异或 __xor __(自己,其他)

    在Python中重载关系运算符 (Overloading Relational Operators in Python)

    Relational operators are overloaded in a very similar way in python. But the difference is, those operators often return true/false instead of another instance of the object. Let’s work with an example.

    关系运算符在python中以非常相似的方式重载。 但是不同之处在于,这些运算符通常返回true / false,而不是对象的另一个实例。 让我们来看一个例子。

    class GridPoint:  
        def __init__(self, x, y):  
            self.x = x  
            self.y = y  
      
        def __gt__(self, other):  # Overloading the greater than operator
            return self.x > other.x  
    # Returns true if value of x in the left operand is greater than that in the right one. Returns false otherwise
      
        def __str__(self):  
            string = str(self.x)  
            string = string + ", " + str(self.y)  
            return string  
      
    point1 = GridPoint(3, 5)  
    point2 = GridPoint(-1, 4)  
    if point1 > point2:  # Compares with the overloaded __gt__() method
        print('point1 is greater than point2')  
    else:  
        print('point1 is not greater than point2')

    Look at line 6, where the ‘greater than’ operator has been loaded. The conventional ‘>’ operator returns true if the operand in the left side of it is greater than the right one. We are going to use this property to compare two instances of class GridPoint.

    查看第6行,已加载“大于”运算符。 如果左侧的操作数大于右侧的操作数,则常规的'>'运算符将返回true。 我们将使用此属性来比较GridPoint类的两个实例。

    Then in line 17, we are comparing the objects of the class GridPoint to obtain a boolean type value which will determine whether the first object has the greater value of ‘x’. In this case, the relational operator returns true as 3 is greater than -1. As a result the program prints ‘point1 is greater than point2’.

    然后在第17行中,我们比较GridPoint类的对象以获得布尔类型值,该值将确定第一个对象是否具有较大的'x'值。 在这种情况下,当3大于-1时,关系运算符返回true。 结果是程序打印“ point1大于point2”。

    python中的更多关系运算符 (More Relational Operators in python)

    Here is a list of relational operators that can be overloaded in the same way.

    这是可以以相同方式重载的关系运算符的列表。

    OperatorDescriptionMethod
    >Greater than__gt__(self, other)
    >=Greater than or equal to__ge__(self, other)
    <Less than__lt__(self, other)
    <=Less than or equal to__le__(self, other)
    ==Equal to__eq__(self, other)
    !=Not equal to__ne__(self, other)
    操作员 描述 方法
    > 比...更棒 __gt __(自己,其他)
    > = 大于或等于 __ge __(自己,其他)
    < 少于 __lt __(自己,其他)
    <= 小于或等于 __le __(自己,其他)
    == 等于 __eq __(自己,其他)
    != 不等于 __ne __(自己,其他)

    That’s all for today about operator overloading in python. Hope to be with you with more tutorials very soon.
    Happy Coding!

    今天就有关python中的运算符重载。 希望很快就会有更多教程与您同在。
    编码愉快!

    Reference: Python.org Docs

    参考: Python.org文档

    翻译自: https://www.journaldev.com/14637/python-operator-overloading

    运算符重载python

    展开全文
  • C++运算符重载和继承

    2019-01-17 14:07:59
    C++编程思想的第十四章,代码,本人亲测通过后才上传的. 技术是不断锤炼出来的,每天进步一点点,最后终将成就你所有
  • 第10章 运算符重载;10.1什么是运算符重载;10.2 运算符重载的方法;例重载+运算符应用于复数类;方法2成员函数 class complex{ Public: complex operator +(complex &c2; }; complex complex:operator +(complex &c2) {...
  • 目录: 运算符重载——算术运算符重载 运算符重载——递增运算符重载 运算符重载——左移运算符重载 运算符重载——赋值运算符重载 应用场景: Person p1(100);// p1有100元 Person p2(50);// p2有50元 //现在我们要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 159,674
精华内容 63,869
关键字:

运算符重载