> 文章列表 > 运算符重载

运算符重载

运算符重载

概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型


 

目录

 一、加号运算符重载

分类:

①成员函数重载+号

②全局函数重载+号

二、左移运算符重载

作用:以输出自定义数据类型

三、递增运算符重载 

前置递增

后置递增

总结:前置递增返回引用,后置递增返回值

四、赋值运算符重载

前提:C++编译器至少给1个类添加4个函数

五、关系运算符重载

作用:重载关系运算符,使2个自定义对象进行对比

六、函数调用运算符重载函数调用运算符()也可以重载


 一、加号运算符重载

“+”号一般代表2个数字之间相加,是内置数据类型
加号运算符重载代表2个自定义数据类型之间相加

分类:

①成员函数重载+号=首先创建代码,对象n1,n2,n3。尝试用n1+n2=n3;

class number
{
public:int m_a;int m_b;
};
void test01()
{number n1;n1.m_a = 10;n1.m_b = 10;number n2;n2.m_a = 5;n2.m_b = 6;number n3;n3 = n1 + n2;
}

 

 接下来,我们在类中创建加号运算符重载函数operator+

class number
{
public:number operator+(number& n) // 加号运算符重载函数{number tmp;tmp.m_a = this->m_a + n.m_a;tmp.m_b = this->m_b + n.m_b;return tmp;}int m_a;int m_b;
};

实际上number n3 = p1.operator+(p2);;等价于 number n3 = n1 + n2;

成功相加

②全局函数重载+号

同样的,只不过是在类外实现,而且参数是2个对象

number operator+(number& n1, number& n2)
{number tmp;tmp.m_a = n1.m_a + n2.m_a;tmp.m_b = n1.m_b + n2.m_b;return tmp;
}

 实际上number n3 = operator+(p1,p2);等价于 number n3 = n1 + n2;

同时,运算符重载也可以发生函数重载

而且,内置数据类型不可以修改


二、左移运算符重载

作用:以输出自定义数据类型

首先创建一个person类与测试函数test01,类中有属性m_a/m_b;

class person
{
public:int m_a;int m_b;
};void test01()
{person p;p.m_a = 10;p.m_b = 5;cout << p.m_a << endl;
}

如果只是想 输出m_a或m_b,可以直接输出

 不过,如果想直接输出p,则不行

接下来,就可以使用operator函数重载左移运算符<<


不过首先,如果我们想要使用成员函数重载<<运算符,这么写

这样的话,实际调用效果是这样:

p.operator(p) 

而如果参数改成cout:

 实际调用效果是这样:

p.operator(cout) == p<<cout ,out在右侧

并非我们要的 cout<<p

因此,我们要使用全局函数重载

void operator<<(ostream &cout, person &p)
{cout << p.m_a << p.m_b;
}

这样实际调用:operator << (cout,p) == cout << p;

 成功输出

不过,如果我们想换行

 因为<<返回的是void不可再输出其他内容。

因此,我们要使它仍然返回cout,修改operator函数

ostream& operator<<(ostream& cout, person& p)
{cout << p.m_a << p.m_b;return cout;
}

 这样就可以实现换行

甚至还可以随便输出其他内容


而此时,如果我们把属性设为私有,函数将无法访问

这样就可以使用上节课学到的友元来解决

class person
{friend ostream& operator<<(ostream& cout, person& p);friend void test01();
private:int m_a;int m_b;
};

三、递增运算符重载 

分为2种:前置递增与后置递增

而且是对我们自定义数据类型进行递增,并非内置数据类型


前置递增

首先创建myInteger类,并创建初始化函数myInteger

这样,在cout时就需要自定义<<左移运算符

class myInteger
{
public:myInteger(){m_a = 5;}int m_a;
};
ostream& operator<<(ostream& cout, myInteger myint) // 自定义<<左移运算符重载
{cout << myint.m_a << myint.m_b;return cout;
}
void test01()
{myInteger myint;cout << ++myint << endl;
}

此时提示没有对应的++运算符


 接下来实现前置++

	myInteger& operator++(){m_a++; // 先自增return *this; // 再返回自身}

令 数据自增后返回即可,同时注意要返回自身返回值要引用&


如果返回值不加引用

	myInteger operator++(){m_a++; // 先自增return *this; // 再返回自身}

会导致每次返回的是新的对象,不是原来接收的数据本身

 因此要加上&


后置递增

思路差不多,只是需要先保存值,再递增,最后输出保存的值

注意,只要在参数列表里写个int,编译器就会认为是后置递增了,而这个int是占位参数

myInteger operator++(int) // int 占位参数{myInteger tmp = *this;m_a++; // 先自增return tmp; // 再返回自身}

注意返回值不能写引用&,因为返回的是tmp,是局部变量函数执行完就被销毁了,引用会返回它自身但是已经被销毁了,相当于非法访问

 因此,也不能连续递增(myint++)++


总结:前置递增返回引用,后置递增返回值

而递减与递增基本相同,只需有把++改成--即可


四、赋值运算符重载

前提:C++编译器至少给1个类添加4个函数

①默认构造函数(无参,空实现,函数体为空)

②默认析构函数(无参,空实现,函数体为空)

③默认拷贝构造函数(对属性进行值拷贝)

④赋值运算符operator=(对属性进行拷贝)

当值拷贝时,也会出现堆区内存重复释放的问题


首先,创建person类和测试函数test01

class person
{
public:person(int age){m_age = new int(age); // 堆区开辟空间存储} int *m_age;
};void test01()
{person p1(10);cout << *p1.m_age << endl;person p2(12);cout << *p2.m_age << endl;p2 = p1;cout << *p2.m_age << endl;
}

正常输出

p2=p1是将p1所有属性浅拷贝给p2

而此时,由于我们上面new出的堆区数据需要我们手动释放,因此补全析构函数

~person(){if (m_age != NULL){delete m_age;m_age = NULL;}}

 再次运行

 

 因为p1与p2指向同一块m_age,delete一次后又进行delete,造成非法访问

这就是因为浅拷贝造成的堆区内存重复释放的问题

因此,我们赋值时,使用深拷贝,同时要先判断对象是否有属性在堆区,有则释放

	void operator=(person& p1) // 复制运算符的重载{if (m_age != NULL) // 判断是否有属性在堆区,有则释放{delete m_age;m_age = NULL;}m_age = new int(*p1.m_age);}

 不会再崩溃

但是,如果想连续赋值

 因为函数返回值是void,所以不能实现,因此修改返回值为person&,而且必须是引用才是返回自身,否则返回一个新的对象

person& operator=(person& p1) // 复制运算符的重载,返回引用才是返回自身{if (m_age != NULL) // 判断是否有属性在堆区,有则释放{delete m_age;m_age = NULL;}m_age = new int(*p1.m_age);return *this;}

成功实现 


五、关系运算符重载

作用:重载关系运算符,使2个自定义对象进行对比

而内置数据类型,int等编译器知道如何比对


class person
{
public:person(string name, int age){m_name = name;m_age = age;}
private:string m_name;int m_age;
};
void test01()
{person p1("joyce", 22);person p2("tatina", 22);if (p1 == p2){cout << "相等" << endl;}else{cout << "不等" << endl;}
}

目前是不行的

接下来实现对比函数

bool operator==(person& p){if (this->m_age == p.m_age && this->m_name == p.m_name){return true;}return false;}

如果要实现不等关系(!=)判断,把==改为!=,判断条件反过来就行  


六、函数调用运算符重载
函数调用运算符()也可以重载


实现一个 打印 类 

class func
{
public:void operator()(string line) // 打印操作{cout << line << endl;}string line;
};void test01()
{func fun;fun("hello,nana");
}

接下来实现一个同样作用的函数

void print(string line)
{cout << line << endl;
}

 正常输出,而由于使用非常类似于函数调用,因此又称为仿函数


实现一个 加法 类

class my_add
{
public:int operator()(int x, int y){return x + y;}
};
void test01()
{my_add myadd;int ret = myadd(100, 200);cout << ret << endl;
}

因此,仿函数非常灵活

同时,加上匿名函数对象

void test01()
{my_add myadd;int ret = myadd(100, 200);cout << ret << endl;// 匿名函数对象cout << my_add()(200, 300) << endl;
}

群头像网