zoukankan      html  css  js  c++  java
  • [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 << "	" << s.units_sold << "	"
        << s.revenue << "	" << 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成员

  • 相关阅读:
    .NET视频学习总结
    hdu5288(2015多校1)OO’s Sequence
    【Go web开发之revel+mgo】第1章 述与环境
    Android获得当前系统时间、星期几、周几
    夯实Java:从面向对象说起
    centos7 安装nginx
    centos7 安装nginx
    centos7 安装nginx
    element-ui中cascader同时获取label和value值
    element-ui中cascader同时获取label和value值
  • 原文地址:https://www.cnblogs.com/itree/p/4908132.html
Copyright © 2011-2022 走看看