C++ STL

有关C++ STL的使用的一些注意点。

一、map

  map在STL中定义为:

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;

  对于一般的使用来说,map<class key, class T>即可。但是对于key无法直接比较大小时,则需要对key类型重载<等操作符。详细代码如下:

#include<map>
#include<string>
#include<iostream>
using namespace std;
typedef struct tagStudentInfo
{
    int nID;
    string strName;
    bool operator < (tagStudentInfo const& _A) const
    {
        //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序
        if(nID < _A.nID)  return true;
        if(nID == _A.nID) return strName.compare(_A.strName) < 0;
        return false;
    }
}StudentInfo, *PStudentInfo;  //学生信息

int main()
{
    //用学生信息映射分数
    map<StudentInfo, int> mapStudent;
    map<StudentInfo, int>::iterator iter;
    StudentInfo studentInfo;
    studentInfo.nID = 1;
    studentInfo.strName = "student_one";
    mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));
    studentInfo.nID = 2;
    studentInfo.strName = "student_two";
    mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));
    for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)
        cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl;  
}  

重载操作符时,注意一般应用const修饰,一个比较浅显的原因是,const对象只能调用const成员函数,对于可以比较的类来说,const对象当然也能够比较大小,因此某种程度上,不修改对象的函数都可以加上const。

  上述代码中,tagStudentInfo类型的结构体无法直接比较大小,因此如果不重载小于<操作符,那么插入map的时候无法根据tagStudentInfo来判断是否相等或比较大小(C++中使用严格弱排序,即不允许a<b和b<a同时成立,而且等于是通过!(a<b)&&!(b<a) 来进行),编译器会对这种行对报错。

  另一种解决办法,则是在map声明中加入第三个参数class Compare: map<class Key, class T, class compare>,使用class compare的函数对象来比较大小,即重载类operator()来实现,代码如下:

#include<map>
#include<string>
using namespace std;
typedef struct tagStudentInfo
{
    int nID;
    string strName;
}StudentInfo, *PStudentInfo;  //学生信息
class compare  
{
    public:
    bool operator() (StudentInfo const & _A, StudentInfo const & _B) const
    {
        if(_A.nID < _B.nID) return true;
        if(_A.nID == _B.nID) return _A.strName.compare(_B.strName) < 0;
        return false;
    }  
};
int main()  
{
    //用学生信息映射分数
    map<StudentInfo, int, compare> mapStudent;
    StudentInfo studentInfo;
    studentInfo.nID = 1;
    studentInfo.strName = "student_one";
    mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));
    studentInfo.nID = 2;
    studentInfo.strName = "student_two";
    mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));  
}

  map成员函数的返回值也需要注意。

  1. insert()函数:返回由map<class key, class T>::iterator和bool类型组成的pair,即:

map<class key, class T> mp;
pair<map<class key, class T>, bool> insert_itor = mp.insert(value_type(class key, class T)(key, T))

  如插入成功,则返回插入的位置和true;若插入失败,则返回冲突的位置和false。

  2. upper_bound(n)和lower_bound(n)函数:返回map<class key, class T>::iterator类型的迭代器,lower_bound(n)返回大于等于n的key位置的迭代器,upper_bound(n)返回大于n的key的位置的迭代器。equal_range()则返回一个由两个map<class key, class T>::iterator类型组成的pair,前者为lower_bound(n)的返回值,后者为upper_bound(n)的返回值。如果n不存在于map中,则equal_range()返回值的first和second()相等,这也可以用来判断元素是否存在于map中。

(上述代码来源于sunshinewave

 

二、unordered_set/unordered_map

   两者在STL中的定义为:

template < class Key,                        // unordered_set::key_type/value_type
           class Hash = hash<Key>,           // unordered_set::hasher
           class Pred = equal_to<Key>,       // unordered_set::key_equal
           class Alloc = allocator<Key>      // unordered_set::allocator_type
           > class unordered_set;

template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;

  unordered_set和unordered_map在STL中有着类似的构造:都是通过hash函数将key值转化为相应的hash值来进行储存,检索时间复杂度皆为O(1)。对于基本数据类型,如int、char等整形值,STL中定义的hash函数只是单纯地将整形转换为size_t型值,然后对当前hashtable的bucket数量取模即可;同时STL也对string型对象提供了一个hash函数。但是对于自定义类型的key值,unordered_set和unordered_map需要自定义hash函数和以及equal_to函数。

  对于hash函数,可以使用函数对象。例如,对于x、y坐标值在0~99的点序列,可以将100*x+y作为其hash值(相当于100进制的整数),于是可以以下面的形式定义hash函数:

#include<bits\stdc++.h> //bits\stdc++.h包含所有头文件 不过编译时间较慢
using namespace std;
struct myHash
{
    size_t operator()(pair<int, int> __val) const
    {
        return static_cast<size_t>(__val.first * 100 + __val.second);
    }
};
int main()
{
    unordered_set<pair<int, int>, myHash> S;
    int x, y;
    while (cin >> x >> y)
        S.insert(make_pair(x, y));
    for (auto it = S.begin(); it != S.end(); ++it)
        cout << it->first << " " << it->second << endl;
    return 0;
}

  对于equal_to函数,则可以重载==操作符或者使用函数对象来实现。类似于map中操作符<的重载和class compare的函数对象来实现。

 

三、string和wstring

  wstring实现对wchar_t类型字符的操作,用于满足非ASCII字符的需求。对于wchar_t类型,STL中都有与string相应的操作,如wcout、wcin等。对于汉字来说,string也可以进行储存,但是是通过两个char储存一个汉字的方式实现的,因此对于汉字字符串的操作需要通过特殊方式实现。而一个wchar_t能储存一个汉字,因此能完全满足汉字字符串的需求。但是在使用wchar_t时,需要通过imbue()函数指定输出时的区域语言对象,如:cout.imbue(locale("chs"))。详细代码如下:

#include<iostream>
#include<string>
#include<locale.h>
using namespace std;

#define tab "\t"

int main()
{
    locale def;
    cout<<def.name()<<endl;
    locale current = cout.getloc();
    cout<<current.name()<<endl;

    //chage to chinese
    cout.imbue(locale("chs"));
    current=cout.getloc();
    cout<<current.name()<<endl;

    //上面是说明locale的用法,下面才是本例的内容,因为其中用到了imbue函数
    cout<<"*********************************"<<endl;

    //为了保证本地化输出(文字/时间/货币等),chs表示中国,wcout必须使用本地化解析编码
    wcout.imbue(std::locale("chs"));

    //string 英文,正确颠倒位置,显示第二个字符正确
    string str1("ABCabc");
    string str11(str1.rbegin(),str1.rend());
    cout<<"UK\ts1\t:"<<str1<<tab<<str1[1]<<tab<<str11<<endl;

    //wstring 英文,正确颠倒位置,显示第二个字符正确
    wstring str2=L"ABCabc";
    wstring str22(str2.rbegin(),str2.rend());
    wcout<<"UK\tws4\t:"<<str2<<tab<<str2[1]<<tab<<str22<<endl;

    //string 中文,颠倒后,变成乱码,第二个字符读取也错误
    string str3("你好么?");
    string str33(str3.rbegin(),str3.rend());
    cout<<"CHN\ts3\t:"<<str3<<tab<<str3[1]<<tab<<str33<<endl;

    //正确的打印第二个字符的方法
    cout<<"CHN\ts3\t:RIGHT\t"<<str3[2]<<str3[3]<<endl;

    //中文,正确颠倒位置,显示第二个字符正确
    wstring str4=L"你好么?";
    wstring str44(str4.rbegin(),str4.rend());
    wcout<<"CHN\tws4\t:"<<str4<<tab<<str4[1]<<tab<<str44<<endl;

    //只有char类型的string时才可以如此构造
    wstring str5(str1.begin(),str1.end());
    wstring str55(str5.rbegin(),str5.rend());
    wcout<<"CHN\tws5\t:"<<str5<<tab<<str5[1]<<tab<<str55<<endl;

    //如此构造将失败!!!!
    wstring str6(str3.begin(),str3.end());
    wstring str66(str6.rbegin(),str6.rend());
    wcout<<"CHN\tws6\t:"<<str6<<tab<<str6[1]<<tab<<str66<<endl;

    return 0;
}

  code::blocks下代码编码出错(error: converting to execution character set: Illegal byte sequence),加上-finput-charset=GB2312等之后仍然有错。待进一步验证问题。。。

 

(持续更新)

posted @ 2017-04-10 07:49  MetaSiBaL  阅读(245)  评论(0)    收藏  举报