C++知识点总结(其他语法1-运算符重载)

运算符重载1(operator overload)

运算符重载(操作符重载) : 可以为运算符增加一些新的功能.

#include <iostream>
using namespace std;

class Point {
    friend Point operator+(const Point &, const Point &);
private:
    int m_x;
    int m_y;
public:
    Point(int x, int y) : m_x(x), m_y(y) {}
    void display() {
        cout << "(" << m_x << ", " << m_y << ")" << endl;
    }
};

Point operator+(const Point &p1, const Point &p2) {
    return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}


int main()
{
    Point p1(10, 20);
    Point p2(20, 30);
    Point p3 = p1 + p2;
    // 本质是Point p3 = operator+(p1, p2);
    p3.display();
    Point p4 = p1 + p2 + p3;
    // 本质是调用了两次operator+, 第一次的结果又作为参数传进去
    p4.display();

    return 0;
}

运算符重载2-完善

(1)为什么拷贝构造函数的参数是const Point &point 形式?
1.必须是引用, 否则报错.

class Point {
public:
    Point(Point point) {
        this->m_x = point.m_x;
        this->m_y = point.m_y;
    }
};
int main() {
    Point p1(10, 20);
    Point p2 = p1;
    // 利用已经存在的对象创建新的对象, 
    // 调用拷贝构造函数, 相当于Point point = p1 
    // 又是利用已经存在的对象创建新的的对象, 又
    // 要调用拷贝构造函数, 就又相当于
    // Point point = p1, 从而陷入死循环.所以
    // 必须是引用. 一旦变成引用就是地址值的赋值
    // 就不存在创建对象了.
}

2.最好是const, 从而保证可以接受const对象和非const对象, 使接受参数的范围更大.

运算符重载3-更多运算符

(1)一般来说跟某个对象相关的代码, 干脆重载为成员函数.
一旦变为成员函数, 就变成了

class Point {
public:
    Point operator+(const Point &point) {
        return Point(this->m_x + point.m_x, this->m_y + point.m_y);
    // 只接受一个参数, 因为一旦变为成员函数, 就
    // 通过对象去调用了, 比如p1 + p2 本质就变成了
    // p1.operator+(p2);就意味着会把p1的地址传给
    // operator+函数, this就指向外面的p1.
    // 对比原来来说简单了, 首先只需要接受一个参数
    // 其次这个功能本来就是跟Point相关的, 所以
    // 直接写在Point类里面.而且一旦写成成员函数就
    // 意味着我们自己的私有成员变量可以访问.
    // 不需要友元函数.
        
    }
};

(2)存在的问题:
在重载运算符的时候, 最好要保留运算符原有的特性, 比如:
因为int a = 10, b = 20; (a + b) 这个表达式不允许被赋值(因为a + b 返回的是常量并没有被存储在内存中), 所以(p1 + p2)也应该不能赋值, 因为p1 + p2 返回的是一个临时对象, 马上就要销毁了, 所以不应该可以赋值, 所以要在operator+函数的返回值前加const声明为返回常量对象, 不允许被赋值, 但这样的话, 又会导致新的问题, 在p1 + p2 + p3 时, 相当于p1.operator+(p2).operator+(p3) 因为常量对象是不能调用非常量函数的(因为在非常量函数里面按语法的话可以修改里面对象的值, 而常量对象又不允许修改, 矛盾), 所以也应该把operator+函数声明为常量函数.最终为
const Point operator+(const Point &point) const {

}
const Point operator-(const Point &point) const {

}
(3)+=运算符
因为int a = 10, b = 20; (a += b) = 10; 是可以被赋值的, 因为a += b是a + b后的结果又赋值给a, 而a可以被赋值, 所以(p1 += p2)也应该可以赋值
所以应该返回p1对象, 而返回对象(把对象作为返回值)又会导致产生中间对象的问题, 所以应该返回引用.

class Point {
public:
    Point &operator+=(const Point &point) {
        this->m_x += point.m_x;
        this->m_y += point.m_y;
        return *this;
    }
};

(4)==运算符
int a = 10, b = 20;
if (a == b) 原来是相等返回1, 不相等返回0, 所以是bool类型

bool operator==(const Point &point) const {
    // 要保证常量对象可以和非常量对象比较
/*  if ((m_x == point.m_x) && (m_y == point.m_y)) {
        return 1;
    }
    else {
        return 0;
    } */
    return (m_x == point.m_x) && (m_y == point.m_y); 
}

(5)!=运算符

bool operator!=(const Point &point) const {
    return (m_x != point.m_x) || (m_y != point.m_y);
}

(6)-运算符(符号)
首先-p1; 不需要传参, p1.operator-();
而且
Point p1(10, 20);
Point p3 = -p1; 时p1并没有改变, 所以应该返回一个临时的
其次int a = 10, b; (-a) = 10;不允许, 因为a并没有被赋值, 返回的是一个临时的值, 所以-p1不允许被赋值, 所以返回const

const Point operator-() const {
    // 因为(-(-p1))时相当于 p1.operator-().operator-(), 
    // 而返回的const对象不能调用非const函数, 所以函数也要声明为const
    return Point(-m_x, -m_y);
}

(7)++, --运算符
1.为了区分前置++和后置++规定
void operator++() {
// 是前置++
}
void operator++(int) {
// 是后置++
}
2.前置++可以被赋值, 后置++不可以被赋值
因为int a = 10;
int b = ++a + 5;
相当于a += 1; int b = a + 5; 也就是说++a是先让a += 1, 再把最新的a返回.所以可以被赋值.
而后置++ int a = 10;
int c = a++ + 5; 会先将a之前的值放到a++的地方等价于int c = 10 + 5; 在 a += 1; 所以a++的话并不会返回a.
3.最终实现:
前置:

Point &operator++() {
    m_x++;
    m_y++;
    return *this;
}

后置:
而Point p2 = p1++ + Point(30, 40);
应该可以, 所以p1不能返回void而应该返回p1之前的值
所以

const Point operator++(int) {
    Point old(this->m_x, this->m_y);
    this->m_x++;
    this->m_y++;
    return old;
}

(8)<<运算符
1.因为cout << p1 << endl;
所以<<重载函数不能是成员函数, 一旦是成员函数就必须通过Point对象调用
其实cout也是对象, 它的类是ostream
ostream -> output stream
在头文件iostream中
2.因为cout << 1 << 2 << endl;
所以调用完函数后应该返回cout
3.因为(cout << 1) = cout; 返回cout后不允许被赋值, 所以const
但是返回值是const之后, 会导致cout << p1 << p2 出问题, 所以参数cout也应该const, 但是因为系统自带的const的定义是非const的, 所以参数是const会导致原来的const找不到.所以放弃, 但是发现其实不能被赋值, 因为在iostream头文件里, 将=赋值运算符重载到了private导致外面不能访问=
所以返回值不能是const, cout参数不能是const

ostream &operator<<(ostream &cout, const Point &point) {
    cout << "(" << point.m_x << ", " << point.m_y;
    return cout;
}

(9)cin运算符
input stream -> istream
1.要去掉后面参数的const, 因为是要输入东西去改变point对象.

istream &operator>>(istream &cin, Point &point) {
    cin >> point.m_x;
    cin >> point.m_y;
    return cin;
}

运算符重载-调用父类的运算符重载函数

class Person {
    int m_age;
public:
    Person &operator=(const Person &person) {
        this->m_age = person.m_age;
    }
};

class Student : public Person {
    int m_score;
public:
    Student &operator=(const Student &student) {
        Person::operator=(student);
        this->m_score = student.m_score;
    }
};
int main() {
    Student stu;
    stu.m_score = 60;
    stu.m_age = 10;
    Student stu1;
    stu1 = stu;
}

运算符重载-仿函数(函数对象)

//int sum(int a, int b) {
//    return a + b;
//}
class Sum {
    int m_age;
public:
    int operator()(int a, int b) {

        return a + b;
    }
};

int main() {
    Sun sum;
    cout << sum(10, 20) << endl;
    // cout << sum.operator()(10, 20) << endl;
    sum(20, 30);
}

仿函数和普通函数的区别是仿函数是成员函数, 意味着可以访问类里面的所有成员变量.

运算符重载注意点

1.有些运算符不可以被重载, 比如
(1)对象成员访问运算符: .
(2)域运算符: ::
(3)三目运算符: ?:
(4)sizeof
2.有些运算符只能重载为成员函数, 比如
(1)赋值运算符: =
(2)下标运算符: []
(3)函数运算符: ()
(4)指针访问成员: ->


其他C++系列文章:

C++知识点总结(基础语法1-函数重载, 默认参数)
C++知识点总结(基础语法2-内联函数, const, 引用)
C++知识点总结(面向对象1-类和对象, this指针, 内存布局)
C++知识点总结(面向对象2-构造函数, 初始化列表)

C++知识点总结(面向对象3-多态)

C++知识点总结(面向对象4-多继承, 静态成员static)
C++知识点总结(面向对象5-const成员, 拷贝构造函数)
C++知识点总结(面向对象6-隐式构造, 友元, 内部类, 局部类)
C++知识点总结(其他语法1-运算符重载)
C++知识点总结(其他语法2-模板, 类型转换, C++11新特性)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章