重载操作符 (c++primer 4e)

Posted on 2010-03-15 16:40 rikisand 阅读(1207) 评论(0)  编辑 收藏 引用 所属分类: C/C++

1.为啥要重载操作符:

通过重载操作符,程序员可以针对“类”类型的操作数定义不同的操作符版本。良好的操作符定义可以使class类型的使用想内置类型一样直观简洁,使用重定义的操作符而不是命名函数使得程序可以用表达式代替函数调用,使程序编写和阅读更容易~

2.哪些不能重载

::     .*      .     ?:    这些不能重载

3.需要注意的地方:

重载必须有一个class类型的操作数,短路求值失去,

重载操作符和内置操作符结合型相同,优先级操作数个数均相同

不要重载一些含有内置定义的操作符 & , && || 这些

·赋值(=)下标(【】)调用 (())和成员访问操作符必须定义为成员

·对称的操作符一般定义为普通非成员函数

++ -- 一般设置为成员函数 ,因为可能改变对象状态

4.定义输入输出操作符

io操作符只能重载为非成员函数,否则做操作符只能是对象成员 用法变成了 object<<cin  不符合我们的习惯,经常把他们设置成为友元,因为可能触及私有变量。

输入必须加入文件结束和输入错误的错误处理

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

如果输入失败,调用默认构造函数恢复对象状态。

5.算术运算符和关系运算符

Sales_item& operator -=(const Sales_item& item){
    units_sold-=item.units_sold;
    revenue-=item.revenue;
    return *this;
}
Sales_item operator - (const Sales_item& lhs,const Sales_item& rhs){
    Sales_item res(lhs);
    res+=rhs;
    return res;
}

 

一般算术运算符设置为非成员函数,与内置运算符对应,选择返回value 而不是引用。赋值运算符重载为成员函数,并用它来实现算术运算符,这样算术运算符不用friend

相等运算符和不等运算符一般成对出现,且用一个实现另一个

关系运算符 == != > < 一般重载成非成员函数

6.赋值操作符

必须为成员函数 (=号)

=和+=  -= 一般都需要返回左操作数的引用

Sales_item& operator = (string is){
    isbn = is;
    return *this;
}

6.下标操作符

必须为类成员函数   返回引用使其可以在赋值操作的任意一边

一般定义一种const 返回常量引用 一种not 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
      };

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

pari<string,string>& operator[] (const  vector< pair<string,string>* > ::size_type index){
    return *wait_list.at(index);//使用at判断是否越界
}

6.成员访问操作符

-> 一般要求重载为成员运算符,*没有要求 ,但成员比较常见~~~

例子:auto-ptr~~~~

ScreenPtr 的用户将会传递一个指针,该指针指向动态分配的 ScreenScreenPtr 类将拥有该指针,并安排在指向基础对象的最后一个 ScreenPtr 消失时删除基础对象。另外,不用为 ScreenPtr 类定义默认构造函数。因此,我们知道一个 ScreenPtr 对象将总是指向一个 Screen 对象,不会有未绑定的 ScreenPtr,这一点与内置指针不同。应用程序可以使用 ScreenPtr 对象而无须首先测试它是否指向一个 Screen 对象。

     // private class for use by ScreenPtr only 私有类,
     class ScrPtr {
         friend class ScreenPtr;
         Screen *sp;
         size_t use;
         ScrPtr(Screen *p): sp(p), use(1) { }
         ~ScrPtr() { delete sp; }
     };
        /*
      * smart pointer: Users pass to a pointer to a dynamically allocated Screen, which
      *                   is automatically destroyed when the last ScreenPtr goes away
      */
     class ScreenPtr {
     public:
         //  no default constructor: ScreenPtrs must be bound to an object
         ScreenPtr(Screen *p): ptr(new ScrPtr(p)) { }
         //  copy members and increment the use count
         ScreenPtr(const ScreenPtr &orig):
            ptr(orig.ptr) { ++ptr->use; }
         ScreenPtr& operator=(const ScreenPtr&);
         //  if use count goes to zero, delete the ScrPtr object
         ~ScreenPtr() { if (--ptr->use == 0) delete ptr; }
     private:
         ScrPtr *ptr;    // points to use-counted ScrPtr class
     };

指针支持的基本操作有解引用操作和箭头操作。我们的类可以这样定义这些操作:

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

解引用操作符是个一元操作符。在这个类中,解引用操作符定义为成员,因此没有显式形参,该操作符返回对 ScreenPtr 所指向的 Screen 的引用。

箭头操作符不接受显式形参。point->action();   等价于  (point->action)();

可以这样使用 ScreenPtr 对象访问 Screen 对象的成员:

ScreenPtr p(&myScreen);     // copies the underlying Screen
p->display(cout);

因为 p 是一个 ScreenPtr 对象,p->display 的含义与对 (p.operator->())->display 求值相同。对 p.operator->() 求值将调用 ScreenPtr 类的 operator->,它返回指向 Screen 对象的指针,该指针用于获取并运行 ScreenPtr 所指对象的 display 成员。

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

6.自增自减操作符

一般重载为成员函数,为了与内置类型一致,前置操作符返回运算结果引用,后置操作符返回运算前的值,value not ref ,为了区分,后置操作符提供了一个实参0;

// 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++(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 parr(ia, ia + size);        // iapoints to an array of ints
parr.operator++(0);                    // call postfix operator++
parr.operator++();                     // call prefix operator++

7 调用操作符和函数对象

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)

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

定义了调用操作符的类,其对象常称为函数对象,即它们是行为类似函数的对象。

函数:

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

函数对象:

// 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;
     };

for (size_t i = 0; i != 11; ++i)
     cout << count_if(words.begin(), words.end(), GT(i))
          << " words " << i
          << " characters or longer" << endl;

函数对象的便捷性】

         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

函数适配器:

banding器,它通过将一个操作数绑定到给定值而将二元函数对象转换为一元函数对象

求反器是一种函数适配器,它将谓词函数对象的真值求反。标准库定义了两个求反器:not1not2 分别求反一元二元对象

8。实参匹配和转换(俺来看重载操作符的原因啊,,,)

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

转换函数采用如下通用形式:

     operator type();

转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空。

虽然转换函数不能指定返回类型,但是每个转换函数必须显式返回一个指定类型的值。例如,operator int 返回一个 int 值;如果定义 operator Sales_item,它将返回一个 Sales_item 对象,诸如此类。

转换函数一般不应该改变被转换的对象。因此,转换操作符通常应定义为 const 成员。

只要存在转换,编译器将在可以使用内置转换的地方自动调用它

  • In expressions:

    在表达式中:

         SmallInt si;
         double dval;
         si >= dval          // si converted to int and then convert to double
  • In conditions:

    在条件中:

         if (si)                // si converted to int and then convert to bool
  • When passing arguments to or returning values from a function:

    将实参传给函数或从函数返回值:

         int calc(int);
         SmallInt si;
         int i = calc(si);      // convert si to int and call calc
  • As operands to overloaded operators:

    作为重载操作符的操作数:

         // convert si to int then call opeator<< on the int value
         cout << si << endl;
  • In an explicit cast:

    在显式类型转换中:

         int ival;
         SmallInt si = 3.541; //
         instruct compiler to cast si to int
         ival = static_cast<int>(si) + 3;
    

    类类型转换之后不能再跟另一个类类型转换。如果需要多个类类型转换,则代码将出错。(指的是不能连续两个自定义的类型转换,但是内置类型转换可以的)

  • 还有一部分是实参匹配和转换 ,没时间了 以后再看~~~~

     

     

     

     

     

     

     

     

     

     

     


    只有注册用户登录后才能发表评论。
    网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理