运算符重载

news/2024/10/12 14:23:10

运算符重载

基本规则

可以重载的运算符:

不可重载的运算符:

//返回类型 operator后面加运算符(参数列表)
//eg.  Integer operator+(Integer l, Integer r);class Integer{
public:Integer(int n = 0) : i(n) {}const Integer operator+(const Integer& v){	//在类中定义运算符重载函数return Integer(i+v.i);}const void print_i(){ cout << i << endl; }
private:int i;
};int main()
{Integer x(10),y(20);Integer z = x + y;	//相当于 x.operator+(y)z.print_i();	//打印结果z = x + 3;	//ok,打印出13z.print_i();	z = 3 + 7;	//ok,将10传给构造函数创建一个Integer对象z.print_i();z = 3 + y;	//error,3不是Integer对象,没有实现运算符重载,会报错z.print_i();	return 0;
}

由上面的例子可以看到,z = 3 + y会报错,因为3不是Integer对象,双目运算符重载调用的是运算符左边的对象的运算符重载函数。可将成员函数修改为全局函数(在类中将该函数添加friend关键字)。

class Integer{
public:Integer(int n = 0) : i(n) {}friend const Integer operator+(const Integer& l, const Integer& r);const void print_i(){ cout << i << endl; }
private:int i;
};const Integer operator+(const Integer& l, const Integer& r){	//全局函数return Integer(l.i + r.i);
}int main()
{Integer x(10),y(20);Integer z = x + y;	//相当于 x.operator+(y)z.print_i();	//打印结果z = x + 3;	//ok,打印出13z.print_i();	z = 3 + 7;	//ok,将10传给构造函数创建一个Integer对象z.print_i();z = 3 + y;	//ok,会调用全局函数 z.print_i();	return 0;
}

是否将运算符重载设置为成员函数的基本规则:

1、单目运算符应该被设置为成员函数

2、= () [] -> ->*必须设置为成员函数

3、赋值运算符应该被设置为成员函数

4、其他的双目运算符应该作为全局函数(如+、-、*、/等)


原型

常见操作符的原型:

操作符++和--,++和--也是可以实现运算符重载,怎么区分是++x还是x++?

首先要了解a++和++a的区别:

a++可以这么理解:先对a原来的值(a=5)拷贝一份,接着执行a = a + 1,最后将之前拷贝的副本赋值给b,于是就有b = 5, a = 6

++a可以这么理解:先执行a = a + 1,最后将新的结果a = 7赋值给b,于是就有b = 7, a = 7

int main()
{int a = 5;int b = a++;	//b = 5, a = 6cout << "b = " << b << ",a = " << a <<endl;int c = ++a;	//a = 7, c = 7cout << "c = " << c << ",a = " << a <<endl;return 0;
}

输出结果:


++、--运算符的重载

按照这样的运算规则对++运算符进行重载。

class Integer{
public:Integer(int n = 0) : i(n) {}friend const Integer operator+(const Integer& l, const Integer& r);friend const Integer operator-(const Integer& l, const Integer& r);const int get() { return i;}const Integer& operator++();	//++x,++做前缀const Integer operator++(int);	//x++,int并不会作为形参传递,返回的是一个新的Integer对象,所以不加引用const Integer& operator--();	//--x,--做前缀const Integer operator--(int);	//x--,int并不会作为形参传递
private:int i;
};//对'+'运算符重载
const Integer operator+(const Integer& l, const Integer& r){	//全局函数return Integer(l.i + r.i);
}//直接对原来对象的值进行修改
const Integer& Integer::operator++(){*this = *this + 1;  //调用'+'的重载函数return *this;   //返回一个integer对象,加&是是因为这是对原来的对象的直接修改
}//返回值不加引用是因为返回的是一个局部对象,return后实际上会执行一个拷贝构造函数操作
const Integer Integer::operator++(int){ //返回一个新创建的对象Integer old(*this);++(*this);  //调用上面的++重载函数return old;
}//实现--x
const Integer& Integer::operator--(){*this = *this - 1;return *this;
}//实现x--
const Integer Integer::operator--(int){Integer old(*this); //拷贝构造函数--(*this);  //调用--x的重构函数return old;
}int main()
{Integer x(5);Integer y = x++;cout << "y.i = " << y.get() << ", x.i = " << x.get() << endl;Integer z = ++x;cout << "z.i = " << z.get() << ", x.i = " << x.get() << endl;return 0;
}

可以看到输出结果与上个例子结果一致。


关系运算符的重载

通过==重载来实现!=的重载,通过<重载来实现>、<=、>=的重载,这么写的好处是充分利用已有函数,后期修改只需修改两个函数

class Integer{
public:Integer(int n = 0) : i(n) {}friend const Integer operator+(const Integer& l, const Integer& r); //友元函数friend const Integer operator-(const Integer& l, const Integer& r);const int get() { return i;}const Integer& operator++();	//++x,++做前缀const Integer operator++(int);	//x++,int并不会作为形参传递,返回的是一个新的Integer对象,所以不加引用const Integer& operator--();	//--x,--做前缀const Integer operator--(int);	//x--,int并不会作为形参传递//overload relational operatorsbool operator==(const Integer& r) const;bool operator!=(const Integer& r) const;bool operator<(const Integer& r) const;bool operator<=(const Integer& r) const;bool operator>=(const Integer& r) const;bool operator>(const Integer& r) const;private:int i;
};// overload relational operators definition
bool Integer::operator==(const Integer& r) const{return (i == r.i);
}bool Integer::operator!=(const Integer& r) const{return !(*this == r);   //调用==的重载函数
}bool Integer::operator<(const Integer& r) const{return i < r.i;
}bool Integer::operator>(const Integer& r) const{return r < *this;   //调用<的重载函数
}bool Integer::operator<=(const Integer& r) const{return !(r < *this);   //调用>的重载函数, <=就是>取反
}bool Integer::operator>=(const Integer& r) const{return !(*this < r);   //调用<的重载函数, >=就是<取反
}int main()
{Integer x(5);Integer y(7);cout << boolalpha << (x < y) <<endl;	//调用x.operator<(y),boolalpha使得打印出bool类型cout << boolalpha << (x > y) <<endl;cout << boolalpha << (x == y) <<endl;return 0;
}

输出结果:


类型转换

用户定义的类型转换:当构造函数是单个参数或运算符转换的隐式类型转换时编译器会进行隐式转换。

C++类型转换:

对于用户定义的类型,有两种方法实现T==>C。一是C中存在用T作为单个参数传递的构造函数,二是T中存在用运算符重载的方式实现T==>C。

构造函数实现自动类型转换

#include <iostream>class One{
public:One()  {}   
};class Two{
public:Two(const One&){}
};void f(Two){}int main()
{One one;    f(one);     //wants a Two, has a onereturn 0;
}

f()函数需要Two类型的对象作为参数,当将对象one作为参数传递时,编译器会查找是否存在用类One来构建类Two的构造函数,这时会调用Two::Two(const One&),结果就是将Two的对象作为参数传递给f()。

自动类型转换可以避免定义两个不同版本的f()函数,但是自动类型转换会隐式地调用Two的构造函数,会对程序的效率有影响。

避免编译器使用构造函数实现自动类型转换需要加关键词explict,如下:

class One{
public:One()  {}   
};class Two{
public:explicit Two(const One&){}
};void f(Two){}int main()
{One one;//f(one);		//errorf(Two(one));     //ok,Two(one)创建了一个临时的对象将其作为参数传递给f()return 0;
}

运算符重载实现自动类型转换

class Three{
private:int i;
public:Three(int ii) : i(ii) {}
};class Four{
private:int x;
public:Four(int xx) : x(xx) {}operator Three() const { return Three(x); } //函数名就是要转换的类型,所以最前面不用加返回值类型
};void g(Three) {}int main()
{Four four(1);g(four);	//实现Four==>Three,再将转换后的对象传递给g()g(1);		//调用Three(1,0)return 0;
}

自动类型转换的缺陷

当上述提到的两种自动类型转换的情况同时存在时,就会产生一个模糊的转换,两种方式都能实现隐式的自动类型转换,编译器不知道使用哪种方式,就会出现报错的情况

class Orange{
public:Orange(const Apple&);       //Convert Apple to Orange
};class Apple{
public:operator Orange() const;    //Convert Apple to Orange
};void f(Orange){ }int main()
{Apple a;//f(a);       //ambiguous conversionreturn 0;
}

类型转换总结

尽量不要使用这种自动类型转换,函数调用时会出现各种问题,使用显式类型转换函数,如double ToDouble()来代替operator double() const。


参考资料:

Thinking in C++,Chapter 12 Operator Overloading

浙江大学翁凯C++教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/27252.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

图的概念、存储与遍历

相关概念图 (graph) 是一个二元组 \(G=(V(G),E(G))\)。其中 \(V(G)\) 是非空集,称为 点集 (vertex set),对于 \(V\) 中的每个元素,我们称其为 顶点 (vertex) 或 节点 (node),简称 点;\(E(G)\) 为 \(V(G)\) 结点之间边的集合,称为 边集 (edge set)。 ​ …

基于SSM的仓库进销存系统毕业设计论文【范文】

摘要 随着信息技术的不断发展,企业对于仓储管理的要求日益提高。为了提升仓库管理的自动化和智能化水平,本研究设计并实现了一个基于Spring、Spring MVC和MyBatis (SSM) 框架的在仓库进销存系统。该系统旨在为企业提供一个高效、准确、实时的库存管理解决方案,以优化库存控制…

FreeRTOS任务通知

FreeRTOS任务通知 FreeRTOS 新增了任务通知(Task Notifictions)这个功能,可以使用任务通知来代替信号量、消息队列、事件标志组等这些东西。使用任务通知的话效率会更高,任务通知在 FreeRTOS 中是一个可选的功能, 使用队列、信号量、事件标志组时都需另外创建一个结构体,通…

uboot-学习笔记

uboot引导程序的作用不同bootloader的对比系统启动自举过程阶段

【软件构造课程相关】幻方及其构造(上)

介绍 ​ 幻方(Magic Square),有时又称魔术方阵或纵横图,由一组排放在正方形中的整数组成,其每行、每列以及每一条主对角线的和均相等。通常幻方由从1到$N2$的连续整数组成,其中N为正方形的行或列的数目。因此N阶幻方有N行N列,并且所填充的数为从1到$N2$。 ​ …

SDL库基础学习

初始化 int SDL_Init(Uint32 flags);* `flags` may be any of the following ORd together:** - `SDL_INIT_TIMER`: timer subsystem* - `SDL_INIT_AUDIO`: audio subsystem* - `SDL_INIT_VIDEO`: video subsystem; automatically initializes the events* subsystem* - `SDL…

2024-5-1 假期第一天 愉快

假期第一天,中午十点多醒的,经过一番挣扎之后还是下定决心去本部开点二硫化硒,于是坐地铁去本部,到了发现皮肤科不开,遂返回,虽然无功而返吗,但是今天天气是真的好,路上骑行看到的风景很美,回来的时候去物美逛了一圈买了点香蕉,买了点饮料,然后又花30买了两杯喜茶,…