1、知识提要v了解长整型、宽字符型v掌握布尔型v掌握数据类型的转换v了解auto、decltype的用法,列表初始化方法v了解数组的替代方案vector、arrayv掌握字符串类stringv掌握new、delete运算符以及引用v了解基于范围的for循环v了解函数重重载、默认参数、内联函数等p 12.1 数据类型v1 整数类型v2 宽字符型v3 布尔类型p 2整数类型v整数类型简称整型,用于表示没有小数部分的数字v“宽度(width)”描述存储整数时使用的内存大小。宽度越大表示这种类型的数据范围越大。v根据宽度递增的排序是short、int、long、long long,每种类型又分为有符号类型
2、和无符号类型。p 3整数类型v运算符sizeof()计算某种类型在当前编译器中的宽度,以字节为单位。如,sizeof(int)等于4,表示在当前系统中int类型的宽度是4个字节v中声明了一些宏常量,表示某种类型的最大值或最小值。如,INT_MIN表示int类型最小值,INT_MAX表示int类型最大值p 4宽字符型v1 wchar_t类型 wchar_t类型,称为宽字符(wide characters)类型或双字节类型。wchar_t数据类型一般为16位或32位使用wcin、wcout输入输出宽字符类型数据。使用字母“L”作为前缀标识宽字符常量或宽字符串。如LA,表示字母A的wchar_t版本
3、。p 5wchar_t wch=LA;/声明wchar_t类型变量wch,初始化为宽字符Awcout wch endl;/输出 Acout sizeof(wch)endl;/在Code:Blocks中输出2wchar_t ws=LHello;/声明wchar_t类型数组ws,初始化为宽字符串Hellocout sizeof(ws)endl;/输出12宽字符型v2 char16_t和char32_t类型 char16_t和char32_t类型,它们都是无符号类型,分别是16位和32位。使用前缀u(小写)表示char16_t字符常量和字符串常量,如uA、uHello world。使用前缀U(大写)
4、表示char32_t字符常量和字符串常量,如UA、UHello world。p 6布尔类型vbool类型(布尔类型)表示真或假,一般占用 1 个字节长度。vbool 类型只有两个取值,true和false。true表示“真”,false表示“假”。如,bool flag=true;vbool类型可与数值型进行混合运算:true转换为1,false转换为0;非零值转换为true,零转换为falsep 7布尔类型vbool类型常用于条件判断。如,v用cout输出bool类型值时,默认输出1(true)或0(false)。可以在输出流中插入操纵符boolalpha强制要求输出true或falsep
5、8bool flag=true;while(flag).;bool flag=true;/声明bool类型变量flag,初始化为truecout flag endl;/输出1cout boolalpha flag endl;/输出truecout noboolalpha flag endl;/输出1类型转换v当操作的数据类型不同时,C+需要对数据类型进行转换,包括:自动类型转换强制类型转换p 9类型转换v自动类型转换混合数据运算的一个基本的原则是“数据类型最好一致”。当数据类型不一致但兼容时,编译器能够实现自动类型转换。把一种类型的值转换为范围更大的类型时通常不会发生什么问题。相反地,把一种类
6、型的值转换为范围更小的类型时将会造成数据的丢失。p 10类型转换v强制类型转换格式一:(类型说明符)(表达式);/C语言风格的强制类型转换格式二:类型说明符(表达式);/C+风格的强制类型转换强制类型转换不会修改原有表达式的值,而是创建一个新的、指定类型的值p 11(int)(x+y);/把x+y的结果强制转换为int类型int(x+y);/把x+y的结果强制转换为int类型,C+风格强制类型转换cout int(A);/输出字母A的ASCII值,即输出65类型转换v类型转换运算符上述强制类型转换操作是不安全的、或者没有意义的。例如,C+提供了四个数据类型转换操作符,分别是static_cas
7、t、const_cast、dynamic_cast和reinterpret_cast,可以实现更安全的数据类型的转换。p 12float pi=3.14;char*pch=(char*)(&pi);static_castv基本格式v功能:把表达式转换为中的类型说明符指定的类型。p 13static_cast(表达式);char c=A;int n=static_cast(c);static_castv用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。把派生类的指针或引用转换成基类类型(称为上行转换)时是安全的;把基类指针或引用转换成派生类类型(称为下行转换)时,由于没有动态类型
8、检查,所以是不安全的。p 14static_castv用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要由开发人员来保证。v把空指针转换成目标类型的指针。v把任何类型的表达式转换成void类型。v注意:static_cast不能转换掉表达式的const、volatile等属性。如,p 15float pi=3.14;int n=static_cast(pi);/正确,n=3const int m=1;int*p=static_cast(&m);/错误:invalid static_cast from type const int*to type
9、int*dynamic_castv基本格式v其功能是把表达式转换为中的类型说明符指定的类型。其中,类型说明符必须是类的指针、类的引用或者void*。vdynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。p 16dynamic _cast(表达式);const_castv基本格式v主要作用是修改类型的const或volatile属性。除了const 或volatile修饰之外,表达式的类型与中的类型说明符是一致的。如,p 17const _cast(表达式);const int a=1;int*p=const_cast(&a);*p=2;cout *p ,a
10、 endl;/输出2,1reinterpret_castv基本格式v用在任意指针(或引用)类型之间的转换,以及指针与足够大的整数类型之间的转换。类型说明符必须是一个指针、引用、算术类型、函数指针或者成员指针。p 18reinterpret _cast(表达式);reinterpret_castv从指针类型到一个足够大的整数类型。v从整数类型或者枚举类型到指针类型。v从一个指向函数的指针到另一个不同类型的指向函数的指针。v从一个指向对象的指针到另一个不同类型的指向对象的指针。v从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针。v从一个指向类数据成员的指针到另一个指向不同类型的数据成
11、员的指针。p 19struct datashort a,b;long n=0 x12345678;data*p=reinterpret_cast(&n);cout hex a;/在Code:Blocks中输出56782.2变量声明及初始化v变量要遵循“先声明,后使用”的原则v变量在使用之前必须为其赋值,可以在声明变量的同时为变量初始化。vC+扩充了变量声明及初始化的方法,可以实现变量类型的自动推断,并实现统一的初始化的方法。p 20auto类型说明vC+11扩充了关键字auto的功能,可以用作类型说明符声明变量,编译器根据为变量初始化的值的类型自动推断变量的类型。例如,v使用auto关键字声明
12、变量时必须为变量显式地指定初始值,因为编译器需要根据初始值的类型推断变量的类型。v自动类型推断更多的是为了简化代码。如,p 21auto n=1;/变量n的类型是int,等价于int n=1;auto ch=A;/变量ch的类型是charvector vs;/声明向量容器vs,vs中存储string类型对象for(vector:iterator it=vs.begin();it!=vs.end();it+)/.C+11允许您使用auto关键字简化上述代码:vector vs;/声明向量容器vs,vs中存储string类型对象for(auto it=vs.begin();it!=vs.end()
13、;it+)/.关键字decltypev作用:选择并返回表达式的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。vdecltype更多地用于函数类型声明中,结合auto和decltype可以在函数模板中定义函数的返回值类型。如,p 22int n=3;decltype(n)m;/变量m的类型与变量n的类型一致,即用n的类型(int)声明变量m,m不使用n的值template auto add(T1 x,T2 y)-decltype(x+y)return x+y;列表初始化v以前,我们最常用的变量初始化的方法如下:v事实上,C+对数据的初始化操作有多种写法。如,vC+
14、11新标准推荐使用大括号“”完成变量初始化。这种初始的形式称为列表初始化(list-initialization)。p 23int n=0;int n1=1;int n2(1);int n3=1;int n41;列表初始化v列表初始化应用于基本数据类型时不允许“缩窄(narrowing)”赋值,即,用宽数据为窄数据类型变量赋值。p 24int n3.14;上述语句编译时出现语法错误:error:narrowing conversion of 3.1400000000000001e+0 from double to int inside 值得注意的是,有些编译器可能仅给出警告,并不出错,如在De
15、v-C+下的提示是:Warning narrowing conversion of 3.1400000000000001e+0 from double to int inside 2.3数组的替代方案v数组是一种构造类型,用于表示一组类型相同的数据。如,v数组一旦声明,其长度就是确定的。数组的下标从0开始,在使用数组元素时尤其要注意下标不能越界。如,v两个数组不能直接通过数组名完成赋值,即使两个数组类型及大小完全相同。如,p 25int nArray5 1,2,3,4,5;for(int i=0;i 5;i+)cout nArrayi ;nArray6=1;在语法上并没有错误,但是这种操作是不
16、安全的。因为,nArray6并不是程序员可控制的内存区域。int arr5=nArray;/nArray是前面声明的数组上述语句编译时出现语法错误:error:array must be initialized with a brace-enclosed initializer向量vectorvvector可以看作一个动态数组,用于存储一组数据类型相同的数据,对数据元素的个数没有限制。v可以把vector看作一个存放任意数据类型的“容器”v使用vector需要包含头文件。v声明vector对象的方法:p 26vector对象名;vector对象名(元素个数);vector对象名元素初值列表;v
17、ectornV1;vector fV2(10);vector nV31,2,3,4,5;向量vectorv元素的访问方法有两种:下标法使用函数at()v例2-1p 27vector nV31,2,3,4,5;nV32;nV3.at(2);类模板arrayvarray与普通数组一样,也实现为一个定长的数组v使用array需要包含头文件v声明array类型变量的基本格式如下:p 28array数组名;array arr1;array arr21,2,3,4,5;类模板arrayv1.元素访问下标运算符“”、at()、front()、back()、data()等成员函数(1)下标运算符下标从0开始,
18、程序员控制下标不能越界p 29基本格式:数组名下标cout arr22;/输出下标为2的元素,输出3arr20=0;/第一个元素值改为0类模板arrayv1.元素访问(1)at()注意:下标不能越界,如果下标越界编译时无法检查出错误,但是程序运行时会出错,抛出异常。如,p 30基本格式:数组名.at(下标)cout arr2.at(2);/3,输出下标为2的元素arr2.at(0)=0;/第一个元素值改为0cout arr2.at(10);/arr2的下标值的范围是0,9运行时出错,如图类模板arrayv1.元素访问(3)front()p 31基本格式:数组名.front()函数功能:返回第一
19、个元素,相当于at(0)。如,cout arr2.front();/1,输出第一个元素值arr2.front()=0;/第一个元素值改为0类模板arrayv1.元素访问(4)back()基本格式:数组名.back()函数功能:返回最后一个元素。如,cout arr2.back();/5,输出第一个元素值arr2.back()=0;/最后一个元素值改为0p 32类模板arrayv1.元素访问(5)data()基本格式:数组名.data()函数功能:返回指向第一个元素的指针。如,array arr21,2,3,4,5;*arr2.data()=0;/相当于arr2.front()=0;cout *
20、(arr2.data()+2);/3,输出下标为2的元素值p 33类模板arrayv2.迭代器运算函数迭代器(iterator)即指向array容器的元素的指针,它具有指针的一般性质,如解引用运算“*”、指针移动(如自增“+”自减“-”)等。相关的成员函数包括:begin()、end()、cbegin()、cend()、rbegin()、rend()。p 34类模板arrayv2.迭代器运算函数(1)begin()、end()基本格式:数组名.begin()或数组名.end(),分别返回第一个元素的迭代器和最后一个元素后面位置的迭代器。如图p 35类模板arrayv2.迭代器运算函数(2)cb
21、egin()、cend()基本格式与功能与begin()、end()相同与begin()、end()的区别是,begin()、end()可以通过迭代器修改迭代器指向的元素的值,而cbegin()和cend()迭代器是常量(const)迭代器,不能修改迭代器指向的元素的值。p 36类模板arrayv2.迭代器运算函数(3)rbegin()、rend()基本格式:数组名.rbegin()或数组名.rend(),分别返回最后一个元素的迭代器和第一个元素前面位置的迭代器。如图。v例2-2p 372.3数组的替代方案v数组、vector、array的主要区别有:(1)数组元素访问方法只有下标法,而且对数
22、组元素的下标不做越界检查。vector和array提供了两种元素访问方法:下标法及成员函数at()。其中,成员函数at()能够对下标越界做出反应,若下标越界会抛出异常。p 382.3数组的替代方案v数组、vector、array的主要区别有:(2)数组和array实现为一个定长的数组,而vector可以实现为动态数组。所以当数组元素个数不确定并且有较大的变化时,使用vector实现。但是,vector在实现上效率较低,如果数组元素个数是确定的,优先使用数组或array;(3)数组无法直接实现复制,必须逐个元素进行复制。而两个相同类型的vector和array对象可以直接进行赋值。p 392.4
23、字符串stringvstring是C+标准模板库中的一个字符串类,包含在头文件中。vstring类位于名称空间std中,必须提供一条using编译指令或者使用std:string来引用它。v使用string处理字符串方便快捷,不用考虑字符串的长度、内存空间不足等情况。vstring类提供了丰富的成员函数。p 40定义string字符串v常用的格式1.string s1;/声明字符串s1,s1是空字符串,没有任何元素。2.string s2(Hello world);/声明字符串s2,用字符串常量Hello world初始化。这种格式还可以等价地写为:string s2=Hello world;
24、3.string s3Hello world;/声明字符串s3,使用列表初始化的方法初始化s3。p 41定义string字符串v常用的格式4.string s4(5,a);声明字符串s4,用5个字符a初始化s4。5.string s5(s2);声明字符串s5,并用字符串s2初始化s5。p 42string的常用操作v1.字符串数据的输入与输出p 43string的常用操作v1.字符串数据的输入与输出可以使用输入流对象cin输入一个字符串,但输入的字符串中不能包含空格、回车、Tab制表符等可以使用getline()函数输入字符串,其基本格式p 44格式一:getline(cin,字符串,终止符)
25、格式二:getline(cin,字符串)getline(cin,s1,*);getline(cin,s1);string s1;cin s1;/可以输入Hello、world等字符串,但是不能输入Hello world字符串string的常用操作v1.字符串数据的输入与输出字符串的输出很简单,直接使用cout把字符串的内容输出到显示器上。p 45cout s1;string的常用操作v2.字符串的连接可以使用运算符“+”实现两个字符串的连接,不必考虑内存大小的问题。如,p 46s1=s1+s2;/把字符串s2连接到字符串s1的末尾,无需考虑s1的大小string的常用操作v3.字符串大小比较可
26、以直接使用关系运算符比较两个字符串的大小,比较规则是从头开始依次比较每个字符的ASCII大小,直到比较出结果为止。如,成员函数compare()也提供了字符串大小比较的功能。compare()有多种用法,其中最基本的用法是:p 47int compare(const string&str)const;如,pare(s2)string s1abc;string s2ad);则s1s2结果为true。string的常用操作v4.访问字符串中某个元素可以使用下标运算符“”或者成员函数at()访问某个元素。但是需要注意下标不能越界。如,最后一条语句不能正常执行,因为字符串Hello world有11个
27、字符,所以s1的长度是11,下标最大值是10。所以s1.at(11)的下标越界,运行时会抛出异常。p 48string s1Hello world;cout s10 ,s1.at(1);/输出H,ecout s1.at(11);string的常用操作v5.部分常用成员函数string类提供了丰富的成员函数,表2-1列出了部分常用成员函数。(请参考课本)v例2-3p 49string的常用操作v6.与string相关的类型转换函数p 502.5指针与引用vC+中的指针是继承于C语言的一种复合数据类型,其使用方法也是C语言一样。在定义指针变量时,通常要对指针变量赋初值,防止使用野指针导致程序异常。
28、如,v如果指针无法确定指向哪块内存,通常的做法是将指针赋值为“空”指针。如,p 51int*p;*p=1;/错误,对未经赋值的指针直接修改其内存是非法的int*p1=NULL;int*p2=0;int*p3=nullptr;/C+中的标准用法2.5指针与引用v在C+编程中经常使用指针管理计算机内存,如内存的申请与释放。vC+使用运算符new请求正确数量的内存以及使用指针来跟踪新分配的内存空间的位置。虽然在C语言中也提供了库函数(如malloc()、free()等)实现内存的管理,但是C+中新提供的运算符new和delete远比它们使用方便,而且功能更强。p 52new运算符v基本格式格式一:类
29、型说明符*指针变量名=new 类型说明符;格式二:类型说明符*指针变量名=new 类型说明符数组长度;p 53int*p=new int();/申请一块能够存储一个int类型数据的内存空间,默认初值为0int*p=new int(1);/申请一块能够存储一个int类型数据的内存空间,默认初值为1int*p=new int1;/完全等价于int*p=new int(1);int*p=new int10;/申请连续的内存存储10个int类型的数据int*p=new int10();/编译器自动为元素初始化为0int*p=new int51,2,3,4,5;/进行初始化int*p=new int51
30、;/第一个元素初始化值为1int*p=new int-1;/错误,程序抛出异常delete运算符v使用格式格式一:delete 指针名;格式二:delete 指针名;v例2-4v例2-5p 54delete p;delete p;new与delete运算符v通常情况下,new和delete成对使用,而new 与delete成对使用。如,p 55int*p1=new int;.;delete p1;int*p2=new int5;.;delete p2;引用运算符v引用(reference)是C+引入的新语言特性,是C+常用的一个重要内容之一。v引用是隐式的指针,就是给某一变量(或对象)定义一个
31、别名。声明引用后,对引用的操作与对变量的操作是完全一样的。引用分两种左值引用右值引用。p 56引用运算符v1.声明左值引用左值(lvalue)一般是指可以放在赋值号左侧的对象,普通的变量均是左值。声明格式:p 57类型说明符&引用名=目标变量名;int a;int&ra=a;/声明引用ra,它是变量a的引用,即变量a的别名引用运算符v左值引用的几点说明:(1)“&”不是取地址运算,而是起标识作用,用于说明所定义的标识符是一个引用。(2)类型说明符是指目标变量的类型,即引用的类型与目标变量的类型必须相同。(3)声明引用时,必须同时对其进行初始化。如,语句int&ra;是非法的。p 58引用运算符
32、v左值引用的几点说明:(4)引用声明后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其它变量名的别名。例如,p 59int a=1,b=2;int&ra=a;ra=b;第三句ra=b相当于a=b,即用变量b为变量a赋值,并不是说ra重新引用变量b。引用运算符v左值引用的几点说明:(5)声明一个引用,不是新定义了一个变量,它本身不是一种数据类型。因此编译器不给引用分配存储单元。在设计函数的形式参数时通常把形参设计为引用,利用这个特征节省内存。(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。p 60引用运算符v左值引用的几
33、点说明:(7)引用实际上是隐式的指针,它与指针的区别是指针是一种数据类型(构造数据类型)而引用不是。指针可以转换为它所指向的变量的数据类型。如,引用不能进行数据类型转换,必须与目标变量的类型一致。如,p 61int a=1;double*p=(double*)(&a);/虽然这样做可能没有任何意义,但从语法角度说是正确的int a=1;double&ra=a;/语法错误:error:invalid initialization of non-const reference of type double&from an rvalue of type double引用运算符v左值引用的几点说明:(
34、8)左值引用不能绑定常量,即不能建立常量的左值引用,如p 62int&ra=1;/语法错误:error:invalid initialization of non-const reference of type int&from an rvalue of type int引用运算符v2.用const限定左值引用const的内涵是指不能通过常引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。如,常变量只能创建常左值引用,不能创建非常左值引用。如p 63const 类型说明符&引用名=目标变量名;int a=0;const int&ra=a;ra=1;/语法错误:er
35、ror:assignment of read-only reference raconst int a=1;const int&cra=a;/正确int&ra=a;/错误error:binding const int to reference of type int&discards qualifiers引用运算符v3.右值引用右值是相对于左值而言的,右值一般可以放在赋值号的右端,常见的常量、表达式等都是右值。为了支持移动操作(包括移动构造函数和移动赋值函数),C+引入了一种新的引用类型右值引用(rvalue reference),可以自由接管右值引用的对象内容。右值引用的一个重要特性就是能够
36、绑定一个将要销毁的对象。p 64引用运算符v3.右值引用左值引用必须引用一个变量,与左值引用相反,右值引用不能引用变量。如,p 65类型说明符&右值引用名=右值;int&ra=1;int&rb=(2*a);int a=1;int&ra=a;/错误Error:cannot bind int lvalue to int&2.6基于范围的for循环vC+11提供了一种称为基于范围(range-based)的for循环。基本格式如下:p 66for(变量声明:表达式)语句序列;array arr1,2,3,4,5;for(int x:arr)cout x ;/输出1 2 3 4 5 string sc
37、hina;for(char&x:s)x=x-32;cout s s2;引用作为函数参数v用const限定形参引用总是最“好”的选择,因为:(1)使用const可以避免无意中修改实参变量的值。(2)使用const声明形参,使函数能够处理const和非const实参,否则将只能接受非const实参。(3)使用const引用使函数能够接收临时变量或常量。p 73bool isLarger(const string&s1,const string&s2)return s1 s2;并且有如下变量:const string s1=Hello,s2=Hi;string s3=fine,s4=good;则,以
38、下函数调用都是正确的:if(isLarger(s1,s2).;if(isLarger(s2,s4).;if(isLarger(Shandong,Shanghai).;if(isLarger(s1+s3,s2+s4).;函数重载v“函数重载(overload)”是指在同一个作用域内定义多个函数名相同但形参不同的函数。即,同名函数实现不同的函数功能。v函数的形参列表也称为函数的特征标,函数重载的关键是函数具有不同的特征标。v函数重载时,函数的参数不完全一样,包括参数个数、类型及顺序。p 74int add(int x,int y);float add(float x,float y);int ad
39、d(int x,int y,int z);float add(int x,float y);float add(float y,int x);函数重载v例2-9v使用函数重载时要注意以下几点问题(1)不能用函数返回值的类型区别两个重载函数。(2)形式参数的名字不能用做区别两个同名函数。(3)普通变量与其引用具有相同的特征标。(4)如果形参是指针或者引用,使用const定义的常指针与常引用可以与不使用const的指针与引用区别,实现函数重载。p 75int add(int x,int y);float add(int x,int y);int add(int x,int y);int add(i
40、nt a,int b);double square(double x);double square(double&x);void func1(int*p);void func1(const int*p);void func2(int&ra);void func2(const int&ra);函数重载v例2-10v使用函数重载时要注意以下几点问题(5)函数重载只发生在同一作用域内的同名函数之间,不同作用域的同名函数不能实现重载。p 76void f(int);void g()void f(double);f(1);/调用f(double)默认参数v默认参数指的是当调用函数中省略了实参的值时自动使
41、用的一个值。v在函数原型中为形式参数赋初值。v例2-11p 77int add(int,int,int=0,int=0);默认参数v使用默认参数的函数时要注意以下几点问题(1)如果有函数声明,默认参数只能在函数声明中指定。(2)如果没有函数声明,只有函数定义,可以在函数定义中指定默认值。(3)默认参数定义的顺序是自右向左,如果某个参数设定了默认参数,则其右边的所有参数必须设置默认参数。(4)默认参数可以是全局变量、常量、甚至是一个函数,但是不能使用局部变量。(5)同时使用默认参数的函数和函数重载时需要避免函数调用上的二义性。p 78内联函数v内联函数(inline function)是C+为了
42、提高程序的运行效率所做一项改进。v内联函数的声明及定义很简单,只需在函数的声明及定义前加上关键字inline即可。v当被调用函数代码少、执行速度快、需要频繁调用时才考虑使用内联函数。p 79inline int add(int x,int y);/内联函数声明inline int add(int x,int y)/内联函数的定义return x+y;小结vC+中任何数据都有类型,区别类型的目的在于不同的类型表达数据的范围不同、可对其进行的操作也不同。v不同类型之间进行数据运算时需要对数据进行类型转换。v变量声明之后再使用、使用之前赋初值是基本常识,必须深入到程序员的脑海中。p 80小结v数组是最常用的一种数据结构,C+为我们提供了使用更加方便、安全的类模板vector和array。v字符串类string的出现也为我们处理字符提供了便利vnew和delete运算符使我们能够轻松地使用计算机内存资源,对内存真正做到“按需要所取”。v函数是构成程序的基本单位,是大程序模块化设计的基础,建议读者熟练掌握各类函数的编写。p 81