3.5.2  字符串类型

使用char类型的变量我们可以表示单个字符,那么,我们又该如何表示拥有多个字符的字符串呢? 我们注意到,一个字符串是由多个字符串连起来形成的。很自然地,一种最简单直接的方法就是利用数组(一种数据组织管理方式,它将多个相同类型的数据元素组织起来,形成一个数据序列以便于访问。更多可以参考后文3.6小节对数组的介绍)来保存一个字符串中的各个字符,最后用一个特殊字符‘\0’表示字符串的结束,以此来将多个char类型的字符数据串联成字符串。例如:

  1. // 定义一个字符数组,用以保存字符串
  2. char msg[];
  3. // 将各个字符依次保存到数组相应位置上
  4. msg[] = 'G'; // 第一个字符,保存到数组的第一个位置,后面的以此类推
  5. msg[] = 'o';
  6. msg[] = 'o';
  7. msg[] = 'd';
  8. // 在最后的位置保存一个‘\0’,表示字符串结束
  9. msg[] = '\0';
  10. // 输出msg数组中的字符串
  11. cout<<msg<<endl;

用字符数组表示字符串的方式虽然简单可行,可是却有诸多使用上的不便。在C++中,我们更多地是用STL(Standard Template Library,标准模板库,是利用模板技术实现的一套函数库,其中提供了一些常用的容器和算法以便于对数据进行处理。这里的字符串类型 string就是由它定义的一种用以表示字符串的数据类型。关于STL,在稍后的第8章中我们还将详细介绍)中定义好的字符串类型string来表示字符串。这种字符串类型实质上是对一个char类型字符数组的包装,在保存字符串数据的同时还增加了一些对字符串的常用操作,比如获取字符串长度,查找特定字符等,这样字符串的处理就方便多了。就像char和wchar_t相互对应处理不同范围的字符一样,与string对应的,C++还提供了可以处理wchar_t类型字符的wstring字符串类型。例如:

  1. #include <iostream>
  2. #include <string> // string类型所在的头文件
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. // 定义一个string类型变量,表示英文字符串
  9. string strEn = "Good Morning!";
  10. // 定义一个wstring类型变量,表示中文字符串
  11. wstring strChs = L"陕A-82103";
  12. // 用cout输出string类型字符串
  13. // 用wcout输出wstring类型字符串
  14. cout<<strEn;
  15.  
  16. wcout.imbue( locale ( "chs" ) ); // 设置区域
  17.  
  18. wcout<<strChs<<endl;
  19.  
  20. return ;
  21. }

可以看到,因为string和wstring编码方式的不同,不同的字符串在输出的时候需要采用不同的方式。对于wstring类型的字符串变量,在输出的时候需要使用wcout对象,并且需要用imbue()函数设定字符的编码方式。另外,这里值得指出的一点是,英文字符串不仅可以用string类型表示,也可以使用wstring类型表示,而中文字符串则只能使用wstring类型表示。

string类型不仅包装了字符数组,可以存储字符串中的各个字符,同时还提供了很多跟字符串相关的操作,例如,可以获得一个字符串的长度,或者在字符串中查找某个字符等等,极大地方便了对字符串的处理。例如:

  1. // 定义一个字符串变量,用于保存用户输入的用户名
  2. string strName = "";
  3.  
  4. cout<<"请输入用户名:"<<endl;
  5. // 获取用户输入的字符串,并保存到strName变量
  6. cin>>strName;
  7. // 通过string类型的length()函数获取strName的长度
  8. // 并判断其长度是否小于6(字符串中是否少于6个字符)
  9. if(strName.length() < )
  10. {
  11. // 如果小于,则进行错误提示
  12. cout<<"错误:用户名至少包含6个字符"<<endl;
  13. }

在上面这段代码中,我们首先定义了一个string类型的变量strName,用以保存用户输入的用户名字符串,然后用cin获取用户输入的字符串并保存到strName变量。接着再利用string类型的length()函数获取字符串的长度,也就是strName中的字符个数。最后用if条件结构将其与我们要求的字符串长度6进行比较,如果不符合条件则进行错误提示。

知道更多:auto类型变量——根据初始值推断真实的数据类型

在前面的章节中,我们介绍了C++中的多种数据类型:有表示整数的int类型也有表示浮点数的float类型;有表示单个字符的char类型也有表示字符串的string类型,这些意义不同用途各异的数据类型为我们定义变量来表示现实世界中的数据提供了丰富的选择。但是,这些数据类型在使用上有一个共同的要求,那就是在定义变量表示数据时,我们必须先要知道所要表示的数据是什么类型,到底是一个小数呢还是一串字符,然后才能据此确定到底是该使用float呢还是string。可是在开发实践中,有时候我们并不能非常容易地确定一个变量应该具有的数据类型。比如,将某个复杂表达式作为初始值赋值给一个新定义的变量时,我们往往很难确定这个表达式的数据类型,从而无法确定变量应有的数据类型。为了解决这个问题,C++11为我们提供了auto关键字,使用它作为某个变量定义的数据类型,编译器会根据这个变量的初始值,自动推断出这个变量合理的数据类型而无需我们人为指定。例如:

  1. auto x = ; // 使用整数7对变量x进行初始化,x被推断为int类型
  2. auto y = 1.982; // 使用浮点数1.982对变量y进行初始化,y被推断为double类型
  3.  
  4. Handler GetHandler();
  5. // 使用GetHandler()函数的返回值对变量handler进行初始化
  6. // handler被推断为Handler类型
  7. auto handler = GetHandler();

这里我们在定义变量x的时候,并没有指定其具体的数据类型,而是使用auto做为代替。这样,编译器在编译这段代码时,会根据7这个初始值自动推断x的实际数据类型为int。同样的道理,使用浮点数1.982进行初始化的变量y会被编译器自动推断为double类型;而最后的一个变量handler会被初始化为GetHandler()函数的返回值类型Handler。虽然auto关键字会根据初始值自动推断变量的数据类型,但是,它的使用并不需要花费额外的编译时间。有好处而又没有额外的花费,auto关键字就像商场的免费大赠送,这样的大便宜谁不喜欢呢?

实际上,可以把auto关键字看成是一个变量定义中的数据类型占位符,它占据了原来应该是具体数据类型的位置。而在编译的时候,编译器会根据这个变量的初始值,推断出这个变量应有的具体数据类型,然后替换掉auto关键字,就成为一个普通的带有具体数据类型的变量定义了。用auto关键字定义变量的形式跟一般的定义变量的形式并无二异,唯一的差别之处在于,用auto关键字定义变量时,变量必须有初始值:

  1. auto 变量名 = 初始值表达式; // 赋值形式
  2. // 或
  3. auto 变量名{初始值表达式}; // 初始化列表形式

这样,这个初始值表达式计算结果的数据类型将被编译器推断为变量的数据类型。

通常在定义变量时,如果我们很难准确地推断它的数据类型,或者是这个变量的数据类型难于书写,就可以使用auto作为变量的数据类型来定义变量,而真正的数据类型就交由编译器去根据变量的初始值推断得到好了。做这种苦力活,电脑要比人脑快多了。这样做不仅省去了我们自己推断数据类型的麻烦,避免了可能的人为错误,同时也可以达到简化代码的目的。例如:

  1. template <typename T>
  2. // 数据类型vector<T>之后的“&”符号,表示其后所定义的变量是一个引用
  3. // 引用是C++中一种访问数据的特殊方式,在稍后的7.1小节中我们将详细介绍
  4. void printall(const vector<T>& v)
  5. {
  6. // 根据v.begin()的返回值类型自动推断变量it的数据类型
  7. for (auto it = v.begin(); it != v.end(); ++it)
  8. cout << *it << endl;
  9. }

为了表示同样的意义,如果没有auto关键字帮忙,我们不得不写成下面这种繁琐的形式:

  1. template <typename T>
  2. void printall(const vector<T>& v)
  3. {
  4. for (typename vector<T>::const_iterator it = v.begin();
  5. it != v.end(); ++it)
  6. cout << *it << endl;
  7. }

除了简化代码之外,auto关键字有时候甚至能够帮助我们完成一些在C++11之前不可能完成的任务,成为一种必需。比如,在模板函数中,当一个变量的数据类型依赖于模板参数时,如果不使用auto关键字,将根本无法确定变量的数据类型,因为我们根本无法提前预知用户使用何种数据类型作为模板参数来调用这个模板函数,从而也就无法确定这个变量的数据类型。但是使用auto关键字之后,一切难题都将迎刃而解。例如:

  1. template <typename T,typename U>
  2. void mul(const T& t,const U& u)
  3. {
  4. // ...
  5. // 用auto关键字做数据类型,编译器将根据u和t的实际数据类型,
  6. // 自动推断变量tmp的数据类型
  7. auto tmp = t*u;
  8.  
  9. // ...
  10. }

在这里,变量tmp的数据类型应该与模板参数T和U相乘结果的数据类型相同,也就是依赖于T和U的数据类型。对于程序员来说,在编写这个模板函数的时候,模板参数T和U的类型尚未确定,这样变量tmp的类型也就无法确定。所以,我们用auto关键字作为占位符,占据数据类型的位置,而真正的数据类型,则留待编译器在最终编译的时候,根据具体给定的模板参数T和U的类型而推断得到。这样,就把一件原来不可能的事情变成了可能。

使用auto关键字,可以根据变量的初始值自动推断其数据类型,这样就极大地方便了复杂数据类型变量的定义。但是,这种方式好是好,却有一个缺点,那就是每次推断得到的数据类型只能在定义变量的时候使用一次,无法保留下来继续使用。好不容易推断得到的数据类型只能使用一次,这就显得有点不够低碳环保了。而有时候,我们也需要这个推断得到的数据能够保留下来,从而可以重复使用以定义相同类型的多个变量。为了弥补这个缺点,C++11还提供了一个decltype关键字。它的使用语法形式如下:

  1. typedef decltype(表达式) 用户数据类型;

其中,decltype(表达式)是这个表达式的推断数据类型(declared type),也就是这个表达式计算结果的数据类型。而typedef则是将这个数据类型定义为用户自定义的数据类型,换句话说,也就是为这个推断数据类型取一个名字,从而可以把它作为一个新的数据类型,用在定义变量、创建对象等任何需要数据类型的地方。例如,我们可以用decltype关键字改写上面的例子:

  1. template <typename T,typename U>
  2. void mul(const T& t,const U& u)
  3. {
  4. // ...
  5. // 用decltype得到t*u的数据类型,
  6. // 并用typedef关键字将其定义成一个新的数据类型M
  7. typedef decltype(t*u) M;
  8. // 用这个新的数据类型M定义指针变量(表示变量或函数地址的变量),创建M类型对象
  9. M* tmp = nullptr;
  10. tmp = new M; // ...
  11. }

auto和decltype的作用有些相似,都可以推断某个表达式的具体数据类型。但是,两者的使用还是稍有差别。如果我们仅仅是想根据初始值确定一个变量合适的数据类型,那么auto是最佳人选。而只有当我们需要推断某个表达式的数据类型,并将其作为一种新的数据类型重复使用(比如,定义多个相同类型变量)或者单独使用(比如,作为函数的返回值类型)时,我们才真正需要用到decltype。


[Chen1]确认

你好,C++(11)如何用string数据类型表示一串文字?根据初始值自动推断数据类型的auto关键字(C++ 11)的更多相关文章

  1. java 八种基本数据类型之初始值、取值范围、对应的封装类

      CreateTime--2017年12月6日10:03:53 Author:Marydon 一.java数据类型之基本数据类型 (二)八种基本数据类型的特征 import java.math.Bi ...

  2. 【C++11新特性】 auto关键字

    原文链接: http://blog.csdn.net/xiejingfa/article/details/50469045 熟悉脚本语言的人都知道,很多脚本语言都引入了“类型自动推断”技术:比如pyt ...

  3. C++ 11 auto关键字

    熟悉脚本语言的人都知道,很多脚本语言都引入了“类型自动推断”技术:比如Python,可以直接声明变量,在运行时进行类型检查.随着C++11标准的发布,C++语言也引入了类型自动推断的功能,这就是我们今 ...

  4. C++11之右值引用(三):使用C++11编写string类以及“异常安全”的=运算符

    前面两节,说明了右值引用和它的作用.下面通过一个string类的编写,来说明右值引用的使用. 相对于C++98,主要是多了移动构造函数和移动赋值运算符. 先给出一个简要的声明: class Strin ...

  5. C++11特性:auto关键字

    前言 本文的内容已经不新鲜了.关于auto,翻来覆去被人知道的都是这些东西,本文并没有提出新颖的auto用法. 本人原是痛恨博客一篇篇都是copy而来缺乏新意的探索,当然,本文不是copy而来,但发布 ...

  6. C++11 - 类型推导auto关键字

    在C++11中,auto关键字被作为类型自动类型推导关键字 (1)基本用法 C++98:类型 变量名 = 初值;   int i = 10; C++11:auto 变量名 = 初值;  auto i ...

  7. 【校招面试 之 C/C++】第30题 C++ 11新特性(一)之auto关键字

    1.自动类型推断 auto自动类型推断,用于从初始化表达式中推断出变量的数据类型.通过auto的自动类型推断,可以大大简化我们的编程工作.下面是一些使用auto的例子. #include <ve ...

  8. C++11中的tuple应用:让函数返回多个值

    在没有tuple之前,如果函数需要返回多个值,则必须定义一个结构体,有了C++11,可以基于tuple直接做了,下面是个示例: // 编译:g++ -std=c++11 -g -o x x.cpp # ...

  9. Java 数据类型和初始值

    Java 基本数据类型 byte.short.int.long.float.double.boolean.char byte 数据类型是8位.有符号的,以二进制补码表示的整数 short 数据类型是 ...

随机推荐

  1. 【HDOJ】2579 Dating with girls(2)

    简单BFS. /* 2579 */ #include <iostream> #include <queue> #include <cstdio> #include ...

  2. java学习之数组排序一:选择排序

    在讲完java中数组定义的两种方式之外,这里需要讲一下对数组常用的方法,第一个就是排序. 加入我们现在有个数组:int[] arr = {12,87,34,3,98,33,103}; 思路1: 1.首 ...

  3. Unity 动态载入Panel并实现淡入淡出

    unity版本:4.5 NGUI版本:3.6.5 参考链接:http://tieba.baidu.com/p/3206366700,作者:百度贴吧 水岸上 动态载入NGUI控件,这里用Panel为例说 ...

  4. MFC弹出模拟对话框

    Windows对话框分为两类:模态对话框和非模态对话框. 模态对话框是这样的对话框,当它弹出后,本应用程序其他窗口将不再接受用户输入,只有该对话框响应用户输入,在对它进行相应操作退出后,其他窗口才能继 ...

  5. Git基本操作(Windows下)

    在开始使用Git之前,我觉得是很有必要了解下Git与其他版本控制系统的差异与文件在Git中的三种状态.可以到下面这个网站看下:Git详解之一 Git起步,了解之后,可以对Git的基本操作有一个更清晰的 ...

  6. 【动态规划】Vijos P1218 数字游戏(NOIP2003普及组)

    题目链接: https://vijos.org/p/1218 题目大意: 一个N个数的环,分成M块,块内的数求和%10,最后每块地值累乘,求最大和最小. n(1≤n≤50)和m(1≤m≤9)太小了可以 ...

  7. C字符数组及其应用

    1.字符数组和其他数值类型的数组的定义引用和初始化都是相同的. 特别注意的是: 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串. \0'是由C编译系统自动加上的. 2. C语言允许 ...

  8. 动态规划——G 回文串

    G - 回文串 Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Stat ...

  9. 使用国内镜像通过pip安装python的一些包 Cannot fetch index base URL http://pypi.python.org/simple/

    原文地址:http://www.xuebuyuan.com/1157602.html 学习flask,安装virtualenv环境,这些带都ok,但是一安装包总是出错无法安装, 比如这样超时的问题: ...

  10. SPBF(队列优化的Bellman-Foord)