这一讲我们集中解说类和他的一些特性.首先我们从自己定义一个有理数类来開始.
在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 << ' '; }
执行结果例如以下:
如今能够看出这确实是一个类型,那么如今我们就进一步扩展这个类型,首先,添加一个分数自己主动约分的功能.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 << ' '; }
运行结果例如以下:
注意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 << ' '; }
在上面的代码中,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 << ' '; }
当中的构造函数就是
运行结果例如以下:
对上面的构造函数分析。能够知道构造函数的形式和要求为:
- 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 << ' ' ; } 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 << ' '; cout << result_bool_b << ' '; rational result(3 * rational(1, 3)); cout << result.numerator << '/' << result.denominator << ' '; }
上面就是全部的运算符重载的函数,当中的构造函数有两个,一个是双參数一个是单參数,所以在上面的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 << ' ' ; } 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 << ' '; cout << result_bool_b << ' '; rational result(3 * rational(1, 3)); cout << result.numerator << '/' << result.denominator << ' '; 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 << ' '; cout << "bmi = " << bmi.as_float() << ' '; }
运行结果例如以下:
如今我们的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('