LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

C++面向对象程序设计笔记:第四章 运算符重载

2022/9/13 C++入门

C++面向对象程序设计笔记:第四章 运算符重载

重载为成员函数时,参数个数为运算符目数减一。
重载为普通函数时,参数个数为运算符目数。

class Complex {
public:
    double real, imag;
    Complex(double r = 0.0, double i = 0.0) : read(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);
}
/*
c = a + b; // 等价于c = operator+(a, b);
c = a - b; // 等价于c = a.operator(b);
*/

赋值运算符重载

只能重载为成员函数。

class String {
private:
    char* str;
public:
    String ():str(new char[1]) {str[0] = 0;}
    ~String() {delete[] str};
    const char* c_str() {return str;}
    String& operator=(const char* s);
};
String& String::operator=(const char* s) {
    delete[] str;
    str = new char[strlen(s) + 1]; // +1为了放下/0
    strcpy(str, s);
    return *this;
}
int main() {
    String s;
    s = "Good Luck,";   // 等价于s.operator=("Good Luck,");
    // String s2 = "hello!"; // 出错,会执行构造函数,而不是赋值
    return 0;
}

浅拷贝和深拷贝

class String;
int main() {
    String S1, S2;
    S1 = "this";
    S2 = "that";
    S1 = S2; // S1.str指针指向S2.str指向的位置
    return 0;
}

如果不定义自己的赋值运算符,那么S1=S2实际上导致S1.str和S2.str指向同一个位置。
如果S1对象消亡,析构函数将释放S1.str指向的空间,则S2消亡时还要再释放一次,会出错。
另外,如果执行S1=”other”;会到导致S2.str指向的地方被delete掉。

因此,需要在class String里面添加成员函数

String& operator=(const String& s) {
    delete[] str;
    str = new char[strlen(s.str) + 1];
    strcpy(str, s.str);
    return *this.
}

如果执行s = s;会出错,需要做出修改。

String& operator=(const String& s) {
    if(this == &s) 
        return *this;
    delete[] str;
    str = new char[strlen(s.str) + 1];
    strcpy(str, s.str);
    return *this.
}

为什么”operator=”返回值的类型是”String&”?

为了保留运算符原有的特性不变。
例如:
a=b=c; 其中(b=c)的返回值是b,相当于b=c;a=b;。
(a=b)=c; 相当于a=b;a=c;

为String类编写复制构造函数的时候,会面临和 = 同样的问题,用同样的方法处理。

String(String& s) {
    str = new char[strlen(s.str) + 1];
    strcpy(str, s.str);
}

运算符重载为友元函数

使用情况:
一般情况下,将运算符重载为类的成员函数,是较好的选择;
但是当重载为成员函数不能满足使用要求,重载为普通函数又不能访问类的私有对象,就需要将运算符重载为友元。

class Complex {
    double real, imag;
public:
    Complex(double r, double imag):real(r), iamg(i){ }
    Complex operator+(double r);
};
Complex Complex::operator+(double r) {
    return Complex(real + r, imag);
}
int main(){
    Complex c;
    c = c + 5;
    c = 5 + c; // 编译错误
    return 0;
}

需要对上面代码做出修改:

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

流插入运算符的重载

流插入运算符其实是将”左移/右移”操作进行了重载。

cout是在iostream中定义的,ostream类的对象。
“<<”能用在cout上是因为iostream里对”<<”进行了重载。

实现cout<<5; 等价于cout.operator<<(5);

void ostream::operator<<(int n) {
    .. // 输出n的代码
    return;
}

实现cout<<5<<”this”; 等价于cout.operator<<(5).operator<<(“this”);

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

不能为ostream添加成员函数,可以重载为全局函数。

ostream& operator<<(ostream& o, const CStudent& s) {
    o << s.nAge;
    return o;
}

重载类型转换运算符

class Compelx {
    double real, iamg;
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 << "\n"; // 输出1.2(显式转换)
    double n = 2 + c; // 等价于double n = 2 + c.operator double();(隐式转换)
    cout << n;
}

自增、自减运算符

自增运算符++、自减运算符–有前置和后置之分,为了区分所重载的是前置运算符还是后置运算符,C++规定:

  • 前置运算符作为一元运算符重载:
    重载为成员函数:
    T& operator++();
    T& operator–();
    重载为全局函数:
    T1& operator++(T2);
    T1& operator–(T2);

  • 后置运算符作为二元运算符重载,多写一个没用的参数:
    重载为成员函数:
    T& operator++(int);
    T& operator–(int);
    重载为全局函数:
    T1& operator++(T2, int);
    T1& operator–(T2, int);

注:在没有后置运算符重载而有前置重载的情况下,在vs中obj++也调用前置重载,而dev中会报错。

注:(a++)=1;不会使得a=1,因为(a++)返回的是一个临时的对象,而不是a的引用。
这就是为什么(++i)比(i++)快!

注意事项:

  • 运算符重载不改变运算符的优先级。
  • “.”, “.*”, “::”, “?:”, “sizeof”不允许被重载。
  • 重载运算符()、[]、->或者赋值运算符=时,运算符重载必须声明为类的成员函数。

课后习题

014:MyString

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MyString {
    char * p;
public:
    MyString(const char * s) {
        if( s) {
            p = new char[strlen(s) + 1];
            strcpy(p,s);
        }
        else
            p = NULL;

    }
    ~MyString() { if(p) delete [] p; }
void Copy(char* s) {
        if(p) delete[] p;
        if( s) {
            p = new char[strlen(s) + 1];
            strcpy(p,s);
        }
        else
            p = NULL;
    }
    friend ostream& operator<< (ostream &o, const MyString& s) {
        if(s.p)
            o << s.p;
        return o;
    }
    MyString& operator=(const char* s) {
        if(p) delete[] p;
        if( s) {
            p = new char[strlen(s) + 1];
            strcpy(p,s);
        }
        else
            p = NULL;
        return *this;
    }
    MyString& operator= (const MyString& str) {
        if(p) delete[] p;
        char* s = str.p;
        if( s) {
            p = new char[strlen(s) + 1];
            strcpy(p,s);
        }
        else
            p = NULL;
        return *this;
    }
    MyString (const MyString& str) {
        if(p) delete[] p;
        char* s = str.p;
        if( s) {
            p = new char[strlen(s) + 1];
            strcpy(p,s);
        }
        else
            p = NULL;
    }
};
int main()
{
    char w1[200],w2[100];
    while( cin >> w1 >> w2) {
        MyString s1(w1),s2 = s1;
        MyString s3(NULL);
        s3.Copy(w1);
        cout << s1 << "," << s2 << "," << s3 << endl;

        s2 = w2;
        s3 = s2;
        s1 = s3;
        cout << s1 << "," << s2 << "," << s3 << endl;
        
    }
}

015:看上去好坑的运算符重载

#include <iostream> 
using namespace std;
class MyInt 
{ 
    int nVal; 
    public: 
    MyInt( int n) { nVal = n ;}
MyInt& operator-(int n) {
        nVal -= n;
        return *this;
    }
    friend int Inc(MyInt& i) {
        return i.nVal + 1;
    }
}; 
int Inc(int n) {
    return n + 1;
}
int main () { 
    int n;
    while(cin >>n) {
        MyInt objInt(n); 
        objInt-2-1-3; 
        cout << Inc(objInt);
        cout <<","; 
        objInt-2-1; 
        cout << Inc(objInt) << endl;
    }
    return 0;
}

016:惊呆!Point竟然能这样输入输出

#include <iostream> 
using namespace std;
class Point { 
    private: 
        int x; 
        int y; 
    public: 
        Point() { };
friend istream& operator>>(istream& in, Point& p) {
        in >> p.x >> p.y;
        return in;
    }
    friend ostream& operator<<(ostream& out, Point& p) {
        out << p.x << "," << p.y;
        return out;
    }
}; 
int main() 
{ 
     Point p;
     while(cin >> p) {
         cout << p << endl;
     }
    return 0;
}

017:二维数组类

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

class Array2 {
    int** nums;
public:
    Array2():nums(NULL) { }
    Array2(const int n, const int m) {
        nums = new int*[n];
        for(int i = 0; i < n; i++) 
            nums[i] = new int[m];
    }
    int& operator ()(int i, int j) {
        return nums[i][j];
    }
    int* operator [](int pos) {
        return nums[pos];
    }
};

int main() {
    Array2 a(3,4);
    int i,j;
    for(  i = 0;i < 3; ++i )
        for(  j = 0; j < 4; j ++ )
            a[i][j] = i * 4 + j;
    for(  i = 0;i < 3; ++i ) {
        for(  j = 0; j < 4; j ++ ) {
            cout << a(i,j) << ",";
        }
        cout << endl;
    }
    cout << "next" << endl;
    Array2 b;     b = a;
    for(  i = 0;i < 3; ++i ) {
        for(  j = 0; j < 4; j ++ ) {
            cout << b[i][j] << ",";
        }
        cout << endl;
    }
    return 0;
}

018:别叫,这个大整数已经很简化了!

#include <iostream> 
#include <cstring> 
#include <cstdlib> 
#include <cstdio> 
using namespace std;
const int MAX = 110; 
class CHugeInt {
int num[210];
public:
    CHugeInt(char* s) {
        memset(num, 0, sizeof num);
        int n = strlen(s);
        for(int i = 0; i < n; i++) {
            num[n - i - 1] = s[i] - '0';
        }
    }
    CHugeInt(int s_int) {
        memset(num, 0, sizeof num);
        string s = to_string(s_int);
        int n = s.length();
        for(int i = 0; i < n; i++) {
            num[n - i - 1] = s[i] - '0';
        }
    }
    CHugeInt(const CHugeInt& a) {
        for(int i = 0; i < 210; i++) 
            num[i] = a.num[i];
    }
    CHugeInt operator+(const CHugeInt& a) {
        CHugeInt b(0);
        int tmp = 0;
        for(int i = 0; i < 210; i++) {
            int cur = num[i] + a.num[i] + tmp;
            b.num[i] = cur % 10;
            tmp = cur / 10;
        }
        return b;
    }
    CHugeInt& operator++() {
        CHugeInt a(1);
        int tmp = 0;
        for(int i = 0; i < 210; i++) {
            int cur = num[i] + a.num[i] + tmp;
            num[i] = cur % 10;
            tmp = cur / 10;
        }
        return *this;
    }
    CHugeInt operator++(int) {
        CHugeInt res(*this);
        CHugeInt a(1);
        int tmp = 0;
        for(int i = 0; i < 210; i++) {
            int cur = num[i] + a.num[i] + tmp;
            num[i] = cur % 10;
            tmp = cur / 10;
        }
        return res;
    }
    friend CHugeInt& operator+=(CHugeInt& a, int b_int) {
        CHugeInt b(b_int);
        a = a + b;
        return a;
    }
    friend CHugeInt operator+(CHugeInt& a, int b_int) {
        CHugeInt b(b_int);
        b = a + b;
        return b;
    }
    friend CHugeInt operator+(int b_int, CHugeInt& a) {
        CHugeInt b(b_int);
        b = a + b;
        return b;
    }
    friend ostream& operator<<(ostream& out, CHugeInt a) {
        int len = 209;
        while(len > 0 && a.num[len] == 0) {
            if(a.num[len] != 0) break;
            len--;
        }
        len++;
        for(int i = 0; i < len; i++) {
            out << a.num[len - 1 - i];
        }
        return out;
    }
};
int  main() 
{ 
    char s[210];
    int n;

    while (cin >> s >> n) {
        CHugeInt a(s);
        CHugeInt b(n);

        cout << a + b << endl;
        cout << n + a << endl;
        cout << a + n << endl;
        b += n;
        cout  << ++ b << endl;
        cout << b++ << endl;
        cout << b << endl;
    }
    return 0;
}
img_show