C++基本功和 Design Pattern系列 Operator 下

继续上一章的内容,下面是经过调整后的Test Class代码:

class Test {
private:
    int internalData;
public:
    // constructor and destructor
    Test(int data = 0) : internalData(data) {};
    Test(const Test & Para) : internalData(Para.internalData) {};
    ~Test() {};
  
    // Operator overlording
    Test & operator += (const Test & Para1);
    Test operator + (const Test & Para1); 
};

Test & Test::operator += ( const Test & Para1 )
{
    internalData += Para1.internalData;
    return * this;
}

Test Test::operator + (const Test & Para1)
{
    return Test(*this) += Para1;
}
下面我们假设要给这个Test Class添加一种新的功能,让Test Class 和 Integer之间能够进行加法操作。 也就是说可以执行下列代码:

Test x1(10);
x1 = x1 + 5;
x1 += 5;

实际上,我们不需要进行任何修改,上面的代码就能够正确执行。因为我们提供的构造函数Test(int data = 0) 能够隐性的 (implicit type conversion) 把一个integer 转换成一个Temporary Test Object,然后掉用 Test Test::operator + (const Test & Para1)。因此,上面的代码等同于:

x1 = x1.operator + (Test(5));
x1 = x1.operator += (Test(5));

Implicit Type Conversion 实际上会带来很多的麻烦,要想避免潜在的危险,最好在Test(int data = 0)前面加上explicit,表示如果对interger转换成Test,必须由程序员来控制,compiler不得进行隐性的操作。因此,要想似的 x1 = x1 + 5能够正常运行,有2种方法:

x1 = x1 + static_cast<Test>(5);

x1 = x1 + Test(5);

还有一点需要注意的是,如果不用explicit type conversion,可以运行:

x1 = x1 + 5;

但是在编译:

x1 = 5 + x1

的时候就会报错了,除非使用一个Temporary Object ,如:

x1 = Test(5) + x1;

要想使Test Class 支持 x1 = 5 + x1,最好的方法就是用helper function. 下面我们来看看Operator的另外一中定义方式。

==================分割线==================

我们可以使用friend function 来定义Test Class 的加法运算,代码如下:

class Test {
    Test(int data = 0) : internalData(data) {};
    ...
    // 针对这个Test Class, 并不需要下面这行。
    friend Test operator + ( const Test & Para1, const Test & Para2);
};

Test operator + ( const Test & Para1, const Test & Para2)
{
    return Test(Para1) += Para2;
}

首先我们需要注意的是,Test(int data = 0)没有用explicit,也就是说可以进行隐性的类型转换,因此无论是运行:
    x1 = x1 + 5;
还是:
    x1 = 5 + x1;
都能够编译通过。

解决了基本的功能问题,让我们继续考虑一下效率。无论是在x1 = x1 + 5,还是在x1 = 5 + x1,都至少会掉用额外的constructor和destructor把5转换成Test Object,这种浪费是很没有必要的。其次允许compiler进行implicit type conversion并不是一个良好的习惯。解决这些问题的方法,就是提供专用的 operator + 来进行integer和Test object之间的加法操作,具体代码如下:

========== 支持 x1 + 5 ==========
Test operator + ( const Test & Para1, int Para2)
{
    return Test(Para2) += Para1;
}

========== 支持 5 + x1 ==========
Test operator + ( int Para1, const Test & Para2 )
{
    return Test(Para1) += Para2;
}

同时还要在class Test中加如下面2行(对于此例子并不需要,不过正常情况是需要的):

friend Test operator + ( int Para1, const Test & Para1 );
friend Test operator + ( const Test & Para1, int Para2 );

并且在constructor前面加上 explicit。当然,你也可以用Template进行定义,如下:

========== 支持 x1 + 5 ==========
template <class T>
T operator + ( const T & Para1, int Para2)
{
    return T(Para2) += Para1;
}

实际上对于 template的定义,我个人并不推荐. 首先是因为namespace的问题,到底是global namespace呢?还是一个local namespace?如果是global namespace,那不一定所有的global class 都需要 operator +,这样就提供了多余的class操作。local namespace倒是可以用,前提是所有的class都定义了 +=. 也许对于大多数class来讲,并不需要operator + 的操作。所以我觉得对于 operator 的定义,尽量少用 template (个人观点).

==================分割线==================

下面说说关于类型转换的operator. 对于一个Abstract Data Type来说,类型转换是经常用到的,比如我们前面提到的从 integer转换成 Test, 可以使用implicit type conversion 和 explicit type conversion. 但是如果我们想从Test 转换成 integer,compiler无法支持自动的类型转换,因此需要我们提供相应的operator:

class Test {
    ...
    // Type converstion from Test to int
    operator int() { return internalData; };
}

那么我们就可以执行:
    int i = Test(10);

实际上,operator int()又是一种implicit type conversion,这并是收程序员的控制。良好的程序设计,是programmer能够精确的控制每一个细微的操作。因此并不推荐使用 operator int(),好的方法是按照 < effective c++ > 中给出的那样,提供一个asInt() method,来做explicti type conversion:

============ explicti ============
class Test {
    ...
    // Type converstion from Test to int
    int asInt() { return internalData; };
}

================== Test++ & ++Test ==================

相信大家都知道 Prefix ++ 和 Postfix ++的区别是什么,下面是代码:

// Prefix
Test& operator++()
{
    ++internalData;
    return (*this);
}

// Postfix
Test operator++(int)
{
    ++*this;
    return --Test(*this); // 为了使用返回值优化,需要定义 --Test
}

我们只是简单的看下效率问题,在 Prefix中也就是 ++ 返回的是reference,没有temporary object,在 Postfix中返回的是个object,使用了Temporary。相信大家都知道了,能不使用 Test++的地方就不要使用,尽量使用 ++Test。

比如:

for( iterator i = XXX; XXX; ++i) // 不要使用 i++

对于postfix, compiler并不能保证肯定会优化成 prefix,所以写代码的时候尽量注意。

================== 其他关于Operator ==================

有些operator,并不推荐进行overload,因为会出现无法预料的情况。这些operator 包括:

&&, || , & , |  , ","

举个简单的例子,如果你overload了",",那么有一个for循环如下:

for( Test x1 = x2,i = 0; ; ) {....}

到底是x1 = x2 和 i = 0呢?还是 x1 = x2.operator , (i) = 0 呢?如果overload了 & ,对于逻辑判断,x1 && x2,到底是  x1 && x2呢?还是 x1.operator & (&x2)呢?因此这些overload都会产生很多让人费解的问题。

其次,很多operator overload需要很小心的对待,这些operator 如下:

new new[] delete delete[] -> [] ()

请仔细阅读 C++ 标准,了解详细内容后,再对这些operator进行overload,不然很容易造成程序的不稳定。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值