抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

结构体

结构体定义

    整形、长整型、字符型、浮点型这些数据类型只能记录单一的数据,他们只能被称作基本的数据类型,如果要描述一个人的基本信息,就需要定义多个变量来记录这些信息。例如,身高需要一个变量,体重需要一个变量,年龄需要一个变量。如果有一个类型可以将这些变量包括在一起,则会大大减少程序代码的离散性,使程序代码阅读更加符合逻辑,结构体则是实现这一功能的类型,
    结构体的定义如下:

1
2
3
4
5
struct 结构体类型名 {
成员类型 成员名;
...
成员类型 成员名;
};

    struct是定义结构体的关键字。结构体类型名是一种标识符,该标识符代表一个新的变量。结构体使用大括号将成员括起来,每个成员都有自己的类型,成员类型可以是常规的基础类型,也可以是自定义类型,同时还可以是一个类型。
    例如,定义一个简单员工信息的结构体:

1
2
3
4
struct worker {
string w_name;
int w_index;
};

其中,

  • 结构体类型名是worker。在结构体中定义了2个不同的变量。这2个变量就好像2个球放到了1个盒子里,只要能找到盒子就能找到这2个球。同样,找到名字worker的结构体,就可以找到结构下的变量。这2个变量的数据类型各不相同,有字符串型,有整形,分别定义了员工的姓名和工号。

给结构体下个定义,就是由多个不同类型的数据组合成的数据组合,而数组是相同元素的组合。

结构体变量

    结构体是一个构造类型,前面只是定义了结构体,形成一个新的数据类型,还需要使用该数据类型来定义变量,结构体变量有2种类声明形式。

    1. 在定义结构体后,使用结构体类型名声明,例如:
1
2
3
4
5
struct Worker {
string w_name;
int w_index;
};
Worker worker;
    1. 定义结构体时直接声明,例如:
1
2
3
4
struct Worker {
string w_name;
int w_index;
} worker;
    1. 直接声明结构体变量时,可以声明多个变量,例如:
1
2
3
4
struct Worker {
string w_name;
int w_index;
} worker, worker1;

结构体成员及初始化

    引用结构体成员有2种方式,一种是声明结构体变量后,通过成员运算符.引用;另一种是声明结构体指针变量,使用指向运算符->引用。

    使用成员运算符.引用结构体成员,一般形式如下:

1
结构体变量名.成员名

    例如:

1
2
3
4
5
6
7
8
struct Student {
string w_name;
float w_grade;
};

Student student;
student.w_name = 'Ciraos';
student.w_grade = 94.6;

    引用到结构体成员后,就可以分别对结构体成员进行赋值,对于每个结构体成员就和使用普通变量一样。
    例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

struct Student {
string w_name;
float w_grade;
int w_age;
};

int main()
{
Student student;
student.w_age = 24;
student.w_index = 96.2;
student.w_name = 'Ciraos';

cout << student.w_name << endl;
cout << student.w_age << endl;
cout << student.w_grade << endl;

return 0;
}

    程序分别为引用结构体的每个成员,然后赋值,其中为字符数组赋值需要使用strcpy字符串复制函数,结构体可以在定义时直接对结构体变量进行赋值,
    例如:

1
2
3
4
struct Studentinfo {
string w_name;
int w_index;
} student={"Ciraos", 96};

    在定义结构体时,可以同时声明结构体指针变量,
    例如:

1
2
3
4
5
6
struct Student {
string w_name;
int w_index;
};

Student *student;

    如果要引用指针结构体变量的成员,需要使用指向运算符->
    一般形式如下:

1
结构体指针变量->成员名

    例如:

1
2
worker->name
worker->index

结构体指针变量只有在初始化后才能使用。

    例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

struct Student {
string w_name;
int w_index;
} *student, studenti={"Ciraos", 90};

int main()
{
student = &studenti;

cout << student->w_name << endl;
cout << student->w_index << endl;

return 0;
}

结构体的嵌套

    定义完结构体后就形成一个新的数据类型,C++语言在定义结构体时可以声明其他已经定义好的结构体变量,也可以在定义结构体时定义子结构体。

    1. 在定义结构体中定义子结构体,例如:
1
2
3
4
5
6
7
8
9
struct Student {
string w_name;
int w_index;
int w_age;

struct StudyPlace {
string addressArray[100];
};
};
    1. 在定义时声明其他已经定义好的结构体变量,例如:
1
2
3
4
5
6
7
8
9
10
struct Student {
string w_name;
int w_index;
}

struct Student_Book {
string address;
string postcode;
Student student;
};
    1. 通过上面两种形式都可以完成结构体的嵌套,下面通过第一钟方式来实现结构体的嵌套。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

struct Personinfo {
string name;
int index;

struct Work_Place {
string address;
string postcode;
} workerplace;
};

int main()
{
Personinfo pinfo;
string address;
string postcode;
cin >> address >> postcode;

//...

return 0;
}

结构体的大小

    结构体是一种构造的数据类型,数据类型都与占用内存多少有关。在没有字符对齐要求或结构成员对其单位为1时。结构体变量的大小是定义结构体时成员大小之和,
    例如,Personinfo结构体:

1
2
3
4
struct Personinfo {
string name;
int index;
}

其中,

  • Personinfo结构体的大小是成员nameindex大小之和。成员name是字符串,占用24个字节;成员index是整型数据,在32位系统中,占用4个字节。使用,Personinfo结构体的大小为24+4=28
        可以使用sizeof()运算符来获取结构体的大小,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string.h>
using namespace std;

struct Personinfo {
string name;
int index;
};

int main(int argc, char const *argv[])
{
cout << sizeof(Personinfo) << endl;

return 0;
}

重命名数据类型

    C++允许使用关键字typedef给一个数据类型定义一个别名,
    例如:

1
typedef int flag;

    这样,程序中flag就可以作为int的数据类型来使用:

1
flag a;

    a实际上是int类型的数据,此时int类型的别名就是flag
    类或者结构在声明的时候使用typedef
    例如:

1
2
3
typedef class asdfgh {
//....
} myclass, classA;

    这样就令声明的类有myclassclassA两个别名。

    typedef的主要用途如下:

  1. 代替很复杂的基本类型名称,例如函数指针int (*)(int i)
1
typedef int (*)(int i) pFun;
  1. 使用其他人开发的类型时,使它的类型名符合自己的代码习惯(规范)。typedef关键字具有作用域,范围是别名声明所在区域(包含名称空间)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
using namespace std;

namespace pet {
typedef string kind;
typedef string petname;
typedef string voice;

typedef class dog {
private:
kind m_kindname;
protected:
petname m_dogname;
int m_age;
voice m_voice;
void setVoice(kind name);
public:
dog(kind name);
void sound();

void setName(petname name);
} Dog, DOG;

void dog::setVoice(kind name) {
if (name == "北京犬") {
m_voice = "嗷嗷";
}
else if (name == "狼犬") {
m_voice = "嗷呜";
}
else if (name == "黄丹犬") {
m_voice = "喔嗷";
}
}

dog::dog(kind name) {
m_kindname = name;
m_dogname = name;
setVoice(name);
}

void dog::sound() {
cout << m_dogname << "发出" << m_voice << "的叫声。" << endl;
}

void dog::SetName(petname name) {
m_dogname = name;
}
}

using pet::dog;
using pet::DOG;

int main()
{
dog a = dog("北京犬");
pet::Dog b = pet::Dog("狼犬");
pet::DOG c = pet::DOG("黄丹犬");

a.setName("小白");
c.setNmae("阿黄");

a.sound();
b.sound();
c.sound();

return 0;
}

    在pet名称空间中定义了多种类型别名。这些类型的实际别名并不发生改变,在主函数内演示了如何使用名称空间中的类别名。
    宠物狗dog类中使用string类来区分小狗的种类,通过setVoice()函数来设定每种小狗的声音,那么,有没有比使用string对象更简便的方法了呢?除了建立3个子类之外,有没有更简便的方法呢?请听下回分解!

结构体与函数

结构体变量做函数参数

    可以把结构体变量当普通变量一样作为u函数参数传递的,这样可以减少函数参数的个数,使代码看起来更简洁。
    例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

struct Personinfo {
string name;
int idnex;
};

void showPersonMessage( struct Personinfo myinfo ) {
cout << myinfo.name << endl;
cout << myinfo.index < endl;
}

int main()
{
Personifno pinfo;
pinfo.index = 1;
//strcpy(pinfo.name, "Ciraos");
pinfo.age = 20;
showPersonMesage(pinfo);

return 0;
}

其中,

  • 程序自定义了参数showPersonMessage,该函数使用Personinfo结构体作为参数。如果不适用结构体作为参数,函数需要将nameindex2个成员分别定义为参数。

结构体指针做函数参数

    使用结构体指针变量做函数参数时传递的只是地址,减少了时间、空间上的开销,0能够提高程序的运行效率。这种方式再在实际运用中效果比较好。
    例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

struct Personinfo {
string name;
int idnex;
};

void showPersonMessage( struct Personinfo myinfo ) {
cout << myinfo->name << endl;
cout << myinfo->index < endl;
}

int main()
{
Personifno pinfo;
pinfo.index = 1;
//strcpy(pinfo.name, "Ciraos");
pinfo.age = 20;
showPersonMesage(pinfo);

return 0;
}

结构体数组

结构体数组声明与引用

    数组的元素也可以是结构体类型的,因此可以构成结构体类型数组。结构体数组的每一个元素都是具相同统结构类型的下标结构变量。

    1. 在定义结构体时直接声明,例如:
1
2
3
4
struct studentInfo {
string name;
int index, age;
} student[10];
    1. 直接声明结构体数组,例如:
1
2
3
4
5
struct Studentinfo {
string name;
int index, age;
};
Studentinfo student[10];
    1. 直接声明结构体数组,例如:
1
2
3
4
struct {
string w_name;
int w_index, w_age;
} student[10];

可以在声明结构体时直接对数组进行初始化:

1
2
3
4
5
6
7
8
struct Studentinfo {
string w_name;
int w_index, w_age;
} student[3] = {
{"张三", 0, 20},
{"李四", 1, 21},
{"王二麻子", 2, 22},
};

当对全部元素进行初始化赋值时,也可不给出数组长度。

指针访问结构体数组

    指针变量可以指向一个结构体数组,这时结构体指针变量的值是这个结构体数组的首地址。结构体指针变量也可指向结构体数组的一个元素,这时结构体指针变量的值是该结构体数组元素的首地址。

    使用指针访问结构体数组,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;

struct Student {
string w_name;
int w_index, w_age;
} student[3] = {
{"张三", 0, 20},
{"李四", 1, 21},
{"王二麻子", 2, 22},
};

int main()
{
struct Student *sinfo;
sinfo = student;

for ( int i=0; i<3; sinfo++) {
cout << sinfo->w_name << endl;
cout << sinfo->w_index << endl;
cout << sinfo->w_age << endl;
}

return 0;
}

共用体

    共用体数据类型是指将不同的数据项组织为一个整体,它和结构体有点类似,但共用体在内存中占用首地址相同的一段存储单元。因为共用体的关键字为union,中文意思为联合,所以共用体也成为联合体。

共用体的定义与声明

    定义共用体类型的一般形式如下:

1
2
3
4
5
6
union 共用体类型名 {
成员类型 共用体成员名1;
成员类型 共用体成员名2;
.......
成员类型 共用体成员名n;
};
  • uniuon是定义共用体数据类型的关键字,共用体类型名是一个该标识符,该标识符以后就是一个新的数据类型,成员类型是常规的身负巨类型,用来设置共用体成员储存空间。

    声明共用体数据类型变量有以下几种方式:

    1. 先定义共用体,然后声明共用体变量,例如:
1
2
3
4
5
6
7
union Myunion {
int i;
string str;
float t;
};

Myunion u;
    1. 可以直接在定义时声明共用体变量,例如:
1
2
3
4
uniuon Myunion {
int i;
string str;
} u;
    1. 也可以直接声明共用体变量,例如:
1
2
3
4
union {
int i;
sring str;
} u;

其中,
    第三种方式省略了共用体类型名,直接声明了变量u
    引用共用体对象成员和引用结构体对象类型的方式相同,也就是使用.运算符,例如,引用共用体u的成员:

1
2
3
u.i
u.ch
u.f

共用体的大小

    共用体每个成员分别占有自己的内存单元。共用体变量所占的内存长度等于最长的成员的长度。一个共用体变量不能同时存放多个成员的值,某一时刻只能存放其中的一个成员的值,这就是最后赋予它的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
using namespace std;

union Myunion {
int iData;
string sData;
float fData;
}

int main()
{
Myunion ustr;

ustr.iData = 10;
ustr.sData = 'a';
ustr.fData = 10.2;
cout << ustr.iData << endl;
cout << ustr.sData << endl;
cout << ustr.fData << endl;

ustr.iData = 20;
ustr.sData = 'b';
ustr.fData = 12.2;
cout << ustr.iData << endl;
cout << ustr.sData << endl;
cout << ustr.fData << endl;

ustr.iData = 30;
ustr.sData = 'c';
ustr.fData = 14.2;
cout << ustr.iData << endl;
cout << ustr.sData << endl;
cout << ustr.fData << endl;

return 0;
}

    程序中按不同顺序为ustr变量的3个成员赋值,结果显示只有最后赋值的成员才能正确显示。

共用体的特点

    共用体数据类型有以下几个特点:

  1. 使用共用体变量的目的是希望用同一个内存段存放几种不同类型的数据,但请注意,在每一瞬时只能存放其中一种,而不是同时存放几种;
  2. 能够访问的是共用体变量中最后一次被赋值的成员,在对一个新的成员赋值后原有的成员就失去作用;
  3. 不能对共用体变量名进行赋值,不能企图引用变量名来得到一个值;不能在定义共用体变量时对它初始化;不能用共用体变量名作为函数参数。

枚举类型

    枚举就是一一列举的意思,在C++类型中枚举类型就是一些标识符的集合,从形式上看枚举类型就是用大括号将不同标识符名称放在一起。用枚举类型声明的变量,其变量的值只能取自括号内的这些标识符。

枚举的类型声明

    枚举类型定义有一下两种声明形式:

    1. 一般形式:
1
enum 枚举类型名 {标识符列表};

例如:

1
enum weekday { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

    enum是定义枚举类型的关键字,weekday是新定义的类型名,大括号内就是枚举类型变量应取的值。

    1. 带赋值的枚举类型声明形式如下:
1
2
3
4
5
6
7
enum 枚举类型名 {
标识符[=整型常数];
标识符[=整型常数];
......
标识符[=整型常数];
} 枚举变量;

例如:

1
enum weekday { Sunday=0, Monday=1, Tuesday=2, Wednesday=3, Thursday=4, Friday=5, Saturday=6 };

    使用枚举类型的说明如下:

    1. 编译器默认将标识符自动赋上整型常数,例如:
1
2
3
4
5
enum weekday { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

// or:

enum weekday { Sunday=0, Monday=1, Tuesday=2, Wednesday=3, Thursday=4, Friday=5, Saturday=6 };
    1. 可以自行修改整型常数的值,例如:
1
enum weekday { Sunday=1, Monday=2, Tuesday=3, Wednesday=4, Thursday=5, Friday=6, Saturday=7 };
    1. 如果只给前几个标识符赋整型常数,编译器会给后面标识符累加赋值,例如:
1
enum weekday { Sunday=7, Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday };

枚举类型变量

    在声明了枚举类型后,可以用它来定义变量,例如:

1
2
enum weekday { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
[enum] weekday myworkday;

myworkdayweekday的变量。在C++语言中,枚举类型名包括关键字enum,在C++中允许不写enum关键字

    关于使用枚举变量的说明如下:

    1. 枚举变量的值只能是SaturdaySunday之一,例如:
1
2
myworkday = Tuesday;
myworkday = Saturday;
    1. 一个整数不能直接赋给一个枚举变量,例如:
1
2
enum weekday { Sunday=7, Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday };
enum weekday day;

    则day=(enum weeekday)3;等价于day=Wednesday;day=3是错误的。
    整形虽然不能直接作为枚举类型变量赋值,但是可以通过强制类型转换,将整数转换为合适的枚举型数值。

    枚举变量赋值,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

int main()
{
enum Weekday { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
int a=2, b=1;
Weekday day;

day = (Weekday)a;
cout << day << endl;

day = (Weekday)(a-b);
cout << day << endl;

day = (Weekday)(Sunday + Wednesday);
cout << day << endl;

day = (Weekday)5;
cout << day << endl;

return 0;
}

    程序中使用了各种形式的赋值,其原理都是一样的,都是通过强制转换为枚举变量赋值。

    1. 可以直接定义枚举变量,例如:
1
enum (sun, mon, tue, wed, thu, fri, sat) workday, week_end;

枚举类型的运算

    枚举值相当于整形变量,可以用枚举值来进行一些运算。

    1. 利用枚举值做判断比较,枚举值可以和整型变量一起比较,枚举值和枚举值之间也可以比较。

    枚举值的比较运算,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

int main()
{
enum Weekday { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

Weekday day1, day2;
day1 = Monday;
day2 = Saturday;
int n;
n = day1;
n = day2+1;

if ( n>day1 ) cout << "n>day1" << endl;
if ( day1<day2 ) cout << "day1<day2" << endl;

return 0;
}

    程序进行变量n和枚举变量day1的比较以及枚举变量day1day2的比较。

    1. 利用枚举类型进行减法运算,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

enum weekday { Sunda=2, Monday=3, Tuesday=4, Wednesday=5,, Thursday=6, Friday=7, Saturday=8 };

int main()
{
weekday m_eType1, m_eType2;
m_eTyte1 = Sunday;
m_eType2 = Friday;
cout << m_eType1-m_eType2 << endl;

return 0;
}

    程序直接将两个枚举变量m_eType1m_eType2进行减法运算,然后将运算结果输出。

自定义数据类型

    C++语言提供了丰富的数据类型,对于一些由用户自己的构造型数据类型,还允许用户自己定义类型说明符,即由用户为定义的数据类型名另外再取一个别名。以便简化对类型名的引用,或增加程序的可读性。这个功能由类型定义符typedef完成。

    typedef的使用形式如下:

1
typedef <原类型名> <新类型名>

    原类型名是任意已定义的数据类型,包括系统的各种基本数据类型名以及用户自定义的构造类型名。
    新类型名是用户自己命名的标识符。在以后变量的声明中可以直接使用该名称,例如:

1
2
3
4
5
typedef int INTEGER;
INTEGER a, b;

//等价于:
int a, b;

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

typedef string *CString;

int main()
{
CString str;
str = "Hell world!";

cout << str << endll;

return 0;
}

    程序将字符指针重命名为CString,CString就代表字符指针。

    自定义数据类型的大小用原数据类型一样,例如:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
using namespace std;

typedef string *CString;

int main()
{
cout << sizeof(CString) << endl;
cout << sizeof(string) << endl;

return 0;
}

异常处理

    异常处理是程序设计中除田松hi之外的另一种错误处理方法,它往往被大多数程序设计人员在实际设计中忽略。异常处理引起的代码膨胀将不可避免地增加程序阅读的困难,这对于程序设计人员来说是十分烦恼的。异常处理与真正的错误处理有一定区别,异常处理不但可以对系统错误作出反应,还可以对人为制造的错误作出反应并处理。

抛出异常

    当程序执行到某一函数或方法内部时,程序本身出现了一些异常,但这些异常并不能由系统所捕捉,这时就可以创建一个错误信息,再由系统捕获该错误信息并处理。创建错误信息并发送这一过程就是抛出异常。
    最初异常信息的抛出只是定义一些变量,这些变量通常是整型值或字符串信息。
    下面的代码是通过整型值创建的异常抛出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;

int main()
{
try {
throw 1;
}
catch( int error ) {
if ( error == 1 )
cout << "产生异常!" << endl;
}

return0;
}

    在C++中,异常的抛出是使用throw关键字来实现的,在这个关键字的后面可以跟随任何类型的值。在上面的代码中将整型值1作为异常信息抛出,当异常捕获时就可以根据该信息进行异常的处理。

    异常的抛出还可以使用字符串作为异常信息进行发送,程序代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

int main()
{
try {
throw "异常产生!";
}
catch ( string *error ) {
cout << error << endl;
}

return 0;
}

    可以看到,字符串形式的异常信息适合于异常信息的显示,但并不适合于异常信息的处理,那么是否可以将整形信息与字符串信息结合起来作为异常信息进行抛出呢?之前说过,throw关键字后面跟随的是类型值,所以不但可以跟随基本数据类型的值,还可以跟随类类型的值,之间可以通过类的构造函数将整型值与字符串结合在一起,并且还可以同时应用更加灵活的功能。

    例如,将错误ID和错误信息以类对象的形式进行异常抛出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
using namespace std;

class CCustomError {
private:
int m_error_ID;
string m_Error;

public:
CCustomError() {
m_errorID = 1;
M_Error = "出现异常!";
}

int GetErrorID() {
return m_ErrorID;
}

string *GetError() {
return m_Error;
}
}

int main()
{
try {
throw ( new CCustomError() );
}
catch ( CCustomError *error ) {
cout << "异常ID:" << error->GetErrorID() << endl;
cout << "异常信息:" <<error->GetError() << endl;
}

return 0;
}

    程序中定义了一个异常类,这个类包含了两个内容 ,一个是异常ID,也就是异常信息的编号,另一个是异常信息,也就是异常的说明文本。通过throw关键字抛出异常时,需要指定这两个参数。

捕捉异常

    异常捕获是指当一个异常被抛出时,不一定就在异常抛出的位置来处理这个异常,而是可以在别的地方通过捕获这个异常信息后再进行处理。这样不仅增加了程序结构的灵活性,也提高了异常处理的方便性。

    如果在函数内抛出一个异常(或在函数调用时抛出一个异常),将在异常抛出时退出函数。如果不想在异常抛出时退出函数,可在函数内创建一个特殊块用于解决实际程序中的问题。这个特殊块try由关键字组成,例如:

1
2
3
try {
//抛出异常
}

    异常抛出信息发出后,一旦被异常处理器接收到就被销毁。异常处理器应具备接受任何异常的能。异常处理器紧随try块之后,处理的方法由catch块引导。

1
2
3
4
5
6
try {
//...
}
catch {type obj} {
//...
}

    异常处理部分必须直接放在测试块之后。如果一个异常信号被抛出,异常处理器中第一个参数与异常抛出对象相匹配的函数将捕获该异常信号,然后进入相应的catch语句,执行异常处理程序。catchswitch块语句同,它不需要在每个catch语句后加入break去中断后面程序的执行。
    下面通过trycatch语句来捕获一个异常,程序代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

Class CcustomError {
private:
int m_ErrorID;
string m_Error;

public:
CcustomError() {
m_ErrorID = 1;
m_Error = "出现异常!";
}
int GetErrorID() {
return m_ErrorID;
}
string *GetError() {
return m_Error;
}
}

int main()
{
try {
throw(new CCustomError());
}
catch(CCustomError *error) {
//输出异常信息
cout << "异常ID:" << error->GetErrorID() << endl;
cout << "异常信息:" << eoor->GetError() << endl;
}
return 0;
}

    在上面的代码中可以看到try语句块中用于捕获throw所抛出的异常。对于throw异常的抛出,可以直接写在try语句块的内部,也可以写在函数或类方法的内部。但函数和方法必须写在try语句块的内部,才可以捕获到异常。

    异常处理器可以成组的出现,同时根据try语句块获取的异常信息处理不同的异常。
    获取不同异常的trycatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
try
{
throw "字符串异常!";
}
catch (CCustomError *error)
{
//输出异常信息
cout << "异常ID:" << error->GetErrorID() << endl;
cout << "异常信息:" << error->GetError() << endl;
}
catch (string *error)
{
cout << "异常信息:" << error << endl;
}

return 0;
}

    有时不一定在列出的异常处理中包含所有可能发生的异常类型,所以c++提供了可以处理任何异常类型的方法,就是在catch后面的括号内添加...,程序代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
try
{
throw "字符串异常!";
//throw(new CCustomError()); //抛出异常
}
catch (string *error)
{
cout << "异常信息:" << error << endl;
}
catch(...)
{
cout << "未知异常信息!" << endl;
}
return 0;
}

    有时需要重新抛出刚接收到的异常,尤其是在程序无法得到有关异常的信息,而用省略号捕获任意的异常时。这些工作通过不断加入不带参数的throw就可完成。

1
2
3
4
5
catch(...) 
{
cout << "未知异常!" << endl;
throw;
}

    如果一个catch语句忽略了异常,那么这个异常将进入更高层的异常处理环境。由于每个异常抛出的对象是被保留的,因此更高层的异常处理器可抛出来自这个对象的所有信息。

异常匹配

    当程序中有异常抛出时,异常处理系统会根据异常处理器的顺序找到最近的异常处理块,并不会搜索更多的异常处理块。

    异常匹配并不要求与异常处理器进行完美匹配,一个对象或一个派生类对象的引用将与基类处理器进行匹配。若抛出的是类对象的指针,则指针会匹配相对应的对象类型,但不会自动转换成其他对象的类型,示例如下:

1

    

    

    

标准异常

    

使用宏定义替换复杂的数据

    

不带参数的宏定义

    

带参数的宏定义

    

小结

    本章主要介绍了两种构造类型:结构体和共用体,自定义类型及枚举类型,使用C++语言开发的程序一般都大量使用结构体,在C++中更是增加了结构体的功能,程序设计阶段应多将关联紧密的数据组合成一个大结构体,便于阅读及二次开发。

实践与练习

  • 练习题1

    输入学生期中成绩、期末成绩、平时考核成绩,按照30%、50%、20%计算学生的综合成绩。

  • 练习题2

    设计一个选票程序。假设有3个候选人,手续输入要选择的候选人的候选人姓名,然后输出每个人的得票结果。

评论

君既已浏览至此,何不留一句?