• 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<<"	"<<s.units_sold<<"	"
            <<s.revenue<<"	"<<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);
    }

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

  • 相关阅读:
    队列

    有序数组
    集合:一条规则决定性能
    基础数据结构:数组
    空间复杂度
    插入排序
    重新认识Javascript的一些误区总结
    Knockout: 使用knockout validation插件进行校验, 给未通过校验的输入框添加红色边框突出显示.
    Knockout: 使用CSS绑定和event的blur失去焦点事件, 给未通过校验的输入框添加红色边框突出显示.
  • 原文地址:https://www.cnblogs.com/jackStudy/p/4364391.html
Copyright © 2020-2023  润新知