《C++程序设计》笔记*潘建瑜2019年6月9日*本笔记仅供课堂教学使用·ii·纸上得来终觉浅,绝知此事要躬行.
学而不思则罔,思而不学则殆.
知识,只有当它靠积极的思考得来,而不是凭记忆得来的时候,才是真正的知识.
目录第一讲计算机基础11.
1信息的表示与存储11.
1.
1计算机的数字系统11.
1.
2常见的进制数及它们之间的转换11.
1.
3二进制数的编码表示21.
2算法基本概念41.
2.
1什么是算法41.
2.
2算法的特征与评价41.
2.
3算法的描述方法与基本控制结构41.
3课后阅读51.
4课后练习5第二讲C++编程基础62.
1C++语言概述62.
1.
1C++源程序结构72.
1.
2C++源程序书写规范82.
1.
3书写漂亮的程序82.
1.
4编译器82.
2C++基础知识82.
2.
1C++字符集,标识符,关键字82.
2.
2C++数据类型92.
2.
3变量与常量122.
2.
4运算与表达式142.
3C++简单输入输出182.
4程序示例202.
5上机练习22第三讲选择与循环233.
1关系运算与逻辑运算233.
2选择结构24iii·iv·目录3.
2.
1IF语句243.
2.
2SWITCH结构243.
3循环结构263.
3.
1WHILE循环263.
3.
2DOWHILE循环273.
3.
3FOR循环273.
3.
4循环的非正常终止283.
4程序示例293.
5上机练习33第四讲函数344.
1函数的声明、定义与调用344.
2函数间的参数传递414.
3函数嵌套与内联函数424.
4数据的作用域444.
5形参带缺省值的函数474.
6函数重载484.
7预编译处理与多文件结构494.
8上机练习52第五讲数组545.
1一维数组545.
2二维数组555.
3数组作为函数参数565.
4应用实例:矩阵乘积的快速算法585.
4.
1普通方法585.
4.
2Strassen方法585.
5应用实例:列主元Gauss消去法求解线性方程组595.
6上机练习59第六讲指针与字符串数组616.
1指针的定义与运算616.
2指针与一维数组636.
3指针与二维数组646.
4指针与引用666.
5指针与函数666.
6持久动态内存分配686.
7字符串(字符数组)706.
8上机练习72目录·v·第七讲简单输入输出737.
1C++基本输入输出(I/O)流737.
2C语言格式化输出757.
3C语言文件读写767.
4上机练习78第八讲排序算法及其C++实现798.
1引言798.
2选择排序808.
3插入排序818.
4希尔排序818.
5冒泡排序828.
6快速排序838.
7上机练习84第九讲类与对象I:面向对象基础859.
1为什么面向对象859.
2类和对象基本操作869.
3构造函数909.
4复制构造函数929.
5匿名对象949.
6类与对象举例:游泳池959.
7析构函数969.
8上机练习96第十讲类与对象II:面向对象进阶9710.
1类的组合9710.
2结构体与联合体9910.
3类作用域10010.
4静态成员10110.
5友元关系10410.
6类的UML描述10510.
7上机练习106·vi·目录第十一讲类与对象III:面向对象提高10711.
1常对象与常成员10711.
2对象数组与对象指针11011.
3动态对象11111.
4向量类:vector11111.
5字符串类:string11511.
5.
1字符串匹配算法11811.
6上机练习119第十二讲运算符重载与类型转换12012.
1为什么要重载运算符12012.
2怎么实现运算符重载12012.
3运算符重载:成员函数方式12112.
4运算符重载:非成员函数方式12312.
5成员函数or非成员函数12612.
6运算符[]的重载12712.
7运算符重载注意事项12812.
8自动类型转换12812.
9上机练习130第十三讲继承与派生13213.
1继承与派生13213.
2派生类的定义13213.
3构造函数13313.
4派生类成员的标识与访问13613.
5派生类的复制构造函数13713.
6派生类的析构函数13713.
7类型兼容规则13713.
8虚父类13713.
9上机练习139第十四讲多态14014.
1什么是多态14014.
2虚函数14014.
3纯虚函数与抽象类14314.
4模板14514.
5模板函数14614.
6模板类14814.
7上机练习151目录·vii·第十五讲文件流与输出输入重载15215.
1输入输出流15215.
2文件流类与文件流对象15215.
3文件的打开与关闭15315.
4文件读写:文本文件与二进制文件15415.
5移动或获取文件读写指针15615.
6重载short-->int-->unsigned-->long-->unsignedlong-->float-->double(2)强制转换/显式转换§¤类型标识符(表达式)//C++风格,将表达式的值转化为指定的数据类型(类型标识符)表达式//C语言风格,作用同上例2.
1(类型转换)已知有下面的代码1inta=2,b=5;2doublex,y,z;34x=b/a;//x=2.
05y=double(b)/a;//y=2.
56z=double(b/a);//z=2.
0(1)由于a和b都是整型,因此表达式b/a的值也是整型,即b/a=2.
将其赋值给double型变量x,因此x=2.
0.
(2)在计算double(b)/a时,先将b的值转化为double型(注意,不是将b转化为double型,变量b的类型是不会改变的!
),然后与a相除.
由于是一个double型的数据与一个int型的数据进行运算,所以系统会自动将int型的数据转化为double型的数据,然后再做运算.
所以最后的结果是2.
5.
(3)在计算double(b/a)时,是先计算b/a,然后将结果转化为double型,所以最后的结果是z=double(2)=2.
0(4)需要注意的是,变量a,b的类型在整个计算过程中是始终不变的,即一直是int型.
类型转换不会改变变量的数据类型!
(5)类型转换规则:-浮点型转整型:直接丢掉小数部分;-字符型转整型:取字符的ASCII码;-整型转字符型:ASCII码对应的字符.
·12·第二讲C++编程基础例2.
2已知有下面的代码1inti;2chara;3i=3.
6;cout转义字符;(4)字符串常量:用双引号括起来的字符序列;(5)布尔常量:true和false.
例:下面都是常量1123,-456,123L,456U;21.
2,1.
2F,1.
2L,1.
2e8,1.
2e8F,1.
2e-8L3'M','A','T','H'4"MATH@ECNU"符号常量:用标识符表示常量(1)声明方式:2.
2C++基础知识·13·§¤const数据类型标识符变量名=常量值;例:声明符号常量1constfloatPI=3.
1415926;(2)符号常量在声明时必须初始化;(3)符号常量的值在程序中不能被修改(不能重新赋值)typedef:为一个已有的数据类型另外命名(取别名),可以取一个别名,也可以取多个别名.
§¤typedef已有类型名新类型名表;-可以同时为一个已有的数据类型取多个别名.
例:为一个已有的数据类型另外命名1typedefdoublearea,volume;//为double取两个别名area和volume2typedefintnatural;//为int取别名natural3naturali,j;//等价于inti,j;4areas;//等价于doubles;5volumev;//等价于doublev;枚举:定义新的数据类型§¤enum枚举类型名{变量可取值列表,即枚举元素};(1)对枚举元素按常量处理,不能对它们赋值(2)枚举元素具有默认值,依次为:0,1,2,.
.
.
;(3)也可以在声明时指定枚举元素的值,如:源代码2.
1.
枚举示例§¤1//枚举类型2#include34usingnamespacestd;56intmain()7{8enumweekday{sun,mon,tue,wed,thu,fri,sat};//定义枚举数据类型weekday9weekdaytoday,tomorrow;1011today=wed;12cout>(右移)(9)指针运算符:*(取内容)、&(取地址)运算符优先级:表2.
2.
C++运算符优先级优先级运算符描述结合性1::作用域解析从左到右2a++a--后缀自增与自减type()类型转型(C++风格)f()函数调用a[]下标.
->成员访问3++a--a前置自增与自减从右到左+a-a一元加与减!
~逻辑非和逐位非(type)类型转型(C语言风格)*a指针运算&a指针运算:取地址sizeof大小(所占字节数)co_awaitawait表达式(C++20)newnew[]动态内存分配2.
2C++基础知识·15·deletedelete[]动态内存分配4.
*->*指向成员指针从左到右5a*ba/ba%b乘法、除法、取余数6a+ba-b加法与减法7>逐位左移与右移8三路比较运算符(C++20)9>=分别为>与≥的关系运算符10==!
=分别为=与≠的关系运算符11a&b逐位与12^逐位异或(排除或)13|逐位或(包含或)14&&逻辑与15||逻辑或16ab:c三元条件运算从右到左throwthrow运算符co_yieldyield表达式(C++20)=赋值+=-=以和及差复合赋值以积、商及余数复合赋值>=以逐位左移及右移复合赋值以逐位与、异或及或复合赋值17,逗号运算从左到右C++语句(1)空语句(只有分号)(2)声明语句;(3)表达式语句(赋值表达式);(4)复合语句(将多个语句用{}括起来组成的一个语句);(5)选择语句,循环语句,跳转语句;(6)表达式与表达式语句(1)表达式:由运算符连接常量、变量、函数等所组成的式子;(2)表达式语句:表达式后加分号;(3)表达式中的运算符包含赋值运算符;赋值表达式的值是赋值号左边变量的值;·16·第二讲C++编程基础(4)表达式可以包含在其它表达式中,但语句不行!
赋值语句(1)标准赋值语句§¤变量=表达式;例:赋值运算1x=3;2x=y=3;//等价于y=3;x=y;这种赋值方式不能用于初始化(2)自增自减:++,---前置:先自增或自减,然后参与表达式运算;-后置:先参与表达式运算,然后自增或自减;-不要在同一语句中包含一个变量的多个++或--,因为它们的解释在C/C++标准中没有规定,完全取决于编译器的个人行为例2.
3自增自减运算.
1x++;//等价于x=x+1;2++x;//等价于x=x+1;3y=x++*x;//等价于y=x*x;x=x+1;4y=++x*x;//等价于x=x+1;y=x*x;(3)复合赋值运算符:例:复合赋值运算.
1intx2x+=3;//等价于x=x+3;3x/=3;//等价于x=x/3;45inta,b,c,d,e,f;6a=5;7b=a+3;//b=88a=a+(c=6);//c=6;a=11;9d=e=f=a;//f=11,e=11,d=1110e*=d;//e=12111f/=c-2;//f=(1or2)逗号运算符:§¤表达式1,表达式2(1)先计算表达式1,再计算表达式2,并将表达式2的值作为整个表达式的结果.
2.
2C++基础知识·17·例2.
4逗号运算(注意运算的优先级!
)1inta=2,b;2a=3*5,a+10;//a=12or153b=(3*5,a+10);//b=为了避免由运算优先级所导致的低级错误,建议多使用小括号.
位运算符:按二进制位进行运算&、|、^(异或)、~(取反)、>(右移)例2.
5二进制位运算1shorta=5;//a:00000000000001012shortb=14;//b:000000000000111034shortc1,c2,c3,c4,c5,c6;5c1=a&b;//:00000000000001006c2=a|b;//:00000000000011117C3=~a;//:11111111111110108C4=a^b;//:00000000000010119C5=a>2;//:0000000000000001求字节数运算符:§¤sizeof(数据类型)//返回指定数据类型所占的字节数sizeof(表达式)//返回表达式结果所对应的数据类型所占的字节数例2.
6sizeof举例1inta,b,c;2a=sizeof(int);3b=sizeof(3+5);4c=sizeof(3.
0L+5);常用数学函数(需加入cmath头文件:#include)绝对值abs(x)平方根sqrt(x)指数函数exp(x)xypower(x,y)对数函数log(x),ln(x)取整函数ceil(x),floor(x),round(x),trunk(x)三角函数sin,cos,tan,asin,acos,atan·18·第二讲C++编程基础双曲三角函数sinh,cosh,tanh,asinh,acosh,atanh例2.
7数学函数举例1#include2#include//数学函数3usingnamespacestd;4intmain()5{6doublex,y;78cout>变量名或字符串;`cout>转义字符,常见的转义字符有\a响铃\r回车\\反斜杠\b退后一格\t水平制表符\'单引号\n换行\v垂直制表符\"双引号例:cout举例.
1x=3.
14159;2y=2.
71828;3cout>x;2.
3C++简单输入输出·19·操纵符:控制输出格式.
常用的操纵符有(需加入头文件#include)·20·第二讲C++编程基础操作符含义endl插入换行符,并刷新流(将缓冲区中的内容刷入输出设备)setw(n)设置域宽cout.
width(n)fixed使用定点方式输出scientific使用指数形式输出setfill(c)设置填充,c可以是任意字符,缺省为空格,cout.
fill(c)如setfill('*'),setfill('0')setprecision(n)设置输出的有效数字个数,若在fixed或scientific后使用,则设置小数位数left左对齐right右对齐(缺省方式)showpoint显示小数点和尾随零(即使没有小数部分)操纵符的作用范围:-setw仅对紧随其后的输出起作用;-其它操纵符:至下一个同类型命令为止.
例2.
8操纵符举例1doublex=3.
14159,y=12.
3456789;2cout4#include//数学函数56usingnamespacestd;78intmain()9{10doubleLoan,rate_year,rate_month,year;11doublepayment_month,payment_total;1213cout>Loan;15cout>rate_year;17rate_month=rate_year/1200;18cout>year;2021//计算每月需还款金额22doublevtmp=pow(1+rate_month,12*year);23payment_month=Loan*rate_month*vtmp/(vtmp-1);24payment_total=payment_month*year*12;2526cout3#include45usingnamespacestd;67intmain()8{9longSecond,Minute,Hour;1011Second=time(NULL);12Minute=Second/60;13Hour=Minute/60;14cout23usingnamespacestd;45intmain()6{7intx1=1,x2=2,x3=3;8boolb1,b2,b3;910b1=(x1x2>x1;//有没有问题15cout23usingnamespacestd;45intmain()6{7intday;89cout>day;1112switch(day)13{14case0:15cout23.
3循环结构·27·3usingnamespacestd;45intmain()6{7inti=1,n,s=0;89cout>n;1112while(i23usingnamespacestd;45intmain()6{7intn,k;89cout>n;11cout23usingnamespacestd;45intmain()6{7inti,j;89for(i=1;i23usingnamespacestd;45intmain()6{7intk;89cout23usingnamespacestd;43.
4程序示例·31·5intmain()6{7intm,n,gcd;89cout>m;10cout>n;1112for(intk=1;k34usingnamespacestd;56intmain()7{8intm,n,gcd;910cout>m;11cout>n;1213intk=(m0;k--)15{16if(m%k==0&&n%k==0)17{18gcd=k;break;19}20}21cout23usingnamespacestd;45intmain()6{7intn,k;89cout>n;1112if(n==1)13cout23usingnamespacestd;45intmain()6{7intbirthday=0;8charanswer;910cout>answer;14if(answer=='Y'||answer=='y')15birthday=birthday+16;1617cout>answer;21if(answer=='Y'||answer=='y')22birthday=birthday+8;2324cout>answer;3.
5上机练习·33·28if(answer=='Y'||answer=='y')29birthday=birthday+4;3031cout>answer;35if(answer=='Y'||answer=='y')36birthday=birthday+2;3738cout>answer;42if(answer=='Y'||answer=='y')43birthday=birthday+1;4445cout23usingnamespacestd;45intmy_max(intx,inty)6{7if(x>y)returnx;8elsereturny;9}1011intmain()12{13intm,n;1415cout>m>>n;1718cout23usingnamespacestd;45doublemy_power(doublex,intk);//函数声明67intmain()8{9doublex;1011x=my_power(3,3)+my_power(4,4);12cout23usingnamespacestd;45intmy_power2(intk);//compute2^k,函数声明67intmain()8{9longx,x0;10inty=0,k=0,t;1112cout>x;1415x0=x;16while(x!
=0)17{18t=x%10;19if(t==1)y=y+my_power2(k);20x=x/10;21k=k+1;22}2324cout2#include34usingnamespacestd;56doublemysin(doublex)//computesin(x)4.
1函数的声明、定义与调用·37·7{8doubley,ak;9intk;1011y=0.
0;12k=1;13ak=x;14while(fabs(ak)>=1e-15)//fabs-->absolutevalue15{16y=y+ak;17ak=-ak*x*x/(2*k*(2*k+1));18k=k+1;19}2021returny;22}2324intmain()25{26doublex,y;2728cout>x;3031y=mysin(x);3233cout23usingnamespacestd;45boolissymm(longn)//判别回文数6{7longi,m;89i=n;10m=0;1112while(i)13{14m=m*10+i%10;·38·第四讲函数15i=i/10;16}17return(m==n);18}1920intmain()21{22longm;2324for(m=11;m2#include34usingnamespacestd;56intmain()7{8intseed,x;910cout>seed;1213srand(seed);14x=rand();15cout2#include34usingnamespacestd;56constintn=20000;78intmain()9{10clock_tt0,t1;11doubletotaltime;12inti,j,a;1314t0=clock();1516//程序开始17for(i=0;i2#include34usingnamespacestd;56constintn=20000;78intmain()9{·40·第四讲函数10time_tt0,t1;11inti,j,a;1213t0=time(NULL);1415//程序开始16for(i=0;i2#include3#include45usingnamespacestd;67intmain()8{9intx,guess,flag=1;10intN=7;1112srand(time(NULL));13x=rand()%100+1;1415cout>guess;21if(guessx)cout23usingnamespacestd;45voidswap_old(inta,intb)//值传递6{7intt=a;8a=b;b=t;9}1011voidswap_new(int&a,int&b)//引用传递12{13intt=a;14a=b;b=t;15}1617intmain()18{19intx=5,y=8;20·42·第四讲函数21cout0.
1#include23usingnamespacestd;45intfactorial_loop(intn);//普通方式(循环)6intfactorial_recursion(intn);//递归方式78intmain()9{10intn,y;11cout>n;1314y=factorial_loop(n);1516cout23usingnamespacestd;45voidmove(charsrc,chardest)//移动一个盘子:从src到dest6{7cout>m;28cout2#include34usingnamespacestd;56inlinedoublef(doublex)//内联函数7{8return2*x*x-1;//f(x)=2x^2-19}1011intmain()12{1314cout23usingnamespacestd;45namespacemynames6{7intk=3;·46·第四讲函数8doublepi=3.
14;9intmy_max(intx,inty);10doublemy_power(doublex,intk);11}1213intmain()14{15usingmynames::my_power;16usingmynames::k;1718doublex=3.
0,y;1920//couty)returnx;31elsereturny;32}3334doublemynames::my_power(doublex,intk)35{36if(k==1)returnx;37else38{39doubley=1.
0;4041for(inti=1;i23usingnamespacestd;45intadd(intx=1,inty=2);//声明时设定缺省值67intmain()8{9intadd(intx=3,inty=4);//不同作用域的声明可设定不同的缺省值10cout23usingnamespacestd;45intadd(intx=1,inty=2)//定义时设定缺省值6{7returnx+y;8}910intmain()11{12intadd(intx=3,inty=4);//不同作用域的声明可设定不同的缺省值13cout23usingnamespacestd;45intadd(intx,inty)6{returnx+y;}78doubleadd(doublex,doubley)//函数名与上面相同,但形参不同9{returnx+y;}1011intmain()12{13inta=1,b=2;14doublex=1.
1,y=2.
2;1516cout//按标准方式导入头文件,即在系统目录中寻找指定的文件#include"文件名"//先在当前目录中寻找,然后再按标准方式搜索表4.
1.
常用头文件iostream基本输入输出iomanip操纵符cmath数学函数ctime计时函数,clock,time,clock_t,time_t,CLOCKS_PER_SEC,.
.
.
cstdlibabs,rand,srand,system,.
.
.
cstringC语言字符串操作cctypeC语言字符操作cstdioC语言文件操作:fopen,fclose,fread,fwrite,printf,scanf,.
.
.
·50·第四讲函数string字符串类vector向量类fstream文件流定义符号常量§¤1#definePI3.
14159//定义符号常量2#undefPI//删除由#define定义的符号常量在很多情况下可以由const实现该功能.
此外,#define还可以用来定义带参数的宏,但也可以用内联函数取代.
条件编译§¤#if常量表达式程序正文//当"常量表达式"非零时编译#endif§¤#if常量表达式程序正文//当"常量表达式"非零时编译#else程序正文//否则编译这段程序#endif§¤#if常量表达式1程序正文//当"常量表达式1"非零时编译#elif常量表达式2程序正文//否则,当"常量表达式2"非零时编译#elif常量表达式3程序正文//否则,当"常量表达式3"非零时编译.
.
.
.
.
.
#else程序正文//否则编译这段程序#endif§¤#ifdef标识符程序正文//当"标识符"已由#define定义时编译#else程序正文//否则编译这段程序#endif4.
7预编译处理与多文件结构·51·§¤#ifndef标识符程序正文//当"标识符"没有定义时编译#else程序正文//否则编译这段程序#endif多文件结构一个程序可以由多个文件组成,编译时可以使用工程/项目来组合.
若使用命令行编译,则需要同时编译.
外部变量:如果需要用到其它文件中定义的变量,则需要用extern声明其为外部变量.
§¤extern类型名变量名;外部函数:如果需要用到其它文件中定义的函数,则需要用extern声明其为外部函数.
§¤extern函数声明/函数原型;系统函数(1)标准C++函数(库函数):参见http://www.
cppreference.
com(2)C++的系统库中提供了几百个函数可供程序员直接使用,使用库函数时要包含相应的头文件;(3)非标准C++函数:操作系统或编译环境提高的系统函数充分使用库函数不仅可以大大减少编程工作量,还可以提高代码可靠性和执行效率.
例4.
18用蒙特卡洛(MonteCarlo)方法计算定积分∫π20sin(x)dx的近似值.
分析:定积分计算∫baf(x)dx=limxi→0∑xif(ξi)(xi=xixi1,ξi∈[xi1,xi])=limn→∞n∑i=1hf(xi)(取xi=hban,ξi=xi)=limn→∞hn∑i=1f(xi).
1#include2#include3#include4#include56usingnamespacestd;78constintn=100000;//随机点的个数9constdoublea=0.
0;10constdoubleb=atan(1)*2;//b=pi/2·52·第四讲函数11constdoublelength=b-a;//区间长度1213intmain()14{15longdoubleS=0.
0;16doublex;1718srand(time(0));19for(inti=1;ib).
由此,可以通过递归方法(辗转求余法,类似于辗转相除法)设计求解最大公约数的高效算法.
练习4.
11用蒙特卡洛(MonteCarlo)投点法计算定积分∫π20sin(x)dx的近似值的近似值.
第五讲数组数组:具有一定顺序关系的若干相同类型变量的集合体.
5.
1一维数组一维数组的声明:§¤类型标识符数组名[n]//声明一个长度为n的一维数组(向量)(1)数组名存放的是数组在内存中的首地址;(2)类型标识符:数组元素的数据类型;(3)n为数组的长度,即元素的个数,可以是一个表达式,但值必须是一个确定的正整数.
一维数组的引用:§¤数组名[k]//注:下标k的取值为0到n-1(1)只能逐个引用数组元素,而不能一次引用整个数组;(2)数组元素在内存中顺序存放,它们的地址是连续的;(3)数组名代表数组存放在内存中的首地址.
注意:数组的下标不能越界,否则可能会引起严重的后果!
例5.
1一维数组举例.
1#include2#include34usingnamespacestd;56intmain()7{8inti,x[5];910for(i=0;i2#include3#include45usingnamespacestd;67constintn=1000;//矩阵维数89intmain()10{11clock_tt0,t1;12inti,j;13doubleH[n][n],x[n],y[n];1415t0=clock();16for(i=0;i2#include34usingnamespacestd;56voidmy_swap(inta[],intb[],intn)7{8intt,i;910for(i=0;i2#include3#include4·58·第五讲数组5usingnamespacestd;67constintm=3,n=4;//矩阵维数89voidsum_col(doubleA[][n],doubles[])//计算矩阵列和,只能省略第一维的大小10{11inti,j;12for(j=0;j2#include34usingnamespacestd;56intmain()7{8constintn=5;9inta[n]={0,2,4,6,8};1011//第一种方式:数组名与下标12coutpa[i]*(pa+i)*(a+i)6.
3指针与二维数组在C++中,二维数组是按行存储的,可以理解为由一维数组组成的数组.
6.
3指针与二维数组·65·引用二维数组的元素可以通过数组名和指针运算进行;对于二维数组A,虽然A、A[0]都是数组首地址,但二者指向的对象不同:(1)A[0]是一维数组名,它指向的是行数组A[0]的首元素,即*A[0]与A[0][0]等价;(2)A是二维数组名,它指向的是它的首元素,而它的元素都是一维数组(即行数组),因此*A与A[0]等价.
另外,它的指针移动单位是"行",所以A+i对应的是第i个行数组,即A[i].
§¤1intA[2][3]={{1,2,3},{7,8,9}};2int*p=A[0];//OK,p指向A[0][0]3int*p=A;//ERROR!
p只能指向一个具体的整型变量,不能是向量指针与二维数组:设指针pa=&A[0][0],则A[i][j]*(pa+n*i+j)//这里假定A的列数为n例6.
2指针与二维数组举例.
1#include2#include34usingnamespacestd;56constintn=5;78intmain()9{10intA[n][n];11inti,j;1213for(i=0;i2#include3#include45usingnamespacestd;67intGcd(intx,inty)//最大公约数8{9intz=x;10if(x>y)z=y;//取x和y中的最小值11for(inti=z;i>=1;i--)12if(x%i==0&&y%i==0)returni;13}1415intLcm(intx,inty)//最小公倍数16{17for(inti=1;i>a>>b;2930pf=Gcd;//pf指向函数Gcd31cout2#include36.
6持久动态内存分配·69·4usingnamespacestd;56intmain()7{8intN=5;910int*pa=newint[N]();11int*pb=newint[N]{1,2,3,4,5};//初始化列表,C++11新标准1213for(inti=0;i2#include34usingnamespacestd;56voidfindNprime(int*p,intN)//寻找前N个素数7{8intn=2,k=0,flag,i;9while(k>N;·70·第六讲指针与字符串数组2526int*pa=newint[N];2728findNprime(pa,N);2930cout>str;//输入单个字符串,中间不能有空格cin.
getline(str,N,结束符);//整行输入6.
7字符串(字符数组)·71·(1)cin.
getline→连续输入多个字符(可以有空格)直到读入N-1个字符为止,或遇到指定的结束符.
(2)不存储结束符,结束符可以省略,缺省为'\n'(换行符)单个字符的输入§¤ch=getchar();//ch为字符变量字符串相关函数(头文件cstring和cstdlib)函数含义strlen(str)求字符串长度strcat(dest,src)字符串连接strcpy(dest,src)字符串复制strcmp(str1,str2)字符串比较atoi(str)将字符串转换为整数atol(str)将字符串转换为longatof(str)将字符串转换为doubleitoa(int,str,raidx)将整数转换为字符串这些函数只能作用在字符串上,不能作用在字符上.
C++字符检测函数(头文件cctype)函数含义示例isdigit是否为数字isdigit('3')isalpha是否为字母isalpha('a')isalnum是否为字母或数字isalnum('c')islower是否为小写islower('b')isupper是否为大写isupper('B')isspace是否为空格isspace('')isprint是否为可打印字符,包含空格isprint('A')isgraph是否为可打印字符,不含空格isgraph('a')ispunct除字母数字空格外的可打印字符ispunct('*')iscntrl是否为控制符iscntrl('\n')tolower将大写转换为小写tolower('A')toupper将小写转换为大写toupper('a')以上检测和转换函数只针对单个字符,而不是字符串.
字符与整数的运算·72·第六讲指针与字符串数组(1)字符参加算术运算时,自动转换为整数(按ASCII码转换)6.
8上机练习练习6.
1引用与指针作为函数参数练习6.
2前100个素数,用三种方式在屏幕上输出练习6.
3矩阵乘积,用指针实现练习6.
4统计满足要求的数字个数练习6.
5二进制转十进制,用字符串实现练习6.
6字符易位破译练习6.
7围圈报数第七讲简单输入输出本讲主要内容:C++基本输入输出操作C语言格式化输出C语言文件读写7.
1C++基本输入输出(I/O)流C++本身没有输入输出语句,其输入输出是通过相应的I/O流类库实现的.
数据流:将数据从一个对象到另一个对象的流动抽象为"流".
(1)提取(读):从流对象中获取数据,运算符为"";(2)插入(写):向流对象中添加数据,运算符为"";(3)提取和插入运算符是所有标准C++数据类型预先设计的;(4)插入运算符与操纵符一起使用,可以控制输出格式.
头文件iostream中预定义的四个输入输出对象§¤cin标准输入(键盘等)cout标准输出(屏幕、打印机等)cerr标准错误输出,没有缓冲,立即被输出clog与cerr类似,但有缓冲,缓冲区满时被输出常用操纵符(头文件iomanip)操作符含义endl插入换行符,并刷新流(将缓冲区中的内容刷入输出设备)setw(n)设置域宽,若数据超过设置的宽度,则显示全部值cout.
width(n)fixed使用定点方式输出scientific使用指数形式输出setfill(c)设置填充,c可以是任意字符,缺省为空格,cout.
fill(c)如setfill('*'),setfill('0')setprecision(n)设置输出的有效数字个数,若在fixed或scientific后使用,则设置小数位数left左对齐right右对齐(缺省方式)73·74·第七讲简单输入输出showpoint显示小数点和尾随零(即使没有小数部分)除setw和cout.
width外,其它操纵符一直有效,直到遇到相同类型的操纵符为止.
例7.
1操纵符的使用:域宽和填充.
1#include2#include3#include45usingnamespacestd;67intmain()8{9intA[3][3]={{11,12,13},{21,22,23},{31,32,33}};1011cout2#include3#include45usingnamespacestd;67intmain()8{7.
2C语言格式化输出·75·9doublea[3]={2.
7182818,31.
416,987000};1011cout转义字符"(2)格式字符串:以%开头,后面跟格式说明符和其它选项§¤%[flag][输出最小宽度][.
精度]格式说明符§¤1intk=5;2doublea=3.
14;3printf("k=%d,a=%f\n",k,a);//普通字符串按原样输出;一个格式字符串对应一个变量.
常见的格式说明符c字符型g浮点数(系统自动选择输出格式)d十进制整数o八进制e浮点数(科学计数法)s字符串f浮点数(小数形式)x/X十六进制·76·第七讲简单输入输出常见的转义字符(输出特殊符号)\b退后一格\t水平制表符\f换页\\反斜杠\n换行\"双引号\r回车%%百分号7.
3C语言文件读写文件读写基本步骤:打开文件、读取或写入、关闭文件文件指针:FILE*pf;文件类型-按数据的组织形式划分:文件文件和二进制文件.
文件打开§¤pf=fopen(文件名,打开方式);(1)文件名:普通字符串(2)打开方式:指定读写方式和文件类型,取值有§¤rt、wt、at、rb、wb、ab、rt+、wt+、at+、rb+、wb+、ab+r为读,w为写,+为读写,t为文本,b为二进制(3)若文件打开成功,返回指向文件的指针;若打开失败,则返回一个空指针(NULL)文件关闭:fclose(pf);(1)正常关闭返回值为0;出错时,返回值为非0写文本文件§¤fprintf(pf,"格式控制字符串",输出变量列表);(1)fprintf的用法与printf类似例7.
3C语言文件读写:文本文件1#include2#include3#include4#include56usingnamespacestd;78intmain()9{10FILE*pf;1112pf=fopen("out1.
txt","wt");7.
3C语言文件读写·77·1314doublepi=3.
1415926;15fprintf("pi=%-12.
6f\n",pi);//在屏幕上输出16fprintf(pf,"pi=%-12.
6f\n",pi);//写入到文件中1718fclose(pf);1920return0;21}读文本文件§¤fscanf(pf,"格式控制字符串",地址列表);写二进制文件§¤fwrite(buffer,size,count,pf);将count个长度为size的连续数据写入到pf指向的文件中,buffer是这些数据的首地址(可以是指针或数组名)读二进制文件§¤fread(buffer,size,count,pf);从pf指向的文件中读取count个长度为size的连续数据,buffer是存放这些数据的首地址(可以是指针或数组名)例7.
4C语言文件读写:二进制文件.
1#include2#include3#include4#include56usingnamespacestd;78intmain()9{10intA[3][3]={{11,12,13},{21,22,23},{31,32,33}};1112FILE*pf;1314pf=fopen("data1.
dat","wb");//写文件15fwrite(A,sizeof(int),9,pf);//也可以写为fwrite(A,sizeof(A),1,pf);16fclose(pf);1718pf=fopen("data1.
dat","rb");//读文件19intB[3][3];20fread(B,sizeof(int),9,pf);21fclose(pf);2223cout2#include3#include4#include56usingnamespacestd;78intfindmin(int*px,intn)//找出最小值所在的位置9{10intidx=0,xmin=*px;11for(inti=1;i0&&x(i)>key)6x(i+1)=x(i);%将前面k个元素中大于x(k+1)的数据向后移动7i=i-1;8end9x(i+1)=key;%将当前元素插入合适的位置10end8.
4希尔排序希尔排序又称为"缩小增量排序"(DiminishingIncrementSort),由D.
Shell于1959年提出,是对插入排序的改进.
基本过程(1)把序列按照某个增量(gap)分成几个子序列,对这几个子序列进行插入排序;(2)不断缩小增量,扩大每个子序列的元素数量,并对每个子序列进行插入排序;(3)当增量为1时,子序列就是整个序列,而此时序列已经基本有序了,因此只需做少量的比较和移动就可以完成对整个序列的排序.
出发点:插入排序在元素基本有序的情况下,效率很高.
增量(gap)的选取:初始值可设为n/2,然后不断减半.
例8.
3希尔排序的MATLAB实现1functionx=Sort_Shell(x,n)2gap=floor(n/2);%增量从数组长度的一半开始,每次减小一倍·82·第八讲排序算法及其C++实现3whilegap>04fork=gap+1:n5key=x(k);%需要插入的元素6i=k-gap;%对一组增量为step的元素进行插入排序7while(i>0&&x(i)>key)8x(i+gap)=x(i);9i=i-gap;10end11x(i+gap)=key;%将当前元素插入合适的位置12end13gap=floor(gap/2);14end8.
5冒泡排序基本过程描述如下:(1)走访需要排序的序列,比较相邻的两个元素,如果他们的顺序错误就把他们交换过来.
(2)不断重复上述过程,直到没有元素需要交换,排序结束.
(3)这个算法的名字由来是因为越大的元素会经由交换慢慢"浮"到数列的顶端.
具体过程(1)将第1个和第2个元素进行比较,如果前者大于后者,则交换两者的位置,否则位置不变;(2)然后将第2个元素与第3个元素进行比较,如果前者大于后者,则交换两者的位置,否则位置不变;(3)依此类推,直到最后两个元素比较完毕为止.
这就是第一轮冒泡过程,这个过程结束后,最大的元素就"浮"到了最后一个位置上.
(4)对前面n1个元素进行第二轮冒泡排序,结束后,这n1个元素中的最大值就被安放在了第n-1个位置上.
(5)对前面的n2个元素进行第三轮冒泡排序.
(6)以此类推,当执行完第n1轮冒泡过程后,排序结束.
冒泡排序的优化如果在某轮冒泡过程中没有发生元素交换,这说明整个序列已经排好序了,这时就不用再进行后面的冒泡过程,可以直接结束程序.
冒泡排序的进一步优化假设有100个数的数组,仅前面10个无序,后面90个都已排好序且都大于前面10个数字,那么在第一轮冒泡过程后,最后发生交换的位置必定小于10,且这个位置之后的数据必定已经有序了,记录下这位置,第二次只要从数组头部遍历到这个位置就可以了.
例8.
4冒泡排序的MATLAB实现1functionx=Sort_Bubble(x,n)2fork=1:n3flag=0;%用于记录是否有需要交换的元素4fori=1:n-k5ifx(i)>x(i+1)6flag=1;8.
6快速排序·83·7t=x(i);x(i)=x(i+1);x(i+1)=t;8end9end10ifflag==0%如果这次遍历没有元素需要交换,那么排序结束11break;12end13end8.
6快速排序快速排序是目前最常用的排序算法之一,它采用的是分而治之思想:将原问题分解为若干个规模更小但结构与原问题相似的子问题,然后递归求解这些子问题,最后将这些子问题的解组合为原问题的解.
具体实现过程:(1)随机选定其中一个元素作为基准数(pivot)(通常可采用第一个元素),然后通过循环和比较运算,将原序列分割成两部分,使得新序列中在该基准数前面的元素都小于等于这个元素,而其后面的元素都大于等于这个元素.
(这时基准数已经归位)(2)依此类推,再对这两个分割好的子序列进行上述过程,直到排序结束.
(递归思想,分而治之)第一步的具体实现:(假定基准数的值为a,原始位置是i1=1)(1)先从原序列的最右边开始,往左找出第一个小于a的数,然后将该数与基准数交换位置,设基准数新位置为i2;(2)从i1右边的位置开始,往右找出第一个大于a的数,然后将该数与基准数交换位置,设基准数新位置为i3;(3)从i2左边的位置开始,往左找出第一个小于a的数,然后将该数与基准数交换位置,设基准数新位置为i4;(4)从i3右边的位置开始,往右找出第一个大于a的数,然后将该数与基准数交换位置,设基准数新位置为i5;(5)不断重复以上过程,遍历整个序列.
事实上,可以不用交换,而是先把基准数保留,然后直接覆盖即可.
后面步骤:(1)对基准数所在位置前面的子序列和后面的子序列,分别重复第一步的过程(2)不断重复以上过程,通过递归实现排序.
快速排序还有很多改进版本,如随机选择基准数,区间内数据较少时直接用其它的方法排序以减小递归深度等等.
有兴趣的同学可以深入研究.
例8.
5快速排序的MATLAB实现1functionsort_quick(left,right)2globalx·84·第八讲排序算法及其C++实现34ifleft=pivot17right=right-1;18end19%将这个比中间元素小的元素移到前半部分20if(left3#include45usingnamespacestd;67classClock//时钟类的声明8{9public://外部接口,公有成员函数·90·第九讲类与对象I:面向对象基础10voidSetTime(inth=0,intm=0,ints=0);11voidShowTime();1213private://私有数据成员14inthour,minute,second;15};1617voidClock::SetTime(inth,intm,ints)18{hour=h;minute=m;second=s;}1920voidClock::ShowTime()21{22cout3#include45usingnamespacestd;67classClock8{9public:10Clock();//不带形参的构造函数11Clock(intx,inty,intz);//带三个形参的构造函数12voidShowTime();1314private:15inthour,minute,second;16};1718//不带形参的构造函数19Clock::Clock()20{hour=0;minute=0;second=0;}2122//带三个形参的构造函数23Clock::Clock(intx,inty,intz)24{hour=x;minute=y;second=z;}252627inlinevoidClock::ShowTime()28{cout3#include45usingnamespacestd;67classPoint//Point类的声明8{9public://外部接口10Point(inta=0,intb=0)//构造函数11{x=a;y=b;}12Point(constPoint&);//复制构造函数,常引用(对象的常引用将在后面介绍)13intGetx(){returnx;}1415private://私有数据16intx,y;17};1819//复制构造函数20Point::Point(constPoint&p)21{22x=p.
x;y=p.
y;23cout5#include67usingnamespacestd;89constfloatpi=3.
1415927;//给出pi的值10constfloatprice1=35.
0;//栅栏的单价11constfloatprice2=20.
0;//过道造价1213classCircle//声明类Circle14{15public://外部接口16Circle(floatx){r=x;}//构造函数17floatCircum();//计算圆周长18floatArea();//计算圆面积1920private://私有数据成员21floatr;22};2324//成员函数25floatCircle::Circum()//计算圆的周长26{27return2*pi*r;28}2930floatCircle::Area()//计算圆的面积31{32returnpi*r*r;33}343536intmain()//主函数37{38floatx,y,z;3940cout>x;4243CirclePool(x);//小圆:游泳池44CirclePoolRim(x+3);//大圆:游泳池加过道4546//计算栅栏造价并输出·96·第九讲类与对象I:面向对象基础47y=PoolRim.
Circum()*price1;48cout2#include34usingnamespacestd;56classPoint//声明类Point7{8public://外部接口9Point(doublenewx=0,doublenewy=0)//构造函数10{x=newx;y=newy;}11doublegetx(){returnx;}//获取横坐标12doublegety(){returny;}//获取纵坐标1314private://私有数据成员15doublex,y;16};1718classLine//声明类Line19{20public://外部接口21Line(doublexA,doubleyA,doublexB,doubleyB):A(xA,yA)//构造函数22{B=Point(xB,yB);}23doublegetlength();//计算线段的长度2425private://私有数据成员26PointA,B;27intx,y;28};2930doubleLine::getlength()//计算线段的长度31{32doublexx,yy;33xx=B.
getx()-A.
getx();34yy=B.
gety()-A.
gety();35returnsqrt(xx*xx+yy*yy);36}3738intmain()//主函数39{40LineAB(1,2,4,6);4142cout2#include34usingnamespacestd;56classPoint//Point类的声明7{8public://外部接口9Point(doublea,doubleb)//构造函数10{x=a;y=b;}11voidmycout()10.
4静态成员·101·12{13cout23usingnamespacestd;45classBox6{7public:8Box(int,int);9intvolume();1011staticintheight;//把height定义为公有的静态数据成员12intwidth;13intlength;14};1516intBox::height=10;//对静态数据成员height初始化1718Box::Box(intwid,intlen)//通过构造函数对width和length赋初值19{20width=wid;length=len;21}2223intBox::volume()//计算体积24{25return(height*width*length);//直接使用静态数据成员height26}2728intmain()29{30Boxa(15,20),b(20,30);3132cout23usingnamespacestd;45classStudent6{7public:8Student(intnum=0,floats=0)9{id=num;score=s;}10voidtotal();//普通成员函数11staticfloataverage();//静态成员函数1213private:14intid;15floatscore;16staticfloatsum;//静态数据成员17staticintcount;//静态数据成员18};1920floatStudent::sum=0;//对静态数据成员初始化21intStudent::count=0;//对静态数据成员初始化·104·第十讲类与对象II:面向对象进阶2223voidStudent::total()//普通成员函数24{25sum+=score;//计算总分,访问普通数据成员26count++;//已统计的人数,访问静态数据成员27}2829floatStudent::average()//静态函数30{31return(sum/count);32}3334intmain()35{36Studentstu[4]={Student(1001,70),37Student(1002,78),38Student(1005,98),39Student()};40intn;41cout>n;//统计前n名学生的平均成绩4344for(inti=0;i2#include34usingnamespacestd;56classMyclass7{107·108·第十一讲类与对象III:面向对象提高8public:9Myclass(intx,inty);10voiddisplay()const;//常函数成员11voidshow();1213private:14constinta;//常数据成员15intb;16};1718Myclass::Myclass(intx,inty):a(x)//常数据成员的初始化19{20b=y;21}22//也可以写为:Myclass::Myclass(intx,inty):a(x),b(y){}23//但不能是:Myclass::Myclass(intx,inty){a=x;b=y;}2425voidMyclass::display()const26{27cout2#include3#include45usingnamespacestd;67classComplex8{9public:10Complex(doublea,doubleb){x=a;y=b;}11doublemydist(constComplex&);//形参是常引用12doublemydistnew(Complex&);//形参是普通引用1314private:15doublex,y;16};1718doubleComplex::mydist(constComplex&z)19{20doublexx=x-z.
x;21doubleyy=y-z.
y;2223returnsqrt(pow(xx,2)+pow(yy,2));24}2526doubleComplex::mydistnew(Complex&z)27{28doublexx=x-z.
x;29doubleyy=y-z.
y;3031returnsqrt(pow(xx,2)+pow(yy,2));32}3334intmain()35{36Complexa(2.
5,3.
8),b(-4.
2,5.
6);//普通对象37constComplexc(9.
4,10.
2);//常对象3839//常引用40cout成员名(*对象指针名).
成员名-其中前面一种方式是对象指针所特有的使用方式,推荐使用.
(3)指向常对象的指针:const类名*对象指针名this指针:隐含在非静态成员函数中的特殊指针,永远指向目的对象.
(1)this指针是常指针;(2)当局部作用域中声明了与类成员同名的标识符(如变量名)时,可以通过this指针来访问目的对象的成员;(3)当通过一个对象来调用成员函数(非静态成员函数)时,系统会把该对象的起始地址赋给this指针11.
3动态对象·111·非静态成员函数有this指针,而静态成员函数没有this指针(静态成员函数没有目的对象!
)指向非静态成员的指针:直接指向类的成员(数据成员或函数成员)§¤类型标识符类名::*指针名//指向数据成员,与普通指针的区别:加类名类型标识符(类名::*指针名)(参数)//指向函数成员(1)指向数据成员的指针的赋值:§¤指针名=&类名::数据成员名(2)指向数据成员指针的引用:§¤对象名.
*指针名对象指针名->*指针名(3)指向函数成员指针的赋值:§¤指针名=&类名::函数成员名(4)指向函数成员指针的引用:§¤(对象名.
*指针名)(参数)(对象指针名->*指针名)(参数)指向静态成员的指针:由于对静态成员的访问不依赖于对象,因此可以通过普通的指针来访问静态成员,即声明时不用加类名.
11.
3动态对象动态对象的创建:new§¤类名*指针名=new类名()//调用不带参数的构造函数进行初始化类名*指针名=new类名(参数列表)//调用带参数的构造函数进行初始化动态对象的释放:delete§¤delete指针名动态对象使用结束后一定要用delete手工释放,否则可能会造成内存泄漏.
11.
4向量类:vectorC++提供了向量类,用于创建向量.
向量是对象,其使用与一维数组类似,但向量的长度可以根据需要自动增减,而且提供了更加丰富的成员函数,比普通数组更加灵活和方便.
·112·第十一讲类与对象III:面向对象提高包含向量类的头文件:#include向量的声明:§¤vector向量名;§¤1//创建向量对象2#include3.
.
.
.
.
.
4vectorx(10);//创建一个长度为10的整型向量,注意这里是"()",不是"[]"5vectory(3);//创建一个长度为3的双精度型向量(1)可创建不同数据类型的向量,需要指出的是,这里x是对象,不是数组名;(2)由于向量是对象,所以创建时会直接初始化(调用相应的构造函数);(3)向量的元素也可以是某个类的对象.
例11.
3向量类:创建向量.
1#include2#include34usingnamespacestd;56intmain()7{8vectorx;9vectory(10,1);//长度为10,所有元素都是11011cout2#include34usingnamespacestd;56classPoint7{8public:9point(){x=0;y=0;}11.
4向量类:vector·113·10Point(inta,intb){x=a;y=b;}11voidDisplay()12{coutxy1314private:15intx,y;16};1718intmain()19{20vectorx;//长度为02122cout();//缺省构造函数,创建一个空向量,即长度为0的向量vector(intn);//创建长度为n的向量vector(intn,constType&a);//创建长度为n的向量,并用a初始化所有分量vector(constvector&x);//复制构造函数,用向量x初始化新向量.
.
.
.
.
.
这里的Type可以是基本数据类型,如int,float,double等,也可以是类名,如Point.
§¤1vectorx(100);//创建长度为100的整型向量,值全部为02vectory(10,2.
1);//创建长度为10的单精度型向量,初值都是2.
13vectorz;//创建一个双精度型的向量,长度为0创建向量时,如果是基本数据类型,且只指定长度,如上面代码的第一行,则所有分量都会被初始化为0.
如果分量是都是某个类的对象,则创建向量时可以不指定长度(即长度为0),然后通过成员函数push_back()逐步添加元素.
向量的基本操作:·114·第十一讲类与对象III:面向对象提高v[k]下标运算,返回第k个分量的值(下标从0开始)v1=v2赋值运算(复制)v1==v2比较运算,个数和值都相等时返回真,否则为假v1!
=v2比较运算,不相等时返回真,否则为假比较运算,按字典顺序进行比较常用成员函数:at(intk)返回下标为k的分量size()返回向量的长度clear()清空向量中的数据,只清空数据,不改变向量长度empty()判断向量是否为空(长度为0)front()返回第一个分量的值back()返回最后一个分量的值push_back(数据)在向量末尾插入一个数据pop_back()删除最后一个分量swap(vector&)交换目的向量与指定向量(形参)的值一定要铭记:向量名不是地址!
向量是对象.
§¤1//误区:下标只能用于获取已存在的元素,所以下面的用法是不对的2vectorx;3for(intk=0;kx;8for(intk=0;kx(10);13for(intk=0;k2#include11.
5字符串类:string·115·3#include45usingnamespacestd;67boolis_prime(intn)8{9if(n&x)17{18for(intk=a;kx;2930cout>a>>b;3233find_prime(a,b,x);3435coutab§¤1#include//注意不是cstring2.
.
.
.
.
.
·116·第十一讲类与对象III:面向对象提高3stringstr;//创建一个空字符串对象4stringstr1="Math.
",str2("ECNU");//创建字符串对象并初始化5stringstr3=str1+str2;//str3="Math.
ECNU"6stringstr4(5,'c');//创建长度为5的字符串,分量都是字符'c'string类构造函数(部分):§¤string();//缺省构造函数string(conststring&);//复制构造函数string(constchar*s);//用字符串常量进行初始化string(conststring&s,unsignedintp,unsignedintn);//从位置p开始,取n个字符,即s[p],.
.
,s[p+n-1]string(constchar*s,unsignedintn);//使用s的前n个字符进行初始化string(unsignedintn,charc);//给定的字符重复n次.
.
.
.
.
.
字符串的输入输出:§¤cin>>str;cout2#include3#include45usingnamespacestd;67longbin2dec(conststring&str);89intmain()10{11stringstr;1213cout运算符重载运算符重载基本规则:(1)只能重载已有的运算符;(2)重载不改变运算符的优先级和结合率;(3)运算符重载不改变运算符的操作数的个数;(4)重载的运算符的功能应该与已有的功能类似;(5)运算符重载是为了满足新数据类型(类与对象)的需要,因此要求至少有一个操作数是新类型的数据(这里可以理解为自定义类的对象)四个不能被重载的运算符:§¤12.
2怎么实现运算符重载运算符重载的一般形式:(以成员函数方式为例)(1)声明:§¤类型标识符operator运算符(形参列表);(2)定义:可以在声明时直接定义,也可以在类内部只声明,然后在类外部定义,此时需加上类名,即§¤类型标识符类名::operator运算符(形参列表){函数体;}12012.
3运算符重载:成员函数方式·121·§¤1//重载加法运算,使得Complex类对象也能相加2ComplexComplex::operator+(Complex&c2)3{4returnComplex(real+c2.
real,imag+c2.
imag);5}这里的类型标识符可以是类名或基本数据类型.
运算符重载的两种实现方式:成员函数和非成员函数(即外部函数).
12.
3运算符重载:成员函数方式特点:(1)可以自由访问本类对象的(私有)数据成员;(2)运算符重载为成员函数时,形参个数可以少一个;(3)若是双目运算,则左操作数就是目的对象本身,可使用this指针来调用;(4)若是单目运算,则目的对象就是操作数,不需要其它形参(注:后置++和后置--除外)例12.
1运算符重载:有理数加法运算.
1#include23usingnamespacestd;45classRational6{7public:8Rational(){x=0;y=1;}9Rational(intx,inty){this->x=x;this->y=y;}10Rationaloperator+(constRational&p);11voidDisplay(){cout2#include34usingnamespacestd;56classClock//时钟类7{8public:9Clock(intH=0,intM=0,intS=0);10voidShowTime()const;11Clockoperator+前置单目运算符重载12Clockoperator++(int);//后置单目运算符重载1314private:15inthour,minute,second;16};1718Clock::Clock(intH,intM,intS)//构造函数19{20if(0=60)38{39second-=60;40minute++;41if(minute>=60)42{43minute-=60;44hour=(++hour)%24;45}46}47return*this;48}4950ClockClock::operator++(int)//后置单目运算符重载51{//注意形参表中的整型参数52Clockold=*this;53++(*this);//调用前置++54returnold;55}5657intmain()58{59ClockmyClock(23,59,59);6061cout2#include34usingnamespacestd;56classRational7{8public:9Rational(){x=0;y=1;}10Rational(intx,inty){this->x=x;this->y=y;}11voidDisplay(){cout2#include34usingnamespacestd;56classClock7{8public:9Clock(intH=0,intM=0,intS=0);10voidShowTime()const;11friendClockoperator++(Clock&);//前置单目运算符重载12friendClockoperator++(Clock&,int);//后置单目运算符重载1314private:15inthour,minute,second;16};1718Clock::Clock(intH,intM,intS)//构造函数19{20if(0=60)38{39c1.
second=c1.
second-60;40c1.
minute=c1.
minute+1;41if(c1.
minute>=60)42{43c1.
minute=c1.
minute-60;44c1.
hour=(c1.
hour+1)%24;45}46}47returnc1;48}4950Clockoperator++(Clock&c1,int)//后置单目运算符重载51{52Clockold=c1;53++(c1);//调用前置++54returnold;55}5657intmain()58{59ClockmyClock(23,59,59);6061cout>,其他运算符则既可以用成员函数方式重载,也可以用非成员函数重载.
采用何种方式,建议如下:单目运算符,建议用成员函数方式重载;双目运算符,如果两个操作数的地位是平等的,且不改变操作数的值,则建议用非成员函数方式;双目运算符,如果两个操作数的地位不平等,则建议用成员函数方式,如+=等.
12.
6运算符[]的重载·127·12.
6运算符[]的重载为什么要重载[]在数组运算中,可以通过[]来引用指定位置的元素.
现在对于Rational类,我们希望用r[0]表示分子,r[1]表示分母,怎么实现我们可以这么做:§¤1//成员函数方式重载2intRational::operator[](intidx)3{4if(idx==0)5returnx;6else7returny;8}这样,我们就可以用r[0]代表分子,r[1]代表分母了,如:§¤1Rationala(4,5);2cout23usingnamespacestd;45classRational6{7public:8Rational(){x=0;y=1;}9Rational(intx,inty){this->x=x;this->y=y;}10voiddisplay(){cout>必须以非成员函数重载(这两个运算符的重载涉及到输入输出流,将在文件流中介绍)算术运算符和关系运算符建议以非成员函数方式重载,以便实现一些简单的自动类型转换.
12.
8自动类型转换为什么要自动类型转换→实现对象与基本数据类型变量之间的运算.
怎么实现→将对象自动转换为基本数据类型,或者将基本数据类型自动转换为对象.
基本数据类型→对象§¤1Rationala(1,2),b;2intc=3;3b=a+c;//怎么实现-->将整数转换为有理数,然后参与运算.
(1)实现方法:构造函数,例如有理数与整数的加法运算.
12.
8自动类型转换·129·例12.
6自动类型转换:基本数据类型→对象.
1#include2#include34usingnamespacestd;56classRational7{8public:9Rational(){x=0;y=1;}10Rational(intx,inty){this->x=x;this->y=y;}11Rational(intx){this->x=x;y=1;};//构造函数,整数转有理数12Rationaloperator+(constRational&);//一定要加const!
13voiddisplay(){cout将有理数转换为双精度数,然后参与运算.
(1)实现方法:重载类型转换函数(必须以成员函数方式).
(2)声明:§¤operator转换函数名();(3)定义(假定在类外部定义):·130·第十二讲运算符重载与类型转换§¤类名::operator转换函数名(){函数体};注意:没有返回数据类型,因为转换函数名就指定了返回数据类型.
例12.
7自动类型转换:对象→基本数据类型.
1#include2#include34usingnamespacestd;56classRational7{8public:9Rational(){x=0;y=1;}10Rational(intx,inty){this->x=x;this->y=y;}11voiddisplay(){cout2#include34usingnamespacestd;56classB1//类B1,构造函数有参数7{8public:9B1(inti){cout2#include34usingnamespacestd;56classPerson//父类7{8public:9Person(string&str,intage)10{name=str;this->age=age;}11voidshow()12{coutstuid=stuid;}25voidshowStu()26{this->show();//不能直接访问name和age27cout2#include34usingnamespacestd;56classPerson//公共父类Person7{8public:9Person(conststring&str,inta)//构造函数10{name=str;age=a;}11protected://保护成员12stringname;13intage;14};1516classTeacher:virtualpublicPerson//声明Person为公用继承的虚父类17{18public:19Teacher(conststring&str,inta,conststring&tit):Person(str,a)20{title=tit;}//虚父类的构造函数带有参数,因此必须包含虚父类构造函数的直接调用21protected:22stringtitle;//职称23};2425classStudent:virtualpublicPerson//声明Person为公用继承的虚父类26{27public:28Student(conststring&str,inta,floatsco)//构造函数29:Person(str,a),score(sco){}//必须包含虚父类构造函数的直接调用30protected:31floatscore;//成绩13.
9上机练习·139·32};3334classGraduate:publicTeacher,publicStudent//多重继承35{36public:37Graduate(conststring&str,inta,conststring&tit,floatsco,floatw)38:Person(str,a),Teacher(str,a,tit),Student(str,a,sco),wage(w){}39//必须包含虚父类构造函数的直接调用40voidshow()41{42cout2#include34usingnamespacestd;56classPerson//父类7{8public:9Person(conststring&name,intage)10{11this->name=name;this->age=age;12}13virtualvoidshow()//关键字virtual,即声明为虚函数14{15coutstuid=stuid;29}30virtualvoidshow()//派生类中可以不带virtual31{32Person::show();33coutshow();//父类指针p指向父类对象,因此调用的是父类的show()4748p=&stu1;49pstu->show();//父类指针p指向派生类对象,此时调用的是派生类的show()5051return0;52}在这个例子里,p是父类指针,当它指向的对象是父类的对象时,则实现的时父类的功能,而当它指向派生类的对象时,则能实现派生类的功能.
这表明,父类指针可以按照父类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,这种现象就是多态(Polymorphism).
派生类中声明虚函数时,关键字virtual可以省略,这意味着,只要在父类中声明的虚函数,则所有派生类中的同名函数(形参也相同)都是虚函数.
虚函数提供了一种通过基类访问派生类(包括直接派生和间接派生)的功能的机制;虚函数只能借助于指针或引用才能达到多态的效果,否则无法实现多态,比如下面的例子就无法实现多态.
例14.
2虚函数举例二1//2//前面部分的程序同上例·142·第十四讲多态3//4intmain()5{6Studentstu1("XiJiajia",18,20150108);7Personp1=stu1;89p1.
show();//不能实现多态,调用的仍然是Person::show()1011return0;12}引用一旦依附到某个对象上后就不能修改,不如指针灵活,因此在多态方面缺乏表现力,所以实现多态时一般是用指针.
关于虚函数的几点说明只需在声明处加关键字virtual,函数定义(在外部定义)时不能加;如果派生类中没有对虚函数的具体定义,则仍使用父类的虚函数;构造函数不能是虚函数,但析构函数可以;对于具有复杂继承关系的大中型程序,多态可以增加其灵活性,让代码更具有表现力.
构成多态的条件:(1)必须存在继承关系;(2)继承关系中必须有虚函数,并且它们形成屏蔽关系,即父类与派生类中存在具有相同函数原型的虚函数;(3)只能通过父类的指针或引用来调用虚函数.
例14.
3虚函数举例三1#include23usingnamespacestd;45classPoint//父类6{7public:8Point(){x=0;}9virtualvoidSetpoint(){x=-1;}10virtualvoidSetpoint(floata){x=a;}11virtualvoidDisplay()12{13coutSetpoint();//OK,调用派生类的虚函数38p->Display();3940p->Setpoint(2);//OK,调用父类的函数41p->Display();4243//p->Setpoint(3,4);//ERROR44//p->Display();4546return0;47}在上例中,如果Point2D中没有把voidSetpoint(floata,floatb)声明为虚函数,则程序能否正常运行有了虚函数,就可能出现这种情况:一个函数的调用并不是在编译时刻被确定的,而是在程序运行时才被确定.
比如下面的例子,只有当程序运行时,根据指针p指向的是父类对象还是派生类对象,才能决定调用的是父类的成员函数还是派生类的成员函数.
这种现象就称为推迟联编或动态联编.
1voidfun(Person*p)2{3p->show();//Person:show()orStudent:show()程序运行是才能确定4}//同一段代码,可以产生不同效果→多态由于编写代码的时候并不能确定被调用的是父类的成员函数还是派生类的成员函数,所以称为"虚"函数.
14.
3纯虚函数与抽象类纯虚函数的声明:§¤virtual类型标识符函数名(形参列表)=0;//"=0"表示一个虚函数为纯虚函数在声明虚函数时,末尾=0,表明此函数为纯虚函数.
·144·第十四讲多态纯虚函数没有函数体.
纯虚函数用来规范派生类的行为,实际上就是所谓的"接口",它在父类中只声明不定义.
它的作用仅仅是告诉使用者,我的派生类都会有这个函数.
如果类中有纯虚函数,则表示:我是一个抽象类,不能实例化,即不能创建对象.
抽象类:含有纯虚函数的类称为抽象类.
-抽象类是一种特殊的类,它通常是为了派生而建立的,一般处于继承层次结构的较上层;-抽象类不能用来声明对象(即不能实例化),只能用来派生,所以是"抽象"的;-如果纯虚函数在派生类中也没有具体定义,则这个派生类还是个抽象类;-抽象类的主要作用是将有关的功能作为接口组织在一个继承层次结构中,由它为派生类提供一个公共的根,具体实现由派生类完成.
定义纯虚函数的一个目的就是让父类不可实例化.
事实上,有些父类只是用来派生,如动物,用它来声明对象没有任何实际意义.
例14.
4纯虚函数举例1#include2#include34usingnamespacestd;56classPerson//父类7{8public:9Person(conststring&name,intage)10{11this->name=name;this->age=age;12}13virtualvoidshow()=0;//纯虚函数14protected:15stringname;//姓名16intage;//年龄17};1819classTeacher:publicPerson//派生类20{21public:22Teacher(conststring&name,intage,conststring&title):Person(name,age)23{24this->title=title;25}26voidshow()//纯虚函数在派生类中要有定义,即实例化27{28coutstuid=stuid;42}43voidshow()//每个派生类中都要实例化,否则该派生类仍然是抽象类44{45coutshow();//Teacher::show()61coutshow();//Student::show()65cout//模板头,typename是关键字,T是形参,称为类型参数Tfun(Tx,Ty){.
.
.
}§¤template//也可以有多个类型参数T3fun(T1x,T2y){.
.
.
}(1)模板头和函数名是一个整体,为了使得代码更加清晰,模板头通常独占一行;(2)一但定义了模板函数,就可以用类型参数来声明和定义函数了,也就是说,原来使用int,float,double等数据类型标识符的地方,都可以用类型参数来代替;(3)调用模板函数时,用于传递给类型参数的实参可以是基本数据类型,也可以是用户自己定义的类.
类型参数原则上可以任意合法的标识符,如typenamemytype,但使用T,T1,T2,.
.
.
已经形成了一种惯例.
关键字typename也可以用class替代,它们没有任何区别.
C++早期对模板的支持并不严谨,没有引入新的关键字,而是用class来指明类型参数,但是class已经用在类的定义中了,这样做显得不太友好,所以后来引入一个新的关键字typename,专门用来定义类型参数.
不过至今仍然有很多代码在使用class,包括C++标准库、一些开源程序等.
例14.
5模板函数举例一1#include23usingnamespacestd;45template6TMax(Tx,Ty)7{8if(x>=y)9returnx;10else11returny;12}1314intmain()15{14.
5模板函数·147·16inta=2,b=3;17doublee=2.
2,f=3.
3;1819cout(a,b)(e,f)23usingnamespacestd;45template6T3Max(T1&x,T2&y)7{8if(x>=y)9returnx;10else11returny;12}1314intmain()15{16inta=2,b=3;17doublee=2.
2,f=2.
3;1819cout(a,e)23usingnamespacestd;45classRational6{7public:8Rational(){x=0;y=1;}9Rational(intx,inty){this->x=x;this->y=y;}10Rationaloperator+(constRational&p);11voidDisplay(){cout//模板函数24TAdd(T&x,T&y)25{26returnx+y;27}2829intmain()30{31Rationala(4,5),b(7,3),c;3233c=Add(a,b);//用类名Rational作为类型实参34cout23usingnamespacestd;45template6TDiv(Tx,Ty)7{8returnx/y;9}1011intmain()12{13inta=3,b=2;14doublee=3.
0,f=2.
0;1516cout(a,b)class类名{可以直接使用类型参数};在类外部定义成员函数时,仍然需要带上模板头:§¤template类型标识符类名::函数名(形参列表){函数体中可以直接使用类型参数};-需要注意,除模板头以外,类名后面也要带上类型参数,但不用加typename例14.
9模板类举例一1#include23usingnamespacestd;45template6classPoint7{8public:9Point(Ta,Tb);10TGetx();11TGety();1213private:14Tx,y;15};1617template18Point::Point(Ta,Tb)19{x=a;y=b;}2021template22TPoint::Getx()23{returnx;}2425template26TPoint::Gety()27{returny;}2829intmain()·150·第十四讲多态30{31PointA(4,5);32coutB(4.
2,5.
3);35cout23usingnamespacestd;45template//类型参数带缺省值6classPoint7{8public:9Point(Ta,Tb);10TGetx();11TGety();1213private:14Tx,y;15};1617template18Point::Point(Ta,Tb)19{x=a;y=b;}2021template22TPoint::Getx()23{returnx;}2425template26TPoint::Gety()27{returny;}2829intmain()30{31PointA(4.
2,5.
3);//使用缺省值32coutB(4.
2,5.
3);35cout)§¤ofstream向文件写入数据ifstream从文件读取数据15215.
3文件的打开与关闭·153·fstream可以读写文件创建文件流对象§¤fstreamfstrm;//创建一个文件流对象,未绑定到任何文件fstreamfstrm(fname);//创建一个文件流,并绑定到文件fnamefstreamfstrm(fname,mode);//创建文件流的同时指定文件的打开模式(1)这里的类fstream也可以是ifstream或ofstream(2)ifstream对象所关联的文件只能读(3)ofstream对象所关联的文件只能写15.
3文件的打开与关闭文件流对象基本操作(成员函数)§¤fstrm.
open(fname)//将文件流对象fstrm绑定到文件fnamefstrm.
close()//关闭与文件流对象fstrm绑定的文件fstrm.
is_open()//测试文件是否已顺利打开(且未关闭)(1)将文件流对象关联到其它文件时,须先关闭已绑定的文件(2)文件流对象被释放时,close会被自动调用文件打开方式§¤ios::in//只读ios::out//只写,若文件存在,则内容被清除ios::app//追加,即每次写操作均定位到文件末尾ios::ate//打开文件后立即定位到文件末尾ios::Trunc//若文件存在,则清除文件中原有的内容ios::binary//以二进制方式进行读写(1)输入输出方式是在ios类中定义的(2)以上方式可以组合使用,用"|"隔开,如ios::out|ios::binary(3)ios::app通常与ios::out组合使用(4)在缺省情形下,文件以文本方式打开(5)ifstream对象只能设定in模式,缺省为in(6)ofstream对象只能设定out模式,缺省为out(7)fstream对象可以设定in或/和out模式(8)建议使用fstream对象进行文件读写操作§¤1ifstreamifstrm;2ofstreamofstrm;3fstreamfstrm;4·154·第十五讲文件流与输出输入重载5ifstrm.
open("fname1");//以缺省方式打开6ofstrm.
open("fname2",ios::out);//指定打开方式7fstrm.
open("fname3",ios::out|ios::app);//指定打开方式15.
4文件读写:文本文件与二进制文件文本文件操作(1)文本文件的写:>或getline(3)我们是如何使用cin和cout的,就可以同样来使用文件流对象§¤1//将数据写入文本文件2fstreamfstrm("fname.
txt",ios::out);3fstrm>str1;//缺省以空格为输入结束符5fstrm.
getline(str2,12);//也可以用getline读取一整行6fstrm.
close();例15.
1文件流:文本文件的读写.
1#include2#include34usingnamespacestd;56intmain()7{8ifstreamifstrm;9ofstreamofstrm;10fstreamfstrm;1112//写文本文件13ofstrm.
open("fout.
txt");//文本文件,缺省模式为in14ofstrm>str1;//缺省以空格为结束符2021ifstrm.
getline(str2,13);//获取接下来的13个字符2215.
4文件读写:文本文件与二进制文件·155·23cout>或getline是没有意义的(2)写:使用父类ostream的成员函数write(3)读:使用父类istream的成员函数read§¤write(constchar*buf,intn);read(char*buf,intn);//buf指向内存中一段存储空间,n是读写字节数§¤1输出文件流对象.
write(buf,50);2//将buf所指定的地址开始的50个字节的内容不加转换地写到流对象中3输入文件流对象.
read(buf,30);4//从流对象所关联的文件中,读入30个字节(或至文件结尾),存放在buf所指向的内存空间内例15.
2文件流:二进制文件的读写.
1#include2#include34usingnamespacestd;56constintn=5;7intmain()8{9intA[n]={1,2,3,4,5};1011cout>·157·将文件读指针定位到文件尾部,然后用tellg获取文件读指针的位置,即可获取文件长度.
§¤1fstrm.
seekg(0,ios::end);//定位读指针到文件尾部2length=fstrm.
tellg();//文件长度=length+115.
6重载>IO标准库分别使用>执行输出和输入操作,为了使得它们也适用于新定义的类,即也能用>进行相应对象的输出和输入,需要对这两个运算符进行重载.
§¤1Rationala(1,2);2cout>例15.
3文件流:重载>1#include23usingnamespacestd;45classRational6{7public:8Rational(){x=0;y=1;}9Rational(intx,inty){this->x=x;this->y=y;}10friendostream&operator>(istream&,Rational&);//重载>>1213private:14intx,y;15};1617ostream&operator>(istream&in,Rational&a)//只能以非成员函数方式重载24{25cout>a.
x;27cout>a.
y;29returnin;30}3132intmain()33{34Rationala(1,2);·158·第十五讲文件流与输出输入重载3536cout>a;//输入39cout>时,尽量减少格式化操作!
如换行等.
15.
7上机练习练习15.
1用文件流方式进行文本文件和二进制文件的读写练习15.
2重载复数类的输出操作符>第十六讲泛化程序设计标准的C++由三个重要部分组成:核心语言:提供C++程序设计基本构件,包括变量、数据类型、控制结构等.
C++标准库:提供大量的函数,用于数学计算、字符串处理、文件操作、格式化输入输出等.
标准模板库:提供大量的模板和方法,用于操作数据结构等.
16.
1STL标准模板库16.
2容器16.
3迭代16.
4算法159附录A一些小知识A.
1π的计算C++中没有提供π,但可以通过下面的方法来实现π=4atan(1).
事实上,很多编译器会在cmath宏包中提供常量M_PI来表示足够精度的π近似值.
A.
2自动数据类型auto自动数据类型就是根据变量的值来确定变量的数据类型,如§¤1autox=3;也可以用在函数返回值类型上,如§¤1autois_prime(intn);160参考文献·161·参考文献[1]V.
Strassen,Gaussianeliminationisnotoptimal,NumerischeMathematik,3(1969),354–356.
Citedonpage58.
棉花云官网棉花云隶属于江西乐网科技有限公司,前身是2014年就运营的2014IDC,专注海外线路已有7年有余,是国内较早从事海外专线的互联网基础服务提供商。公司专注为用户提供低价高性能云计算产品,致力于云计算应用的易用性开发,并引导云计算在国内普及。目前公司研发以及运营云服务基础设施服务平台(IaaS),面向全球客户提供基于云计算的IT解决方案与客户服务(SaaS),拥有丰富的国内BGP、双线高防...
今天中午的时候有网友联系到在选择网站域名建站和主机的时候问到域名和IP地址有没有关联,或者需要注意的问题。毕竟我们在需要建站的时候,我们需要选择网站域名和主机,而主机有虚拟主机,包括共享和独立IP,同时还有云服务器、独立服务器、站群服务器等形式。通过这篇文章,简单的梳理关于网站域名和IP之间的关系。第一、什么是域名所谓网站域名,就是我们看到的类似"www.laozuo.org",我们可以通过直接记...
今天有看到Raksmart账户中有一台VPS主机即将到期,这台机器之前是用来测试评测使用的。这里有不打算续费,这不面对万一导致被自动续费忘记,所以我还是取消自动续费设置。如果我们也有类似的问题,这里就演示截图设置Raksmart取消自动续费。这里我们可以看到上图,在对应VPS主机的【其余操作】中可以看到默认已经是不自动续费,所以我们也不要担心被自动续费的。当然,如果有被自动续费,我们确实不想续费的...
转义字符为你推荐
搜狗输入法安利定制版问与答山东省水利安全生产远程教育培训平台股份一卡通系统followgoogle支持ipad支持ipad支持ipad支持ipadcss3圆角css实现圆角的几种方法是什么?photoshop技术photoshop技术对哪些工作有用?
万网域名查询 国外免费vps 北京vps主机 淘宝二级域名 百度云100as 免备案空间 wdcp 鲜果阅读 debian6 国内php空间 个人空间申请 免费个人空间申请 秒杀预告 微软服务器操作系统 能外链的相册 彩虹云 360云服务 web服务器是什么 带宽租赁 东莞主机托管 更多