读书笔记之:C++探秘——68讲贯通C++

第1讲 打磨工具

1. C++/CLI与C++是不同的

第2讲 阅读C++代码

1.核心语言与标准库

C++与C以及其他很多语言类似,区分核心语言与标准库。核心语言和标准库都是标准语言的一部分,不包含这二者的工具套件是不完整的。

二者的区别在于核心语言是自包含的。例如,有些类型是语言内建的,编译器对其提供内在的支持,而其他类型是通过内建类型来定义的,他们在标准库中声明,使用时需要通知编译器将其导入。

第3讲 整数表达式

第17讲 字符集

1. 单词计数:将单词限定为字母和类字母的字符

代码如下:

View Code
#include <iostream>
#include <iomanip>
#include <map>
#include <string>
using namespace std;
int main(){
    map<string,int> counts;
    string word;

    string okay("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"
            "0123456789_");
    while(cin>>word){
        string copy;
        for(string::iterator w(word.begin());w!=word.end();++w)
            if(okay.find(*w)!=string::npos)
                copy.push_back(*w);
            else
            {
                if(!copy.empty())
                    ++counts[copy];
                copy.clear();
            }
//        if(!copy.empty())
//            ++counts[copy];
    }
    size_t longest(0);
   typename map<string,int>::iterator ite;
   for(ite=counts.begin();ite!=counts.end();++ite)
       if(ite->first.size()>longest)
           longest=ite->first.size();
   const int count_size(10);
   for(ite=counts.begin();ite!=counts.end();++ite)
       cout<<setw(longest)<<left<<ite->first<<
           setw(count_size)<<right<<ite->second<<'\n';
}

 

第26讲 项目1:身体质量指数

代码如下:

View Code
/** @file
 * @brief Compute body-mass index.
 
*/
/** @mainpage Project 1 - Body-Mass Index
 * This program is Project 1 in <em>Exploring C++</em>,
 * by Ray Lischner (Apress).
 *
 * Your task is to write a program that reads records,
 * prints the records, and computes some statistics.
 * The program starts by asking for a threshold BMI.
 * Only records with a BMI greater than or equal to the
 * threshold will be printed. Each record consists of a
 * person's name (which might contain spaces), weight in
 * kilograms, height in centimeters (not meters), and
 * the person's sex ('M' or 'F'). Let the user enter
 * the sex in uppercase or lowercase.
 *
 * Print each person's BMI after reading that person's record.
 * After collecting information for everyone, print two tables
 * of the information. One table is for men and the other is
 * for women. Mark records for which the BMI meets or exceeds
 * the threshold with an asterisk after the BMI. After each table,
 * print the mean (average) and median BMI. (Median is the value
 * such that half the BMI values are less than the median and half
 * are greater than the median. If the user enters an even number
 * of records, take the average of the two values in the middle.)
 * Compute individual BMI values as integers. Compute the mean and
 * median BMIs as floating point numbers, and print the mean with
 * one place after the decimal point.
 *
 * Body-mass index is defined as weight in kg/(height in m)<sup>2</sup>,
 * converted to a unitless number.
 *
 * Source file:
 * @link bmi.cpp bmi.cpp @endlink
 * @author Ray Lischner
 
*/

#include <algorithm>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <istream>
#include <limits>
#include <locale>
#include <ostream>
#include <string>
#include <vector>

/** @brief Compute the body-mass index (BMI).
 * The height is in centimeters, so the computed value
 * is off by 10,000. Integer division truncates, but we
 * want to round off to nearest, so add 0.5 and then
 * truncate by casting to an integer.
 *
 * @param height The person's height in centimeters.
 * @param weight The person's weight in kilograms.
 * @return the person's BMI, rounded to an integer
 
*/
int compute_bmi(int height, int weight)
{
   return static_cast<int>(weight * 10000 / (height * height) + 0.5);
}

/** @brief Skip the rest of the input line. */
void skip_line()
{
  std::cin.ignore(std::numeric_limits<int>::max(), '\n');
}

/** @brief Get a single person's record.
 * Store the name, height, weight, and sex in the supplied vectors.
 * @param names array of persons' full names
 * @param heights array of height in centimeters
 * @param weights array of weight in kilograms
 * @param sexes array of persons' sex: 'M' means male and 'F' means female
 * @return true to continue reading records or false to stop
 
*/
bool get_record(std::vector<std::string>& names,
                std::vector<int>& heights,
                std::vector<int>& weights,
                std::vector<char>& sexes)
{
   std::string name;
   int height(0);
   int weight(0);
   char sex('?');

  std::cout << "Name " << names.size()+1 << "";
  if (not std::getline(std::cin, name))
    return false;

  // Enforce minimal sanity check on the height, which must be
  
// between 10 and 300 cm, or baby- to giant-size.
  int const min_height(10);
  int const max_height(300);
  std::cout << "Height (cm): ";
  if (not (std::cin >> height))
    return false;
  skip_line();
  if (height < min_height or height > max_height)
  {
    std::cout << "Invalid height. Aborting.\n";
    return false;
  }

  // Enforce minimal sanity check on the weight, which must
  
// be between premature-baby and giant size.
  const int min_weight(1);
  const int max_weight(500);
  std::cout << "Weight (kg): ";
  if (not (std::cin >> weight))
    return false;
  skip_line();
  if (weight < min_weight or weight > max_weight)
  {
    std::cout << "Invalid weight. Aborting.\n";
    return false;
  }

  std::cout << "Sex (M or F): ";
  if (not (std::cin >> sex))
    return false;
  skip_line();
  sex = std::toupper(sex, std::locale());
  if (sex != 'M' and sex != 'F')
  {
    std::cout << "Invalid sex. Aborting.\n";
    return false;
  }

  // All information has now been collected, so
  
// append it all to the respective vectors.
  names.push_back(name);
  heights.push_back(height);
  weights.push_back(weight);
  sexes.push_back(sex);

  return true;
}

/** @brief Print a table.
 * Print a table of height, weight, sex, BMI, and name.
 * Print only records for which sex matches @p sex.
 * At the end of each table, print the mean and median BMI.
 *
 * @param sex the sex to match
 * @param heights the array of heights
 * @param weights the array of weights
 * @param bmis the array of BMIs
 * @param sexes the array of sexes
 * @param names the array of names
 * @param threshold print only elements for which BMI > this
 
*/
void print_table(char sex,
                 std::vector<int>         const& heights,
                 std::vector<int>         const& weights,
                 std::vector<int>         const& bmis,
                 std::vector<char>        const& sexes,
                 std::vector<std::stringconst& names,
                 int                      threshold)
{
  std::cout << "Ht(cm) Wt(kg) Sex  BMI  Name\n";

  float bmi_sum(0);
  long int bmi_count(0);
  std::vector<int> tmpbmis; // store only the BMIs that are printed
                            
// to compute the median
  for (std::vector<int>::size_type i(0); i != heights.size(); ++i)
    if (sexes.at(i) == sex)
    {
      bmi_sum = bmi_sum + bmis.at(i);
      ++bmi_count;
      tmpbmis.push_back(bmis.at(i));
      std::cout << std::setw(6) << heights.at(i)
                << std::setw(7) << weights.at(i)
                << std::setw(3) << sexes.at(i)
                << std::setw(6) << bmis.at(i);
      if (bmis.at(i) >= threshold)
        std::cout << '*';
      else
        std::cout << ' ';
      std::cout << ' ' << names.at(i) << '\n';
    }

  // If the vectors are not empty, print basic statistics.
  if (bmi_count != 0)
  {
    std::cout << "Mean BMI = "
              << std::setprecision(1) << std::fixed << bmi_sum / bmi_count
              << '\n';

    // Median BMI is trickier. The easy way is to sort the
    
// array and pick out the middle item or items.
    std::sort(tmpbmis.begin(), tmpbmis.end());
    std::cout << "Median BMI = ";
    // Index of median item.
    int i(tmpbmis.size() / 2);
    if (tmpbmis.size() % 2 == 0)
      std::cout << (tmpbmis.at(i) + tmpbmis.at(i-1)) / 2.0 << '\n';
    else
      std::cout << tmpbmis.at(i) << '\n';
  }
}

/** @brief Main program to compute BMI. */
int main()
{
  std::locale::global(std::locale(""));
  std::cout.imbue(std::locale());
  std::cin.imbue(std::locale());

  std::vector<std::string> names;
  std::vector<int>         heights;
  std::vector<int>         weights;
  std::vector<char>        sexes;
  std::vector<int>         bmis;
  int threshold;

  std::cout << "Enter threshold BMI: ";
  if (not (std::cin >> threshold))
    return EXIT_FAILURE;
  skip_line();

  std::cout << "Enter name, height (in cm),"
               " and weight (in kg) for each person:\n";
  while (get_record(names, heights, weights, sexes))
  {
    int bmi(compute_bmi(heights.back(), weights.back()));
    bmis.push_back(bmi);
    std::cout << "BMI = " << bmi << '\n';
  }

  // Print the data.
  std::cout << "\n\nMale data\n";
  print_table('M', heights, weights, bmis, sexes, names, threshold);
  std::cout << "\nFemale data\n";
  print_table('F', heights, weights, bmis, sexes, names, threshold);
}

 

第33讲 访问级别

1. 简单旧式数据

第37讲 类与类型

1. RAAI资源获取即为初始化

第43讲 异常

1. 程序栈/执行栈/调用栈/堆栈帧

原书给出的代码:

View Code
#include <exception>
#include <iostream>
#include <istream>
#include <ostream>
#include <string>

/// Make visual the construction and destruction of objects.
class visual
{
public:
  visual(std::string const& what)
  : id_(serial_), what_(what)
  {
    ++serial_;
    print("");
  }
  visual(visual const& ex)
  : id_(ex.id_), what_(ex.what_)
  {
    print("copy ");
  }
  ~visual()
  {
    print("~");
  }
  void print(std::string const& label)
  const
  {
    std::cout << label << "visual(" << what_ << "" << id_ << ")\n";
  }
private:
  static int serial_;
  int const id_;
  std::string const what_;
};

int visual::serial_(0);

void count_down(int n)
{
  std::cout << "start count_down(" << n << ")\n";
  visual v("count_down local");
  try
  {
    if (n == 3)
      throw visual("exception");
    else if (n > 0)
      count_down(n - 1);
  }
  catch (visual ex)
  {
    ex.print("catch ");
    throw;
  }
  std::cout << "end count_down(" << n << ")\n";
}

int main()
{
  try
  {
    count_down(2);
    count_down(4);
  }
  catch (visual const ex)
  {
    ex.print("catch ");
  }
  std::cout << "All done!\n";
}

 为了便于查看,对代码做了简单的修改,结果如上图所示:

View Code
 1 #include <exception>
 2 #include <iostream>
 3 #include <istream>
 4 #include <ostream>
 5 #include <string>
 6 
 7 /// Make visual the construction and destruction of objects.
 8 class visual
 9 {
10 public:
11   visual(std::string const& what)
12   : id_(serial_), what_(what),preid(-1)
13   {
14     ++serial_;
15     print("");
16   }
17   visual(visual const& ex)
18   : id_(serial_), what_(ex.what_),preid(ex.id_)
19   {
20     ++serial_;print("copy ");
21   }
22   ~visual()
23   {
24     print("~");
25   }
26   void print(std::string const& label)
27   const
28   {
29     std::cout << label << "visual(" << what_ << ": id=" << id_ << ",preid= "<<preid<<")\n";
30   }
31 private:
32   static int serial_;
33   int const id_;int preid;
34   std::string const what_;
35 };
36 
37 int visual::serial_(0);
38 
39 void count_down(int n)
40 {
41   std::cout << "start count_down(" << n << ")\n";
42   visual v("count_down local");
43   try
44   {
45     if (n == 3)
46       throw visual("exception");
47     else if (n > 0)
48       count_down(n - 1);
49   }
50   catch (visual ex)
51   {
52     ex.print("catch-A ");
53     throw;
54   }
55   std::cout << "end count_down(" << n << ")\n";
56 }
57 
58 int main()
59 {
60   try
61   {
62     count_down(2);
63     count_down(4);
64   }
65   catch (visual const ex)
66   {
67     ex.print("catch-B ");
68   }
69   std::cout << "All done!\n";
70 }

 第45讲 项目2:定点数

代码如下:

(1)类定义:

View Code
/** @file
 * @brief Fixed-point numbers.
 
*/
/** @mainpage Project 2 - FIxed-Point Numbers
 * This program is Project 2 in <em>Exploring C++</em>,
 * by Ray Lischner (Apress).
 *
 * Your task for Project 2 is to implement a simple fixed-point
 * number class. The class represents fixed-point numbers using
 * an integer type. The number of places after the decimal point
 * is a fixed constant, four. For example, represent the number
 * 3.1415 as the integer 31415 and 3.14 as 31400. You must overload
 * the arithmetic, comparison, and I/O operators to maintain the
 * fixed-point fiction.
 *
 * Source files:
 *  - @link fixed.hpp fixed.hpp @endlink
 *  - @link fixed.cpp fixed.cpp @endlink
 *  - @link test.cpp test.cpp @endlink: test program
 *  - @link test.hpp test.hpp @endlink: test header (see Exploration 28)
 *  - @link ioflags.hpp ioflags.hpp @endlink: save and restore I/O stream flags (Exploration 37)
 *
 * @author Ray Lischner
 
*/

#ifndef FIXED_HPP_
#define FIXED_HPP_

#include <istream>
#include <ostream>
#include <string>

/** @brief Implement a fixed-point number class.
 * Values have @c places places after the decimal point.
 * All arithmetic follows the usual rules.
 
*/
class fixed
{
public:
  typedef int value_type;                    ///< Type of the actual value

  static int const places = 4;               ///< number of decimal places
  static value_type const places10 = 10000;  ///< 10<sup>places</sup>

  
/// Default constructor initializes to zero.
  fixed() : value_(0) {}

  /// Construct from separate integer and fractional parts,
  
/// e.g., initialize to 123.45 with fixed(123, 45). Initialize
  
/// to 12.07 with fixed(12, 7).
  fixed(value_type integer, value_type fraction);

  /// Construct from an integer, with no fraction.
  fixed(value_type integer);

  /// Construct by rounding off a floating point number.
  fixed(double value)
  : value_(static_cast<value_type>(value * places10 + (value < 0 ? -0.5 : 0.5)))
  {}

  /// Convert to a string.
  
/// @returns a string representation of the value, e.g., "123.04"
  std::string as_string() const;
  /// Read from a stream.
  
/// Overwrite this value with the value read from the stream.
  
/// @param strm the stream to read
  
/// @returns true for success or false for failure
  bool read(std::istream& strm);
  /// Convert to long double.
  double as_long_double() const { return static_cast<long double>(value()) / places10; }
  /// Convert to double.
  double as_double() const { return static_cast<double>(value()) / places10; }
  /// Convert to float
  float as_float() const { return static_cast<float>(value()) / places10; }
  /// Return just the integer part, rounded off to the nearest integer.
  
/// If the value lies equidistant between two integers, round even
  
/// numbers up and odd numbers down (banker's rounding).
  value_type round() const;

  /// Return the integer part (which is the same as trunc()).
  value_type integer() const { return value() / places10; }
  /// Return the fractional part, e.g., 3 for 12.03
  value_type fraction() const;

  /// Addition assignment operator
  fixedoperator+=(fixed f);
  /// Subtraction assignment operator
  fixedoperator-=(fixed f);
  /// Multiplication assignment operator
  fixedoperator*=(fixed f);
  /// Division assignment operator
  fixedoperator/=(fixed f);

  /// Negate this value.
  void negate();

  /// Pre-increment
  fixedoperator++();
  /// Post-increment
  fixed operator++(int);
  /// Pre-decrement
  fixedoperator--();
  /// Post-decrement
  fixed operator--(int);

  /// Return the internal value.
  value_type value()    const { return value_; }
private:
  /// Reduce frac to the range [0, places10) by discarding digits to the right.
  value_type reduce(value_type frac);
  value_type value_;
};

/// Read a fixed value
std::istream& operator>>(std::istream& strm, fixed& f);
/// Write a fixed value
std::ostream& operator<<(std::ostream& strm, fixed f);

/// Add fixed values
fixed operator+(fixed a, fixed b);
/// Subtract fixed values
fixed operator-(fixed a, fixed b);
/// Multiply fixed values
fixed operator*(fixed a, fixed b);
/// Divide fixed values
fixed operator/(fixed a, fixed b);
/// Negate a fixed value
fixed operator-(fixed a);

/// Compare fixed values for equality by comparing the underlying values.
bool operator==(fixed a, fixed b);
/// Compare fixed values for inequality by comparing the underlying values.
bool operator!=(fixed a, fixed b);
/// Compare fixed values for less-than by comparing the underlying values.
bool operator<(fixed a, fixed b);
/// Compare fixed values for greater-than by comparing the underlying values.
bool operator>(fixed a, fixed b);
/// Compare fixed values for less-than-or-equal by comparing the underlying values.
bool operator<=(fixed a, fixed b);
/// Compare fixed values for greater-than-or-equal by comparing the underlying values.
bool operator>=(fixed a, fixed b);

#endif

 

(2)类实现:

View Code
#include <cassert>
#include <cstdlib>
#include <iomanip>
#include <istream>
#include <locale>
#include <ostream>
#include <sstream>
#include <sstream>
#include <stdexcept>
#include <string>

#include "fixed.hpp"
#include "ioflags.hpp"

// Construct a fixed value from an integer part and a fraction part
fixed::fixed(value_type integer, value_type fraction)
{
  if (fraction < 0)
    throw std::invalid_argument("negative fraction not allowed");
  fraction = reduce(fraction);
  if (integer < 0)
    value_ = integer * places10 - fraction;
  else
    value_ = integer * places10 + fraction;
}

// Construct a fixed value from an integer part with no fraction
fixed::fixed(value_type integer)
: value_(integer * places10)
{}

// Get the fraction part
fixed::value_type fixed::fraction()
const
{
  return std::abs(value()) % places10;
}

/// Reduce the fractional part to the range [0, places10).
/// Imagine frac has the format F(G(XY*)?)?.
/// The resulting value is FH, where H == 0 if G is absent,
/// or H == G+1 if X==5 and Y* == 0* and G is odd, or
/// H == G+1 if X>5 or X==5 and Y*>0*, else H == G.
/// In other words, check that frac ends with only zero digits,
/// then a 5, then two more digits (searching from least-significant
/// to most-significant). If so, implement banker's rounding.
/// Otherwise, round GXY* to the nearest value (G+1 or G).
fixed::value_type fixed::reduce(value_type frac)
{
  // First scan for zero digits on the right.
  value_type f(frac);
  while (f >= places10*10 and f % 10 == 0)
  {
    f /= 10;
  }

  if (f >= places10*10)
  {
    int x(0);
    // Loop ended because a non-zero digit was seen so Y* > 0.
    
// Discard the remaining digits, but keep track of the last
    
// digit to be processed (X).
    while (f >= places10)
    {
      x = f % 10;
      f /= 10;
    }
    // Round up if the last digit (X) is 5 or more
    if (x >= 5)
      ++f;
    return f;
  }
  // Else all digits so far are zero. Check how many digits there were,
  
// that is, check whether G, and X at least are present.
  else if (f >= places10)
  {
    // Yes, G and X are present. If X == 5, implement banker's rounding.
    
// Otherwise, round to nearest.
    int x(f % 10);
    f /= 10;
    assert(f < places10);
    if (x == 5)
    {
      // Yes, so implement banker's rounding.
      if (f % 2 != 0)
        ++f;
      return f;
    }
    else if (x < 5)
    {
      // Round down.
      return f;
    }
    else
    {
      // Round up.
      return f + 1;
    }
  }
  // Not enough digits, so nothing to round.
  assert(frac < places10);
  return frac;
}

// Round off to nearest integer.
fixed::value_type fixed::round()
const
{
  const value_type frac(fraction());
  int adjust(value() < 0 ? -1 : +1);
  if (frac > places10/2)
    return integer()+adjust;
  else if (frac < places10/2)
    return integer();
  else if (integer() % 2 == 0)
    return integer();
  else
    return integer()+adjust;
}

// Convert to a string using fixed-point notation.
std::string fixed::as_string()
const
{
  std::ostringstream out;
  out << integer() << '.'
      << std::setfill('0') << std::setw(places) << fraction();
  return out.str();
}

fixedfixed::operator+=(fixed f)
{
  value_ += f.value();
  return *this;
}

fixedfixed::operator-=(fixed f)
{
  value_ -= f.value();
  return *this;
}

fixedfixed::operator*=(fixed f)
{
  value_ = (value_ * f.value()) / places10;
  return *this;
}

fixedfixed::operator/=(fixed f)
{
  value_ = (value_ * places10) / f.value();
  return *this;
}

void fixed::negate()
{
  value_ = -value_;
}

fixedfixed::operator++()
{
  value_ += places10;
  return *this;
}

fixed fixed::operator++(int)
{
  fixed result(*this);
  ++*this;
  return result;
}

fixedfixed::operator--()
{
  value_ -= places10;
  return *this;
}

fixed fixed::operator--(int)
{
  fixed result(*this);
  --*this;
  return result;
}

fixed operator-(fixed a)
{
  a.negate();
  return a;
}

bool fixed::read(std::istream& strm)
{
  ioflags flags(strm);

  value_type integer;
  char decimal;
  if (not (strm >> integer))
    return false;
  strm.unsetf(std::ios_base::skipws);
  if (not (strm >> decimal) or decimal != '.')
  {
    // Just an integer is fine. Push back the non-decimal character,
    
// if there is one, and reset the stream flags to show that
    
// reading the fixed value succeeded.
    strm.unget();
    strm.clear(strm.rdstate() & ~strm.failbit);
    value_ = integer * places10;
    return true;
  }
  else
  {
    value_type fraction(0);
    char c;
    int p(0);
    // Read one extra place for round-off.
    for (;
         p != places+1 and strm >> c and std::isdigit(c, strm.getloc());
         ++p)
    {
        fraction = fraction * 10 + (c - '0');
    }
    // Pad out to the requisite number of decimal places.
    for (; p < places; ++p)
      fraction = fraction * 10;
    // If the loop terminated because the maximum number of decimal
    
// places were read, keep reading the stream to discard excees digits.
    while (strm and std::isdigit(c, strm.getloc()))
      strm >> c;
    // Push back the last, non-digit character read from the stream.
    
// If the stream reached EOF, unget() is harmless.
    strm.unget();
    // Clear failbit because even if reading a character or whatever
    
// failed, reading the fixed value did not.
    strm.clear(strm.rdstate() & ~strm.failbit);
    fraction = reduce(fraction);
    if (integer < 0)
      value_ = integer * places10 - fraction;
    else
      value_ = integer * places10 + fraction;
  }
  return true;
}

std::istream& operator>>(std::istream& strm, fixed& f)
{
  if (not f.read(strm))
    strm.setstate(strm.failbit);
  return strm;
}

std::ostream& operator<<(std::ostream& strm, fixed f)
{
  strm << f.as_string();
  return strm;
}

fixed operator+(fixed a, fixed b)
{
  a += b;
  return a;
}

fixed operator-(fixed a, fixed b)
{
  a -= b;
  return a;
}

fixed operator*(fixed a, fixed b)
{
  a *= b;
  return a;
}

fixed operator/(fixed a, fixed b)
{
  a /= b;
  return a;
}

bool operator==(fixed a, fixed b)
{
  return a.value() == b.value();
}

bool operator!=(fixed a, fixed b)
{
  return not (a == b);
}

bool operator<(fixed a, fixed b)
{
  return a.value() < b.value();
}

bool operator>(fixed a, fixed b)
{
  return b < a;
}

bool operator<=(fixed a, fixed b)
{
  return not (b < a);
}

bool operator>=(fixed a, fixed b)
{
  return not (a < b);
}

 

(3)异常定义:

View Code
#ifndef TEST_HPP_
#define TEST_HPP_

#include <exception>
#include <iostream>
#include <ostream>

// For internal use by the test() macro.
// Turn the macro argument into a character string literal
#define test_stringify(x) #x

// For internal use by the test() macro.
// Report a test failure.
inline void test_failed(char const* expr, char const* file, int line)
{
   std::cerr << file << ", line " << line << ": test failed: " << expr << '\n';
}

// For internal use by the test() macro
// Run a test. Report a failure if the condition is false or
inline void test_run(bool condition, char const* expr, char const* file, int line)
{
  if (not condition)
    test_failed(expr, file, line);
}

// For internal use by the test() macro.
// Report an exception.
inline void test_exception(std::exception const& ex, char const* expr, char const* file, int line)
{
  std::string msg( expr );
  msg += " threw an exception: ";
  msg += ex.what();
  test_failed(msg.c_str(), file, line);
}

/// Test a condition, @p x.
/// If @p x evaluates to @c true the test passes.
/// If not, the test fails, and a message is printed to @c cerr.
/// The text of @p x, plus the file name and line number are printed.
///
/// See Boost.Test for a real test framework
///
/// @param x A condition to test; the condition must be able to be converted implicitly to @c bool.
#define test(x) \
try {\
  test_run(x, test_stringify(x), __FILE__, __LINE__);\
}\
catch(std::exception const& ex)\
{\
  test_exception(ex, test_stringify(x), __FILE__, __LINE__);\
}

#endif

(4)测试代码:

View Code
/** @file test.cpp */
/** Listing 45-1. Testing the fixed Class */
#include <iostream>
#include <istream>
#include <ostream>
#include <sstream>
#include <stdexcept>

#include "test.hpp"
#include "fixed.hpp"

int main()
{
  fixed f1;
  test(f1.value() == 0);
  fixed f2(1);
  test(f2.value() == 10000);
  fixed f3(314162);
  test(f3.value() == 31416);
  fixed f4(214159265);
  test(f4.value() == 21416);
  test(f2 + f4 == f1 + f3);
  test(f2 + f4 <= f1 + f3);
  test(f2 + f4 >= f1 + f3);
  test(f1 < f2);
  test(f1 <= f2);
  test(f1 != f2);
  test(f2 > f1);
  test(f2 >= f1);
  test(f2 != f1);

  test(f2 + f4 == f3 - f1);
  test(f2 * f3 == f3);
  test(f3 / f2 == f3);
  f4 += f2;
  test(f3 == f4);
  f4 -= f1;
  test(f3 == f4);
  f4 *= f2;
  test(f3 == f4);
  f4 /= f2;
  test(f3 == f4);

  test(-f4 == f1 - f4);
  test(-(-f4) == f4);
  --f4;
  test(f4 + 1 == f3);
  f4--;
  test(f4 + 2 == f3);
  ++f4;
  test(f4 + 1 == f3);
  f4++;
  test(f4 == f3);
  ++f3;
  test(++f4 == f3);
  test(f4-- == f3);
  test(f4++ == --f3);
  test(--f4 == f3);

  test(f4 / f3 == f2);
  test(f4 - f3 == f1);

  test(f4.as_string() == "3.1416");
  test(f4.integer() == 3);
  f4 += fixed(0,4584);
  test(f4 == 3.6);
  test(f4.integer() == 3);
  test(f4.round() == 4);

  test(f3.integer() == 3);
  test((-f3).integer() == -3);
  test(f3.fraction() == 1416);
  test((-f3).fraction() == 1416);

  test(fixed(7,4999).round() == 7);
  test(fixed(7,5000).round() == 8);
  test(fixed(7,5001).round() == 8);
  test(fixed(7,4999).round() == 7);
  test(fixed(8,5000).round() == 8);
  test(fixed(8,5001).round() == 9);

  test(fixed(123,2345500) == fixed(123,2346));
  test(fixed(123,2345501) == fixed(123,2346));
  test(fixed(123,2345499) == fixed(123,2345));
  test(fixed(123,2346500) == fixed(123,2346));
  test(fixed(123,2346501) == fixed(123,2347));
  test(fixed(123,2346499) == fixed(123,2346));
  test(fixed(123,2346400) == fixed(123,2346));
  test(fixed(123,2346600) == fixed(123,2347));

  test(fixed(-7,4999).round() == -7);
  test(fixed(-7,5000).round() == -8);
  test(fixed(-7,5001).round() == -8);
  test(fixed(-7,4999).round() == -7);
  test(fixed(-8,5000).round() == -8);
  test(fixed(-8,5001).round() == -9);

  test(fixed(-3.14159265).value() == -31416);
  test(fixed(123,456789).value() == 1234568);
  test(fixed(123,4).value() == 1230004);
  test(fixed(-10,1111).value() == -101111);

  std::ostringstream out;
  out << f3 << " 3.14159265 " << fixed(-10,12) << " 3 421.4 end";
  fixed f5;
  std::istringstream in(out.str());
  test(in >> f5);
  test(f5 == f3);
  test(in >> f5);
  test(f5 == f3);
  test(in >> f5);
  test(f5.value() == -100012);
  test(in >> f5);
  test(f5.value() == 30000);
  test(in >> f5);
  test(f5.value() == 4214000);
  test(not (in >> f5));

  test(fixed(31.4159265) == fixed(314159));
  test(fixed(31.41595) == fixed(314160));

  bool okay(false);
  try {
    fixed f6(1, -1);
  } catch (std::invalid_argument const& ex) {
    okay = true;
  } catch (...) {
  }
  test(okay);
}

第51讲 容器

1. 各种容器的复杂度

2. C++技术报告TR1

第54讲 文件I/O

1. 整数/浮点等转化为字符串

#include <ostream> // for the << operator
#include <sstream> // for ostringstream
#include <string>  // for string

template<class T>
std::string to_string(T const& obj)
{
  std::ostringstream out;
  out << obj;
  return out.str();
}

2. 从字符串得到整数/浮点数

#include <istream> // for the >> operator
#include <sstream> // for ostringstream
#include <string>  // for string
#include "conversion_error.hpp"

template<class T>
T from_string(std::string const& str)
{
  std::istringstream in(str);
  T result;
  if (in >> result)
    return result;
  else
    throw conversion_error(str);
}

 

第55讲 项目3:货币类型

currency.hpp:

View Code
/** @file currency.hpp
 * Implement a currency type.
 
*/

#ifndef CURRENCY_HPP_
#define CURRENCY_HPP_

#include <iomanip>
#include <istream>
#include <locale>
#include <ostream>
#include <sstream>
#include <string>
#include <stdexcept>

#include "ioflags.hpp"
#include "fixed.hpp"
#include "rational.hpp"

/** Class to represent a currency value in the global locale. */
template<class T=longint N=2>
class currency
{
public:
  typedef T int_type;                    ///< Storage type
  typedef fixed<T,N> value_type;         ///< Type of the actual value

  
/// Default constructor initializes the value to zero.
  currency() : value_() {}
  /// Initialize the value to @p integer
  
/// @param integer The integer initial value; the fractional part is zero.
  currency(T integer) : value_(integer) {}
  /// Initialize the value.
  
/// The interpretation of the fractional part depends on @p N.
  
/// For example, if @p N is 2, a @p fraction of 9 represents 0.09,
  
/// buf if @p N is 5, @p fraction of 9 means 0.0009.
  
/// @param integer The integer part of the initial value
  
/// @param fraction The fractional part of the initial value
  currency(T integer, T fraction) : value_(integer, fraction) {}
  /// Initialize from a floating point number.
  
/// @param value the initial value
  currency(double value) : value_(value) {}

  /// Copy a value that uses a different precision.
  template<class U, int M>
  currency(currency<U, M> const& rhs): value_(rhs.value()) {}

  /// Assign a value that uses a different precision.
  template<class U, int M>
  currency& operator=(currency<U, M> rhs)
  {
    value_ = rhs.value();
    return *this;
  }

  /// Convert to a string.
  
/// @returns a string representation of the value, e.g., "$123.04"
  std::string as_string() const;
  /// Overwrite this value with the value read from the stream.
  
/// The value in the stream must have the correct number of digits.
  
/// If the showbase flag is set, the currency symbol must be present.
  
/// @param strm Input stream
  
/// @return true if the read is success and @c *this has been modified,
  
/// or false if the read fails. Check @p strm for details.
  template<class Char, class Traits>
  bool read(std::basic_istream<Char, Traits>& strm);

  /// Convert the value to a different numeric type.
  
/// Typically, the other type is a floating-point type.
  template<class U>
  /// Convert to some other type, especially floating point.
  U convert() const { return value().convert<U>(); }

  /// Round off to the nearest integer, using banker's rounding.
  int_type round() const { return value().round(); }

  /// Return the integer part (which is the same as trunc()).
  int_type integer()  const { return value().integer(); }
  /// Return the fractional part, to @p M places.
  template<int M>
  int_type fraction() const { return value().fraction<M>(); }
  /// Return the fractional part.
  int_type fraction() const { return value().fraction(); }

  /// Addition operator.
  
/// @param c the value to add
  
/// @return @c *this
  currency& operator+=(currency c);
  /// Subtraction operator.
  
/// @param c the value to subtract
  
/// @return @c *this
  currency& operator-=(currency c);
  /// Multiplication operator.
  
/// @param m the value to multiply
  
/// @return @c *this
  currency& operator*=(value_type m);
  /// Multiplication operator.
  
/// @param m the value to multiply
  
/// @return @c *this
  currency& operator*=(int_type m);
  /// Division operator.
  
/// @param m the divisor
  
/// @return @c *this
  currency& operator/=(value_type m);
  /// Division operator.
  
/// @param m the divisor
  
/// @return @c *this
  currency& operator/=(int_type m);

  /// Negate this value.
  void negate();

  /// Pre-increment operator.
  currency& operator++();
  /// Post-increment operator.
  currency operator++(int);
  /// Pre-decrement operator.
  currency& operator--();
  /// Post-decrement operator.
  currency operator--(int);

  /// Return the internal value.
  value_type value()    const { return value_; }

private:
  value_type value_;
};

template<class T, int N>
std::string currency<T,N>::as_string()
const
{
  std::ostringstream digits;
  digits.imbue(std::locale::classic());
  digits << integer() << std::setw(value_type::places) << std::setfill('0') << fraction();

  std::ostringstream out;
  std::money_put<charconst& put(std::use_facet<std::money_put<char> >(std::locale()));
  put.put(std::ostreambuf_iterator<char>(out), falseout'0', digits.str());
  return out.str();
}

template<class T, int N>
currency<T,N>& currency<T,N>::operator+=(currency f)
{
  value_ += f.value();
  return *this;
}

template<class T, int N>
currency<T,N>& currency<T,N>::operator-=(currency f)
{
  value_ -= f.value();
  return *this;
}

template<class T, int N>
currency<T,N>& currency<T,N>::operator*=(value_type i)
{
  value_ *= i;
  return *this;
}
template<class T, int N>
currency<T,N>& currency<T,N>::operator*=(int_type i)
{
  value_ *= i;
  return *this;
}

template<class T, int N>
currency<T,N>& currency<T,N>::operator/=(value_type i)
{
  value_ /= i;
  return *this;
}
template<class T, int N>
currency<T,N>& currency<T,N>::operator/=(int_type i)
{
  value_ /= i;
  return *this;
}

template<class T, int N>
void currency<T,N>::negate()
{
  value_ = -value_;
}

template<class T, int N>
currency<T,N>& currency<T,N>::operator++()
{
  ++value_;
  return *this;
}

template<class T, int N>
currency<T,N> currency<T,N>::operator++(int)
{
  currency result(*this);
  ++value_;
  return result;
}

template<class T, int N>
currency<T,N>& currency<T,N>::operator--()
{
  --value_;
  return *this;
}

template<class T, int N>
currency<T,N> currency<T,N>::operator--(int)
{
  currency result(*this);
  --value_;
  return result;
}

template<class T, int N>
template<class CharT, class Traits>
bool currency<T,N>::read(std::basic_istream<CharT,Traits>& strm)
{
  ioflags flags(strm);
  typename std::basic_istream<CharT, Traits>::sentry sentry(strm, false);
  if (not sentry)
    return false;

  std::ios_base::iostate error(std::ios_base::goodbit);
  std::string digits;
  std::money_get<CharT> constget(
      std::use_facet<std::money_get<CharT> >(strm.getloc()));
  get.get(std::istreambuf_iterator<CharT>(strm), std::istreambuf_iterator<CharT>(),
          false, strm, error, digits);

  if ((error & std::ios_base::failbit) != 0)
    return false;

  std::moneypunct<CharT> const& punct(
      std::use_facet<std::moneypunct<CharT> >(strm.getloc()));

  // Set fraction to the rightmost frac_digits() characters of digits.
  std::string fraction(digits.substr(digits.size() - punct.frac_digits(), punct.frac_digits()));
  // Set integer to the remainder of digits.
  std::string integer(digits.substr(0, digits.size() - punct.frac_digits()));

  std::istringstream fixed_stream(integer + "." + fraction);
  return value_.read(fixed_stream);
}

/// Read a currency value
/// @param strm The input stream
/// @param[out] c Store the value here
template<class T, int N, class Char, class Traits>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& strm, currency<T,N>& c)
{
  if (not c.read(strm))
    strm.setstate(strm.failbit);
  return strm;
}

/// Write a currency value
/// @param strm The output stream
/// @param c The value to write
template<class T, int N, class Char, class Traits>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& strm, currency<T,N> c)
{
  typename std::basic_ostream<Char, Traits>::sentry sentry(strm);
  strm << c.as_string();
  return strm;
}

/// Negate a currency value
template<class T, int N>
currency<T,N> operator-(currency<T,N> a)
{
  a.negate();
  return a;
}

/// Add currency values
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
currency<T,N> operator+(currency<T,N> a, currency<T,N> b)
{
  a += b;
  return a;
}

/// Subtract currency values
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
currency<T,N> operator-(currency<T,N> a, currency<T,N> b)
{
  a -= b;
  return a;
}

/// Multiply currency value and an integer
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
currency<T,N> operator*(currency<T,N> a, T b)
{
  a *= b;
  return a;
}

/// Multiply currency value and an integer
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
currency<T,N> operator*(T a, currency<T,N> b)
{
  b *= a;
  return b;
}

/// Divide currency value by an integer
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
currency<T,N> operator/(currency<T,N> a, T b)
{
  a /= b;
  return a;
}

/// Divide currency values to yield a rational result.
/// @param n the numerator
/// @param d the denominator
template<class T, int N>
rational<T> operator/(currency<T,N> n, currency<T,N> d)
{
  // Extract the underlying value of the fixed values. No adjustment
  
// to scaling is needed because the numerator and denominator are
  
// both scaled to the same amount.
  return rational<T>(n.value().value(), d.value().value());
}

/// Compare currency values for equality by comparing the underlying values.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
bool operator==(currency<T,N> a, currency<T,N> b)
{
  return a.value() == b.value();
}
/// Compare currency value and an integer for equality
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
bool operator==(currency<T,N> a, T b)
{
  return a.value() == b;
}
/// Compare currency value and an integer for equality
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
bool operator==(T a, currency<T,N> b)
{
  return a == b.value();
}

/// Compare currency values for inequality.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator!=(currency<T,N> a, currency<T,N> b)
{
  return not (a == b);
}
/// Compare currency value and an integer for inequality
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator!=(currency<T,N> a, T b)
{
  return not (a == b);
}
/// Compare currency value and an integer for inequality
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator!=(T a, currency<T,N> b)
{
  return not (a == b);
}

/// Compare currency values for less-than by comparing the underlying values.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
bool operator<(currency<T,N> a, currency<T,N> b)
{
  return a.value() < b.value();
}
/// Compare a currency value and an integer for less-than.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
bool operator<(currency<T,N> a, T b)
{
  return a.value() < b;
}
/// Compare a currency value and an integer for less-than.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
bool operator<(T a, currency<T,N> b)
{
  return a < b.value();
}

/// Compare currency values for greater-than.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator>(currency<T,N> a, currency<T,N> b)
{
  return b < a;
}
/// Compare a currency value and an integer for greater-than.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator>(currency<T,N> a, T b)
{
  return b < a;
}
/// Compare a currency value and an integer for greater-than.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator>(T a, currency<T,N> b)
{
  return b < a;
}

/// Compare currency values for less-than-or-equal.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator<=(currency<T,N> a, currency<T,N> b)
{
  return not (b < a);
}
/// Compare a currency value and an integer for less-than-or-equal.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator<=(currency<T,N> a, T b)
{
  return not (b < a);
}
/// Compare a currency value and an integer for less-than-or-equal.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator<=(T a, currency<T,N> b)
{
  return not (b < a);
}

/// Compare currency values for greater-than-or-equal.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator>=(currency<T,N> a, currency<T,N> b)
{
  return not (a < b);
}
/// Compare a currency value and an integer for greater-than-or-equal.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator>=(currency<T,N> a, T b)
{
  return not (a < b);
}
/// Compare a currency value and an integer for greater-than-or-equal.
/// @param a The left-hand operand
/// @param b The right-hand operand
template<class T, int N>
inline bool operator>=(T a, currency<T,N> b)
{
  return not (a < b);
}

#endif

fixed.hpp:

View Code
/** @file fixed.hpp */
/** Listing 49-5. Changing fixed from a Class to a Class Template */

#ifndef FIXED_HPP_
#define FIXED_HPP_

#include <cassert>
#include <cmath>
#include <iomanip>
#include <ios>
#include <istream>
#include <locale>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <string>

#include "ioflags.hpp"

/** @brief Implement a fixed-point number class template.
 * Values have @c N places after the decimal point.
 * All arithmetic follows the usual rules.
 
*/
template<class T, int N>
class fixed
{
public:
    typedef T value_type;                    ///< Type of the actual value

    static value_type const places = N;      ///< number of decimal places
    static value_type const places10;        ///< 10<sup>places</sup>

    
/// Default constructor initializes to zero.
    fixed() : value_() {}

    /// Construct from separate integer and fractional parts,
    
/// e.g., initialize to 123.45 with fixed(123, 45). Initialize
    
/// to 12.07 with fixed(12, 7).
    fixed(value_type integer, value_type fraction);

    /// Construct from an integer with no fractional part.
    fixed(value_type integer);

    /// Construct by rounding off a floating point number.
    fixed(double value)
    : value_(static_cast<value_type>(value * places10 + (value < 0 ? -0.5 : 0.5)))
    {}

    /// Convert to a string.
    
/// @returns a string representation of the value, e.g., "123.04"
    std::string as_string() const;
    /// Read from a stream.
    
/// Overwrite this value with the value read from the stream.
    
/// @param strm the stream to read
    
/// @returns true for success or false for failure
    template<class Char, class Traits>
    bool read(std::basic_istream<Char, Traits>& strm);
    /// Convert to long double.
    double as_long_double() const { return static_cast<long double>(value()) / places10; }
    /// Convert to double.
    double as_double() const { return static_cast<double>(value()) / places10; }
    /// Convert to float
    float as_float() const { return static_cast<float>(value()) / places10; }
    /// Return just the integer part, rounded off to the nearest integer.
    
/// If the value lies equidistant between two integers, round even
    
/// numbers up and odd numbers down (banker's rounding).
    value_type round() const;

    /// Return the integer part (which is the same as trunc()).
    value_type integer() const { return value() / places10; }
    /// Return the fractional part, e.g., 3 for 12.03
    value_type fraction() const;

    /// Addition assignment operator
    fixedoperator+=(fixed f);
    /// Subtraction assignment operator
    fixedoperator-=(fixed f);
    /// Multiplication assignment operator
    fixedoperator*=(fixed f);
    /// Division assignment operator
    fixedoperator/=(fixed f);

    /// Negate this value.
    void negate();

    /// Pre-increment
    fixedoperator++();
    /// Post-increment
    fixed operator++(int);
    /// Pre-decrement
    fixedoperator--();
    /// Post-decrement
    fixed operator--(int);

    /// Return the internal value.
    value_type value()    const { return value_; }
private:
    /// Reduce frac to the range [0, places10) by discarding digits to the right.
    value_type reduce(value_type frac);
    value_type value_;
};

template<class T, int N>
typename fixed<T,N>::value_type const fixed<T,N>::places10 = static_cast<typename fixed<T,N>::value_type>(std::pow(10.0double(places)));

// Construct a fixed value from an integer part and a fraction part
template<class T, int N>
fixed<T,N>::fixed(value_type integer, value_type fraction)
{
  if (fraction < T())
    throw std::invalid_argument("negative fraction not allowed");
  fraction = reduce(fraction);
  if (integer < T())
    value_ = integer * places10 - fraction;
  else
    value_ = integer * places10 + fraction;
}

// Construct a fixed value from an integer part with no fraction
template<class T, int N>
fixed<T,N>::fixed(value_type integer)
: value_(integer * places10)
{}

// Get the fraction part
template<class T, int N>
typename fixed<T,N>::value_type fixed<T,N>::fraction()
const
{
  return std::abs(value()) % places10;
}

/// Reduce the fractional part to the range [0, places10).
/// Imagine frac has the format F(G(XY*)?)?.
/// The resulting value is FH, where H == 0 if G is absent,
/// or H == G+1 if X==5 and Y* == 0* and G is odd, or
/// H == G+1 if X>5 or X==5 and Y*>0*, else H == G.
/// In other words, check that frac ends with only zero digits,
/// then a 5, then two more digits (searching from least-significant
/// to most-significant). If so, implement banker's rounding.
/// Otherwise, round GXY* to the nearest value (G+1 or G).
template<class T, int N>
typename fixed<T,N>::value_type fixed<T,N>::reduce(value_type frac)
{
  // First scan for zero digits on the right.
  value_type f(frac);
  while (f >= places10*10 and f % 10 == 0)
  {
    f /= 10;
  }

  if (f >= places10*10)
  {
    int x(0);
    // Loop ended because a non-zero digit was seen so Y* > 0.
    
// Discard the remaining digits, but keep track of the last
    
// digit to be processed (X).
    while (f >= places10)
    {
      x = f % 10;
      f /= 10;
    }
    // Round up if the last digit (X) is 5 or more
    if (x >= 5)
      ++f;
    return f;
  }
  // Else all digits so far are zero. Check how many digits there were,
  
// that is, check whether G, and X at least are present.
  else if (f >= places10)
  {
    // Yes, G and X are present. If X == 5, implement banker's rounding.
    
// Otherwise, round to nearest.
    int x(f % 10);
    f /= 10;
    assert(f < places10);
    if (x == 5)
    {
      // Yes, so implement banker's rounding.
      if (f % 2 != 0)
        ++f;
      return f;
    }
    else if (x < 5)
    {
      // Round down.
      return f;
    }
    else
    {
      // Round up.
      return f + 1;
    }
  }
  // Not enough digits, so nothing to round.
  assert(frac < places10);
  return frac;
}

// Round off to nearest integer.
template<class T, int N>
typename fixed<T,N>::value_type fixed<T,N>::round()
const
{
  const value_type frac(fraction());
  int adjust(value() < 0 ? -1 : +1);
  if (frac > places10/2)
    return integer()+adjust;
  else if (frac < places10/2)
    return integer();
  else if (integer() % 2 == 0)
    return integer();
  else
    return integer()+adjust;
}

// Convert to a string using fixed-point notation.
template<class T, int N>
std::string fixed<T,N>::as_string()
const
{
  std::ostringstream out;
  out << integer() << '.'
      << std::setfill('0') << std::setw(places) << fraction();
  return out.str();
}

template<class T, int N>
fixed<T,N>& fixed<T,N>::operator+=(fixed f)
{
  value_ += f.value();
  return *this;
}

template<class T, int N>
fixed<T,N>& fixed<T,N>::operator-=(fixed f)
{
  value_ -= f.value();
  return *this;
}

template<class T, int N>
fixed<T,N>& fixed<T,N>::operator*=(fixed f)
{
  value_ = (value_ * f.value()) / places10;
  return *this;
}

template<class T, int N>
fixed<T,N>& fixed<T,N>::operator/=(fixed f)
{
  value_ = (value_ * places10) / f.value();
  return *this;
}

template<class T, int N>
void fixed<T,N>::negate()
{
  value_ = -value_;
}

template<class T, int N>
fixed<T,N>& fixed<T,N>::operator++()
{
  value_ += places10;
  return *this;
}

template<class T, int N>
fixed<T,N> fixed<T,N>::operator++(int)
{
  fixed result(*this);
  ++*this;
  return result;
}

template<class T, int N>
fixed<T,N>& fixed<T,N>::operator--()
{
  value_ -= places10;
  return *this;
}

template<class T, int N>
fixed<T,N> fixed<T,N>::operator--(int)
{
  fixed result(*this);
  --*this;
  return result;
}

template<class T, int N>
template<class Char, class Traits>
bool fixed<T,N>::read(std::basic_istream<Char, Traits>& strm)
{
  ioflags flags(strm);

  value_type integer;
  char decimal;
  if (not (strm >> integer))
    return false;
  strm.unsetf(std::ios_base::skipws);
  if (not (strm >> decimal) or decimal != '.')
  {
    // Just an integer is fine. Push back the non-decimal character,
    
// if there is one, and reset the stream flags to show that
    
// reading the fixed value succeeded.
    strm.unget();
    strm.clear(strm.rdstate() & ~strm.failbit);
    value_ = integer * places10;
    return true;
  }
  else
  {
    value_type fraction(0);
    char c;
    int p(0);
    // Read one extra place for round-off.
    for (;
          p != places+1 and strm >> c and std::isdigit(c, strm.getloc());
          ++p)
    {
      fraction = fraction * 10 + (c - '0');
    }
    // Pad out to the requisite number of decimal places.
    for (; p < places; ++p)
      fraction = fraction * 10;
    // If the loop terminated because the maximum number of decimal
    
// places were read, keep reading the stream to discard excees digits.
    while (strm and std::isdigit(c, strm.getloc()))
      strm >> c;
    // Push back the last, non-digit character read from the stream.
    
// If the stream reached EOF, unget() is harmless.
    strm.unget();
    // Clear failbit because even if reading a character or whatever
    
// failed, reading the fixed value did not.
    strm.clear(strm.rdstate() & ~strm.failbit);
    fraction = reduce(fraction);
    if (integer < 0)
      value_ = integer * places10 - fraction;
    else
      value_ = integer * places10 + fraction;
  }
  return true;
}

/// Read a fixed value
template<class T, int N, class Char, class Traits>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& strm, fixed<T,N>& f)
{
  if (not f.read(strm))
    strm.setstate(strm.failbit);
  return strm;
}

/// Write a fixed value
template<class T, int N, class Char, class Traits>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& strm, fixed<T,N> f)
{
  strm << f.as_string();
  return strm;
}

/// Add fixed values
template<class T, int N>
fixed<T,N> operator+(fixed<T,N> a, fixed<T,N> b)
{
  a += b;
  return a;
}

/// Subtract fixed values
template<class T, int N>
fixed<T,N> operator-(fixed<T,N> a, fixed<T,N> b)
{
  a -= b;
  return a;
}

/// Multiply fixed values
template<class T, int N>
fixed<T,N> operator*(fixed<T,N> a, fixed<T,N> b)
{
  a *= b;
  return a;
}

/// Divide fixed values
template<class T, int N>
fixed<T,N> operator/(fixed<T,N> a, fixed<T,N> b)
{
  a /= b;
  return a;
}

/// Negate a fixed value
template<class T, int N>
fixed<T,N> operator-(fixed<T,N> a)
{
  a.negate();
  return a;
}

/// Compare fixed values for equality by comparing the underlying values.
template<class T, int N>
bool operator==(fixed<T,N> a, fixed<T,N> b)
{
  return a.value() == b.value();
}
/// Compare fixed values for equality by comparing the value wih an integer.
template<class T, int N>
bool operator==(T a, fixed<T,N> b)
{
  return a == b.value();
}
/// Compare fixed values for equality by comparing the value wih an integer.
template<class T, int N>
bool operator==(fixed<T,N> a, T b)
{
  return a.value() == b;
}

/// Compare fixed values for inequality by comparing the underlying values.
template<class T, int N>
bool operator!=(fixed<T,N> a, fixed<T,N> b)
{
  return not (a == b);
}
/// Compare fixed values for inequality by comparing the value wih an integer.
template<class T, int N>
bool operator!=(T a, fixed<T,N> b)
{
  return not (a == b);
}
/// Compare fixed values for inequality by comparing the value wih an integer.
template<class T, int N>
bool operator!=(fixed<T,N> a, T b)
{
  return not (a == b);
}

/// Compare fixed values for less-than by comparing the underlying values.
template<class T, int N>
bool operator<(fixed<T,N> a, fixed<T,N> b)
{
  return a.value() < b.value();
}
/// Compare fixed values for less-than by comparing the value wih an integer.
template<class T, int N>
bool operator<(T a, fixed<T,N> b)
{
  return a < b.value();
}
/// Compare fixed values for less-than by comparing the value wih an integer.
template<class T, int N>
bool operator<(fixed<T,N> a, T b)
{
  return a.value() < b;
}

/// Compare fixed values for greater-than by comparing the underlying values.
template<class T, int N>
bool operator>(fixed<T,N> a, fixed<T,N> b)
{
  return b < a;
}
/// Compare fixed values for greater-than by comparing the value wih an integer.
template<class T, int N>
bool operator>(T a, fixed<T,N> b)
{
  return b < a;
}
/// Compare fixed values for greater-than by comparing the value wih an integer.
template<class T, int N>
bool operator>(fixed<T,N> a, T b)
{
  return b < a;
}

/// Compare fixed values for less-than-or-equal by comparing the underlying values.
template<class T, int N>
bool operator<=(fixed<T,N> a, fixed<T,N> b)
{
  return not (b < a);
}
/// Compare fixed values for less-than-or-equal by comparing the value wih an integer.
template<class T, int N>
bool operator<=(T a, fixed<T,N> b)
{
  return not (b < a);
}
/// Compare fixed values for less-than-or-equal by comparing the value wih an integer.
template<class T, int N>
bool operator<=(fixed<T,N> a, T b)
{
  return not (b < a);
}

/// Compare fixed values for greater-than-or-equal by comparing the underlying values.
template<class T, int N>
bool operator>=(fixed<T,N> a, fixed<T,N> b)
{
  return not (a < b);
}
/// Compare fixed values for greater-than-or-equal by comparing the value wih an integer.
template<class T, int N>
bool operator>=(T a, fixed<T,N> b)
{
  return not (a < b);
}
/// Compare fixed values for greater-than-or-equal by comparing the value wih an integer.
template<class T, int N>
bool operator>=(fixed<T,N> a, T b)
{
  return not (a < b);
}

#endif

gcd.hpp:

View Code
#include "gcd.hpp"

int gcd(int n, int m)
{
  if (n < 0)
    n = -n;
  while (m != 0) {
    int tmp(n % m);
    n = m;
    m = tmp;
  }
  return n;
}

gcd.cpp:

View Code
#ifndef GCD_HPP_
#define GCD_HPP_

/// Compute greatest-common-denominator.
/// @param n
/// @param m
int gcd(int n, int m);

#endif

ioflags.hpp:

View Code
#ifndef IOFLAGS_HPP_
#define IOFLAGS_HPP_

/** @file
 * @brief Save and restore I/O stream flags.
 
*/

/** Save and restore I/O stream flags.
 * When a function needs to temporarily alter an I/O stream flags,
 * simply define an object of type @c ioflags. Set whatever flags
 * you want. When the block exits or function returns, the
 * original flags are restored.
 
*/

class ioflags
{
public:
  /// Save the formatting flags from @p stream.
  
/// @param stream The stream that will have its flags modified and restored.
  ioflags(std::basic_ios<char>& stream) : stream_(stream), flags_(stream.flags()) {}
  /// Restore the formatting flags.
  ~ioflags() { stream_.flags(flags_); }
private:
  std::basic_ios<char>& stream_;
  std::ios_base::fmtflags flags_;
};

#endif

rational.hpp:

View Code
#ifndef RATIONAL_HPP_
#define RATIONAL_HPP_

#include <istream>
#include <limits>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <string>

#include "gcd.hpp"
#include "ioflags.hpp"

/// Represent a rational number (fraction) as a numerator and denominator.
template<class T>
class rational
{
public:
  /// Convenience typedef for the integral type of the numerator and denominator.
  typedef T value_type;
  /// Exception class if the denominator is ever zero.
  class zero_denominator : public std::logic_error
  {
  public:
    /// Construct the exception object.
    zero_denominator(std::string const& what) : logic_error(what) {}
  };

  /// Default constructor and constructor from a single value.
  
/// As a default constructor, initializes to zero.
  
/// Otherwise, initializes to the integer @p num.
  
/// @param num The integer value to use as the initial value
  rational(value_type num = 0): numerator_(num), denominator_(1) {}
  /// Construct a rational number
  
/// @param num numerator
  
/// @param den denominator
  
/// @throws zero_denominator if @p den == 0
  rational(value_type num, value_type den);
  /// Initialize the rational number with an approximation of @p r
  
/// @param r the initial value
  rational(double r);
  /// Copy from a different type of rational.
  template<class U>
  rational(rational<U> const& that);

  /// Return the numerator
  value_type numerator()              const { return numerator_; }
  /// Return the denominator
  value_type denominator()            const { return denominator_; }
  /// Convert the rational number to another type, especially floating-point.
  template<class U>
  U as() const { return static_cast<U>(numerator()) / denominator(); }

  /// Assignment of an integer
  rational& operator=(value_type); // optimization to avoid an unneeded call to reduce()

  /// Assignment of a rational with a different size.
  template<class U>
  rational& operator=(rational<U> const& rhs);

  /// Addition assignment operator
  rational& operator+=(rational const& rhs);
  /// Addition assignment operator
  rational& operator+=(value_type const& rhs);

  /// Subtraction assignment operator
  rational& operator-=(rational const& rhs);
  /// Subtraction assignment operator
  rational& operator-=(value_type const& rhs);

  /// Multiplication assignment operator
  rational& operator*=(rational const& rhs);
  /// Multiplication assignment operator
  rational& operator*=(value_type const& rhs);

  /// Division assignment operator
  rational& operator/=(rational const& rhs);
  /// Division assignment operator
  rational& operator/=(value_type const& rhs);

  /// Pre-increment
  rational& operator++();
  /// Pre-decrement
  rational& operator--();
  /// Post-increment
  rational operator++(int);
  /// Post-decrement
  rational operator--(int);

private:
  /// Reduce the numerator and denominator by their GCD.
  void reduce();
  /// Reduce the numerator and denominator, and normalize the signs of both,
  
/// that is, ensure denominator is not negative.
  void normalize();
  /// Scale an integer of type @p U to the value_type. If @p U has more
  
/// digits than @p value_type shift @p value to the right.
  template<class U>
  value_type scale(U value);

  value_type numerator_;
  value_type denominator_;
};

template<class T>
rational<T>::rational(value_type num, value_type den)
: numerator_(num),
  denominator_(den == value_type() ? throw zero_denominator("zero denominator") : den)
{
  normalize();
}

template<class T>
rational<T>::rational(double r)
: numerator_(static_cast<T>(r / 100000)), denominator_(static_cast<T>(100000))
{}

template<class T>
template<class U>
rational<T>::rational(rational<U> const& that)
: numerator_(scale<U>(that.numerator())), denominator_(scale<U>(that.denominator()))
{
  reduce();
}

template<class T>
template<class U>
T rational<T>::scale(U value)
{
  if (std::numeric_limits<T>::digits >= std::numeric_limits<U>::digits)
    return T(value);
  else
    return T(value >> (std::numeric_limits<U>::digits - std::numeric_limits<T>::digits));
}

template<class T>
void rational<T>::normalize()
{
  if (denominator_ < value_type())
  {
    denominator_ = -denominator_;
    numerator_ = -numerator_;
  }
  reduce();
}

template<class T>
void rational<T>::reduce()
{
  value_type div(gcd(numerator(), denominator()));
  if (div == value_type())
    throw zero_denominator("zero denominator");
  numerator_ /= div;
  denominator_ /= div;
}

template<class T>
rational<T>& rational<T>::operator=(T num)
{
  numerator_ = num;
  denominator_ = value_type(1);
  return *this;
}

template<class T>
template<class U>
rational<T>& rational<T>::operator=(rational<U> const& rhs)
{
  numerator_ = scale<U>(rhs.numerator());
  denominator_ = scale<U>(rhs.denominator());
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator+=(rational const& rhs)
{
  numerator_ = numerator() * rhs.denominator() + rhs.numerator() * denominator();
  denominator_ *= rhs.denominator();
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator+=(value_type const& rhs)
{
  numerator_ = numerator() + rhs * denominator();
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator-=(rational const& rhs)
{
  numerator_ = numerator() * rhs.denominator() - rhs.numerator() * denominator();
  denominator_ *= rhs.denominator();
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator-=(value_type const& rhs)
{
  numerator_ = numerator() - rhs * denominator();
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator*=(rational const& rhs)
{
  numerator_ *= rhs.numerator();
  denominator_ *= rhs.denominator();
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator*=(value_type const& rhs)
{
  numerator_ *= rhs;
  reduce();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator/=(rational const& rhs)
{
  if (rhs.numerator() == value_type())
    throw zero_denominator("divide by zero");
  numerator_ *= rhs.denominator();
  denominator_ *= rhs.numerator();
  normalize();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator/=(value_type const& rhs)
{
  if (rhs == value_type())
    throw zero_denominator("divide by zero");
  denominator_ *= rhs;
  normalize();
  return *this;
}

template<class T>
rational<T>& rational<T>::operator++()
{
  numerator_ += denominator();
  return *this;
}

template<class T>
rational<T> rational<T>::operator++(int)
{
  rational result(*this);
  ++*this;
  return result;
}

template<class T>
rational<T>& rational<T>::operator--()
{
  numerator_ -= denominator();
  return *this;
}

template<class T>
rational<T> rational<T>::operator--(int)
{
  rational result(*this);
  --*this;
  return result;
}

/// Negate a rational number
template<class T>
rational<T> operator-(rational<T> const& r)
{
  return rational<T>(-r.numerator(), r.denominator());
}

template<class T>
rational<T> absval(rational<T> const& r)
{
  using namespace std;
  return rational<T>(abs(r.numerator()), r.denominator());
}

/// Addition
template<class T>
rational<T> operator+(rational<T> lhs, rational<T> const& rhs)
{
  lhs += rhs;
  return lhs;
}

/// Addition
template<class T>
rational<T> operator+(rational<T> lhs, T const& rhs)
{
  lhs += rhs;
  return lhs;
}

/// Addition
template<class T>
rational<T> operator+(T const& lhs, rational<T> rhs)
{
  rhs += lhs;
  return rhs;
}

/// Subtraction
template<class T>
rational<T> operator-(rational<T> lhs, rational<T> const& rhs)
{
  lhs -= rhs;
  return lhs;
}

/// Subtraction
template<class T>
rational<T> operator-(rational<T> lhs, T const& rhs)
{
  lhs -= rhs;
  return lhs;
}

/// Subtraction
template<class T>
rational<T> operator-(T const& lhs, rational<T> rhs)
{
  // Gotta be a little tricky.
  rhs += -lhs;
  return -rhs;
}

/// Multiplication
template<class T>
rational<T> operator*(rational<T> lhs, rational<T> const& rhs)
{
  lhs *= rhs;
  return lhs;
}

/// Multiplication
template<class T>
rational<T> operator*(rational<T> lhs, T const& rhs)
{
  lhs *= rhs;
  return lhs;
}

/// Multiplication
template<class T>
    rational<T> operator*(T const& lhs, rational<T> rhs)
{
  rhs *= lhs;
  return rhs;
}

/// Division
template<class T>
rational<T> operator/(rational<T> lhs, rational<T> const& rhs)
{
  lhs /= rhs;
  return lhs;
}

/// Division
template<class T>
rational<T> operator/(rational<T> lhs, T const& rhs)
{
  lhs /= rhs;
  return lhs;
}

/// Division
template<class T>
rational<T> operator/(T const& lhs, rational<T> rhs)
{
  return rational<T>(lhs * rhs.denominator(), rhs.numerator());
}


/// Equality comparison
template<class T, class U>
bool operator==(rational<T> const& a, rational<U> const& b)
{
  return a.numerator() == b.numerator() and
         a.denominator() == b.denominator();
}

/// Equality comparison
template<class T>
bool operator==(rational<T> const& lhs, T rhs)
{
  return lhs.denominator() == 1 and
         lhs.numerator()   == rhs;
}

/// Equality comparison
template<class T>
bool operator==(T lhs, rational<T> const& rhs)
{
  return rhs.denominator() == 1 and
         rhs.numerator()   == lhs;
}

/// Less-than comparison
template<class T>
bool operator<(rational<T> const& a, rational<T> const& b)
{
  return a.numerator() * b.denominator() < b.numerator() * a.denominator();
}

/// Less-than comparison
template<class T>
bool operator<(rational<T> const& a, T const& b)
{
  return a.numerator() < b * a.denominator();
}

/// Less-than comparison
template<class T>
bool operator<(T const& a, rational<T> const& b)
{
  return a * b.denominator() < b.numerator();
}

/// Inequality comparison
template<class T, class U>
inline bool operator!=(rational<T> const& a, rational<U> const& b)
{
  return not (a == b);
}

/// Inequality comparison
template<class T>
inline bool operator!=(rational<T> const& a, T b)
{
  return not (a == b);
}

/// Inequality comparison
template<class T>
inline bool operator!=(T a, rational<T> const& b)
{
  return not (a == b);
}

/// Less-than-or-equal comparison
template<class T>
inline bool operator<=(rational<T> const& a, rational<T> const& b)
{
  return not (b < a);
}

/// Less-than-or-equal comparison
template<class T>
inline bool operator<=(rational<T> const& a, T const& b)
{
  return not (b < a);
}

/// Less-than-or-equal comparison
template<class T>
inline bool operator<=(T const& a, rational<T> const& b)
{
  return not (b < a);
}

/// Greater-than comparison
template<class T>
inline bool operator>(rational<T> const& a, rational<T> const& b)
{
  return b < a;
}

/// Greater-than comparison
template<class T>
inline bool operator>(rational<T> const& a, T const& b)
{
  return b < a;
}

/// Greater-than comparison
template<class T>
inline bool operator>(T const& a, rational<T> const& b)
{
  return b < a;
}

/// Greater-than-or-equal comparison
template<class T>
inline bool operator>=(rational<T> const& a, rational<T> const& b)
{
  return not (b > a);
}

/// Greater-than-or-equal comparison
template<class T>
inline bool operator>=(rational<T> const& a, T const& b)
{
  return not (b > a);
}

/// Greater-than-or-equal comparison
template<class T>
inline bool operator>=(T const& a, rational<T> const& b)
{
  return not (b > a);
}

/// Input operator
template<class T, class Char, class Traits>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, rational<T>& rat)
{
  typename std::basic_istream<Char, Traits>::sentry sentry(infalse);
  ioflags flags(in);

  T n = T();
  if (not (in >> n))
    // Error reading the numerator.
    return in;

  in >> std::noskipws;
  char sep('\0');
  if (not (in >> sep))
    // Error reading the separator character.
    return in;
  else if (sep != '/')
  {
    // Push sep back into the input stream, so the next input operation
    
// will read it.
    in.unget();
    rat = n;
    return in;
  }
  else
  {
    T d = T();
    if (in >> d)
      // Successfully read numerator, separator, and denominator.
      rat = rational<T>(n, d);
  }

  return in;
}

/// Output operator
template<class T, class Char, class Traits>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, rational<T> const& rat)
{
  typename std::basic_ostream<Char, Traits>::sentry sentry(out);
  std::ostringstream stream;
  stream << rat.numerator() << '/' << rat.denominator();
  out << stream.str();
  return out;
}

#endif

test.cpp:

View Code
/** @file test.cpp */
/** Listing 45-1. Testing the fixed Class */
#include <iostream>
#include <istream>
#include <locale>
#include <ostream>
#include <sstream>
#include <stdexcept>

#include "test.hpp"
#include "currency.hpp"

int main(int argc, char** argv)
{
  if (argc >= 2)
    std::locale::global(std::locale(argv[1]));
  else
    std::locale::global(std::locale(""));
  std::cin.imbue(std::locale());
  std::cout.imbue(std::locale());
  typedef currency<long,4> currency4;
  typedef currency<long,2> currency2;
  currency2 c1;
  TEST(c1.value().value() == 0);
  currency2 c2(1L);
  TEST(c2.value().value() == 100);
  currency2 c3(31416);
  TEST(c3.value().value() == 314);
  currency2 c4(6275);
  TEST(c4.value().value() == 628);
  currency2 c5(5279);
  TEST(c5.value().value() == 528);
  TEST(c3 + c3 == c1 + c4);
  TEST(c3 + c3 <= c1 + c4);
  TEST(c3 + c3 >= c1 + c4);
  TEST(c1 < c2);
  TEST(c1 <= c2);
  TEST(c1 != c2);
  TEST(c2 > c1);
  TEST(c2 >= c1);
  TEST(c2 != c1);
  TEST(c2 / 2L == currency2(050));

  TEST(c4 - c5 == c2);
  TEST(c3 * 2L == c4);
  TEST(c4 / 2L == c3);
  c5 += c2;
  TEST(c5 == c4);
  c5 /= 2L;
  TEST(c3 == c5);
  c3 *= 2L;
  TEST(c3 == c4);
  c3 -= c5;
  TEST(c3 == c5);

  TEST(-c4 == c1 - c4);
  TEST(-(-c4) == c4);
  TEST(c3 + c5 == --c4 + c2);
  TEST(c3 + c5 == c4 + c2);
  TEST(c3 + c5 == c4-- + c2);
  TEST(c3 + c5 == c4 + c2 + c2);
  TEST(c3 + c5 == ++c4 + c2);
  TEST(c3 + c5 == c4 + c2);
  TEST(c3 + c5 == c4++ + c2);
  TEST(c3 + c5 == c4);

  c2 *= 2L;
  TEST(c4 / c2 == rational<long>(314100));
  TEST((c4 /= 2L) == c5);
  TEST(c4 == c5);

  TEST(c4.as_string() == "3.14");
  TEST(c4.integer() == 3);
  c4 += currency2(-1,8);
  TEST((c4 == currency2(2.06)));
  TEST(c4.integer() == 2);
  TEST(c4.round() == 2);
  c4 += c2 / 2L;
  TEST(c4.round() == 3);

  TEST(c3.integer() == 3);
  TEST((-c3).integer() == -3);
  TEST(c3.fraction() == 14);
  TEST((-c3).fraction() == 14);

  TEST(currency4(7,4999).round() == 7L);
  TEST(currency4(7,5000).round() == 8L);
  TEST(currency4(7,5001).round() == 8L);
  TEST(currency4(7,4999).round() == 7L);
  TEST(currency4(8,5000).round() == 8L);
  TEST(currency4(8,5001).round() == 9L);

  TEST(currency4(123,2345500) == currency4(123,2346));
  TEST(currency4(123,2345501) == currency4(123,2346));
  TEST(currency4(123,2345499) == currency4(123,2345));
  TEST(currency4(123,2346500) == currency4(123,2346));
  TEST(currency4(123,2346501) == currency4(123,2347));
  TEST(currency4(123,2346499) == currency4(123,2346));
  TEST(currency4(123,2346400) == currency4(123,2346));
  TEST(currency4(123,2346600) == currency4(123,2347));

  TEST(currency4(-3.14159265).value().value() == -31416L);
  TEST(currency4(123,456789).value().value() == 1234568L);
  TEST(currency4(123,4).value().value() == 1230004L);
  TEST(currency4(-10,1111).value().value() == -101111L);

  std::ostringstream out;
  c3 = currency2(314);
  out << std::showbase << c3 << " 3.14 " << currency2(-10,12) << " 3.00 421.40 end";
  currency2 c6;
  std::istringstream in(out.str());
  TEST(in >> c6);
  TEST(c6 == c3);
  c6 = currency2();
  TEST(in >> c6);
  TEST(c6 == c3);
  TEST(in >> c6);
  TEST(c6.value() == -1012L);
  TEST(in >> c6);
  TEST(c6.value() == 300L);
  TEST(in >> c6);
  TEST(c6.value() == 42140L);
  TEST(not (in >> c6));

  TEST(currency2(31.59265) == currency2(3159));
  TEST(currency2(31.595) == currency2(3160));

  // Adjust the following test to match your native locale.
  currency2 big(123456789);
  TEST(big.as_string() == "1,234,567.89");

  bool okay(false);
  try {
    currency2 c7(1, -1);
  } catch (std::invalid_argument const& ex) {
    okay = true;
  } catch (std::exception const& ex) {
    std::cerr << "wrong exception: " << ex.what() << '\n';
  }
  TEST(okay);
}

test.hpp:

View Code
#ifndef TEST_HPP_
#define TEST_HPP_

#include <exception>
#include <iostream>
#include <ostream>

// For internal use by the TEST() macro.
// Turn the macro argument into a character string literal
#define TEST_STRINGIFY(x) #x

// For internal use by the TEST() macro.
// Report a test failure.
inline void test_failed(char const* expr, char const* file, int line)
{
   std::cerr << file << ", line " << line << ": test failed: " << expr << '\n';
}

// For internal use by the TEST() macro
// Run a test. Report a failure if the condition is false or
inline void test_run(bool condition, char const* expr, char const* file, int line)
{
  if (not condition)
    test_failed(expr, file, line);
}

// For internal use by the TEST() macro.
// Report an exception.
inline void test_exception(std::exception const& ex, char const* expr, char const* file, int line)
{
  std::string msg( expr );
  msg += " threw an exception: ";
  msg += ex.what();
  test_failed(msg.c_str(), file, line);
}

/// Test a condition, @p x.
/// If @p x evaluates to @c true the test passes.
/// If not, the test fails, and a message is printed to @c cerr.
/// The text of @p x, plus the file name and line number are printed.
///
/// See Boost.Test for a real test framework
///
/// @param x A condition to test; the condition must be able to be converted implicitly to @c bool.
#define TEST(x) \
try {\
  test_run(x, TEST_STRINGIFY(x), __FILE__, __LINE__);\
}\
catch(std::exception const& ex)\
{\
  test_exception(ex, TEST_STRINGIFY(x), __FILE__, __LINE__);\
}

#endif

第56讲 指针

1. 拓扑排序,简单make程序

 

TOPOLOGICAL_SORT_HPP:
View Code
/** @file list5601.cpp */
/** Listing 56-1. Topological Sort of a Directed Acyclic Graph */
#ifndef TOPOLOGICAL_SORT_HPP_
#define TOPOLOGICAL_SORT_HPP_

#include <queue>
#include <stdexcept>

/// Helper function for topological_sort().
/// Finds all the nodes in the graph with no incoming edges,
/// that is, with empty values. Removes each one from the graph
/// and adds it to the set @p nodes.
/// @param[in,out] graph A map of node/set pairs
/// @param[in,out] nodes A queue of nodes with no incoming edges
template<class Graph, class Nodes>
void topsort_clean_graph(Graph& graph, Nodes& nodes)
{
  for (typename Graph::iterator iter(graph.begin()); iter != graph.end(); )
  {
    if (iter->second.empty())
    {
      nodes.push(iter->first);
      graph.erase(iter++);  // advance iterator before erase invalidates it
    }
    else
      ++iter;
  }
}

/// Topological sort of a directed acyclic graph.
/// A graph is a map keyed by nodes, with sets of nodes as values.
/// Edges run from values to keys. The sorted list of nodes
/// is copied to an output iterator in reverse order.
/// @param graph The graph
/// @param sorted The output iterator
/// @throws std::runtime_error if the graph contains a cycle
/// @pre Graph::key_type == Graph::mapped_type::key_type
template<class Graph, class OutIterator>
void topological_sort(Graph graph, OutIterator sorted)
{
  std::queue<typename Graph::key_type> nodes;
  // Start with the set of nodes with no incoming edges.
  topsort_clean_graph(graph, nodes);

  while (not nodes.empty())
  {
    // Grab the first node to process, output it to sorted,
    
// and remove it from the graph.
    typename Graph::key_type n(nodes.front());
    nodes.pop();
    *sorted = n;
    ++sorted;

    // Erase n from the graph
    for (typename Graph::iterator iter(graph.begin()); iter != graph.end(); ++iter)
    {
      iter->second.erase(n);
    }
    // After removing n, find any nodes that no longer
    
// have any incoming edges.
    topsort_clean_graph(graph, nodes);
  }
  if (not graph.empty())
    throw std::invalid_argument("Dependency graph contains cycles");
}

#endif // TOPOLOGICAL_SORT_HPP_

 MAIN.cpp:

 

View Code
/** @file list5602.cpp */
/** Listing 56-2. First Draft of the Pseudo-Make Program */
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <istream>
#include <iterator>
#include <map>
#include <ostream>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#include "topological_sort.hpp"

typedef std::string artifact; ///< A target, dependency, or both

class dependency_graph
{
public:
  typedef std::map<artifact, std::set<artifact> > graph_type;

  void store_dependency(artifact target, artifact dependency)
  {
    graph_[dependency].insert(target);
  }

  graph_type const& graph() const { return graph_; }

  template<class OutIter>
  void sort(OutIter sorted)
  const
  {
    topological_sort(graph_, sorted);
  }

private:
  graph_type graph_;
};

int main()
{
  dependency_graph graph;

  std::string line;
  while (std::getline(std::cin, line))
  {
    std::string target, dependency;
    std::istringstream stream(line);
    if (stream >> target >> dependency)
      graph.store_dependency(target, dependency);
    else if (not target.empty())
      // Input line has a target with no dependency,
      
// so report an error.
      std::cerr << "malformed input: target, " << target <<
                   ", must be followed by a dependency name\n";
    // else ignore blank lines
  }

  try {
    // Get the artifacts in reverse order.
    std::vector<artifact> sorted;
    graph.sort(std::back_inserter(sorted));
    // Then print them in the correct order.
    std::copy(sorted.rbegin(), sorted.rend(),
              std::ostream_iterator<artifact>(std::cout, "\n"));
  } catch (std::runtime_error const& ex) {
    std::cerr << ex.what() << '\n';
    return EXIT_FAILURE;
  }
}

ARTIFACT_HPP:

View Code
/** @file list5603.cpp */
/** Listing 56-3. New Definition of an Artifact */
#ifndef ARTIFACT_HPP_
#define ARTIFACT_HPP_

#include <ctime>
#include <string>

class artifact
{
public:
  artifact() : name_(), mod_time_(static_cast<time_t>(-1)) {}
  artifact(std::string const& name)
  : name_(name), mod_time_(get_mod_time())
  {}

  std::string const& name()     const { return name_; }
  std::time_t        mod_time() const { return mod_time_; }

  /// Build a target.
  
/// After completing the actions (not yet implemented),
  
/// update the modification time.
  void build();

  /// Look up the modification time of the artifact.
  
/// Return static_cast<time_t>(-1) if the artifact does not
  
/// exist (and therefore must be built) or if the time cannot
  
/// be obtained for any other reason.
  
/// Also see boost::file_system::last_write_time.
  std::time_t get_mod_time()
  {
    // Real programs should get this information from the
    
// operating system. This program returns the current time.
    return std::time(0);
  }
private:
  std::string name_;
  std::time_t mod_time_;
};

#endif // ARTIFACT_HPP_

 

 MAIN2.cpp:

View Code
/** @file list5604.cpp */
/** Listing 56-4. Second Draft, Adding Modification Times to Artifacts */
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <istream>
#include <iterator>
#include <map>
#include <ostream>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#include "artifact.hpp"
#include "topological_sort.hpp"

typedef std::size_t artifact_index;

class dependency_graph
{
public:
  typedef std::map<artifact_index, std::set<artifact_index> > graph_type;

  void store_dependency(artifact_index target, artifact_index dependency)
  {
    graph_[dependency].insert(target);
  }

  graph_type const& graph() const { return graph_; }

  template<class OutIter>
  void sort(OutIter sorted)
  const
  {
    topological_sort(graph_, sorted);
  }

private:
  graph_type graph_;
};

std::vector<artifact> artifacts;
artifact_index lookup_artifact(std::string const& name)
{
  for (artifact_index i(0); i != artifacts.size(); ++i)
    if (artifacts[i].name() == name)
      return i;
  // Artifact not found, so add it to the end.
  artifacts.push_back(artifact(name));
  return artifacts.size() - 1;
}

int main()
{
  dependency_graph graph;

  std::string line;
  while (std::getline(std::cin, line))
  {
    std::string target_name, dependency_name;
    std::istringstream stream(line);
    if (stream >> target_name >> dependency_name)
    {
      artifact_index target(lookup_artifact(target_name));
      artifact_index dependency(lookup_artifact(dependency_name));
      graph.store_dependency(target, dependency);
    }
    else if (not target_name.empty())
      // Input line has a target with no dependency,
      
// so report an error.
      std::cerr << "malformed input: target, " << target_name <<
                   ", must be followed by a dependency name\n";
    // else ignore blank lines
  }

  try {
    // Get the sorted artifact indices in reverse order.
    std::vector<artifact_index> sorted;
    graph.sort(std::back_inserter(sorted));
    // Then print the artifacts in the correct order.
    for (std::vector<artifact_index>::reverse_iterator it(sorted.rbegin());
         it != sorted.rend();
         ++it)
    {
      std::cout << artifacts.at(*it).name() << '\n';
    }
  } catch (std::runtime_error const& ex) {
    std::cerr << ex.what() << '\n';
    return EXIT_FAILURE;
  }
}

 MAIN3.cpp:

View Code
/** @file list5605.cpp */
/** Listing 56-5. Storing Pointers in the Dependency Graph */
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <istream>
#include <iterator>
#include <map>
#include <ostream>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#include "artifact.hpp"
#include "topological_sort.hpp"

class dependency_graph
{
public:
  typedef std::map<artifact*, std::set<artifact*> > graph_type;

  void store_dependency(artifact* target, artifact* dependency)
  {
    graph_[dependency].insert(target);
  }

  graph_type const& graph() const { return graph_; }

  template<class OutIter>
  void sort(OutIter sorted)
  const
  {
    topological_sort(graph_, sorted);
  }

private:
  graph_type graph_;
};

std::map<std::string, artifact> artifacts;

artifact* lookup_artifact(std::string const& name)
{
  return &artifacts[name];
}

int main()
{
  dependency_graph graph;

  std::string line;
  while (std::getline(std::cin, line))
  {
    std::string target_name, dependency_name;
    std::istringstream stream(line);
    if (stream >> target_name >> dependency_name)
    {
      artifact* target(lookup_artifact(target_name));
      artifact* dependency(lookup_artifact(dependency_name));
      graph.store_dependency(target, dependency);
    }
    else if (not target_name.empty())
      // Input line has a target with no dependency, so report an error.
      std::cerr << "malformed input: target, " << target_name <<
                   ", must be followed by a dependency name\n";
    // else ignore blank lines
  }

  try {
    // Get the sorted artifacts in reverse order.
    std::vector<artifact*> sorted;
    graph.sort(std::back_inserter(sorted));
    // Then print the artifacts in the correct order.
    for (std::vector<artifact*>::reverse_iterator it(sorted.rbegin());
         it != sorted.rend();
         ++it)
    {
      std::cout << (*it)->name() << '\n';
    }
  } catch (std::runtime_error const& ex) {
    std::cerr << ex.what() << '\n';
    return EXIT_FAILURE;
  }
}

2. 地址与引用

第60讲 智能指针

1. auto_ptr

简单示例:

View Code
/** @file list6002.cpp */
/** Listing 60-2. The Correct Way to Copy auto_ptr Objects */
#include <memory>

std::auto_ptr<int> does_this_work(std::auto_ptr<int> x)
{
  std::auto_ptr<int> y(x);
  return y;
}

int main()
{
  std::auto_ptr<int> a, b;
  a.reset(new int(42));
  b = does_this_work(a);
}

 

2. shared_ptr

简单示例:

View Code
/** @file list6003.cpp */
/** Listing 60-3. Working with shared_ptr */
#include <iostream>
#include <memory>
#include <ostream>
#include <vector>

class see_me
{
public:
  see_me(int x) : x_(x) { std::cout <<  "see_me(" << x_ << ")\n"; }
  ~see_me()             { std::cout << "~see_me(" << x_ << ")\n"; }
  int value() const     { return x_; }
private:
  int x_;
};

std::tr1::shared_ptr<see_me> does_this_work(std::tr1::shared_ptr<see_me> x)
{
  std::tr1::shared_ptr<see_me> y(x);
  return y;
}

int main()
{
  std::tr1::shared_ptr<see_me> a, b;
  a.reset(new see_me(42));
  b = does_this_work(a);
  std::vector<std::tr1::shared_ptr<see_me> > v;
  v.push_back(a);
  v.push_back(b);
}

 

第62讲 枚举

1. C++中的static用法

第64讲 特征萃取与策略

1.特征萃取

2.基于策略的编程

newstring:

View Code
/** @file list6405.cpp */
/** Listing 64-5. The newstring Class Template */
template<class Char, class Storage, class Traits>
class newstring {
public:
   typedef Char value_type;
   typedef std::size_t size_type;
   typedef typename Storage::iterator iterator;
   typedef typename Storage::const_iterator const_iterator;
   newstring() : storage_() {}
   newstring(Storage const& storage) : storage_(storage) {}
   newstring(newstring const& str) : storage_(str.storage_) {}
   newstring(Char const* ptr, size_type size) : storage_() {
      resize(size);
      std::copy(ptr, ptr + size, begin());
   }
 
   static const size_type npos = static_cast<size_type>(-1);

   newstring& operator=(newstring str) { swap(str); return *this; }
   newstring& operator=(std::string const& str) {
      return *this = newstring(str.data(), str.size());
   }
   void swap(newstring& str) { storage_.swap(str.storage_); }

   Char operator[](size_type i) const { return *(storage_.begin() + i); }
   Char& operator[](size_type i)      { return *(storage_.begin() + i); }

   void resize(size_type size, Char value = Char()) {
     storage_.resize(size, value);
   }
   void reserve(size_type size) { storage_.reserve(size); }
   size_type size() const       { return storage_.end() - storage_.begin(); }
   size_type max_size() const   { return storage_.max_size(); }

   Char const* c_str() const { return storage_.c_str(); }
   Char const* data() const  { return storage_.c_str(); }

   iterator begin()             { return storage_.begin(); }
   const_iterator begin() const { return storage_.begin(); }
   iterator end()               { return storage_.end(); }
   const_iterator end() const   { return storage_.end(); }

   size_type find(newstring const& s, size_type pos = 0const {
      pos = std::min(pos, size());
      const_iterator result( std::search(begin() + pos, end(),
                             s.begin(), s.end(), Traits::eq) );
      if (result == end())
         return npos;
      else
         return result - begin();
   }
         
private:
   Storage storage_;
};

template<class Traits>
class newstringcmp
{
public:
   bool operator()(typename Traits::value_type a, typename Traits::value_type b)
   const
   {
      return Traits::cmp(a, b) < 0;
   }
};

template<class Char, class Storage1, class Storage2, class Traits>
bool operator <(newstring<Char, Storage1, Traits> const& a,
                newstring<Char, Storage2, Traits> const& b)
{
   return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
                                       newstringcmp<Traits>());
}

template<class Char, class Storage1, class Storage2, class Traits>
bool operator ==(newstring<Char, Storage1, Traits> const& a,
                 newstring<Char, Storage2, Traits> const& b)
{
   return std::equal(a.begin(), a.end(), b.begin(), b.end(), Traits::eq);
}

 

vector_storage:

View Code
/** @file list6406.cpp */
/** Listing 64-6. The vector_storage Class Template */
#include <vector>

template<class Char>
class vector_storage {
public:
   typedef std::size_t size_type;
   typedef Char value_type;
   typedef typename std::vector<Char>::iterator iterator;
   typedef typename std::vector<Char>::const_iterator const_iterator;

   vector_storage() : string_(1, Char()) {}

   void swap(vector_storage& storage) { string_.swap(storage.string_); }
   size_type max_size() const { return string_.max_size() - 1; }
   void reserve(size_type size) { string_.reserve(size + 1); }
   void resize(size_type newsize, value_type value) {
      // if the string grows, overwrite the null character, then resize
      if (newsize >= string_.size()) {
         string_[string_.size() - 1] = value;
         string_.resize(newsize + 1, value);
      }
      string_[string_.size() - 1] = Char();
   }
   Char const* c_str() const { return &string_[0]; }

   iterator begin()             { return string_.begin(); }
   const_iterator begin() const { return string_.begin(); }
   // Skip over the trailing null character at the end of the vector
   iterator end()               { return string_.end() - 1; }
   const_iterator end() const   { return string_.end() - 1; }

private:
   std::vector<Char> string_;
};

 

deque_storage:

View Code
/** @file list6407.cpp */
/** Listing 64-7. The deque_storage Class Template */
template<class Char>
class deque_storage {
public:
   typedef std::size_t size_type;
   typedef Char value_type;
   typedef typename std::deque<Char>::iterator iterator;
   typedef typename std::deque<Char>::const_iterator const_iterator;

   deque_storage() : string_() {}

   void swap(deque_storage& storage) { string_.swap(storage.string_); }
   size_type max_size() const        { return string_.max_size(); }
   void reserve(size_type size)      { string_.reserve(size); }
   void resize(size_type size, value_type value) { string_.resize(size, value); }
   Char const* c_str() const {
      data_.assign(begin(), end()); 
      data_.push_back(Char());
      return &data_[0];
   }

   iterator begin()             { return string_.begin(); }
   const_iterator begin() const { return string_.begin(); }
   iterator end()               { return string_.end(); }
   const_iterator end() const   { return string_.end(); }

private:
   std::deque<Char> string_;
   std::vector<Char> data_;
};

array_storage:

View Code
/** @file list6408.cpp */
/** Listing 64-8. The array_storage Class Template */
#include <algorithm>
#include <cstdlib>
#include <stdexcept>

template<class Char, std::size_t MaxSize>
class array_storage {
public:
   typedef Char array_type[MaxSize];
   typedef std::size_t size_type;
   typedef Char value_type;
   typedef Char* iterator;
   typedef Char const* const_iterator;

   array_storage() : size_(0), string_() { string_[0] = Char(); }

   void swap(array_storage& storage) {
      // linear complexity
      std::swap_ranges(string_.begin(), string_.end(), storage.string_.begin());
      std::swap(size_, storage.size_);
   }
   size_type max_size() const { return MaxSize - 1; }
   void reserve(size_type size) {
     if (size > max_size()) throw std::length_error("reserve");
   }
   void resize(size_type newsize, value_type value) {
      if (newsize > max_size())
         throw std::length_error("resize");
      if (newsize > size_)
         std::fill(begin() + size_, begin() + newsize, value);
      size_ = newsize;
      string_[size_] = Char();
   }
   Char const* c_str() const { return &string_[0]; }

   iterator begin()             { return &string_[0]; }
   const_iterator begin() const { return &string_[0]; }
   iterator end()               { return begin() + size_; }
   const_iterator end() const   { return begin() + size_; }

private:
   size_type size_;
   array_type string_;
};

array_storage2:

View Code
/** @file list6409.cpp */
/** Listing 64-9. The array_storage Class Template, Based on array<>  */
#include <algorithm>
#include <cstdlib>
#include <stdexcept>
#include <array>

template<class Char, std::size_t MaxSize>
class array_storage {
public:
   typedef std::tr1::array<Char, MaxSize> array_type;
   typedef std::size_t size_type;
   typedef Char value_type;
   typedef typename array_type::iterator iterator;
   typedef typename array_type::const_iterator const_iterator;

   array_storage() : size_(0), string_() { string_[0] = Char(); }

   void swap(array_storage& storage) {
      string_.swap(storage.string_);
      std::swap(size_, storage.size_);
   }
   size_type max_size() const { return string_.max_size() - 1; }
   void reserve(size_type size) {
     if (size > max_size()) throw std::length_error("reserve");
   }
   void resize(size_type newsize, value_type value) {
      if (newsize > max_size())
         throw std::length_error("resize");
      if (newsize > size_)
         std::fill(begin() + size_, begin() + newsize, value);
      size_ = newsize;
      string_[size_] = Char();
   }
   Char const* c_str() const { return &string_[0]; }

   iterator begin()             { return string_.begin(); }
   const_iterator begin() const { return string_.begin(); }
   iterator end()               { return begin() + size_; }
   const_iterator end() const   { return begin() + size_; }

private:
   size_type size_;
   array_type string_;
};

 

第67讲 元编程

元编程是编写编译时运行的代码

示例代码:

View Code
/** @file list6702.cpp */
/** Listing 67-2. Simple Implementation of the power10 Class Template */
template<unsigned N>
struct power10
{
  enum t { value = 10 * power10<N – 1>::value };
};

template<>
struct power10<0>
{
  enum t { value = 1 };
};

 

第68讲 项目4:计算器

代码如下:

 

 

posted @ 2012-07-09 18:53  Mr.Rico  阅读(...)  评论(...编辑  收藏