您的位置:首页 > 编程语言 > C语言/C++

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

2017-06-06 00:58 211 查看

前言

上一章,复习了基本数据类型,趁热打铁,继续把字符串、向量(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

s1 + s2
s1 = s2
s1 == s2
s1 != 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
-- iter

iter1 == iter2
iter1 != 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 0
int arr[] = {1, 1} // 1 1
int 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是一个数组,包含10个int *类型指针的数组
int (*ptr)[10]; // ptr是一个指针,指向含有10个int类型整数的数组

int (&ptr_ref)[10]; // ptr_ref是一个引用,绑定的是含有10个int类型整数的数组


C 风格字符串(安全风险,需要自己管理大小)

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

#include <string.h>
strlen(p);

strcmp(p1, p2); // 返回比较值,+、-、0
strcat(p1, p2); // p2 附加到p1 ,返回p1
strcpy(p1, p2); // p2 拷贝给p1,返回p1


3.6 多维数组

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

int ia[3][4]; // 默认初始化,大小为3的数组,每个元素是4个整数的数组

int arr[100][200] = {0} // 初始化为0

int 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];
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: