C++核心知识回顾(函数&参数、异常、动态分配)

2023-05-29,,

复习C++的核心知识

函数与参数

传值参数、模板函数、引用参数、常量引用参数

传值参数

int abc(int a,int b,int c) {
return a + b * c;
}

a、b、c是函数abc的形参,下面语句中调用函数abc:

z = abc(2,x,y);

2、x、y就是分别于a b c对应的实参,形参a、b、c实际上是传值参数,在运行时,函数abc执行前,将实参复制给形参,复制过由形参类型的复制构造函数来完成,如果类型不同会进行转换。当函数运行结束后,形参类型的析构函数负责释放形式参数

模板函数

将上述abc函数改写的更具有通用性,参数类型是一个变量,其值由编译器决定,即使用模板语句

template<class T>
T abc(T a,T b,T c) {
return a + b * c;
}

引用参数

使用形参会增加程序运行的时间,上述函数中,当a、b、c是传值参数时,一进入函数调用,类型T的复制构造函数就会启用,返回时,析构函数就会启用,如果T是比较复杂的类型,复制构造函数和析构函数可能带来较大的开销。使用引用参数,便不会调用复制构造函数和析构函数

template<class T>
T abc(T &a,T &b,T &c) {
return a + b * c;
}

常量引用参数

常量引用下指明的引用参数不能被函数修改

template<class T>
T abc(const T &a,const T &b,const T &c) {
return a + b * c;
}

使用const来指明函数不可修改的引用参数

综上可以得到更通用的写法:

template<class Ta,class Tb,class Tc>
Ta abc(const Ta &a,const Tb &b,const Tc &c) {
return a + b * c;
}

返回值

一个函数可以返回一个值、一个引用或一个常量引用,在上述例子中,返回的对象被复制到调用环境中,函数所计算出来的表达式结果被存储在一个局部的临时变量中,当函数结束时,当函数结束时,这个临时变量所占用的空间将被释放,为了不丢失这个值,在释放空间之前,要把这个值从临时变量复制到调用该函数的环境中去。但如果给函数返回类型增加一个后缀&,便指定了一个引用返回:

T& mystery(int i,T &z)

使用语句return z返回,这种形式不会将z的值复制到返回环境中,当函数结束时,形参i以及所有的局部变量的空间都被释放掉,z是对一个实参的引用,所以不受影响

重载函数

一个函数的签名,是由这个函数的形参类型及其个数确定的。定义多个同名函数的机制叫函数重载,将函数调用语句中的签名与函数定义中的签名进行匹配,编译器就可以确定哪一个重载函数被调用

float abc(float a,float b,float c) {
return a * b + c;
} int abc(int a,int b,int c) {
return a * b + c;
}

异常

异常是表示程序出现错误的信息

抛出异常

可以对一些异常情况进行检查,当查出一个异常就抛出,例如下方程序,当三个参数都大于0时才计算表达式的值,其他情况都抛出异常

int abc(int a,int b,int c) {
if (a <= 0 || b <= 0 || c <= 0) {
throw "All parameters should be > 0";
}
return a + b * c;
}

程序可能抛出的异常有很多类型,C++具有一个异常类的层次结构,类exception是根,标准C++函数通过抛出一种异常来表明异常的出现,而这种异常是从基类exception派生的类型

处理异常

一段代码抛出的异常由包含这段代码的try语句块来处理

int abc(int a,int b,int c) {
if (a <= 0 || b <= 0 || c <= 0) {
throw "All parameters should be > 0";
}
return a + b * c;
} int main(void) {
try {
cout << abc(2,0,4) << endl;
} catch (char const* e) {
cout << "The parameters to abc were 2,0 and 4" << endl;
cout << "An exception has been thrown" << endl;
cout << e << endl;
} return 0;
}

紧跟在try块之后的catch块,每一个catch块都有一个参数,参数的类型决定了这个catch块要捕获的异常类

输出:

The parameters to abc were 2,0 and 4
An exception has been thrown
All parameters should be > 0

可以有多个catch块,如果有一个异常被抛出,那么try块的正常运行停止,程序进入第一个能捕获到这种异常类型的catch块,其他catch块将被忽略,如果没有一个catch块能够与抛出的异常类型相对应,那么异常就会跨越嵌入在try块里的层次结构,寻找在层次结构中能够处理这个异常的第一个catch块,如果依然没有,那么程序非正常停止

动态存储空间分配

操作符new

new用于进行动态存储分配或运行时存储分配,其值是一个指针,指向所分配空间,例如给一个整数动态分配存储空间,并复制为10:

int *y = new int(10);

等价于:

int *y = new int;
*y = 10;

操作符new分配了一块能够存储一个整数的空间,并将该空间的指针赋给y,y是对整数指针的引用,而*y是对整数本身的引用

一维数组

动态存储分配的方式创建一维数组,如下创建一个长度为n的一维浮点数组

float *x = new float[n];

操作符new为n个浮点数分配了存储空间,并返回第一个浮点数空间的指针

异常处理

如果计算机没有足够的内存可以分配,那么new就会抛出一个bad_alloc类型的异常,可以利用try-catch来捕获:

int main(void) {
int n = 10;
float *x;
try {
x = new float[n];
} catch (bad_alloc e) {
cerr << "Out of Memory" << endl;
::exit(1);
} return 0;
}

二维数组

使用动态分配创建一个二维数组

template <class T>
void make2dArray(T** &x,int numberOfRows,int numberOfColumns) {
x = new T*[numberOfColumns];
for (int i=0;i<numberOfRows;i++) {
x[i] = new int [numberOfColumns];
}
}

如果数组的列数在编译时是未知的,那么不可能仅调用一次new就能创建这个二维数组,要构造这样的二维数组,可以将其看作是油若干行所构成的结构,每一行用new来创建一个一维数组

释放内存空间:

template<class T>
void delete2dArray(T** &x,int numberOfRows) {
for (int i=0;i<numberOfRows;i++) {
delete[] x[i];
}
delete[] x;
x = NULL;
}

分两步释放二维数组空间,首先释放为每一行所分配的空间,然后释放为行指针所分配的空间,将x置NULL,防止用户继续访问已被释放的空间

C++核心知识回顾(函数&参数、异常、动态分配)的相关教程结束。

《C++核心知识回顾(函数&参数、异常、动态分配).doc》

下载本文的Word格式文档,以方便收藏与打印。