c++ primer 字符串、向量和数组--1

命名空间的using声明

  std::cin表示从标准输入中读取内容。此处使用作用域操作符(::)(参见1.2节,第7页)的含义是:编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字。因此,std::cin 的意思就是要使用命名空间std中的名字cin。
  更简单的途径是使用using 声明。

  using namespace ::name;   就可以直接使用 cin>> i;

头文件不应包含using声明

  是因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件就都会有这个声明。对于某些程序来说,由于不经意间包含了一些名字,反而可能产生始料未及的名字冲突。


标准库string

string s1;      //默认初始化,s1是一个空字符串
string s2 = s1;     // s2是s1的副本
string s3 ="hiya" ;    // s3是该字符串字面值的副本
string s4(10, 'c');    // s4的内容是cccccccccc

string对象上的操作

读取未知数量的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;return 0;

string:size_type类型

  过去,string::size_type这种类型有点儿神秘,不太容易理解和使用。在C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型:
  auto len = line.size ();// len的类型是string: :size_type
  由于size函数返回的是一个无符号整型数,因此切记,如果在表达式中混用了带符号数和无符号数将可能产生意想不到的结果(参见2.1.2节,第33页)。例如,假设n是一个具有负值的int,则表达式s.size ()<n的判断结果几乎肯定是true。这是因为负值n会自动地转换成一个比较大的无符号值。

比较string 对象

  1.如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string 对象对应位置上的字符相同,就说较短string对象小于较长 string对象。
  2.如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果。下面是string对象比较的一个示例:

  string str = "Hello";
  string phrase ="Hello World";
  string slang ="Hiya";

   根据规则1可判断,对象str小于对象phrase;根据规则2可判断,对象slang既大于str也大于phrase。slang与str或phrase第一对相异字符为'i'和'e',而'i'的字典顺序大于'e'的字典顺序。

字面值和string对象相加

  当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string:

  string s4 = s1 + ","; //正确:把一个string对象和一个字面值相加
    string s5 = "hello"+ ",";//错误:两个运算对象都不是string
  //正确:每个加法运算符都有一个运算对象是string
  string s6=s1 +"," + "world" ;
  string s7 = "hello" + "," +s2;//错误:不能把字面值直接相加

  可通过如下方式使s6合理:

  string s6=(s1 +",")+"world" ;


处理string对象中的

可以通过cctype头文件中的函数处理string中的字符

建议:使用c++版本的C标准库头文件,我们需要理清楚cname 与name.h之间的区别

  以#include<iostream>与#include<iostream.h>为例,前者需要更新的编译器。iostream.h里面定义的所有类以及对象都是在全局空间里,所以你可以直接用cout   但在iostream里面,它所定义的东西都在名字空间std里面,所以你必须加上   using namespace std才能使用cout。一般一个C++的老的带“.h”扩展名的库文件,比如iostream.h,在新标准后的标准库中都有一个不带“.h”扩展名的相对应,区别除了后者的好多改进之外,还有一点就是后者的东东都塞进了“std”名字空间中。

但唯独string特别
  问题在于C++要兼容C的标准库,而C的标准库里碰巧也已经有一个名字叫做“string.h”的头文件,包含一些常用的C字符串处理函数,比如楼主提到的strcmp。这个头文件跟C++的string类半点关系也没有,所以<string>并非<string.h>的“升级版本”,他们是毫无关系的两个头文件

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

for (declaration : expression)
    statement

  其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

string s( "Hello world!! ! ");// punct_cnt的类型和s.size的返回类型一样;
decltype(s.size()) punct_cnt = 0;//统计s中标点符号的数量
for (auto c: s)//对于s中的每个字符
    if (ispunct (c))//如果该字符是标点符号
        ++punct_cnt;//将标点符号的计数值加1
cout << punct_cnt
    <<" punctuation characters in " <<s<< endl;

   如果想要改变string对象中字符的值,必须把循环变量定义成引用类型

string s("Hello world!! !");    //转换成大写形式。
for (auto &c : s)               //对于s中的每个字符(注意:c是引用)
    c = toupper(c);             // c是一个引用,因此赋值语句将改变s中字符的值
cout<< s<< endl;

  如果想要改变string部分字符的值,必须使用下标运算符:下标范围为0到s.size()-1


标准库类型vector

  要想使用vector,必须包含适当的头文件。在后续的例子中,都将假定做了如下using声明:
  #include <vector>

  using std: : vector;

C++语言既有类模板(class template),也有函数模板,其中 vector是一个类模板。
模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。
对于类模板来说,我们通过提供一些额外信息来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。提供信息的方式总是这样:即在模板名字后面跟一对尖括号,在括号内放上信息。
以vector为例,提供的额外信息是vector内所存放对象的类型:

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

不存在包含引用的vector

初始化vector对象

vector对象复制必须是同一类型

列表初始化

  用花括号括起来的0个或多个初始元素值被赋给vector对象:

vector<string>articles = {"a", "an", "the"};

还可以用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象

vector<int> ivec(10,-1);      //10个int类型的元素,每个都被初始化为-1
vector<string> svec(10,"hi! ");  // 10个string类型的元素,
                   //每个都被初始化为"hi ! "

 如果vector对象的元素是内置类型,比如int,则元素初始值自动设为0。如果元素是某种类类型,比如string,则元素由类默认初始化:

vector<int> ivec(10);       // 10个元素,每个都初始化为0
vector<string> svec(10);    // 10个元素,每个都是空string对象

对这种初始化的方式有两个特殊限制:

  其一,有些类要求必须明确地提供初始值,如果vector对象中元素的类型不支持默认初始化,我们就必须提供初始的元素值。对这种类型的对象来说,只提供元素的数量而不设定初始值无法完成初始化工作。

  其二,如果只提供了元素的数量而没有设定初始值,只能使用直接初始化:
  vector<int> vi = 10;//错误:必须使用直接初始化的形式指定向量大小

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对象的vector对象,应该提供能赋给string对象的初值。此时不难区分到底是要列表初始化vector对象的元素还是用给定的容量值来构造vector对象:

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

  要想列表初始,花括号里的值必须与元素类型相同。确认无法执行列表初始化后,编译器慧尝试用默认值初始化vector对象。

posted @ 2022-08-18 18:39  ahab1016  阅读(11)  评论(0)    收藏  举报