类机制细节

来源:互联网 发布:nginx配置域名代理转发 编辑:程序博客网 时间:2024/05/04 10:27

 

类中哪些接口应该作为类的成员函数,哪些做成非成员函数能更好的发挥作用?类中的操作符重载应当设计成成员函数、非成员函数还是友元函数?这篇blog尝试着通过一个例子来简单讨论这些问题。

 

首先来看下面这个复数类

 

 

以上这个类的设计存在诸多问题,下面分析的重点在于关注类机制的细节。

 


 

1、构造函数

 

    Complex(double real, double imaginary = 0)

    : _real(real), _imaginary(imaginary)

 

 

由于第二个参数有默认值,所以该构造函数可以作为一个单参数的构造函数,这就暗含了一个double类型向Complex的隐式转换。

 

                        Complex d(1.0);

                 d + 2.0;

当在客户代码部分写下了如此的代码后,操作数 2.0 在运算过程中就被隐式转换成了Complex类型。虽然,有时这样的转换是符合我们要求的,但在很多细微的地方隐式转换会让代码调试变得越发困难。

 

TIPS:尽可能使构造函数成为explicit型和避免编写转换操作符是避免隐式转换构造的临时对象。

 

2operator+

 

    void operator+ (Complex other)    {    _real = _real + other._real;    _imaginary =_imaginary + other._imaginary;    }
这个操作符需要多些的讨论。

首先是个简单的效率问题,在对函数进行参数传递的时候,应选择指针或者引用方式,避免不必要的参数拷贝构造过程。

 

TIPS:一般,能保证参数不变的情况下,当以常量引用方式进行函数参数传递。

 

其次的一个大问题就是它改变了原有对象的值,加法操作不应该产生这样的函数副作用。正确的做法是返回一个操作结果的临时对象const Complex,而非该对象本身。

 

接下来的改进是相对于+操作符本身,我们更应该提供一个 += 的操作符版本。通常,选择编写 a op= b 要比 a = a op b 要更有效率。这是因为op=操作符直接对左边的对象进行操作,并且返回一个自身的引用,而不是像op操作符那样返回一个临时对象。

这两个操作符对于某个类型T的实现范式如下:

 

 

TIPS:op=通常应该根据op来实现。

 

对于这个操作符的编写,还要指出的是,operator+不应当是一个类的成员函数。作为类成员函数的操作符最大的一个限制就是左操作数必须是该对象本身。这就限制了用户在写加法的时候只能写”a=b+1.0”,而不能写成”a=1.0+b”。所以正确的做法应该将operator+作为一个非成员函数。

 

TIPS:

操作符的成员与非成员版本选择原则:

= () [] ->必须是成员函数

>><<始终是非成员函数(如果有必要,可以是友元)

对于其他操作符:

如果函数必须是虚拟的,那么使它成为一个成员函数;

如果它需要向它的最左边的参数进行类型转换,那么使它成为一个成员函数(如果有必要,可以是友元);

如果它可以只使用类的公共接口来实现,那么使它成为非成员函数和非友元函数;

否则它为成员函数。


注意:其中说到向第一个参数进行类型转换,可以参考构造函数中提到的隐式转换。

 

3operator<<

 

void operator<< (ostream os

{

os <<"("<< _real <<","<< _imaginary <<")";

}


参照2中的原则就可以发现,这个操作符需要成为非成员函数(或友元函数),并返回流引用。

此处仅给出正确的函数声明,这也是比较标准的<<操作符重载:

ostream& operator<<(ostream&os, Complex& complex)

 

 

4operator++()

    Complex operator++()    {        ++_real;        return *this;    }

这个是前置递增操作符。这个的返回值需要改为非常量引用Complex&,避免不必要的返回对象构造过程,提高效率。

 

 

5operator++(int)

    Complex operator++(int)    {        Complex temp = *this;        ++_real;        return temp;    }

后置递增操作符应当返回一个常量值const Complex。通过禁止改变返回对象,就可以阻止”a++++”这样的代码。

 

TIPS:后置递增应当根据前置递增来实现。

 

 

最后附上修改完的Complex类。当然标准库里已经有complex这个类了。无论何时,我们都应该优先考虑使用标准库中的代码。

 

 

 

参考内容:

Exceptional C++

第20条 类机制