• 【C++ Primer Chapter 14 总结】操作符重载和转换


    1. 重载的操作符函数的形参数量要与操作符的操作数相同。
    2. 如果操作符函数是成员函数,则第一个(左)操作数绑定到隐式的this指针。
    3. 操作符函数必须是类的成员或具有至少一个类类型形参。(因为内置类型的操作符已定义且不能更改)
    4. 重载运算符的返回类型通常应该与内置版本兼容:逻辑和关系运算符应该返回布尔值,算术运算符应该返回一个值的类类型,以及赋值和复合赋值操作应该返回左操作数的引用。
    5.=,(),->操作符必须是成员函数。
    6. 改变其对象状态的操作符,或者与给定类型(如自增、自减和解引用)密切相关的操作符,通常应该是成员函数。
    7. 对称操作符,即那些可以转换任意操作数的操作符,例如算术操作符、相等操作符、关系操作符和位操作符,通常应该定义为普通非成员函数。
     
    8.输入输出操作符的重载。不能是成员函数。
    ostream &operator<<(ostream &os, const Sales_data &item) {
        os << item.isbn() << " " << item.units_sold << " "
        << item.revenue << " " << item.avg_price();
        return os;
    }
     
    istream &operator>>(istream &is, Sales_data &item) {
        double price;
        is >> item.bookNo >> item.units_sold >> price;
        if (is)
            item.revenue = item.units_sold * price;
        else
            item = Sales_data(); 
        return is;
    }
    

      

    9.算数和关系操作符。
    Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) {
        Sales_data sum = lhs;
        sum += rhs;
        return sum;
    }
    bool operator==(const Sales_data &lhs, const Sales_data &rhs) {
        return lhs.isbn() == rhs.isbn() && lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue;
    }
    bool operator!=(const Sales_data &lhs, const Sales_data &rhs) {
        return !(lhs == rhs);
    }
    

      

    10.  如果一个类有一个下标操作符,它通常应该定义两个版本:一个返回普通引用,另一个是const成员并返回对const的引用。
    当下标操作符应用于const对象时,它应该返回对const的引用,这样就不能对返回的对象赋值。
    class StrVec {
    public:
        std::string& operator[](std::size_t n) { return elements[n]; }
        const std::string& operator[](std::size_t n) const { return elements[n]; }
    private:
        std::string *elements; 
    };
    

      

    11.自增/自减操作符。 
    前缀操作符应该是返回对递增或递减对象的引用。
    普通的函数重载无法区分前置和后置++/--,因此后置++版本会额外需要一个int参数以示区分。
    在间接调用操作符函数(使用操作符)时没有区别,但直接使用函数调用时,调用后置++必须带有参数,否则调用的是前置++。
    class StrBlobPtr {
    public:
        StrBlobPtr& operator++();     // 前缀自增
        StrBlobPtr& operator--();     // 前缀自减
        StrBlobPtr operator++(int);   // 后缀自增
        StrBlobPtr operator--(int);   // 后缀自减
    };
    StrBlobPtr& StrBlobPtr::operator++() {
        check(curr, "increment past end of StrBlobPtr");
        ++curr; 
        return *this;
    }
    StrBlobPtr& StrBlobPtr::operator--() {
        --curr; 
        check(-1, "decrement past begin of StrBlobPtr");
        return *this;
    }
     
    StrBlobPtr StrBlobPtr::operator++(int) {
        StrBlobPtr ret = *this;
        ++*this;
        return ret;
    }
    StrBlobPtr StrBlobPtr::operator--(int) {
        StrBlobPtr ret = *this;
        --*this;
        return ret;
    }
     
    StrBlobPtr p(a1);   // p points to the vector inside a1
    p.operator++(0);    // 直接使用函数调用,后缀++
    p.operator++();     // 前缀++
    

      

    12.函数调用操作符。
    重载调用操作符的类允许像使用函数一样使用其类型的对象。即: 通过“对象名+(参数列表)”的方式使用一个类,其实质是对operator()操作符的重载。
    定义了调用操作符的类的对象被称为函数对象。
    struct absInt {
        int operator()(int val) const {
            return val < 0 ? -val : val;
        }
    };
    int i = -42;
    absInt absObj; 
    int ui = absObj(i);   // 调用对象会运行其重载的调用操作符
     
    13.函数对象类通常包含用于自定义调用操作符中的操作的数据成员。
    函数对象可以用作通用算法的参数。
    class PrintString {
    public:
        PrintString(ostream &o = cout, char c = ' '): os(o), sep(c) { }
        void operator()(const string &s) const { os << s << sep;
    }
    private:
        ostream &os;
        char sep;
    };
     
    PrintString printer;
    printer(s);
    PrintString errors(cerr, '
    ');
    errors(s);
     
    for_each(vs.begin(), vs.end(), PrintString(cerr, '
    '));
    

      

    14. lambda是函数对象。
    当编写lambda时,编译器将该表达式转换为未命名类的未命名对象从lambda生成的类包含重载函数调用操作符。
    从lambda表达式生成的类有一个 = deleted 的默认构造函数、= deleted 的赋值操作符和一个默认析构函数。
    stable_sort(words.begin(), words.end(), [](const string &a, const string &b) { 
                                                    return a.size() < b.size();
                                             });
    // similar
    class ShorterString {
    public:
        bool operator()(const string &s1, const string &s2) const { return s1.size() < s2.size(); }
    };
    

      

    15.lambda函数的捕获列表按值获得变量时,对应的类需要将该变量设置为数据成员。
    按引用获得变量时,则不需要,因为编译器可以直接使用该引用的变量。
    auto wc = find_if(words.begin(), words.end(), [sz](const string &a) { return s.size() >= sz; });
     
    class SizeComp {
        SizeComp(size_t n): sz(n) { } 
        bool operator()(const string &s) const { return s.size() >= sz; }
    private:
        size_t sz; 
    };
    

      

    16. 标准库定义的一组表示算术、关系和逻辑操作符的类。
    表示运算符的函数对象类通常可用于覆盖算法使用的默认运算符。
    greater<T>
    less<T>
     
    sort(svec.begin(), svec.end(), greater<string>());   // 调用greater<T>类的无命名对象的函数调用操作符
    

      

    17. c++的可调用对象:函数和指向函数的指针,lambda,由bind创建的对象,以及重载函数调用操作符的类。
    18. 可调用对象有一个类型。例如,每个lambda都有自己独特的(未命名的)类类型。
    两个具有不同类型的可调用对象可能共享相同的调用签名( call signature)。调用签名指定调用对象返回的类型和传递的参数类型。
    int(int, int)                                       // 调用签名对应于函数类型
     
    int add(int i, int j) { return i + j; }            // 普通函数
    auto mod = [](int i, int j) { return i % j; };     // lambda
    struct div {                                       // 类
        int operator()(int denominator, int divisor) {  
            return denominator / divisor;
        }
    };
    

      

    19.使用标准库类型function表示一类可调用对象。
    function<T>是一个模版类,int (int, int)是调用签名,可以表示一类可调用对象:有两个int参数并返回int。
    function<int(int, int)>                    // function<int (int, int)>是一个类,存储的类型是调用签名为int (int, int)的可调用对象
     
     
    function<int(int, int)> f1 = add;                 // 函数指针
    function<int(int, int)> f2 = div();               // 函数对象类的对象
    function<int(int, int)> f3 = [](int i, int j) { return i * j; }; // lambda
    cout << f1(4,2) << endl;                       //  6
    cout << f2(4,2) << endl;                    //  2
    cout << f3(4,2) << endl;                      //  8
     
    map<string, function<int(int, int)>> binops = {
        {"+", add},
        {"-", std::minus<int>()},
        {"/", div()},
        {"*", [](int i, int j) { return i * j; }},   // 未命名的lambda
        {"%", mod} };                        // 命名的lambda
     
    binops["+"](10, 5);                     // calls add(10, 5)
    binops["-"](10, 5);                   // uses the call operator of the minus<int> object
    binops["/"](10, 5);                   // uses the call operator of the div object
    binops["*"](10, 5);                  // calls the lambda function object
    binops["%"](10, 5);                   // calls the lambda function object
     
     
    20.如果有重载函数,则不能直接存储重载函数的名字。
    int add(int i, int j) { return i + j; }
    Sales_data add(const Sales_data&, const Sales_data&);  // overloaded 
    map<string, function<int(int, int)>> binops;
    binops.insert( {"+", add} );                 // error: 哪个add?
     
    // 解决:
    int (*fp)(int,int) = add;                   // 使用函数指针
    binops.insert( {"+", fp} );
    binops.insert( {"+", [](int a, int b) {return add(a, b);} });  // 或者lambda
     
    21. 转换操作符是一种特殊的成员函数,它将类类型的值转换为其他类型的值。
    operator type() const;    // 类型转换操作符没有参数列表,没有返回类型,但是必须返回函数名类型一样的值
     
    class SmallInt {
    public:
        SmallInt(int i = 0): val(i) {
            if (i < 0 || i > 255)
                throw std::out_of_range("Bad SmallInt value");
        }
        operator int() const { return val; }
    private:
        std::size_t val;
    };
    SmallInt si;
    si = 4;   // 将 4 隐式转换为 SmallInt 类型后调用 SmallInt::operator=
    si + 3;   // 将 si 隐式装换为 int 类型后做整数加法
    

      

    22.显示的类型转换操作符只能通过static_cast<type>调用。只有在条件判断语句中才会由class类型隐式转换到其他类型。
    class SmallInt {
    public:
        explicit operator int() const { return val; }
        // other members as before
    };
    SmallInt si = 3; 
    si + 3; 
    static_cast<int>(si) + 3;
     
    23. 在条件语句中使用流对象,都使用为IO类型定义的bool转换函数。
    while (std::cin >> value)    // cin由istream操作符bool转换函数隐式转换
     
    24. 有两种方式可以实现多种转换路径。
    • 两个类提供相互转换。
    struct B;
    struct A {
        A() = default;
        A(const B&);              // converts a B to an A
    }; 
    struct B {
        operator A() const;      // also converts a B to an A
    };
     
    A f(const A&);
    B b;
    A a = f(b);              // error: 歧义,不知道调用哪个转换函数
     
    A a1 = f(b.operator A());   // ok: 显式调用
    A a2 = f(A(b));
    • 当一个类定义了多个到其他类型的转换操作时,如果这些类型之间,那么也会产生歧义。e.g.: 给定的类定义了多个与算术类型的转换。
    struct A {
        A(int = 0);
        A(double);
        operator int() const;
        operator double() const; 
    };
    void f2(long double);
    A a;
    f2(a);       // error: 歧义:f(A::operator int()) or f(A::operator double())
    long lg;
    A a2(lg);    // error 歧义:A::A(int) or A::A(double)
     
    25. 当调用是通过类类型的对象(或通过此类对象的引用或指针)进行时,则只考虑该类的成员函数。
    当在表达式中使用重载操作符时,无法表明使用的是成员函数还是非成员函数。
    class SmallInt {
    friend SmallInt operator+(const SmallInt&, const SmallInt&);
    public:
        SmallInt(int = 0); // conversion from int
        operator int() const { return val; } // conversion to int
    private:
        std::size_t val;
    };
     
    SmallInt s1, s2;
    SmallInt s3 = s1 + s2;   // uses overloaded operator+
    int i = s3 + 0;          // error:歧义,无法知道是s3(SmallInt)转换成int类型,还是0(int)转换成SmallInt类型
     
  • 相关阅读:
    Alpha 答辩总结
    Alpha 冲刺 (10/10)
    Alpha 冲刺 (9/10)
    Alpha 冲刺 (8/10)
    Alpha 冲刺 (7/10)
    Alpha 冲刺 (6/10)
    团队作业-随堂小测(同学录)
    Alpha 冲刺 (5/10)
    第07组 Alpha事后诸葛亮
    第07组 Alpha冲刺(6/6)
  • 原文地址:https://www.cnblogs.com/tristatl/p/14825157.html
Copyright © 2020-2023  润新知