这一讲我们集中解说类和他的一些特性.首先我们从自己定义一个有理数类来開始.

在C语言中有一个keyword: struct ,用来创建一个结构体类型.可是在C++中这个关键的含义就不只如此了,以下我们能够看下演示样例:

  1. /// Represent a rational number.
  2. struct rational
  3. {
  4. int numerator; ///< numerator gets the sign of the rational value
  5. int denominator; ///< denominator is always positive
  6. };

首先认为这个和C语言中看起来是一样的,分子是numerator,分母是denominator,事实上不然,上面那段代码实际上是创建了一个类型,即是一个类型的定义,换言之,就是编译器会记住rational命名了一个类型,仅仅是没有为对象numerator和denominator分配内存,用C++的说法,numerator和denominator是数据成员. 注意花括号后面的分号结束符!!!!

以下为了验证这是一个类型,我们能够创建一个rational的对象,然后使用点(.)操作符訪问成员,例如以下:

  1. /** Using a Class and Its Members */
  2. #include <iostream>
  3. #include <ostream>
  4.  
  5. /// Represent a rational number.
  6. struct rational
  7. {
  8. int numerator; ///< numerator gets the sign of the rational value
  9. int denominator; ///< denominator is always positive
  10. };
  11.  
  12. int main()
  13. {
  14. rational pi;
  15. pi.numerator = 355;
  16. pi.denominator = 113;
  17. std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
  18. }

执行结果例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3Vvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

如今能够看出这确实是一个类型,那么如今我们就进一步扩展这个类型,首先,添加一个分数自己主动约分的功能.rational类型已经有了数据成员,我们如今为其编写一个约分的函数成员,例如以下:

  1. /** @file ReduceduceRational.cpp */
  2. /** Adding the reduce Member Function */
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <ostream>
  7.  
  8. /// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
  9. int gcd(int n, int m)
  10. {
  11. n = abs(n);
  12. while (m != 0) {
  13. int tmp(n % m);
  14. n = m;
  15. m = tmp;
  16. }
  17. return n;
  18. }
  19.  
  20. /// Represent a rational number.
  21. struct rational
  22. {
  23. /// Reduce the numerator and denominator by their GCD.
  24. void reduce()
  25. {
  26. assert(denominator != 0);
  27. int div(gcd(numerator, denominator));
  28. numerator = numerator / div;
  29. denominator = denominator / div;
  30. }
  31.  
  32. int numerator; ///< numerator gets the sign of the rational value
  33. int denominator; ///< denominator is always positive
  34. };
  35.  
  36. int main()
  37. {
  38. rational pi;
  39. pi.numerator = 1420;
  40. pi.denominator = 452;
  41. pi.reduce();
  42. std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
  43. }

运行结果例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3Vvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

注意reduce看起来是一个普通的函数,可是出如今rational的类型定义内.并要注意在reduce内部调用rational数据成员的方式.而当调用reduce函数时候,点操作符的左边必须是一个对象,当reduce()函数指向一个数据成员时,该数据成员从左操作数获取.

上面代码中gcd函数是利用欧几里得算法就最大公约数,是一个自由函数,不论什么的两个整数都能够作为參数调用它,他作为自由函数和rational类型没有不论什么的关系,不过用作计算最大公约数,全然没有改动rational()的数据成员.

而在上面的代码中我们定义了一个reduce()成员函数,如今我们继续扩展rational类型,添加一个assign()成员函数,并让assign()成员函数调用reduce()函数.即是在类型定义内部能够定义多个函数,而且函数之间能够调用.代码例如以下:

  1. /** @file ReduceAssignRational.cpp */
  2. /** Adding the assign Member Function */
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <ostream>
  7. /// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
  8. int gcd(int n, int m)
  9. {
  10. n = abs(n);
  11. while (m != 0) {
  12. int tmp(n % m);
  13. n = m;
  14. m = tmp;
  15. }
  16. return n;
  17. }
  18.  
  19. /// Represent a rational number.
  20. struct rational
  21. {
  22. /// Assign a numerator and a denominator, then reduce to normal form.
  23. /// @param num numerator
  24. /// @param den denominator
  25. /// @pre denominator > 0
  26. void assign(int num, int den)
  27. {
  28. numerator = num;
  29. denominator = den;
  30. reduce();
  31. }
  32.  
  33. /// Reduce the numerator and denominator by their GCD.
  34. void reduce()
  35. {
  36. assert(denominator != 0);
  37. int div(gcd(numerator, denominator));
  38. numerator = numerator / div;
  39. denominator = denominator / div;
  40. }
  41.  
  42. int numerator; ///< numerator gets the sign of the rational value
  43. int denominator; ///< denominator is always positive
  44. };
  45.  
  46. int main()
  47. {
  48. rational pi;
  49. pi.assign(1420, 452);
  50. std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
  51. }

在上面的代码中,assign()函数调用了reduce()函数,可是assign()函数的定义早于reduce()函数,这里我们有这种规则----编译器在使用一个名字前必须看到该名字的至少一个声明,新类型的成员能够调用其它成员,而不必考虑在类型内的声明顺序.在其它情况下,声明必须早于调用.

同一时候我们也能够看出,在上面创建rational实例对象的时候,我们要么是直接给数据成员赋值,要么调用当中的一个assign()函数赋值,而学习过其它高级语言的应该都知道我们应该通过构造函数来初始化一个类型的实例,那么如今我们就把rational类型加入构造函数,(构造器),代码例如以下:

  1. /** @file ConstructRational.cpp */
  2. /** Adding the Ability to Initialize a rational Object */
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <ostream>
  7.  
  8. /// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
  9. int gcd(int n, int m)
  10. {
  11. n = abs(n);
  12. while (m != 0) {
  13. int tmp(n % m);
  14. n = m;
  15. m = tmp;
  16. }
  17. return n;
  18. }
  19.  
  20. /// Represent a rational number.
  21. struct rational
  22. {
  23. /// Construct a rational object, given a numerator and a denominator.
  24. /// Always reduce to normal form.
  25. /// @param num numerator
  26. /// @param den denominator
  27. /// @pre denominator > 0
  28. rational(int num, int den)
  29. : numerator(num), denominator(den)
  30. {
  31. reduce();
  32. }
  33.  
  34. /// Assign a numerator and a denominator, then reduce to normal form.
  35. /// @param num numerator
  36. /// @param den denominator
  37. /// @pre denominator > 0
  38. void assign(int num, int den)
  39. {
  40. numerator = num;
  41. denominator = den;
  42. reduce();
  43. }
  44.  
  45. /// Reduce the numerator and denominator by their GCD.
  46. void reduce()
  47. {
  48. assert(denominator != 0);
  49. int div(gcd(numerator, denominator));
  50. numerator = numerator / div;
  51. denominator = denominator / div;
  52. }
  53.  
  54. int numerator; ///< numerator gets the sign of the rational value
  55. int denominator; ///< denominator is always positive
  56. };
  57.  
  58. int main()
  59. {
  60. rational pi(1420, 452);
  61. std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
  62. }

当中的构造函数就是

运行结果例如以下:

对上面的构造函数分析。能够知道构造函数的形式和要求为:

  • l  函数名称必须为类型名称
  • l  函数能够有參数或者无參数(按需而定)
  • l  函数參数(非空)后有一个冒号(:)后面是相应的參数初始化相应的数据成员。

如今我们继续扩展这个rational类型,上面的代码都是默觉得分母为正,分子可为负,如今我们要使可以初始化的时候传递负数的分母,这就要求我们对分子分母同一时候取反,这样可以保证分母依旧为正,而数值不变.部分代码例如以下:

  1. /// Reduce the numerator and denominator by their GCD.
  2. void reduce()
  3. {
  4. assert(denominator != 0);
  5. if (denominator < 0) // if denominator < 0
  6. {
  7. denominator = -denominator;
  8. numerator = -numerator;
  9. }
  10. int div(gcd(numerator, denominator));
  11. numerator = numerator / div;
  12. denominator = denominator / div;
  13. }

构造函数是我们接触的最新的函数类型,他相同的能够被重载,重载构造函数的代码例如以下:

  1. /** Constructing a Rational Object from an Integer */
  2. rational(int num)
  3. : numerator(num), denominator(1)
  4. {}

这个是一个參数的构造函数,默认分母为1.并且显然不须要调用reduce函数.

我们既然创造了一个新的数据类型,那么就也须要创造这个数据类型的操作行为,就是重载操作符,使他也可以实现四则运算和大小比較等.以下就是运算符重载的代码:

  1. /** @file OverrideOperator.cpp */
  2. /** Overloading the Equality Operator */
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <ostream>
  7.  
  8. using namespace std;
  9.  
  10. /// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
  11. int gcd(int n, int m)
  12. {
  13. n = abs(n);
  14. while (m != 0) {
  15. int tmp(n % m);
  16. n = m;
  17. m = tmp;
  18. }
  19. return n;
  20. }
  21.  
  22. /// Represent a rational number.
  23. struct rational
  24. {
  25. /// Construct a rational object, given a numerator and a denominator.
  26. /// Always reduce to normal form.
  27. /// @param num numerator
  28. /// @param den denominator
  29. /// @pre denominator > 0
  30. rational(int num, int den)
  31. : numerator(num), denominator(den)
  32. {
  33. reduce();
  34. }
  35. rational(int num)
  36. : numerator(num), denominator(1)
  37. {}
  38.  
  39. /// Assign a numerator and a denominator, then reduce to normal form.
  40. /// @param num numerator
  41. /// @param den denominator
  42. /// @pre denominator > 0
  43. void assign(int num, int den)
  44. {
  45. numerator = num;
  46. denominator = den;
  47. reduce();
  48. }
  49.  
  50. /// Reduce the numerator and denominator by their GCD.
  51. void reduce()
  52. {
  53. assert(denominator != 0);
  54. if (denominator < 0)
  55. {
  56. denominator = -denominator;
  57. numerator = -numerator;
  58. }
  59. int div(gcd(numerator, denominator));
  60. numerator = numerator / div;
  61. denominator = denominator / div;
  62. }
  63.  
  64. int numerator; ///< numerator gets the sign of the rational value
  65. int denominator; ///< denominator is always positive
  66. };
  67.  
  68. /// Compare two rational numbers for equality.
  69. /// @pre @p a and @p b are reduced to normal form
  70. bool operator==(rational const& a, rational const& b)
  71. {
  72. return a.numerator == b.numerator && a.denominator == b.denominator; // ==
  73. }
  74.  
  75. /// Compare two rational numbers for inequality.
  76. /// @pre @p a and @p b are reduced to normal form
  77. inline bool operator!=(rational const& a, rational const& b)
  78. {
  79. return ! (a == b); // !=
  80. }
  81.  
  82. /// Compare two rational numbers for less-than.
  83. bool operator<(rational const& a, rational const& b)
  84. {
  85. return a.numerator * b.denominator < b.numerator * a.denominator; // <
  86. }
  87.  
  88. /// Compare two rational numbers for less-than-or-equal.
  89. inline bool operator<=(rational const& a, rational const& b)
  90. {
  91. return ! (b < a); // <=
  92. }
  93.  
  94. /// Compare two rational numbers for greater-than.
  95. inline bool operator>(rational const& a, rational const& b)
  96. {
  97. return b < a; // >
  98. }
  99.  
  100. /// Compare two rational numbers for greater-than-or-equal.
  101. inline bool operator>=(rational const& a, rational const& b)
  102. {
  103. return ! (b > a); // >=
  104. }
  105.  
  106. /** Addition Operator for the rational Type */
  107. rational operator+(rational const& lhs, rational const& rhs)
  108. {
  109. return rational(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
  110. lhs.denominator * rhs.denominator); // +
  111. }
  112.  
  113. rational operator-(rational const& r)
  114. {
  115. return rational(-r.numerator, r.denominator); // -
  116. }
  117.  
  118. /** Arithmetic Operators for the rational Type */
  119. rational operator-(rational const& lhs, rational const& rhs)
  120. {
  121. return rational(lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator,
  122. lhs.denominator * rhs.denominator); // -
  123. }
  124.  
  125. rational operator*(rational const& lhs, rational const& rhs)
  126. {
  127. return rational(lhs.numerator * rhs.numerator,
  128. lhs.denominator * rhs.denominator); // *
  129. }
  130.  
  131. rational operator/(rational const& lhs, rational const& rhs)
  132. {
  133. return rational(lhs.numerator * rhs.denominator,
  134. lhs.denominator * rhs.numerator); // /
  135. }
  136.  
  137. void print(rational const & result)
  138. {
  139. cout << result.numerator << '/' << result.denominator << '\n' ;
  140. }
  141.  
  142. int main()
  143. {
  144. rational rat_a (1,9);
  145. rational rat_b (2,7);
  146. rational rat_c (3,8);
  147. rational rat_d (4,7);
  148. rational result_a(rat_a + rat_b);
  149. rational result_b(rat_a / rat_b);
  150. rational result_c(rat_c - rat_a); // the result < 0
  151. rational result_d(- rat_d); // override -
  152. bool result_bool_a (rat_a > rat_d);
  153. bool result_bool_b (rat_a <= rat_c);
  154.  
  155. print(rat_a);
  156. print(rat_b);
  157. print(rat_c);
  158. print(rat_d);
  159. print(result_a);
  160. print(result_b);
  161. print(result_c);
  162. print(result_d);
  163. cout << result_bool_a << '\n';
  164. cout << result_bool_b << '\n';
  165.  
  166. rational result(3 * rational(1, 3));
  167. cout << result.numerator << '/' << result.denominator << '\n';
  168. }

上面就是全部的运算符重载的函数,当中的构造函数有两个,一个是双參数一个是单參数,所以在上面的main函数中最后两行代码才干够通过编译并成功运行,由于里面的整数3被自己主动转换为rational类型的.

相同一些数学函数也是一样,比方求绝对值的函数重载代码段例如以下:

  1. /** Computing the Absolute Value of a Rational Number */
  2. rational absval(rational const& r)
  3. {
  4. return rational(abs(r.numerator), r.denominator);
  5. }

当然这个函数非常easy重载,可是假设是涉及到浮点数的怎么办呢?比方sqrt开开方函数.而假设编译器知道怎样将有理数rational转换为浮点数的话,就能够将rational实參传递给已有的浮点函数,无需再做进一步的工作.可是要用那种类型的浮点数,这个因为需求不同而变化,所以我们要放弃自己主动转换浮点类型,取而代之的是使用三个显式的计算有理数的浮点值的函数.代码段例如以下(须要使用static_cast将分子和分母转为须要的浮点类型)

  1. /** Converting to Floating-point Types */
  2. struct rational
  3. {
  4. float as_float()
  5. {
  6. return static_cast<float>(numerator) / denominator;
  7. }
  8.  
  9. double as_double()
  10. {
  11. return numerator / static_cast<double>(denominator);
  12. }
  13.  
  14. long double as_long_double()
  15. {
  16. return static_cast<long double>(numerator) /
  17. static_cast<long double>(denominator);
  18. }
  19. };

眼下为止的完整版的代码例如以下:

  1. /** @file OverrideOperator.cpp */
  2. /** Overloading the Equality Operator */
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <ostream>
  7.  
  8. using namespace std;
  9.  
  10. /// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
  11. int gcd(int n, int m)
  12. {
  13. n = abs(n);
  14. while (m != 0) {
  15. int tmp(n % m);
  16. n = m;
  17. m = tmp;
  18. }
  19. return n;
  20. }
  21.  
  22. /// Represent a rational number.
  23. struct rational
  24. {
  25. /// Construct a rational object, given a numerator and a denominator.
  26. /// Always reduce to normal form.
  27. /// @param num numerator
  28. /// @param den denominator
  29. /// @pre denominator > 0
  30. rational(int num, int den)
  31. : numerator(num), denominator(den)
  32. {
  33. reduce();
  34. }
  35. rational(int num)
  36. : numerator(num), denominator(1)
  37. {}
  38.  
  39. /// Assign a numerator and a denominator, then reduce to normal form.
  40. /// @param num numerator
  41. /// @param den denominator
  42. /// @pre denominator > 0
  43. void assign(int num, int den)
  44. {
  45. numerator = num;
  46. denominator = den;
  47. reduce();
  48. }
  49.  
  50. /// Reduce the numerator and denominator by their GCD.
  51. void reduce()
  52. {
  53. assert(denominator != 0);
  54. if (denominator < 0)
  55. {
  56. denominator = -denominator;
  57. numerator = -numerator;
  58. }
  59. int div(gcd(numerator, denominator));
  60. numerator = numerator / div;
  61. denominator = denominator / div;
  62. }
  63.  
  64. /** Converting to Floating-point Types */
  65. float as_float()
  66. {
  67. return static_cast<float>(numerator) / denominator;
  68. }
  69.  
  70. double as_double()
  71. {
  72. return numerator / static_cast<double>(denominator);
  73. }
  74.  
  75. long double as_long_double()
  76. {
  77. return static_cast<long double>(numerator) /
  78. static_cast<long double>(denominator);
  79. }
  80.  
  81. int numerator; ///< numerator gets the sign of the rational value
  82. int denominator; ///< denominator is always positive
  83. };
  84.  
  85. /// Compare two rational numbers for equality.
  86. /// @pre @p a and @p b are reduced to normal form
  87. bool operator==(rational const& a, rational const& b)
  88. {
  89. return a.numerator == b.numerator && a.denominator == b.denominator; // ==
  90. }
  91.  
  92. /// Compare two rational numbers for inequality.
  93. /// @pre @p a and @p b are reduced to normal form
  94. inline bool operator!=(rational const& a, rational const& b)
  95. {
  96. return ! (a == b); // !=
  97. }
  98.  
  99. /// Compare two rational numbers for less-than.
  100. bool operator<(rational const& a, rational const& b)
  101. {
  102. return a.numerator * b.denominator < b.numerator * a.denominator; // <
  103. }
  104.  
  105. /// Compare two rational numbers for less-than-or-equal.
  106. inline bool operator<=(rational const& a, rational const& b)
  107. {
  108. return ! (b < a); // <=
  109. }
  110.  
  111. /// Compare two rational numbers for greater-than.
  112. inline bool operator>(rational const& a, rational const& b)
  113. {
  114. return b < a; // >
  115. }
  116.  
  117. /// Compare two rational numbers for greater-than-or-equal.
  118. inline bool operator>=(rational const& a, rational const& b)
  119. {
  120. return ! (b > a); // >=
  121. }
  122.  
  123. /** Addition Operator for the rational Type */
  124. rational operator+(rational const& lhs, rational const& rhs)
  125. {
  126. return rational(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
  127. lhs.denominator * rhs.denominator); // +
  128. }
  129.  
  130. rational operator-(rational const& r)
  131. {
  132. return rational(-r.numerator, r.denominator); // -
  133. }
  134.  
  135. /** Arithmetic Operators for the rational Type */
  136. rational operator-(rational const& lhs, rational const& rhs)
  137. {
  138. return rational(lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator,
  139. lhs.denominator * rhs.denominator); // -
  140. }
  141.  
  142. rational operator*(rational const& lhs, rational const& rhs)
  143. {
  144. return rational(lhs.numerator * rhs.numerator,
  145. lhs.denominator * rhs.denominator); // *
  146. }
  147.  
  148. rational operator/(rational const& lhs, rational const& rhs)
  149. {
  150. return rational(lhs.numerator * rhs.denominator,
  151. lhs.denominator * rhs.numerator); // /
  152. }
  153.  
  154. void print(rational const & result)
  155. {
  156. cout << result.numerator << '/' << result.denominator << '\n' ;
  157. }
  158.  
  159. int main()
  160. {
  161. rational rat_a (1,9);
  162. rational rat_b (2,7);
  163. rational rat_c (3,8);
  164. rational rat_d (4,7);
  165. rational result_a(rat_a + rat_b);
  166. rational result_b(rat_a / rat_b);
  167. rational result_c(rat_c - rat_a); // the result < 0
  168. rational result_d(- rat_d); // override -
  169. bool result_bool_a (rat_a > rat_d);
  170. bool result_bool_b (rat_a <= rat_c);
  171.  
  172. print(rat_a);
  173. print(rat_b);
  174. print(rat_c);
  175. print(rat_d);
  176. print(result_a);
  177. print(result_b);
  178. print(result_c);
  179. print(result_d);
  180. cout << result_bool_a << '\n';
  181. cout << result_bool_b << '\n';
  182.  
  183. rational result(3 * rational(1, 3));
  184. cout << result.numerator << '/' << result.denominator << '\n';
  185.  
  186. rational pi(355, 113);
  187. rational bmi(90*100*100, 180*180); // Body-mass index of 90 kg, 180 cm
  188. double circumference(0), radius(10);
  189.  
  190. circumference = 2 * pi.as_double() * radius;
  191. cout << "circumference of circle with radius " << radius << " is about "
  192. << circumference << '\n';
  193. cout << "bmi = " << bmi.as_float() << '\n';
  194.  
  195. }

运行结果例如以下:

如今我们的rational类型差点儿已经和int内置类型差点儿相同了,可是另一个功能有待实现,那就是输入输出,上面的输出输入是通过专门的函数实现的,而不能直接向内置数据类型的输入输出那样,所以我们须要重载I/O操作符.

在C++中,输入出符一样能够被重载.对于输入操作符,及时提取器(>>)。他的第一个參数为std::istream& 。必须是很了引用。以便在函数中改动流对象。而第二个參数由于要存储输入值,也必须是很量引用。

返回值通常就是他的第一个參数,类型std::istream& ,以便在同一个表达式中连接多个输入操作符。

函数体完毕输入的提取解析和翻译工作。虽然完好的错误处理眼下还难以实现,可是假如主要的错误处理还是非常easy的。

每一个流用一个状态掩码跟踪错误,例如以下:

当输入非法时,输入函数在流的错误状态设置failbit位。

调用者通过状态測试字检測流状态。若状态字设置了failbit则说明流出现了错误。

以下定义rational类型格式,一方面应该易于阅读和书写。同一时候也应该易于高速读取和解析,并且要让输入格式可以识别输出格式以及其它格式。

定义格式为整数、斜线(/),整数,且各个元素之间可插入随意的空格,除非设置流的状态位为禁用。若一个整数后没有斜线,则该数即为结果(分母为一)。成员函数unget()使输入操作符退回多读入的字符。

整数操作符与此类似:尽量多读入字符直到读入的字符不属于该整数为止,然后回退最后一个字符。

输入操作符的重载代码例如以下:

  1. /** @file OverrideIO.cpp */
  2. /** Input Operator */
  3. #include <ios> // declares failbit, etc.
  4. #include <istream> // declares std::istream and the necessary >> operators
  5.  
  6. std::istream& operator>>(std::istream& in, rational& rat)
  7. {
  8. int n(0), d(0);
  9. char sep('\0');
  10. if (not (in >> n >> sep))
  11. // Error reading the numerator or the separator character.
  12. in.setstate(std::cin.failbit);
  13. else if (sep != '/')
  14. {
  15. // Read numerator successfully, but it is not followed by /.
  16. // Push sep back into the input stream, so the next input operation
  17. // will read it.
  18. in.unget();
  19. rat.assign(n, 1);
  20. }
  21. else if (in >> d)
  22. // Successfully read numerator, separator, and denominator.
  23. rat.assign(n, d);
  24. else
  25. // Error reading denominator.
  26. in.setstate(std::cin.failbit);
  27.  
  28. return in;
  29. }

使用输出操作符时。面对很多的棘手的格式化问题,包含字符宽度设置和对齐方式选择,按需插入填充字符,以及仅重设字符宽度而不改变其它格式设置等。

编写复杂的输出操作符的关键在于利用暂时输入流将文字存在string类型的字符串中。Std::osringstream类型在<sstream>中声明,使用方式与cout等输出流同样。操作完毕后,使用成员函数str()返回生成的string类型字符串。

在rational的输入操作符中,创建一个ostringstream,并写入分子。分隔符,分母。然后将生成的字符串写入实际的输出流中,并让输出流处理宽度,对齐和填充。

假设将分子分隔符,分母直接写入输出中。则宽度设置仅仅能应用于分子。使对齐格式出错。和输入符一样,输出操作符返回也是第一个參数。类型std::ostream& ,第二个參数能够按值传递或者传入常量引用。代码例如以下:

  1. /** @file Output.cpp */
  2. /** Output Operator */
  3. #include <ostream> // declares the necessary << operators
  4. #include <sstream> // declares the std::ostringstream type
  5.  
  6. std::ostream& operator<<(std::ostream& out, rational const& rat)
  7. {
  8. std::ostringstream tmp;
  9. tmp << rat.numerator;
  10. if (rat.denominator != 1)
  11. tmp << '/' << rat.denominator;
  12. out << tmp.str();
  13.  
  14. return out;
  15. }

以下就是检測I/O错误的解说:

首先上文说过输入输出流有几个标志位。程序通过检查和设置标志位来測试错误与否。

输入循环在正常情况下会处理输入流的所有数据,直到到达输入流尾端时设置eofbit。此时fail()仍然返回false表明输入流状态正常。因此输入继续下一轮循环。但这一轮中再无数据可读,因此设置failbit并返回错误。循环条件由于false而退出。

当流中包括非法输入。。比如在要读取一个整数的时候却都到了非数字字符。或者出现了硬件错误时候,循环会退出。首先调用bad()成员函数检測硬件错误,返回true则退出,其次eof()成员函数用来检測是否到达文件尾,仅当eofbit被设置返回true。

若bad() eof()同一时候为false且fail()为true。则表明流中含有非法输入。然后程序按需处理。

能够调用clear函数清除错误状态,跳过后进行下一次读取。

代码例如以下,:

  1. /// Tests for failbit only
  2. bool iofailure(istream& in)
  3. {
  4. return in.fail() && !(in.bad());
  5. }
  6. // test for istream
  7. bool infoistream(istream & in)
  8. {
  9. return !(in.bad()) && !(in.eof()) && in.fail()
  10. }

完毕rational类型的最后一步是完好操作符函数和构造函数,首先是赋值操作符的重载,示比例如以下:

  1. /** First Version of the Assignment Operator */
  2. struct rational
  3. {
  4. rational& operator=(rational const& rhs)
  5. {
  6. numerator = rhs.numerator;
  7. denominator = rhs.denominator;
  8. return *this;
  9. }
  10. };

第二种实现例如以下:

  1. /** Assignment Operator with Explicit Use of this-> */
  2. struct rational
  3. {
  4. rational& operator=(rational const& that)
  5. {
  6. this->numerator = that.numerator;
  7. this->denominator = that.denominator;
  8. return *this;
  9. }
  10. };

上面仅仅是赋值操作符的參数为rational类型的实现,參数也能够是整数,对赋值操作符再次重载例如以下:

  1. /** Assignment of an Integer to a Rational */
  2. struct rational
  3. {
  4. rational& operator=(int num)
  5. {
  6. this->numerator = num;
  7. this->denominator = 1;
  8. return *this;
  9. }
  10. };

编译器会自己主动生成构造函数.特别的是,复制构造函数会将一个rational对象的全部对象成员拷贝到还有一个rational对象.仅仅要声明一个按值传递的rational实參,编译器就调用复制构造函数将实參拷贝到形參,或者定义一个rational变量且用还有一个rational值初始化时,编译器也会通过复制构造函数生成该变量.和赋值操作符一样,编译器默认实现版本号和我们的须要的版本号一样,因此不是必需再次手动编写复制构造函数.

构造函数一样,必需要编写自己实现的不同的參数的重载版本号的构造函数,才干避免编译器自己主动生成可能不符合的默认构造函数.完整版的构造函数例如以下:

  1. /** Overloaded Constructors for rational */
  2. struct rational
  3. {
  4. rational()
  5. : numerator(0), denominator(1)
  6. {/*empty*/}
  7.  
  8. rational(int num)
  9. : numerator(num), denominator(1)
  10. {/*empty*/}
  11.  
  12. rational(int num, int den)
  13. : numerator(num), denominator(den)
  14. {
  15. reduce();
  16. }
  17. };

如今为止,对rational类型的定义大概完毕了,如今贴一下眼下为止的完整版的代码,以及測试执行结果:

  1. /** @file OverrideOperator.cpp */
  2. /** Overloading the Equality Operator */
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <ostream>
  7. #include <sstream>
  8.  
  9. using namespace std;
  10.  
  11. /// Compute the greatest common divisor of two integers, using Euclids algorithm.
  12. int gcd(int n, int m)
  13. {
  14. n = abs(n);
  15. while (m != 0)
  16. {
  17. int tmp(n % m);
  18. n = m;
  19. m = tmp;
  20. }
  21. return n;
  22. }
  23.  
  24. /// Represent a rational number.
  25. struct rational
  26. {
  27. /// Construct a rational object, given a numerator and a denominator.
  28. /// Always reduce to normal form.
  29. /// @param num numerator
  30. /// @param den denominator
  31. /// @pre denominator > 0
  32. // ˫΍
  33. rational(int num, int den)
  34. : numerator(num), denominator(den)
  35. {
  36. reduce();
  37. }
  38. // Ί
  39. rational(int num)
  40. : numerator(num), denominator(1)
  41. {}
  42. // ΞΊ
  43. rational()
  44. : numerator(0), denominator(1)
  45. {/*empty*/}
  46. // ㊽Ί
  47. rational(double r)
  48. : numerator(static_cast<int>(r * 10000)), denominator(10000)
  49. {
  50. reduce();
  51. }
  52. /// Assign a numerator and a denominator, then reduce to normal form.
  53. /// @param num numerator
  54. /// @param den denominator
  55. /// @pre denominator > 0
  56. void assign(int num, int den)
  57. {
  58. numerator = num;
  59. denominator = den;
  60. reduce();
  61. }
  62.  
  63. rational& operator=(int num)
  64. {
  65. this->numerator = num;
  66. this->denominator = 1;
  67. return *this;
  68. }
  69.  
  70. /// Reduce the numerator and denominator by their GCD.
  71. void reduce()
  72. {
  73. assert(denominator != 0);
  74. if (denominator < 0)
  75. {
  76. denominator = -denominator;
  77. numerator = -numerator;
  78. }
  79. int div(gcd(numerator, denominator));
  80. numerator = numerator / div;
  81. denominator = denominator / div;
  82. }
  83.  
  84. /** Converting to Floating-point Types */
  85. float as_float()
  86. {
  87. return static_cast<float>(numerator) / denominator;
  88. }
  89.  
  90. double as_double()
  91. {
  92. return numerator / static_cast<double>(denominator);
  93. }
  94.  
  95. long double as_long_double()
  96. {
  97. return static_cast<long double>(numerator) /
  98. static_cast<long double>(denominator);
  99. }
  100.  
  101. int numerator; ///< numerator gets the sign of the rational value
  102. int denominator; ///< denominator is always positive
  103. };
  104.  
  105. /// Compare two rational numbers for equality.
  106. /// @pre @p a and @p b are reduced to normal form
  107. bool operator==(rational const& a, rational const& b)
  108. {
  109. return a.numerator == b.numerator && a.denominator == b.denominator; // ==
  110. }
  111.  
  112. /// Compare two rational numbers for inequality.
  113. /// @pre @p a and @p b are reduced to normal form
  114. inline bool operator!=(rational const& a, rational const& b)
  115. {
  116. return ! (a == b); // !=
  117. }
  118.  
  119. /// Compare two rational numbers for less-than.
  120. bool operator<(rational const& a, rational const& b)
  121. {
  122. return a.numerator * b.denominator < b.numerator * a.denominator; // <
  123. }
  124.  
  125. /// Compare two rational numbers for less-than-or-equal.
  126. inline bool operator<=(rational const& a, rational const& b)
  127. {
  128. return ! (b < a); // <=
  129. }
  130.  
  131. /// Compare two rational numbers for greater-than.
  132. inline bool operator>(rational const& a, rational const& b)
  133. {
  134. return b < a; // >
  135. }
  136.  
  137. /// Compare two rational numbers for greater-than-or-equal.
  138. inline bool operator>=(rational const& a, rational const& b)
  139. {
  140. return ! (b > a); // >=
  141. }
  142.  
  143. /** Addition Operator for the rational Type */
  144. rational operator+(rational const& lhs, rational const& rhs)
  145. {
  146. return rational(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
  147. lhs.denominator * rhs.denominator); // +
  148. }
  149.  
  150. rational operator-(rational const& r)
  151. {
  152. return rational(-r.numerator, r.denominator); // -
  153. }
  154.  
  155. /** Arithmetic Operators for the rational Type */
  156. rational operator-(rational const& lhs, rational const& rhs)
  157. {
  158. return rational(lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator,
  159. lhs.denominator * rhs.denominator); // -
  160. }
  161.  
  162. rational operator*(rational const& lhs, rational const& rhs)
  163. {
  164. return rational(lhs.numerator * rhs.numerator,
  165. lhs.denominator * rhs.denominator); // *
  166. }
  167.  
  168. rational operator/(rational const& lhs, rational const& rhs)
  169. {
  170. return rational(lhs.numerator * rhs.denominator,
  171. lhs.denominator * rhs.numerator); // /
  172. }
  173. /** Input and output operators for the rational Type */
  174. istream& operator>>(std::istream& in, rational& rat)
  175. {
  176. int n(0), d(0);
  177. char sep('\0');
  178. if ( !(in >> n >> sep)) // read integer and separator from istream
  179. // Error reading the numerator or the separator character.
  180. in.setstate(std::cin.failbit); // if con`t read them , return failbit, and exit or print tips
  181. else if (sep != '/')
  182. {
  183. // Read numerator successfully, but it is not followed by /.
  184. // Push sep back into the input stream, so the next input operation
  185. // will read it.
  186. in.unget(); // only one integer ,no separator
  187. rat.assign(n, 1);
  188. }
  189. else if (in >> d) // separator is /
  190. // Successfully read numerator, separator, and denominator.
  191. rat.assign(n, d); // assign the rantional
  192. else
  193. // Error reading denominator.
  194. in.setstate(std::cin.failbit); // read fail
  195.  
  196. return in;
  197. }
  198.  
  199. ostream& operator<<(std::ostream& out, rational const& rat)
  200. {
  201. std::ostringstream tmp;
  202. tmp << rat.numerator;
  203. if (rat.denominator != 1)
  204. tmp << '/' << rat.denominator;
  205. out << tmp.str();
  206.  
  207. return out;
  208. }
  209.  
  210. /// Tests for failbit only
  211. bool iofailure(istream& in)
  212. {
  213. return in.fail() && !(in.bad());
  214. }
  215.  
  216. // test for istream
  217. bool infoistream(istream & in)
  218. {
  219. return !(in.bad()) && !(in.eof()) && in.fail();
  220. }
  221.  
  222. void print(rational const & result)
  223. {
  224. cout << result.numerator << '/' << result.denominator << '\n' ;
  225. }
  226.  
  227. int main()
  228. {
  229. rational rat_a (1,9);
  230. rational rat_b (2,7);
  231. rational rat_c (3,8);
  232. rational rat_d (4,7);
  233. rational result_a(rat_a + rat_b);
  234. rational result_b(rat_a / rat_b);
  235. rational result_c(rat_c - rat_a); // the result < 0
  236. rational result_d(- rat_d); // override -
  237. bool result_bool_a (rat_a > rat_d);
  238. bool result_bool_b (rat_a <= rat_c);
  239.  
  240. print(rat_a);
  241. print(rat_b);
  242. print(rat_c);
  243. print(rat_d);
  244. print(result_a);
  245. print(result_b);
  246. print(result_c);
  247. print(result_d);
  248. cout << result_bool_a << '\n';
  249. cout << result_bool_b << '\n';
  250.  
  251. rational result(3 * rational(1, 3));
  252. cout << result.numerator << '/' << result.denominator << '\n';
  253.  
  254. rational pi(355, 113);
  255. rational bmi(90*100*100, 180*180); // Body-mass index of 90 kg, 180 cm
  256. double circumference(0), radius(10);
  257.  
  258. circumference = 2 * pi.as_double() * radius;
  259. cout << "circumference of circle with radius " << radius << " is about "
  260. << circumference << '\n';
  261. cout << "bmi = " << bmi.as_float() << '\n';
  262.  
  263. // cout << "Test for I/O operator !" << endl;
  264. // Ԭʽ
  265. //cout << "ʤȫʽז,rational Ѝ"
  266.  
  267. rational inte(12);
  268. rational dou(123.2);
  269. rational z(12,213);
  270. //cout << rational r() << endl;
  271. cout << inte << endl;
  272. cout << dou << endl;
  273. cout << z << endl;
  274. /*
  275. while (cin)
  276. {
  277. if (cin >> r)
  278. // Read succeeded, so no error state is set in the stream.
  279. cout << r << '\n';
  280. else if (iofailure(cin))
  281. {
  282. // Only failbit is set, meaning invalid input. Clear the error state,
  283. // and then skip the rest of the input line.
  284. cout << "δ֪펳,ǫ̐ʤȫ!" << endl;
  285. cin.clear();
  286. cin.ignore(numeric_limits<int>::max(), '\n');
  287. }
  288. else if (infoistream(cin))
  289. {
  290. cout << "Error input " << endl;
  291. }
  292. }
  293.  
  294. if (cin.bad())
  295. cerr << "Unrecoverable input error\n";
  296. */
  297. }

结果例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3Vvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

如今我们就能够继续深入的学习与类相关的编写了.

C++基础学习教程(五)的更多相关文章

  1. C++基础 学习笔记五:重载之运算符重载

    C++基础 学习笔记五:重载之运算符重载 什么是运算符重载 用同一个运算符完成不同的功能即同一个运算符可以有不同的功能的方法叫做运算符重载.运算符重载是静态多态性的体现. 运算符重载的规则 重载公式 ...

  2. redis学习教程五《管道、分区》

    redis学习教程五<管道.分区>  一:管道 Redis是一个TCP服务器,支持请求/响应协议. 在Redis中,请求通过以下步骤完成: 客户端向服务器发送查询,并从套接字读取,通常以阻 ...

  3. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  4. salesforce lightning零基础学习(十五) 公用组件之 获取表字段的Picklist(多语言)

    此篇参考:salesforce 零基础学习(六十二)获取sObject中类型为Picklist的field values(含record type) 我们在lightning中在前台会经常碰到获取pi ...

  5. java基础学习笔记五(抽象类)

    java基础学习总结——抽象类 抽象类介绍

  6. C++基础学习教程(一)

    開始自己的C++复习进阶之路. 声明: 这次写的博文纯当是一个回想复习的教程.一些非常基础的知识将不再出现.或者一掠而过,这次的主要风格就是演示样例代码非常多~~~ 全部代码在Ubuntu 14.04 ...

  7. Java基础学习笔记(五) - 常用的API

    API介绍 概念:API 即应用编程程序接口.Java API是JDK中提供给我们使用的类说明文档,这些类将底层的代码实现封装.无需关心这些类是如何实现,只需要学习如何使用. 使用:通过API找到需要 ...

  8. spring boot基础学习教程

    Spring boot 标签(空格分隔): springboot HelloWorld 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新 ...

  9. C++基础学习教程(八)

    转载请注明出处:http://blog.csdn.net/suool/article/details/38300117 引入 在进行下一步的学习之前,我们须要厘清几个概念. RAII 首先介绍一个编程 ...

随机推荐

  1. Lambda演算(一)大道至简

    从选择信息专业开始到回炉读书为止,四舍五入码了八年代码.对于计算机科学的认知仅限于: 1)使用不同语言实现特定功能 2)实现不同算法以增进系统性能 3)搭建不同架构进行组织管理   但从未思考一些本质 ...

  2. 火焰图&perf命令

    最近恶补后端技术,发现还是很多不懂,一直写业务逻辑容易迷失,也没有成长.自己做系统,也习惯用自己已知的知识来解决,以后应该多点调研,学到更多的东西应用起来. 先学一个新的性能分析命令. NAME pe ...

  3. RabbitMQ (一)

    学习RabbitMQ 可以先先了解一下AMQP 简单介绍: AMQP从一开始就设计成为开放标准,以解决众多的消息队列需求和拓扑结构问题,凭借开发,任何人都可以执行这一标准.针对标准编码的任何人都可以和 ...

  4. 深度理解python中的元类

    本文转自:(英文版)https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python   (翻译版)   http:// ...

  5. php基础知识一

    1.PHP是什么: 开源,免费的,跨平台的 2.PHP能做什么: 3.PHP的特点: 4.PHP的标记风格: <?php ?> <? ?> <script languag ...

  6. 网络流24题之最长k可重区间集问题

    对于每个点向后一个点连流量为k费用为0的边 对每一区间连l到r流量为1费用为r-l的边 然后最小费用最大流,输出取反 一开始写的r-l+1错了半天... By:大奕哥 #include<bits ...

  7. 【tarjan+拓扑】BZOJ3887-[Usaco2015 Jan]Grass Cownoisseur

    [题目大意] 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1) [思路] 首先 ...

  8. bzoj 4097: [Usaco2013 dec]Vacation Planning

    4097: [Usaco2013 dec]Vacation Planning Description Air Bovinia is planning to connect the N farms (1 ...

  9. hdu 1171 多重背包

    题意:给出价值和数量,求能分开的最近的两个总价值,例如10,20*2,30,分开就是40,40 链接:点我 #include<cstdio> #include<iostream> ...

  10. bzoj 4836: [Lydsy2017年4月月赛]二元运算 -- 分治+FFT

    4836: [Lydsy2017年4月月赛]二元运算 Time Limit: 8 Sec  Memory Limit: 128 MB Description 定义二元运算 opt 满足   现在给定一 ...