概述
在面向对象程序(OOP)设计思想出现之前,程序采用面向过程的设计方法,程序由数据和函数组成,函数是程序的基本组成单元。面向过程方法灵活性强,逻辑简单,但函数名众多容易造成混乱,且函数和数据分开存放,联系不够紧密,在大型软件开发中采用面向过程方式效率低下、维护困难。
类和对象
类和对象是面向对象编程的核心,类和对象类似建筑图纸和大楼的关系,类是对象的一种抽象,对象是类的一个实例。在面向对象世界中,一切事物都可以用对象表示,对象间通过消息进行交流,无需了解对象的内部实现,只要知道各个对象提供的功能接口就能实现各种高级功能。
#include "stdafx.h"
#include <iostream>
using namespace std;
class Rect { //矩形类
private : //私有成员变量
int x1, y1, x2, y2;
public : //公共的
Rect(int leftTop[],int rightBottom[]) { //构造函数1,参数为左下角,右下角坐标数组
x1 = leftTop[0];
y1 = leftTop[1];
x2 = rightBottom[0];
y2 = rightBottom[1];
}
Rect(int _x1,int _y1,int width,int height) { //构造函数2,参数为左上角,宽度,高度
x1 = _x1;
y1 = _y1;
x2 = _x1 + width;
y2 = _y1 + height;
}
int Area();
};
int Rect::Area(){ //类体外实现操作
return abs(x2-x1)*abs(x2-y1); //宽乘以高(取绝对值)
}
int main()
{
int leftTop[] = {12,15};
int rightBottom[] = { 30,60 };
Rect r1(leftTop,rightBottom);
Rect r2(12,15,17,60);
cout<< "r1 面积:" << r1.Area()<<endl;
cout << "r2 面积:" << r2.Area() << endl;
return 0;
}
类和对象的关系
类是一种自定义数据类型,类似于C中的结构体(struct),不同之处在于类中包括数据和函数,而结构体只有数据。类是对象的形式上的抽象,不是一个实际存在的事物,它规定有哪些数据成员,哪些成员函数及具体功能,是对具体事物的一种描述。对象是类的实现(implement),对象根据类的描述信息生成一个真实的事物,占用实际内存,对象生成时要调用类的构造函数(construct)进行初始化(initialize),对象释放(Release)时要调用类的析构函数做必要的清理工作。
定义类
使用class关键字定义一个类,可在类中添加成员变量(member variable)和函数。一般在类里声明函数,在类外实现函数以体现类的封装性,也可以声明和实现都在类中完成,适用于简短且频繁调用的成员函数。通过类名调用函数使用作用域解析操作符::,如A::Calc(); ,通过对象名调用函数使用圆点操作符.,如a.Calc(); 。
构造函数
类不同于C++的基本数据类型,类的结构可能非常复杂,包括大量的成员变量和函数,仅仅简单的分配一块足够大小的内存给类对象是不够的,还需要对类中的成员变量进行集中初始化。C++提供构造函数来完成类对象的初始化操作,构造函数先根据类的大小分配一块足够的内存用于存放类对象,再根据构造函数传入的参数和具体实现完成类对象的初始化。
析构函数
类在释放自身之前可能需要做一些清理工作,如类在使用过程中动态分配了内存空间,在类释放之前需要手动释放这些内存。析构函数用于完成类释放之前的一些清理工作,由C++自动调用。
同构造函数,析构函数名为在类名前加~符号,如~Rect(),析构函数没有返回值也没有参数列表,每个类只有一个析构函数,若没有添加析构函数,C++提供默认的析构函数,但什么也不做。
#include "stdafx.h"
#include <iostream>
#include <string.h>
using namespace std;
class STR { //自定义的处理字符串类
private:
char* pValue; // 字符指针,指向字符串
int nCount; //字符串长度,不包括末尾的’\0’
public:
STR(const char* str) { //构造函数
nCount = strlen(str); //获取传入字符串的长度
pValue = new char[nCount + 1]; //根据长度分配相应大小的内存
strcpy_s(pValue, nCount + 1,str); //将传入字符串的值复制到类成员变量中
cout << "动态创建字符数组" << endl;
}
~STR() //析构函数
{
delete [] pValue; //释放动态创建的字符数组
pValue = NULL; //字符指针值设为0
cout<<"动态创建的字符数组被释放" <<endl;
}
char getValue(int nIndex){ //获取单个字符值
if (nIndex<0 || nIndex>=nCount) { //若索引参数超出了范围,返回’\0’
return '\0';
}
else
{
return pValue[nIndex]; //返回字符值
}
}
};
int main() {
const char* c;
c = "hello the world";
STR s1(c);
cout<<" 第6个字符:" << s1.getValue(5) <<endl;
cout << "第20个字符:" << s1.getValue(19) << endl;
return 0;
}
内联函数
程序代码中经常要调用函数,从调用点进入到函数的内部,执行完毕后返回调用点,函数调用需要一定的开销。C语言为节省函数调用带来的开销,常使用宏(macro)的方式模拟函数,如求和运算定义宏 #define SUM(a,b) (a+b) ,编译时用宏替换代码,减少函数调用的开销。
#include <iostream>
using namespace std;
class AB
{
private:
int a;
int b;
public:
inline int GetA(); // inline关键字声明为内联函数
int GetB(){ return b; } //函数在类内实现,默认为内联函数
void SetA(int _a){ a=_a; } //设置a的值
void SetB(int _b){ b=_b; } //设置b的值
};
int AB::GetA(){ //内联函数,返回a
return a;
}
int main()
{
AB ab;
ab.SetA(100);
ab.SetB(45);
cout<<"a="<<ab.GetA()<<endl; //内联函数,调用时直接将函数代码插入到该位置
cout<<"b="<<ab.GetB()<<endl;
return 0;
}
extern:
extern int eVar; //表明该变量已在其他文件中定义过
extern void eFun(); //表明该函数已定义过
//extern void sFun(); //静态函数,只在函数定义所在的文件中可用
int main()
{
eFun(); //调用外部定义的函数
return 0;
}
static成员
static(静态)关键字在不同场合具有不同的意义,也是常混淆用途的一个关键字(keyword)。在C语言中,static有两层含义,若在函数外用static修饰全局变量和函数,表示具有文件作用域,只能在本文件中可用,不能在其他文件中使用。
#include <iostream>
using namespace std;
void display() //自定义函数
{
static int num=1; //静态局部变量,永久存在
for(int i=1;i<=num;i++) //输出1到num间的值
cout<<i<<" ";
cout<<endl;
num++; //静态局部变量递增
}
int main()
{
display(); //第1次调用,num为1
display(); //第2次调用,num为2
display(); //第3次调用,num为3
return 0;
}
------------------------------------------------------------------------------------------------------------------
class STA
{
public:
static int a; //静态成员变量,所有对象使用同一个值
};
int STA::a = 0; //静态成员在类体外初始化
int main()
{
STA s1, s2, s3; //创建类对象
s1.a = 1; //设置变量值
cout << "s2.a " << s2.a << endl; //查看值
cout << "s3.a " << s3.a << endl; //查看值
return 0;
}
const成员
const(constant常量)修饰符用于表示一个变量在初始化后不能再被修改,即只读(read only)。const变量在定义时必须同时进行初始化,常用在函数参数中,表示该参数在函数内部不会被修改。
在传递类对象时,若采用传值方式,将类对象的拷贝传入函数,在类对象复杂的时候效率很低,一般传递类对象的引用,但若使用引用方式,类对象的值可能会被函数修改,这时使用const修饰符可限定类对象的值为只读,如GetLength(const CString& str)。
#include "stdafx.h"
#include <iostream>
#include <string.h>
using namespace std;
class Constant
{
public:
const int a; //const成员变量
int b;
Constant(int _a, int _b) :a(_a) { //构造函数,const成员要用初始化列表
b = _b; //非const成员初始化
}
int Sum() const { //const成员函数,不能修改成员变量值
return a + b;
}
void SetB(const int _b) { //const参数在函数体内不能被修改
this->b = _b;
}
};
int main()
{
Constant c1(20, 30); //创建对象并初始化
c1.SetB(50); //非const对象调用非const成员函数
cout << "非const对象:" << c1.Sum() << endl; //非const对象调用const成员函数
const Constant c2(20, 30); //const对象
cout << "const对象: " << c2.Sum() << endl; //const对象只能调用const成员函数
return 0;
}
友元
类的私有成员一般情况下在类外不可访问,但有时候需要在类外访问,可使用friend关键字,在被访问类中声明为friend的类或函数可以访问其私有成员。
使用friend修饰的类、函数称为友元类、友元函数,友元不是类的成员,但能像类成员函数一样访问类的私有成员,当需要同时访问多个类的私有成员,适宜使用友元。
#include "stdafx.h"
#include <iostream>
#include <string.h>
using namespace std;
class Equal; //友元类,在定义前使用需要做类声明
class FRI1
{
private:
int a; //私有成员
public:
friend class Equal; //类Equal是友元类,可以访问FRI1类的私有成员
FRI1(int _a) :a(_a) {} //构造函数
};
class FRI2
{
private:
int a;
public:
friend class Equal; //类Equal是友元类,可以访问FRI2类的私有成员
FRI2(int _a) :a(_a) {}
};
class Equal
{
public: //静态函数,可直接用类名调用,比较两个类的值是否相等
static bool equal(const FRI1& f1, const FRI2& f2) {
if (f1.a == f2.a) //若FRI1类对象的私有成员a等于FRI2类对象的私有成员
return true; //返回true
else
return false;
}
};
int main()
{
FRI1 f1(20);
FRI2 f2(20);
cout << "f1和f2的值是否相等:" << Equal::equal(f1, f2) << endl;
return 0;
}
运算符重载
C++提供一些运算符用于完成基本的运算,如+可以计算两个整型或浮点类型数值的和,==可以判断两个值是否相等,但都只能用于预置的基本类型。若想用+得到两个字符串拼接后的新字符串,用==判断两个类是否包含相同的值,可使用C++提供的运算符重载功能,类似函数重载,运算符可看做函数名,运算符两边的变量可看做函数的参数,调用时根据运算符的参数数目和类型自动选择匹配的版本。
了解运算符重载
运算符重载在定义时使用operator关键字,根据运算符操作数的数目分为一元重载和二元重载,如+、/、%为二元运算符,++、--为一元运算符,-当做减号为二元,当做负号为一元。
运算符可看做为函数,如operator +类似Add(STR a,int b); +两边的操作数类似参数a、b,重载的运算符可作为类的成员,也可作为类的友元函数,也可作为普通函数。
#include "stdafx.h"
#include <iostream>
using namespace std;
class IntOper
{
private:
int a; //私有成员
public:
void SetA(int _a) { //设置a的值
a = _a;
}
int GetA() { //获取a的值
return a;
}
int operator ++() { //默认为前缀,先自增,后赋值
a = a + 1;
return a;
}
int operator ++(int) { //添加int参数,表示后缀,先赋值,后自增
a = a + 1;
return a - 1;
}
int operator --() { //默认为前缀,先自减,后赋值
a = a - 1;
return a;
}
int operator --(int) { //添加int参数,表示后缀,先赋值,后自减
a = a - 1;
return a + 1;
}
};
int main()
{
IntOper oper;
oper.SetA(20); //设置值
cout << "oper : " << oper.GetA() << endl; //获取当前值
cout << "++oper : " << ++oper << endl; //前缀自增
cout << "oper++ : " << oper++ << endl; //后缀自增
cout << "--oper : " << --oper << endl; //前缀自减
cout << "oper-- : " << oper-- << endl; //后缀自减
return 0;
}
一元重载
一元重载只有一个操作数即类名,一般作为类成员,常用的一元运算符有自增、自减、求负运算符,其中自增、自减运算有前后之分,运算符在前表示先自增自减,然后再赋值,在后表示先赋值然后再自增自减,若为单独的表达式则没有区别。默认重载++、--为前缀版本,若表示后缀版本,添加一个int参数。
class IntArray
{
private:
int* pInt; //整型指针,指向动态创建的数组
int nCount; //元素数目
public:
IntArray(int count){ //构造函数,根据传入参数创建相应大小的数组
nCount=count;
pInt=new int[nCount];
}
~IntArray(){ //析构函数,释放动态创建的数组
delete [] pInt;
pInt=NULL;
}
int operator[](int index) const{ //重载[],参数为下标索引
if(index<0 || index>=nCount) //若参数不合理,返回0
return 0;
return pInt[index]; //返回对应位置的元素值
}
void SetAt(int index,int value){ //设置元素值,参数为索引和元素值
if(index<0 || index>=nCount) //若索引不合理,直接返回
return;
pInt[index]=value; //设置对应位置的元素值
}
int GetLength(){ //获取元素数目
return nCount;
}
};
int main()
{
IntArray arr(3); //创建对象,元素数目为3
arr.SetAt(0,12); //设置每个元素值
arr.SetAt(1,45);
arr.SetAt(2,80);
for(int i=0;i<arr.GetLength();i++) //输出所有元素
{
cout<<"第"<<i+1<<"个元素:"<<arr[i]<<endl; //调用重载的[]运算符,返回第i个元素值
}
cout<<endl;
return 0;
}
二元重载
二元重载有两个操作数,若作为类成员,只需一个参数,否则需要两个参数。参数的顺序代表操作数的位置,如operator +(A a,int b){} 调用形式为a+2; 不能为2+a; 若调换位置也可用,要再重载一次如operator +(int b,A a){}
继承性
面向对象编程的一个重要特性是继承性,类之间可以有继承关系,通过从一个父类派生出子类,子类在拥有父类功能的基础上,添加新增的功能,无需从零开始构建整个类,只需要补充新增的功能即可。C++允许多重继承,即从多个父类派生出一个子类,子类拥有多个父类的功能,多重继承虽功能强大,却复杂难以理解,在C#、Java中已取消了多重继承,应避免使用。
MFC类库中绝大多数类都继承于CObject类,CObject类提供了基本的服务,如运行时(runtime)的类型判断、对象的序列化存储等,所有从CObject类继承的类自动具有这些功能。
类的继承
父类称为基类,从基类继承的子类称为派生类,在类定义时指定要继承的基类,派生类自动获取基类的成员变量和函数,但不会获取基类的构造函数和析构函数。
创建派生类对象时,首先要初始化基类,可在派生类的构造函数里调用基类的构造函数,若没有显示调用基类的构造函数,自动调用基类的默认无参数版本构造函数。
#include <iostream>
#include <string> //包含string类头文件
using namespace std;
class Person //基类
{
protected: //保护成员,派生类中可访问
int nYear; //年龄
string sex; //性别
string name; //名称
public:
Person(){} //无参构造函数
Person(int _year,string _sex,string _name){
nYear=_year; //有参构造函数,初始化成员变量
sex=_sex;
name=_name;
}
void display(){ //输出成员,基类版本
cout<<"姓名:"<<name<<endl;
cout<<"年龄:"<<nYear<<endl;
cout<<"性别:"<<sex<<endl;
}
};
class Student:public Person //派生类,公有继承于类Person
{
protected: //保护成员
string school; //学校
string number; //学号
public:
Student(int _year,string _sex,string _name,string _school,string _number)
:Person(_year,_sex,_name){ //调用基类的构造函数
school=_school; //初始化新增成员变量
number=_number;
}
void display() //输出所有成员,派生类版本,函数重定义
{
Person::display(); //调用基类版本函数
cout<<"学校:"<<school<<endl; //输出
cout<<"学号:"<<number<<endl;
}
};
int main()
{
Student s1(1985,"男","Luo","hpu","0216"); //创建类对象并初始化
s1.display(); //调用派生类版本输出函数
return 0;
}
访问控制
类继承有三种方式:公有(public)、保护(protected)、私有(private)继承。基类的私有成员无论何种继承方式,在派生类中都不可访问。
若为私有继承,基类的公有和保护成员在派生类中变为私有。若为保护继承,基类的公有和保护成员在派生类变为保护成员。若为公有继承,基类的公有成员和保护成员在派生类中保持不变,一般情况下只使用public公有继承方式
调用流程
基类的构造函数和析构函数不会被派生类继承。若类B继承类A,类C继承类B,则创建一个类C对象时,先调用类A的构造函数,然后类B,再调用类C的构造函数,若类C对象释放时,先调用类C的析构函数,然后类B,最后调用类A的析构函数。即构造函数调用顺序为从基类到派生类,析构函数与构造函数顺序相反,从派生类到基类。
#include <iostream>
using namespace std;
class A //类A
{
public:
A(){ //构造函数
cout<<"构造 A"<<endl;
}
~A(){ //析构函数
cout<<"析构 A"<<endl;
}
};
class B:public A //类B派生自类A
{
public:
B(){
cout<<" 构造 B"<<endl;
}
~B(){
cout<<" 析构 B"<<endl;
}
};
class C:public B //类C派生自类B
{
public:
C(){
cout<<" 构造 C"<<endl;
}
~C(){
cout<<" 析构 C"<<endl;
}
};
int main()
{
C c1; //类C对象,创建时调用构造函数,释放时调用析构函数
return 0;
}
多态性
用类指针去调用类成员函数时,根据指针的类型即可确定调用的函数版本。如类B继承类A,类B重定义了类A的Calc函数,若有类A指针p调用Calc函数,调用了类A版本的Calc函数,若将类A指针p改为指向类B对象(指针类型没有变),再调用Calc函数,仍调用类A版本的Calc函数。重定义函数的调用仅与指针类型相关,与指针实际指向对象无关。
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
class A //基类
{
public:
virtual void Calc() { //使用virtual关键字,表明为虚函数
cout << "类A 版本的Calc" << endl;
}
};
class B :public A //派生类
{
public:
void Calc() { //重写虚函数
cout << "类B 版本的Calc" << endl;
}
};
int main()
{
A a; //类A对象
B b; //类B对象
A* p = &a; //类A指针,指向A对象
p->Calc(); //调用类A版本的Calc
p = &b; //指针改为指向类B对象
p->Calc(); //调用类B版本的Calc
return 0;
}
多态性的实现
多态性与函数重定义的区别在于:重定义函数的调用与指针类型相关,在程序编译时就确定了调用的函数版本,多态性的函数调用与指针实际所指对象相关,在程序运行时才确定调用的函数版本。
多态性通过一种晚期绑定的方式实现运行时的识别,实现晚期绑定可使用virtual关键字声明一个函数是虚函数,只需要在基类中声明函数为虚函数,在派生类中对该函数重写后,就可实现动态绑定。
virtual虚函数
添加一个virtual关键字就可以实现动态绑定,在于编译器为每个包含虚函数的类生成一个虚函数表(virtual table),同时在类对象的内存空间的头部添加一个虚指针,指向生成的虚函数表。虚函数表存放该类所有的虚函数,若在派生类中重写了基类的虚函数,则派生类的虚函数表中存放的是重写后的虚函数。
抽象类
在实际开发中,可能某些基类只是做个规范,形式上确定有哪些基本成员及其功能,并不涉及具体的实现。如基类CMap定义有虚函数Draw(),作为所有图形类的绘制函数,基类下有派生类CLine、CPoint、CRect分别用于绘制线、点、矩形,具体的图形绘制方法在派生类的Draw函数里完成,而基类的Draw只是一个接口的规范,不需要具体实现,此时可设置Draw函数为纯虚函数。
模板
标准C++库除了包含标准C函数库、IO输入输出类、string字符串类之外,还提供一套强大的通用类和算法库,即标准模板库STL(Standard Template Library),包括常见的数据结构和算法,如链表、栈、队列等。
得益于C++的模板(Template)机制,STL适用于任何数据类型,如链表list既可存放整型,也可存放string类型。模板也被称为泛型编程,忽略实际的数据类型,使用模板的函数和类可以适用于多种数据类型,从而节省工作量,提高代码重用性。
如何定义模板
在定义类或函数时使用模板可以忽略实际数据类型,调用时根据实际的数据类型,生成一个函数或类的具体定义。通过在函数或类的定义前添加template<class T>,表明定义的函数或类使用了模板机制,class是类型关键字,T是一个未知的数据类型,调用时被实际类型替换。若有两个模板参数,形式为template<class T1,class T2>,T1和T2为两个未知的数据类型。
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
template<class T> //模板声明,T为参数类型
T Sum(T a, T b) //模板函数,计算两个未知类型的和
{
return a + b;
}
int main()
{
int a = 10, b = 50;
double c = 15.2, d = 45.3;
char e = '0', f = '1';
cout << "int " << Sum(a, b) << endl; //整型参数
cout << "double " << Sum(c, d) << endl; //浮点型参数
cout << "char " << Sum(e, f) << endl; //字符型参数
return 0;
}
模板类
模板类是泛型程序设计的基础,可以处理多种类型的数据,将通用功能集成到模板类中,可节省大量重复性工作。类似于函数模板,定义模板类之前使用template<class T>做模板声明,在类中可用未知类型T进行相关处理。
template<class T>只能在其后的类或函数中使用一次,若要在多个类或函数中使用模板,需要各自在定义前声明一次。传入实际数据类型时,形式为Calc<int> c1(12,35); 在<>中输入数据类型。
template<class T> //模板声明
class Calc //模板类
{
private:
T a; //成员变量,未知类型
T b;
public:
Calc(T _a,T _b):a(_a),b(_b){} //构造函数
T Add(); //成员函数
T Sub();
};
template<class T> //模板函数
T Calc<T>::Add(){ //类外实现
return a+b;
}
template<class T>
T Calc<T>::Sub(){
return a-b;
}
int main()
{
Calc<int> c1(12,35); //模板类对象,指明对象类型为int
Calc<double> c2(35.8,15.3); //指明对象类型为double
cout<<"int "<<c1.Add()<<endl;
cout<<"double "<<c2.Sub()<<endl;
return 0;
}
标准模板库STL
STL是标准C++库的一部分,包含链表(list)、向量(vector)、栈(stack)、队列(queue)等常见数据结构,STL中的类都是模板类,适用于任意数据类型,也称为容器类。STL使用迭代器(iterator)遍历容器中的元素,迭代器类似于指针,根据迭代器变量可查找到容器中的某个元素。
通过迭代器重载的运算符可操作迭代器,如++用于将迭代器指向下一个元素,--用于指向上一个元素,==和!=用于判断两个迭代器是否指向同一个元素,*用于获取迭代器指向元素的引用。
向量:
#include <iostream>
#include <vector> //包含vector头文件
#include <string> //包含string头文件
using namespace std;
int main()
{
vector<string> v; //存放string类型的vector
v.push_back("1.Hello"); //尾部添加string字符串
v.push_back("2.Vector");
v.push_back("3.Test");
cout<<"元素数目:"<<v.size()<<endl; //元素数目
for(vector<string>::iterator p=v.begin();p!=v.end();p++) //使用迭代器遍历向量
cout<<*p<<endl; //获取迭代器指向的值
cout<<"第1个元素:"<<v[0]<<endl; //使用[]获取元素值
return 0;
}
链表(list)
#include <list> //包含list头文件
using namespace std;
int main()
{
list<int> ls; //存放int类型的list
cout << "所有元素: ";
for (int i = 1; i <= 10; i++) //添加10个元素
{
int value = rand() % 10; //获取0-9的随机值
ls.push_back(value); //添加到list尾部
cout << value << " "; //输出当前元素
}
cout << "排序后: "<< endl;
ls.sort(); //list排序,默认为升序
list<int>::iterator p; //当前类型的迭代器
for (p = ls.begin(); p != ls.end(); p++) //遍历list
cout << *p << " ";
cout << endl << "去除重复元素:";
ls.unique(); //list去除重复项
for (p = ls.begin(); p != ls.end(); p++)
cout << *p << " ";
cout << endl;
return 0;
}
异常处理
程序在执行过程中时常会发生一些异常(exception),如打开文件时找不到指定文件、分配内存时内存不足、除法运算中分母为0、连接不上数据库等,有些异常可通过代码判断,但有些是难以预测的。为保证程序运行时能够正确处理各种异常情况,不会直接崩溃退出,C++提供异常处理机制,可以获取运行时的异常情况,并提供错误处理方法,从而提高程序的健壮性。
处理程序异常
C++提供try和catch关键字处理异常情况,将正常执行的代码放入try{}语句块中,catch{}块中放入处理异常的代码。若无异常发生,只执行try块中的语句,若发生了异常,停止执行try块中剩下的语句,直接跳入catch块中执行异常处理代码,使用try和catch可以将正常代码和异常代码区分开来,增强代码的可读性。
double divide(double a,double b) //可抛出异常的函数
{
if(b==0) //若分母为0,抛出整型异常
throw 0;
return a/b;
}
int main()
{
double a=12.5;
double b=0;
double result;
try{ //异常处理
result=divide(a,b); //调用函数
cout<<"结果是"<<result;
}
catch(int e){ //捕获函数divide抛出的异常,参数类型与抛出类型一致
cout<<"分母不能为"<<e<<endl; //异常处理语句
}
return 0;
}
自定义异常类
throw可抛出任意类型的异常,可自定义一个异常类,用以提示更多的错误信息。MFC类库提供一系列异常类,用以抛出不同类型的异常,如内存不足抛出CMemoryException异常、文件错误抛出CFileException异常。
#include <iostream>
#include <string>
using namespace std;
class Exception //自定义异常类
{
private:
string message; //错误提示信息
public:
Exception(){ //构造函数
message="自定义的异常类";
}
string GetMessage(){ //获取错误信息
return message;
}
};
int main()
{
try{
int b=0;
if(b==0)
throw Exception(); //抛出自定义类异常
}
catch (Exception e){ //捕获抛出的自定义类异常
cout<<e.GetMessage()<<endl; //输出自定义类的错误信息
}
return 0;
}