很久以前(八十年代),没有办法区分++和--操作符的前缀与后缀调用。这个问题遭到程序员的报怨,于是C++语言得到了扩展,允许重载increment 和 decrement操作符的两种形式。

  然而有一个句法上的问题,重载函数间的区别决定于它们的参数类型上的差异,但是不论是increment或decrement的前缀还是后缀都只有一个参数。为了解决这个语言问题,C++规定后缀形式有一个int类型参数,当函数被调用时,编译器传递一个0做为int参数的值给该函数:

class UPInt { // "unlimited precision int"
public:
 UPInt& operator++(); // ++ 前缀
 const UPInt operator++(int); // ++ 后缀
 UPInt& operator--(); // -- 前缀
 const UPInt operator--(int); // -- 后缀
 UPInt& operator+=(int); // += 操作符,UPInts
 // 与ints 相运算
 ...
};

UPInt i;

++i; // 调用 i.operator++();
i++; // 调用 i.operator++(0);
--i; // 调用 i.operator--();
i--; // 调用 i.operator--(0);

  这个规范有一些古怪,不过你会习惯的。而尤其要注意的是这些操作符前缀与后缀形式返回值类型是不同的。前缀形式返回一个引用,后缀形式返回一个const类型。下面我们将讨论++操作符的前缀与后缀形式,这些说明也同样使用与--操作符。

  从你开始做C程序员那天开始,你就记住increment的前缀形式有时叫做“增加然后取回”,后缀形式叫做“取回然后增加”。这两句话非常重要,因为它们是increment前缀与后缀的形式上的规范。

// 前缀形式:增加然后取回值

UPInt& UPInt::operator++()
{
 *this += 1; // 增加
 return *this; // 取回值
}

// postfix form: fetch and increment

const UPInt UPInt::operator++(int)
{
 UPInt oldValue = *this; // 取回值
 ++(*this); // 增加
 return oldValue; // 返回被取回的值
}

  后缀操作符函数没有使用它的参数。它的参数只是用来区分前缀与后缀函数调用。如果你没有在函数里使用参数,许多编译器会显示警告信息,很令人讨厌。为了避免这些警告信息,一种经常使用的方法时省略掉你不想使用的参数名称;如上所示。

  很明显一个后缀increment必须返回一个对象(它返回的是增加前的值),但是为什么是const对象呢?假设不是const对象,下面的代码就是正确的:

UPInt i;
i++++; // 两次increment后缀
// 运算

  这组代码与下面的代码相同:

i.operator++(0).operator++(0);

  很明显,第一个调用的operator++函数返回的对象调用了第二个operator++函数。

  有两个理由导致我们应该厌恶上述这种做法,第一是与内置类型行为不一致。当设计一个类遇到问题时,一个好的准则是使该类的行为与int类型一致。而int类型不允许连续进行两次后缀increment:

int i;
i++++; // 错误!

  第二个原因是使用两次后缀increment所产生的结果与调用者期望的不一致。如上所示,第二次调用operator++改变的值是第一次调用返回对象的值,而不是原始对象的值。因此如果:

i++++;

  是合法的,i将仅仅增加了一次。这与人的直觉相违背,使人迷惑(对于int类型和UPInt都是一样),所以最好禁止这么做。

  C++禁止int类型这么做,同时你也必须禁止你自己写的类有这样的行为。最容易的方法是让后缀increment 返回const对象。当编译器遇到这样的代码:

i++++; // same as i.operator++(0).operator++(0);

  它发现从第一个operator++函数返回的const对象又调用operator++函数,然而这个函数是一个non-const成员函数,所以const对象不能调用这个函数。如果你原来想过让一个函数返回const对象没有任何意义,现在你就知道有时还是有用的,后缀increment和decrement就是例子。(更多的例子参见Effective
C++ 条款21)

  如果你很关心效率问题,当你第一次看到后缀increment函数时,
你可能觉得有些问题。这个函数必须建立一个临时对象以做为它的返回值,(参见条款19),上述实现代码建立了一个显示的临时对象(oldValue),这个临时对象必须被构造并在最后被结构。前缀increment函数没有这样的临时对象。由此得出一个令人惊讶的结论,如果仅为了提高代码效率,UPInt的调用者应该尽量使用前缀increment,少用后缀increment,除非确实需要使用后缀increment。让我们明确一下,当处理用户定义的类型时,尽可能地使用前缀increment,因为它的效率较高。

  我们再观察一下后缀与前缀increment
操作符。它们除了返回值不同外,所完成的功能是一样的,即值加一。简而言之,它们被认为功能一样。那么你如何确保后缀increment和前缀increment的行为一致呢?当不同的程序员去维护和升级代码时,有什么能保证它们不会产生差异?除非你遵守上述代码里的原则,这才能得到确保。这个原则是后缀increment和decrement应该根据它们的前缀形式来实现。你仅仅需要维护前缀版本,因为后缀形式自动与前缀形式的行为一致。

  正如你所看到的,掌握前缀和后缀increment和decrement是容易的。一旦了解了他们正确的返回值类型以及后缀操作符应该以前缀操作符为基础来实现的规则,就足够了。

operator++()和operator++(int)的区别的更多相关文章

  1. (转+原)VC编译错误:uafxcw.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) 已经在 LIBCMT.lib(new.obj) 中定义

    参考网址:http://zhanyonhu.blog.163.com/blog/static/16186044201023094754832/ 1>uafxcw.lib(afxmem.obj) ...

  2. Solve Error: nafxcw.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new[](unsigned int)" (??_U@YAPAXI@Z) already defined in libcpmt.lib(newaop.obj)

    Error: nafxcw.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new[](unsigned int)&quo ...

  3. 【C++】operator new/new operator/placement new之间的区别

    new operator new operator即是c++中的关键字new.比如A* = new A; 中的new就是new operator. 它执行了三个步骤: 1. 分配内存空间 事实上,分配 ...

  4. uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)"解决办法

    如果在编译MFC程序的时候出现下列及类似的错误: 1>uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator ...

  5. MFC程序出现uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)解决办法

    在同一个地方摔倒两次之后,决定记录下来这个东西. 问题 1>uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl opera ...

  6. “void * __cdecl operator new(unsigned int)”(??2@YAPAXI@Z) already defined in LIBCMTD.lib(new.obj)

    转自VC错误:http://www.vcerror.com/?p=1377 问题描述: 当 C 运行时 (CRT) 库和 Microsoft 基础类 (MFC) 库的链接顺序有误时,可能会出现以下 L ...

  7. C++中的new/delete与operator new/operator delete

    new operator/delete operator就是new和delete操作符,而operator new/operator delete是函数. new operator(1)调用opera ...

  8. C++中的::operator new, ::operator delete

    一般在使用new  和 delete的时候,做了两件事情,一是空间的配置( new 是分配,delete是回收),而是调用对象的析构函数 但是也有办法将这两个过程分开 那就是显式的调用::operat ...

  9. C++ new operator, delete operator, operator new, operator delete, new placement

    http://www.younfor.com/cpp-new-placement-new-operator-new.html http://www.cnblogs.com/luxiaoxun/arch ...

随机推荐

  1. JAVA 获取分行符

    public static final String CR_LF = System.getProperty("os.name").startsWith("Windows& ...

  2. Django基础-Lesson1

    web框架概念 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统. 对于所有的Web应用,本质上其实就是一个socket服务端, ...

  3. python中的super( test, self).__init__()

    python中的super( test, self).__init__() 对继承自父类的属性进行初始化 首先找到test的父类(比如是类A),然后把类test的对象self转换为类A的对象,然后“被 ...

  4. JSOI2018R2题解

    D1T1:潜入行动 裸的树上DP.f[i][j][0/1][0/1]表示以i为根的子树放j个设备,根有没有放,根有没有被子树监听,的方案数.转移显然. #include<cstdio> # ...

  5. [CodeForces-585F]Digits of Number Pi

    题目大意: 给你一个数字串s,一个序列范围l和r,(l和r的数字位数为d)求l到r中有多少个数,满足它的长度为d/2的子串,能够在s中被匹配. 思路: 首先将s中每一个长度为d/2的子串插入后缀自动机 ...

  6. Codeforces Round #222 (Div. 1) C. Captains Mode 状压

    C. Captains Mode 题目连接: http://codeforces.com/contest/377/problem/C Description Kostya is a progamer ...

  7. js面向对象写页面

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. CentOS 7 与老版本CentOS防火墙配置的区别

    一.CentOS 7 以下版本防火墙的配置: 1.开放80,22,8080 端口/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT/sbin/ipt ...

  9. 实现观察者模式(Observer Pattern)的2种方式

    在观察者模式中有2个要素:一个是被观察对象,另一个是观察者.但被观察对象的状态发生改变会通知观察者. 举例:把订阅报纸的人看作是观察者,把报纸看作被观察对象.每当有新的新闻就要通知订阅报纸的人.本篇分 ...

  10. 用最简单的例子理解对象为Null模式(Null Object Pattern)

    所谓的"对象为Null模式",就是要求开发者考虑对象为Null的情况,并设计出在这种情况下的应对方法. 拿"用最简单的例子理解策略模式(Strategy Pattern) ...