首页 > 代码库 > C++ Primer 学习笔记_61_重载操作符与转换 --自增/自减操作符

C++ Primer 学习笔记_61_重载操作符与转换 --自增/自减操作符

重载操作符与转换

--调用操作符和函数对象

引言:

    可以为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符

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

通过为类类型的对象提供一个实参表而使用调用操作符,所用的方式看起来系那个一个函数调用:

    absInt absObj;
    int i = -1;
    cout << absObj(i) << endl;

尽管absObj是一个对象而不是函数,我们仍然可以“调用”该对象,效果是运行有absObj对象定义的重载调用操作符,该操作接受一个int值并返回它的绝对值

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

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

//P450 习题14.31
struct ifElse
{
    int operator() (int x,int y,int z)
    {
        return x ? y : z;
    }
};

int main()
{
    ifElse ifObj;
    cout << ifObj(0,1,2) << endl;
    cout << ifObj(1,1,2) << endl;
}

一、将函数对象用于标准库算法

     函数对象经常用作通用算法的实参。回忆曾经的一段程序:

bool GT6(const string &str)
{
    return str.size() > 6;
}


使用GT6作为传给count_if算法的实参,以计算使GT6返回true的单词数目:

    vector<string>::size_type wc =
        count_if(words.begin(),words.end(),GT6);

1、函数对象可以比函数更灵活

    通过将GT6定义为带函数调用成员的类,可以使得传递的string与我们想要的长度进行测试,这样,可以使用同一段代码对不同长度的字符串进行计数: 

class GT_cls
{
public:
    GT_cls(size_t val = 0):bound(val){}

    bool operator() (const string &str)
    {
        return str.size() >= bound;
    }

private:
    std::string::size_type bound;
};

2、使用GT_cls函数对象

    cout << count_if(words.begin(),words.end(),GT_cls(6))
         << " words 6 characters or longer" << endl;

这个count_if调用传递一个GT_cls类型的临时对象而不再是名为GT6的函数。用整型值6来初始化那个临时对象,构造函数将这个值存储在bound成员中。现在,count_if每次调用它的函数形参时,都使用GT_cls调用操作符,该调用操作符根据bound的值测试其string实参的长度。

    使用函数对象,容易修改程序以根据其他值进行测试,只需为传给count_if的对象改变构造函数实参即可。

    //计算长度在5个字符以上的单词数
    cout << count_if(words.begin(),words.end(),GT_cls(5))
         << " words 6 characters or longer" << endl;  

还可以用来计算长度在110个字符的单词数:

    for (size_t i = 1; i != 11; ++i)
    {
        cout << count_if(words.begin(),words.end(),GT_cls(i))
             << " words " << i << " characters or longer" << endl;
    }

如果使用函数替代函数对象来编写这个程序,可能需要编写10个(⊙﹏⊙b汗)不同的函数,每个函数测试一个不同的值!

//P451 习题14.33
class GT_cls
{
public:
    GT_cls(size_t val = 0):bound(val) {}

    bool operator() (const string &str)
    {
        return str.size() > bound;  //将>=改为>
    }

private:
    std::string::size_type bound;
};

int main()
{
    vector<string> words;
    ifstream inFile("input");
    string word;
    while (inFile >> word)
    {
        words.push_back(word);
    }

    vector<string>::iterator findIter = find_if(words.begin(),words.end(),GT_cls(5));

    if(findIter != words.end())
    {
        cout << *findIter << endl;
    }
    else
    {
        cout << "Have No Found!" << endl;
    }
}

//习题14.34
struct IsSame
{
    bool operator() (const string &s1,const string &s2)
    {
        return s1 == s2;
    }
};

int main()
{
    IsSame isSame;
    cout << isSame("xiao","fang") << endl;
    cout << isSame("Ha","Ha") << endl;
}

//习题14.35/36
class BET_cls
{
public:
    BET_cls(size_t Max = 0,size_t Min = 0):max(Max),min(Min) {}
    bool operator() (const string &str)
    {
        return str.size() <= max && str.size() >= min;
    }

private:
    std::string::size_type max;
    std::string::size_type min;
};

class GT_cls
{
public:
    GT_cls(size_t size = 0):bound(size) {}
    bool operator() (const string &str)
    {
        return str.size() >= bound;
    }

private:
    std::string::size_type bound;
};
int main()
{
    ifstream inFile("input");
    vector<string> words;
    string word;

    while (inFile >> word)
    {
        words.push_back(word);
    }

    cout << "The is " << count_if(words.begin(),words.end(),BET_cls(9,1))
         << " words`s size is between 1 and 9" << endl;

    cout << "And " << count_if(words.begin(),words.end(),GT_cls(10))
         << " words`s size is equal to 10 or longer" << endl;
}


二、标准库定义的函数对象

     标准库定义了一组算术、关系与逻辑函数对象类。标准库还定义了一组函数适配器,使我们能够特化或者扩展标准库所定义的以及自定义的函数对象类。这些标准库函数对象类型是在functional头文件中定义的。

标准库函数对象

类型

函数对象

所应用的操作符

算术函数对象类型

plus<Type>

+

minus<Type>

-

multiplies<Type>

*

divides<Type>

/

modulus<Type>

%

关系函数对象类型

negate<Type>

-

equal_to<Type>

==

not_equal_to<Type>

!=

greater<Type>

>

greater_equal<Type>

>=

less<Type>

<

less_equal<Type>

<=

逻辑函数对象类型

logical_and<Type>

&&

logical_or<Type>

||

logical_not<Type>

!



1、每个类表示一个给定操作符

    每个标准库函数对象类表示一个操作符,每个都定义了应用命名操作的调用操作符。如plus是表示加法操作符的模板类型,plus模板中的调用操作符对一对操作数使用+运算

//假设Type类型为int,简单实现一下plus
class Plus
{
public:
    int operator() (int a,int b)
    {
        return a + b;
    }
//...
};

    有两个一元函数对象类:一元减(negate<Type>))和逻辑非(logical_not<Type>))。其余的标准库函数对象都是表示二元操作符的二元函数对象类。为二元操作符定义的调用操作符需要两个给定类型的形参,而一元函数对象类型定义了接受一个实参的调用操作符。


2、表示操作数类型的模板类型

   每个函数对象类都是一个类模板,我们需要为该模板提供一个类型:

    plus<string> strAdd;
    plus<int> intAdd;


应用:

    plus<string> strAdd;
    cout << strAdd("Hello ","World\n");

    plus<int> intAdd;
    negate<int> intNegate;
    cout << intAdd(10,20) << endl;

    int sum = intAdd(10,intNegate(20));
    cout << sum << endl;

3、在算法中使用标准库函数对象

    函数对象常用于覆盖算法使用的默认操作符。例如,sort默认使用operator<按升序对容器进行排序。为了按降序对容器进行排序,可以传递函数对象greater。该类将产生一个调用操作符,调用基础对象的大于操作符。

    sort(strVec.begin(),strVec.end(),greater<string>());  //降序排序

//附:完整程序测试
void printVec(const vector<string> &vec)
{
    for (vector<string>::const_iterator iter = vec.begin();
            iter != vec.end(); ++iter)
    {
        cout << *iter << ‘\t‘;
    }
    cout << endl;
}

int main()
{
    ifstream inFile("input");

    vector<string> strVec;
    string word;
    while (inFile >> word)
    {
        strVec.push_back(word);
    }

    sort(strVec.begin(),strVec.end());  //升序排序
    printVec(strVec);

    sort(strVec.begin(),strVec.end(),greater<string>());  //降序排序
    printVec(strVec);
}

   第二个sort函数的第三个实参greater<string>是一个临时对象,是一个将>操作符应用于两个string操作数的函数对象


三、函数对象的函数适配器

    标准库提供了一组函数适配器,用于特化扩展一元和二元函数对象。函数适配器分为:

1、绑定器:是一种函数适配器,它通过将一个操作数绑定到给定值,而将二元函数对象转换为一元函数对象。有:bind1str,bind2nd.

    每个绑定器接受一个函数对象和一个值,bind1st给定值绑定到二元函数对象的第一个实参,bind2nd将给定值绑定到二元函数对象的第二个实参

    //计算容器中所有小于或等于 10 的元素的个数
    count_if(vec.begin(),vec.end(),bind2nd(less_equal<int>(),10));

   该适配器返回一个函数对象,该对象用10作右操作数应用<=操作符,调用计算输入范围中小于或等于10的元素的个数。


2、求反器:将谓词函数的真值求反。有:not1not2

   not1一元函数对象的真值求反,not2二元函数对象的真值求反。

    //是对不 <= 10[即:> 10] 的那些元素进行计数
    count_if(vec.begin(),vec.end(),
             not1(bind2nd(less_equal<int>(),10)));

//P453 习题14.37
//(a)
int main()
{
    vector<int> ivec;

    ifstream inFile("input");
    int val;

    while (inFile >> val)
    {
        ivec.push_back(val);
    }

    typedef vector<int>::iterator iterType;
    iterType iter = ivec.begin();

    while ((iter = find_if(iter,ivec.end(),bind2nd(greater<int>(),10))) != ivec.end())
    {
        cout << *iter << endl;
        ++ iter;
    }
}


//(b)
int main()
{
    vector<string> strVec;

    ifstream inFile("input");
    string val;

    while (inFile >> val)
    {
        strVec.push_back(val);
    }

    typedef vector<string>::iterator iterType;
    iterType iter = strVec.begin();

    //主要的改动在于是换成了not_equal_to
    while ((iter = find_if(iter,strVec.end(),
                           bind2nd(not_equal_to<string>(),"pooh")))
            != strVec.end())
    {
        cout << *iter << endl;
        ++ iter;
    }
}


//(3)
int main()
{
    vector<int> ivec;

    ifstream inFile("input");
    int val;

    while (inFile >> val)
    {
        ivec.push_back(val);
    }


    typedef vector<int>::iterator iterType;

    multiplies<int> intMulti;
    for (iterType iter = ivec.begin(); iter != ivec.end(); ++iter)
    {
        *iter = intMulti(*iter,2);
    }

    /**或者使用标准库算法:transform
    *transform(ivec.begin(),ivec.end(),ivec.begin(),bind2nd(multiplies<int>(),2));
    */

    for (iterType iter = ivec.begin(); iter != ivec.end(); ++iter)
    {
        cout << *iter << endl;
    }
}

//习题14.39
int main()
{
    ifstream inFile("input");
    vector<string> words;
    string word;

    while (inFile >> word)
    {
        words.push_back(word);
    }

    cout << "And " << count_if(words.begin(),words.end(),GT_cls(3))
         << " words`s size is equal to 3 or longer" << endl;

    greater_equal<string::size_type> sizeGreEQ;
    string::size_type wc = 0;
    for (vector<string>::iterator iter = words.begin();
            iter != words.end(); ++iter)
    {
        if (sizeGreEQ(iter -> size(),3))
            ++ wc;
    }
    cout << "And " << wc
         << " words`s size is equal to 3 or longer" << endl;
}