【C++笔记】字符串、向量和数组

string(C++Primer 5th)

string & vector

  string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列。

using声明

  头文件不应包含using声明,否则头文件的内容会拷贝到所有引用它的文件中去。

string初始化赋值

  使用等号=初始化的是拷贝初始化,不使用等号的称为直接初始化。
size()函数的返回值是类型size_type,无符号整数型。如果n是负值的int,则s.size()

string s(10,'c');               // s的内容是cccccccccc

cin>>word;                      // 遇到空格停止
getline(cin,line);              // 读入一整行,直到换行符

auto len = line.size();         // len的类型是string::size_type

string s1 = s + "," + "world";  // 加法需保证+两次的运算对象至少有一个是string 
string s2 = "hello" + "," + s1; // 错误:两个运算对象都不是string
使用C++版本的C标准库头文件

  一般来说,C++程序应该使用名为cname的头文件而不使用name.h的形式。

基于范围的for语句
// 统计string对象中标点符号的个数
string s("Hello World!!!");     
decltype(s.size())  punct_cnt=0; // 用于统计个数,无符号整型确保下标不小于0 
for(auto c:s)
    if(ispunct(c))  punct_cnt++; 
// 使用范围for语句改变字符串中的字符
string s("Hello World!!!");
for(auto &c:s)
    c = toupper(c);             // 将string对象转换成大写
cout << s << endl;




vector

vector概念

  标准库类型vector表示对象的集合,其中所有对象的类型都相同,通常也称容器。

  vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,如vector<int>、vector<vector<int>>

  由于引用不是对象,所以不存在包含引用的vector。

列表初始化 OR 元素数量
vector<int> v1(10);         // v1有10个元素,每个的值都是0
vector<int> v2{10};         // v2有1个元素,该元素的值是10

vector<int> v3(10,1);       // v3有10个元素,每个的值都是1
vector<int> v4{10,1};       // v4有2个元素,值分别是10和1

vector<string> v5{"hi"};    // v5有一个元素"hi"
vector<string> v6("hi");    // 错误,不能用字符串字面值构建vector对象
vector<string> v7{10};      // v7有10个默认初始化的元素
vector<string> v8{10,"hi"}; // v8有10个值为"hi"的元素
向vector对象中添加元素

  添加元素的方法是先创建一个空vector,然后在运行时利用vector的成员函数push_back向其中添加元素。PS:预先指定vector对象的容量没有什么必要,反而可能导致性能变差,不建议。

  如果循环体内部含有向vector对象添加元素的语句,则不能使用范围for循环!范围for语句体内不应改变其所遍历序列的大小。

vector<int> v;              // 空vector对象
for(int i = 0; i != 100; i++)
    v.push_back(i);         // 往vector中添加0到99,不能用下标方式添加元素!

/*《C++Primer 5th》P94.练习3.16*/

#include <iostream>
#include <vector>
#include <string>
using namespace std;

void main()
{
    vector<int> v1, v2(10), v3(10, 42), v4{ 10 }, v5{ 10,42 };
    vector<string> v6{ 10 }, v7{ 10,"hi" };

    cout << "v1:"     << endl;  for (auto c : v1)       cout << c << "\t";  
    cout << "\n\nv2:" << endl;  for (auto c : v2)       cout << c << "\t";  
    cout << "\n\nv3:" << endl;  for (auto c : v3)       cout << c << "\t";  
    cout << "\n\nv4:" << endl;  for (auto c : v4)       cout << c << "\t";  
    cout << "\n\nv5:" << endl;  for (auto c : v5)       cout << c << "\t";  
    cout << "\n\nv6:" << endl;  for (auto c : v6)       cout << c << "\t";  
    cout << "\n\nv7:" << endl;  for (auto c : v7)       cout << c << "\t";

    getchar();
    return ;
}


迭代器

相比下标运算符

  所有标准库容器如vector都可以使用迭代器,但是只有少数几种才同时支持下标运算符。严格来说,string对象不属于容器类型,但它支持迭代器。

begin、end

  begin成员负责返回指向第一个元素的迭代器,end成员负责返回尾元素的下一位置(该位置并无元素)的迭代器。end返回的迭代器通常称为尾后迭代器或尾迭代器。

  如果容器为空,则begin和end返回的是同一个迭代器即尾后迭代器。

  所有标准库容器的迭代器都定义了==和!=,但其中大多数并没有定义<运算符!!!

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

vector<int>::iterator it;           // it能读写vector<int>的元素
string::iterator it2;               // it2能读写string对象中的字符

vector<int>::const_iterator it3;    // it3只能读元素,不能写元素
string::const_iterator it4;         // it4只能读字符,不能写字符
const_iterator

  begin和end返回的具体类型由对象是否是常量来决定,如果对象是常量,begin和end返回const_iterator;如果对象不是常量,返回iterator。

cbegin、cend

  C++11新标准引入了cbegin和cend,用法与begin、end相同,不同的是不论vector对象是否常量,返回值都是const_iterator。

解引用与成员访问

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

(*it).empty()                       // 解引用it,然后调用empty成员
*it.empty()                         // 错误,试图访问it的empty成员(并不存在)

for(auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
    cout<< *it <<endl;
difference_type

  迭代器的距离是指一个迭代器移动多少位置能追上另一个迭代器,类型位difference_type的带符号整型数,可正可负。

二分搜索

  迭代器的经典算法时二分搜索。




数组

数组初始化

  定义数组时必须指定数组类型,不允许使用auto关键字由初始值的列表推断类型。和vector一样,数组元素应为对象,因此不存在引用的数组。

// 错误示范

const char a[6] = "Daniel";         // 错误,没有空间存放空字符

int a[] = {0,1,2};  
int a2 = a;                         // 错误,不允许使用一个数组初始化另一个数组
a2 = a;                             // 错误,不能把一个数组直接赋值给另一个数组
数组声明
int *ptrs[10];                      // ptrs是含有10个整型指针的数组
int &refs[10] = /*?/;               // 错误,不存在引用的数组
int (*parray)[10] = &arr;           // parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;            // arrRef引用一个含有10个整数的数组
int *(&array)[10] = ptrs;           // arry是数组的引用,该数组含有10个指针
指针也是迭代器
int arr = {0,1,2,3,4,5,6,7,8,9};
int *e = &arr[10];                  // 指向arr尾元素的下一位置的指针
for(int *b = arr; b != e; ++b)      // arr表示首元素的地址
    cout << *b << endl;

  说明:arr有10个元素,尾元素所在位置的索引是9,下一位置的不存在的元素用于提供地址初始化e。与尾后迭代器雷系,尾后指针不指向具体的元素,不能进行解引用或是递增操作。

标准库函数begin和end

  C++11标准引入了begin和end两个函数,与容器中的成员函数功能相似。但数组不是类类型,所以begin和end不是数组的成员函数,正确的使用形式是将数组作为它们的参数。

int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia);               // 指向ia首元素的指针
int *last = end(ia);                // 指向arr尾元素的下一位置的指针
指针运算
constexpr = size_t sz =5;
int arr[sz] = {1,2,3,4,5};
int *ip = arr;
int *ip1 = ip + 4;                  // ip1指向arr的尾元素arr[4]

auto n = end(arr)-begin(arr);       // n为5,类型为ptrdiff_t带符号类型

  如果两个指针分别指向不相关的对象,则不能比较它们。

  如果p是空指针,允许给p加上或减去一个值为0的整型常量表达式。两个空指针也允许彼此相减,结果为0。


C风格字符串

cstring

  定义在cstring头文件中,cstring是C语言头文件string.h的C++版本。

  对大多数应用来说,使用标准库string要比使用C风格字符串更安全、更高效。

string s("hello world");            // string初始化
char *str = s;                      // 错误,不能用string对象初始化char*
const char *str = s.c_str();        
// 正确,返回C风格的字符串,不改变字符数组内容,改变s的值会使返回的数组失效
使用数组初始化vector对象
int int_arr[] = {0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr));
遍历多维数组

  要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

size_t cnt =0;
for(auto &row : ia)                 // for(auto row : ia )错误,自动转换成ia首元素的指针
    for(auto &col : row)            // for(auto col : row)也正确
    {
        col = cnt;
        ++cnt;
    }

for(auto p = begin(ia); p != end(ia); ++p)
    for(auto q = begin(*p); q != end(*p); ++q)
        cout << *q << ' ';


发布了99 篇原创文章 · 获赞 210 · 访问量 40万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章