Book Image

C++ Windows Programming

By : Stefan Björnander
Book Image

C++ Windows Programming

By: Stefan Björnander

Overview of this book

It is critical that modern developers have the right tools to build practical, user-friendly, and efficient applications in order to compete in today’s market. Through hands-on guidance, this book illustrates and demonstrates C++ best practices and the Small Windows object-oriented class library to ease your development of interactive Windows applications. Begin with a focus on high level application development using Small Windows. Learn how to build four real-world applications which focus on the general problems faced when developing graphical applications. Get essential troubleshooting guidance on drawing, spreadsheet, and word processing applications. Finally finish up with a deep dive into the workings of the Small Windows class library, which will give you all the insights you need to build your own object-oriented class library in C++.
Table of Contents (22 chapters)
C++ Windows Programming
Credits
About the Author
About the Reviewer
www.PacktPub.com
Dedication
Preface
Free Chapter
1
Introduction

Rational numbers


A rational number can be expressed as a fraction of two integers, called the numerator and denominator.

Rational.h

namespace SmallWindows { 
  class NotaRationalNumber : public exception { 
    public: 
      NotaRationalNumber() {/* Empty. */} 
  }; 

The default constructor initializes the numerator and denominator to 0 and 1, respectively. The second constructor takes a string and throws a NotaRationalNumber exception if the string does not hold a valid rational number. The copy constructor and the assignment operator take another rational number. The String conversion operator returns the rational number as a string:

  class Rational { 
    public: 
      Rational(int numerator = 0, int denominator = 1); 
      Rational(const String& text); 
      Rational(const Rational &rational); 
      Rational operator=(const Rational &complex); 
      operator String() const; 
      bool operator==(const Rational &rational) const; 
      bool operator!=(const Rational &rational) const; 
      bool operator< (const Rational &rational) const; 
      bool operator<=(const Rational &rational) const; 
      bool operator> (const Rational &rational) const; 
      bool operator>=(const Rational &rational) const; 
      Rational operator+(const Rational &rational) const; 
      Rational operator-(const Rational &rational) const; 
      Rational operator*(const Rational &rational) const; 
      Rational operator/(const Rational &rational) const; 

A rational number is always normalized when it has been created by the constructor or any of the arithmetic operators: the numerator and the denominator are divided by their Greatest Common Divisor (GCD):

    private: 
      void Normalize(); 
      int GCD(int iNum1, int iNum2); 
      int numerator, denominator; 
  }; 
}; 

Rational.cpp

#include "SmallWindows.h" 

The default constructor initializes the numerator and the denominator, and throws an exception if the denominator is zero. This constructor and the next constructor that takes a string are actually the only places where the denominator can be zero. The following constructors and arithmetic operators always produce a rational number with non-zero denominators:

namespace SmallWindows { 
  Rational::Rational(int numerator /* = 0 */, 
                     int denominator /* = 1 */) 
   :numerator(numerator), 
    denominator(denominator) { 
    if (denominator == 0) { 
      throw NotaRationalNumber(); 
    } 
    Normalize(); 
  } 

Text can hold a rational number in two formats: as an integer followed by a slash (/) and another integer, or as a single integer. We start by initializing the numerator and the denominator to 0 and 1:

  Rational::Rational(const String& text) 
   :numerator(0), 
    denominator(1) { 
    String trimText(Trim(text)); 

First, we try two integers and a slash; we read the numerator, slash, and denominator. Before the slash we set the skipws flag, which causes the stream to skip any potential white spaces before the slash. If we have reached the end of the line, the denominator is not 0, the character read into the slash variable really is a slash, the text holds a rational number, and we have read the numerator and denominator, then we are done and we return:

    { IStringStream totalStream(trimText); 
      TCHAR slash; 
      totalStream >> numerator >> setiosflags(ios::skipws) 
                  >> slash >> denominator; 
      if (totalStream.eof() && (denominator != 0) && 
          (slash == TEXT('/'))) { 
        Normalize(); 
        return; 
      } 
    } 

If using two integers and a slash does not work, we try the case of a single integer. We create a new stream and read the numerator. If we have reached the end of the stream after that, the string holds a valid integer. We let the numerator hold its initialized value, which was 1, and return.

    { IStringStream numeratorStream(trimText); 
      numeratorStream >> numerator; 
      if (numeratorStream.eof()) { 
        return; 
      } 
    } 

If two integers and a slash as well as a single integer both failed, we have to draw the conclusion that the string does not hold a valid rational number, and we throw a NotaRationalNumber exception:

    throw NotaRationalNumber(); 
  } 

The copy constructor simply copies the numerator and denominator of the rational number:

  Rational::Rational(const Rational &rational) 
   :numerator(rational.numerator), 
    denominator(rational.denominator) { 
    // Empty. 
  } 

The assignment operator also copies the numerator and denominator of the rational number and returns its own Rational object (*this):

  Rational Rational::operator=(const Rational &rational) { 
    numerator = rational.numerator; 
    denominator = rational.denominator; 
    return *this; 
  } 

The String conversion operator creates an OStringStream object and looks into the denominator. If it is 1, the rational number can be expressed as a single integer; otherwise, it needs to be expressed as a fraction of the numerator and denominator. Finally, the stream is converted into a string that is returned:

  Rational::operator String() const { 
    OStringStream outStream; 
 
    if (denominator == 1) { 
      outStream << numerator; 
    } 
    else { 
      outStream << numerator << TEXT("/") << denominator; 
    } 
 
    return outStream.str(); 
  } 

As rational numbers are always normalized, we can conclude that two rational numbers are equal if they have the same numerator and denominator:

  bool Rational::operator==(const Rational &rational) const { 
    return (numerator == rational.numerator) && 
           (denominator == rational.denominator); 
  } 
 
  bool Rational::operator!=(const Rational &rational) const { 
    return !(*this == rational); 
  } 

When deciding whether a rational number is smaller than another rational number, in order not to involve floating values, we multiply both sides by the denominator and compare the products:

  bool Rational::operator<(const Rational &rational) const { 
    return ((numerator * rational.denominator) < 
            (rational.numerator * denominator)); 
  } 
 
  bool Rational::operator<=(const Rational &rational) const { 
    return ((*this < rational) || (*this == rational)); 
  } 
 
  bool Rational::operator>(const Rational &rational) const { 
    return !(*this <= rational); 
  } 
 
  bool Rational::operator>=(const Rational &rational) const { 
    return !(*this < rational); 
  } 

When adding two rational numbers, we multiply the numerator by the opposite denominator in each term:

 
  Rational Rational::operator+(const Rational &rational) const { 
    Rational result((numerator * rational.denominator) + 
                    (rational.numerator * denominator), 
                    denominator * rational.denominator); 
    result.Normalize(); 
    return result; 
  } 

When subtracting two rational numbers, we also multiply the numerator by the opposite denominator in each term:

 
  Rational Rational::operator-(const Rational &rational) const { 
    Rational result((numerator * rational.denominator) - 
                    (rational.numerator * denominator), 
                    denominator * rational.denominator); 


    result.Normalize(); 
    return result; 
  } 

When multiplying two rational numbers, we simply multiply the numerators and denominators:

  Rational Rational::operator*(const Rational &rational) const { 
    Rational result(numerator * rational.numerator, 
                    denominator * rational.denominator); 
    result.Normalize(); 
    return result; 
  } 

When dividing two rational numbers, we invert the second operand and then multiply the numerators and denominators:

  Rational Rational::operator/(const Rational &rational) const { 
    assert(rational.numerator != 0); 
    Rational result(numerator * rational.denominator, 
                    denominator * rational.numerator); 
    result.Normalize(); 
    return result; 
  } 

When normalizing the rational number, we first look into the numerator. If it is 0, we set the denominator to 1 regardless of its previous value and return:

  void Rational::Normalize() { 
    if (numerator == 0) { 
      denominator = 1; 
      return; 
    } 

However, if the numerator is not 0, we look into the denominator. If it is less than 0, we switch the sign of both the numerator and denominator so that the denominator is always greater than 0:

    if (denominator < 0) { 
      numerator = -numerator; 
      denominator = -denominator; 
    } 

Then we calculate the Greatest Common Divisor by calling GCD, and then we divide both the numerator and denominator by the Greatest Common Divisor:

    int gcd = GCD(abs(numerator), denominator); 
    numerator /= gcd; 
    denominator /= gcd; 
  } 

The GCD method calls itself recursively by comparing the numbers and subtracting the smaller number from the larger number. When they are equal, we return the number. The GCD algorithm is regarded as the world's oldest non-trivial algorithm.

  int Rational::GCD(int number1, int number2) { 
    if (number1 > number2) { 
      return GCD(number1 - number2, number2); 
    } 
    else if (number1 < number2) { 
      return GCD(number1, number2 - number1); 
    } 
    else { 
      return number1; 
    } 
  } 
};