很久之前就有写博客的想法,久久没有行动,今天终于写出我的第一篇博客了,哈哈。在阅读了好多牛人的博客后,让我受益匪浅,这也是我要写博客的主要原因之一,书写能让人更好的思考,更能挖掘出问题的本质,呵呵,你觉着呢?
闲话少说了,步入正题吧,最近在看C++Primer,我学计算机有5年多了(目前还在读书,悲哀,呵呵),对c++也算有一定理解了,可看了第一章后才发现c++真的是门博大精深的语言,很多东西,不理解本质,是很难完全掌握的。还好C++Primer给了我们去深入理解它的机会,经典书籍啊,第一章中有个例子我觉着值得研究,就是卖书记录那个类的定义和实现(Sales_item class),卖书记录类的定义中包括三个私有成员书目的编码 isbn , 此记录中卖出的数目 units_sold , 总收入 revenue 。先把代码拷过来了,下面是Sales_item.h中的代码,其中定义了各种操作符重载函数,使你能够像使用build-in type 的对象一样去使用Sales_item类型的对象。本文主要就是要探讨一下它对操作符重载的实现方式,大家先看下代码吧
#include <iostream>
#include <string>
class Sales_item {
friend bool operator==(const Sales_item&, const Sales_item&);
public:
// added constructors to initialize from a string or an istream
Sales_item(const std::string &book):
isbn(book), units_sold(0), revenue(0.0) { }
Sales_item(std::istream &is) { is >> *this; }
friend std::istream& operator>>(std::istream&, Sales_item&);
friend std::ostream& operator<<(std::ostream&, const Sales_item&);
public:
// operations on Sales_item objects
// member binary operator: left-hand operand bound to implicit this pointer
Sales_item& operator+=(const Sales_item&);
public:
// operations on Sales_item objects
double avg_price() const;
bool same_isbn(const Sales_item &rhs) const
{ return isbn == rhs.isbn; }
// default constructor needed to initialize members of built-in type
Sales_item(): units_sold(0), revenue(0.0) { }
// private members as before
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
// nonmember binary operator: must declare a parameter for each operand
Sales_item operator+(const Sales_item&, const Sales_item&);
inline bool
operator==(const Sales_item &lhs, const Sales_item &rhs)
{
// must be made a friend of Sales_item
return lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue &&
lhs.same_isbn(rhs);
}
inline bool
operator!=(const Sales_item &lhs, const Sales_item &rhs)
{
return !(lhs == rhs); // != defined in terms of operator==
}
using std::istream; using std::ostream;
// assumes that both objects refer to the same isbn
inline
Sales_item& Sales_item::operator+=(const Sales_item& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// assumes that both objects refer to the same isbn
inline
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs); // copy lhs into a local object that we'll return*****************(1)
ret += rhs; // add in the contents of rhs
return ret; // return ret by value
}
inline
istream&
operator>>(istream& in, Sales_item& s)
{
double price;
in >> s.isbn >> s.units_sold >> price;
// check that the inputs succeeded
if (in)//****************************************************************(2)
s.revenue = s.units_sold * price;
else
s = Sales_item(); // input failed: reset object to default state*******************(3)
return in;
}
inline
ostream&
operator<<(ostream& out, const Sales_item& s)
{
out << s.isbn << "/t" << s.units_sold << "/t"
<< s.revenue << "/t" << s.avg_price();
return out;
}
inline
double Sales_item::avg_price() const
{
if (units_sold)
return revenue/units_sold;
else
return 0;
}
让我们一起来分析分析它吧,该文件一共定义了六个操作符重载函数,类中声明了4个 == += << >> ,类外面声明并定义了两个 != 和 +,他们的实现方式是有区别的,你看出来了吗?我觉着可以将他们分为以下三类。
第一类:通过friend关键字在类中声明该操作符重载函数为此类的友元,从而可以在函数体中访问类的私有成员进而实现相应操作符的功能,在这种定义方式中操作符所需要的操作数应该与函数参数相对应来定义(nonmember binary operator: must declare a parameter for each operand),即对二元操作符来说重载函数就应该有两个函数参数,这与第二种实现方式是完全不一样的,通过此种实现方式的操作符有三个 == << >>,我觉着其中<< >> 这两个操作符只能通过这种方式来实现,因为他们的左操作数并不是Sales_item类型的,重载函数无法定义成类的成员函数。==就不一样了,它的左操作数正是Sales_item类型。
第二类:将重载函数定义为类的成员函数,既然是成员函数了,当然可以访问私有成员了,操作符重载函数的功能也就实现了。这种实现方式操作符的左操作数并不作为参数传给成员函数,而是可以通过this直接访问左操作数,更确切的说是编译器通过左操作数调用的成员函数,当然不用传了,你说对吧?这样的话二元操作符的重载函数就只有一个函数参数了,与右操作数对应(member binary operator: left-hand operand bound to implicit this pointer)。+=操作符用的就是这种实现方式。
第三类:这类指的是!= 和 + 两个操作符重载函数,他们被单独搞出一类是因为他们不需要访问类的私有成员,因此他们不需要声明成类的友元函数,直接定义就好了,当然他们也可以定义成类的成员函数,左操作数都是Sales_item类型啊,对吧。
我觉得代码中还有几个地方值得注意一下(代码中已标出(1) (2) (3)),下面简单解释下:
1.Sales_item ret(lhs); // copy lhs into a local object that we'll return 应该是调用了编译器为该类加上的默认拷贝构造函数(浅拷贝)
2.if (in) 其中in是istream类型的对象,之所以可以这吗写,应该是istream类型中定义了强制类型转换操作符重载函数 boolean()
3.s = Sales_item(); // input failed: reset object to default state 我理解这句话应该是先定义了一个匿名对象(Sales_item类型),之后调用拷贝构造函数,拷贝到s中。不知道这吗解释对不对?
哈哈,终于写完了,第一次写,难免语句生硬,大家凑合着看吧,有什么技术上的错误,欢迎指出。