《C++编程惯用法——高级程序员常用方法和技巧》——2.5 操作符重载:成员或非成员?

简介:

本节书摘来自异步社区出版社《C++编程惯用法——高级程序员常用方法和技巧》一书中的第2章,第2.5节,作者: 【美】Robert B. Murray ,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.5 操作符重载:成员或非成员?

在C++中,对于操作符的重载有着两种方法:将该操作符作为成员函数或者不作为成员函数。在本节中,我们将会对如何为一个特定的操作符选择合适的重载方式给出一些评判标准。

显而易见的是,如果我们正在为一个不属于我们的类重载一个操作符,这个操作符应该是一个非成员函数:如果类不属于我们,我们也就不能向其中增加成员了!在本节的剩余部分中假定了我们可以决定是否将操作符定义为一个成员函数。

对于成员和非成员的选择结果会影响到使用该操作符的代码,例如,作为成员函数的操作符可以使用this指针,并且也可以用非限定形式来使用类中的成员。此外,这个选择结果还会影响到用户眼中该操作符的行为。如果操作符被实现为一个成员函数,那么我们就无法对第一个(最左边的)操作符进行隐式类型转换。这种不同存在于用户眼中,但对于编码来说则并不明显,因此它也应该成为我们在成员和非成员之间进行选择的一个主要评判标准。

2.5.1 一元操作符

我们先来看一个有关一元操作符的例子。假设我们有一个类Vector(向量)[3],它创建自一个Direction(方向)对象和一个用double描述的数值:

class Direction {
//此处忽略细节
};

class Vector {
public:
   Vector(const Direction&, double magnitude = 0.0);
};

由于Vector的构造函数中第二个参数有缺省值,我们就可以从一个Direction对象经过隐式转换得到一个Vector对象。

现在我们希望为Vector重载一个一元操作符。如果我们把它定义为非成员函数,那么我们也就可以将它作用到Direction上去(因为Direction可以隐式地转换为Vector):

Vector operator-(const Vector&);
main () {
   Direction d;
   Vector V=-d; //合法的:operator-(Vector(d))
}

如果operator-是一个成员函数,那么隐式转换就将被禁止:

class Vector {
public:
   Vector (const Direction&, double magnitude = 0.0);
   Vector operator-()const;
};

main () {
   Direction d;
   Vector v = -d; //编译期错误
}

我个人认为禁止隐式转换是一件好事,如同我们在2.4节中所看到的,使用隐式转换会使得代码难以被维护和被理解。出于这个原因,我建议:在可能的情况下,最好将一元操作符实现为成员函数。

2.5.2 =,[ ],()以及->

按照C++语言定义来说,这四种操作符必须被实现为成员函数。任何将它们中的某个实现为非成员的尝试都会造成一个编译期的错误:

extern int operator=(Complex&,const Complex&);

//编译期错误:operator=必须是成员函数

2.5.3 其他的二元操作符

对于其他的二元操作符来说,是否将它们实现为成员函数取决于我们是否需要对左操作数进行隐式转换。对于赋值类操作符(如+ =)来说,我们希望的是禁止对左操作数进行隐式转换:

Complex c;
C+=5;

我们很难想象c在+=的操作实施前就被隐式转换成为其他的东西。如果上面的代码顺利地通过了编译,在它运行后c的值将不会得到改变,这一点肯定会让人迷惑不解。如果我们将赋值类操作符定义为成员函数的话,我们就可以禁止这种情况的出现,并且还可以保证它们和operator =(它必须是一个成员函数)具有一致的行为。

此外,对于那些非赋值类从操作符来说,禁止对左操作数进行隐式转换但却允许对右操作数进行隐式转换同样会让人困惑。例如:

class Complex {
//此处忽略细节
public:
   Complex(double = 0.0, double = 0.0);
   Compiex operator+(const Complex&)const;
};

main () {
   Compiex c(1.0):
   Complex d = c + 1.0;  //OK
   Complex e = 1.0 + c;  //编译期错误
}

注意:我们在Complex的构造函数中使用了有缺省值的参数。这使得我们可以从double通过隐式转换得到Complex(这时,构造函数的第二个参数将会是缺省值0.0)。上面代码中对于operator+的第二次调用出错的原因在于:在当前作用域中,没有一个合适的声明对应于这次函数调用;编译器不会对成员函数的左操作数进行隐式转换。如果我们将operator+声明为非成员函数,那么上面两种形式的调用都将顺利地通过编译。

当两个操作数都是操作的输入,并且没有一个会受到操作的影响,那么我们最好将该操作符定义为一个非成员函数。这可以确保在有隐式转换的条件下,这两个操作数都可以以同样的方式来进行操作。

图2.1中归纳了本节中对于操作符是否该定义为成员函数的建议:
image

相关文章
|
27天前
|
安全 算法 C++
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
46 3
|
28天前
|
安全 算法 编译器
【C++ 泛型编程 进阶篇】深入探究C++模板参数推导:从基础到高级
【C++ 泛型编程 进阶篇】深入探究C++模板参数推导:从基础到高级
245 3
|
28天前
|
设计模式 程序员 C++
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
252 2
|
6天前
|
编译器 C++
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
17 0
|
7天前
|
C++
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
|
7天前
|
存储 编译器 C++
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
|
21天前
|
NoSQL C++
c++中包含string成员的结构体拷贝导致的double free问题
c++中包含string成员的结构体拷贝导致的double free问题
6 0
|
23天前
|
存储 安全 编译器
【C++】类的六大默认成员函数及其特性(万字详解)
【C++】类的六大默认成员函数及其特性(万字详解)
35 3
|
27天前
|
存储 移动开发 安全
【C/C++ 口语】C++ 编程常见接口发音一览(不断更新)
【C/C++ 口语】C++ 编程常见接口发音一览(不断更新)
22 0
|
27天前
|
算法 编译器 C++
【C++ 模板编程 基础知识】C++ 模板类部分特例化的参数顺序
【C++ 模板编程 基础知识】C++ 模板类部分特例化的参数顺序
21 0

热门文章

最新文章