[c++primer][14]重载操作符与转换

14.1 重载操作符的定义

不能重载的操作符:.  ?:  sizeof  ::  .*

不能为任何内置类型定义额外的新的操作符;优先级和结合性是固定的;不再具备短路求值特性(不建议重载&&、||、逗号);

一般将算术和关系操作符定义为非成员函数,而将赋值操作符定义为成员;

使用重载操作符的方式:

1)  与内置类型使用操作符方式一样;

2)  也可像调用普通函数一样调用重载操作符函数,指定函数并传递适当类型适当数目的形参;

item1 += item2; // expression based "call"
item1.operator+=(item2); // equivalent call to member operator function

重载操作符的设计

1)不要重载具有内置含义的操作符。如果没有特定重载版本,编译器就自己定义以下这些操作符:

 - 合成赋值操作符(=):逐个成员赋值

 - 取地址操作符(&)、逗号操作符(,)与内置含义一致

 - 内置逻辑与(&&)和逻辑或(||)操作符使用短路求值,重新定义将会失去该特征。

2)大多数操作符对类对象没有意义,设计类的时候应该确定要支持哪些操作符。

3)复合赋值操作符。如果一个类有算术操作符或位操作符,那么提供相应的复合赋值操作符一般是个好的做法。

4)相等和关系操作符。将用作关联容器类型的类应定义<操作符;即使类只存储在顺序容器中,也应该定义相等操作符和小于操作符;如果类定义了相等操作符,它也应该定义不等操作符。

5)选择成员或非成员实现(经验原则)

 - 赋值下标调用和成员访问箭头(=、[ ]、( )、->)等操作符必须定义为成员,将这些操作符定义为非成员将在编译时标记为错误;

 - 复合赋值通常定义为类的成员(不是必须这样做);

 - 改变对象状态或与给定类型紧密联系的一些操作符通常定义为成员,如自增自减解引用

 - 对称操作符,如算术、相等、关系和位操作符,最好定义为非成员。

14.2 输入和输出操作符

输出操作符<<的重载

ostream&
operator<<(ostream& out, const Sales_item& s)
{
  out << s.isbn << "\t" << s.units_sold << "\t"
    << s.revenue << "\t" << s.avg_price();
  return out;
}//注意:IO操作符必须为非成员函数

输入操作符>>的重载

istream&
operator>>(istream& in, Sales_item& s)
{
  double price;
  in >> s.isbn >> s.units_sold >> price;
  // check that the inputs succeeded
  if (in)
    s.revenue = s.units_sold * price;
  else
    s = Sales_item(); // input failed: reset object to default state
  return in;
}

14.3 算术操作符和关系操作符

一般而言,将算术和关系操作符定义为非成员函数

算术操作符

// assumes that both objects refer to the same isbn
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
  ret += rhs; // add in the contents of rhs
  return ret; // return ret by value
} 

注意:同时定义了算术操作符和相关复合赋值操作符的类,一般用复合赋值实现算术操作

相等操作符

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== }

14.4 赋值操作符

赋值操作符必须是类的成员,赋值必须返回对*this的引用

14.5 下标操作符

下标操作符返回引用。一般要定义两个版本:一个为非const成员并返回引用,一个为const成员返回const引用。

class Foo {
public:
  int &operator[] (const size_t);
  const int &operator[] (const size_t) const;
  // other interface members
private:
  vector<int> data;
  // other member data and private utility functions
}; 

14.6 成员访问操作符

箭头(->)操作符一般为类成员,解引用(*)不要求定义为成员,但将它作为成员也是正确的。

class ScreenPtr {
public:
  // constructor and copy control members as before
  Screen &operator*() { return *ptr->sp; }
  Screen *operator->() { return ptr->sp; }
  const Screen &operator*() const { return *ptr->sp; }
  const Screen *operator->() const { return ptr->sp; }
private:
  ScrPtr *ptr; // points to use-counted ScrPtr class
}; 

重载解引用

分别定义解引用操作的const和非const版本;const成员返回const引用以防止用户改变基础对象。

重载箭头操作符

表现得像二元操作符:接受一个对象和一个成员名。对对象解引用以获取成员,—>的右操作数对应着类成员的一个标识符。

当编写:point->action(); 

由于优先级规则,等价于 (point->action)();

1)如果point是指针,指向具有action成员的类对象,编译器将代码编译为调用该对象的action成员;

2)如果point是定义了operator->操作符的类,则point->action与point.operator->()->action相同,然后使用该结果重复这三步。

3)否则,代码出错。

重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。

14.7 自增操作符和自减操作符

C++不要求自增自减一定作为类的成员,但是因为这些操作符改变对象状态,故倾向于作为成员。

/*
* smart pointer: Checks access to elements throws an out_of_range
* exception if attempt to access a nonexistent element
* users allocate and free the array
*/
class CheckedPtr {
public:
  // no default constructor; CheckedPtrs must be bound to an object
  CheckedPtr(int *b, int *e): beg(b), end(e), curr(b) { }
  // dereference and increment operations
private:
  int* beg; // pointer to beginning of the array
  int* end; // one past the end of the array
  int* curr; // current position within the array
};

前自增/自减(返回对象的引用)

class CheckedPtr {
public:
  CheckedPtr& operator++(); // prefix operators
  CheckedPtr& operator--();
  // other members as before
};

// prefix: return reference to incremented/decremented object
CheckedPtr& CheckedPtr::operator++()
{
  if (curr == end)
    throw out_of_range
      ("increment past the end of CheckedPtr");
  ++curr; // advance current state
  return *this;
}

CheckedPtr& CheckedPtr::operator--()
{
  if (curr == beg)
    throw out_of_range
      ("decrement past the beginning of CheckedPtr");
  --curr; // move current state back one element
  return *this;
} 

后自增/自减(返回对象的旧值)

为做区别,后缀操作符接受一个额外的int形参,使用时编译器提供0初始化它

class CheckedPtr {
public:
  // increment and decrement
  CheckedPtr operator++(int); // postfix operators
  CheckedPtr operator--(int);
  // other members as before
};

// postfix: increment/decrement object but return unchanged value
CheckedPtr CheckedPtr::operator++(int)
{
  // no check needed here, the call to prefix increment will do the check
  CheckedPtr ret(*this); // save current value
  ++*this; // advance one element, checking the increment
  return ret; // return saved state
}
CheckedPtr CheckedPtr::operator--(int) {   // no check needed here, the call to prefix decrement will do the check   CheckedPtr ret(*this); // save current value   --*this; // move backward one element and check   return ret; // return saved state }

调用

CheckedPtr parr(ia, ia + size); // iapoints to an array of ints
parr.operator++(0); // call postfix operator++
parr.operator++(); // call prefix operator++

14.8 调用操作符和函数对象

函数调用操作符必须声明为成员函数。定义了调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象。

struct absInt {
  int operator() (int val) {
    return val < 0 ? -val : val;
  }
};

int i = -42;
absInt absObj; // object that defines function call operator
unsigned int ui = absObj(i); // calls absInt::operator(int)

将函数对象用于标准库算法

谓词(predicate)是做某些检测的函数,返回用于条件判断的类型,指出条件是否成立。

// determine whether a length of a given word is 6 or more
bool GT6(const string &s)
{
  return s.size() >= 6;
}

vector<string>::size_type wc =
count_if(words.begin(), words.end(), GT6);

函数对象可以更灵活

// determine whether a length of a given word is longer than a stored bound
class GT_cls {
public:
  GT_cls(size_t val = 0): bound(val) { }
  bool operator()(const string &s)
  { return s.size() >= bound; }
private:
  std::string::size_type bound;
};
cout << count_if(words.begin(), word.end(), GT_cls(6)
   << " words 6 characters or longer" << endl;

标准库定义的函数对象

标准库定义了一组算术、关系与逻辑函数对象类,以及一组函数适配器,用于特化或扩展标准库所定义的以及自定义的函数对象类。标准库函数对象类型在functional头文件定义。

不同的函数对象定义了执行不同操作符的调用操作符;两个一元函数对象类:negate<Type>(一元减),logical_not<Type>(逻辑非)

每个函数对象类都是一个模板,我们需要为该模板提供一个类型

plus<int> intAdd; // function object that can add two int values
negate<int> intNegate; // function object that can negate an int value
// uses intAdd::operator(int, int) to add 10 and 20
int sum = intAdd(10, 20); // sum = 30
// uses intNegate::operator(int) to generate -10 as second parameter
// to intAdd::operator(int, int)
sum = intAdd(10, intNegate(10)); // sum = 0

函数对象常用于覆盖算法使用的默认操作符。

// passes temporary function object that applies > operator to two strings
sort(svec.begin(), svec.end(), greater<string>());

函数对象的函数适配器

1)绑定器(bind1st,bind2nd)

count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10));

2)求反器(not1,not2)

count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10)));

14.9 转换与类类型

一个实参调用的非explicit构造函数定义了一个隐式转换,这种构造函数定义了到类型的转换。我们还可以定义从类型的转换,像其他转换一样,编译器将自动应用这个转换。

转换操作符

转换操作符是一种特殊的类成员函数。它定义将类类型转变为其他类型值的转换。转换操作符在类定义体内声明,在operator后跟着转换的目标类型。通用形式:

operator type();

1)不允许转换为数组或函数类型,转换为指针和引用是可以的;

2)转换函数必须是成员函数,不能指定返回类型,但是必须显式返回一个指定类型的值,形参表空;

3)因不改变被转换的对象,通常定义为const成员

 

posted @ 2015-10-25 01:15  treeland  阅读(134)  评论(0编辑  收藏  举报