关联容器(第3章)(条款19,20)

条款19:理解相等和等价的区别

在关联容器set/multiset, map/multimap中,有一个模板参数用来确定容器元素的排序规则,使用的是标准款less,而在set, map等只能保留唯一的元素中,是以刚才的排序规则来定义等价的。

模板类map形式如下:

std::map
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;

标准模板类set形式如下:

std::set
template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T>      // set::allocator_type
           > class set;

等价按照如下定义,通俗的说就是a不在b前面,b也不再a前面,但是跟相等不是一回事,相等时直接用operator==比较,两个容器的元素可能等价,但是不一定相等

!comp(a,b) && !comp(b,a))

一个例子,字符串之间以不区分大小写进行比较的一个实现(可能有点Low,进行了字符串拷贝,这不是重点忽略)

    // string不区分大小写的比较
    struct CIStringCompare : public std::binary_function<string, string, bool> {
        bool operator()(const string &lhs, const string &rhs) const {
            string lstr(lhs);
            std::transform(lhs.cbegin(), lhs.cend(), lstr.begin(), tolower);
            string rstr(rhs);
            std::transform(rhs.cbegin(), rhs.cend(), rstr.begin(), tolower);
            return lstr < rstr;
        }
    };


    void test_19() {
        set<string, CIStringCompare> ciss; // 不区分大小写的字符串集合
        ciss.insert("Persephone"); // 显然,这两个字符串不相等,但是根据我们对排序规则的定义,它们是等价的
        ciss.insert("persephone");
        std::for_each(ciss.cbegin(), ciss.cend(), [](const string &str) { std::cout << str << std::endl; });

        return;
    }

条款20:为包含指针的关联容器指定比较类型

假设定义一个包含string*的set容器,我们依次插入一些动物的名字到该集合中,然后逐一打印,如下所示,没错,你打印了个寂寞,*Iter是取元素,也就是string*,打印字符串还需要加上*,取指针的内容噻

    void test_20() {
        set<string *> ssp;
        ssp.insert(new string("anteater"));
        ssp.insert(new string("wombat"));
        ssp.insert(new string("lemur"));
        ssp.insert(new string("penguin"));
        for(auto iter = ssp.cbegin(); iter != ssp.cend(); ++iter) {
            std::cout << *iter << std::endl;
        }

        return;
    }

输出:
0x3a5170
0x3a5220
0x3a52d0
0x3a5380

修改后正常了

std::cout << **iter << std::endl;

输出:
anteater
wombat
lemur
penguin

以上都不是重点,你注意到没,怎么字符串没有按照规则排序呢,因为容器元素是指针,less默认对指针排序了,有字符串什么事,因此引出本条款的核心,你应该为指针写出自己的比较类型,如下所示,正确了。

    // string*比较
    struct StrPtrCompare : public std::binary_function<string *, string *, bool> {
        bool operator()(const string *lhs, const string *rhs) const {
            return *lhs < *rhs;
        }
    };

    void test_20() {
        set<string *, StrPtrCompare> ssp;
        ssp.insert(new string("anteater"));
        ssp.insert(new string("wombat"));
        ssp.insert(new string("lemur"));
        ssp.insert(new string("penguin"));
        for (auto iter = ssp.cbegin(); iter != ssp.cend(); ++iter) {
            std::cout << **iter << std::endl;
        }

        return;
    }

输出:
anteater
lemur
penguin
wombat

等会,还没完呢,我们竟然直接new string插入数组,呵呵,你是想自己管理内存然后一个个delete释放吗,不,C++11给我们提供了智能指针

    // std::shared_ptr<string>比较
    struct SharedPtrCompare : public std::binary_function<std::shared_ptr<string>, std::shared_ptr<string>, bool> {
        bool operator()(const std::shared_ptr<string> &lhs, const std::shared_ptr<string> &rhs) const {
            return *lhs < *rhs;
        }
    };

    void test_20() {
        // 智能指针
        set<std::shared_ptr<string>, SharedPtrCompare> sSharedp;
        sSharedp.insert(std::make_shared<string>("anteater"));
        sSharedp.insert(std::make_shared<string>("wombat"));
        sSharedp.insert(std::make_shared<string>("lemur"));
        sSharedp.insert(std::make_shared<string>("penguin"));
        for (auto iter = sSharedp.cbegin(); iter != sSharedp.cend(); ++iter) {
            std::cout << **iter << std::endl;
        }
        
        return;
    }

输出:
anteater
lemur
penguin
wombat

最后,定义一个模板他不香吗,为啥要为每个类型都定义一个比较类型

    // 为指针、智能指针定义的通用比较模板类
    struct DereferenceLess {
        template<typename PtrType>
        bool operator()(PtrType p1, PtrType p2) const {
            return *p1 < *p2;
        }
    };

    void test_20() {
        // 普通指针
//        set<string *, StrPtrCompare> ssp;
        set<string *, DereferenceLess> ssp;
        ssp.insert(new string("anteater"));
        ssp.insert(new string("wombat"));
        ssp.insert(new string("lemur"));
        ssp.insert(new string("penguin"));
        for (auto iter = ssp.cbegin(); iter != ssp.cend(); ++iter) {
            std::cout << **iter << std::endl;
        }

        // 智能指针
//        set<std::shared_ptr<string>, SharedPtrCompare> sSharedp;
        set<std::shared_ptr<string>, DereferenceLess> sSharedp;
        sSharedp.insert(std::make_shared<string>("anteater"));
        sSharedp.insert(std::make_shared<string>("wombat"));
        sSharedp.insert(std::make_shared<string>("lemur"));
        sSharedp.insert(std::make_shared<string>("penguin"));
        for (auto iter = sSharedp.cbegin(); iter != sSharedp.cend(); ++iter) {
            std::cout << **iter << std::endl;
        }

        return;
    }

输出:
anteater
lemur
penguin
wombat
anteater
lemur
penguin
wombat

参考:《Effective STL中文版》第3章

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112382157