C++中操作符重载的使用

当运算符作用于类类型的运算对象时,可以通过运算符重载重新定义该运算符的含义。重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符共同组成。和其它函数一样,重载的运算符也包含返回类型、参数列表以及函数体。

重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个。对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。除了重载的函数调用运算符operator ()之外,其它重载运算符不能含有默认实参。

         如果一个运算符函数是成员函数,则它的第一个(左侧)运算对象绑定到隐式的this指针上,因此,成员运算符函数的(显示)参数数量比运算符的运算对象总数少一个。当一个重载的运算符是成员函数时,this绑定到左侧运算对象。成员运算符函数的(显示)参数数量比运算对象的数量少一个

         对于一个运算符函数来说,它或者是类的成员,或者至少含有一个类类型的参数。这一约定意味着当运算符作用于内置类型的运算对象时,我们无法改变该运算符的含义。

         我们可以重载大多数(但不是全部)运算符,如下表:

C++中操作符重载的使用_第1张图片

         我们只能重载已有的运算符,而无权发明新的运算符号。有四个符号(+、-、*、&)既是一元运算符也是二元运算符,所有这些运算符都能被重载,从参数的数量我们可以推断到底定义的是哪种运算符。

         对于一个重载的运算符来说,其优先级和结合律与对应的内置运算符保持一致。

         某些运算符不应该被重载:因为使用重载的运算符本质上是一次函数调用,所以这些关于运算对象求值顺序的规则无法应用到重载的运算符上。特别是,逻辑与运算符、逻辑或运算符和逗号运算符的运算对象求值顺序规则无法保留下来。除此之外,&&和||运算符的重载版本也无法保留内置运算符的短路求值属性,两个运算对象总是会被求值。还有一个原因使得我们一般不重载逗号运算符和取地址运算符:C++语言已经定义了这两种运算符用于类类型对象时的特殊含义,这一点与大多数运算符都不相同。因为这两种运算符已经有了内置的含义,所以一般来说它们不应该被重载,否则它们的行为将异于常态,从而导致类的用户无法适应。通常情况下,不应该重载逗号、取地址、逻辑与和逻辑或运算符

         赋值和复合赋值运算符:赋值运算符的行为与复合版本的类似:赋值之后,左侧运算对象和右侧运算对象的相等,并且运算符应该返回它左侧运算对象的一个引用。重载的赋值运算符应该继承而非违背其内置版本的含义。如果类含有算术运算符或者位运算符,则最好也提供对应的复合赋值运算符。

         选择作为成员或者非成员:当我们定义重载的运算符时,必须首先决定是将其声明为类的成员函数还是声明为一个普通的非成员函数。在某些时候我们别无选择,因为有的运算符必须作为成员;另一些情况下,运算符作为普通函数比作为成员更好。

         下面的准则有助于我们在将运算符定义为成员函数还是普通的非成员函数做出抉择:

         (1)、赋值(=)、小标([])、调用(())和成员访问箭头(->)运算符必须是成员。

         (2)、复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。

         (3)、改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是成员。

         (4)、具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,因此它们通常应该是普通的非成员函数。如果我们想提供含有类对象的混合类型表达式,则运算符必须定义成非成员函数。

当我们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象。

         输入和输出运算符:IO标准库分别使用>>和<<执行输入和输出操作。对于这两个运算符来说,IO库定义了用其读写内置类型的版本,而类则需要自定义适合其对象的新版本以支持IO操作。

         重载输出运算符<<:通常情况下,输出运算符的第一个形参是一个非常量ostream对象的引用。之所以ostream是非常量是因为向流写入内容会改变其状态;而该形参是引用是因为我们无法直接复制一个ostream对象。第二个形参一般来说是一个常量的引用,该常量是我们想要打印的类类型。第二个形参是引用的原因是我们希望避免复制实参;而之所以该形参可以是常量是因为(通常情况下)打印对象不会改变对象的内容。为了与其它输出运算符保持一致,operator << 一般要返回它的ostream形参。

         输出运算符尽量减少格式化操作:用于内置类型的输出运算符不太考虑格式化操作,尤其不会打印换行符,用户希望类的输出运算符也像如此行事。如果运算符打印了换行符,则用户就无法在对象的同一行内接着打印一些描述性的文本了。相反,令输出运算符尽量减少格式化操作可以使用户有权控制输出的细节。通常,输出运算符应该主要负责打印对象的内容非控制格式,输出运算符不应该打印换行符

         输入输出运算符必须是非成员函数:与iostream标准库兼容的输入输出运算符必须是普通的非成员函数,而不能是类的成员函数。否则,它们的左侧运算对象将是我们的类的一个对象。假设输入输出运算符是某个类的成员,则它们也必须是istream或ostream的成员。然而,这两个类属于标准库,并且我们无法给标准库中的类添加任何成员。因此,如果我们希望为类自定义IO运算符,则必须将其定义成非成员函数。当然,IO运算符通常需要读写类的非公有数据成员,所以IO运算符一般被声明为友元。

         重载输入运算符>>:通常情况下,输入运算符的第一个形参是运算符将要读取的流的引用,第二个形参是将要读入到的(非常量)对象的引用。该运算符通常会返回某个给定流的引用。第二个形参之所以必须是个非常量是因为输入运算符本身的目的就是将数据读入到这个对象中。输入运算符必须处理输入可能失败的情况,而输出运算符不需要。在执行输入运算符时可能发生下列错误:当流含有错误类型的数据时读取操作可能失败;当读取操作到达文件末尾或者遇到输入流的其它错误时也会失败。当读取操作发生错误时,输入运算符应该负责从错误中恢复。

         算术和关系运算符:通常情况下,我们把算术和关系运算符定义成非成员函数以允许对左侧或右侧的运算对象进行转换。因此这些运算符一般不需要改变运算对象的状态,所以形参都是常量的引用。

         算术运算符通常会计算它的两个运算对象并得到一个新值,这个值有别于任意一个运算对象,常常位于一个局部变量之内,操作完成后返回该局部变量的副本作为其结果。如果类定义了算术运算符,则它一般也会定义一个对应的复合赋值运算符。此时,最有效的方式是使用复合赋值来定义算术运算符。如果类同时定义了算术运算符和相关的复合赋值运算符,则通常情况下应该使用复合赋值来实现算术运算符

         相等运算符:通常情况下,C++中的类通过定义相等运算符来检验两个对象是否相等。也就是说,它们会比较对象的每一个数据成员,只有当所有对应的成员都相等时才认为两个对象相等:(1)、如果一个类含有判断两个对象是否相等的操作,则它显然应该把函数定义成operator == 而非一个普通的命名函数。(2)、如果类定义了operator ==,则该运算符应该能判断一组给定的对象中是否含有重复数据。(3)、通常情况下,相等运算符应该具有传递性,换句话说,如果a==b和b==c都为真,则a==c也应该为真。(4)、如果类定义了operator==,则这个类也应该定义operator !=。对于用户来说,当他们能使用==时肯定也希望能使用!=,反之亦然。(5)、相等运算符和不相等运算符中的一个应该把工作委托给另外一个,这意味着其中一个运算符应该负责实际比较对象的工作,而另一个运算符则只是调用那个真正工作的运算符。如果某个类在逻辑上有相等性的含义,则该类应该定义operator ==,这样做可以使得用户更容易使用标准库算法来处理这个类。

         关系运算符:定义了相等运算符的类也常常(但不总是)包含关系运算符。特别是,因为关联容器和一些算法要用到小于运算符,所以定义operator <会比较有用。通常情况下关系运算符应该:(1)、定义顺序关系,令其与关联容器中对关键字的要求一致;并且(2)、如果类同时也含有==运算符的话,则定义一种关系令其与==保持一致。特别是,如果两个对象是!=的,那么一个对象应该<另外一个。如果存在唯一一种逻辑可靠的<定义,则应该考虑为这个类定义<运算符。如果类同时还包含==,则当且仅当<的定义和==产生的结果一致时才定义<运算符。

         赋值运算符:我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。

         复合赋值运算符:复合赋值运算符不非得是类的成员,不过我们还是倾向于把包括复合赋值在内的所有赋值运算都定义在类的内部。为了与内置类型的复合赋值保持一致,类中的复合赋值运算符也要返回其左侧运算对象的引用赋值运算符必须定义成类的成员,复合赋值运算符通常情况下也应该这样做。这两类运算符都应该返回左侧运算对象的引用

         下标运算符:表示容器的类通常可以通过元素在容器中的位置访问元素,这些类一般会定义下标运算符operator []。下标运算符必须是成员函数。为了与下标的原始定义兼容,下标运算符通常以所访问元素的引用作为返回值,这样做的好处是下标可以出现在赋值运算符的任意一端。进一步,我们最好同时定义下标运算符的常量版本和非常量版本,当作用于一个常量对象时,下标运算符返回常量引用以确保我们不会给返回的对象赋值。如果一个类包含下标运算符,则它通常会定义两个版本:一个返回普通引用,另一个是类的常量成员并且返回常量引用

         递增和递减运算符:在迭代器类中通常会实现递增运算符(++)和递减运算符(--),这两种运算符使得类可以在元素的序列中前后移动。C++语言并不要求递增和递减运算符必须是类的成员,但是因为它们改变的正好是所操作对象的状态,所以建议将其设定为成员函数。对于内置类型来说,递增和递减运算符既有前置版本也有后置版本。同样,我们也应该为类定义两个版本的递增和递减运算符。定义递增和递减运算符的类应该同时定义前置版本和后置版本。这些运算符通常应该被定义成类的成员。

         定义前置递增/递减运算符:为了和内置版本保持一致,前置运算符应该返回递增或递减后对象的引用

         区分前置和后置运算符:要想同时定义前置和后置运算符,必须首先解决一个问题,即普通的重载形式无法区分这两种情况。前置和后置版本使用的是同一个符号,意味着其重载版本所用的名字将是相同的,并且运算对象的数量和类型也相同。为了解决这个问题,后置版本接受一个额外的(不被使用)int类型的形参。因为我们不会用到int形参,所以无须为其命名。当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参。尽管从语法上来说后置函数可以使用这个额外的形参,但是在实际过程中通常不会这么做。这个形参的唯一作用就是区分前置版本和后置版本的函数,而不是真的要在实现后置版本时参与运算。为了与内置版本保持一致,后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用。显式地调用后置运算符。尽管传入的值通常会被运算符函数忽略,但却必不可少,因为编译器只有通过它才能知道应该使用后置版本。

         成员访问运算符:在迭代器类及智能指针类中常常用到解引用运算符(*)和箭头运算符(->)。箭头运算符必须是类的成员。解引用运算符通常也是类的成员,尽管并非必须如此。

         对箭头运算符返回值的限定:和大多数其它运算符一样(尽管这么做不太好),我们能令operator*完成任何我们指定的操作。换句话说,我们可以让operator*返回一个固定值42,或者打印对象的内容,或者其它。箭头运算符则不是这样,它永远不能丢掉成员访问这个最基本的含义。当我们重载箭头时,可以改变的是箭头从哪个对象当中获取成员,而箭头获取成员这一事实则永远不变。重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象

         函数调用运算符如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象。因为这样的类同时也能存储状态,所以与普通函数相比它们更加灵活。调用对象实际上是在运算重载的调用运算符。函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别

         含有状态的函数对象类:和其它类一样,函数对象除了operator()之外也可以包含其它成员。函数对象类通常含有一些数据成员,这些成员被用于定制调用运算符中的操作。函数对象常常作为泛型算法的实参。

         lambda是函数对象:lambda表达式产生的类不含有默认构造函数、赋值运算符及默认析构函数;它是否含有默认的拷贝/移动构造函数则通常要视捕获的数据成员类型而定。

         标准库定义的函数对象:标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符,例如std::plus。这些类都被定义成模板的形式,我们可以为其指定具体的应用类型,这里的类型即调用运算符的形参类型。下表所列的类型定义在functional头文件中:

C++中操作符重载的使用_第2张图片

         在算法中使用标准库函数对象:表示运算符的函数对象类常用于替换算法中的默认运算符。如,在默认情况下排序算法使用operator <将序列按照升序排列。如果要执行降序排列的话,我们可以传入一个greater类型的对象。该类将产生一个调用运算符并负责执行待排序类型的大于运算。标准库规定其函数对象对于指针同样适用

         一个重载的运算符必须是某个类的成员或者至少拥有一个类类型的运算对象。重载运算符的运算对象数量、结合律、优先级与对应的用于内置类型的运算符完全一致。当运算符被定义为类的成员时,类对象的隐式this指针绑定到第一个运算对象。赋值、下标、函数调用和箭头运算符必须作为类的成员。

         如果类重载了函数调用运算符operator (),则该类的对象被称作”函数对象”。这样的对象常用在标准函数中。lambda表达式是一种简便的定义函数对象类的方式。

         函数对象(function object):定义了重载调用运算符的对象。在需要使用函数的地方都能使用函数对象

         重载的运算符(overloadedoperator):重定义了某种内置运算符的含义的函数。重载的运算符函数含有关键字operator,之后是要定义的符号。重载的运算符必须含有至少一个类类型的运算对象。重载运算符的优先级、结合律、运算对象数量都与其内置版本一致。

在C++中,operator关键字将声明一个用于指定operator-symbol含义的函数。这将为运算符提供多个含义,或者将”重载”它。编译器通过检查其操作数类型来区分运算符不同的含义。你可以在全局或为各个类重新定义大多数内置运算符的函数。重载运算符作为函数来实现。

操作符重载的实现方式:通过”友元函数”、全局函数、类成员函数。

要实现操作符重载就要使用操作符重载函数,操作符重载函数用关键字operator实现。操作符重载函数是一个函数,只不过这个函数的函数名为operator再加上后面要重载的操作符而已。

当操作符重载函数作为类的成员函数时,操作符重载函数的参数会比作为友元或者全局函数的操作符重载函数少一个参数,因为操作符重载类成员函数把调用该函数的对象作为函数的第一个参数,也就是隐含的this指针指向调用该函数的第一个对象,所以会少一个参数。

一般来说操作符重载函数一般不要求作为类的成员函数或者是友元函数,一般情况下可以将操作符重载函数作为类的成员函数。但是有一种情况必须要求操作符函数作为类的友元函数或者是全局函数,就是一个内置类型和对象相加的情况。

必须把它作为类成员函数的运算符有:(),[],->;和任何赋值运算符,重载这些运算符时必须把操作符函数声明为类的成员函数。

利用友元函数重载二元操作符时,形式参数是两个,而利用类成员函数时,形式参数却只有一个。这是因为类成员函数中存在this指针,这相当于一个参数,所以类成员实现操作符重载需要的形式参数比原来少一个。

运算符重载的原则:

(1)、C++中只能对已有的C++运算符进行重载,不允许用户自己定义新的运算符;

(2)、C++中绝大部分的运算符可重载,除了成员访问运算符.,作用域运算符::,长度运算符sizeof以及条件运算符?:;

(3)、运算符重载后不能改变运算符的操作对象(操作数)的个数;

(4)、重载不能改变运算符原有的优先级和原有的结合性;

(5)、运算符重载不能全部是C++中预定义的基本数据,这样做的目的是为了防止用户修改用于基本类型数据的运算符性质。

除6种操作符不能被重载外,其它均可以,不能重载的操作符包括:(1)、?: (conditional); (2)、. (member selection); (3)、.* (member selection with pointer-to-member); (4)、:: (scope resolution); (5)、sizeof (object size information); (6)、typeid (object type information)

         以上内容主要摘自:《C++ Primer(第五版中文版)》第14章

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

operator.hpp:

#ifndef FBC_MESSY_TEST_OPERATOR_HPP_
#define FBC_MESSY_TEST_OPERATOR_HPP_

#include 
#include 
#include 
#include 

namespace operator_ {
/////////////////////////////////////////////////////
// reference: https://msdn.microsoft.com/en-us/library/f6s9k9ta.aspx
class Point {
public:
	// various constructors
	Point() : x(0), y(0) {}
	Point(int x_, int y_) : x(x_), y(y_) {}
	Point(const Point& pt_) : x(pt_.x), y(pt_.y) {}

	// assignment operator: =
	Point& operator = (const Point& pt_);

	// compound assignment operators: += -= *= /= %= &= |= ^= <<= >>=
	Point& operator += (const Point& pt_);
	Point& operator -= (const Point& pt_);
	Point& operator *= (const Point& pt_);

	// comparison operators: == != > < >= <=
	bool operator == (const Point& pt_) const;
	bool operator != (const Point& pt_) const;
	bool operator > (const Point& pt_) const;

	// bitwise operators: ^ | & ~ << >>
	// note: this function is not a member function
	friend std::ostream& operator << (std::ostream& out, const Point& pt_); // output
	// note: this function is not a member function
	friend std::istream& operator >> (std::istream& in, Point& pt_); // input

	// logical operators: ! && ||

	//increment and decrement operators: ++ --
	Point& operator ++ (); // prefix: ++
	Point& operator ++ (int); // postfix: ++

public:
	int x, y; //< the point coordinates
};

// note: these function are not member function
Point operator + (const Point& pt_, int a);
Point operator + (const Point& a, const Point& b);
Point operator - (const Point& a, const Point& b);
Point operator * (const Point& a, const Point& b);

///////////////////////////////////////////////////////////
// 函数调用运算符 operator ()
struct absInt {
	int operator () (int val) const
	{
		return val < 0 ? -val : val;
	}
};

///////////////////////////////////////////////////////////
// 含有状态的函数对象类
class PrintString {
public:
	PrintString(std::ostream& o = std::cout, char c = ' ') : os(o), sep(c) {}
	void operator () (const std::string& s) const
	{
		os << s << sep;
	}

private:
	std::ostream& os;
	char sep;
};

//////////////////////////////////////////////////////
// reference: http://en.cppreference.com/w/cpp/language/operators
//           http://www.geeksforgeeks.org/operator-overloading-c/
class Fraction {
	int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
	int n, d;
public:
	Fraction(int n, int d = 1) : n(n / gcd(n, d)), d(d / gcd(n, d)) { }
	int num() const { return n; }
	int den() const { return d; }
	Fraction& operator *= (const Fraction& rhs)
	{
		int new_n = n * rhs.n / gcd(n * rhs.n, d * rhs.d);
		d = d * rhs.d / gcd(n * rhs.n, d * rhs.d);
		n = new_n;
		return *this;
	}

	// conversion operator: return float value of fraction
	//operator float() const {
	//	return float(n) / float(d);
	//}
};

/////////////////////////////////////////////////////////////
// reference: http://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators-using-friend-functions/
class Cents
{
private:
	int m_cents;

public:
	Cents(int cents) { m_cents = cents; }

	// add Cents + Cents using a friend function
	friend Cents operator+(const Cents &c1, const Cents &c2);

	int getCents() const { return m_cents; }
};

//////////////////////////////////////////////////
// reference: https://msdn.microsoft.com/en-us/library/5tk49fh2.aspx
struct Complex {
	Complex(double r, double i) : re(r), im(i) {}
	Complex operator+(Complex &other);
	void Display() { std::cout << re << ", " << im << std::endl; }
private:
	double re, im;
};

////////////////////////////////////////////////
int test_operator_1();
int test_operator_2();
int test_operator_3();
int test_operator_4();
int test_operator_5();
int test_operator_6();

} // namespace operator_

#endif // FBC_MESSY_TEST_OPERATOR_HPP_
operator.cpp:

#include 
#include "operator.hpp"
#include "universal.hpp"

namespace operator_ {
/////////////////////////////////////////////////////////////
Point& Point::operator = (const Point& pt_)
{
	if (this == &pt_) {
		return *this;
	}

	this->x = pt_.x;
	this->y = pt_.y;

	return *this;
}

Point& Point::operator += (const Point& pt_)
{
	this->x += pt_.x;
	this->y += pt_.y;

	return *this;
}

Point& Point::operator -= (const Point& pt_)
{
	this->x -= pt_.x;
	this->y -= pt_.y;

	return *this;
}

Point& Point::operator *= (const Point& pt_)
{
	this->x *= pt_.x;
	this->y *= pt_.y;

	return *this;
}

bool Point::operator == (const Point& pt_) const
{
	return (this->x == pt_.x && this->y == pt_.y);
}

bool Point::operator != (const Point& pt_) const
{
	return !(operator == (pt_));
}

bool Point::operator > (const Point& pt_) const
{
	return (this->x > pt_.x && this->y > pt_.y);
}

std::ostream& operator << (std::ostream& out, const Point& pt_) // output
{
	out << "x: " << pt_.x << std::endl;
	out << "y: " << pt_.y << std::endl;

	return out;
}

std::istream& operator >> (std::istream& in, Point& pt_) // input
{
	std::cout << "x: ";
	in >> pt_.x;
	std::cout << std::endl;
	std::cout << "y: ";
	in >> pt_.y;
	std::cout << std::endl;

	return in;
}

Point& Point::operator ++ ()
{
	++this->x;
	++this->y;

	return *this;
}

Point& Point::operator ++ (int)
{
	Point tmp(*this);
	operator++();

	return tmp;
}

Point operator + (const Point& pt_, int a)
{
	Point tmp;
	tmp.x = pt_.x + a;
	tmp.y = pt_.y + a;

	return tmp;
}

Point operator + (const Point& a, const Point& b)
{
	Point pt = a;
	pt += b;
	return pt;
}

Point operator - (const Point& a, const Point& b)
{
	//return Point((a.x - b.x), (a.y - b.y));
	Point pt = a;
	pt -= b;
	return pt;
}

Point operator * (const Point& a, const Point& b)
{
	//return Point((a.x * b.x), (a.y * b.y));
	Point pt = a;
	pt *= b;
	return pt;
}

int test_operator_1()
{
	Point pt1;
	Point pt2(1, 2);
	Point pt0(pt2);

	Point pt3;
	pt3 = pt2; // =, same as pt3.operator=(pt2);
	CHECK(pt3.x == 1 && pt3.y == 2);

	Point pt4(3, 4);
	pt4 += pt3;
	CHECK(pt4.x == 4 && pt4.y == 6);

	pt4 -= pt2;
	CHECK(pt4.x == 3 && pt4.y == 4);

	pt4 *= pt2;
	CHECK(pt4.x == 3 && pt4.y == 8);

	pt4 = pt3 + pt2;
	CHECK(pt4.x == 2 && pt4.y == 4);

	pt4 += pt2;
	CHECK(pt4.x == 3 && pt4.y == 6);

	Point pt5(3, 6);
	bool flag = (pt4 == pt5);
	CHECK(flag == true);

	flag = (pt4 != pt5);
	CHECK(flag == false);

	flag = (pt4 > pt2);
	CHECK(flag == true);

	Point pt6(4, 5);
	std::cout << pt4 << std::endl; // output

	std::cin >> pt1; // input
	std::cout << pt1 << std::endl;

	Point pt7(4, 5);
	++pt7;
	CHECK(pt7.x == 5 && pt7.y == 6);

	Point pt8(6, 7);
	pt7 = pt8++;
	CHECK(pt7.x == 6 && pt7.y == 7);

	int a = 10;
	pt7 = pt7 + a;
	CHECK(pt7.x == 16 && pt7.y == 17);

	return 0;
}

/////////////////////////////////////////////////
int test_operator_2()
{
	int i = -42;
	absInt absobj;
	int ui = absobj(i);
	fprintf(stdout, "ui = %d\n", ui); // 42

	return 0;
}

/////////////////////////////////////////////
int test_operator_3()
{
	const std::string s{ "http://blog.csdn.net/fengbingchun/" };

	PrintString printer;
	printer(s);

	PrintString errors(std::cerr, '\n');
	errors(s);

	return 0;
}

////////////////////////////////////////
std::ostream& operator << (std::ostream& out, const Fraction& f)
{
	return out << f.num() << '/' << f.den();
}

bool operator == (const Fraction& lhs, const Fraction& rhs)
{
	return lhs.num() == rhs.num() && lhs.den() == rhs.den();
}

bool operator != (const Fraction& lhs, const Fraction& rhs)
{
	return !(lhs == rhs);
}

Fraction operator * (Fraction lhs, const Fraction& rhs)
{
	return lhs *= rhs;
}

int test_operator_4()
{
	Fraction f1(3, 8), f2(1, 2), f3(10, 2);
	std::cout << f1 << " * " << f2 << " = " << f1 * f2 << '\n'
		<< f2 << " * " << f3 << " = " << f2 * f3 << '\n'
		<< 2 << " * " << f1 << " = " << 2 * f1 << '\n';

	//Fraction f(2, 5);
	//float val = f;
	//std::cout << val <

GitHub: https://github.com/fengbingchun/Messy_Test

你可能感兴趣的:(C/C++/C++11)