第3章 字符串、向量和数组

 

3.1 命名空间的using声明

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

头文件不应该包含using声明:因为头文件中的内容会拷贝到所有引用它的文件中去,如果头文件里面有某个using声明,那么每个使用了该头文件的文件就都会有这个声明,可能引发名字冲突。

3.2 标准库类型string

3.2.1 定义和初始化string对象

//初始化string对象的方式

string s1;
string s2(s1);
string s2=s1;
string s3("value");
string s3="value";
string s4(n,‘c’);

 直接初始化和拷贝初始化

string s5 ="hiya" ;       //拷贝初始化,把等号右侧的初始值拷贝到新创建的对象中
string s6("hiya");        //直接初始化
string s7(10,'c');        //这种情况只能直接初始化。初始化的值有多个就要使用直接初始化。
string s8 = string(10,'c'); //拷贝初始化,需要显示地创建一个临时对象用于拷贝

3.2.2 string对象上的操作

读写string对象

int main(){
    string s;
    cin>>s;            //输入"  Hellow World!"
    cout<< s <<endl;    //输出 "Hellow"
    
    string s1,s2;    
    cin>>s1>>s2;        //输入"  Hellow World!"
    cout<<s1<<s2<<endl;     //输出 "HellowWorld!"
    return 0;
}

读取未知数量的string对象

int main(){
    string word;
    while (cin>>word){        //反复读取,直至到达文件末尾
        cout<< word << endl;    //逐个输出单词,每个单词后面紧跟一个换行
    }
    return 0;
}

使用getline读取一整行

int main(){
    string line;
    while(getline(cin,line))        //触发getline函数返回的是换行符,得到string对象中不包含该换
        cout<<line<<endl;           //换行符
    return 0;
}

string的empty和size操作

while(getline(cin,line))
    if(!line.empty())
        cout<< line <<endl;

while(getline(cin,line))
    if(line.size()>80)
        cout<< line <<endl;

比较string对象

每个字符串字母都一样的情况下,短的<长的

字母不一样的情况下,比较两字符串第一个不同字母的大小

字面值和string对象相加

string s1 = "hello", s2="world";
string s3 = s1+","+s2 +'\n';
//当把string对象和字符字面值混在一条语句中使用时,必须确保每个假发运算符的两侧的运算对象至少有一个string

string s4 = s1+",";            //正确:把一个string对象和一个字面值相加
string s5 = "hellow" + ",";    //错误:两个运算对象都不是string
string s6 = s1+ "," + "world"; //正确:s1+","会重新变成一个string对象
string s7 ="hellow" + "," +s2; //错误,前两个不能直接相加

3.2.3 处理string对象中的字符

使用基于范围的for语句,处理每个字符

string s("Hello World!!!");
//punct_cnt的类型和s.size的返回类型一样
decltype(s.size()) punct_cnt =0;
for(auto c:s)
    if(ispunct(c))
        ++punct_cnt;
cout<< punct_cnt << s << endl;

使用范围for语句改变字符串中的字符

string s("Hello World!!!");
//转换成大写模式
for (auto &c:s)            //必须使用引用类型才能改变原字符串中的值
    c=toupper(c);
cout<< s <<endl;

3.3 标准库类型vector

模板本身不是类和函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化。

vector<int> ivec;
vector<Sales_item> Sales_vec;
vector<vector<string>> file;        //该向量的元素是vector对象

3.3.1 定义和初始化vector对象

列表初始化vector对象

列表初始化方式的几个特殊情况:一.使用拷贝初始化时(=),只能提供一个初始值

                                                     二.如果提供的是一个类内初始值,则只能使用拷贝初始化,或者花括号初始化

                                                     三.如果提供的是初始元素值的列表,则只能把初始值都放在花括号里进行列表初始化。

vector<string> v1{"a","an","the"};        //列表初始化
vector<string> v2("a","an","the");        //错误

3.3.2 向vector对象中添加元素

string word;
vector<string> text;
while(cin >> word) {
    text.push_back(word);
}

3.3.3 其他vector操作

//关于下标必须明确一点:只能对确知存在的元素执行下标操作
vector<int> ivec;        //空对象
cout<< ivec[0];          //错误

vector<int> ivec2(10);    
cout<< ivec2[10];        //错误,越界

试图用下标的形式去访问一个不存在的元素将引发错误,不过这种错误不会被编译器发现,而是在运行时产生一个不可预知的值。

不幸的是,这种通过下标访问不存在的元素的行为非常常见,而且会产生很严重的后果。所谓的缓冲区溢出(buffer overflow)指的就是这类错误,这也是导致PC及其他设备商应用程序出现安全问题的一个重要原因。

确保下标合法的一种有效手段就是尽可能使用范围for语句

3.4 迭代器介绍

3.4.1 使用迭代器

迭代器运算符

迭代器类型

vector<int>::iterator it;
string::iterator it2;

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

const_iterator和常量指针类似,能读不能改,且若对象是一个常量只能用const_iterator

结合解引用和成员访问操作

(*it).empty()  //圆括号必不可少,先解引用,在调用结果对象的empty成员
*it.empty()    //错误,试图访问it的名为empty的成员,但it是个迭代器,没有empty成员

为简化上述表达式,it->mem和(*it).mem表达的意思相同

3.4.2迭代器运算

使用迭代器运算

auto beg = text.begin(),end=text.end();
auto mid = text.begin()+(end-beg)/2;

while(mid !=end && *mid!=sought){
    if(sought<*mid)
        end=mid;
    else
        beg=mid+1;
    mid=beg+(end-beg)/2;
}

3.5 数组

3.5.1 定义和初始化内置数组

unsigned cnt = 42;                //不是常量表达式
constexpr unsigned sz = 42;       //常量表达式
int arr[10];
int *parr[sz];                    //含有42个整型指针的数组
string bad[cnt];                  //错误:cnt不是常量表达式
string strs[get_size()];          //当get_size()是constexpr时正确;否则错误

字符数组的特殊性

char a1[]={'c','+','+'};        //3个元素,没有空字符
char a2[]={'c','+','+','\0'};    //4个元素,有显式的空字符
char a3[]="C++";                  //4个元素 自动添加表示字符串结束的空字符
const char a4[6]= "Daniel";       //错误,没有空间存放 空字符

不允许拷贝和赋值

int a[]={0,1,2};
int a2[] = a;                    //错误,不允许使用一个数组初始化另一个数组
a2=a;                            //错误,不能把一个数组直接赋值给另一个数组

理解复杂的数组声明

int *ptrs[10];                        //ptrs是含有10个整型指针的数组
                                      //这个可以用从右到左阅读

int &refs[10]=/* ? */;                //错误,不存在引用的数组

//下两个从右到左阅读不适用,应从内而外后,再从右到左
int (*Parray)[10] = &arr;             //一个指向含有10个整数的数组
int (&arrRef)[10] = arr;              //arrRef 引用一个含有10个整数的数组

int *(&arry)[10] =ptrs;               //arry 是数组的引用,该数组含有10个指针

3.5.2 访问数组元素

检查下标的值

要想防止数组下标越界,除了小心谨慎注意细节没有其他好办法。

 3.5.3 指针和数组

string nums[] = {"one","two","three"};
string *p = &nums[0];
string *p2 = nums;

int ia[]={0,1,2,3,4,5,6,7,8,9};
auto ia2(ia);            //这里 ia2是一个整型指针,指向ia的第一个元素
//类似于  auto ia2(&ia[0]); 显然ia2的类型是int*

ia2 =42;                 //错误:ia2是一个指针


decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};    //decltype(ia)返回类型是10个整数构成的数组
ia3 = p;                 //错误
ia3[4] = i;              //正确

标准库函数begin和end

C++11新标准引入了两个名为 begin和end的函数,这两个函数与容器中的两个同名成员功能类似。不过数组毕竟不是类类型,因此这两个函数不是成员函数,正确的使用形式是将数组作为它们的参数。

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

3.5.4 C风格字符串

比较字符串

//string库比较方法
string s1= “A string example”;
string s2= "A different string";
if(s1<s2)

//如果把上述操作运用到c风格字符串上,实际情况为
const char ca1[] ="A string example";
const char ca2[] ="A different string";
if(ca1<ca2)        //未定义的:试图比较两个无关地址,实际比较的是两个const char*的值
                   //即为两个地址,两个指针并非指向同一对象,所以错误。

//正确c方法
if(strcmp(ca1,ca2)<0)

目标字符串的大小由调用者指定

//string库方法连接拷贝字符串
string largeStr = s1+" "+s2;

//c正确方法
//缺陷:必须正确计算largeStr的大小,否则会产生严重错误
strcpy(largeStr,ca1);
strcat(largeStr," ");
strcat(largeStr,ca2);

一般来说使用string标准库要比使用C风格字符串更安全,更高效

3.5.5 与旧代码的接口

3.6 多维数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值