运算符重载
1. 运算符重载:使同一个运算符作用于不同类型的数据时导致不同的行为的这种机制被称为运算符重载。
2. 在c++中,除了以下5个运算符之外,其余运算符均可以被重载
-
成员选择运算符---- .
-
成员指针运算符--- .*
-
作用域分辨符----- ::
-
三目选择运算符--- ?:
-
计算数据所占内存空间的大小:sizeof
3. 合法的运算符重载有哪些限制?
-
重载之后的运算符不能改变运算符的优先级和结合性;
-
重载之后的运算符不能改变运算符操作数的个数及语法结构;
-
重载的运算符操作数至少有一个是自定义类型的;
-
重载运算符函数通常不能有默认的参数。
4. 用户自定义类中的运算符重载函数的定义规则是:
返回类型 operator op(参数表)
{
//运算符函数功能的实现代码
}
eg:
(1)complex operate+(complex c) //运算符在complex类上的运算符函数的声明
(2)Time operator- (Time t); // -运算符在Time类上的运算符函数的声明
(3)如Person对流提取运算符函数的声明:friend istream & opeerator >> (istream &din,Person) // >>运算符函数声明
(4)bool operator > (Date t);
// > 运算符函数在Date类上的声明。`
5. 运算符函数实现主要有两种方式:
(1)以类的成员函数的方式实现
(2)以友元函数的方式实现-----为全局函数,不属于类,需要在类中声明为类的友元函数。
注:
- 对于流插入运算符和流提取运算符只能以友元的方式实现
- 以成员函数方式实现的运算符函数,对于操作数不同的运算符函数,重载函数的调用和定义是不一样的
6. 双目运算符 B
oprd1 B oprd2
------本质是oprd1.operator B(oprd2)
(1)定义时是按成员函数定义,
(2)操作数1 是对象自己,
(3)运算符函数名称是operator B
,操作2是运算符函数的参数
(4)对运算符函数的使用本质上是操作数1对运算符函数的调用。
7. 前置单目运算符 U
U oprd
-------本质上是oprd.operator U()
(1)写法是运算符 + 操作数,
(2)定义时是按成员函数定义
(3)对象本身就是操作数
(4)运算符函数 operatorU 是类的成员函数,由于单目运算法只有一个参数,所以运算符函数不带参数
8. 后置运算符 ++ 和 --
oprd ++
------ 本质是 oprd.operator ++(0)
(1)操作数+运算符,为了与前置单目运算区分开,所以在运算符函数中加了整型参数。
(2)唯一的操作数oprd 即为对象本身,
(3)运算符的使用本质是,操作数对其成员函数的调用
9. 以类的成员函数实现运算符重载,定义语法形式:
返回类型 类名::operator 运算符(参数表)
{
//运算符函数的定义体
}
10. 以友元的方式重载运算符及重载方式选择规则
- 在类中声明友元运算符的格式
friend 返回类型 operator 运算符(形参列表);
//该运算符函数在类外实现,其形式如下:
返回类型 operator 运算符 (形参表)
{
//运算符函数的定义体
}
注:
- 对双目运算符而言,成员运算符函数带有一个参数,而友元运算符函数带有两个参数;
- 对于单目运算符而言,成员运算符不带有参数,二友元运算符函数带有一个参数;
友元调用与成员调用的区别:
习惯性形式 | 友元调用形式 | 成员调用形式 |
---|---|---|
a+b | operator+(a,b) | a.operator+(b) |
-a | operator-(a) | a.operator-() |
a++ | operator++(a,0) | a.operator+(0) |
eg: //定义流插入运算符函数operator <<,实现自定义类型的屏幕输出。
ostream& operator <<(ostream &out ,complex c) //函数返回值类型是输入流基类 ostream 对象的引用函数,第一个参数是 ostream 的引用,第二个参数是附属类 complex 对象。
{
if (c.real != 0)
cout << c.real;
if (c.imag > 0)
cout << '+' << c.imag<< 'i' ;
else
cout << c.imag << 'i' ;
return out;
}
//由于实部real 和虚部 imag 是复数complex 对象的私有数据,外部不可访问,若需访问需声明为complex类的友元函数;
//主函数调用: operator<<(cout,c1)
- 运算符重载方式选择的规则:
(1)单目运算符,建议用 ————> 成员函数
(2)运算符“=、()、【】、->”只能作为成员函数重载;
(3)复合赋值运算符,建议重载为成员函数( +=、-=、/=、*=、&=、!=、~=、%=、>>=、<<= )
(4)流提取和流插入 —————> 只能使用友元函数重载实现
(5) 对其他运算符,建议重载为友元函数重载
(6)若运算符的操作数(尤其是第一个操作数)有隐式类型转换,则只能选用友元函数重载
(7)当需要重载运算符的运算具有可交换性是,选择友元函数重载。
转换构造函数实现类型转换
1. 用户自定义类型之间、用户自定义类型和系统的内置类型之间如何进行转换,编译器没发处理,需要程序提供。
2. 不一致的类型间进行类型转换,一共有三种情况
- 从基本类型到用户自定义类型
eg:int s=3600; Time t=s;
- 从类类型到基本类型
Time t(1,0,0); int s=(int) t;
- 从某个用户自定义类型到另外一个自定义类型
Point p(1,2) ;
complex c;
c = p; // 这行代码的执行就要求把Point对象 p 首先转换成 complex 对象,再赋给 complex 对象 c。
3. 类型转换方式有两种:隐式类型转换和显示类型转换
- 隐式类型转换:编译器根据需要自动对数据类型做转换,---eg:
Time t=s
; - 显式类型转换:强制转换、函数法,---eg:
int s=(int) t
;
4. 转换方法:
- 转换构造函数能够实现从基本类型向自定义类类型的转换---eg:
Time t=s;
- 类型转换函数能够把源类型转换成另一种目标类型---eg:
int s=(int) t;
ps:目标类型可以是自定义类型,也可以是系统定义类型。
5. 转换构造函数:含有一个参数的构造函数,且参数不同于该类的一个数据类型
自动调用:
(1)如果声明类对象的初始化表同转换构造函数的参数表相匹配,该函数就会被调用
(2)当在需要该类的地方使用了别的数据类型,编译器就会自动调用转换构造函数进行转换。
注:转换构造函数要点,类名和类同名,含有一个不同于自身类型的参数,由于是构造函数也不需要返回类型,函数体不需要写 return 语句。
类型转换函数实现类型间的转换
1. 通过类型转换函数可以将类类型自动转换为内置类型或其他类型;或类型转换函数必须定义为类的成员函数
类型转换函数的格式:
operator 类型名()const
{
......
return (结果类型表达式)
}
注:
- 函数头没有类型声明,在类中声明----
operator double() const;
- 函数没有参数,对象自己就是函数要处理的数据
- 函数体中含有 return 语句
- const 只对对象自身数据的保护