zoukankan      html  css  js  c++  java
  • C++ 标准库类型-String,Vector and Bitset

    《C++ Primer 4th》读书摘要

    最重要的标准库类型是 string 和 vector,它们分别定义了大小可变的字符串和集合。这些标准库类型是语言组成部分中更基本的那些数据类型(如数组和指针)的抽象。另一种标准库类型 bitset,提供了一种抽象方法来操作位的集合。

     

    标准库string类型

    有一种情况下,必须总是使用完全限定的标准库名字:在头文件中。理由是头文件的内容会被预处理器复制到程序中。

     

    #include <string>

    using std::string;

    几种初始化string 对象的方式,因为历史原因以及为了与 C 语言兼容,字符串字面值与标准库 string 类型不是同一种类型。这一点很容易引起混乱,编程时一定要注意区分字符串字面值和 string 数据类型的使用,这很重要。

    string s1;

    默认构造函数 s1 为空串

    string s2(s1);

    将 s2 初始化为 s1 的一个副本

    string   s3("value");

    将 s3 初始化为一个字符串字面值副本

    string s4(n, 'c');

    将 s4 初始化为字符 'c' 的 n   个副本

     

    从标准输入读取 string 并将读入的串存储在 s 中。string 类型的输入操作符:

    • 读取并忽略开头所有的空白字符(如空格,换行符,制表符)。

    • 读取字符直至再次遇到空白字符,读取终止。

     

    和输入操作符不一样的是,getline 并不忽略行开头的换行符。只要 getline 遇到换行符,即便它是输入的第一个字符,getline 也将停止读入并返回。如果第一个字符就是换行符,则 string 参数将被置为空 string。由于 getline 函数返回时丢弃换行符,换行符将不会存储在 string 对象中。

     

    string 对象的操作

    s.empty()

    如果 s 为空串,则返回   true,否则返回 false。

    s.size()

    返回 s 中字符的个数

    s[n]

    返回 s 中位置为 n 的字符,位置从 0   开始计数

    s1 + s2

    把 s1 和s2   连接成一个新字符串,返回新生成的字符串

    s1 = s2

    把 s1 内容替换为 s2 的副本

    v1 == v2

    比较 v1 与 v2 的内容,相等则返回   true,否则返

    回 false

    !=, <, <=,   >, and >=

    保持这些操作符惯有的含义

     

    string 类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能与机器无关(machine-independent)。任何存储 string 的 size 操作结果的变量必须为 string::size_type 类型。特别重要的是,不要把 size 的返回值赋给一个 int 变量。

    大多数 string 库类型的赋值等操作的实现都会遇到一些效率上的问题,但值得注意的是,从概念上讲,赋值操作确实需要做一些工作。它必须先把 st1 占用的相关内存释放掉,然后再分配给 st2 足够存放 st2 副本的内存空间,最后把 st2 中的所有字符复制到新分配的内存空间。

    当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作数必须至少有一个是 string 类型的。

    string 类型通过下标操作符([ ])来访问 string 对象中的单个字符。下标操作符需要取一个 size_type 类型的值,来标明要访问字符的位置。

     

    对 string 对象中的单个字符进行处理。下表列出了各种字符操作函数,适用于 string 对象的字符(或其他任何 char 值)。这些函数都在 cctype 头文件中定义。可打印的字符是指那些可以表示的字符,空白字符则是空格、制表符、垂直制表符、回车符、换行符和进纸符中的任意一种;标点符号则是除了数字、字母或(可打印的)空白字符(如空格)以外的其他可打印字符。

    isalnum(c)

    如果 c 是字母或数字,则为 True。

    isalpha(c)

    如果 c 是字母,则为 true。

    iscntrl(c)

    如果 c 是控制字符,则为 true

    isdigit(c)

    如果 c 是数字,则为 true。

    isgraph(c)

    如果 c 不是空格,但可打印,则为   true。

    islower(c)

    如果 c 是小写字母,则为 true。

    isprint(c)

    如果 c 是可打印的字符,则为 true。

    ispunct(c)

    如果 c 是标点符号,则 true。

    isspace(c)

    如果 c 是空白字符,则为 true。

    isupper(c)

    如果 c 是大写字母,则 true。

    isxdigit(c)

    如果是 c 十六进制数,则为 true。

    tolower(c)

    如果 c   大写字母,返回其小写字母形式,否则直接返回 c。

    toupper(c)

    如果 c   是小写字母,则返回其大写字母形式,否则直接返回 c。

     

    C 标准库头文件命名形式为 name.h 而 C++ 版本则命名为 cname ,少了后缀,.h 而在头文件名前加了 c 表示这个头文件源自 C 标准库

     

     

    标准库 vector 类型

     

    vector 是同一种类型的对象的集合,vector称为容器。vector 不是一种数据类型,而只是一个类模板,可用来定义任

     

    意多种数据类型。vector 类型的每一种都指定了其保存元素的类型。因此,vector<int> 和 vector<string> 都是数据类型。

     

    vector<T> v1;

    vector 保存类型为 T   对象。默认构造函数 v1 为空。

    vector<T>   v2(v1);

    v2 是 v1 的一个副本。

    vector<T> v3(n,   i);

    v3 包含 n 个值为 i 的元素。

    vector<T>   v4(n);

    v4 含有值初始化的元素的 n 个副本

     

    vector 对象(以及其他标准库容器对象)的重要属性就在于可以在运行时高效地添加元素。虽然可以对给定元素个数的 vector 对象预先分配内存,但更有效的方法是先初始化一个空 vector 对象,然后再动态地增加元素。

     

    如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化(value initializationd)。这个由库生成的初始值将用来初始化容器中的每个元素,具体值为何,取决于存储在 vector 中元素的数据类型。元素类型可能是没有定义任何构造函数的类类型。这种情况下,标准库仍产生一个带初始值的对象,这个对象的每个成员进行了值初始化。

     

     

     

    vector 操作

     

    v.empty()

    如果 v 为空,则返回 true,否则返回   false。

    v.size()

    返回 v 中元素的个数。

    使用 size_type   类型时,必须指出该类型是在哪里定义的。

    vector 类型总是包括总是包括   vector 的元素类型:vector<int>::size_type

    v.empty()

    如果 v 为空,则返回 true,否则返回   false。

    v.push_back(t)

    在 v 的末尾增加一个值为 t 的元素。

    v[n]

    返回 v 中位置为 n 的元素。

    v1 = v2

    把 v1 的元素替换为 v2   中元素的副本。

    v1 == v2

    如果 v1 与 v2 相等,则返回   true。

    !=, <, <=,>,   and >=

    保持这些操作符惯有的含义。

     

     

     

    C++ 程序员习惯于优先选用 != 而不是 < 来编写循环判断条件,选用或不用某种操作符并没有特别的取舍理由。学习完泛型编程后,你将会明白这种习惯的合理性。


     reference:http://www.cnblogs.com/qlee/archive/2011/05/16/2048026.html

     

         vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!

    原因如下:       reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。

          resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。

          两个函数的参数形式也有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。下面是这两个函数使用例子:

    #include <iostream>
    #include <stdio.h>
    #include <vector>
    
    using namespace std;
    
    int vecTest1()
    {
        vector<int> myVect;
        myVect.reserve(100);
        for(int i=0;i<100;i++)
        {
            myVect.push_back(i);
        }
        myVect.resize(102);
        myVect[100]=1;
        myVect[101]=2;
    
        for(vector<int>::iterator iter=myVect.begin(); iter!=myVect.end();++iter)
            cout<<*iter<<endl;
    
        return 0;
    }
    
    int vecTest2()
    {
        vector<int> myVect;
        myVect.push_back(1);
        myVect.push_back(2);
        myVect.push_back(3);
        myVect.push_back(4);
        myVect.reserve(100);
        cout<<myVect.size()<<endl;
        cout<<myVect.capacity()<<endl;
        for(vector<int>::size_type i=0;i<104;++i)
        {
            cout<<myVect[i]<<endl;
        }
    
        return 0;
    }
    
    int vecTest3()
    {
        vector<int> myVect;
        myVect.push_back(1);
        myVect.push_back(2);
        myVect.push_back(3);
        myVect.push_back(4);
        myVect.resize(100);
        cout<<myVect.size()<<endl;
        cout<<myVect.capacity()<<endl;
        for(vector<int>::size_type i=0;i<100;++i)
        {
            cout<<myVect[i]<<endl;
        }
    
        return 0;
    }
    
    int vecTest4()
    {
        vector<int> myVect;
        myVect.resize(100);
        myVect.push_back(1);
        myVect.push_back(2);
        myVect.push_back(3);
        myVect.push_back(4);
        cout<<myVect.size()<<endl;
        cout<<myVect.capacity()<<endl;
        for(vector<int>::size_type i=0;i<104;++i)
        {
            cout<<myVect[i]<<endl;
        }
    
        return 0;
    }

     Reference:http://www.cnblogs.com/summerRQ/articles/2407974.html

    vector内存释放

    由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

    如果需要空间动态缩小,可以考虑使用deque。如果非vector不可,可以用swap()来帮助你释放内存。具体方法如下:

    int freeVec()
    {
        vector<int> myVect;
        myVect.push_back(1);
        myVect.push_back(2);
        myVect.push_back(3);
        myVect.push_back(4);
        vector<int>().swap(myVect);
    
        return 0;
    }

    swap()是交换函数,使vector离开其自身的作用域,从而强制释放vector所占的内存空间,总而言之,释放vector内存最简单的方法是vector<int>.swap(nums)。当时如果nums是一个类的成员,不能把vector<int>.swap(nums)写进类的析构函数中,否则会导致double free or corruption (fasttop)的错误,原因可能是重复释放内存。标准解决方法如下:

    template < class T >
    void ClearVector( vector< T >& vt ) 
    {
        vector< T > vtTemp; 
        veTemp.swap( vt );
    }

    利用vector释放指针

    如果vector中存放的是指针,那么当vector销毁时,这些指针指向的对象不会被销毁,那么内存就不会被释放。如下面这种情况,vector中的元素时由new操作动态申请出来的对象指针:

    #include <vector> 
    using namespace std; 
    
    vector<void *> v;

    每次new之后调用v.push_back()该指针,在程序退出或者根据需要,用以下代码进行内存的释放: 

    for (vector<void *>::iterator it = v.begin(); it != v.end(); it ++) 
        if (NULL != *it) 
        {
            delete *it; 
            *it = NULL;
        }
    v.clear();

    C++中STL的vector容器的析构函数不用自己调用,系统会进行析构,但是vector内元素的清空需要手动进行。

    1. 非指针的数据类型,比如 int、string、char ,还包括自定义的数据结构、自定义的类 等等只需要手动调用vector的clesr函数就可以了,空间的释放和析构系统都会自动进行。

    2.  指针类型的数据,这种情况需要手动进行释放。也就是说new 产生的内存需要手动使用free进行释放。


    迭代器是一种检查容器内元素并遍历元素的数据类型。标准库为每一种标准容器(包括 vector)定义了一种迭代器类型,而只有少数的容器支持下标操作。

     

    每种容器类型都定义了自己的迭代器类型,如 vector:

     

    vector<int>::iterator iter;

     

    每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。由 end 操作返回的迭代器并不指向 vector 中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完 vector 中所有元素。

     

    迭代器类型可使用解引用操作符(dereference operator)(*操作符)来访问迭代器所指向的元素:

     

    *iter = 0;

     

    迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。因此,如果 iter 指向第一个元素,则 ++iter 指向第二个元素。

     

    由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。

     

    用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。

     

    每种容器类型还定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素,但不能改变其值。

     

    任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了。

     

    iter + n

     

    iter - n

     

    可以对迭代器对象加上或减去一个整形值。这样做将产生一个新的迭代器,其位置在 iter 所指元素之前(加)或之后(减) n 个元素的位置。

     

    iter1 - iter2

     

    该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值,这里的 difference_type 是 signed 类型,因为减法运算可能产生负数的结果。

     

     

     

    标准库 bitset

     

    bitset 类是一种类模板;而与 vector 不一样的是 bitset 类型对象的区别仅在其长度而不在其类型。在定义 bitset 时,要明确 bitset 含有多少位,须在尖括号内给出它的长度值:

     

    初始化bitset 对象的方法:

     

    bitset<n> b;

    b 有 n 位,每位都 0

    bitset<n> b(u);

    b 是 unsigned long 型 u   的一个副本

    bitset<n> b(s);

    b 是 string 对象 s   中含有的位串的副本

    bitset<n> b(s,   pos, n);

    b 是 s 中从位置 pos   开始的&nbps;n 个位的副本。

     

    bitset 操作

     

    b.any()

    b 中是否存在置为 1 的二进制位?

    b.none()

    b 中不存在置为 1 的二进制位吗?

    b.count()

    b 中置为 1 的二进制位的个数

    b.size()

    b 中二进制位的个数

    b[pos]

    访问 b 中在 pos 处二进制位

    b.test(pos)

    b 中在 pos 处的二进制位置为 1   么?

    b.set()

    把 b 中所有二进制位都置为 1

    b.set(pos)

    把 b 中在 pos 处的二进制位置为 1

    b.any()

    b 中是否存在置为 1 的二进制位?

    b.reset()

    把 b 中所有二进制位都置为 0

    b.reset(pos)

    把 b 中在 pos 处的二进制位置为 0

    b.flip()

    把 b 中所有二进制位逐位取反

    b.flip(pos)

    把 b 中在 pos 处的二进制位取反

    b.to_ulong()

    用 b 中同样的二进制位返回一个   unsigned long 值

    os << b

    把 b 中的位集输出到 os 流

     

    count 操作的返回类型是标准库中命名为 size_t 类型。size_t 类型定义在 cstddef 头文件中,该文件是 C 标准库的头文件 stddef.h 的 C++ 版本。

     

  • 相关阅读:
    模块化工具require 学习笔记
    学习Jade模板引擎
    通过border来实现各种三角符号
    使用vscode 编译 sass
    Javascript 运行机制
    Vue调试工具 vue-devtools
    MVVM框架
    通信类
    面向对象
    原型和原型链
  • 原文地址:https://www.cnblogs.com/1zhk/p/4970313.html
Copyright © 2011-2022 走看看