运算符重载

运算符重载

  • 实现自定义类型的运算
  • 对于内置数据类型的表达式的运算是不可以改变的 (1+1不能改成3)
  • 四个运算符不能重载, 八个点 ::, ., .*, :?

加号运算符重载

  • 成员函数重载加号运算符
class Person
{
public:
	Person operator+(Person& p)
	{
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}
	int m_A;
	int m_B;
}

void test01()
{
	Person p1;
	p1.m_A = 10;
	p2.m_B = 10;
	Person p2;
	p2.m_A = 20;
	p2.m_B = 20;
	Person p3 = p1 + p2;
	// 本质 : Person p3 = p1.operator+(p2);
}
  • 全局函数重载加号运算符
Person operator+(Person& p1, Person& p2)
{
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}

// 运算符重载, 也可以发生函数重载
Person operator+(Person& p, int a)
{
	Person temp;
	temp.m_A = p.m_A + a;
	temp.m_B = p.m_B + a;
	return temp;
}
void test01()
{
	Person p1;
	p1.m_A = 10;
	p2.m_B = 10;
	Person p2;
	p2.m_A = 20;
	p2.m_B = 20;
	Person p3 = p1 + p2;
	Person p4 = p1 + 4;
	// 本质 : Person p3 = operator+(p1, p2);
}

左移运算符

  • 输出自定义数据类型
  • 成员运算符重载左移运算符
    • 一般不使用成员函数重载<<运算符, 因为无法实现cout在左侧
class Person
{
public:
    Person(int a, int b): a(a), b(b) {}
    void operator<<(ostream& cout) // 名字可以改, 相应的函数里也需要改, 但是最后调用的时候仍然要用cout
    {
        cout<<a<<" "<<b<<endl;
    }
    int a;
    int b;
};
int main(){
    Person p(1,2);
//    p.operator<<(cout); 也可
    p<<cout;
    return 0;
}

1 2
Program ended with exit code: 0
  • 全局函数重载左移运算符
    • 不能通过更改形参的顺序, 来实现p<<cout, 会报错.
    • 必须第一个行参是ostream对象引用, 第二个是自定义数据类型
void operator<<(ostream& cout, Person& p)
{
    cout<<p.a<<" "<<p.b<<endl;
}

int main()
{
    operator<<(cout, p);
    cout<<p;
}
  • 上述这种情况, 不能进行链式输出 (cout<<p<<endl;) , 因为返回值是void, 想要实现链式输出, 只需要把返回对象改为ostream& (返回引用作为左值)
  • cout是一个内置的已经存在的ostream对象, 形参中可以用其自定义改名, 但是传递的实参必须是cout
ostream& operator<<(ostream& cout, Person& p)
{
    cout<<p.a<<" "<<p.b;
    return cout;
}

int main(){
    Person p(1,2);
    operator<<(cout, p)<<endl; // 前面返回的是cout
    cout<<p<<endl;
    return 0;
}

  • 一般成员设为私有属性, 重载左移运算符的全局函数作为友元函数
class Person
{
    friend ostream& operator<<(ostream& cout, Person& p);
public:
    Person(int a, int b): a(a), b(b) {}
    void operator<<(ostream& cout)
    {
        cout<<a<<" "<<b<<endl;
    }
private:
    int a;
    int b;
};

ostream& operator<<(ostream& cout, Person& p)
{
    cout<<p.a<<" "<<p.b;
    return cout;
}

int main(){
    Person p(1,2);
    operator<<(cout, p)<<endl;
    cout<<p<<endl;
    return 0;
}

递增运算符重载

  • 重载的条件是 作用域, 函数名, 参数列表, 和返回值无关.
  • 后置自增运算符 (可以连续前置递增, ++(++m)可以)
    • 取值, 复制, 原值++, 返回复制值
    • 本质调用 : int a; obj.operator++(a);
  • 前置自增运算符 (不可以连续后置递增, (m++)++ 报错)
    • 取值, 原值++, 返回原值
    • 本质调用 : obj.operator++();
class  MyInt
{
    friend ostream& operator<<(ostream& cout, MyInt m);
public:
    MyInt(int a) : a(a) {}
    MyInt(){}
    // 返回引用, 一直对一个数据进行递增操作, ++(++m), 如果返回值, 第二次递增就不是原来的值
    // 前置递增运算符重载
    MyInt& operator++()
    {
        a++;
        return *this;
    }
    
    // 后置递增运算符重载
    // int 仅是占位参数, 用来区分前置递增 和 后置递增
    // 后置递增必须返回值, 因为我们返回的是局部对象, 函数结束会被销毁, 引用非法
    MyInt operator++(int)
    {
        MyInt temp = *this;
        a++;
        return temp;
    }
private:
    int a;
};

ostream& operator<<(ostream& cout, MyInt m)
{
    cout<<m.a;
    return cout;
}

int main(){
    MyInt m(1);
    cout<<"m : "<<m<<endl;
    cout<<"++(++m) : "<<++(++m)<<endl;
    cout<<"m : "<<m<<endl;
    cout<<"m++ : "<<m++<<endl;
    cout<<"m : "<<m<<endl;
    return 0;
}

赋值运算符重载

  • C++编译器至少给一个类添加4个函数
    • 默认构造函数(无参, 函数体为空)
    • 默认析构函数(无参, 函数体为空)
    • 默认拷贝构造函数, 对属性进行值拷贝
    • 赋值运算符operator=, 对属性进行值拷贝
      • 如果类中有属性指向堆区, 赋值操作时会造成深浅拷贝问题 (堆区重复释放)

在这里插入图片描述

  • 赋值运算符是会返回一个值的, 因此有连续赋值操作a=b=c
  • 初始化的时候用赋值符号. Student s2 = s1, 只是在栈上创建了一个Student变量, 指向了s1对象, 并没有创建新的对象. 因此如果也会发生重复释放的问题, s2在函数执行完后自动调用s1的析构函数, s1对象自己又回调用一次.
  • 常规的赋值操作, 是把对象的数据全部拷贝一份到新的对象
class Student
{
public:
    Student(int age)
    {
        this->age = new int(age);
    }
    Student(){}
    ~Student()
    {
        cout<<"~Student"<<endl;
        if(age != NULL)
        {
            delete age;
            age = NULL;
        }
    }
    // 返回引用, 因为返回的对象是仍然存在的
    Student& operator=(Student& s)
    {
        // 编译器提供的
        // this->age = s.age

        //先判断自己有没有堆区, 有的话先释放
        if(age != NULL)
        {
            delete age;
            age = NULL;
        }
        // 深拷贝
        age = new int(*s.age);
        // 返回自身
        return *this;
    }
    int *age;
};
int main(){
    Student s1(10);
    Student s2(20);
    Student s3;
    s3 = s2 = s1;
    cout<<s1.age<<endl;
    cout<<s2.age<<endl;
    cout<<s3.age<<endl;
    return 0;
}

关系运算符重载

class Car
{
public:
    Car(int n, string s) : num(n), color(s){}
    bool operator==(Car& c)
    {
        if(this->num == c.num && this->color == c.color)
            return true;
        else
            return false;
    }
    bool operator!=(Car& c)
    {
        if(this->num == c.num && this->color == c.color)
            return false;
        else
            return true;
    }
    int num;
    string color;
};
int main(){
    Car c1(5, "white");
    Car c2(5, "white");
    Car c3(6, "white");
    cout<<(c1==c2)<<endl;
    cout<<(c1!=c3)<<endl;
    cout<<(c2==c3)<<endl;
    return 0;
}

函数调用运算符重载

  • 函数调用运算符() 也可以重载
  • 由于重载后使用的方式非常像函数的调用, 因此称为仿函数
  • 仿函数没有固定写法, 非常灵活
  • 数字常量不能当作实参传入引用的形参
class MyPrint
{
public:
    void operator()(string s)
    {
        cout<<s<<endl;
    }
};

void MyPrint2(string s)
{
	cout<<s<<endl;
}
void test01()
{
    MyPrint myprint;
    myprint("hello");
    MyPrint2("hello2"); 
}
// 直接传入数字 不能算是引用
class MyAdd
{
public:
    int operator()(int a, int b)
    {
        return a+b;
    }
};


void test02()
{
    MyAdd myadd;
    int c = myadd(1,2);
    cout<<c<<endl;
    // 匿名函数对象 (匿名对象 + 重载函数符号)
    cout<<MyAdd()(100, 100)<<endl;
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值