c++primer笔记三、字符串向量和数组

3.1命名空间

使用std::cin时,使用作用域操作符,说明要使用命名空间std中的名字cin

使用using就无需用前缀也能使用所需名字

using namespace::name;

例如using std::cin;
下次在使用cin时就不需要使用"::"

每个名字都需要独立的using声明

using std::cout;using std::ednl;

头文件中不应该使用using,因为头文件的内容会拷贝到所有引用他的文件中,可能会有冲突。

using namespace std

可以将作用域std中所有的标识符

3.2 string

string表示可变长的字符序列,使用时必须包含头文件

#include<string>
using std::string

初始化方法

string s1; //默认初始化,s1是空字符串
string s2 = s1; //s2是s1副本
string s3 = "hiya";
string s4(10,'c'); //cccccccccc
string s5("value")
string s6(s5);

如果使用=号,执行的是拷贝初始化。反之是直接初始化

string的操作

os<<s       //将s写到输出流os中,返回os
is>>s       //从is中读取字符串给s,字符串以空白分割,返回is
getline(is,s) //从is中读取一行赋值给s,返回is
s.empty()   //是否为空
s.size()    //字符个数
s[n]        //返回第n个字符的引用,从0开始
s1+s2       //连接
s1=s2       //s2替换s1
s1==s2      //是否一样
s1!=s2      //对大小写敏感
<,<=,>,>=   //利用字典中的顺序比较

读写string

string s;
cin >> s;
cout << s <<endl;

读取时会自动忽略开头空白从第一个真正字符开始读取,直到下一处空白。
输入"Hello World"结果为"Hello"

可以多个一起连携
string s1,s2;
cin >> s1 >> s2;
cout << s1 << s2 << endl;

输入"Hello World"结果为"HelloWorld"

读取位置数量的string

string word;
while(cin >> word)
    cout << word << end1;

使用getline读取一整行
getline遇到换行符就结束操作(包括换行符)

string line;
while(getline(cin, line))
    cout << line <<endl;
    
每次都能读入一整行,而不是一个单词。触发getline的换行符被丢弃了,得到的string对象中没有换行符。
size()函数返回的是string::size_type的类型值  
配套类型体现了标准库类型和机器无关的特性  
是一个无符号类型值且能足够放下任何string对象的大小。
因此表达式中不要混用带符号和无符号数  
s.size()<n当n为负数时为true

string的比较

1、长度不同,字符都相同,则短的小于长的
2、字符串不同,则比较第一个相异字符

string的加法
必须确保有一侧是string

string s4 = s1 + ",";   //正确
string s5 = "hello"+"/" //错误
string s6 = s1 + "," + "world"//正确
string s7 = "," + "world" + s1 //错误
有连续输入连续输出的工作机理

处理string中的字符

先取单个字符,然后使用cctype中的函数库

isalnum(c)  //是否为字母或数字
isalpha(c)  //字母
iscntrl(c)  //控制字符
isdigit(c)  //数字
isgraph(c)  //不是空格但可以打印时为真
islower(c)  //小写
isprint(c)  //可打印(c时空格或具有可是形式)
ispunct(c)  //标点符号
isspace(c)  //空白(空格,制表符,回车符,换行符,进纸符)
isupper(c)  //大写
isxdigit(c) //十六进制数字
tolower(c)  //转换成小写
toupper(c)  //转换成大写

使用范围for

string str("Hello World!");
for (auto c : str)
    cout << c << endl;

如果要修改值,则需要引用

for (auto &c : str)
    c = toupper(c)

用下标运算符之前要先检查字符串是否为空,否则会出错。

3.3 vector

表示对象的集合,常被成为容器,也叫向量。

使用前先包含头文件
#include <vector>
using std::vector

vector是一个类模板,通过提供的额外信息来指定模板到底实例化成什么类,提供的方式是尖括号.

vector<int> ivec ;
vector<vector<string>> file;

初始化的方法

vector<T> v1;       //空的
vector<T> v2(v1);   //v2包含V1所有元素副本
vector<T> v2 = v1;  //和上一个一样
vector<T> v3(n,val);    //包含了n个重复元素,每个元素都为val
vector<T> v4(n);    //包含了n个重复地执行了值初始化地对象
vector<T> v5{a,b,c,d,e};    //包含了初始值地元素
vector<T> v5 = {a,b,c,d,e}; //和上一个一样

列表初始化vector对象

vector<string> articles = {"a", "an", "the"};
需要使用花括号

值初始化

vector<int> ivec(10);   //10个元素,都为0
vector<string> svec(10);    //10个元素,都是空string

使用括号是构造对象,花括号是列表初始化.

添加元素使用push_back

for(int i = 0; i != 100; ++i)
    vec.push_back(i);
    
while(cin >> word)
    text.push_back(word);

为了性能,不需要指定其容量.

注意:范围for语句体内不应该改变其遍历序列地大小.

其他vector操作

v.empty()       //空
v.size()        //个数
v.pushback(i)   //添加
v[n]            //返回第n个位置上的元素地引用
v1 = v2         //v2拷贝替换V1
v1 = {a, b, c}
v1 == v2
v1 != v2
<,<=,>,>=

注意:不能直接用下标添加元素,会出现缓冲区溢出.

3.4迭代器

所有标准库容器都可以使用迭代器。少数几种才支持下标。
使用迭代器

auto b = v.begin(); //第一个元素
auto e = v.end();//指向尾元素地下一个位置

迭代器运算符

*iter   //返回迭代器iter所指元素地引用
iter->mem   //获取该元素名为mem地成员,等价于(*iter).mem
++iter  //iter指向下一个元素
--iter  //iter指向上一个元素
==  //是否指向同一个元素
!=  

例:把string对象第一个字母改为大写

if(s.begin() !=  s.end())   //确保S非空
{
    auto it = s.begin();    //it表示s地第一个字符
    *it = toupper(*it);     //当前字符改写成大写
}

将迭代器一个元素移动到另外一个元素
例:把第一个单词改写成大写

for(auto it = s.begin(); it != s.end()& !isspace(*it);++it)
    *it = toupper(*it);

迭代器类型

iterator        //可以读可以写
const_iterator  //可以读不能写

begin和end运算符
返回的类型由对象是否是常量决定,如果对象是常量,返回const_iterator
如果不是常量,返回iterator。

如果需要读操作而不需要改,最好使用const_iterator,可以用cbegin,cend

auto it3 = v.cbegin();  //it3的类型是vector<int>::const_iterator

解引用和成员访问操作
解引用时必须要加括号

(*it).empty()   //检查元素是否为空

为了简化表达式,定义了箭头运算符(->),把解引用和成员访问操作结合在一起

it->mem和(*it).mem表达的意思相同

在范围for循环中,不能向vector添加元素。
任何改变容量的操作,都会使vector对象的迭代器失效

迭代器的运算

    • < >= <=

    • 计算位置
      比较运算表的的时前还是后
auto mid = vi.begin() + vi.size()/2;    //取中间位置的元素

两个迭代器相减得到difference_type的带符号整数,表示距离,可负。

3.5数组

数组的大小确定不变,不能随意增加元素。
数组的初始化必须如a[d],其中d是数字的维度,必须是常量。
默认清空下,数组的元素会被初始化。

const unsigned sz = 3;
int ial[sz] = {0,1,2};
int a2[] = {0,1,2};
int a3[5] = {0,1,2};    //剩下的补0

字符数字的特殊性:字符数组如果用字符串字面值初始化会有空字符

char a1[] = {'c','+','+'};  //没空字符
char a2[] = {'c','+','+',''};  //有空字符
char a3[] = "c++";           //自动添加空字符 
const char a4[6] = "Daniel";    //错误,没空间存放空字符

数组是无法拷贝的,不能作为其他数组的初始值,也不能赋值

数字可以定义指针或者引用

int *ptrs[10];          //ptrs是含有10个整型指针的数组
int &ref[10];           //错误,不存在引用的数组
int (*Parray)[10] = &arr'   //Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;    //arrRef引用一个含有10个数组的整数

访问数组元素
1、范围for 2、使用下标(size_t类型),定义在stddef.h中
使用时需要注意下标的值

数组和指针
使用数组的时候编译器一般会转换成指针。
一般用取地址符来获取指针。

string nums = ["one", "two", "three"];
string *p = &nums[0];       //p指向nums的第一个元素

在用到数组名字的地方编译器会自动替换成一个指向数组首元素的指针

string *p2 = nums;  //等价于
string *p2 = &nums[0]

指针也是迭代器

int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;
++p;    //p指向arr[1]
int *e = &arr[10]   //索引一个不存在的元素,用于提供其地址初始化e

for(int *b = arr; b != e; ++b)
    cout << *b << endl;     //输出arr的元素

数组也有begin和end

int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(arr);  //指向第一个元素的指针
int *last = end(arr);   //指向arr尾元素的下一个位置的指针

PS:不过尾后指针无法解引用和递增操作。

指针运算

int *ip2 = ip + 4;      //指针+整数,就变化位置,要注意有意义。
auto n = end(arr) - begin(arr);     //指针相减,结果是ptrdiff_t的带符号数
也可以比较,结果是数组的位置次序

如果两个指针分别指向不相关对象,则比较无意义。

解引用和指针运算的交互

int ia[] = {0,2,4,6,8};
int last = *(ia + 4);   //last初始化8

对数字使用下标运算时,其实就是对指针操作

int *p = ia;
i = *(p + 2)
等价于
i = ia[2]

内置的下标可以为负

int *p = &ia[2];
int j = p[1];   //j等价于ia[3]
int k = p[-2];  //等价于ia[0]

c风格字符串(尽量不要使用)
char类型的数组代表字符串。

const char ca[] = "A string example"
与旧代码的接口

字符串的混用:
1、允许以空字符结束的字符数组来初始化string或赋值
2、string对象的加法中允许以空字符结束的字符数组作为运算对象。
在string对象的复合赋值运算允许使用以以空字符结束的字符数组作为右侧运算对象。
反过来不成立.

从string转字符数组,可用成员函数c_str();

const char *str = s.c_str();

使用数组初始化vector

int int_arr[] = {0,2,3,4,5,6,};
vector<int> ivec(begin(int_arr),end(int_arr));  //复制数组的元素
vector<int> subVec(int_arr + 1, int_arr + 4);   //复制int_arr[1]、int_arr[2]、int_arr[3]
多维数组

初始化

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
int ia[3][4] = {{0},{4},{8}};       //初始化每行首元素

使用范围for处理多维数组

size_t cnt = 0;
for(auto &row : ia)
    for(auto &col : row)
    {
        col = cnt;
        ++cnt;
    }

在循环控制变量最好使用引用,为了避免数组被自动转成指针。

for (const auto &row : ia)
    for(auto co1 : row)
        cout << co1 <<endl;
        
//如果如下则无法编译
for(auto row : ia)
    for(auto col : row)

因为第一个循环遍历ia的所有元素,会初始化row为转向改数组首元素的指针,row的类型是int*。

当程序使用多维数组的名字时,换自动转换成指向数组首元素指针。

int ia[3][4];
int (*p)[4] = ia;   //p是指向一个维度为4的数组的指针
p = &ia[2];
int *ip[4]; //整型指针的数组
int (*ip)[4];//含有4个整数的数组的指针

循环时使用auto来避免在数组前面加上指针

for (auto p = ia; p != ia + 3; ++p)
    for(auto q = *p; q!= *p + 4l ++q)
        cout << *q << '';
    cout << endl;

使用类型别名可以简化多维数组的指针
4个整数数组命名为int_array

using int_array = int[4];
typedef int int_array[4];   //等价的typedef声明

for (int_array *p = ia; p != ia + 3; ++p)
    for(int *q = *p;q != *p + 4;++q)
        cout << *q << '';
    cout << endl;
原文地址:https://www.cnblogs.com/aqq2828/p/13958614.html