2. 字符串、向量和数组

字符串、向量和数组

头文件中不应该使用using的声明,因为头文件的内容会拷贝到所有引用它的文件中去,若头文件中使用了using,则每个引用该头文件的文件都会有这个声明。

std::string

std::string的初始化方式:

#include <string>
using std::string;
string s1;
string s2(s1);      // s2是s1的副本
string s2=s2;       // s2(s1)等价
string s3="value";  // s3是字面值value的副本,等价s3("value")
string s4(5,'c');   // 等价 s4="ccccc";

使用等号的是拷贝初始化,不用等号的是直接初始化。

使用std::cinstd::coutstring进行读写操作。

读取未知量的string对象,遇见文件结束标志后停止:

int main(){
    string word;
    while(cin>>word){
        cout<<word<<endl;
    }
    return 0;
}

使用getline读取整行,但是不包括结尾的换行符!

int main(){
    string line;
    while(getline(cin,.line)){
        cout<<line<<endl;
    }
}

string::size_type是表示string大小的数据类型,表达式中出现了string::size()时,不要使用int,而是使用auto自动推导。

string s("12345");
auto len=s.length();  // len是string::size_type类型

字符串运算:

  • ==:长度和对应字符完全相同
  • >=<=><比较第一个相异字符的字典序
  • +,两个字符串拼接,也可以和字面值进行拼接,s+"value";但是字面值不能直接相加

遍历字符串使用for

string s("1234567");
for(auto c:s){  // 在这里的c是拷贝的,不是引用!
    cout<<c;
}
for(auto &c:s){ // 这里是引用!
    cout<<toupper(c);  // 原来的也变成了大写
}

也可以按照下表访问:

string s("1234567890");
// 注意使用类型推导
for(decltype(s.size()) index=0;index!=s.size();++index){
    s[index]=toupper(s[index]);  // 字母改成大写
}

std::vector

vector对于类型一般是引用的方式,而非拷贝赋值,因此不存在对vector的引用!

初始化的方法:

vector<T> v1;           // 空的vector
vector<T> v2(v1);       // v2包含v1的所有副本
vector<T> v3=v1;        // 等价于v3(v1)
vector<T> v4(n,val);    // v4包含n个val
vector<T> v5{a,b,c};    // v5包含a b c
vector<T> v6={a,b,c};   // v6和v5等价
vector<T> v7(10);       // v7包含10个元素,初始值由元素的类型确定
vector<string> v8{10,"hi"};  // 包含10个hi

使用vector::push_back向末尾添加元素,这种做法是最高效的。

其他的一些比较操作类比于string。使用size时,要注意指定类型:

vecotr<int>::size_type; // 这是正确的
vector::size_type;      // 这是错误的!!!!!!!

下标规则参照string

迭代器

所有的标准库成员都有迭代器,begin()是第一个元素,end()是最后一个元素的后一位,实际意义仅仅是结束的标记。空的容器的迭代器的begin()==end()

vector<int> v;
auto b=v.begin(), e=v.end();    // 使用自动推导
auto cb=v.cbegin(),ce=v.cend(); // 区别在于不能通过迭代器修改元素

一般规则:

  • *iter 返回迭代器指向元素的引用
  • iter->mem(*item).mem等价,是访问某元素的mem成员
  • ++iter指向下一个元素
  • --iter指向上一个元素
  • iter1==iter2或者iter1!=iter2判断是否指向同一个元素。
  • 不能对end进行递增或者解引用操作
  • 使用了迭代器的循环体后,不能在循环体中向容器添加元素!!!

vectorstring支持的运算规则:

  • iter+n向后指向的第n个,或者指向end
  • ietr-n向前指向的第n个,或者是begin
  • iter-=niter+=n是对本身的操作
  • >= > <= <比较的是相对位置

迭代器位置做差,返回的是difference_type的类型,一般用auto自动推导:

// 二分查找
auto b = text.begin(), e = text.end();
auto mid = text.begin() + (e - b) / 2;

while(mid != e && *mid != goal) {  // goal是目标值
    if(goal < *mid) {
        e = mid;
    } else {
        b = mid + 1;
    }
    mid = b + (e - b) / 2;
}

数组

初始化的方式:

const unsigned sz=3;
int a1[sz]={0,1,2};     // 0 1 2 三个元素
int a2[]={1,2,3};       // 0 1 2 三个元素
int a3[5]={0,1,2,};     // 0 1 2 0 0 
string a4[3]={"hello","world"}; // "hello" "world" ""
int a5[2]={0,1,2}; // 错误,初始值过多

默认情况下,数组类型从右向左依次绑定。数组名是地址,指向第一个元素。数组的下表是size_t类型。数组的指针也是迭代器,满足迭代器的一般运算规则,数组迭代器同时满足vector的标准。

现代的C++程序应该尽量使用vector而非数组,使用string而非C风格的字符串。

两种不同的for索引多维数组的方式:

constexpr size_t rowCnt = 3, colCnt = 4;

int ia[rowCnt][colCnt];

for(size_t i = 0; i != rowCnt; ++i) {
    for(size_t j = 0; j < colCnt; ++j) {
        ia[i][j] = i * colCnt + j;
    }
}

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

若使用上述第二种方式遍历,除了最内层的循环外,其他所有循环的控制变量都必须是引用类型!!!!如果不想更改数据,可以添加const限定!

多维数组的数组名是指针的指针:

int a[3][4];    // 大小为3的数组,每个元素都是含有4个整数的数组
int (*p)[4]=a;  // 指向含有4个整数的数组
int *ip[4];     // 整形指针的数组

猜你喜欢

转载自blog.csdn.net/qq_35976351/article/details/81773349