[C++学习笔记] 数组

字符数组

Getline()函数读一行 直到读入换行符为止
但是读入换行符并不存入 读入的换行符被丢弃 得到的string不包含换行符

endl操作符 作用是结束当前行并刷新显示缓冲区

string.size() 返回的长度是无符号数 与int混用时可能会产生错误

当使用 + 号来进行字符串操作时 保证两个操作数中至少有一个是string才行
不能直接将两个字面值字符串进行相加
例如"hello" + "word"是错的 因为重载的是string类定义的运算符+

连续输入时可以存在相邻都是字面值
例如 string s1;string s2 = s1 + "he"+"llo";
因为根据自左向右的计算规则 仍旧保证了有一个类型为string
但反过来 string s3 = "he"+"llo"+s1 ; 则不成立

c中标准库xxx.h文件在c++中为cxxx 尽量避免使用xxx.h的形式 因为cxxx的名字都包含在std中

C++迭代器 for(迭代变量: 序列)

string s("abcd"); 
for(auto i : s) cout<<i<<endl;

但如果想要改变string中字符的值 必须将迭代变量定义为引用类型 for(auto &i : s) i = 'e';
当然也可以使用传统的下标索引法

容器

vector 也被称为容器 表示对象的集合 其中所有对象的类型都相同
vector是一种类模板 利用模板创建类或者函数的过程称之为实例化
类模板必须包含其中元素的类型:

vector<int> v; 
vector<vector<int>> a(10);

创建vector时 可不指定数目创建 也可用()指定元素数量
考虑到vector能高效添加元素 一般不指定元素数目 除非是需要创建所有值都相同的vector 否则反而会降低效率

利用vector的成成员函数push_back()来向vector尾部添加元素 v.push_back(1);
vector其他操作类似于string 比如 迭代遍历 改变值的操作可以用for遍历 也可以用下标索引,
获取大小也是使用.size()成员函数

另一种更加通用的遍历容器中元素或string中字符的方法是迭代器
所有标准库容器都支持迭代器 但只有少数几种支持下标
凡是支持迭代的类型都可以返回迭代器成员(如beginend,分别表示指向第一个元素和尾元素的下一位置的迭代器成员)

auto b=v.begin(), e=v.end(); 一般而言使用auto定义迭代器类型 因为我们不关注其真正类型
end是尾后迭代器,表示已经处理完了所有元素 若容器为空 beginend返回的是一样的

可以通过++/--来令迭代器指向下/上一个元素
通过begin = begin + n来指向后n个字符
通过*begin解引用来获取迭代器所指向元素的引用

for(auto it = s.begin(); it!=s.end() && ! Isspace(*it); ++it) 
    *it= toupper(*it);
// 依次处理s的字符直至处理完全部字符或遇到空白 实现将string中第一个单词大写的功能

C++在for循环中更常用!=运算符而不是<运算符,因为!=支持的类型更多,很多类型未定义<

可以使用cbegincend表示常量类型的迭代器

箭头运算符等价于先解引用再使用.运算 例如it->mem等价于(*it).mem
括号不可少 因为点运算符. 优先级高于解引用运算符*

注意 当循环中可能改变容器(如vector)容量的时候 不能使用for循环语句 因为for循环中不能改变所遍历序列的大小 同理 也不能使用迭代器

数组与vector的区别在于数组的大小确定不变并且不能用赋值方法拷贝另一个数组

Int a[]= {1,2,3}; 
int b[]=a; 
b=a; //后两种都是错的 不能使用数组给数组初始化或赋值

引用不是对象,因此,不存在引用数组 只有数组的引用

数组定义要由内而外看 例如 parray 首先自内而外地看 是一个指针 然后发现是个10个元素的数组 并且类型为int
同理 arrRef 是一个引用 然后看到是一个10个元素的数组引用 类型为int

int *(&array) [10] =ptrs; array是一个包含十个指针的数组的引用
由内而外看 array是一个引用 然后看到是一个10个元素的数组 类型为int *

全局变量未被初始化时自动初始化为默认值(一般为0或空) 局部变量未被初始化时处于未定义

使用auto推断数组时得到的是指针类型 而使用的decltype推断时返回的是数组类型

指针也是一种迭代器
如果要在数组中使用迭代器,不是通过成员函数
因为数组不是类 而是使用数组本身作为参数调用库函数 得到返回的指针

int a[] = {1,2,3}; 
int *beg=begin(a); 
int *ed = end(a);

数组的下标运算等价于指针加对应数值

int a[] = {1,2,3}; 
int *p= &a[1]; 
p[1]=1; //等价于 a[2] =1, p[n] 等价于 *(p+n)

c风格字符串必须以'\0'结尾 使用其函数也必须保证传入的字符串符合此要求(如strlen等)
C++中string对象可以通过c_str()返回符合c格式的字符指针

string s="1"; 
const char * str=s.c_str();

不能使用数组和vector来初始化数组 但反过来可以使用数组来初始化vector

int arr[] = {0,1,2}; 
vector<int> iv(begin(arr), end(arr)); 
vector<int> ivv(arr+1, arr+3);

严格来说 C++没有多维数组 只有数组的数组
int a[3][4] 事实上定义的是一个大小为3的数组 数组中的每一个元素都是大小为4的数组

数组初始化时若只指定了一部分值 那么剩下的将被默认初始化
int a [10]={0}; int a[2][2]={0}; 事实上将所有元素都初始化为 0 了

使用for循环遍历时 除了最内层元素 都应该使用引用来遍历 否则对于多维数组会被识别为指

for(auto &i: s){ 
    for(auto & j: i){ 
        do something;
    } 
}

//如果不想修改数组内的值的话
for(const auto &i: s){ 
    for(auto j: i) { 
        do something;
    } 
} 

其次 使用引用遍历可以避免拷贝元素 尤其是元素较大时可以提高效率 比如string对象的vector