C++基础学习教程(五)

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

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

/// Represent a rational number.
struct rational
{
  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

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

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

/** Using a Class and Its Members */
#include <iostream>
#include <ostream>

/// Represent a rational number.
struct rational
{
  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

int main()
{
  rational pi;
  pi.numerator = 355;
  pi.denominator = 113;
  std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
}

执行结果例如以下:

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

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

/** @file ReduceduceRational.cpp */
/** Adding the reduce Member Function */
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <ostream>

/// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
int gcd(int n, int m)
{
  n = abs(n);
  while (m != 0) {
    int tmp(n % m);
    n = m;
    m = tmp;
  }
  return n;
}

/// Represent a rational number.
struct rational
{
  /// Reduce the numerator and denominator by their GCD.
  void reduce()
  {
    assert(denominator != 0);
    int div(gcd(numerator, denominator));
    numerator = numerator / div;
    denominator = denominator / div;
  }

  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

int main()
{
  rational pi;
  pi.numerator = 1420;
  pi.denominator = 452;
  pi.reduce();
  std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
}

运行结果例如以下:

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

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

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

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

/** @file ReduceAssignRational.cpp */
/** Adding the assign Member Function */
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <ostream>
/// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
int gcd(int n, int m)
{
  n = abs(n);
  while (m != 0) {
    int tmp(n % m);
    n = m;
    m = tmp;
  }
  return n;
}

/// Represent a rational number.
struct rational
{
  /// Assign a numerator and a denominator, then reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  void assign(int num, int den)
  {
    numerator = num;
    denominator = den;
    reduce();
  }

  /// Reduce the numerator and denominator by their GCD.
  void reduce()
  {
    assert(denominator != 0);
    int div(gcd(numerator, denominator));
    numerator = numerator / div;
    denominator = denominator / div;
  }

  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

int main()
{
  rational pi;
  pi.assign(1420, 452);
  std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
}

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

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

/** @file ConstructRational.cpp */
/** Adding the Ability to Initialize a rational Object */
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <ostream>

/// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
int gcd(int n, int m)
{
  n = abs(n);
  while (m != 0) {
    int tmp(n % m);
    n = m;
    m = tmp;
  }
  return n;
}

/// Represent a rational number.
struct rational
{
  /// Construct a rational object, given a numerator and a denominator.
  /// Always reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  rational(int num, int den)
  : numerator(num), denominator(den)
  {
    reduce();
  }

  /// Assign a numerator and a denominator, then reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  void assign(int num, int den)
  {
    numerator = num;
    denominator = den;
    reduce();
  }

  /// Reduce the numerator and denominator by their GCD.
  void reduce()
  {
    assert(denominator != 0);
    int div(gcd(numerator, denominator));
    numerator = numerator / div;
    denominator = denominator / div;
  }

  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

int main()
{
  rational pi(1420, 452);
  std::cout << "pi is about " << pi.numerator << "/" << pi.denominator << '\n';
}

当中的构造函数就是

运行结果例如以下:

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

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

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

 /// Reduce the numerator and denominator by their GCD.
  void reduce()
  {
    assert(denominator != 0);
    if (denominator < 0)   // if denominator < 0
    {
      denominator = -denominator;
      numerator = -numerator;
    }
    int div(gcd(numerator, denominator));
    numerator = numerator / div;
    denominator = denominator / div;
  }

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

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

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

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

/** @file OverrideOperator.cpp */
/** Overloading the Equality Operator */
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <ostream>

using namespace std;

/// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
int gcd(int n, int m)
{
  n = abs(n);
  while (m != 0) {
    int tmp(n % m);
    n = m;
    m = tmp;
  }
  return n;
}

/// Represent a rational number.
struct rational
{
  /// Construct a rational object, given a numerator and a denominator.
  /// Always reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  rational(int num, int den)
  : numerator(num), denominator(den)
  {
    reduce();
  }
  rational(int num)
  : numerator(num), denominator(1)
  {}

  /// Assign a numerator and a denominator, then reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  void assign(int num, int den)
  {
    numerator = num;
    denominator = den;
    reduce();
  }

  /// Reduce the numerator and denominator by their GCD.
  void reduce()
  {
    assert(denominator != 0);
    if (denominator < 0)
    {
      denominator = -denominator;
      numerator = -numerator;
    }
    int div(gcd(numerator, denominator));
    numerator = numerator / div;
    denominator = denominator / div;
  }

  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

/// Compare two rational numbers for equality.
/// @pre @p a and @p b are reduced to normal form
bool operator==(rational const& a, rational const& b)
{
  return a.numerator == b.numerator && a.denominator == b.denominator;  // ==
}

/// Compare two rational numbers for inequality.
/// @pre @p a and @p b are reduced to normal form
inline bool operator!=(rational const& a, rational const& b)
{
  return ! (a == b);                                                   // !=
}

/// Compare two rational numbers for less-than.
bool operator<(rational const& a, rational const& b)
{
  return a.numerator * b.denominator < b.numerator * a.denominator;      // <
}

/// Compare two rational numbers for less-than-or-equal.
inline bool operator<=(rational const& a, rational const& b)
{
  return ! (b < a);                                                    // <=
}

/// Compare two rational numbers for greater-than.
inline bool operator>(rational const& a, rational const& b)
{
  return b < a;                                                          // >
}

/// Compare two rational numbers for greater-than-or-equal.
inline bool operator>=(rational const& a, rational const& b)
{
  return ! (b > a);                                                    // >=
}

/** Addition Operator for the rational Type */
rational operator+(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
                  lhs.denominator * rhs.denominator);                   // +
}

rational operator-(rational const& r)
{
  return rational(-r.numerator, r.denominator);                         // -
}

/** Arithmetic Operators for the rational Type */
rational operator-(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator,
                  lhs.denominator * rhs.denominator);                  // -
}

rational operator*(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.numerator,
                  lhs.denominator * rhs.denominator);                  // *
}

rational operator/(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.denominator,
                  lhs.denominator * rhs.numerator);                   // /
}

void print(rational const & result)
{
    cout << result.numerator << '/' << result.denominator << '\n' ;
}

int main()
{
  rational rat_a (1,9);
  rational rat_b (2,7);
  rational rat_c (3,8);
  rational rat_d (4,7);
  rational result_a(rat_a + rat_b);
  rational result_b(rat_a / rat_b);
  rational result_c(rat_c - rat_a);  // the result < 0
  rational result_d(- rat_d);        // override -
  bool result_bool_a (rat_a > rat_d);
  bool result_bool_b (rat_a <= rat_c);

  print(rat_a);
  print(rat_b);
  print(rat_c);
  print(rat_d);
  print(result_a);
  print(result_b);
  print(result_c);
  print(result_d);
  cout << result_bool_a << '\n';
  cout << result_bool_b << '\n';

 rational result(3 * rational(1, 3));
 cout << result.numerator << '/' << result.denominator << '\n';
}

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

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

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

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

/** Converting to Floating-point Types */
struct rational
{
  float as_float()
  {
    return static_cast<float>(numerator) / denominator;
  }

  double as_double()
  {
    return numerator / static_cast<double>(denominator);
  }

  long double as_long_double()
  {
    return static_cast<long double>(numerator) /
           static_cast<long double>(denominator);
  }
};

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

/** @file OverrideOperator.cpp */
/** Overloading the Equality Operator */
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <ostream>

using namespace std;

/// Compute the greatest common divisor of two integers, using Euclid’s algorithm.
int gcd(int n, int m)
{
  n = abs(n);
  while (m != 0) {
    int tmp(n % m);
    n = m;
    m = tmp;
  }
  return n;
}

/// Represent a rational number.
struct rational
{
  /// Construct a rational object, given a numerator and a denominator.
  /// Always reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  rational(int num, int den)
  : numerator(num), denominator(den)
  {
    reduce();
  }
  rational(int num)
  : numerator(num), denominator(1)
  {}

  /// Assign a numerator and a denominator, then reduce to normal form.
  /// @param num numerator
  /// @param den denominator
  /// @pre denominator > 0
  void assign(int num, int den)
  {
    numerator = num;
    denominator = den;
    reduce();
  }

  /// Reduce the numerator and denominator by their GCD.
  void reduce()
  {
    assert(denominator != 0);
    if (denominator < 0)
    {
      denominator = -denominator;
      numerator = -numerator;
    }
    int div(gcd(numerator, denominator));
    numerator = numerator / div;
    denominator = denominator / div;
  }

  /** Converting to Floating-point Types */
  float as_float()
  {
    return static_cast<float>(numerator) / denominator;
  }

  double as_double()
  {
    return numerator / static_cast<double>(denominator);
  }

  long double as_long_double()
  {
    return static_cast<long double>(numerator) /
           static_cast<long double>(denominator);
  }

  int numerator;     ///< numerator gets the sign of the rational value
  int denominator;   ///< denominator is always positive
};

/// Compare two rational numbers for equality.
/// @pre @p a and @p b are reduced to normal form
bool operator==(rational const& a, rational const& b)
{
  return a.numerator == b.numerator && a.denominator == b.denominator;  // ==
}

/// Compare two rational numbers for inequality.
/// @pre @p a and @p b are reduced to normal form
inline bool operator!=(rational const& a, rational const& b)
{
  return ! (a == b);                                                   // !=
}

/// Compare two rational numbers for less-than.
bool operator<(rational const& a, rational const& b)
{
  return a.numerator * b.denominator < b.numerator * a.denominator;      // <
}

/// Compare two rational numbers for less-than-or-equal.
inline bool operator<=(rational const& a, rational const& b)
{
  return ! (b < a);                                                    // <=
}

/// Compare two rational numbers for greater-than.
inline bool operator>(rational const& a, rational const& b)
{
  return b < a;                                                          // >
}

/// Compare two rational numbers for greater-than-or-equal.
inline bool operator>=(rational const& a, rational const& b)
{
  return ! (b > a);                                                    // >=
}

/** Addition Operator for the rational Type */
rational operator+(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
                  lhs.denominator * rhs.denominator);                   // +
}

rational operator-(rational const& r)
{
  return rational(-r.numerator, r.denominator);                         // -
}

/** Arithmetic Operators for the rational Type */
rational operator-(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator,
                  lhs.denominator * rhs.denominator);                  // -
}

rational operator*(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.numerator,
                  lhs.denominator * rhs.denominator);                  // *
}

rational operator/(rational const& lhs, rational const& rhs)
{
  return rational(lhs.numerator * rhs.denominator,
                  lhs.denominator * rhs.numerator);                   // /
}

void print(rational const & result)
{
    cout << result.numerator << '/' << result.denominator << '\n' ;
}

int main()
{
  rational rat_a (1,9);
  rational rat_b (2,7);
  rational rat_c (3,8);
  rational rat_d (4,7);
  rational result_a(rat_a + rat_b);
  rational result_b(rat_a / rat_b);
  rational result_c(rat_c - rat_a);  // the result < 0
  rational result_d(- rat_d);        // override -
  bool result_bool_a (rat_a > rat_d);
  bool result_bool_b (rat_a <= rat_c);

  print(rat_a);
  print(rat_b);
  print(rat_c);
  print(rat_d);
  print(result_a);
  print(result_b);
  print(result_c);
  print(result_d);
  cout << result_bool_a << '\n';
  cout << result_bool_b << '\n';

  rational result(3 * rational(1, 3));
  cout << result.numerator << '/' << result.denominator << '\n';

  rational pi(355, 113);
  rational bmi(90*100*100, 180*180); // Body-mass index of 90 kg, 180 cm
  double circumference(0), radius(10);

  circumference = 2 * pi.as_double() * radius;
  cout << "circumference of circle with radius " << radius << " is about "
            << circumference << '\n';
  cout << "bmi = " << bmi.as_float() << '\n';

}

运行结果例如以下:

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

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

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

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

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

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

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

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

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

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

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

/** @file OverrideIO.cpp */
/** Input Operator */
#include <ios>      // declares failbit, etc.
#include <istream>  // declares std::istream and the necessary >> operators

std::istream& operator>>(std::istream& in, rational& rat)
{
  int n(0), d(0);
  char sep('\0');
  if (not (in >> n >> sep))
    // Error reading the numerator or the separator character.
    in.setstate(std::cin.failbit);
  else if (sep != '/')
  {
    // Read numerator successfully, but it is not followed by /.
    // Push sep back into the input stream, so the next input operation
    // will read it.
    in.unget();
    rat.assign(n, 1);
  }
  else if (in >> d)
    // Successfully read numerator, separator, and denominator.
    rat.assign(n, d);
  else
    // Error reading denominator.
    in.setstate(std::cin.failbit);

  return in;
}

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

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

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

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

/** @file Output.cpp */
/** Output Operator */
#include <ostream>  // declares the necessary << operators
#include <sstream>  // declares the std::ostringstream type

std::ostream& operator<<(std::ostream& out, rational const& rat)
{
  std::ostringstream tmp;
  tmp << rat.numerator;
  if (rat.denominator != 1)
    tmp << '/' << rat.denominator;
  out << tmp.str();

  return out;
}

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

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

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

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

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

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

代码例如以下,:

/// Tests for failbit only
bool iofailure(istream& in)
{
  return in.fail() && !(in.bad());
}
// test for istream
bool infoistream(istream & in)
{
  return !(in.bad()) && !(in.eof()) && in.fail()
}

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

/** First Version of the Assignment Operator */
struct rational
{
  rational& operator=(rational const& rhs)
  {
    numerator = rhs.numerator;
    denominator = rhs.denominator;
    return *this;
  }
};

第二种实现例如以下:

/** Assignment Operator with Explicit Use of this-> */
struct rational
{
  rational& operator=(rational const& that)
  {
    this->numerator = that.numerator;
    this->denominator = that.denominator;
    return *this;
  }
};

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

/** Assignment of an Integer to a Rational */
struct rational
{
  rational& operator=(int num)
  {
    this->numerator = num;
    this->denominator = 1;
    return *this;
  }
};

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

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

/** Overloaded Constructors for rational */
struct rational
{
  rational()
  : numerator(0), denominator(1)
  {/*empty*/}

  rational(int num)
  : numerator(num), denominator(1)
  {/*empty*/}

  rational(int num, int den)
  : numerator(num), denominator(den)
  {
    reduce();
  }
};

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

/** @file OverrideOperator.cpp */
/** Overloading the Equality Operator */
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <sstream>

using namespace std;

/// Compute the greatest common divisor of two integers, using Euclids algorithm.
int gcd(int n, int m)
{
    n = abs(n);
    while (m != 0)
    {
        int tmp(n % m);
        n = m;
        m = tmp;
    }
    return n;
}

/// Represent a rational number.
struct rational
{
    /// Construct a rational object, given a numerator and a denominator.
    /// Always reduce to normal form.
    /// @param num numerator
    /// @param den denominator
    /// @pre denominator > 0
    // ??
    rational(int num, int den)
        : numerator(num), denominator(den)
    {
        reduce();
    }
    // ?
    rational(int num)
        : numerator(num), denominator(1)
    {}
    // Ξ?
    rational()
        : numerator(0), denominator(1)
    {/*empty*/}
    // ??
    rational(double r)
        : numerator(static_cast<int>(r * 10000)), denominator(10000)
    {
        reduce();
    }
    /// Assign a numerator and a denominator, then reduce to normal form.
    /// @param num numerator
    /// @param den denominator
    /// @pre denominator > 0
    void assign(int num, int den)
    {
        numerator = num;
        denominator = den;
        reduce();
    }

    rational& operator=(int num)
    {
        this->numerator = num;
        this->denominator = 1;
        return *this;
    }

    /// Reduce the numerator and denominator by their GCD.
    void reduce()
    {
        assert(denominator != 0);
        if (denominator < 0)
        {
            denominator = -denominator;
            numerator = -numerator;
        }
        int div(gcd(numerator, denominator));
        numerator = numerator / div;
        denominator = denominator / div;
    }

    /** Converting to Floating-point Types */
    float as_float()
    {
        return static_cast<float>(numerator) / denominator;
    }

    double as_double()
    {
        return numerator / static_cast<double>(denominator);
    }

    long double as_long_double()
    {
        return static_cast<long double>(numerator) /
            static_cast<long double>(denominator);
    }

    int numerator;     ///< numerator gets the sign of the rational value
    int denominator;   ///< denominator is always positive
};

/// Compare two rational numbers for equality.
/// @pre @p a and @p b are reduced to normal form
bool operator==(rational const& a, rational const& b)
{
    return a.numerator == b.numerator && a.denominator == b.denominator;  // ==
}

/// Compare two rational numbers for inequality.
/// @pre @p a and @p b are reduced to normal form
inline bool operator!=(rational const& a, rational const& b)
{
    return ! (a == b);                                                   // !=
}

/// Compare two rational numbers for less-than.
bool operator<(rational const& a, rational const& b)
{
    return a.numerator * b.denominator < b.numerator * a.denominator;      // <
}

/// Compare two rational numbers for less-than-or-equal.
inline bool operator<=(rational const& a, rational const& b)
{
    return ! (b < a);                                                    // <=
}

/// Compare two rational numbers for greater-than.
inline bool operator>(rational const& a, rational const& b)
{
    return b < a;                                                          // >
}

/// Compare two rational numbers for greater-than-or-equal.
inline bool operator>=(rational const& a, rational const& b)
{
    return ! (b > a);                                                    // >=
}

/** Addition Operator for the rational Type */
rational operator+(rational const& lhs, rational const& rhs)
{
    return rational(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
        lhs.denominator * rhs.denominator);                   // +
}

rational operator-(rational const& r)
{
    return rational(-r.numerator, r.denominator);                         // -
}

/** Arithmetic Operators for the rational Type */
rational operator-(rational const& lhs, rational const& rhs)
{
    return rational(lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator,
        lhs.denominator * rhs.denominator);                  // -
}

rational operator*(rational const& lhs, rational const& rhs)
{
    return rational(lhs.numerator * rhs.numerator,
        lhs.denominator * rhs.denominator);                  // *
}

rational operator/(rational const& lhs, rational const& rhs)
{
    return rational(lhs.numerator * rhs.denominator,
        lhs.denominator * rhs.numerator);                   // /
}
/** Input and output operators for the rational Type */
istream& operator>>(std::istream& in, rational& rat)
{
    int n(0), d(0);
    char sep('\0');
    if ( !(in >> n >> sep))                 // read integer and separator from istream
        // Error reading the numerator or the separator character.
        in.setstate(std::cin.failbit);          // if con`t read them , return failbit, and exit or print tips
    else if (sep != '/')
    {
        // Read numerator successfully, but it is not followed by /.
        // Push sep back into the input stream, so the next input operation
        // will read it.
        in.unget();                            // only one integer ,no separator
        rat.assign(n, 1);
    }
    else if (in >> d)                        // separator is /
        // Successfully read numerator, separator, and denominator.
        rat.assign(n, d);                      // assign the rantional
    else
        // Error reading denominator.
        in.setstate(std::cin.failbit);         // read fail

    return in;
}

ostream& operator<<(std::ostream& out, rational const& rat)
{
    std::ostringstream tmp;
    tmp << rat.numerator;
    if (rat.denominator != 1)
        tmp << '/' << rat.denominator;
    out << tmp.str();

    return out;
}

/// Tests for failbit only
bool iofailure(istream& in)
{
    return in.fail() && !(in.bad());
}

// test for istream
bool infoistream(istream & in)
{
    return !(in.bad()) && !(in.eof()) && in.fail();
}

void print(rational const & result)
{
    cout << result.numerator << '/' << result.denominator << '\n' ;
}

int main()
{
    rational rat_a (1,9);
    rational rat_b (2,7);
    rational rat_c (3,8);
    rational rat_d (4,7);
    rational result_a(rat_a + rat_b);
    rational result_b(rat_a / rat_b);
    rational result_c(rat_c - rat_a);  // the result < 0
    rational result_d(- rat_d);        // override -
    bool result_bool_a (rat_a > rat_d);
    bool result_bool_b (rat_a <= rat_c);

    print(rat_a);
    print(rat_b);
    print(rat_c);
    print(rat_d);
    print(result_a);
    print(result_b);
    print(result_c);
    print(result_d);
    cout << result_bool_a << '\n';
    cout << result_bool_b << '\n';

    rational result(3 * rational(1, 3));
    cout << result.numerator << '/' << result.denominator << '\n';

    rational pi(355, 113);
    rational bmi(90*100*100, 180*180); // Body-mass index of 90 kg, 180 cm
    double circumference(0), radius(10);

    circumference = 2 * pi.as_double() * radius;
    cout << "circumference of circle with radius " << radius << " is about "
        << circumference << '\n';
    cout << "bmi = " << bmi.as_float() << '\n';

    //  cout << "Test for I/O operator !" << endl;
    // ??
    //cout << "????,rational ?"

    rational inte(12);
    rational dou(123.2);
    rational z(12,213);
    //cout << rational r() << endl;
    cout <<  inte << endl;
    cout << dou << endl;
    cout << z << endl;
    /*
    while (cin)
    {
    if (cin >> r)
    // Read succeeded, so no error state is set in the stream.
    cout << r << '\n';
    else if (iofailure(cin))
    {
    // Only failbit is set, meaning invalid input. Clear the error state,
    // and then skip the rest of the input line.
    cout << "δ??,????!" << endl;
    cin.clear();
    cin.ignore(numeric_limits<int>::max(), '\n');
    }
    else if (infoistream(cin))
    {
    cout << "Error input " << endl;
    }
    }

      if (cin.bad())
      cerr << "Unrecoverable input error\n";
    */
}

结果例如以下:

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

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

时间: 2024-12-21 02:52:36

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

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

开始自己的C++复习进阶之路. 声明: 这次写的博文纯当是一个回顾复习的教程,一些非常基础的知识将不再出现,或者一掠而过,这次的主要风格就是示例代码很多~~~ 所有代码在Ubuntu 14.04 LTS 版,GCC4.8.1(g++)编译通过.其他的平台没试过,估计有些代码在VC6.0下面通过不了,因为有些语言特性是C++11标准的. 下面就是正文的开始吧. 一.C++必须说和必须略过的一些东西 1.工具 工具的话,简答的编程貌似现在已经习惯了在GCC(g++)下了.Linux平台下面,一般不需

C++基础学习教程(二)

接上一节内容 2.5条件和逻辑 自增和自减操作符 这个主要区别就是在前和后,大多数学习过其他语言的应该都知道.所以,一个程序带过. 示例如下: /************************************************************************* > File Name: list1001_++.cpp > Author: suool_hu > Mail: [email protected] > Created Time: 2014年0

javascript基础学习(五)

javascript之函数 学习要点: 函数的介绍 函数的参数 函数的属性和方法 系统函数 一.函数的介绍 1.函数就是一段javascript代码.可以分为用户自定义函数和系统函数.   如果一个函数是javascript内置的函数,就称为系统函数.如果函数是自己编写的函数,就是自定义函数. 2.在javascript用function来定义一个函数.function 函数名(参数1,参数2,...){<语句块>  return 返回值} (PS:return语句可以省略) 3.函数的嵌套定

C++基础学习教程(三)

承接上一讲. 2.7文件I/O 关于读写文件,C++中有一个专门的头文件<fstream>. 首先是读文件示例,如下: </pre><pre> /************************************************************************* > File Name: list1301_file.cpp > Author: suool > Mail: [email protected] > Cre

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

转载请注明出处:http://blog.csdn.net/suool/article/details/38300117 引入 在进行下一步的学习之前,我们需要厘清几个概念. RAII 首先介绍一个编程习语,"RAII"(ResourceAcquisition Is Initialization,资源获取即为初始化),他描述了利用构造函数\析构函数,并在函数返回时自动析构的机制.简言之,RAII意为构造函数获取一种资源;打开一个文件,一个网络连接,或仅仅是从某I/O流中复制一些标志.这种

C++基础学习教程(四)

2.9字符专题 2.9.1类型同义词 也就是typedef声明,这个东西就是相当于起绰号,为了方便记忆和简化而生.相信在学习其他语言的时候一定有所了解,在此不再赘述. 再次示例一个之前写过的用typedef改写的程序: /************************************************************************* > File Name: char_count.cpp > Author: suool > Mail: [email pr

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

redis学习教程五<管道.分区> 一:管道 Redis是一个TCP服务器,支持请求/响应协议. 在Redis中,请求通过以下步骤完成: 客户端向服务器发送查询,并从套接字读取,通常以阻塞的方式,用于服务器响应. 服务器处理命令并将响应发送回客户端. 管道的意义 管道的基本含义是,客户端可以向服务器发送多个请求,而不必等待回复,并最终在一个步骤中读取回复. 示例 要检查Redis管道,只需启动Redis实例,并在终端中键入以下命令. (echo -en "PING\r\n SET t

MongDB基础学习(五)——投影,分页,排序,聚合

MongDB基础学习(五)--投影,分页,排序,聚合 基础语法的学习 马上就要过年啦,明天也是情人,后天就要放假啦,希望自己能够在放假前把五和六的章节写完.很高兴的一点,昨天看到我写的"[MongoDB for Java]Java操作MongoDB"能够被放到CSDN博客的首页的头条,本章节的学习内容如下: (1)    MongoDB的投影的语法和示例学习 (2)    MongoDB的Limit()和Skip()方法学习,从而达到分页的效果 (3)    MongoDB的sort(

C++基础学习教程(七)----类编写及类的两个特性解析---&gt;多态&amp;继承

类引入 到目前为止我们所写的自定义类型都是关键字struct,从现在起我们将采用class方式定义类,这种方式对于学习过其他高级语言包括脚本(Such as Python)的人来说再熟悉不过了. 但是在写之前我们还是需要比较一下用struct和class之间有什么区别. 首先对于struct,在C兼容性方面很重要,尽管C++是有别于C的另一门语言,但许多程序还是必须与C交互,C++有两个重要功能,可以方便的与C交互.其中之一的就是POD,即是Plain Old Data(简单旧式数据)的缩写.

C++基础学习教程(六)----类编写的前情回顾以及项目实战(1)

在开始类的编写之前我们依然需要回顾整理一下前面所说的内容,(前面虽然是一个自定义数据类型的实现过程,但是内容有点繁杂). 先看一段代码: /** @file calssStruct.cpp */ /** Member Functions for Class point */ #include <cmath> // for sqrt and atan using namespace std; struct point { point() : x_(0.0), y_(0.0) {} point(d