p.s.本文仅为个人复习笔记,主要写的是自己不熟练的知识,一些比较基础的知识点的可能没有涉及到
数据的存储,表示形式和基本运算
c++的数据类型
基本类型:
*整型:短整型,整型,长整型
*字符型
*浮点型:单精度型,双精度型,长双精度型
派生类型:
*指针类型
*枚举类型
*数组类型
*结构体类型
*共用体类型
*类类型
空类型(void)
各类型字节数和范围
类型 | 标识符 | 字节数(1字节=8位) | 数值范围 |
整型 | [signed] int | 4 | -2147483648~2147483647(1e9数量级) |
无符号整型 | unsigned [int] | 4 | 0~4294967295 |
短整型 | short [int] | 2 | -32768~32767 |
无符号短整型 | unsighed short [int] | 2 | 0~66535 |
长整型 | long [int] | 4 | -2147483648~2147483647 |
无符号长整型 | unsighed long [int] | 4 | 0~4294967295 |
字符型(只能装英文) | [signed] char | 1 | -128~127 |
无符号字符型(同上) | unsigned char | 1 | 0~255 |
单精度型 | float | 4 | 3.4×10^(-38)~3.4×10^38 |
双精度型 | double | 8 | 1.7×10^(-308)~1.7×10^(308)) |
长双精度型 | long double | 8 | 1.7×10^(-308)~1.7×10^(308)) |
长长整型 | long long | 8 | 9×10^18这个数量级 |
*带符号的最高位存符号
*float提供6位有效数字,double提供15位有效数字
常量(无unsignd型)
进制表示:
十进制:不需要特殊表示
八进制:常数开头加数字0
十六进制:常数开头加x
浮点数表示:
十进制小数形式:3.14
指数形式:314e-2
在内存存储方式:+ | .314 | 1 (数字部分<1, 指数部分必须为整数)
字符常量(内存中ASCⅡ码储存-->和整型可以互相赋值):
只包括一个字符
区分大小写
转义字符:
常用转义字符 |
\a 响铃(BEL) 007 |
\b 退格(BS) 008 |
\f 换页(FF) 012 |
\n 换行(LF) 010 |
\r 回车(CR) 013 |
\t 水平制表(HT) (对齐数据)009 |
\v 垂直制表(VT) 011 |
\\反斜杠 092 |
\? 问号字符 063 |
\' 单引号字符 039 |
\" 双引号字符 034 |
\0 空字符(NULL) 000 |
\ddd 任意字符 1-3位八进制 |
\xhh 任意字符 1-2位十六进制 |
字符串常量:“hahaha”
符号常量(改起来方便):#define Π 3.1415
变量
*没赋初值变量值不确定
*加const以后变量值不能改变(函数参数)
运算符

*++i 和i++
i=3 | i++ ++i | i-- --i | |
变量值 | 4 4 | 2 2 | 如果单独看变量的话++放前后面效果一样 |
表达式值 | 3 4 | 3 2 |
![]() |
*小赋给大可能丢数据(溢出)
*强制类型转换(类型名)(表达式(括完整))
*两整数相除结果为整数,取整方向不确定
*算数运算符,逗号运算符自左向右结合,赋值运算符从右向左结合
*浮点数不能做%运算(可以强制转换为整型再%)
*x%=y+3 -> x%=(y+3)
*赋值运算符左值不能为表达式or长变量,左值都能作为右值
*逗号表达式:1,2,......,n 整个表达式的值是n的值(a=3,a×4 -> a=12)
*关于结合性的例题:(1.从左向右解析 -> 从右向左算 2.赋值运算符return回去的变量值替代原来的变量值)

基本的语句和操作
算法
*算法+数据结构=程序
*算法的分类
数值算法:求数值解
非数值算法:其他
*算法表示:
自然语言
流程图
伪代码
计算机语言
程序结构
*每一个程序单位由三部分组成
预处理指令:#include,#define等
全局声明:函数外做的定义
函数
c++的语句
声明语句
执行语句:
控制语句: 九种(if语句,三种循环,continue,break,switch,goto,return)
函数和流对象调用语句:sort(a,b) cout<<"Hello"
表达式语句
空语句:只有一个分号的语句(;)
复合语句:用{}括起来的一些语句
输入输出
<< : 流插入运算符,>> : 流提取运算符 -> #include <iostream>
*执行cout语句时直到缓冲区满或者遇到endl才输出
*用cin输入时系统会根据变量的类型从输入流中提取相应长度字节
*分割变量:Tab 空格 回车
*格式控制符(#include <iomanip>):
控制符 | 作用 |
dec | 设置数值基数为10 |
hex | 设置数值基数为16 |
oct | 设置数值基数为8 |
setfill(c) | 设置填充字符c,c可以是变量或常量 |
setprecision(n) | 设置精度,跟ios::fixed合用设置小数位数 |
setw(n) | 设置字符宽度 |
setiosflags(ios::scientific) | 浮点数科学计数法表示 |
setiosflags(ios::fixed) | 浮点数固定小数位 |
setiosflags(ios::left) | 输出数据左对齐 |
setiosflags(ios::right) | 输出数据右对齐 |
setiosflags(ios::skipws) | 忽略前导空格 |
setiosflags(ios::uppercase) | 十六进制输出字母大写 |
setiosflags(ios::lowercase) | 十六进制输出字母小写 |
setiosflags(ios::showpos) | 输入正数显示+ |


(默认右对齐)
#include<bits/stdc++.h>usingnamespace std;
intmain(){
for (longlong i = 1; i < 1000000000; i *= 10)
{
cout << setiosflags(ios::left);
cout << setw(8) << i << ";" << endl;
}
for (longlong i = 1; i < 1000000000; i *= 10)
{
// cout << setiosflags(ios::right);
cout << setw(8) << i << ";" << endl;
}
for (longlong i = 1; i < 1000000000; i *= 10)
{
cout << setiosflags(ios::right);
cout << setw(8) << i << ";" << endl;
}
return0;
}
//输出结果10 ;
100 ;
1000 ;
10000 ;
100000 ;
1000000 ;
10000000;
100000000;
1 ;
10 ;
100 ;
1000 ;
10000 ;
100000 ;
1000000 ;
10000000;
100000000;
1;
10;
100;
1000;
10000;
100000;
1000000;
10000000;
100000000;
getchar&putchar
*putchar():向终端输出一个字符
可以输出转义字符(putchar('\n')输出换行符)
putchar(数字)输出对应ASCⅡ码的符号 -> 也就是输出不了数字
*getchar():从终端输入一个字符
也是以ASCⅡ码存
程序结构
*顺序结构:从上到下依次执行
*关系表达式:用关系运算符将两个表达式连接起来的句子
*逻辑表达式:两个关系表达式用逻辑运算符连接
*逻辑运算优先顺序:!> && > ||
*判断逻辑量标准:0 -> false, 其他 ->true('c'&&'d' -> true[ASCⅡ] 4&&0||2 -> true )
*选择结构 if(表达式),表达式结果非0则执行下面的语句
*条件运算符:a?b:c 判断a真假,真就执行b,否则执行c
*switch(表达式):
别漏了default
除了defaul,每条语句后加break
各个case和default顺序不影响结果
*循环结构
while()
do... while();
for(循环变量初值a;循环继续条件b;循环变量增值c):abc每个都可省略,abc可以是逗号表达式
*break&conyinue:
break: 跳出这个循环
continue:跳出这次循环
用函数实现指定功能
函数的分类
从用户角度:库函数,自定义函数
从系统角度:无参函数,有参函数
定义函数的一般形式
无参函数
类型名 函数名([void])
{
声明部分
执行语句
}
有参函数
类型名 函数名(参数表)
{
声明部分
执行语句
}
函数参数和函数值
形式参数
定义函数时括号内的变量
不占存储单元
不能为表达式
实际参数
调用函数时括号内变量
占用存储单元
可以是表达式
类型应与形参兼容
调用时:实参 ——> 形参(单向值传递)
函数返回值
函数可以有一个以上返回值,执行到哪个return,哪个return语句起作用
return后面可以跟表达式
如果函数值类型跟return中表达式值不一致,以函数类型为准,即函数类型决定返回值类型
函数的调用
调用方式
函数语句:单独作为一个语句 ->print()
函数表达式:出现在一个表达式中 ->c=max(a,b)*2
函数参数:作为实参 ->m=max(s,sqrt(b))
函数的定义和声明
定义:对函数功能确立,包括指定函数名,函数类型,形参及其类型
声明:不包括函数体,可以省略形参名,只给形参类型
根据main()位置选择是否声明
内置函数
在编译时将所调用函数代码嵌入到主函数中,用关键字 inline表示
可以在声明和定义都加 inline,也可以只在声明时加
一般将小规模(5行以下),使用频繁的函数声明为内置函数
inline声明只是建议,不一定被编译器采纳
函数的重载
可重载的形式:参数类型不同,参数数量不同,参数顺序不同(至少有一者不同)
不能只有函数类型不同而参数个数跟类型相同 -> int f(int) long f(int) void f(int) //invalid
函数模板
关键字 template<typename T [,typename T2] >
template<typename T>
T max(T a,T b,T c){
if(b>a) a=b;
if(c>a) a=c;
return a;
}
有默认参数的函数
float area(float r=6.5);
然后可以这样调用
area();//相当于area(6.5)
如果不想取这个默认值可以通过实参给出
area(7.5);
指定部分默认参数的话默认参数要放形参列表最右端
void f(float a,int b,int c=0,char d='a')
函数调用前要声明or定义默认值(让编译器知道)
一个函数不能既作为重载函数又作为有默认参数的函数
变量声明和定义
局部变量作用范围内同名全局变量被屏蔽
变量定义建立存储空间,变量声明不建立存储空间
变量前缀
本文件中
多文件中
extern
提前声明
找其他文件里这个变量
auto
动态存储,可省略
/
static
静态存储,函数结束不释放,不赋值默认为0或‘/0’
/
register
局部变量存cpu里,少,快
/
内部函数和外部函数
内部函数:只能被本文件其他函数调用,关键字 static
外部函数:默认都是外部函数,也可以显式声明 extern
头文件
头文件包括以下内容:
对类型声明
函数声明
内置函数定义
宏定义
全局变量定义
外部变量声明
根据需要包含其他头文件
用数组处理数据
一维数组
不允许对数组长度做动态定义(其实现在编译器很多都可以QWQ)
定义成全局变量初始值为0,否则初值不确定
给一部分元素赋值完其他元素默认值为0
对全部元素赋初值时可以不指定数组长度
不能对两个数组用 '=' 赋值
二维数组
内存里按行存放
对行部分元素赋值用{ }
对全部元素赋初值,第一维长度可以不指定,但是第二维长度不能省略
用数组作为函数参数
数组元素作实参
跟变量作实参差不多
数组名作为函数参数
如果函数实参是数组名,形参也要是数组名
数组名代表数组首元素地址
声明形参数组可以不带元素个数
用数组名作实参时,如果改变形参数组元素的值将同时改变实参数组元素的值(指针)
字符数组
默认为空字符
不能对两个数组用 '=' 赋值
遇到 '/0' 就表示字符串结束,结束输出
一些函数(const应该都能省略)
原型 | 作用 |
strcat(char[] ,const char[]) | 将第二个字符串连接到第一个后面 |
strcpy(char[] ,const char[]) | 将第二个字符串复制到第一个上并覆盖相应字符 |
strcmp(const char[], const char[]) | '='->返回0 '>'->返回正整数 '<'->返回负整数(ASCⅡ顺序比较) |
strlen(const char[]) | 返回字符串实际长度(不包括 '/0' ) |
string类型
可以 str1=str2这样赋值
字符串连接用加号
比较可以直接用关系运算符
字符串数组:每一个元素都是一个字符串
用数组处理数据
一维数组
不允许对数组长度做动态定义(其实现在编译器很多都可以QWQ)
定义成全局变量初始值为0,否则初值不确定
给一部分元素赋值完其他元素默认值为0
对全部元素赋初值时可以不指定数组长度
不能对两个数组用 '=' 赋值
二维数组
内存里按行存放
对行部分元素赋值用{ }
对全部元素赋初值,第一维长度可以不指定,但是第二维长度不能省略
用数组作为函数参数
数组元素作实参
跟变量作实参差不多
数组名作为函数参数
如果函数实参是数组名,形参也要是数组名
数组名代表数组首元素地址
声明形参数组可以不带元素个数
用数组名作实参时,如果改变形参数组元素的值将同时改变实参数组元素的值(指针)
字符数组
默认为空字符
不能对两个数组用 '=' 赋值
遇到 '/0' 就表示字符串结束,结束输出
一些函数(const应该都能省略)
原型 | 作用 |
strcat(char[] ,const char[]) | 将第二个字符串连接到第一个后面 |
strcpy(char[] ,const char[]) | 将第二个字符串复制到第一个上并覆盖相应字符 |
strcmp(const char[], const char[]) | '='->返回0 '>'->返回正整数 '<'->返回负整数(ASCⅡ顺序比较) |
strlen(const char[]) | 返回字符串实际长度(不包括 '/0' ) |
string类型
可以 str1=str2这样赋值
字符串连接用加号
比较可以直接用关系运算符
字符串数组:每一个元素都是一个字符串
指针和引用
变量与指针:
一个变量的地址称为这个变量的指针
如果有一个变量专门用来存放地址,称之为指针变量
'*' 是取值运算符,'&'是取地址运算符
用法:形如 int *p=&i ——> 把i的地址存到p中
不能用整数给指针变量赋值
指针变量只能指向同类型的指针变量
用指针作函数参数
#include<iostream>usingnamespace std;
voidswap(int *p1,int *p2){
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
intmain(){
int a,b;
cin>>a>>b;
int *p1=&a,*p2=&b;
swap(p1,p2);
cout<<a<<b;
}
输出a,b值是交换过后的值
不能定义野指针
为了使在函数中被改变的变量值能被main所用,应该用指针变量作为函数实参,在函数执行过程中使指针变量所指向的变量值发生变化
不能通过改变形参指针变量的值使实参指针变量的值改变
voidswap(int *p1,int *p2)//wrong!!{
int *temp;
temp=p1;
p1=p2;
p2=temp;
}
总之,函数内只能对值进行操作,传进来的是指针变量
数组与指针
若p已经指向数组一个元素,则p+1指向这个数组下一个元素
int a[10]
int *p=a
则a[5],*(p+5),*(a+5)等价
用数组名做参数的函数,传递的是数组首元素的地址 void sore(int arr[])
字符串与指针
char *str="hello world";
cout<<str;//注意不需要写成cout<<*str
函数与指针
函数的入口地址称为函数的指针
函数类型(*变量名)(形参表)int(*p)(int ,int )
#include<iostream>usingnamespace std;
intmax(int x,int y){
return(x>y?x:y);
}
intmain(){
int a,b;
int(*p)(int,int)//
p=max;//
cin>>a>>b;
int m=p(a,b);
cout<<m<<endl;
}
void指针
必须转化为指定一个确定的数据类型的数据才能访问实际存在的数据
意味着使用void指针变量时要对他进行类型转化
int a=3;
int *p=&a;
void *p2;
p2=(void*)p;
cout<<*(int*)p2;//把p2转化成int*型,可以指向变量a
有关指针数据类型
变量定义 | 含义 |
int *p | 指向整形数据的指针变量 |
int *p[4] | 指针数组p,由4个指向整型数据的指针元素构成 |
int(*p)[4] | p为包含四个元素的一维数组的指针变量(数组指针->指向一行元素) |
int *p() | p为返回一个指针的函数,该指针指向整型数据 |
int(*p)() | p为指向函数的指针,该函数返回一个整型值 |
int **p | p为指向一个指向整形数据的指针变量 |
int const *p | 常指针,指向不能变 |
const int *p | p是指向常量的指针,不能通过p改变其指向对象的值(作为形参) |
const int *const p | 指向常量的常指针 |
void *p | 不指向具体对象 |
指针运算
加减一个整数:参考数组
赋值
p=NULL
两个指针变量指向同一个数组元素,则他们的差值是两个指针之间元素的个数
比大小:指向同个数组,靠前元素指针变量小
引用
相当于起别名 int a; int &b=a;
引用只有声明,没有定义
声明引用同时必须初始化
声明一个引用之后不能让它再作为其他变量的引用
不能建立引用数组
不能建立引用的引用
没有引用的指针
自定义类型
结构体
struct 结构体类型名
{
成员表
}成员;
每个成员也称为结构体当中的一个域,成员表又称作域表
引用结构体成员-> ' . '(成员运算符,优先级最高)
结构体数组:存结构体的数组
指向结构体变量的指针:Student *p=&stu 定义p为指向Student数据类型的指针变量并指向stu
引用: (*p).成员名 或 p->成员名
用结构体构成链表:见ACM整理(😜)
结构体作为函数参数:
voidprint(Student stu){
'.'
}
Student stud;
print(stud);
//voidprint(Student *p){
'->'
}
Student *pt=&stud;
print(pt);
//voidprint(Student &stud){
'.'
}
Student stud;
print(stud);
new&delete
new:
newint;//开辟一个存放整数的储存空间,返回一个指向该存储空间的地址newint(100);//开辟一个存放整数的储存空间,指定该整数初值为100,返回一个指向该存储空间的地址newchar[10];//开辟一个存放字符数组(10个元素)的空间,返回字符数组首元素地址newint[5][4];//开辟一个存放二维整型数组(5*4)的空间,返回首元素地址float *p=newfloat(3.14);//开辟一个存放单精度的空间,指定初值为3.14,将返回空间的地址赋给指针变量p
delete:
delete 指针变量(对变量)
delete [] 指针变量(对数组)
枚举
enum weekday{sum,mon,tue,wed,fri}
枚举里的元素是常量,第一个是0
可以在声明时自己指定枚举元素的值 enum weekday{sun=7,mon=1,tue,wed...)没有指定的默认为前一个值加一
typedef声明新的类型名
typedefint INTEGER;
则int a等价于INTEGER a;
类和对象特性
面向对象的程序设计
面向对象程序设计四个主要特点:封装,抽象,继承和多态
封装:把对象内部实现和外部行为分开
抽象:表示同一类事物的本质
类是对象的抽象,对象是类的特例
多态:由继承产生的不同派生类,其对象会对同一消息做出不同反应
初识类
class 类名 {private: 私有数据成员和函数 public: 公有数据成员和函数 protected: 受保护的数据成员和函数 }
定义对象:Student stud1
可以在声明类同时定义对象->跟结构体差不多
也可以不出现类名直接定义对象(少用)
类外定义成员函数:返回值类型 类名:函数名(参数列表)
如果类中定义函数不包括循环等控制结构,自动划分为内置函数,类外定义要显式声明,并且把类定义和成员函数定义写一起(.hpp)
不同对象执行同样代码时识别不同对象中数据:this指针,调用哪个对象就指向那个对象(隐式)
访问对象成员:对象名.成员名
指针访问对象成员:
Time t,*p;
p=&t;
cout<<p->hour;
一个c++程序由三部分组成:
类声明头文件(.h)
类实现头文件(.cpp)
主文件
怎样使用类和对象
构造函数初始化数据成员
不能在类声明数据成员的时候初始化(类是抽象实体,不占据储存空间)
构造函数名字跟类名相同,不具有任何类型,无返回值
classTime
{public:
Time()//不带参数的构造函数
{
h=0,m=1,s=0;初始化类成员
}
....
}
Time t//自动调用构造函数进行赋值
--
classBox
{public:
/*1*/Box(int h,int w,ine len)//带参数的构造函数
{
hight=h;
width=w;
lenth=l
}
/*2*/Box(int h,int w,int len):hight(h),width(w),lenth(l){}//用参数初始化列表进行初始化private:
int hight,lenth,width;
}
Box b(1,1,1)
也可以在类外定义构造函数,要加 ' :: '
构造函数不需要用户调用也不能被用户调用
一般声明构造函数为public
如果用户没有自己定义构造函数,系统会自动生成一个空构造函数
如果数据成员是数组,则应当在构造函数体中赋值而不能在参数初始化列表中初始化
构造函数可以重载
构造函数可以使用默认参数,如果类外定义则在类内声明时就要指定默认值
类中定义了全部是默认参数的构造函数后不能再重载构造函数(歧义)
用析构函数进行清理
作用:在撤销对象占用内存之前完成清理工作,使这部分内存可以分配给新对象使用
什么情况下调用析构函数:
1.函数内局部对象调用结束
2.静态局部对象在函数调用结束时不释放,等到main函数结束后或调用exit结束程序后再调用析构函数
3.全局对象等到main结束或者exit后再调用析构函数
4.new建立对象等到delete时调用析构函数
析构函数不能被重载,一个类只能有一个
先构造的后析构,后构造的先析构
对象数组
形如 Student stu[50]
如果构造函数只有一个参数可以 Student stu[3]={1,2,3}
有多个参数的话就要 Student stu[2]={Student(1,1,1),Student(2,2,2)}
对象指针
指向对象的指针
对象空间的起始地址就是对象的指针
classTime
{
...
}
Time *p;
Time t1;
p=&t1;//p就是指向t1的指针,可以用'->'//
指向对象数据成员的指针
int *p=&t.hour;
cout<<*p<<endl;//输出t中hour的值//
指向对象成员函数的指针
void(Time::*p)()//定义p为指向Time类中公有成员函数的指针变量
p=&Time::get_time//指向Time中get_time函数//
指向当前对象的this指针
int Box::valume()
{
return(h*w*l);
用this指针:return(this->h*this->w*this->l)//其实没必要
}
共用数据保护
对象前加上const指定为常对象,必须有初值且无法修改数据成员的值,只能调用该对象的常成员函数 类名 const 对象名[实参表]
函数后面加const定义为常成员函数,可以访问常对象中数据成员但不允许修改 void get_time() const
常数据成员:用const声明,值无法改变,只能通过构造函数初始化表对长数据成员初始化 const int hour
指向对象的常指针:Time *const p,p=&t//p指向对象t,此后不能改变指向
指向常对象的指针变量:const Time *p=&t//p是指向t的指针变量,此后t值不能改变
原则:当希望在调用函数时对象的值不被修改,应把形参定义为指向常对象的指针变量,并用对象的地址做实参(对象可以是const也可以不是)
对象的动态建立和释放
new Box;//开辟内存空间存放Box类变量,返回一个指向新对象的指针
用法:
Box *p;
p=new Box;//p中存放新建对象起始地址
对象的赋值和复制
一个对象的值可以赋给另一个同类的对象 stu2=stu1
复制构造函数(系统自动实现,了解即可)
Box(const Box&b)//建议只读
{
h=b.h;
l=b.l;
...
}
Box box1(1,1,1)
Box box2(box1)//建立一个新对象box2,将box1中各数据成员值赋给box2
不同对象间的数据共享
如果希望同类的个对象中数据成员值一样,就可以把他定义为静态数据成员static,所有对象都可引用
静态成员的值对每个对象都一样,如果改变则在各个对象中的值也都改变
不能用构造函数的参数初始化表对静态成员初始化
可以在类外通过对象名访问公有static成员也可以通过类名引用
成员函数前加static成为静态成员函数,无this指针,主要用来访问静态数据成员
友元
关键字friend,类的友元函数可访问类私有成员 friend void display(Time&t)//形参是引用
友元类:在类定义里声明另一个类为友元类,则成员函数互为友元(关系是单向的,且不能传递)
类模板
template<classT>//声明一个模板,虚拟类型名为TclassCompare
{public:
Compare(T a,T b) 如果在类外定义的话要写成类模板形式-> template<classT>
{ T Compare<T>::max()
x=a,y=b {return(x>y?x:y);}
}
T max(){
return(x>y?x:y);
}
private:
int x,y;
}//也就是类里变量类型全部用T代替
Compare<int>cmp1(1,2);
cout<<cmp1.max;
Compare<double>cmp2(1.1,1.2);//实例化
cout<<cmp2.max;
运算符重载
运算符重载的方法
实质是函数的重载
一般格式 operator 运算符名称(形参表){函数体}
对运算符+重载用于复数相加
classComplex
{public:
Complex operator+(Complex&c2)
{
Complex c;
c.real=real+c2.real;
c.imag=imag+c2.imag;
return c;//返回一个类
}
private:
double real,image;
}//就可以c3=c1+c2这样调用
运算符重载后原有功能仍然保留(+重载后依然可以当作普通加号用)
重载运算符规则
只能对c++原有运算符重载
不能重载的运算符:'.' '*' '::' 'sizeof' '?:'
重载不能改变运算符操作数个数
重载不能改变运算符优先级别
重载不能改变运算符结合性
重载的运算符不能有默认参数
重载的参数不能全都是标准类型(至少一种类对象(的引用),不然重载干啥嘞😐)
'=' 和 '&'不必用户重载
运算符重载函数作为类成员函数和友元函数
如果将运算符重载函数作为成员函数,它可以通过this指针(隐式)访问本类数据成员,因此可以少写一个参数
将双目运算符重载为友元函数,由于友元函数不是该类的成员函数,因此形参列表中要有两个参数
将负数和整数相加
//成员函数
Comlex Complex::operator+(int &i)
{
returnComplex(real+i,image);//这里的real和image本身就是Complex类成员
}
这样的话+左侧应该是Complex对象(real+i里面real再左侧)
//友元函数friend Complex operator+(int &i,Complex &c)//第一个参数可以不是类对象
{
returnComplex(i+c.real,c.image);//要用成员运算符 '.'
}
+右侧是Complex对象,想放左侧要再重载一次
规定:
= ,[] ,() ,-> 必须作为成员函数重载
<<,>>只能作为友元函数
一般将单目运算符和复合运算符重载为成员函数
一般将双目运算符重载为友元函数
返回当前对象值 return *this
重载流插入和流提取运算符
重载形式:istream &operator>>(istream &,自定义类 &)//ostream &operator>>(ostream &,自定义类 &)
只能作为友元函数
classComplex
{public:
friend ostream& operator<<(ostream&,Complex&);
friend istream& operator<<(istream&,Complex&);
private:
double real,image;
};
ostream& operator<<(ostream& output,Complx c)
{
output<<c.real<<' '<<c.image;
return output;//
}
istream& operator<<(istream& input,Complx c)
{
input>>c.real>>c.image;
return input;//
}
用转换构造函数将其他数据类型转换为类对象
转换构造函数只有一个形参
Complex(double r)
{
real=r,image=0;
}
Complex c1(3.5)//把3.5转化为一个Complex对象
用类型转换函数将类对象转化为其他数据类型
一般形式 operator 类型名(){实现转换的语句}
类型转换函数只能作为成员函数,因为转换的主体是本类对象
如果已经声明一个Complex类c,可以这样定义转换类型函数
operatordouble(){
return real;
}
double1=double2+c;//需要时系统会自动调用符合条件的类型转换函数
类的继承
基类和派生类的概念
一个新类从已有类那里获得其已有特性称为类的继承
已有的类产生一个新的子类称为类的派生
一个派生类只从一个基类派生称为单继承,一个派生类有两个以上基类称为多继承
派生类是基类的具体化,基类是派生类的抽象
派生类
class 派生类名:[继承方式]基类名
{
派生类新成员
};
//继承方式不写默认私有继承
公用继承
基类公有成员和保护成员在派生类中保持原有访问属性,私有成员仍为基类私有
在基类访问属性 | 在派生类访问属性 |
private | 不可访问(只能通过基类公用成员函数访问) |
public | public |
protected | protected |
私有继承
基类公用成员和保护成员在派生类访问属性变为私有,基类私有成员仍为基类私有
在基类访问属性 | 在派生类访问属性 |
private | 不可访问 |
public | private |
protected | private |
保护继承
保护成员可以被派生类成员函数引用
希望在派生类访问基类'私有'成员,应声明为保护
在基类访问属性 | 在派生类访问属性 |
private | 不可访问 |
public | protected |
protected | protected |
多级派生
直接基类,间接基类
直接派生类,间接派生类
派生类的构造函数
形式 派生类构造函数名(总参数表):基类构造函数名(参数表),[*子对象名(参数表)*] {派生类新增成员初始化语句(不需要的话函数体为空)}
基类构造函数是实参,不用写参数名
也可以在类中声明,类外定义,声明格式跟普通的构造函数一样,定义的时候再写上面那种格式
多层派生的话只要写出上一层(直接基类)的构造函数即可
如果在基类中没有定义构造函数,或者定义了没有参数的构造函数那么在定义派生类构造函数时不用写基类构造函数(系统隐式调用)
如果基类没定义构造函数,派生类成员也不用初始化,可以不写构造函数
classStudent
{public:
Student(int n,string name,char s)
{
num=n,name=nam,sex=s;
}
protected:
int num;
string name;
char sex;
};
classStudent1:public Student
{
public:
Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)//派生类构造函数
{
age=a,addr=ad;
}
Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s),age(a),addr(ad){}//也可以这样写
...
}
派生类的析构函数-不需要操心😁
多重继承
形式 class D:public A,private B,protected C
构造函数 派生类构造函数名(总参数表):基类1构造函数名(参数表),基类2构造函数名(参数表)...,{派生类新增成员初始化语句}
多重实继承引用数据成员或者函数最好指明作用域(防止重名出现歧义)
虚继承
虚基类:在继承共同间接基类时只保留一份数据成员
虚基类是在声明派生类时指定继承方式的时候声明的
声明虚基类 class 派生类名:virtual 继承方式 基类名
为保证虚基类在派生类只继承一次,应当在虚基类所有直接派生类都声明虚继承
虚基类初始化:构造函数初始化表,子类中不仅要对直接基类初始化,也要用虚基类构造函数对虚基类初始化
基类和派生类的转换
可以用公有派生类对象给基类对象赋值(多->少✔)
不能用基类对象为子类对象赋值,同一基类不同派生类之间不能相互赋值
派生类对象可以替代基类对象向基类对象的引用赋值或初始化
A a1//定义基类A对象a1
B b1//定义公用派生类B对象b1
A&r=a1 A&r=b1
如果函数参数是基类对象(的引用),相应的实参可以用子类对象(也就是可以对子类用基类的函数)
指向基类的指针变量也可以指向派生类对象,但此时只能访问派生类中继承的成员无法访问新增数据成员
c++的多态性
多态性
定义:向不同的对象发送同个信息,不同对象在接受时产生不同做法
多态性分为两类:静态多态性和动态多态性
静态多态性:通过函数重载实现,编译时决定,速度快效率高但缺乏灵活性
动态多态性:通过虚函数实现,运行时决定
利用虚函数实现多态
虚函数作用:允许派生类重新定义与基类同名函数,并且可以通过基类指针或引用来访问
当把基类某成员函数声明为虚函数后,允许其在派生类中对该函数重新定义赋予新功能,并且可以通过指向基类的指针指向同一类族中不同类的对象,从而调用其中的同名函数
虚函数使用方法:
1.在基类中用 virtual声明成员函数为虚函数,在类外定义虚函数时不用加virtual
2.在派生类重新定义此函数,函数要素要相同,一个成员函数被声明为虚函数之后派生类同名函数自动成为虚函数,所以可以不加virtual(但习惯加),如果派生类无重定义,则简单继承基类虚函数
3.定义一个指向基类的指针变量,并使它指向同一类族中需要调用该函数的对象
4.通过该指针变量调用次虚函数,此时调用的就是指针变量指向的对象的同名函数
什么时候可以定义成虚函数:做基类,具体功能给派生类实现,通过指针或引用访问
最好把基类析构函数声明为虚函数(否则指向基类的指针在delete时可能只执行基类析构函数)
构造函数不能声明为虚函数
纯虚函数与抽象类
一般形式 virtual 函数类型 函数名(参数表)=0;
纯虚函数只是告诉编译系统,在这里声明一个虚函数,留给派生类定义
不用来定义对象只作为基本继承类型的类-抽象类,凡是包含纯虚函数的类都叫抽象(基)类
#include<iostream>usingnamespace std;
classShape
{public:
virtualfloatarea()const{ return0.0; }//虚函数
virtualfloatvolume()const{ return0.0; }//虚函数
virtualvoidshapeName()const= 0;//纯虚函数
};
classPoint:public Shape
{
public:
Point(float = 0, float= 0);
voidsetPoint(float, float);
floatgetX()const{ return x; }//读x坐标,getX函数为常成员函数
floatgetY()const{ return y; }
virtualvoidshapeName()const{ cout << "Point:"; }//对虚函数再定义
friend ostream&operator<<(ostream&, const Point&);
protected:
float x, y;
};
Point::Point(float a, float b)//基类构造函数,并对x,y初始化
{
x = a; y = b;
}
voidPoint::setPoint(float a, float b)//定义成员函数{
x = a; y = b;
}
ostream&operator<<(ostream&output, const Point&p)
{
output << "[" << p.x << "," << p.y << "]" << endl;
return output;
}
classCircle :public Point
{
public:
Circle(float x = 0, float y = 0, float r = 0);
voidsetRadius(float);
floatgetRadius()const;
virtualfloatarea()const;
virtualvoidshapeName()const{ cout << "Circle:"; }
friend ostream&operator<<(ostream&, const Circle&);
protected:
float radius;
};
Circle::Circle(float a, float b, float r)
:Point(a, b), radius(r){}//定义派生类构造函数voidCircle::setRadius(float r){
radius = r;
}
floatCircle::getRadius()const{ return radius; }
floatCircle::area()const{ return3.14159*radius*radius; }
ostream&operator<<(ostream&output, const Circle&c)
{
output << "Center[" << c.x << "," << c.y << "],r="
<< c.radius << ",area=" << c.area() << endl;
return output;
}
classCylinder :public Circle
{
public:
Cylinder(float x = 0, float y = 0, float r = 0, float h = 0);
voidsetHeight(float);
//float getHeight()const;
virtualfloatarea()const;
virtualfloatvolume()const;
virtualvoidshapeName()const{ cout << "Cylinder:"; }
friend ostream&operator<<(ostream&, const Cylinder&);
protected:
float height;
};
Cylinder::Cylinder(float a, float b, float r, float h)
:Circle(a, b, r), height(h){}//定义派生类构造函数voidCylinder::setHeight(float h){
height = h;
}
floatCylinder::area()const{
return2 * Circle::area() +
2 * 3.14159*radius*height;
}
floatCylinder::volume()const{ return Circle::area()*height; }
ostream&operator<<(ostream&output, const Cylinder&cy)
{
output << "Center[" << cy.x << "," << cy.y << "],r="
<< cy.radius << ",h=" << cy.height << "\narea=" << cy.area()
<< ",volume=" << cy.volume() << endl;
return output;
}
intmain(){
Point point(3.2, 4.5);
Circle circle(2.4, 1.2, 5.6);
Cylinder cylinder(3.5, 6.4, 5.2, 10.5);
point.shapeName();//用对象名建立静态关联
cout << point << endl;
circle.shapeName(); //静态关联
cout << circle << endl;
cylinder.shapeName();//静态关联
cout << cylinder << endl << endl;
Shape *pt;//定义基类指针
pt=&point;//使指针指向Point类对象
pt->shapeName();//用指针建立动态关联
cout << "x=" << point.getX() << ",y=" << point.getY() <<
"\narea=" << pt->area() << "\nvolume=" << pt->volume()
<< "\n\n";
pt = &circle;
pt->shapeName();//动态关联
cout << "x=" << circle.getX() << ",y=" << circle.getY() <<
"\narea=" << pt->area() << "\nvolume=" << pt->volume()
<< "\n\n";
pt = &cylinder;
pt->shapeName();//动态关联
cout << "x=" << cylinder.getX() << ",y=" << cylinder.getY() <<
"\narea=" << pt->area() << "\nvolume=" << pt->volume()
<< "\n\n";
return0;
}
输入输出流
c++的输入输出流
c++的输入输出包括三个方面
1.标准IO,从键盘输入数据,输出到显示器屏幕
2.文件IO,以外存为对象进行输入输出
3.串IO,对内存指定空间进行IO
c++的输入输出流指由若干字节组成的字节序列,缓冲区中的数据就是流
流库的基类:ios类和streambuf类(处理缓冲区)
常用流类:
类名 | 作用 | 头文件 |
ios | 抽象基类 | iostream |
istream ostream iostream | 通用输入流和其他输入流的基类 通用输出流和其他输出流的基类 通用输入输出流和其他输入输出流的基类 | iostream iostream iostream |
ifstream ofstream fstream | 输入文件流类 输出文件流类 输入输出文件流类 | fstream fstream fstream |
istrstream ostrstream strstream | 输入字符串流类 输出字符串流类 输入输出字符串流类 | strstream strstream strstream |
iostream中定义四种流对象
对象 | 含义 | 对应设备 |
cin cout cerr clog | 标准输入流 标准输出流 标准错误六 标准日志流 | 键盘 屏幕(开辟缓冲区用来存数据) 屏幕(不经过缓冲区) 屏幕 |
cout输出时加endl清空缓冲区,加'\n'不清空
格式控制符
流成员函数 | 与之作用相同的控制符 | 作用 |
precision(n) | setpresicion(n) | 设置实数精度n位 |
width(n) | setw(n) | 设置字符宽度n位 |
fill(c) | setfill(c) | 设置填充字符c |
setf() | setiosflags | 设置输出格式状态 |
unsetf() | resetioflags() | 终止以指定的输出格式状态 |
格式状态
格式标志 | 作用 |
ios::left ios::right ios::internal | 输出数据在本域范围内左对齐 输出数据在本域范围内右对齐 数值符号位在域宽内左对齐,数值右对齐,中间由填充字符填充 |
ios::dec ios::oct ios::hex | 设置整数基数10 设置整数基数8 设置整数基数16 |
ios::showbase ios::showpoint ios::uppercase ios::showpos | 强制输出整数的基数(八进制0开头,16进制0x开头) 强制输出浮点数小数点和尾数0 在以科学计数法格式E和十六进制输出字母时以大写表示 对正数显示+号 |
ios::scientific ios::fixed | 浮点数以科学计数法格式输出 浮点数以定点形式输出 |
ios::unitbuf ios::stdio | 每次输出之后刷新所有的流 每次输出之后清除stdout,stderr |
/**用流对象的成员函数控制输出数据格式**/#include<iostream>usingnamespace std;
intmain(void){
int a = 21;
cout.setf(ios::showbase);
cout << "dec:" << a << endl;
cout.unsetf(ios::dec);
cout.setf(ios::hex);
cout << "hex:" << a << endl;
cout.unsetf(ios::hex);
cout.setf(ios::oct);
cout << "oct:" << a << endl;
char *pt = "China";
cout.width(10);
cout << pt << endl;
cout.width(10);
cout.fill('*');
cout << pt << endl;
double pi = 22.0 / 7.0;
cout.setf(ios::scientific);
cout << "pi=";
cout.width(14);
cout << pi << endl;
cout.unsetf(ios::scientific);
cout.setf(ios::fixed);
cout.width(12);
cout.setf(ios::showpos);
cout.setf(ios::internal);
cout.precision(6);
cout << pi << endl;
return0;
}
输出:
dec:21
hex:0x15
oct:025
china
*****china
pi=**3.142857e+00
+***3.142857
puts&gets-输出(入)字符串
单个字符IO
cout.put('a')//输出a
cout.put('65+32')//也是输出a
cin.get()//从指定输入流提取一个字符,跟getchar()作用相同
cin.get(ch)//从输入流读取一个字符赋值给ch(=getchar(ch)),读取成功返回true,否则返回false
cin.get(字符数组(指针),字符个数n,终止字符)//从输入流读取n-1个字符赋值给指定的字符数组,遇到中止字符则提前结束读取,返回bool值
**getline(cin,str)//接受一行字符,赋值给str
istream类其他函数
.eof()//判断文件是否结束,返回bool值--while(!cin.eof())
.peek()//观测下一个字符,但返回值是当前字符指针--c=cin.peek()
.putback()//将前面用get或getline函数从流中读取的ch字符返回到输出流,插入当前指针位置供后面读取--cin.putback(ch)
cin.ignore(n,终止字符)//跳过输入流n个字符或在遇到指定的终止字符时提前结束
对文件的操作和文件流
文件可分为二进制文件和ASCⅡ文件,ASCⅡ码文件每个字节放一个ASCⅡ码代表一个字符,二进制文件把内存中数据按其在内存中储存形式原样输出到磁盘上存放,对于字符信息没差,数值信息二者不同
打开磁盘文件 ostream 文件流对象(磁盘文件名,输入输出方式)

关闭磁盘文件 文件名.close()
对ASCⅡ文件的操作
//有一个整形数组,含10个元素,从键盘输入10个元素给数组,将此数组送到磁盘文件中存放 #include<fstream>usingnamespace std;
intmain(){
int a[10];
ofstream outfile("f1.dat",ios::out);
if(!outfile)
{
cerr<<"open error!"<<endl;
exit(1);
}
cout<<"enter 10 integer numbers:"<<endl;
for(int i=0;i<10;i++)
{
cin>>a[i];
outfile<<a[i]<<" ";
}
outfile.close();
return0;
}
//从例13.8建立的数据文件f1.dat中读入10个整数放在数组中,找出并输出10个数中的最大者和它在数组中的序号。#include<iostream>#include<fstream>usingnamespace std;
intmain(){
int a[10],max,i,order;
ifstream infile("f1.dat",ios::in);//ios::nocreate是在C++标准制定之前在<fstream.h>中有定义的。C++标准中没有nocreate,因为这是一个默认的动作,无需写上。
if(!infile)
{
cerr<<"open error!"<<endl;
exit(1);
}
for(i=0;i<10;i++)
{
infile>>a[i];
cout<<a[i]<<" ";
}
cout<<endl;
max=a[0];
order=0;
for(i=1;i<10;i++)
if(a[i]>max)
{
max=a[i];
order=i;
}
cout<<"max="<<max<<endl<<"order="<<order<<endl;
infile.close();
return0;
}
//从键盘读入一行字符,把其中的字母字符依次存放在磁盘文件f2.dat中。#include<iostream>#include<fstream>usingnamespace std;
voidsave_to_file(){
ofstream outfile("f2.dat");
if(!outfile)
{
cerr<<"open f2.dat error!"<<endl;
exit(1);
}
char c[80];
cin.getline(c,80);
for(int i=0;c[i]!=0;i++)
{
if(c[i]>=65&&c[i]<=90||c[i]>=97&&c[i]<=122)
{
outfile.put(c[i]);
cout<<c[i];
}
}
cout<<endl;
outfile.close();
}
voidget_from_file(){
char ch;
ifstream infile("f2.dat",ios::in);//ios::nocreate是在C++标准制定之前在<fstream.h>中有定义的。C++标准中没有nocreate,因为这是一个默认的动作,无需写上。
if(!infile)
{
cerr<<"open f2.dat error!"<<endl;
exit(1);
}
ofstream outfile("f3.dat");
if(!outfile)
{
cerr<<"open f3.dat error!"<<endl;
exit(1);
}
while(infile.get(ch))
{
if(ch>=97&&ch<=122)
ch=ch-32;
outfile.put(ch);
cout<<ch;
}
cout<<endl;
infile.close();
outfile.close();
}
intmain(){
save_to_file();
get_from_file();
return0;
}
//可以编写一个专用函数,将磁盘文件内容读入内存,然后输出到显示器。#include<iostream>#include<fstream>usingnamespace std;
voiddisplay_file(constchar *filename){
ifstream infile(filename,ios::in);//ios::nocreate是在C++标准制定之前在<fstream.h>中有定义的。C++标准中没有nocreate,因为这是一个默认的动作,无需写上。
if(!infile)
{
cerr<<"open error!"<<endl;
exit(1);
}
char ch;
while(infile.get(ch))
{
cout.put(ch);
}
cout<<endl;
infile.close();
}
intmain(){
display_file("f3.dat");
return0;
}
对二进制文件的操作
打开时要指定为ios::binary,,可以是既能输入又能输出的文件(跟ASCⅡ不同)
读写二进制文件:
istream& read(char *buffer,int len)//buffer指向内存中一段存储空间,len是读写的字节数
ostream& write(constchar *buffer,int len)
调用
a.write(p1,50);//将字符指针p1指向的单元开始的50字节内容不加转换的写到与a关联的磁盘文件
b.read(p2,30);//从b关联的磁盘对象中读入30字节(或遇到EOF结束),存放在指针p2所指的一段空间内
//将一批数据以二进制形式存放在磁盘文件中。#include<iostream>#include<fstream>usingnamespace std;
structstudent
{
char name[20];
int num;
int age;
char sex;
};
intmain(){
student stud[3]=
{
"Li",1001,18,'f',
"Fang",1002,19,'m',
"Wang",1004,17,'f'
};
ofstream outfile("stud.dat",ios::binary);
if(!outfile)
{
cerr<<"open error!"<<endl;
abort();//退出程序
}
for(int i=0;i<3;i++)
{
outfile.write((char*)&stud[i],sizeof(stud[i]));
}
outfile.close();
return0;
}
//将执行例13.11程序时存放在磁盘文件中的二进制形式的数据读入内存并在显示器上显示。#include<iostream>#include<fstream>usingnamespace std;
structstudent
{
char name[20];//string name只是浅拷贝,只有指针的值,没有指针指向的值;char name[20]指针的值和指针指向的值都有
int num;
int age;
char sex;
};
intmain(){
student stud[3];
int i;
ifstream infile("stud.dat",ios::binary);
if(!infile)
{
cerr<<"open error!"<<endl;
abort();
}
for(i=0;i<3;i++)
{
infile.read((char*)&stud[i],sizeof(stud[i]));
}
infile.close();
for(i=0;i<3;i++)
{
cout<<"NO."<<i+1<<endl;
cout<<"name:"<<stud[i].name<<endl;
cout<<"num:"<<stud[i].num<<endl;
cout<<"age:"<<stud[i].age<<endl;
cout<<"sex:"<<stud[i].sex<<endl<<endl;
}
return0;
}
与文件指针有关的流成员函数


随机访问二进制数据文件
一般情况下读写是顺序进行的,即逐个字节进行读写。但是对于二进制数据文件来说,可以利用上面的成员函数移动指针,随机地访问文件中任一位置上的数据,还可以修改文件中的内容。
/*
有个学生的数据,要求:
把它们存到磁盘文件中;
将磁盘文件中的第,3,5个学生数据读入程序,并显示出来;
将第个学生的数据修改后存回磁盘文件中的原有位置。
从磁盘文件读入修改后的个学生的数据并显示出来。
*/#include<fstream>usingnamespace std;
structstudent
{int num;
char name[20];
float score;
};
intmain( ){
student stud[5]={1001,"Li",85,1002,"Fun",97.5,1004,"Wang",54,1006,"Tan",76.5,1010,"ling",96};
fstream iofile("stud.dat",ios::in|ios::out|ios::binary);
//用fstream类定义输入输出二进制文件流对象iofileif(!iofile)
{
cerr<<"open error!"<<endl;
abort( );
}
for(int i=0;i<5;i++) //向磁盘文件输出个学生的数据
iofile.write((char *)&stud[i],sizeof(stud[i]));
student stud1[5]; //用来存放从磁盘文件读入的数据for(int i=0;i<5;i=i+2)
{
iofile.seekg(i*sizeof(stud[i]),ios::beg); //定位于第,2,4学生数据开头//先后读入个学生的数据,存放在stud1[0],stud[1]和stud[2]中
iofile.read((char *)&stud1[i/2],sizeof(stud1[0]));
//输出stud1[0],stud[1]和stud[2]各成员的值
cout<<stud1[i/2].num<<" "<<stud1[i/2].name<<" "<<stud1[i/2].score<<endl;
}
cout<<endl;
stud[2].num=1012; //修改第个学生(序号为)的数据strcpy(stud[2].name,"Wu");
stud[2].score=60;
iofile.seekp(2*sizeof(stud[0]),ios::beg); //定位于第个学生数据的开头
iofile.write((char *)&stud[2],sizeof(stud[2])); //更新第个学生数据
iofile.seekg(0,ios::beg); //重新定位于文件开头for(int i=0;i<5;i++)
{
iofile.read((char *)&stud[i],sizeof(stud[i])); //读入个学生的数据
cout<<stud[i].num<<" "<<stud[i].name<<" "<<stud[i].score<<endl;
}
iofile.close( );
return0;
}
运行情况如下:
1001 Li 85(第个学生数据)
1004 Wang 54 (第个学生数据)
1010 ling 96 (第个学生数据)
1001 Li 85 (输出修改后个学生数据)
1002 Fun 97.51012 Wu 60 (已修改的第个学生数据)
1006 Tan 76.51010 ling 96
c++工具
对异常情况出现的处理
异常处理机制由三部分组成:检查(try),抛出(throw)和捕捉(catch)
//throw语句形式throw 表达式;
//try—catch结构try
{被检查的语句}
catch(异常信息类型[变量名])
{进行异常处理的语句}
catch必须紧跟try后面,不能单独使用,但是可以只有try无catch
一个try-catch块只能有一个try,但是可以有多个catch用来捕捉不同类型的异常
如果 catch(...)则表示这个catch块可以处理任意类型的异常
在catch块中包含throw表示'把这个异常丢给上级处理'
如果throw抛出的异常信息找不到与之匹配的catch块系统就会调用terminate函数终止程序运行
异常处理例子
给出三角形的三边a,b,c,求三角形的面积。只有a+b>c,b+c>a,c+a>b时才能构成三角形。设置异常处理,对不符合三角形条件的输出警告信息,不予计算。
#include<iostream>#include<cmath>usingnamespace std;
intmain(){ doubletriangle(double,double,double);
double a,b,c;
cin>>a>>b>>c;
try//在try块中包含要检查的函数
{ while(a>0 && b>0 && c>0)
{ cout<<triangle(a,b,c)<<endl;
cin>>a>>b>>c;}
}
catch(double) //用catch捕捉异常信息并作相应处理
{ cout<<"a="<<a<<",b="<<b<<",c="<<c<<",that is not a traingle!"<<endl;}
cout<<"end"<<endl;
return0; }
doubletriangle(double a,double b,double c)//计算三角形的面积的函数{ double sqrt;
double s=(a+b+c)/2;
if (a+b<=c||b+c<=a||c+a<=b) throw a; //当不符合三角形条什抛出异常信息returnsqrt(s*(s-a)*(s-b)*(s-c)); }
程序运行结果如下:
654 (输入a,b,c的值)
9.92157 (计算出三角形的面积)
11.52 (输入a,b,c的值)
0.726184 (计算出三角形的面积)
121 (输入a,b,c的值)
a=1,b=2,c=1,that is not a triangle! (异常处理)
end
---
在函数嵌套的情况下检测异常处理
#include<iostream>usingnamespace std;
intmain(){ voidf1();
try
{ f1( ); } //调用fl()catch(double)
{ cout<<"OK0!"<<endl;}
cout<<"end0"<<endl;
return0; }
voidf1(){ voidf2();
try
{ f2(); } //调用f2()catch( char )
{ cout<<"OK1!";}
cout<<"end1"<<endl; }
voidf2(){ voidf3();
try
{ f3();} //调用f3()catch(int)
{ cout<<"Ok2!"<<endl;}
cout<<"end2"<<endl; }
voidf3(){ double a=0;
try
{ throw a;} //抛出double类型异常信息catch(float)
{ cout<<"OK3!"<<endl;}
cout<<"end3"<<endl; }