字符串、向量和数组---C++ 基础

来源:互联网 发布:胜任力测评软件 编辑:程序博客网 时间:2024/06/10 21:47

前言

上一章,复习了基本数据类型,趁热打铁,继续把字符串、向量(vector)和数组搞定。

字符串是由字符组成,也是序列结构,因此,本章其实设计到的数据结构是序列。那么就需要考虑是否能够随机访问,如何遍历,如何进行一些基本操作,以及这些基本操作的效率问题。

整个系列的复习会将所有涉及到的stl进行分析,工作量可能会大,但是复习嘛,总是一步一个脚印。废话不多说,开始干货。

3.1 using声明命名空间

命名空间这个东西使得大的项目的名称管理的效率得到了提升。如果需要使用库函数,那么需要加上命名空间才能使用相应的名称。而using就使得不用每个名称前都需要加命名空间。

#include <iostream>using std::cin; // 一般来说,大家都喜欢之间using namespace std,这样就不用对每个对象都使用using了int main() {}

既然提到了#include,我觉得有必要复习一下预处理器命令,以及宏,这个还是非常重要,我在复习设计模式的,涉及到的一个问题,c++如何实现反射?,详细请见博客另一篇文章《C++反射的实现》

注意

头文件里不应该包含using声明,因为,头文件的内容会拷贝到所有引用它的文件中,如果头文件里有某个声明,那么每个使用了这个头文件的文件都会有这个声明,名称污染。。。不知哪听来的


3.2 string类型

此处的string类型是标准库中的string,定义在std命名空间里。导入的头文件是string,而不是string.h,后者是c标准下的字符串数组的库函数集合。

#include <string>// 注意,不是string.h,如果想要使用c下的字符串处理函数,使用后者using namespace std;

string类型是可变长的字符序列。

  • 初始化
string s1; // 默认初始化,空串string s2 = s1; // s2 是 s1 的副本string s2(s1); // 直接初始化string s3("hiya");string s3 = "hiya"; // s3 是 字面值的副本string s4(10, 'c'); // s4 "cccccccccc"
  • 操作
os << s // 写入输出流is >> s // 从流读出字符串,以空白分隔getline(is, s) // 从流中读出一行s.empty() s.size()s[n]s1 + s2s1 = s2s1 == s2s1 != s2 <, <=, >, >=s.push_back(char s)s.pop_back()s.begin()s.end()s.rbegin()s.rend()s.reverse()s.clear()s.back()s.front()s += "hello"s.find(str, pos) // 没找到返回-1, 返回string::size_type类型,保证能够在当前机器上存储string对象大小

注意:

string.size() 返回的是size_type类型,这是一个无符号类型,因此,在比较时要特别注意隐式转换(有符号类型会隐式转换成无符号类型),我们知道找不到返回的是size_t 类型的 -1,这个数其实是可能的最大数。

另外string类型的加法,必须要有一个运算对象是string,(字符串字面值不是字符串string类型)

注意:

s = "hello" + "," + s; // 错误,因为"hello"是一个字符串字面值,不是字符串
  • 处理string对象中的字符
#include <cctype>string s = "hello world";for (auto &c : s) { // 要修改字符,必须要使用引用    cout << isalnum(c) << endl;    tolower(c);}char a = 'a';isalnum(a);isalpha(a);isdigit(a);isspace(a);tolower(a);toupper(a);

3.3 标准库类型vector

vector是标准库里的一类容器,表示对象的有序集合(不包含引用)。

  • 定义和初始化vector对象

vector其内存占用空间是只增不减,erase和clear都不能减少vector占用的内存。所有内存空间在vector析构的时候回收。

#include <vector>using namespace std;vector<T> v1;vector<T> v2(v1);vector<T> v2 = v1;vector<T> v3(n, val);vector<T> v4(n);vector<T> v5{a, b, c};vector<T> v5 = {a, b, c};vector<string> v6 = {10, "1"}; // 十个string对象vector<int> v7 = {10, 1}; // 两个对象
  • 操作
vector<int > arr;arr.push_back(10);arr.pop_back();arr.empty();arr.size();arr.front();arr.back();vector<int> foo(3, 100);foo.swap(bar); // swap 是常数时间复杂度(仅仅交换了指针),因此常常用来清空vector,进行内存释放for (auto &e : arr) {    e = 11; // 可以进行修改,但不要对arr进行添加或者删除}

前面提到了vector怎样清除内存占用的问题。这里就要用到临时对象以及析构的概念了。

vector<int> arr(100000, 0);...//建立临时对象,然后调用swap函数,临时对象会立即调用析构函数,进行内存释放vector<int>().swap(arr);a.pop_back();vector<int>(arr).swap(arr); // 释放一个

注意:

vector 使用下标访问元素,必须是已经存在的下标,不要放低级错误


3.4 迭代器介绍

迭代器在标准库中是一种访问容器对象更加通用的方式,只有少数几个容器是支持下标访问的,目前已知的有vector、string、deque、map、unordered_map。

  • 使用迭代器
auto b = v.begin();auto e = v.end(); // 记住auto的方法,非常简便,不用写过多的类型*iter // 返回对象引用iter -> mem // 取名为mem的成员++ iter-- iteriter1 == iter2iter1 != iter2

注意:

再次提醒,任何改变容器对象数量,都会使得迭代器失效

  • 迭代器运算
iter + n iter - n iter += n iter -= n iter1 - iter2 >, >=, <, <=auto mid = vi.begin() + v.size() / 2; // 取中点

3.5 数组

数组类似vector,但是数组的大小在定义时就已经确定了。

注意:

数组不允许使用 auto 关键字由初始化列表来推断类型

  • 定义和初始化
int a[10]; // 默认初始化 // 显示初始化int arr[5] = {1, 1}; // 1 1  0 0 0int arr[] = {1, 1} // 1 1int arr[2] = {1, 2, 3} // 错误,初始值过多int arr[100] = {0} // 0 ...// 字符数组,特殊在最后的空字符char a1[] = {'a', 'b', 'c'} // 最后没有空字符char a2[] = {'a', '\0'} // 最后有显示的空字符char a3[] = "hello"; // 自动添加末尾空字符char a4[5] = "hello word" // 错误,没有空间存放空字符 
  • 理解复杂数组
int &ref[10] = ptr; // 错误,没有引用数组int *ptr[10]; // ptr是一个数组,包含10int *类型指针的数组int (*ptr)[10]; // ptr是一个指针,指向含有10int类型整数的数组int (&ptr_ref)[10]; // ptr_ref是一个引用,绑定的是含有10int类型整数的数组
  • C 风格字符串(安全风险,需要自己管理大小)

C 风格的字符串要求字符串数组要以空字符’\0’结尾。

#include <string.h>strlen(p);strcmp(p1, p2); // 返回比较值,+、-、0strcat(p1, p2); // p2 附加到p1 ,返回p1strcpy(p1, p2); // p2 拷贝给p1,返回p1

3.6 多维数组

严格来说,c++没有多维数组,通常说的多维数组都是数组的数组。因此,初始化、访问时都可以仔细理解一下。

int ia[3][4]; // 默认初始化,大小为3的数组,每个元素是4个整数的数组int arr[100][200] = {0} // 初始化为0int ia[3][4] = {    {0, 1, 2, 3},    {4, 5, 6, 7},    {8, 9, 10 , 11} };// 等价于int ia[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};// 显示初始化每行首元素int ia[3][4] = {{0}, {3}, {4}};// 显示初始化第一行,其他的值初始化int ia[3][4] = {0, 3, 6, 9};
  • 指针和多维数组

时刻记住,多维数组实际上是数组的数组。

int ia[3][4];int (*p)[4] = ia; // p 指向第一行的数组p = &ia[2]; // p 指向ia的末尾行 // 值得注意的是,p是一个指向有四个整数的数组的指针,而数组ia就是一个指针,因此p是一个指针的指针// 比如要找ia[1][1]*(*(ia + 1) + 1);*(ia[1] + 1);*(&ia[0][0] + 4 * 1 + 1);a // 等价于a[0],保存的是指向第一行的数组的指针, 也就是a[0][0]a + 1 // 等价于a[1], 保存的是指向第二行的数组的指针, 也就是a[1][0]
  • 可以使用typedef和using简化多维数组的指针
using int_array = int[4];typedef int int_array[4];
原创粉丝点击