C++ Primer 3章 字符串、向量和数组

叁歲伎倆 2022-06-09 02:12 641阅读 0赞
  • using声明既可以直接声明命名空间 using namespace std,也可以声明命名空间里面的某个方法using std::cin。

标准库类型string

  1. //使用=初始化一个变量,是拷贝初始化,编译器把等号右侧初始值拷贝到新对象中去。
  2. string s0;//s0默认为空字符。
  3. string s3 = "value";//s3是"value"的副本,出字面值最后一个空字符外。
  4. string s4 = string(10,'c');//拷贝初始化。
  5. //不使用等号的就是直接初始化
  6. string s6("value");//和s3一样
  7. string s7(10 , 'c');//s7是10个c
  8. int main()
  9. {
  10. string s;
  11. cin >> s;
  12. cout << s << endl;
  13. exit(0);
  14. }
  15. //先输入一段字符串,string对象自动忽略开头的空白并从第一个字符串读到下一处空白为止。先输入然后enter开始读取。

hello world
hello
按 来关闭窗口…

  1. int main()
  2. {
  3. string s;
  4. string s1;
  5. while( getline(cin , s1) )
  6. cout << s1 << endl;
  7. exit(0);
  8. }
  9. //从输入流读取内容,直到碰到换行符(enter),换行符也读进来了存到s1中(不存输入流中的换行符)。返回流参数,所以可以判断用。
  10. 因为s1不存在换行符,所以手动加endl换行并刷新缓冲区。
  • 比较string对象利用字典顺序:直接从第一字符的开始比较,直到碰到第一个大小不一样的字符为止。
  • 两个string对象相加,直接将对应的字符连在一起赋值给新的string对象。
  • string对象和字符字面值相加必须确保+号两侧至少有一个string对象。string s6 = (s1 + “, “) + “world”;正确。因为字符串字面值并不是string对象,为了和C语言兼容。其实加号暗中含有强制转换。

    /* @file include/cctype This is a Standard C++ Library file. You should @c #include this file in your programs, rather than any of the @a .h implementation files. This is the C++ version of the Standard C Library header @c ctype.h, and its contents are (mostly) the same as that header, but are all contained in the namespace @c std (except for names which are defined as macros in C). /
    //
    // ISO C++ 14882:
    //

    pragma GCC system_header

    include

    include //这里直接包含C库文件

    ifndef _GLIBCXX_CCTYPE

    define _GLIBCXX_CCTYPE 1

    // Get rid of those macros defined in in lieu of real functions.

    undef isalnum

    undef isalpha

    undef iscntrl

    undef isdigit

    undef isgraph

    undef islower

    undef isprint

    undef ispunct

    undef isspace

    undef isupper

    undef isxdigit

    undef tolower

    undef toupper

    namespace std//然后将c库文件里面函数封装到一个命令空间里面去,这就是c库和对应c++库的不同之处。
    {
    using ::isalnum;
    using ::isalpha;
    using ::iscntrl;
    using ::isdigit;
    using ::isgraph;
    using ::islower;
    using ::isprint;
    using ::ispunct;
    using ::isspace;
    using ::isupper;
    using ::isxdigit;
    using ::tolower;
    using ::toupper;
    } // namespace std

    if __cplusplus >= 201103L

    ifdef _GLIBCXX_USE_C99_CTYPE_TR1

    undef isblank

    namespace std
    {
    using ::isblank;
    } // namespace std

    endif // _GLIBCXX_USE_C99_CTYPE_TR1

    endif // C++11

    endif

  • c语言头文件在c++中修改了一点:其一去掉了.h并且在头文件名字前面加了c;其二c++在里面加入了命名空间。所以ctype.h和cctype里面的内容差不多。
    在C++中使用c库应该尽量使用cctype的形式,因为这样不用牢记哪些是从c语言里面继承过来,哪些又是c++独有。因为它的头文件把独有和继承放在了一些,你可以直接使用了,没有关系哦。
    例如我要利用Linux系统函数进行socket编程,我可以在包含linux头文件的基础上,自己利用它的系统调用写一个类,这是没有问题的。很方便,很有用。这就是c++和c语言的强大之处。两者分界线不明显。再次论证c++是c的超集。

    int main()
    {

    1. string s("string World!!!");
    2. cout << s << endl;
    3. decltype(s.size()) count = 0;//返回值的类型定义变量
    4. for(auto &c : s){

    //对于s中的每一个字符,执行下面的某种操作

    1. if(ispunct(c))//实际运用了c标准库
    2. count++;
    3. c = toupper(c);//c是引用,改变string字符,必须使用引用.
    4. }
    5. cout << count << endl;
    6. cout << s << endl;
    7. if(!s.empty())//使用下标索引修改单个字符,类里面定义了该方法.
    8. s[0] = 'W';//下标类型是string::size_type类型.就是无符号整形,为了排除在不同机器上面的差异
    9. cout << s << endl;
    10. for(decltype(s.size()) index = 0 ; index < s.size() && s[index] != ' ';index++)
    11. s[index] = '1'; //使用迭代器修改字符
    12. cout << s << endl;
    13. exit(0);

    }

  • c++11新标准中定义了使用for循环处理每一个字符,每次迭代c被初始化为s的下一个元素值。decltype的作用在上一章已经说明清楚了,利用返回值定义类型。

  • 要for循环改变string对象字符值而不用迭代的方法,必须利用引用。
  • s.size()的类型是string::size_type类型,这是string类里面自定义类型为什么需要有这种类型呢?而不去直接使用int、long等等呢?大多数标准库或者Linux内核里面都定义了几种配套的类型,这些配套的类型与具体机器就无关了。例如我存储一些数值只需要4字节,少了存储不够,多了浪费空间。假如我直接用了int定义,那么可能不同机器上面int所代表的字节不一样。这样定义之后,所有变量定义都需要重新更改成4字节类型。但是我可以直接typedef new_type int;然后用new_type去定义需要4字节的变量,不同机器上面只需要去修改int成相应的类型即可,其他用new_type定义的类型不用更改,屏蔽了不同机器上面的差异。这种方法在Linux内核,以及标准库里面大量用到,应该清楚明白。。
  • 变量应该在使用的地方再定义。迭代必须注意下标的上限是多少。

标准库类型vector

  • vector表示对象集合,所有对象类型相同,也称为容器。可以容纳其他对象,将在后面详细介绍,现在学会用用即可。vector输入类模版。实例化的时候必须提供何种类型。vector v;进行定义。

    //列表初始化,初始化几个元素向量,利用 { }
    vector str = {
    “a” , “an” , “the”};//3个元素列表初始化
    vector str1{
    “a” , “an” , “the”};//3个

    vector in = {
    1 , 2 , 3};//3个元素列表初始化
    vector in{
    1 , 2 , 3};//3个元素列表初始化

    //指定数量初始化,初始化指定个数(必须有个数)向量,利用 ( )
    vector s(10 , “hi”);//10个string,指定数量初始化
    vector s(10);//10个string默认为空,指定数量初始化

    vector in(10 , -1);//10个int类型每个是1,指定数量初始化
    vector in(10 );//10个int类型每个是0,指定数量初始化

    //两种特殊的情况,由编译器根据实际情况选择初始化。
    vector v{
    10};//执行指定数量初始化10个string默认为空,指定数量初始化
    vector v{
    10 , “hi”};//执行指定数量初始化10个string默认为空,指定数量初始化,因为有一个整形。

  • c++11给vector提供了两种初始化方法,其一是列表初始化,其二就是指定数量初始化。

    int main(void)
    {

    1. vector<int> Vnumber;
    2. int num;
    3. while (cin >> num) {
    4. Vnumber.push_back(num);
    5. }
    6. for(decltype(Vnumber.size()) index = 0 ; index < Vnumber.size();index++ )
    7. cout << Vnumber[index] + Vnumber[Vnumber.size() - 1 - index] << endl;
    8. exit(0);

    }

  • 下标索引问题:下标运算符只能访问已经存在的元素并且访问不能越界。不能用于添加元素,添加应该用push_back方法

解释为什么可以列表初始化?

这是C++11最新的特性,

  1. //以前初始化
  2. vector<int> in;
  3. in.pushback(1);
  4. in.pushback(2);
  5. in.pushback(3);
  6. //现在
  7. vector<int> in = {
  8. 1 , 2 , 3};//3个元素列表初始化
  9. vector<int> in{
  10. 1 , 2 , 3};//3个元素列表初始化
  11. //这两种形式是等效的。

C++11允许构造函数和其他函数把初始化列表(通过关键字initializer_list传参)当做参数使用。这时候,只要定义对象的时候,初始化通过{},那么就会调用含有initializer_list关键字的构造函数。这就是为什么支持初始化列表。我们需要些支持初始化列表的类,只需要写一个对应的构造函数即可。

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. class Mynumber{
  5. public:
  6. Mynumber(initializer_list<int> ii){
  7. for(auto i = ii.begin() ; i != ii.end() ; i++)
  8. mynumber.push_back(*i);
  9. }
  10. Mynumber(int i){
  11. mynumber.push_back(i);
  12. }
  13. void Myprint(){
  14. for(int i = 0 ; i < mynumber.size() ; i++)
  15. cout << mynumber[i] << " ";
  16. cout << endl;
  17. }
  18. private:
  19. vector<int> mynumber;
  20. };
  21. int main(void)
  22. {
  23. Mynumber a0{
  24. 1,2,3,4,5,6};//调用第一个列表初始化构造函数
  25. Mynumber a1(1);//调用第二个构造函数
  26. a0.Myprint();//1 2 3 4 5 6
  27. a1.Myprint();//1
  28. }

迭代器介绍

begin负责返回指向第一个元素。
end成员负责返回尾元素的下一个位置,返回的迭代器称为尾后迭代器。
如果容器为空,那么begin和end返回同一个迭代器,也是尾后迭代器。

数组

这里内容和c语言里面就是一样的。但是有几种理解复杂数组的方法必须始终明白清楚。以前在c语言书籍里面没看清楚。

c语言运算符对应优先级:
这里写图片描述

  • 结合性只有在同优先级运算符下才生效。
  • 关系运算符优先级高于逻辑运算符。
  1. //理解复杂数组声明
  2. //方法:运算符优先级+从从内到外,从右到左依次结合理解。
  3. //理解数组声明,最好就是从数组名字开始从内向外顺序阅读。
  4. int *ptrs[10];//首先定义大小为10的数组,名字是ptrs,然后指向int *的指针,因此称之为指针数组。
  5. int (*ptr)[10];//首先括号里面ptr是一个指针,然后右边既指针指向维度10的数组,然后左边,明白数组里面是int类型。ptr是指向数组(存放int 维度10)的指针。
  6. int main(void)
  7. {
  8. char *ptr[] = {
  9. "Hello" , "World" ,"c++"};//指针数组
  10. char **beg = begin(ptr);//注意只用begin的时候,前面变量和后面变量类型应该匹配,ptr[1]里面存放的是指针,那么ptr实际上等效于指向指针的指针,因此双重效果。
  11. char **last = end(ptr);//这里c语言基础知识扎实才可以理解。
  12. while(beg != last){
  13. cout << *beg << endl;
  14. beg++;
  15. }
  16. exit(0);
  17. }
  • c++11标准引入了两个名为begin和end的函数。begin返回数组首元素的指针,end返回尾部元素下一个位置的指针,因此可以利用 !=来进行判断,和迭代器功能类似,但是end返回的指针不能解引用和递增操作,仅仅适合判断。
  • 注意此处指针的指针的使用方法。
  • 数组访问如a[1]等效于(a+1),仅仅为了方便,才弄成a[1]的形式而已计算机处理的是(a+1)。
  • 因为有上面那条结论,所以p[-1],p[-2]这种形式可能是合法的,只要没有越过数组前后界限。

c库字符串函数和c++string对象的区别:

多维数组

未完待续……

发表评论

表情:
评论列表 (有 0 条评论,641人围观)

还没有评论,来说两句吧...

相关阅读