【文档说明】第2章C++对C的扩充课件.ppt,共(41)页,161.188 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-44717.html
以下为本文档部分文字说明:
第2章C++对C的扩充2.1C++的特点2.2C++语言的文件扩展名2.3注释符2.4名字空间2.5C++语言的输入输出2.6变量的定义2.7强制类型转换2.8动态内存的分配与释放2.9作用域运算符::2.10引用2.11co
nst修饰符2.12字符串2.13C++语言中函数的新特性2.1C++的特点C++语言既保留了C语言的有效性、灵活性、便于移植等全部精华和特点,又添加了面向对象编程的支持。用C++编写的程序可读性好,生成的代码质量高,运行效率仅比汇编语言慢10%~20%
。2.2C++语言的文件扩展名为了使编译器能够区别是C语言还是C++语言,C++语言体系规定用“.cpp”(意即CPlus-Plus)作为C++语言源文件的扩展名以区别于C语言用的“.C”文件扩展名。与C
++语言源文件相关的头文件扩展名一般仍用“.h”(有些操作系统也规定使用“.hpp”)。2.3注释符1.段注释:/*…*/2.单行注释://当只做单行注释时便可用“//”符号表示从此符号起至行尾均为行注释内容。程序编译时将忽略所有的注释内容。2.4名字空间(名空间namesp
ace)用花括号把文件的一部分括起来,并以关键字namespace开头给它起一个名字:namespacens1{floata,b,c;fun1(){……}}花括号括起来的部分称声明块。声明块中可以包括:类、变量(带有初始化)、函数(带有定义)等。在域外使用域内的成员时,需加上名字空间名作为前缀,后
面加上域操作符“::”。如:ns1::a,ns1::fun1()。最外层的名字空间域称为全局名字空间域(globalnamespacescope),即文件域。名字空间域可分层嵌套,同样有分层屏蔽作用。如:namespacen1{namespacen2{//名字空
间嵌套classmatrix{……}//名字空间类成员matrix}}访问matrix,可写:n1::n2::matrix使用using声明可只写一次限定修饰名。using声明以关键字using开头,后面是被限定修饰的名字空间成员名,如:usingn1::n2::matrix;//名字
空间类成员matrix的using声明以后在程序中使用matrix时,就可以直接使用matrix,而不必使用限定修饰名。使用using指示符可以一次性地使名字空间中所有成员都可以直接被使用,比using声明方便。using指示符以关键字using开头,后面是关键字name
space,然后是名字空间名。如:usingnamespacens1;标准C++库中的所有组件都是在一个被称为std的名字空间中声明和定义的。在采用标准C++的平台上使用标准C++库中的组件,只要写一个using指示符:usingnamespacestd;就可以直接使用标准C+
+库中的所有成员。注意:如果使用了名空间std,则在使用#include编译预处理命令包含头文件时,必须去掉头文件的扩展名.h,否则会出错。#include<iostream>usingnamespacestd;和#include<iostream.h>是一样的2.5C++语言的
输入输出C++语言另外定义了一套保留字与运算符来替代C语言中对标准输入、输出函数的引用。#include<iostream.h>cout<<“输出内容”<<…;//标准输出流对象(默认输出到显示器)cin>>“
输入内容”>>…;//标准输入流对象(默认从键盘输入)【例2.1】C++的输入输出举例。#include<iostream>//使用名空间std,则必须去掉.h扩展名usingnamespacestd;voidmain(){charname[10];intage;cout<<"pleaseinp
utyourname:";cin>>name;cout<<"Howoldareyou:";cin>>age;cout<<"nameis"<<name<<endl;cout<<"ageis"<<age<<endl;}2.6变量的定义C+
+允许变量的定义语句可以出现在程序的任何位置。C++允许直接使用结构体名(联合名、枚举名)定义变量。【例2.2】C++的变量定义举例。#include<iostream>usingnamespacestd
;voidmain(){structstudent{intno;floatmath;};intn;cin>>n;studentwang;wang.no=n;cin>>wang.math;cout<<wang.no<<""<<wang.math<
<endl;}2.7强制类型转换格式:(数据类型)(表达式)或:数据类型(表达式)intb;floatf;f=float(b);//此时变量b仍然为int类型float(i)*f/*强制类型转换符优先级较高,先将变量
i强制类型转换为float类型,然后与变量f运算*/2.8动态内存的分配与释放1.new运算符做分配指针变量=new数据类型;2.delete运算符做释放delete指针变量;/*其中的指针变量保存着new动态分配的内存的首地址*/注意:
(1)用new获取的内存空间,必须用delete进行释放;(2)对一个指针只能调用一次delete;(3)用delete运算符作用的对象必须是用new分配的内存空间的首地址。#include<iostream>usin
gnamespacestd;voidmain(){int*p;p=newint;//分配内存空间*p=5;cout<<*p;deletep;//释放内存空间}【例2.3】new与delete应用举例。
在用new分配内存的同时进行初始化。使用形式为:指针变量=new数据类型(初始值);例如上例中的:p=newint;*p=5;也可写成:p=newint(5);指针变量=new数据类型[数组大小];此时指针变量指向第一个数组元素的地址。使用new分配数组时,不能提供初始值。使用
new建立的数组变量也由delete释放。其形式为:delete指针变量;或delete[]指针变量;同样,也可以用new来为多维数组分配空间,但是除第一维可以为变量外,其它维数都必须是常量。用new建立
数组类型的变量注意在使用delete时,不用考虑数组的维数。有时,并不能保证一定可以从堆内存中获得所需空间,当不能成功地分配到所需要的内存时,new返回0,即空指针。因此我们可以通过判断new的返回值是否为0,来得知系统中是否有足够的空闲内存来供程序使用。例如:int*p=newint[100
];if(p==0){cout<<"can’tallocatemorememory,terminating."<<endl;exit(1);}其中exit函数的作用是终止程序运行。#include<iostream.h>voidmain(){intn;//定义数组元
素的个数int*p;cout<<"pleaseinputthelengthofthearray:";cin>>n;if((p=newint[n])==0)//分配内存空间{cout<<"can'tall
ocatemorememory,terminating."<<endl;exit(1);}for(inti=0;i<n;i++)p[i]=i*2;cout<<"Nowoutputthearray:"<<endl;for(i=0;i<n;i++)cou
t<<p[i]<<"";cout<<endl;delete[]p;//释放内存空间}【例2.4】从堆内存中获取一个整型数组,赋值后并打印出来。补充:复制初始化和直接初始化。intn(50);//直接初始化intm=20;//复制初始化注意
:初始化不是赋值2.9作用域运算符::通常情况下,如果全局变量与局部变量同名,那么局部变量在其作用域内具有较高的优先权。C++中提供的作用域运算符::,它能指定所需要的作用域。注意:不能用::访问函数中的局部变量。在C++语言中作用域运算符::还用来限定类
的成员。#include<iostream>usingnamespacestd;floata=2.4;//全局变量voidmain(){inta=8;//局部变量cout<<a<<endl;cout<<
::a<<endl;//::a表示全局变量a}2.10引用引用,用于在程序的不同部分使用两个以上的变量名指向同一地址(内存空间),使得对其中任一个变量的操作实际上都是对同一地址单元进行的。被声明为引用类型的变量名则是实际变量名的别名。引用运
算符为&,声明引用的一般形式为:数据类型&引用变量名=变量名;或数据类型&引用变量名=变量名;或数据类型&引用变量名=变量名;对引用进行操作,实际上就是对被引用的变量进行操作引用不是值,不占存储空间引用一旦被初始化,就不能再重新赋值【例2.5】引用举例。#include<iostream.
h>voidmain(){intnum=50;int&ref=num;ref+=10;cout<<"num="<<num<<endl;cout<<"ref="<<ref<<endl;num+=40;cout<<"num=
"<<num<<endl;cout<<"ref="<<ref<<endl;}11点说明:(1)在一行上声明多个引用型变量(函数)名时,要在每个变量(函数)名前都冠以“&”符号。例如:inti=1,j=2;int&ref=i,&a=j;(2)引用不是变
量,所以引用本身不能被修改(不能再改变成为另一个变量的引用)。(3)一个变量被声明为引用时必须进行初始化,除非这个引用是用作函数的参数或返回值,为引用提供的初始值应为变量(包括对象)。引用一旦被初始化,就不能再重新赋值。如:/*接上面两行程序*/int&r
ef=j;//错ref=&j;//错&ref=j;//错ref=j;//对(4)由于引用不是变量,所以,不能说明引用的引用,也不能说明数组元素的类型为引用数组,或指向引用的指针(引用不占内存空间)。例如:i
nt&a[5];//错误int&*p;//错误由于指针也是变量,因此可以说明对指针变量的引用。例如:int*a;int*&p=a;intb;p=&b;//a指向变量b(5)引用与指针不同指针的内容或值是某一变量的内存单元地址,而引用则与初始化它的变量具有相同的内存单元地址。指针是个变量,
可以把它再赋值成其它的地址,然而,建立引用时必须进行初始化并且决不会再指向其它不同的变量。(6)引用运算符和地址运算符不同。例如:intnum=50;int&ref=num;//引用int*p=&ref;//取地址(7)可以用一个引用初始化另一个引用。例如:intnum=
50;int&ref1=num;int&ref2=ref1;ref2=100;//num被修改为100(8)常把函数的参数说明成引用以建立函数参数的引用传递方式。【例2.7】引用作为函数参数实现数值交换(9)有空指针,无空引用(10)引用不能用数据类型来
初始化。如:int&ref=int;//error(11)返回引用的函数调用可作为左值若一个函数返回了引用,那么该函数的调用也可以被赋值。返回值不能是函数的局部变量的引用。引用返回值只用在需对函数的返回值重新赋值的时候。【例2.8】统计学生中A类学生与B类学生各为多少个。A类学生的
标准是平均分在80分以上,其余都是B类学生。*【例2.9】返回的局部作用域内的变量,函数作为左值。2.11const修饰符#definePI3.1415926constfloatPI=3.1415926;const常量有类型有地址
可以用指针指向这个值,但不能修改它C++建议用const取代#define注意:(1)使用const修饰符定义常量时,必须初始化(2)常量一旦被定义,在程序中任何地方都不能再更改。(3)如果用const定义的是一个整型常量,int可以省
略。(4)与#define定义的常量有所不同,const定义的常量可以有自己的数据类型,这样C++编译程序可以进行更加严格的类型检查,具有良好的编译时的检测性。(5)函数参数也可以用const说明,用
于保证实参在该函数内部不被改动。例如,通过函数max求出整型数组a[100]中的最大值,函数原型应该是:intmax(constint*pa);这样做的目的是确保原数组的数据不被破坏,即在函数中对数组元素的操作只许读,不许写。const与指针一起使用的组合情况:(a)
指向常量的指针指向常量的指针是指一个指向常量的指针变量。constchar*pc="abcd";声明指向常量的指针变量pc,它指向一个字符串常量由于使用了const,不允许改变指针所指的常量,因此以下语句是错误的:pc[3]='x';但是由于pc是一个指向常量
的普通指针变量,不是常指针,因此可以改变pc的值。例如以下语句是允许的:pc=“jkkk”;//另外申请了一块内存空间(b)常指针常指针是指指针本身,而不是它指向的对象为常量。例如:char*constpc="abcd";//常指针这个语
句的含义为:声明一个名为pc的指针变量,该指针是指向字符型数据的常指针,用“abcd”的地址初始化该常指针。创建一个常指针,就是创建不能移动的固定指针,但是它所指的数据可以改变。例如:pc[3]='x';//合法(?)
pc="jkkk";//不合法(c)指向常量的常指针指针本身不能改变,它所指向的值也不能改变。要声明一个指向常量的常指针,二者都要声明为const。例如:constchar*constpc="abcd";//指向常量的常指针这个语句的含义为:声
明一个名为pc的指针变量,它是一个指向字符型常量的常指针,用“abcd”的地址初始化该指针。以下两个语句都是错误的:pc[3]='x';//错误,不能改变指针所指的值pc="dfasdfa";//错误,不能改变指针本身2.1
2字符串在C++中提供了一种既方便又好用的string类型。例如:#include<iostream>#include<string>usingnamespacestd;/*使用字符串string类型的程序应包含头文件<string>,而且不能写成
#include<string.h>*/voidmain(){strings,t;cout<<"请输入一个字符串:"<<endl;cin>>s;/*由键盘输入一行文本,并把它赋给sring类型的变量s,使用此方
式输入的字符串中不能包含空格字符*/t="Ilikeprogramming!";cout<<"字符串的输出:"<<endl<<s<<endl<<t<<endl;cout<<s.append("OK!")<<endl;/*append为str
ing类的成员函数*/}2.13C++语言中函数的新特性2.13.1函数原型(FunctionPrototype)2.13.2内联(inline)函数2.13.3带缺省参数的函数2.13.4函数重载(overload)2.13.5函数模板(functiontemplate)C++要求为每一
个函数建立原型,用以说明:1函数的名称2参数个数及类型3函数返回值的类型。养成将声明与定义分别编写的编程习惯。函数原型(声明)与函数的定义要在上述3点上保持一致。写函数原型时,可以省略形参的名字
2.13.1函数原型(FunctionPrototype)2.13.2内联(inline)函数在执行程序过程中如果要进行函数调用,需要时间和空间的开销,使得程序执行效率降低。C++引入了内联函数机制,通过在函数声明前
加上关键字:inline。编译器会将编译后的全部内联函数的目标机器码复制到程序内所有的引用位置并把往返传送的数据也都融合进引用位置的计算当中。使用内联函数是一种用空间换时间的措施,若内联函数较长,且调用太频繁
时,程序将加长很多。因此,通常只有较短的函数才定义为内联函数,对于较长的函数最好作为一般函数处理。一般情况下,我们对内联函数做如下的限制:(1)不能有递归(2)不能包含静态数据(3)不能包含循环(4)不能包含switch和goto语句(5)不能包
含数组若一个内联函数定义不满足以上限制,则编译系统把它当作普通函数对待。【例2.11】内联函数的使用。2.13.3带缺省参数的函数在函数说明中为形参指定一个缺省值,则称此函数为带缺省参数的函数。当函数调用发生后,在形参表中等
号后的各“缺省值”将起实参的传递作用。如果函数有多个缺省参数,则缺省参数必须是从右向左定义,并且在一个缺省参数的右边不能有未指定缺省值的参数。voidfun(inta=3,intb=6,intc,intd);//错voidfun(inta=65,intb=3,intc,intd=3)
;//错voidfun(inta,intb,intc=65,intd=3);//对函数调用时,参数不带缺省值。例:#include<iostream.h>voidfun(inta,intb,intc=65,intd=
3);voidmain(){fun(1,2);fun(1,2,3);fun(1,2,3,4);//分别是什么效果}voidfun(inta,intb,intc,intd)//函数定义时参数不带缺省值{cout<<a<<endl<<b<<endl<<c<<endl
<<d<<endl<<endl;}2.13.4函数重载(overload)C++编译系统允许为两个或两个以上的函数取相同的函数名,但是形参的个数或者形参的类型不应相同(不涉及函数的返回值类型),编译系统会根据实参
和形参的类型及个数的最佳匹配,自动确定调用哪一个函数,这就是所谓的函数重载。函数重载无需特别声明,只要所定义的函数与已经定义的同名函数形参形式不完全相同,C++编译器就认为是函数的重载。例如:voidf1(int,doub
le);voidf1(int,int);voidf1(int,int,int);【例2.12】重载函数应用举例注意:①不可以定义两个具有相同名称、相同参数类型和相同参数个数,只是函数返回值不同的函数。intfunc(intx);floatfun
c(intx);//ambiguous②如果某个函数参数有缺省值,必须保证其参数缺省后调用形式不与其它函数混淆。intf(inta,floatb);voidf(inta,floatb,intc=0);函数调用语句:
f(10,2.0);//ambiguous具有二义性,既可以调用第一个函数,也可以调用第二个函数,编译器不能根据参数的形式确定到底调用哪一个。2.13.5函数模板(functiontemplate)1、模板分为函数模板和类模板。
2、函数模板的一般说明形式如下:template<模板参数表><返回值类型><函数名>(模板函数形参表){//函数定义体}或:template<模板参数表><返回值类型><函数名>(模板函数形参表);//声明template<模板参数表><返回值类型><函数名>(模板函数形参表)//定义{//函数
定义体}#include<iostream.h>//【例2.13】template<classT>Tmin(Ta[],intn);//声明voidmain(){inta[]={1,3,0,2,7,6,4,5,2};doubleb[]={1.2,-3.4,6.8,9.8
};cout<<"a中最小的是"<<min(a,9)<<endl;cout<<"b中最小的是"<<min(b,9)<<endl;}template<classT>Tmin(Ta[],intn)//定义{inti;Tminv=a[0];for(i=1;i<
n;i++)if(minv>a[i])minv=a[i];returnminv;}3、如果类型形参多于一个,则每个类型形参都要使用class或typename。如:template<classT1,classT2>4、模板函数当编译系统发现有一
个函数调用:<函数名><实参表>;和函数模板一致时,将根据<实参表>中的类型生成一个重载函数,即模板函数。该模板函数的定义体与函数模板的函数定义体相同,而<形参表>的类型则以<实参表>的实际类型为依据。5、虽然模板参数T可以实例化成各种类型(包括结构体、类),但是采用模板参数T的各参数之间
必须保持完全一致的类型。模板类型并不具有隐式的类型转换,例如在int与char之间、float与int之间、float与double之间等的隐式类型转换。6.函数模板与重载函数当模板函数与重载函数同时出现在一个程序体内时,C++编译器的求解次序是:1.调用
重载函数2.如果不匹配,则调用模板函数3.如果还不匹配则进行强制类型转换调用重载函数4.报错【例2.14】模板函数与重载函数。上机练习:P361、2所有上课的例子研究思考:1、为什么#include<iostream>usingnamespacestd;
和#include<iostream.h>是一样的为什么#include<string>usingnamespacestd;和#include<string.h>不一样2、如果函数有多个缺省参数,则缺省参数必须从右向左定义,并且在一个缺省参数的
右边不能有未指定缺省值的参数。为什么?编写程序验证你的猜测,并进一步研究函数调用时参数匹配的过程。3、编写一个程序验证当模板函数与重载函数同时出现在一个程序体内时,C++编译器的求解次序是:1.调用重载函数2.如果不匹配,则调用模板
函数3.如果还不匹配则进行强制类型转换调用重载函数