C++primer 阅读点滴记录(三)

14章 操作符重载和转换

   重载操作符是具有特殊名称的函数:保留字operator后接需要定义的操作符符号。

  1、重载的操作符名:

    + – * / % ^ & | ~ ! , = <  >  <= >= ++ – << >> == != && ||等

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

   2、 重载操作符 必须具有一个类类型操作数。

         int operator+(int,int) ;//error : cannot defined built-in operator for ints

  3、优先级和结合性是固定的

  4、不再具备短路求值特性(重载&&和||不是一种好的做法)

  5、类成员和非成员

      大多数重载操作符可以定义为普通非成员函数或类的成员函数。

      注解: 作为类成员的冲在函数,其形参看起来比操作数数目少1。作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。

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

// member binary operator: left-hand operand bound to implicit this pointer
Sales_item& Sales_item::operator+=(const Sales_item&);

//nonmember binary operator: must declare a parameter for each operand

Sales_item operator+(const Sales_item&,const Sales_item&);

 

6、操作符重载和友元关系

  操作符定义为非成员函数时,通常必须将它们设置为所操作类的友元。

 

最佳实践:当一个重载操作符的含义不明显时,给操作取一个名字更好。对于很少用的操作使用命名函数通常比用操作符更好。如果不是普通操作,没必要为简洁而使用操作符。

 

输出操作符

std::ostream& operator<<(std::ostream& os,const Sales_item& s){
    os<<s.isbn<<"\t"<<s.units_sold<<"\t"
        <<s.revenue<<"\t"<<s.avg_price();
    return os;
}

最佳实践

     一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应该输出换行符。

    IO操作符必须为非成员函数

friend std::istream& operator>>(std::istream&,Sales_item&);
    friend std::ostream& operator<<(std::ostream&,const Sales_item&);

 

输入操作符:

  注意: 输入操作的第二形参必须为非const引用,而且输入操作符必须处理错误和文件借宿的可能。

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

 

算术操作符和关系操作符

Sales_item& Sales_item::operator+=(const Sales_item& rhs){
    if (isbn == rhs.isbn){
        units_sold += rhs.units_sold;
        revenue += rhs.revenue;
    }
    return *this;
}

Sales_item operator+(const Sales_item& s1,const Sales_item& s2){
    Sales_item ret(s1);
    ret += s2;//use operator+= 
    return ret;
}

inline bool
    operator==(const Sales_item& lhs,const Sales_item& rhs){
        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 interm of operator==
}

最佳实践

   为了与内置操作符保持一致,加法返回一个右值,而不是一个引用。

   既定义了算术操作又定义了相关复合赋值操作的类,一般应使用复合操作实现算术操作符。

   赋值操作符必须返回*this的引用

 

下标操作符: 

#ifndef FOO_H
#define FOO_H

#include <vector>
using std::vector;

class Foo{
public:
    int &operator[](const size_t);
    const int &operator[](const size_t);
private:
    vector<int> data;
};

int& Foo::operator [](const size_t index){
    return data[index];//no range checking on index
}

const int& Foo::operator [](const size_t index){
    return data[index];//no range checking on index
}

#endif // !FOO_H
最佳实践:

     下标操作符必须定义为类成员函数

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

 

成员访问操作符(*  –>)

  注解: 箭头操作符必须定义为类成员函数,解引用操作符不要定义为成员,但将它作为成员一般也是正确的。

  1、构建更安全的指针

#ifndef SCRPTR_H
#define SCRPTR_H
#include "Person.h"
#include <iostream>

class ScrPtr{
    friend class ScreenPtr;
    Screen *sp;
    size_t use;
    ScrPtr(Screen *p):sp(p),use(1){}
    ~ScrPtr(){delete sp;}
};

class ScreenPtr{
public:
    ScreenPtr(Screen* p):ptr(new ScrPtr(p)){}
    ScreenPtr(const ScreenPtr& orig):ptr(orig.ptr){++ptr->use;}
    ScreenPtr& operator=(const ScreenPtr&);
    ~ScreenPtr(){
        if(--ptr->use = 0)
            delete ptr;
    }
    //两个版本 const和非const
    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;
};

ScreenPtr& ScreenPtr::operator=(const ScreenPtr& rhs){
    ++rhs.ptr->use;
    if(--ptr->use == 0)
        delete ptr;
    ptr = rhs.ptr;
    return *this;//注意赋值 复合赋值操作符必须返回*this
}

void scrptrTest(){
    Screen myscreen;
    ScreenPtr p(&myscreen);
    p->display(std::cout);
}


#endif // !SCRPTR_H

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

 

  自增操作符和自减操作符

#ifndef CHECKED_PTR_H
#define CHECKED_PTR_H

/*
 * smart pointer: Checks access to elements throws an out_of_range
 *                  exception if attempt to access a nonexistent element
 *
 * user allocate and free the array
 */

class CheckedPtr{
public:
    CheckedPtr(int* b, int* e):beg(b),end(e),curr(b){}
    //prefix operators
    CheckedPtr& operator++();
    CheckedPtr& operator--();
    //postfix operators
    CheckedPtr operator++(int);
    CheckedPtr operator--(int);
private:
    int* beg;
    int* end;
    int* curr;
};

CheckedPtr& CheckedPtr::operator ++(){
    if(curr == end)
        throw out_of_range ("increment past the end of CheckedPtr");
    ++curr;
    return *this;
}

CheckedPtr& CheckedPtr::operator --(){
    if(curr == beg)
        throw out_of_range("decrement past the beginning of CheckedPtr");
    --curr;
    return *this;
}

CheckedPtr CheckedPtr::operator ++(int){
    CheckedPtr ret(*this);
    ++*this;
    return ret;
}

CheckedPtr CheckedPtr::operator --(int){
    CheckedPtr ret(*this);
    --*this;
    return ret;
}
#endif // !CHECKED_PTR_H

 

调用操作符和函数对象

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

void absInt_test(){
    int i = -32;
    absInt absObj;

    using int ui = absObj(i);
}

注解:函数调用操作符必须声明为成员函数。一个类可以定义函数调用操作符的多个版本。有形参的数目或类型加以区别。

posted @ 2015-03-24 23:36  hongjack  阅读(154)  评论(0编辑  收藏  举报