C++ 学习笔记之(3)-字符串、向量和数组

C++ 学习笔记之(3)-字符串、向量和数组

命名空间的using声明

C++的命名空间的目的是为了防止名字冲突。为了使用命名空间的成员,可以使用using声明。有了using声明就无需专门的前缀,using声明的形式如下

using namespace::name;
  • 每个名字都需要独立的using声明。
  • 头文件不应该包含using声明,这是因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件都会有此声明。可能会导致名字冲突

注意:命名空间需要深入学习

标准库类型 string

标准库类型string表示可变长的字符序列,string类型定义在命名空间std中。

定义和初始化 string 对象

  • 直接初始化:不使用等号的初始化
  • 拷贝初始化:使用等号=初始化一个变量,编译器把等号右侧的初始值拷贝到新创建的对象中去。

注意:直接初始化和拷贝初始化需要深入学习

string_initialize_methods

string 对象上的操作

类除了定义初始化其对象的方式外,还要定义对象所能执行的操作。
string_operation_methods

  • 如果表达式中存在size()函数,就不要再使用int类型了,因为size函数返回的是string类的配套类型string::size_type, 一个无符号类型的值,混用intunsigned会出现问题。

  • 标准库允许把字符字面值和字符串字面值转换成string对象,即可以将其混用在一条语句中使用,但必须确保每个加法运算符两侧的运算对象至少有一个是string

    string s1 = "hello", s2 = "world";
    string s3 = s1 + ", " + s2 + '\n';
  • 由于历史原因,且为了与C兼容,C++中的字符串字面值并不是标准库类型string的对象。切记,字符串字面值与string是不同的类型。

处理 string 对象中的字符

我们经常需要单独处理string对象中的字符,cctype头文件定义了一组标准库函数处理这部分工作

cctype_functions

  • C语言头文件形如name.h, C++将其命名为cname, C++程序应该使用后者,因为标准库的名字总能在命名空间std中找到,若使用.h头文件,则无法找到。

  • C++11使用范围for语句可以对string对象的每个字符操作,如果要改变string队形中该字符的值,必须把循环变量定义成引用类型。

    for(auto &c: str)
    c = toupper(c);  // c 是一个引用,因此赋值语句将改变 s 中字符的值

标准库类型 vector

标准库类型vector表示对象的集合,其中所有对象的类型都相同,也被称为容器

  • vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,例如vector<int>
  • vector能容纳绝大多谢类型对象作为元素,但不能是引用,因为引用不是对象,元素也可以是vector

定义和初始化 vector 对象

下表列出了定义vector对象的常用方法

vector_initialize

  • 列表初始化:C++11新标准提供的为vector对象的元素赋初值的方法。

  • 拷贝初始化:即使用=时,只能提供一个初始值

  • 如果提供的是一个类内初始值,则只能用拷贝初始化或使用花括号的形式初始化

  • 如果提供是的初始元素值的列表,则只能把初始值放在花括号里进行列表初始化,不能放在圆括号。

    vector<int> v1(10);  // v1 有 10 个元素,每个值都为0
    vector<int> v2{10};  // v2 有 1 个元素,该元素的值是10
    vector<int> v3(10, 1);  // v3 有 10 个元素,每个的值都是1
    vector<int> v4{10, 1};  // v4 有 2 个元素,值为 10 和 1
    
    // 若花括号提供的值不能用来列表初始化,则考虑用值构造 vector 对象
    vector<string> v5{"hi"};  // 列表初始化,v5 有一个元素
    vector<string> v6("hi");  // 错误:不能使用字符串字面值构建 vector 对象
    vector<string> v7{10};  // v7 有 10 个默认初始化的元素
    vector<string> v8{10, "hi"};  // v8 有 10 个值为 "hi" 的元素
  • 上面后四条语句除了第二条语句都用了花括号,但只有v5是列表初始化。要想列表初始化,花括号里的值必须与元素类型相同。

其他 vector 操作

vector_operations_methods

  • vector对象以及string对象的下标运算符可用于访问已存在的元素,而不能用于添加元素
  • 范围for语句体内不应改变其所遍历序列的大小

迭代器介绍

迭代器提供了对对象的间接访问,对象是容器中的元素或者string对象中的字符,迭代器有有效和无效之分,有效的迭代器指向某个元素,或容器中尾元素的下一位置,其他情况都是无效。

使用迭代器

  • 有迭代器的类型都拥有返回迭代器的成员, 比如end成员,用来返回指向容器尾元素的下一位置的迭代器, 也被称为尾后迭代器。如果容器为空,则beginend返回的是同一个迭代器,都是尾后迭代器

iterator_operator
- 尽量使用!=而非<, 因为所有标准库容器的迭代器都定义了==!=, 但大多都没有定义<运算符

  • 标准库类型一般使用iteratorconst_iterator来表示迭代器类型

    vector<int>::iterator it;  // it 能读写 vector<int> 的元素
    vector<int>::const_iterator it3;  // it3 只能读元素,不能写元素
  • 如果vectorstring对象是常量,则只能使用const_iterator

  • 凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

迭代器运算

iterator_arithmetic

  • 两个迭代器相减的结果类型是名为difference_type的带符号整型数, 表示两个迭代器的距离

数组

数组与vector类似,但大小确定不变。

定义和初始化内置数组

  • 数组的维度在编译时应该是已知的, 即维度必须是一个常量表达式

    unsigned cnt = 42;  // 不是常量表达式
    constexpr unsigned sz = 42;  // 常量表达式
    int arr[10];  // 含有 10 个整数的数组
    int *parr[sz];  // 含有 42 个整型指针的数组
    string bad[cnt];  // 错误: cnt 不是常量表达式
    string strs[get_size()];  // 当 get_size 是 constexpr 时正确,否则错误
  • 默认情况下,数组的元素被默认初始化,如果在函数体内部定义的话,则含有未定义的值

  • 定义数组的时候必须指定数组的类型,不允许使用auto关键字推断类型

    int *ptrs[10];  // ptrs 是含有 10 个整型指针的数组
    int &refs[10] = /* ? */;  // 错误:不存在引用的数组
    int (*parray)[10] = &arr;  // Parray 指向一个含有 10 个整数的数组
    int (&arrRef)[10] = arr;  // arrRef 引用一个含有 10 个整数的数组
  • 使用数组下标访问数组元素的时候,类型为size_t, 一种与机器相关的无符号类型,在cstdef头文件中。

 指针和数组

  • 使用数组时,编译器一般会把它转换成指针

  • 在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针

    string nums[] = {"one", "two", "three"};  // 数组的元素是 string 对象
    string *p = &nums[0];  // p 指向 nums 的第一个元素
    string *p2 = nums;  // 同上
    
    auto nums2(nums);  // nums2 是一个 string 类型指针,指向 nums 的第一个元素,等价于 auto nums2(&nums[0])
    
    decltype(nums) nums3 = {"three", "two", "one"}; // nums3 是一个含有三个string类型对象的数组
  • C++11新标准引入了两个名为beginend的函数,与容器的同名函数功能类似,begin函数返回指向数组首元素指针,end函数返回指向数组尾元素下一位置的指针

    int ia[] = {0, 1, 2, 3, 4, 5};
    int *beg = begin(ia);  // 指向 ia 首元素的指针
    int *last = end(ia);  // 指向 arr 尾元素的下一位置的指针
  • 两个指针兴建的结果是他们之间的距离,结果是ptrdiff_t的带符号标准库类型,定义在cstddef

  • 标准库类型限定使用的下标必须是无符号类型,而内置的下标运算无此要求,即内置的下表运算符可以是负值。

C 风格字符串

字符串字面值是一种通用结构的实例,该结构即是继承于C的C风格字符串,习惯是字符串存放在字符数组中,并以空字符结束\0
C_style_string_methods

  • 上述函数不负责验证其字符串参数(比如,字符串未以空字符结尾,会报错)

  • 为了衔接使用了C借口的C++程序,C++提供一组功能;

    • 允许C风格字符串初始化string对象,反之不行,但可以使用std::c_str函数获取C风格字符串,返回结果指针,类型是const char *

    • 允许用数组初始化vector对象

    int int_arr[] = {0, 1, 2, 3, 4, 5};
    vector<int> ivec(begin(int_arr), end(int_arr));

多维数组

严格来说,C++没有多维数组,通常诉说的多维数组其实就是数组的数组

int ia[3][4] = {{0}, {4}, {8}};  // 显示的初始化每行的首元素
int ix[3][4] = {0, 3, 6, 9};  // 显示的初始化第一行,其他元素执行值初始化
  • 要使用范围for语句处理多维数组时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

    for (auto row : ia)
    for(auto col : row)

    如上所示,由于row不是引用类型,所以编译器初始化row时会自动将这些数组形式的元素转换成指向该数组内荣元素的指针,这样row的类型是int *, 所以内层循环会不合法。

结语

stringvector是两种最重要的标准库类型。string对象是一个可变长的字符序列,vector对象是一组同类型对象的容器

迭代器允许对容器中的对昂进行间接访问,对于string对象和vector对象来说,可以通过迭代器访问元素或者在元素箭移动

数组和指向数组元素的指针在一个较低层次上实现了与标准库类型stringvector类似的功能。一般啦说,应该优先选用标准库提供的类型,之后在考虑C++语言内置的底层的替代品数组或指针。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章