01 C++的特点
C++是C语言的继承,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计(泛型编程),还可以进行以继承和多态为特点的面向对象的程序设计(面向对象编程)。
常用于系统开发,引擎开发等应用领域,是最受广大程序员受用的最强大编程语言之一,支持类、封装、继承、多态等特性。
拓展:
面对对象编程:面向对象是一种对现实世界理解和抽象的方法、思想,通过将需求要素转化为对象进行问题处理的一种思想。
泛型编程:泛型编程是一种编程风格,其算法以尽可能抽象的方式编写,而不依赖于执行这些算法的数据形式。
类:类定义了事物的属性和它可以做到的(它的行为)。一个类的方法和属性被称为“成员”。一个类所包含的方法和数据描述一组对象的共同属性和行为。类是在对象之上的抽象,对象则是类的具体化,是类的实例。
封装性:封装使数据和加工该数据的方法(函数)封装为一个整体,把对象的设计者和对象的使用者分开,使用者不必知晓行为实现的细节,可以增加安全性。
继承性:继承性是子类共享父类之间数据和方法的机制。一个类直接继承其它类的全部描述,同时可修改和扩充。可以增强代码的复用性。
多态性:对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种现象称为多态性。使具有不同内部结构的对象共享相同的外部接口。可以增加扩展性。
02 C的异常处理机制
在C语言中,传统的错误处理方式有如下几种:
1.直接终止程序(自杀)
例如:
int main(){
int a = 10;
int b = 20;
int c = a/0;
return 0;
}
当用gcc编译完后,执行,会打印“浮点数例外”,然后程序结束。
这种情况是不允许的,无条件终止程序的库无法运用到不能当机的程序里。
2.返回一个错误的值,附加错误码
这种错误处理方式也很常见,比如我们用C语言打开一个文件失败时:
#include<stdio.h>
#include<errno.h>
int main(){
FILE* fp = fopen("test.txt","r");
if(NULL == fp){
printf("文件打开失败,错误码:%d\n",errno);
}
return 0;
}
这种情况,比较常用,但是有时不合适,例如返回错误码是int,每个调用都要检查错误值,极不方便,也容易让程序规模加倍。
3.返回一个合法的值,让程序处于某种非法的状态
这种例子,最常见的就是 atoi 函数,例如:
#include<stdio.h>
#include<stdlib.h>
int main(){
int a = atoi("123456789"); //atoi()——把字符串转换成整型数
int b = atoi("dasdasdcs");
printf("a = %d\n",a);
printf("b = %d\n",b);
return 0;
}
虽然b为0,但是有一个全局变量表示了这个0属于非法值,不是由字符串0转过来的。
这种情况,很容易误导调用者,万一调用者没有去检查全局变量errno或者通过其他方式检查错误,那是一个灾难,而且这种方式在并发的情况下不能很好工作。
4.调用一个预先准备好在出现"错误"的情况下使用的函数
这种异常处理情况比较少见
#include<stdio.h>
#include<stdlib.h>
void DealError(){
printf("除数为0,老兄你在逗我?\n");
}
typedef void(*fun)();
int Div(int a,int b,fun callback){
if(b==0){
callback();
return 0;
}
return a/b;
}
int main(){
printf("正常情况下的4/2 = %d\n",Div(4,2,DealError));
printf("调用错误处理函数的4/0 = %d\n",Div(4,0,DealError));
return 0;
}
- 通过暴力的方式解决
暴力的方式有两种,abort()函数和常见exit()函数.例如依然处理除0问题,代码可以这样写:
#include<stdio.h>
#include<stdlib.h>
int Div(int a,int b){
if(b==0){
//exit(1); //直接退出
abort(); //在Windows下会弹出一个信息框
}
return a/b;
}
int main(){
printf("正常情况下的4/2 = %d\n",Div(4,2));
printf("调用错误处理函数的4/0 = %d\n",Div(4,0));
return 0;
}
- 使用goto语句
虽然,goto语句十分强大,但违背了程序的顺序执行,打乱的程序的执行流,盲目的使用goto语句可能会出现意想不到的错误,因此,并不推荐使用goto语句.
#include<stdio.h>
#include<stdlib.h>
int main(){
int a = 0;
int b = 0;
printf("请输入两个值:\n");
printf("a = ");
scanf("%d",&a);
printf("b = ");
scanf("%d",&b);
if(b==0){
goto Error;
}
printf("a/b = %d\n",a/b);
return 0;
Error:
printf("除数不能为0,程序异常退出!\n");
exit(-1);
}
03 异常的抛出与捕获
在C++中,异常的抛出和处理主要使用了以下三个关键字:try、 throw 、 catch.
当我们在程序中想抛出一个异常时,可以这样:
#include<iostream>
#include<exception>
using namespace std;
int Div(int left,int right){
if(right==0){
throw exception("除数不能为0");
}
return left/right;
}
当我们想使用这个函数时,需要在函数外部进行异常的捕获:
int main(){
try{
Div(10,20); //合法
Div(10,30); //合法
Div(10,0); //非法,会抛出异常
}catch(exception & e){
cout<<e.what();; //打印异常信息
}
return 0;
}
如果存在不同类型的异常,我们可以这样:
try{
//包含可能抛出异常的语句;
}catch(类型名 [形参名]){
//可能出现的异常1
}catch(类型名 [形参名]){
//可能出现的异常2
}catch(...){
//如果不确定异常类型,在这里可以捕获所有类型异常!
}
1.异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪部分代码.
就上述代码来说,我们throw了一个exception对象,因此在捕获异常时,最终会匹配到catch到exception的代码块.
2.被选中的处理代码是调用链中与该对象类型匹配且离抛出位置最近的那个.
当try内的代码块出现异常时,系统会根据catch的顺序和参数的匹配程度来选择执行哪个代码块,因此,系统会选择最靠前且参数越匹配的代码块.
3.抛出异常后会释放局部存储对象,所以被抛出的对象也就还给系统了,throw表达式会初始化一个抛出特殊的异常对象副本(匿名对象),异常对象由编译管理,异常对象在传给对应的catch处理之后撤销。
也就是说,在上述的除法代码中,我们throw出的对象在抛出异常后会还给操作系统,而throw表达式会自己初始化一个匿名的对象副本,在传给catch相应的代码块后被回收.
04 内存溢出与内存泄漏
内存溢出 out of memory:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak:是指程序在申请内存后,无法释放已申请的内存空间,失去了对该段内存的控制,因而造成了内存的浪费。 一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。