From: shapiro@cobra.cs.unm.edu (Henry Shapiro)
Subject: Overloading postfix ++
Organization: Computer Science Department, University of New Mexico
Date: Fri, 20 Nov 1992 21:58:30 GMT

I'm reasonably new to C++, but have been programming for many years and in
lots of languages.  For a book I am writing I wanted to program up an
infinite precision integer package in C++.  The following code can be
criticized on lots of grounds -- that is NOT the point of this mail.
The point is: According to Lippman in his 2nd edition of the C++ primer,
you can overload the POSTFIX ++ operator.  I have, I believe, followed
his model EXACTLY.  Well.... it doesn't work.  It compiles fine, but
the PREFIX ++ operator is being invoked.  It is easy to see this: 3! prints
as 12 and not 6.  I've tried this on different compilers.  So either they
all have a bug or I don't understand something.  I'm gambling on the latter.
Can anybody straighten me out?

I don't read this newsgroup very often, so please e-mail me responses
at  shapiro@unmvax.cs.unm.edu  .  If something interesting turns up I'll
post the solution/conclusion.

// ======================= Factorial.c ===================================
#include <stream.h>
#include "Long_Arithmetic.h"

LongInt Factorial(LongInt n) {
    // Compute  n! .
    LongInt NFact;
    LongInt i;

    NFact = 1;
    for (i = 2; i <= n; i)
        NFact = NFact * i++;  //IS USING PREFIX AND NOT POSTFIX ++ (try 3!)
    return NFact;
} //Factorial

void main() {
    int n;

    cout << "For what  n  do you wish to compute  n! : "; cin >> n;

    cout << n << "! = " << Factorial(n);
} // main
// ======================= Long_Arithmetic.h ===================================
/* Store an infinite precision natural number in an array which is just
   long enough to hold it (more or less).  Numbers are stored in base 10,
   one digit per element array.  Leading zeros are not part of the number,
   except for zero.
*/
class LongInt {
public:
    LongInt(long = 0); // allows automatic conversion to  LongInt  in a number
		       // of situations where this is desirable
    LongInt(const LongInt&); // allows parameter passing by value to be what
			     // the user expects
    ~LongInt();

    LongInt& operator=(const LongInt&);

    friend int operator< (const LongInt&, const LongInt&);
    friend int operator<=(const LongInt&, const LongInt&);
    friend int operator> (const LongInt&, const LongInt&);
    friend int operator>=(const LongInt&, const LongInt&);
    friend int operator==(const LongInt&, const LongInt&);
    friend int operator!=(const LongInt&, const LongInt&);

    friend LongInt operator+(const LongInt&, const LongInt&);
    friend LongInt operator-(const LongInt&, const LongInt&);
    friend LongInt operator*(const LongInt&, const LongInt&);
    friend LongInt operator/(const LongInt&, const LongInt&);
    friend LongInt operator%(const LongInt&, const LongInt&);

    friend LongInt operator++(LongInt&);     // prefix ++
    friend LongInt operator++(LongInt&,int); // postfix ++

    friend ostream& operator<<(ostream&, const LongInt&);

private:
    long num_digits;
    char *num; // signed char num[] -- each char holds one digit.
	       // The low order digit is in  num[0] .
    int compare(const LongInt& m2) const;
    LongInt partial(int d, long shift) const;
};
// ======================= Long_Arithmetic.c ===================================
#include <stream.h>
#include "Long_Arithmetic.h"

LongInt::LongInt(long n) {
    if (n < 0) n = 0; // or call an error routine
    num_digits = 1;   // zero gets one digit as well

    long i;
    long m = n;

    // Determine how much storage is needed.
    while ((m = m/10) != 0) num_digits++;

    num = new char[num_digits];
    for (i = 0; i < num_digits; i++) {
	num[i] = char(n % 10);
	n /= 10;
    }
} // LongInt(long)

LongInt::LongInt(const LongInt& n) {
    num_digits = n.num_digits;
    // there can't be an old one to destroy
    num = new char[n.num_digits]; // get sufficient storage -- possible
				  // less than used by  n

    for (long i = 0; i < num_digits; i++) num[i] = n.num[i];
	//  *this = n;  won't work, as this copies only the pointer in  num .
} // LongInt(const LongInt&)

LongInt::~LongInt() { delete num; }

LongInt& LongInt::operator=(const LongInt& n) {
    if (&n == this) return *this; // a special case

    num_digits = n.num_digits;
    delete num;                   // recover the old storage
    num = new char[n.num_digits]; // get sufficient storage

    for (long i = 0; i < num_digits; i++) num[i] = n.num[i];
	//  *this = n;  won't work, as this copies only the pointer in  num .

    return *this; /* This actually calls the copy-constructor, which makes
		     a fresh copy.  This is wasteful, since 99% of the time
		     it will just get thrown away anyway, but if we want
		     to make = a right associative operator, like it normally
		     is in C++, we have to do this. */
} // operator=

ostream& operator<<(ostream& output, const LongInt& n) {
    // Effectively, perform  writeln(n)  with commas inserted in the
    // appropriate places for readability.

    /* Ground rules for pretty printing:
         Commas are printed after every block of three digits.
         Numbers that take more than one line to print are justified so that
           the commas line up.
         The short line is the last, which holds the least significant digits.
         " ...more" is used to indicate a number continues on the next line.
         Things can go haywire if the user has printed something on the line
           before calling  WriteNum .
    */

    /* The maximum line length is  4*k + 8, where  k  is the
       number of blocks of three that can appear on a line.  This is
       used to calculate  MAXGROUPSOFTHREE .
    */
    const int MAXGROUPSOFTHREE = 17; // Don't push to column 80, as some
				     // terminals and printers have trouble
				     // with this.
    long j = n.num_digits; // number of digits left to write out -- used for
			   // comma placement
    int i = 0; // number of groups of three written on a line

    // Write out any blanks needed to align commas on multiline numbers.
    switch (n.num_digits % 3) {
      case 0:                 break;
      case 1: output << "  "; break;
      case 2: output << " ";  break;
    }
    while (j--) { // count one ahead of subscript   while (j > 0) { j--; ...
	output << int(n.num[j]); // print digit (field width is 1)
	if (j % 3 == 0 && j != 0) { // finished a group of three and
				    // didn't finish the number
            output << ",";
            if (++i == MAXGROUPSOFTHREE) { // another groupt of three written
                output << " ...more\n";      // is line done?
                i = 0;
	    }
        }
    }
    output << "\n";

    return output;
} // operator<<

int LongInt::compare(const LongInt& m2) const {
    // the asymmetry is annoying, but we wish to keep it private
    if (this->num_digits < m2.num_digits) return -1;
    else if (this->num_digits > m2.num_digits) return 1;
    else { // m1 and m2 are the same length
	for (long i = this->num_digits - 1; i >= 0; i--)
	    if (this->num[i] < m2.num[i]) return -1;
	    else if (this->num[i] > m2.num[i]) return 1;
	return 0;
    }
} // int compare(const LongInt& m2)

int operator<=(const LongInt& m1, const LongInt& m2) {
    return m1.compare(m2) <= 0;
}

LongInt operator+(const LongInt& m1, const LongInt& m2) {
    LongInt n;
    const long max =
        m1.num_digits > m2.num_digits ? m1.num_digits : m2.num_digits;
    const long min =
        m1.num_digits < m2.num_digits ? m1.num_digits : m2.num_digits;
    int carry = 0;

    delete n.num; // n was zero
    n.num = new char[max+1];

    carry = 0;
    for (long i = 0; i < min; i++) { // two numbers to add
	n.num[i] = m1.num[i] + m2.num[i] + carry;
	if (n.num[i] > 9) {
	    n.num[i] -= 10;
	    carry = 1;
	} else
	    carry = 0;
    }
    for ( ; i < m1.num_digits ; i++) { // toss in leftover portion of m1
	n.num[i] = m1.num[i] + carry;
	if (n.num[i] > 9) {
	    n.num[i] -= 10;
	    carry = 1;
	} else
	    carry = 0;
    }
    for ( ; i < m2.num_digits ; i++) { // toss in leftover portion of m1
	n.num[i] = m2.num[i] + carry;
	if (n.num[i] > 9) {
	    n.num[i] -= 10;
	    carry = 1;
	} else
	    carry = 0;
    }
    if (carry == 1) {
	n.num[i] = carry;
	i++;
    }
    n.num_digits = i;
    return n; // unfortunately, this does a copy
} // operator+

LongInt LongInt::partial(int d, long shift) const {
    LongInt n;
    int carry = 0;
    int next;

    if (d == 0) return n;
    delete n.num; // n was zero
    n.num = new char[this->num_digits + shift + 1]; // worst case
    for (long i = 0; i < shift; i++) n.num[i] = 0;
    for (long j = 0; j < this->num_digits; j++, i++) {
	next = this->num[j]*d + carry;
	n.num[i] = next % 10;
	carry = next / 10;
    }
    if (carry > 0) {
	n.num[i] = carry;
	i++;
    }
    n.num_digits = i;
    return n;
}

LongInt operator*(const LongInt& m1, const LongInt& m2) {
    LongInt n = 0; // partial product

    // 0 * anything = 0 -- a special case
    if (m1.num_digits == 1 && m1.num[0] == 0) return n;

    for (long i = 0; i < m2.num_digits; i++)
	n = n + m1.partial(m2.num[i],i);

    return n; // unfortunately, this does a copy
} // operator*

LongInt operator++(LongInt& m1, int) {
    LongInt m2 = m1;
    m1 = m1 + 1;
    return m2;
} // operator++ (postfix)

LongInt operator++(LongInt& m1) {
    m1 = m1 + 1;
    return m1;
} // operator++ (prefix)
Newsgroups: comp.lang.c++