运算符重载
运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义。
例如*
用于地址,将获得存储在这个地址中的值;而用于两个数字之间,得到的是乘积。
要重载运算符,可以使用被称为运算符函数的特殊函数形式,运算符函数的格式如下:operator(argument-list)
例如operator+()
重载+
运算符,operator*()
重载*运算符。op必须是有效的运算符,不能虚构新的符号。比如不存在operator@()
。
1 | Time Time::operator+(const Time &t) const { |
重载限制
- 重载后的运算符必须至少有一个操作数是用户定义的类型。防止用户为标准类型重载运算符。即不能用减法运算符重载求和,会影响性能。
- 使用运算符不能违反运算符原来的句法规则。例如不能将求模(余数)运算符重载成使用一个操作数:
1 | - int x; |
同时不能修改运算符的优先级。
- 不能创建新运算符,例如不能定义
operator**()
函数来表示求幂。 - 不能重载以下运算符:
运算符 | 释义 |
---|---|
sizeof | sizeof求长度运算符 |
. | 成员运算符 |
.* | 成员指针运算符 |
:: | 作用域解析运算符 |
? | 条件运算符 |
typeid | 一个RTTI运算符 |
const_cast | 强制类型转换运算符 |
dynamic_cast | 强制类型转换运算符 |
reinterpret_cast | 强制类型转换运算符 |
static_cast | 强制类型转换运算符 |
友元
C++控制对类对象私有部分的访问,通常公有类提供唯一的访问途径,但是有时候限制太严格,以致于不适合特定的编程情形。因而C++提供另一种形式的访问权限:友元 , 有三种:
- 友元函数
- 友元类
- 友元成员函数(通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。)
在为类重载二元运算符时常常需要友元。
创建友元
将原型放在类声明中,并在原型声明前加上关键字friend
。
friend Time operator* (double m, const Time &t);//goes in class declaration
表明两点:
- 虽然是在类声明中声明,但不是成员函数,因此不能使用成员运算符调用(.或者->),不能使用
Time::
限定符 - 虽然不是成员函数,但是和成员函数的访问权限相同
在函数实现中不要使用关键字friend
1 | Time operator*(double m, const Time &t) { |
返回对象相关
当成员函数或独立的函数返回对象时,有几种返回方式可供选择,可以返回指向对象的引用、指向对象的const引用或const对象
。
返回指向const对象的引用
使用const引用的常见原因是极高效率,但是对何时可以采用这种方式存在限制。如果函数返回(通过调用对象的方法或者将对象作为参数)传递给他的对象,可以通过返回引用来提高效率。
1 | Vector force1(50, 60); |
返回指向非const对象的引用
两种常见的返回非const对象的情形:
- 重载赋值运算符(旨在提高效率)
- 重载与const一起使用的
<<
运算符(必须这么做)
1 | //operator=()的返回值用于连续赋值 |
在这个例子中,返回类型不是const,因为方法operator=()返回一个指向s2的引用,可以对其进行修改。
1 | String s1("Good"); |
在这个例子中,返回类型必须是ostream& ,而不能仅是ostream,否则将调用ostream类的复制构造函数,而ostream没有共有的复制构造函数。
返回对象
如果返回的对象是被调用函数中的局部变量,则不应使用引用方式返回它。因为在被调用函数执行完毕,局部对象将调用其析构函数。
小结
指针和对象使用
- 使用常规表示法来声明指向已有的对象,
String * glamour;
- 可以将指针初始化为指向已有的对象,
String * first = &sayings[0];
- 可以使用new来初始化指针,这将会创建一个新的对象,
String * favorite = new String(sayings[choice]);
- 对类使用new将调用相应的构造函数来初始化新建的对象
- 可以使用
->
运算符通过指针访问类的方法,shortest->length()
- 可以对对象的指针应用解除引用运算符
*
来获得对象,’if (sayings[i] < *first>)’
重载<<运算符
要重新定义<<运算符,以便将他和cout一起用来显示对象的内容,请定义下面的友元运算符函数:
1 | //c_name是类名,如果该类提供了能够返回所需内容的公有方法,则可在运算符函数中使用这些方法,这样编不用将他们设置为友元函数 |
转换函数
要将单个值转换为类类型,需要创建原型如下所示的类构造函数:c_name(type_name value)
其中c_name是类名,type_name是要转换的类型的名称。
要将类转换为其他类型,需要创建类如下所示的类成员函数:operator type_name();
虽然该函数没有声明返回类型,但应返回所需类型的值。使用转换函数时,可以在声明构造函数中使用关键字explicit
,放置它被用于隐式转换。
其构造函数使用new的类
- 对于指向的内存是由new分配的所有类成员,都应在类的析构函数中对其使用delete,该运算符将释放分配的内存
- 如果析构函数通过对指针类成员使用delete来释放内存,则每个构造函数都应当使用new来初始化指针,或将其设置为空指针
- 构造函数中要么使用new,要么使用new[],不能混用。对应的析构函数使用delete,和delete[]
- 应定义一个分配内存的复制构造函数,这样程序能够将类对象初始化为另一个类对象,通常的函数原型:
className(const className &)
- 应定一个重载赋值运算符的类成员函数,其函数定义如下:
1 | c_name & c_name::operator=(const c_name & cn) { |
Share
一个Ubuntu免费使用ultraEdit的方法
在bin目录下建立一个脚本,并且在打开软件前执行:
1 | rm -rfd ~/.idm/uex |