C++学习笔记-2
- 输入/输出控制----I/O流
- 命名空间的定义及使用
- string类型
- 函数改进
- 域解析符::扩大全局变量的作用域
- 形式参数可带有默认值
- 函数重载
- 引用的定义与应用
- 引用的概念及使用
- 引用作为形式参数
- 引用与指针的比较
- 引用作为返回值
- 动态内存空间
- 用new申请动态内存空间
- 用delete释放动态内存空间
- 异常处理
- 异常和异常处理
- 异常处理的步骤
输入/输出控制----I/O流
输入&&输出
在C++中,将数据从一个对象到另一个对象的流动抽象为"流"。流在使用前要建立,使用后要删除
在C++中,数据的输入和输出是通过I/O流来实现的。cin和cout是系统预定义的流类对象,无需编程人员再定义。cin用来处理标准输入,即键盘输入;cout用来处理标准输出,即屏幕输出。
完成输入,不仅要用cin和流对象,还要配合使用系统预定义的提取符">>“。C++中的一般输入方式为:cin>>变量名1>>变量名2…
完成输出,不仅要用cout和流对象,还要配合使用系统预定义的插入符”<<"。C++中的一般输出方式为:cout<<表达式1<<表达式2…
文件包含
C++源程序中需要用#include<iostream>
及using namespace std
;进行文件包含,才能正确使用cin和cout进行输入/输出控制
新增加逻辑类型–boolean型
true–真,false–假;输入:非零为true,零为false;输出:1为true,0为false
在默认情况下,bool表达式的值为1或0,可以使用C++标准库中提供的boolalpha操纵符使其输出为true或false,可以用noboolalpha操纵符使输出恢复为1或0
注释标注
1、注释一般不建议嵌套使用,以免影响可读性
2、/…/方式的注释不能互相嵌套
3、//方式的注释可以嵌套
4、//方式下可以嵌套/…/注释
5、/…/方式下可以嵌套//注释
命名空间的定义及使用
using namespace std
,使用了C++的标准呢命名空间std
C++提供的命名空间std涵盖标准C++的所有定义和声明,包含C++所有的标准库。头文件iostream,在iostream文件中定义的所有变量、函数等都位于命名空间std中,使用using namespace std;
,程序员可以直接使用iostream中定义的所有变量和函数
命名空间的定义
namespace 命名空间名称 {...;}
1、定义命名空间以关键字namespace开头,命名空间名称不许是合法的用户自定义标识符
2、以一对花括号括起该命名空间的开始和结束处,右大括号后面不加分号
3、命名空间大括号内可以出现任何实体的声明或定义
使用命名空间的内容
1、命名空间名称::局部内容名
其中的"::"称为域解析符或作用域运算符,用来指明该局部内容来自哪一个命名空间,避免冲突
2、
using namespace 命名空间名称;
这样声明过后,可以直接使用该命名空间中的所有内容,不需要在内容前面附加命名空间名称和域解析符
3、
using 命名空间名称::局部内容名;
这样声明以后,可以直接使用该命名空间中这局部内容名,而该命名空间中的其余内容在使用时,仍要符加命名空间名称和域解析符
iostream这样的标准C++头文件不以".h"作为文件拓展名,这点与C标准库头文件必须以".h"作为文件拓展名是不同的
string类型
需要加
#include<string>
头文件
.insert(指定数字,自定义字符串)
在指定位数之后加入自定义字符串
.length()
求字符串的长度
.replace(数字1,数字2,自定义字符串)
在字符串索引值为数字1往后数字2个数字替换为自定义字符串
.substr(数字1,数字2)
截取数字1到数字2
.find(自定义字符串)
查找是否存在自定义字符串,若存在则返回自定义字符串在字符串的第一个下标
函数改进
域解析符::扩大全局变量的作用域
全局变量是在函数之外定义的变量,而局部变量是在函数内部定义的变量,包括形式参数
在同名局部变量的作用域内,可以在全局变量前加上域解析符"::"来访问被隐藏的同名全局变量
域解析符解决了同名局部变量与全局变量的重名问题,提供了一种在同名局部变量的作用域内访问全局变量的放啊,扩大了全局变量的作用域,使其真正全局
形式参数可带有默认值
C++允许在函数原型声明中为一个或多个形式参数指定默认参数值。这样,在调用该函数时,允许不为具有默认参数值的形式参数提供实际参数,形式参数直接使用默认参数值;如果提供了实际参数,则仍遵循参数单向值传递的规则,用实际参数来初始化形式参数(实际参数的个数小于或等于形式参数个数)
#include<irostream>
using namespace std;
void Fun(int i , int j =5, int k = 10);
//形参j和k分别制定了默认参数值5和10
int main ()
{
Fun ( 20 ); //形式参数j和k分别用默认参数值5和10
Fun ( 20 , 30 ); //形式参数k使用默认参数值10
Fun ( 20 , 30 , 40 ); //都是用实际参数初始化形参
return 0;
}
void Fun (int i , int j , int k ) //首部不再指定默认参数值
{
cout <<i<<" "<<j<<" "<<k<<endl;
}
运行结果:
20 05 10
20 30 10
20 30 40
对于形式参数带默认参数值的函数说明
1、默认参数值如果在原型中已经给定,则在下面的函数定义首部不能再提供默认参数值;如果定义在先的函数没有另外的原型声明,则默认参数值应该在函数定义首部给出
2、默认参数值给定的顺序一定是从右到左,即具有默认值的参数是从右边开始的
3、 在函数调用时,实际参数提供的顺序应该是从左到右,实际参数的个数应等于不具有默认参数值的形式参数个数。若实际参数个数小于形式参数,则为提供实际参数的形式参数使用其指定的默认参数值
4、如果指定了默认参数值的形式参数在调用时又得到了实际参数,则实际参数值优先。在调用时只有不提供对应的实际参数时,形式参数才使用默认参数值。
函数重载
在C++中,对于功能完全相同或类似,只是在形式参数的个数、类型、顺序方面有区别的不同函数,可以用相同的函数名来命名,这种情形被称为重载。实际调用时,编译器通过匹配实际参数与形式参数来确定具体调用的函数
针对相同功能,由于不同输入造成代码报错而设置的
在相同函数名下,针对不同的类型输入却有着相同功能
#include<iostream>
using namespace std;
int square(int x) //重载函数第一版本,int型参数
{
return x*x;
}
float square(float x) //重载函数第二版本,float型参数
{
return x*x
}
double square(double x=1.5) //重载函数第三版本,double型参数
{
return x*x
}
int main()
{
cout<<"square()="<<square()<<endl;
cout<<"square(10)="<<square(10)<<endl;
cout<<"square(2.5f)="<<square(2.5f)<<endl;
cout<<"square(1.1)="<<square(1.1)<<endl;
return 0;
}
//运行结果
square()=2.25
square(10)=100
square(2.5f)=6.25
square(1.1)=1.21
重载说明
1、重载函数必须具有相同的函数名,但是在形式参数的个数、类型、顺序的某一个或几个方面必须有所区别,返回值类型不是区分重载函数的要素
2、重载的函数与带默认值的函数一起使用时,有可能引起二义性,要分析清楚再使用
3、在函数调用时,如果给出的实际参数和形式参数类型不相符,C++编译器会自动做类型转换工作;如果转换成功,则程序继续运行,否则,有可能产生不可识别的错误,编译器会报错"Ambiguous call to overloaded function"。因此在调用时,最好保证实际参数的个数、类型、顺序与某一版本的重载函数的形参完全一致,以避免不必要的错误
引用的定义与应用
引用是C++语言新增加的概念,在声明时通过"&"来标记,用来为变量起别名,它主要用作形式参数以及作为函数的返回值,在程序中发挥着强大而又灵活的作用
引用的概念及使用
声明一个引用的格式
数据类型 & 引用名 = 一个已定义的变量名
说明
1、在以上声明引用的格式中,"&“不是取地址运算符,而是声明引用的一个特殊标记,与定义指针变量时的”*"号作用类似。引用名需为一个合法的用户自定义 标识符
2、在声明一个引用的同时,如果不是作为函数的参数或返回值,就必须对它进行初始化,以明确该引用是哪一个变量的别名,以后在程序中不可改变这种别名关系
3、引用被声明以后就像普通变量一样使用,使用时无需再带"&"符号,直接用引用名访问
4、因为引用知识某一个变量的别名,所以系统并不为引用另外分配内存空间,它与所代表的变量占用同一段内存空间
5并不是任何类型的数据都可以有引用,不能建立void类型引用、引用的引用、指向引用的指针、引用数组
int x=10,a[10];
int &&r=x; //错误,不能建立引用的引用
int &*p=x; //错误,不能建立指向引用的指针
int &ra[10]=a; //错误,不能建立引用数组
void &r=x; //错误,不能进阿里void类型引用
引用作为形式参数
在C++中,引用最主要的用途是作为函数的形式参数,使其在函数调用时称为实际参数变量在被调函数中的别名
在大部分情况下,使用引用形式参数是为了方便改变对应实际参数变量的值,但是在无需改变对应实际参数变量值时,用引用参数仍然比用值形式参数更高效,因为在引用参数之前增加const修饰符使其称为常引用,一旦修改常引用形式参数,编译器就会报错,无法保证实际参数的安全性
常引用和值形式参数的区别
1、常引用参数在函数中部可以修改,但是值形式参数可以修改
2、用常引用效率更高,因为无需另外分配内存空间,没有复制所需的时间开销
3、与常引用形式参数对应的实际参数只能时变量,而与值形式参数对应的实际参数是表达式(常量、变量是表达式的特殊形式),形式更灵活多样
选用建议:当实际参数需要通过改变形式参数而得到改变时,使用普通的引用形式参数与其对应;当实际参数无需通过改变形式参数得到改变时,究竟是用值形式参数还是常引用参数与其对应,需要在效率及实际参数形式上综合考虑
引用与指针的比较
引用简化程序
引用作为返回值
引用声明
类型名& 函数名(形式参数表);
在形式上与值返回函数相比,就是在返回值类型后面多了一个引用标记"&"。调用时,除了可以作为独立的函数语句、表达式中的某一个运算对象之外,还可以作为左值调用(即将函数的调用放在赋值号左边,当变量使用),这是引用作为返回值的函数的一个特别用法
Fun(a,b,c); //作为独立的函数调用语句使用,这是返回的引用的值被忽略
d=Fun(a,b,c)*2; //作为表达式中的一个运算对象使用,用其返回的引用的值
Fun(a,b,c)=20 //这是引用返回的函数特有的调用方式,相当于语句b=20;
对于引用作为返回值的函数的特殊要求
1、return后面只能是变量(引用也理解为一种特殊的变量),而不能是常量或表达式,因为只有变量才能作为左值使用
2、return后面变量的内存空间在本次函数调用结束后应当仍然存在,因此自动局部变量不能作为引用返回
3、return后面返回的不能是常引用,因为常引用是为了保护对应的实际参数变量不被修改,但是引用返回的函数作为左值必定要引起变量的修改
动态内存空间
指针是专门用于存放内存地址的特殊变量,其值只能通过赋值而不能读入。指针除了可以获得已有地址值(如变量的地址、数组中的地址)或地址常量NULL以外,还可以向系统申请动态内存空间、获得这一段空间的首地址。这样,通过指针就可以访问和申请的这段内存空间。
c中有malloc、calloc函数申请动态空间,用free释放动态空间;C++中用new和delete实现申请和释放
用new申请动态内存空间
使用malloc或new申请的动态内存空间中存放的是随机值,要是动态内存空间中有确定的值,就必须为*ptr、ptr[i]、q[i][j]赋值或读入
强制类型转换方式
(目标类型)待转换类型的表达式;
//兼容c风格
目标类型名(待转换类型的表达式);
//C++新增风格
方式1中的目标类型一定要用一堆括号括起来,待转换表达式一般也建议加圆括号;而方式2中的目标类型名不要用一对括号括起,待转换表达式一定要加圆括号,该形式看起来类似于函数调用
new运算符申请动态空间形式
new 数据类型名;
//申请单个某数据类型的动态空间,得到首地址值
new 数据类型名[整型值]
//申请连续的空间以实现动态一维数组,得到首地址值
注:
用new申请空间可能会因为内存不足等原因申请空间失败而返回NULL值,所以申请空间后需要检查,只有指针不等于NULL时,才可以访问动态空间
用delete释放动态内存空间
动态空间试用结束后,一定要及时释放这部分动态空间,否则回产生内存垃圾,影响程序正常运行,还有可能导致死机
用delete运算符释放动态内存空间
delete 指针变量;
//释放单个动态空间
delete [] 指针变量名
//释放动态一维数组空间
注:
在使用new、delete时,不要将它们与存储管理函数malloc、calloc、free混合使用,建议在C++程序中不使用C语言的存储管理函数,因为这些函数与new、delete、new[]、delete[]不同,它们不能兼容C++语言中的一些重要技术
#include<iostream>
#include<iomanip>
#include<cmath>
#include<ctime>
using namespace std;
const int N=30;
int main(){
int *p,*sum,i; //定义两个指针变量
sum=new int(0); //申请一个int型动态变量,*sum初始化为0
p=new int [N]; //判断是否成功申请
if(p==NULL){
cout<<"allocation dailure.\n";
return 0;
}
srand(time(NULL)); //包含了文件ctime和cmath
for(int i=0;i<N;i++){
p[i]=rand()%100;//生成随机数作为数组的元素值
if(p[i]%2) //判断是否为奇数
(*sum)++; //如果p[i]是奇数,则*sum值加1
}
for(i=0;i<N;i++){ //循环控制下标,输出所有元素
cout<<setw(4)<<p[i];//setw格式控制,包含iomanip文件
if((i+1)%10==0) //每10个一行
cout<<endl;
}
cout<<"the number of odd is:"<<*sum<<endl;//输出奇数个数
delete []p; //释放动态一维数组空间
delete sum; //释放动态变量*sum的空间
return 0;
}
异常处理
异常处理是C++语言的一种工具,这种工具能够对程序中某些事先可以预测的错误进行测试和处理。C++语言使用throw和try-catch语句来支持异常处理
异常和异常处理
程序中的错误可分为语法错误和运行错误两大类。语法错误又包括编译错误和链接错误,这类错误在编译链接时根据出现的错误信息可以纠正。
运行错误是编译链接通过后,程序在运行时出现的错误。这类错误通常包括不可预料的逻辑错误和可以预料的运行异常。逻辑错误常常是由于设计不当引起的(误将赋值号用作关系运算符、排序算法有误导致在边界情况下不能完成排序任务等);运行异常是由系统运行环境造成的,事先可预料(数组下标越界、内存空间不足、文件无法正常打开、0作为除数等),这类错误通过一些预防代码是可以避免的
异常
异常指的是不同、即与期望的结果不同,它是一种错误,但又不是通常意义上的错误。异常这种差错可以被定义、被发现和处理
异常处理
在执行某个函数时检查出了异常,通常不再本函数中处理,而是通过throw抛出异常的机制,将异常传送给调用它的函数(称为上级函数),它的上级函数通过catch捕捉到这个异常信息后进行处理。如果上一级的函数也不处理异常,则只好再传给更上一级函数处理。如果没有任何一级函数处理该异常,则异常可能会终止程序的执行
不在同一个函数中发现和处理异常可以使底层函数专心于实现功能,而不必关心如何处理异常。将异常交给上层函数处理,减轻了底层函数的付但,提高了程序的运行效率
异常处理的步骤
实现步骤
1、检查异常(使用try语句块)
2、抛出异常(使用throw语句块)
3、捕捉异常(使用catch语句块)
其中第1、3两步在上级函数中处理,第2步在可能出现异常的当前函数中处理
throw语句的功能使抛出异常
throw <表达式>;或throw ;
通常使用第一种形式,该语句写在可能出现遗产大哥函数体内
try-catch语句块可以用来检测、捕获并处理异常
try
{
<被进行异常检查的语句>
}
catch (<异常信息类型>或<变量>)
{
<异常处理语句>
}
try-catch语句块必须一起出现在调用可能出现异常的函数的上级函数中,并且一定是try块在先,catch块在后
try和catch块之间不能有任何其他语句。如果二者之间有其他语句,就会被系统认为"try块没有catch处理程序"以及"没有与catch处理程序关联的try块",从而导致编译出错
只能有以恶搞try块,而对应的catch块可以有多个,表示与不同的异常信息相匹配
需要检测异常的还能输调用必须放在try块中,检测到异常后如何处理的语句必须放在catch块中
C++语言中异常处理的完整过程。程序顺序执行try块中的语句。如果在执行try块内的各条语句中都没有发生异常,则跳过catch块,转到执行catch块后面的语句;如果在执行try块内的某一条语句时发生异常,则由throw抛出异常信息