天天看点

[C++]string、vector和数组stringvector迭代器数组

string

需要注意,为了跟C语言兼容,字符串字面值与string是不同的类型。要使用string,先包含如下的内容:

#include <string>
using std::string;
           

初始化string的方式:

[C++]string、vector和数组stringvector迭代器数组

如果不赋值,就得到空字符。如果使用“=”就是拷贝初始化,否则就是直接初始化。像S4那样的操作,只能使用直接初始化;而S2和S3则直接初始化和拷贝初始化都可以使用。

string操作:

[C++]string、vector和数组stringvector迭代器数组

使用cin输入到string对象,会自动忽略开头的空白,值到遇到下一个空白为止,其间的内容才会被放到string对象中,也即是string对象中不会有空白字符。如果想要保留输入的空白字符,可以使用getline()函数,它会将换行符之前的所有内容都放到string对象,注意不包含换行符本身。当然,两者遇到EOF(按Ctrl+z再按Enter)都会退出。

size()返回的类型是string::size_type,它是无符号类型,且足够放下任何string对象的大小。注意size()的返回没有包含字符串最后的空字符(其实是没有空字符),所以"a"这个字符串的size()返回值是1:

int main()
{
    string st("a");
    cout << st.size() << endl;
    return 0;
}
           

这里打印的结果是1。

string对象中的字符可以单独修改。可以通过for来遍历字符:

int main()
{
    string s("Hello World");
    for (auto a : s) {
        cout << a << endl;
    }

    return 0;
}
           

也可以修改字符:

int main()
{
    string s("Hello World");
    for (auto &a : s) {
        a = toupper(a);
    }
    cout << s << endl;

    return 0;
}
           

这里将string对象中的字符都转换成了大写。需要注意跟上一个例子的差异,这里for循环中使用了引用而不是新的变量,否则修改没有意义。

string对象也可以通过[]用下标访问:

int main()
{
    string s("Hello World");
    for (auto i = 0; i <= s.size(); i++) {
        s[i] = toupper(s[i]);
    }
    cout << s << endl;

    return 0;
}
           

像toupper这样的函数,在C语言也有相同功能和名称的函数,在C++中也可以使用,如下:

[C++]string、vector和数组stringvector迭代器数组

还有对C风格字符串的操作(位于<cstring>):

[C++]string、vector和数组stringvector迭代器数组

上述函数不能作用于string对象。注意C风格字符串其实是数组,且以空字符结束。C++中最好不要使用它们。

从内置类型转到string可以使用to_string()函数:

int main()
{
    int i = 42;
    // converts the int i to its character representation
    string s = to_string(i);

    return 0;
}
           

vector

vector表示对象的集合,包含的对象类型需要是相同的,但是不能包括引用(因为引用不是对象)。vector是容器的一种,后面还会介绍其它容器。要使用vector,先包含如下的内容:

#include <vector>
using std::vector;
           

vector的初始化:

[C++]string、vector和数组stringvector迭代器数组

如果未初始化,得到的是空的vector。=是拷贝初始化。如果提供初始元素值,则使用“{}”来初始化。注意跟使用“()”的差别,它是指n个val来初始化vector,val可以是默认值,有对象T本身的类型决定默认值是什么。“{}”可以做“()”能做的初始化,而反过来则不行。

vector的操作:

[C++]string、vector和数组stringvector迭代器数组

size()返回的值不是vector::size_type,而是vector<T>::size_type,T是具体的类型,比如int。通过下标可以索引vector,但是不能添加元素,添加元素使用push_back()。

vector的示例:

int main()
{
    // list initialization, articles has 3 elements
    vector<string> articles = {"a", "an", "the"};

    vector<string> svec; // default initialization has no elements
    vector<int> ivec;  // ivec holds objects of type int
    vector<Sales_item> Sales_vec; // holds Sales_items

    vector<vector<string>> file;  // vector whose elements are vectors
    vector<vector<int>> vecOfvec; // each element is itself a vector

    // all five vectors have size 0
    cout << svec.size() << " " << ivec.size() << " "
         << Sales_vec.size() << " "
         << file.size() << " " << vecOfvec.size() << endl;

    vector<int> ivec2(10);     // ten elements, each initialized to 0
    vector<int> ivec3(10, -1); // ten int elements, each initialized to -1
    vector<string> svec2(10);  // ten elements, each an empty string
    vector<string> svec3(10, "hi!"); // ten strings; each element is "hi!"
    cout << ivec2.size() << " " << ivec3.size() << " "
        << svec2.size() << " " << svec3.size() << endl;

    // 10 is not a string, so cannot be list initialization
    vector<string> v1(10); // construct v1 with ten value-initialized elements
    vector<string> v2{10}; // ten elements value-initialized elements
    vector<string> v3(10, "hi");  // ten elements with value "hi"
    // again list initialization is not viable, so ordinary construction
    vector<string> v4{10, "hi"};  // ten elements with values "hi"

    // all four vectors have size ten
    cout << v1.size() << " " << v2.size()
         << " " << v3.size() << " " << v4.size() << endl;

    vector<string> vs1{"hi"}; // list initialization: vs1 has 1 element
    vector<string> vs2{10};   // ten default-initialized elements
    vector<string> vs3{10, "hi"}; // has ten elements with value "hi"
    cout << vs1.size() << " " << vs2.size() << " " << vs3.size() << endl;

    vector<int> v5(10, 1);  // ten elements with value 1
    vector<int> v6{10, 1};  // two elements with values 10 and 1
    cout << v5.size() << " " << v6.size() << endl;

    // intention is clearer
    vector<int> alt_v3 = {10};    // one element with value 10
    vector<int> alt_v4 = {10, 1}; // two elements with values 10 and 1
    cout << alt_v3.size() << " " << alt_v4.size() << endl;
    for (auto a : alt_v4) {
        cout << a << endl;
    }
    for (auto i = 0; i < alt_v4.size(); i++) {
        cout << alt_v4[i] << endl;
    }

    return 0;
}
           

这里需要主要v2和v4,它们都使用了“{}”,但是因为成员是string,所以10不会被解释成string,而是解释成了个数,所以v2是10个空字符串的vector,跟v1一致;v4是包含10个"hi"的vector,跟v3一致。

迭代器

前面使用了for、下标等方式访问string和vector,还可以使用迭代器来遍历string和vector。迭代器跟指针类似,就是通过从头到尾访问元素的方式完成遍历。string和vector,以及后面会介绍的容器,都支持迭代器,它们拥有begin()和end()两个成员用于返回迭代器,前者指向开头,后者指向尾后,即最后一个元素的下一个(不存在的)元素。如果迭代器类型对象本身为空,则begi和end返回的都是尾后。

标准容器迭代器的操作:

[C++]string、vector和数组stringvector迭代器数组

而string和vector支持的更多:

[C++]string、vector和数组stringvector迭代器数组

n是一个整型数值,但是两个迭代器相减,得到的是类型为difference_type的值,它是带符号整型数。

关于迭代器的示例:

int main()
{
    string s("Hello World");
    for (auto iter = s.begin(); iter != s.end(); iter++) {
        *iter = toupper(*iter);
    }
    cout << s << endl;

    return 0;
}
           

对于迭代器的类型,一般直接就写auto了,让编译器来决定。不过实际上我们还是可以确定其类型的,比如上面的示例中,iter的类型是string::iterator。begin()和end()返回的迭代器可用于修改值,因为*iter是引用,但是如果想要返回的迭代器只用于读操作,则可以使用其const版本:cbegin()和cend(),它返回的是const_iterator。

vector可以通过push_back()插入元素,此时原本的迭代器就失效了,这需要注意。

数组

数组跟C语言中的没有太大区别。C++中增加了迭代器给数组用,使用begin()和end()函数(它们属于std),并返回成员的指针,也就是说指针成了迭代器,可以使用迭代器的运算操作。两个指针相减得到的是类型为ptrdiff_t的值。在C++中编译器在使用数组的时候会把它转换成指针。

关于数组遍历的示例:

int main()
{
    int a[] = {1, 2, 3, 4};
    int *pb = std::begin(a);
    int *pe = std::end(a);
    for (; pb != pe; pb++) {
        cout << *pb << endl;
    }

    return 0;
}
           

返回的指针也是一个指向数组开头,一个指向数组尾后。

继续阅读