013-运算符重载

《C++文章汇总》
上一篇介绍了《012-对象类型的参数和返回值、友元函数、内部类和局部类》,本文介绍运算符重载。

1.运算符重载

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

class Point {
    friend Point operator+(Point,Point);
private:
    int m_x;
    int m_y;
public:
    int getX(){return m_x;};
    int getY(){return m_y;};
    Point(int x,int y):m_x(x),m_y(y){}
    void display(){
        cout << "(" << m_x <<", " << m_y << ")" << endl;
    }
};
Point operator+(Point p1,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;
    p3.display();
    getchar();
    return 0;
}
//输出
(30, 50)

若三个点一起相加,则operator+方法会进入两次,p1+p2+p3 <==> operator+(operator+(p1,p2),p3)

Point operator+(Point p1,Point p2){
    cout << "Point operator+(Point p1,Point p2)" << endl;
    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(30,40);
    Point p4 = p1+p2+p3;
    p4.display();
    getchar();
    return 0;
}
//输出
Point operator+(Point p1,Point p2)
Point operator+(Point p1,Point p2)
(60, 90)

2.运算符重载完善

运算符重载中写成引用变量,参数前加const修饰,既能接收const修饰的对象,又能接收非const修饰的对象

class Point {
    friend Point operator+(const Point &,const Point &);
private:
    int m_x;
    int m_y;
public:
    int getX(){return m_x;};
    int getY(){return m_y;};
    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){
    cout << "Point operator+(Point p1,Point p2)" << endl;
    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(30,40);
    Point p4 = p1+p2+p3;
    //Point p5 = operator+(operator+(p1,p2),p3);
    p4.display();
    getchar();
    return 0;
}
//输出
Point operator+(Point p1,Point p2)
Point operator+(Point p1,Point p2)
(60, 90)

拷贝构造函数为什么要这样写Point(const Point &p1)?
假设这样写Point(Point p1)会有什么后果?
Point p(10,20);
Point p0 = p;会调用拷贝构造函数,进入到参数Point p1 = p;此时又会调用拷贝构造函数,会进入死循环
const修饰的参数能接受const和非const参数
◼ 全局函数、成员函数都支持运算符重载

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

int main(){
    Point p1(10,20);
    Point p2(20,30);
    Point p3(30,40);
    Point p4 = p1+p2+p3;
    p4.display();
    getchar();
    return 0;
}
//输出
(60, 90)

I.实现减号运算符重载

Point operator-(const Point &p){
    return Point(m_x-p.m_x, m_y-p.m_y);
};
int main(){
    Point p1(10,20);
    Point p2(20,30);
    Point p3(30,40);
    Point p5 = p3 - p2;
    p5.display();
    getchar();
    return 0;
}
//输出
(60, 90)
(10, 10)

会存在以下问题p1+p2 = Point(10,20),赋值没有意义,但编译器没有报错,在运算符函数重载前加const修饰符,const修饰的函数返回值无法修改,此时编译器会报错
加const前

图片.png

加const后
图片.png

此时p4 = p1 + p2 + p3又会报错,相当于p1.operator+(p2).operator+(p3),但p1.operator+(p2)返回值是一个const修饰的对象,是无法直接调用非const修饰的成员函数的,此时需要将重载函数变为const修饰的重载函数
图片.png

class Point {
private:
    int m_x;
    int m_y;
public:
    int getX(){return m_x;};
    int getY(){return m_y;};
    Point(int x,int y):m_x(x),m_y(y){}
    void display(){
        cout << "(" << m_x <<", " << m_y << ")" << endl;
    }
    const Point operator+(const Point &p) const{
        return Point(m_x+p.m_x, m_y+p.m_y);
    };
    const Point operator-(const Point &p) const{
        return Point(m_x-p.m_x, m_y-p.m_y);
    };
};

int main(){
    Point p1(10,20);
    Point p2(20,30);
    Point p3(30,40);
    Point p4 = p1+p2+p3;//相当于p1.operator+(p2).operator+(p3),但p1.operator+(p2)返回值是一个const修饰的对象,是无法直接调用非const修饰的成员函数的,此时需要将重载函数变为const修饰的重载函数
//    p1.operator+(p2).operator+(p3);
    p4.display();
    //p1+p2 = Point(10,20);//此处赋值没有任何意义,运算符重载函数前加const修饰
    Point p5 = p3 - p2;
    p5.display();
    getchar();
    return 0;
}

II.重载运算符加减混合运算

const Point operator+(const Point &p) const{
    return Point(m_x+p.m_x, m_y+p.m_y);
};
const Point operator-(const Point &p) const{
    return Point(m_x-p.m_x, m_y-p.m_y);
};
int main(){
    Point p1(10,20);
    Point p2(20,30);
    Point p3(30,40);
    Point p4 = p1+p2-p3;
    p4.display();
    getchar();
    return 0;
}
//输出
(0, 10)

III.加等于,使用引用&不会产生中间对象

Point &operator+=(const Point &p){
    m_x += p.m_x;
    m_y += p.m_y;
    return *this;
};
int main(){
    Point p1(10,20);
    Point p2(20,30);
    (p1 += p2) = Point(50,60);
    p1.display();
}
//输出
(50, 60)

IV.等于等于,加const后常量对象也能比较

bool operator==(const Point &p) const{
    if(m_x == p.m_x && m_y == p.m_y)
        return 1;
    else
        return 0;
};
int main(){
    Point p1(10,20);
    Point p2(20,30);
    cout << (p1 == p2) << endl;
}
//输出
0

V.不等于,加const后常量对象也能比较

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

VI.负号

const Point operator-() const{
    return Point(-m_x,-m_y);
}
int main(){
    const Point p1(10,20);
    Point p2(20,30);
    Point p3 = -(-p1);
    p1.display();
    p3.display();
}
//输出
(10, 20)
(10, 20)

VII.++,--

//前置++
Point &operator++(){
    m_x++;
    m_y++;
    return *this;
}
//后置++
const Point operator++(int){
    //返回调用者之前的值
    Point old(m_x,m_y);
    m_x++;
    m_y++;
    return old;
}
int main(){
    Point p1(10,20);
    Point p2 = p1++ + Point(30,40);
    p1.display();
    p2.display();
}
//输出
(11, 21)
(40, 60)

VIII.<<,cout相当于Java的toString方法,OC的description方法

class Point {
    friend void operator<<(ostream &,const Point &);
    int m_x;
    int m_y;
}
void operator<<(ostream &cout,const Point &p){
    cout << "(" << p.m_x << "," << p.m_y << ")" << endl;
}
int main(){
    Point p1(10,20);
    Point p2 = p1++ + Point(30,40);
    cout << p1;
    cout << p2;
}
//输出
(11,21)
(40,60)

连续打印

class Point {
    friend ostream &operator<<(ostream &,const Point &);
}
ostream &operator<<(ostream &cout,const Point &p){
    cout << "(" << p.m_x << "," << p.m_y << ")";
    return cout;
}
int main(){
    Point p1(10,20);
    Point p2 = p1++ + Point(30,40);
    cout << p1 << p2 << 1 << 2 << 3;
}
//输出
(11,21)(40,60)123

系统自带的打印字符串函数要求左边cout修饰为非const,故重载打印运算符<<返回值前面不能加const,若加了const则连续打印cout << p1 << p2,cout<

//系统自带打印字符串要求左边cout修饰为非const
void operator<<(ostream &cout,const char *string){
    
}

VIV.>>,cin从键盘接收赋值

class Point {
    friend istream &operator>>(istream &,Point &);
}
istream &operator>>(istream &cin,Point &p){
    cin >> p.m_x;
    cin >> p.m_y;
    return cin;
}
int main(){
    Point p1(10,20);
    Point p2 = p1++ + Point(30,40);
    cin >> p1 >> p2;
    cout << p1 << p2 << endl;
    getchar();
    getchar();
    return 0;
}
//输出
11 22 55 66
(11,22)(55,66)

VV.=说明,一般对象的符号如=重载后可以赋值,但cout重载后不能赋值

class Person{
    int m_age;
    int m_height;
public:
    Person(int age,int height):m_age(age),m_height(height){
        
    }
    Person &operator=(const Person &person){
        m_age = person.m_age;
        return *this;;
    }
    void display(){
        cout << "(" << m_age << "," << m_height << ")" << endl;
    }
};

int main(){
    Person p1(10,180);
    Person p2(15,175);
    (p1 = p2) = Person(50,20);
    p1.display();
    getchar();
    return 0;
}
//输出
(50,180)

禁止p1 = p2直接赋值,将重载函数私有化


图片.png

同理,为什么cout重载后不能赋值,因为ostream库将cout重载函数私有化了


图片.png

3.单例模式完善

*rocket1 = *rocket2;//同一个对象,赋值操作没有意义,要禁止掉,将赋值操作符=私有化


图片.png

图片.png

仍旧可以调用拷贝构造函数,没有意义,要禁止掉


图片.png

图片.png

4.赋值运算符注意点

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

class Person{
public:
    int m_age;
    Person &operator=(const Person &person){
        m_age = person.m_age;
        return *this;
    }
};
class Student:public Person{
public:
    int m_score;
    Student &operator=(const Student &student){
        Person::operator=(student);
        m_score = student.m_score;
        return *this;
    }
};
int main(){
    Student stu1;
    stu1.m_age = 10;
    stu1.m_score = 100;
    
    Student stu2;
    stu2 = stu1;
    
    getchar();
    return 0;
}

5.仿函数(函数对象)

◼ 仿函数:将一个对象当作一个函数一样来使用
◼ 对比普通函数,它作为对象可以保存状态

int sum(int a,int b){
    return a+b;
}
class Sum {
    int m_age;
public:
    int operator()(int a,int b){
        cout << "operator()(int a,int b)" << endl;
        return a+b;
    }
    void func(){
        m_age = 10;
    }
};

int main(){
    Sum sum;
    sum.func();
    cout << sum(10,20) << endl;//相当于重载运算符()
    sum(20,30);
//    cout << sum.operator()(10,20) << endl;
    getchar();
    return 0;
}
//输出
operator()(int a,int b)
30
operator()(int a,int b)

6.运算符重载注意点

◼ 有些运算符不可以被重载,比如

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

你可能感兴趣的:(013-运算符重载)