C++ Primer学习心得第三章

来源:互联网 发布:梅县东山中学 知乎 编辑:程序博客网 时间:2024/05/22 22:44

内容:

3.1  命名空间的using声明

3.2  标准库类型string

3.3  标准库类型vector

3.4  迭代器介绍

3.5  数组

3.6  多维数组


3.1命名空间的using声明


1.可参考之前我写过的一些总结:点击打开链接

2.在头文件(*.h或者*.hpp)中不要使用using语句。因为如果头文件中使用了using语句后,每个include它的源文件中都默认包含了这个using,那么可能会与这个源文件中的变量名或者其他的头文件中的变量名产生冲突。


3.2标准库类型string

首先要明确类型是类类型,意味着它有构造函数,也类似我们自定义的类一样的其他类对象。

    它有几种初始化方式如下

    string s1 ;  // 调用默认构造函数初始化对象

    strng s2(s1) ; // 将S2初始化为S1的一个副本

    string s3("value") ; // 用一个字符串值初始化对象

    string s4(n,'c') ; // 用N个字符‘C’组成字符串作为初始化s4的值

    特别要注意的是第一种初始化方式,虽然默认构造函数是没有参数的但是不能因此就写成 string s1()

/*************************************************************************     > File Name: string.cc     > Author: miaobeihai     > Mail: 452686191@qq.com      > Created Time: Tue 27 Jun 2017 08:42:36 AM PDT  ************************************************************************/    #include<iostream>  #include<string>    using std::cout;  using std::cin;  using std::endl;  using std::string;    int main(){      //生产一个空字符串string s();      string zero;      cout<<zero<<endl;      //拷贝构造函数 string (const string &str)      string first("adadadadadI");      cout <<first<<endl;      //n个元素,每个元素都是字母C string(size _type n,char C);      string two(10,'M');      cout <<two<<endl;      string three(first);      cout <<three<<endl;      //复制,n开始,复制m位,当m省略时就复制到结尾string(const string &str,size_type n.size_type m)      string  three1(first,3,2);      cout <<three1<<endl;      string four(&first[3],&first[5]);      cout <<four<<endl;      return 0;    }  

上文说过string是类类型所有有很多类成员(属性和成员函数),下面就是一些常用的操作

    s.empty() ;   // 判断s是否为空,相当于s.size()==0
    s.size() ;       // s的长度
    s[n] ;            // n位置的字符(左值返回)
    s1+s2 ;         // 返回s1和s2连接的串
    s1=s2 ;         // 把s1替换为s2的副本
    s1==s2 ;      // 判断s1,s2是否相等
    !=,<,<=,>,>=    // 按字典顺序比较    

    s.insert(...) ;  // 插入字符操作,有多个重载可用

 

    s.size()函数返回一个表示字符串长度大小的值,其类型并不是我们认为的int类型,而是一个叫string::size_type的类型,为什么不用INT而新创造一个类型呢,原因有如下两点:

    1. 取值范围不同,int有固定的取值范围,并且可以取负数,但字符串长度是不可能为负的,并且长度的大小很可能会超过int的范围而导致溢出

    2. int的范围大小与机器相关,有的机器上范围大些,有点机器小一些。但是字符串长度应该是个不能随机器发生大小改变的值,所以即使用无符号int来表示串大小也是不合适的

( c语言的头文件形如name.h,C++则将这些文件命名为cname。也就是去掉.h的后缀,而在文件名前name添加c。因此,cctype头文件和ctype.h头文件的内容是一样的,只不过前者更符合C++的命名规范)

cctype 头文件所包含的函数主要用来测试字符值,以下是一个列表,但是对于初学者来说自己上机操作一下,后两个返回的是int型,确实很意外,强制转换一下,很简单。    

    isalnum(c) ; // 假如c是字母或数字,则为true

    isalpah(c) ;  // 假如c是字母,则为true

    iscntrl(c) ;   // 假如c是控制字符,则为true

    isdigit(c) ;   // 假如c是数字,则为true

    isgraph(c) ; // 假如c不是空格,则为true

    islower(c) ; // 假如c是小写字母,则为true

    isprint(c) ;  // 假如c是可打印的字符,则为true

    ispunct(c) ; // 假如c是标点符号,则为true

    isspace(c) ; // 假如c是空白字符,则为true

    isupper(c) ; // 假如c是大写字母,则为true

    isxdigit(c) ; // 假如c是十六进制数,则为true

    tolower(c) ; // 假如c是大写字母,则返回小写字母形式(对应的int值),否则返回c。

    toupper(c) ;// 假如c是小写字母,则返回大些字母形式(对应的int值),,否则返回c。

 

    可以举个简单的例子

string line("a1 B,");

isalpah(line[0]); // true

isdigit(line[1]); // true
ispunct(line[4]); // true
tolower(line[0]); // 返回A对应的ascii 65

3.3 标准库类型Vector

 C++标准库容器有好几类,后面会详细介绍。为什么在这里单单要先介绍vector容器呢?这个容器最常用。对于大部分应用来说用它足以满足你的要求。

    vector是个类模板,如果你了解JAVA或C#范型编程的话可以理解为范型类。泛型最大的好处是只需定义一个类或函数就可以提供不同类型版本的操作。

    它的初始化有如下几种方式:

    vector<T> v1 ;         // 默认构造函数v1为空
    vector<T> v2(v1) ;  // v2是v1的一个副本 
    vector<T> v3(n, i) ; // v3包含n个值为i的元素 参数 T 如果是类类型则一定要有拷贝构造函数(未定义的情况下系统会自动分配一个) 
    Vector<T> v4(n) ;    // v4含值初始化的元素个副本 参数 T 如果是类类型则一定要有默认构造函数(未定义的情况下系统会自动分配一个) 如果是内                                             置类型则分配n个0

    vector<T> v5{a,b,c.....} //注意,这里使用的括号是{},而非(),当然字符串要加双引号,vector<string> v5{"a","an","the"};

    vector<T> v6={a,b,c....}//等价于v5{a,b,c...}

    对于类类型如果不能满足红色标示的要求编译会失败。 关于类类型的拷贝构造函数和默认构造函数后续章节有介绍

 

    Vector对象有几种最重要的操作

    v.push_back(t) ;          // 在数组的最后添加一个值为t的数据
    v.size() ;                      // 当前使用数据的大小 返回vector<T>::size_type类型的长度值,其意义类似上面讲过的string::size_type
    v.empty() ;                  // 判断vector是否为空
    v[n] ;                           // 返回v中位置为n的元素 和string类型下标操作类似 是个左值操作
    v1=v2 ;                        // 把v1的元素替换为v2元素的副本
    v1==v2 ;                     // 判断v1与v2是否相等
    !=、<、<=、>、>= ;  // 保持这些操作符惯有含义

c++11中修正了>号在声明STL对象时的bug。

[cpp] view plain copy
  1. vector<vector<int> > ivv; //旧版,两个>之间必须有空格,否则编译错误  
  2. vector<vector<int>> ivv;  //c++11版,不需要在两个>之间加入空格  
注意string对象和vector的size()函数返回的是size_type类型,这是个与机器无关的无符号类型(用int型接着小心溢出...),嫌string::size_type或者vector<type>::size_type写起来麻烦的可以使用auto或者decltype来偷懒。

vector <string> v5{"a","an","the"}for(auto i :v5)    cout<<i <<" ";

使用STL的泛型编程时应该注意,使用迭代器遍历一个序列时,循环中判断是否越界要使用!=,而不要使用<或者>。这是因为所有的标准库容器都定义了==和!=,而只有少数的容器定义了<和>。

[cpp] view plain copy
  1. for(auto it = s.begin(); it!=s.end(); it++)  
  2.     //do sth  
不能通过数组下标来添加元素

vecotr<int> ivec;for(decltype(ivec.size()) ix=0;ix!=10;i++)   //ivec[ix] =ix;//严重错误   ivec.push_back(ix);//正确

只能对确知已存在的元素执行下标操作

vector<int> ivec;cout<<ivec[0];//错误,ivec中不包含任何元素vector<int> ivec2(10);cout<<ivec2[0];//错误,数组下标为0到9

不幸的是这种通过下标访问不存在的元素的行为非常常见,而且会产生很严重的后果。所谓的缓冲区溢出(buffer overflow)指的就是这种错误.




3.4 迭代器的介绍

迭代器的类型: iterator和const_iterator

vector<int>::iterator it;//it能读写vector中的元素string::iterator it2;//it2能读写vector中的元素vector<int>::const_iterator it3;//只能读不能写string::const_iterator it4;//只能读不能写

在容器中,存在一个const_iterator的迭代器,对这个跌迭代器使用*号(解引用)将返回一个const对象。也就说使用const_iterator只能只读的访问容器中的数据,但是const_iterator本身可以修改。注意:const对象的begin()和end()会返回const_iterator,而非const对象的begin()和end()只会返回普通iterator。另外,在c++11中,为了使用方便,定义了两个新的函数cbegin()和cend(),无论对象是否const都返回const_iterator。

[cpp] view plain copy
  1. string sa = "abc";  
  2. const string sb = "def";  
  3. auto it1 = sa.begin(); //普通iterator  
  4. auto it2 = sb.begin(); //const_iterator  
  5. auto it3 = sa.cbegin(); //const_iterator  


3.5 数组

一些复杂数组的声明

int num[10];//10个整型元素的数组int *num[10]; //10个指针元素的数组int (*a)[10] =#//a是数组的指针,该数组有10个整型号元素int *(*b)[10]=&num1;//b是数组的指针,该数组有10个整型的指针int (&c)[10]=num;//c是数组的引用,数组有10个整型的元素int *(&d)[10]=num1;//d是数组的引用,该数组有10个整型的指针


标准库函数的begin和end

c++11新标准引入了两个名为begin和end的函数

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


C风格字符串:

cstring是c语言头文件string.h 的c++版本 ,常见的函数有:

strlen(p) ;

strcmp(p1,p2);

strcat(p1,p2);

strcpy(p1,p2);


传入此类函数的指针必须指向以空字符串作为结束的数组

char ca[]={'c','+','+'};

cout <<strlen(ca)<<endl; //严重错误:ca没有以空字符串结束

string s1(10,'c'); 
cout << s1.size() << " "; //安全可靠

当然还有其他函数也一样,对于大多数应用来说,使用string要比使用c风格字符串更安全,更高效


混用string对象和C风格字符串

如果一个程序某处需要一个C风格的字符串,无法直接用string对象代替它,string专门提供了一个c_str的成员函数

string s("Hello World");char *str =s;//错误,不能用string对象初始化char* const char *str =s.c_str(); //正确

3.6 多维数组

c++语言中没有多维数组,只有数组的数组。

[cpp] view plain copy
  1. int ia[3][4]; //ia是一个大小为3的数组,其中每个元素都是大小为4的int型数组  
范围for与多维数组:通常我们使用下标和多重循环遍历多维数组,但是当我们使用c++11中新增加的范围for的时候,必须声明每一层循环时的对象类型。使用auto时数组名会被自动解析为指针类型,这会导致内层循环无法进行下去(指针不是序列,无法应用范围for)。这时我们需要将除了最内层循环之外的每一层循环的对象类型声明为auto &,就可以避免数组名会被自动解析为指针类型,让内层循环正确运行。
[cpp] view plain copy
  1. int matrix[2][3] = {1,2,3,4,5,6};  
  2. for (auto row : matrix)  
  3.     for (auto element : row) //编译错误,row是指针无法范围for  
  4.         //do sth  
  5. //改为:  
  6. for (auto &row : matrix)  
  7.     for (auto element : row) //ok,row是int [3]  
  8.         //do sth  
多维数组的数组名会被转化为第一层数组首元素的指针(这是15和19加在一起的推论),我们可以使用auto来简化它:
[cpp] view plain copy
  1. int ia[2][3][4];  
  2. int (*p)[3][4] = ia; //转化首元素指针  
  3. auto *p = ia;         //方便了,但是容易记不清类型  

原创粉丝点击