C++stl与类

原文地址: http://www.cnblogs.com/jianxinzhou/p/3982771.html

**********************作者是不是转的我不清楚呀

OOP之类和对象

1. this指针的引入

每个成员函数都有一个额外的隐含的形参,这个参数就是this指针,它指向调用对象的地址。默认情况下,this的类型是指向类类型非常量版本的常量指针。可以表示成如下伪代码形式:

/* 假设现在有一个类Sales_data,以及其非常量Sales_data类型对象,则该隐式的this指针可以写成如下伪代码形式 */
Sales_data *const this = &total;

this指针一般用于解决重名问题和返回自身的值或者引用。例如:

struct A{
    int a;

    void test(int a){
        this->a = a;
    }
};

test函数的形参a和类成员a成名,根据就近原则,直接使用a,调用的是形参a,那么如何使用被屏蔽的成员a呢,这里就是采用this指针。

2. const成员函数

紧随参数列表之后的const关键字作用为:修改隐式this指针所指向的对象的类型,如下:

/* 假设现在有一个类Sales_data,以及Sales_data类型对象,则在const成员函数中隐式的this指针可以写成如下伪代码形式 */
const Sales_data *const this = &total;

这里加const的含义是,这个函数不能修改本对象,其实就是函数体内不得对类的成员进行修改。const主要起到保护的作用。

注意以下几点:

a)非const对象可以调用const成员函数,也可以调用非const成员函数,但是const对象只能调用const成员函数。并且,非const对象优先调用非const成员函数。

b)const成员函数只可以返回本对象的常量引用,如下写法会报错:

Student &print(ostream &os) const
{
    os << id_ << " " << name_ << " " << age_ << endl;
    return *this;
}

报错提示:

clang下:error: binding of reference to type ‘Student‘ to a value of type ‘const Student‘ drops qualifiers
            return *this;

g++下:error: invalid initialization of reference of type ‘Student&’ from e
            return *this;

最后记住:构造函数不能为const。如果为const,怎么完成初始化工作?!

3. const成员函数和非const成员函数可以构成重载。

到此为止,构成函数重载的要素有:类的名称、函数名、函数形参表以及成员函数的const属性。事实上,函数签名就是由这几个部分构成。

在这里我们解释一个问题: 为什么C语言里面没有函数重载? 因为在编译器编译C程序时会维护一张符号表,C语言在记载函数的时候就是简单的记录函数的名字,所以函数名就是C函数的唯一标识。当我们试图定义两个名字相同的函数时,就发生了重定义。

C++是怎么做的呢? 很显然,对于普通函数,它的符号(唯一标识)是根据函数名和参数列表生成的,对于类的成员函数,还要加上类名和const属性,所以我们进行函数重载的时候,这些函数在符号表中的标识是不相同的。 C++正是通过这种机制实现了函数的重载

注意:C++编译器生成函数符号的时候没有考虑返回值,这也是函数重载和返回值无关的原因。

4. 构造函数之构造函数初始值列表(constructor initialize list)

构造函数有一个特殊的地方,就是它可以包含一个构造函数初始化列表,如下:

Person(int id, const string &name, int age)
         :_id(id), _name(name), _age(age){
}

虽然以下形式,也完全可以达到目的:

Person(int id, const string &name, int age){
        _id = id;
        _name = name;
        _age = age;
}

但两者是不同的。第一种形式带构造函数初始值列表,执行的是真正的初始化工作;而第二种形式,进行的是赋值操作。

注意,即使构造函数没有构造函数初始值列表(更确切的说是构造函数初始值列表为空),那么类中的成员变量将会执行默认初始化。因此在以下情况我们必须使用构造函数默认初始化列表:

a)const内置类型变量以及没有显示定义默认构造函数的const类类型变量(可以参考该博文合成的默认构造函数定义为delete的一种情况

b)引用类型成员

c)没有默认构造函数的类类型变量

其本质是因为,const内置类型变量和引用类型必须初始化;而对于类类型对象,可以通过默认构造函数进行默认初始化(非const类类型对象只要有默认构造函数就可以默认初始化,而const类类型对象必须有显示定义的默认构造函数才可以执行默认初始化)

5. 类成员初始化的顺序是它们在类中声明的顺序,而不是初始化列表中列出的顺序

考虑下面的类:

class X {
    int i;
    int j;
public:
    X(int val) :
    j(val), i(j) {
    }
};

我们的设想是这样的,用val初始化j,用j的值初始化i,然而这里初始化的次序是先i然后j。

记住:类成员初始化的顺序是它们在类中声明的顺序,而不是初始化列表中列出的顺序!

6. 析构函数

与构造函数一样,析构函数也是一种特殊的函数。构造函数在对象被创建时调用,析构函数则是在对象被销毁时被调用。构造函数与构造函数一样,同样没有返回值,并且析构函数没有任何参数。如下:

~Person(){
        
}

需要引起注意的是:

a)对于类类型对象foo的析构函数只是在它生命期的最后一刻的回调罢了,管不了foo自己所占的内存,就像自己没法给自己收尸一样。

b)对于堆上的类类型对象:free 干的事情是释放内存。delete 干的事情是调用析构函数,然后释放内存,注意是delete释放的内存空间,而不是析构函数释放的。对于栈上的类类型对象,退出作用域时会自动调用析构函数,然后释放内存。

总结:对于栈上的类类型对象其实和内置类型变量一样,退出作用域后都是由系统自动释放内存的。实际上无论是栈空间,还是堆空间,内置类型对象和类类型对象销毁时的区别,在于类对象会在销毁前调用析构函数。

7. static成员

不用于普通的数据成员,static 数据成员独立于该类的任何一个对象而存在,每个static数据成员是与类关联,并不与该类的对象相关联。

正如类可以定义共享的 static 数据成员一样,类也可以定义 static 成员函数。static 成员函数没有 this 形参(因为static成员不属于任何一个对象),它可以直接访问所属类的 static 成员,但不能直接使用非 static 成员(因为没有this指针)。当我们在类的外部定义 static成员时,无须重复指定 static 保留字,该保留字只出现在类定义体内部的声明处即可。

小结:

a)static 成员是类的组成部分但不是任何对象的组成部分,因此,static 成员函数没有 this 指针

b)因为 static 成员不是任何对象的组成部分,所以 static 成员函数不能是const成员函数。因为,将成员函数声明为 const 就是承诺不会修改该函数所属的对象,而 static 成员不是任何对象的组成部分。

c)static 函数只能使用 static 成员,而不能直接调用普通成员(方法+数据成员),当然如果这样写,static void print(Test &t) 谁也挡不住其调用对象t的普通成员。

d)static 成员一般在类内声明,类外定义。注意,当我们在类的外部定义 static 成员时,无须重复指定 static 保留字,该保留字只出现在类定义体内部的声明处即可。

8. 友元

1. 必须先定义包含成员函数的类,才能将这个类的成员函数设置为另外一个类的友元。

2. 不必预先声明类和非成员函数来将它们设为友元。

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Test
{
    public:
        friend class Other;                //声明某个类是Test的朋友
        friend void bar(const Test &t);     //声明某个函数是Test的朋友
    private:
        int x_;
        int y_;
};

class Other
{
    public:
        void foo(Test &t)
        {
            t.x_ = 10;
            t.y_ = 20;
        }
};

void bar(const Test &t)
{
    cout << t.x_ << endl;
}

int main(int argc, const char *argv[])
{
    Test t;
    return 0;
}

注意:友元关系是单向的,以上例子中Test并不是Other的朋友,因此Test不能访问Other的private成员。(tmd,这不就是在告诉我们,你的是我的,我的还是我的)。顺便黑一下C++:

C++ is a modern language where your parent can‘t touch your privates but your friends can.

多么痛的领悟。

STL之顺序容器

1. 顺序容器的初始化

顺序容器主要是vector和list,他们的初始化方式有以下五种:

1. 直接初始化一个空的容器

2. 用一个容器去初始化另一个容器

3. 指定容器的初始大小

4. 指定容器的初始大小和初始值

5. 用一对迭代器范围去初始化容器

第2种和第5种初始化方式的区别在于:第2种不仅要求容器类型相同,还要求容器元素类型完全一致,而第5种不要求容器相同,对于容器元素,要求能相互兼容即可。

指针可以当做迭代器,所以可以这样做:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main(int argc, char **argv) {
    
    const size_t MAX_SIZE = 3;
    string arr[MAX_SIZE] = { "hello", "world", "foobar" };

    vector<string> vec(arr, arr + MAX_SIZE);

    return 0;
}

注意,凡是传入迭代器作为指定范围的参数,可以使用指针代替。

2. 容器元素的类型约束

凡是放入vector中的元素,必须具备复制和赋值的能力,因为放入vector中的元素只是一份拷贝。下例会报错。

#include <iostream>
#include <string>
#include <vector>
using namespace std;

//Test不支持复制和赋值。所以不能放入vector
class Test
{
    public:
        Test() {}

    private:
        //设为私有,禁用了Test的复制和赋值能力 
        Test(const Test &);           //用于复制(拷贝构造函数)
        void operator=(const Test &); //用于赋值(赋值运算符)
};

int main(int argc, const char *argv[])
{
    vector<Test> vec;
    Test t;
    vec.push_back(t);
    return 0;
}

3. 特殊的迭代器成员 begin和end

有四个特殊的迭代器:

c.begin()   //指向容器C的第一个元素

C.end()     //指向最后一个元素的下一个位置

C.rbegin() //返回一个逆序迭代器,指向容器c的最后一个元素

C.rend()   //返回一个逆序迭代器,指向容器c的第一个元素的前面的位置

分别去顺序迭代和逆序迭代容器,例如:

#include <iostream>
#include <string>
#include <vector>
#include <list>

using namespace std;

int main(int argc, char **argv) {

    vector<string> vec;
    vec.push_back("beijing");
    vec.push_back("shanghai");
    vec.push_back("guangzhou");
    vec.push_back("shenzhen");

    for (vector<string>::iterator iter = vec.begin(); iter != vec.end();
            ++iter) {
        cout << *iter << endl;
    }

    for (vector<string>::reverse_iterator iter = vec.rbegin();
            iter != vec.rend(); ++iter) {
        cout << *iter << endl;
    }

    return 0;
}

/*
output:
beijing
shanghai
guangzhou
shenzhen
shenzhen
guangzhou
shanghai
beijing
*/

4. 顺序容器的插入操作

1. vector没有push_front(vectoe内部实现是数组)。list有push_front。

2. 针对List

a)可以使用insert(p, t)   在指定位置元素之前添加元素,其中p是迭代器,t时元素的值

b)Insert(p, n, t)          在迭代器p指向的位置之前插入n个元素,初始值为t

c)Insert(p, b, e)          在迭代器p指向的位置之前插入迭代器b和迭代器e之间的元素

d)可是使用push_front   头插

5. 顺序容器的删除操作

1. 删第一个或最后一个元素

类似与插入元素,pop_front或者pop_back可以删除第一个或者最后一个元素

2. 删除容器的一个元素

与insert对应,删除采用的是erase操作,该操作有两个版本:删除由一个迭代器指向的元素,或者删除由一对迭代器标记的一段元素。删除元素需要接收返回值,防止迭代器失效,最好使用while循环。

6. 容器大小的操作

vector与容量有关的函数:

a)size         元素数目,类似于会议室中人的数目

b)resize      调整元素数目,类似于调整函数

c)capacity   可容纳数目,类似于会议室中的座位数量

d)reserve   告诉vector容器应该预留多少个元素的存储空间

7. 迭代器的失效问题

任何insert或者push操作都可能导致迭代器失效。当编写循环将元素插入到vector或list容器中时,程序必须确保迭代器在每次循环后都得到更新。

vector迭代器持续有效,除非:

1. 使用者在较小的索引位置插入或者删除元素。

2. 由于容量的变化引起的内存重新分配。

list迭代器持续有效,除非:

将it指向的元素删除,那么it则失效(list内部实现是链表,it指向的元素删了就是没有了,再用it访问直接段错误。vector也有可能失效,只不过后面的元素会往前移,再用it访问可能不会产生段错误)。

删除元素需要接收返回值,最好使用while循环。例如删除下例删除偶数:

vector<int>::iterator it = vec.begin();
while(it != vec.end())
{
    if(*it % 2 == 0)
        //vec.erase(it);
        it = vec.erase(it);
    else
        ++it;
}

8. vector的for_each方法

遍历vector方法:

1. 下标

2. 迭代器

3. for_each

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

void print(int i)
{
    cout << i << endl;
}

int main(int argc, const char *argv[])
{
    vector<int> vec;
    vec.push_back(12);
    vec.push_back(23);
    vec.push_back(45);
    vec.push_back(56);
    vec.push_back(221);
    vec.push_back(35);
    vec.push_back(129);

    for_each(vec.begin(), vec.end(), print);

    return 0;
}

/*
output:
12
23
45
56
221
35
129
*/

9. vector和list的区别

a) vector采用数组实现,list采用链表。

b) vector支持随机访问,list不提供下标。

c) 大量增加删除的操作适合使用list。

10. string之截取子串substr

例子如下:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main(int argc, const char *argv[])
{
    string s = "helloworldfoo";


    string s2 = s.substr(1, 4); //ello
    cout << s2 << endl;

    return 0;
}

注意,迭代器一般是取基地址到尾后地址的一段范围。而下标操作,通常是基地址+长度。

11. stack

#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;

int main(int argc, const char *argv[])
{
    stack<int> s;

    s.push(10);
    s.push(22);
    s.push(23);
    s.push(1);
    s.push(8);
    s.push(99);
    s.push(14);

    while(!s.empty())
    {
        cout << s.top() << endl;
        s.pop();
    }

    return 0;
}

/* 
输出如下:
14
99
8
1
23
22
10
*/

12. queue

#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;

int main(int argc, const char *argv[])
{
    queue<int> q;

    q.push(12);
    q.push(23);
    q.push(4);
    q.push(5);
    q.push(7);


    while(!q.empty())
    {
    
        cout << q.front() << endl;
        q.pop();
    }


    return 0;
}

/*
输出:

12
23
4
5
7

*/

13. 优先级队列(用堆实现)

例1:

#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;

int main(int argc, const char *argv[])
{
    priority_queue<int> q;
    q.push(12);
    q.push(99);
    q.push(23);
    q.push(123);

    while(!q.empty())
    {
        cout << q.top() << endl;        
        q.pop();
    }

    return 0;
}

/*

output:
123
99
23
12

*/

例2:

#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;


int main(int argc, const char *argv[])
{
    priority_queue<int, vector<int>, greater<int> > q;
    
    q.push(12);
    q.push(99);
    q.push(23);
    q.push(123);

    while(!q.empty())
    {
        cout << q.top() << endl;        
        q.pop();
    }

    return 0;
}

/*
output:
12
23
99
123
*/
#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;


int main(int argc, const char *argv[])
{
    priority_queue<int, vector<int>, less<int> > q;
    q.push(12);
    q.push(99);
    q.push(23);
    q.push(123);

    while(!q.empty())
    {
        cout << q.top() << endl;        
        q.pop();
    }

    return 0;
}

/*
output:
123
99
23
12
*/

例3:传入函数对象

#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;


struct Score
{
    int score_;
    string name_;

    Score(int score, const string name)
        :score_(score), name_(name)
    { }
};


class Cmp
{
    public:
        bool operator() (const Score &s1, const Score &s2)
        {
            return s1.score_ < s2.score_;
        }
};

// Cmp p;
// p(s1, s2)


int main(int argc, const char *argv[])
{
    priority_queue<Score, vector<Score>, Cmp> q;
    
    q.push(Score(67, "zhangsan"));
    q.push(Score(88, "lisi"));
    q.push(Score(34, "wangwu"));
    q.push(Score(99, "foo"));
    q.push(Score(0, "bar"));

    while(!q.empty())
    {
        cout << q.top().name_ << " : " << q.top().score_ << endl;
        q.pop();
    }

    return 0;
}

/*
output:
foo : 99
lisi : 88
zhangsan : 67
wangwu : 34
bar : 0
*/

STL之关联容器

1. Pair类型

Pair是一种简单的关联类型。注意:pair不是容器,而是代表一个key-value键值对。

示例1:

#include <iostream>
#include <string>
#include <utility>
using namespace std;

int main(int argc, const char *argv[])
{
    pair<int, int> p1;
    p1.first = 10;
    p1.second = 12;
    
    pair<int, string> p2;
    p2.first = 12;
    p2.second = "hello";

    pair<string, string> p3;
}
示例2:
#include <iostream>
#include <string>
#include <vector>
using namespace std;

//生成pair对象的三种方法
int main(int argc, const char *argv[])
{
    vector<pair<string, int> > vec;

    pair<string, int> word;
    word.first = "hello";
    word.second = 12;
    vec.push_back(word);

    pair<string, int> word2("world", 12);
    vec.push_back(word2);
    
    vec.push_back(make_pair("foo", 3));
}

示例3:vector中装入pair,实现统计词频:

#include <iostream>
#include <string>
#include <vector>
#include <utility>
using namespace std;

typedef vector<pair<string, int> > Dict;

void makeDict(Dict &dict, const vector<string> &words);
void addWordToDict(Dict &dict, const string &word);

int main(int argc, const char *argv[])
{
    vector<string> words;
    string word;

    while(cin >> word)
        words.push_back(word);

    Dict dict;
    makeDict(dict, words);
    
    for(const pair<string, int> &p : dict)
    {
        cout << p.first << " : " << p.second << endl;
    }

    return 0;
}

void makeDict(Dict &dict, const vector<string> &words)
{
    dict.clear();
    for(vector<string>::const_iterator it = words.begin();
        it != words.end();
        ++it)
    {
        addWordToDict(dict, *it);
    }
}

void addWordToDict(Dict &dict, const string &word)
{
    Dict::iterator it;
    for(it = dict.begin();
            it != dict.end();
            ++it)
    {
        if(it->first == word)
        {
            ++it->second;
            break;
        }
    }
    
    if(it == dict.end())
        dict.push_back(make_pair(word, 1));
}

2. map

map可以看做是一种存储pair类型的容器,内部采用二叉树实现(编译器实现为红黑树)。

1. pair不是容器,而是代表一个key-value键值对;而map则是一个容器,里面存储了pair对象,只是存储的方式与vector<pair>这种连续存储,有所不同,map采用的是二叉排序树存储pair,一般而言是红黑树,因此内部是有序的

2. 当map使用下标访问时,如果key不存在,那么会在map中添加一个新的pair,value为默认值

示例1:

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main(int argc, const char *argv[])
{
    map<string, int> m;

    m["beijing"] = 2000;
    m["shenzhen"] = 1000;
    m["shanghai"] = 1500;
    m["hongkong"] = 500;
    m["hangzhou"] = 880;

    for(map<string, int>::const_iterator it = m.begin();
        it != m.end();
        ++it)
    {
        //*it pair
        cout << it->first << " : " << it->second << endl;
    }

    return 0;
}

/*
output:
beijing : 2000
hangzhou : 880
hongkong : 500
shanghai : 1500
shenzhen : 1000
*/
// 由于key是string类型,因此输出按字典序。

示例2:

#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;

int main(int argc, const char *argv[])
{
    map<string, int> m;

    m["beijing"] = 40;
    m["shenzhen"] = 30;
    m["guangzhou"] = 37;

    cout << m.size() << endl; //3
    cout << m["shanghai"] << endl;
    cout << m.size() << endl;

    return 0;
}

/*
output:
3
0
4
*/

3. map 的 key 必须具有小于操作符 operator <

以下为错误代码:

#include <iostream>
#include <map>
using namespace std;

struct Test
{
    int a;
};

int main(int argc, const char *argv[])
{
    map<Test, int> m;  
    Test t;
    m[t] = 1;
}

/* 编译报错,因为Test对象在次数为key-value对中的key,但其并没有定义 operator< 运算符,红黑树无法进行排序 */

4. map查找元素的效率是lgn,因为树的高度不超过O(lgN)

示例:使用map,实现统计词频,如下:

#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;


int main(int argc, const char *argv[])
{
    map<string, int> words;

    string word;
    
    /* 如果key(word)存在,则value++; 如果word不存在,此处会在map(words)中添加一个新的pair,value为默认值(此处为0),然后value++ */
    while(cin >> word)
        words[word]++;

    for(const pair<string, int> &p : words)
        cout << p.first << " : " << p.second << endl;

    return 0;
}

5. 在map中添加元素

刚才我们看到,采用下标的方式,可以给map添加元素,但更好的做法时采用insert插入一个pair对象。

这里值得注意的是insert的返回值,其返回了一个pair对象,第一个元素是指向该key所在的那个pair对象的的迭代器,第二个则表示插入是否成功。使用insert插入map元素时,如果失败,则不会更新原来的值。看下面例子:

#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;

int main(int argc, const char *argv[])
{
    map<string, int> m;

    m.insert(make_pair("hello", 1));
    m.insert(make_pair("foo", 1));
    m.insert(make_pair("bar", 1));
    m.insert(make_pair("hello", 1));

    cout << "size : " << m.size() << endl;

    /* insert的返回值:指向key所在pair的迭代器,以及表示插入是否成功的布尔值 */
    pair<map<string, int>::iterator, bool> ret;

        // 之前没有这个key,插入成功
    ret = m.insert(make_pair("fwfgwfg", 23));
    cout << "ret = " << ret.second << endl;
    
    // 之前已有的key,插入失败。插入失败的话,不会更新原来的value值
    ret = m.insert(make_pair("hello", 25425));
    cout << "ret = " << ret.second << endl;
    cout << ret.first->second << endl;

    return 0;
}

/*
output:
size : 3 
ret = 1       
ret = 0
1
*/

下面的程序仍然是实现统计词频:

#include <iostream>
#include <string>
#include <map>
using namespace std;


int main(int argc, const char *argv[])
{
    map<string, int> words;

    string word;
    pair<map<string, int>::iterator, bool> ret;
    while(cin >> word)
    {
        ret = words.insert(make_pair(word, 1));
        if(ret.second == false) //word已经存在
            ++ret.first->second;
    }

    for(const pair<string, int> &p : words)
        cout << p.first << " : " << p.second << endl;

    return 0;
}

综上,在本章中我们已经使用三种方式,去统计词频了,分别是:vector中使用pair, map的下标访问方式以及map的insert方式。

6. 在map中查找元素

刚才看到可以利用下标获取value的值,但是这样存在一个弊端,如果下标访问的是不存在的元素,那么会自动给map增加一个键值对,这显然不是我们所预期的。

我们可以采用 count 和 find 来解决问题,其中 count 仅仅能得出该元素是否存在,而 find 能够返回该元素的迭代器。

示例1:

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main(int argc, const char *argv[])
{
    map<string, string> m;
    m["beijing"] = "bad";
    m["shanghai"] = "just soso";
    m["shenzhen"] = "well";
    m["hangzhou"] = "good";


    cout << m.count("hangzhou") << endl;
    cout << m.count("HK") << endl;

    return 0;
}

/*
output:
1
0
*/

示例2:

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main(int argc, const char *argv[])
{
    map<string, string> m;
    m["beijing"] = "bad";
    m["shanghai"] = "just soso";
    m["shenzhen"] = "well";
    m["hangzhou"] = "good";

        // find的返回值
    map<string, string>::iterator it = m.find("HK");
    
    if(it == m.end())
        cout << "不存在" << endl;
    else
        cout << it->first << " : " << it->second << endl;
    
    return 0;
}

/*
output:
不存在
*/

3. set

Set类似于数学上的集合,仅仅表示某个元素在集合中是否存在,而不必关心它的具体位置。同样,set中的元素互异,也就是无法两次插入相同的元素。set 底层采用红黑树实现,按照值进行排序,map则按照key进行排序。使用方式和map类似,但是简单很多。

示例1:

#include <iostream>
#include <string>
#include <set>
using namespace std;

int main(int argc, const char *argv[])
{
    set<int> s;

        // set不会插入重复的元素
    for(int i = 0; i < 20 ; ++i)
    {
        s.insert(i);
        s.insert(i);
    }

    cout << "size : " << s.size() << endl;

    return 0;
}

/*
output:
size : 20
*/

示例2:

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <stdlib.h>
using namespace std;


int main(int argc, const char *argv[])
{
    srand(10000);

    set<int> s;
    
    for(int i = 0; i < 40; ++i)
    {
        s.insert(rand() % 100);
    }
    
    // 注意是有序的
    for(int i : s)
    {
        cout << i << " ";
    }
    cout << endl;

    return 0;
}

/*
output:
4 5 8 12 13 15 16 20 21 22 24 25 27 32 38 39 42 43 46 50 54 57 59 63 66 72 78 82 85 93 94 96 98
*/

4. 小结

map 中key 的值是唯一的,set 中的元素都是唯一的。

1. map和set比较:

a) 二者均使用红黑树实现

b) key需要支持<操作

c) map侧重于key-value的快速查找

d) set侧重于查看元素是否存在

2. 用户无法对map和set中的元素进行排序,不然会干扰map和set本身的实现。

最后注意:map 的 key 值是不可更改的。而 set 中的 value 虽然可以更改,但不建议这样做,真有需求,直接删除即可。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。