《C++ Prime Plus》(3)

逻辑操作符的运算优先级

 优先级:NOT操作符>关系运算符>AND逻辑运算符>OR逻辑运算符。

判断字符类型

 C++中通过在头文件中包含cctype可以调用其中函数来判断一个字符变量的类型,返回值为bool型值。可调用函数如下:

switch···case使用注意事项

 在switch···case语句中,case的标签只能是一个值,而且这个值必须是整型(包括char型)而不能是浮点型。

处理错误的cin读入

 例如,申请一个int n,但是却在执行cin >> n时键入了一个字母,此时将会出现意料外的错误输入。对于这种输入,cin对象中的一个错误标记将被激活,对cin方法的调用将返回false。对于这种错误,我们可能有两种处理方式:

 (1)结束输入。只需将cin >> n置于条件判断语句中即可,例如写成while(cin >> n),这样当输入非法类型时,while语句判断条件失效,跳过输入,执行后续语句。

 (2)忽略这个错误,继续输入。为了继续输入,我们需要调用clear()方法清除错误标记,这一方法同时也能清楚EOF标记,调用格式为cin.clear();

cin有两个错误标记,cin.eof()用于检测是否读到文件尾,cin.fail()除了检测文件尾,还能检测类型不匹配的情况。

二维数组相关

 数组作为形参传入函数时,常用形式如下:

1
2
void fun(int arr[], int size); // 一维数组
void fun(int arr[][col], int row); // 二维数组,col为列数,row为行数

 对于二维数组int arr[row][col]以下表达式的不同含义:

1
2
3
4
5
arr // 指向二维数组首行的指针;
arr+row // 指向二维数组第row行的指针;
*(arr+row) // 指向二维数组第row行首个元素的指针;
*(arr+row)+col // 指向二维数组第row行第col个元素的指针;
*(*(arr+row)+col) // 二维数组第row行第col列的元素的值。

函数指针

 函数指针,即用一个指针指向函数来进行相关的操作。例:

1
2
3
4
5
6
7
8
// 函数原型声明
double GDP(Type1 data, Type2 (*pf)(int));
double scheme1(int);
double scheme2(int);

// 函数调用
GDP(data, scheme1);
GDP(data, scheme2);

 上例中,GDP()是一个计算GDP的函数。不同学者对于计算GDP的方式有不同看法,scheme1()scheme2()分别是两个不同的计算方案。问题是,如何在不重新编写GDP计算函数的前提下,通过代入不同的计算方案实现部分改变GDP()函数呢?函数指针就为我们提供了这样一种便捷的方法。在GDP()的参数列表中,第二个参数就是GDP的具体计算方案,它实际上是一个指针函数,pf就是指向这个函数的指针。

 使用中,我们也可以通过用函数名初始化一个指针来实现指针函数,例:

1
2
3
double scheme1(int);
double (*pf)(int);
pf = scheme1; // 此时,double value = (*pf)(123) = pf(123) = scheme1(123);

 在上例中,不要对(*pf)(123) = pf(123)感到奇怪,C++允许像使用函数名一样使用指针名来调用函数,但是切记(*pf)(123) ≠ *pf(123)

内联函数

 声明方法:

1
2
3
inline FuncType fun(···){
···
};

 与普通函数的区别是,主程序在调用普通函数时通过地址跳转来执行被调用函数,这种操作在被调用函数本身执行时间很小的情况下会造成系统资源的浪费——即大部分时间都用在了函数调用与返回的操作上。内联函数则不同,主程序在调用内联函数时,直接将内联函数的内容填充到主程序中,省去了函数调用与返回的操作。

 (1)内联函数不总是优于普通函数。道理很显然,如果函数的执行时间远大于调用返回操作所需时间,则使用内联函数反而会因为其占用更大内存空间的缺点而变得不优越。(2)内联函数的声明并不总是会被编译器接受。当(编译器认为)内联函数过大时,(某些)编译器会拒绝内联函数形式的声明;此外,内联函数不能调用自己,因此递归的内联函数也不会被允许。

 内联函数通常比宏定义函数更为有效。宏定义函数无法实现按值传递,而内联函数从外表上来看与一般函数无异。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define square1(x) x*x
inline int square2(x){ return x*x; }
int c = 1;
square1(c++) => c*c, c++, c++ => 1*1 = 1, c = 3;
square2(c++) => c*c, c++ => 1*1 = 1, c = 2;

# 在函数中使用引用时的注意事项
 避免函数返回一个随函数结束而被释放的内存单元的引用,如:
```cpp
const FuncType & fun(DataType & data){
DataType tmp = data;
return tmp; // tmp为一个引用,它指向的是一个随函数结束而被释放的内存单元,应予以避免。
}

 同样也应该避免返回一个指向临时变量的指针。为避免上述情况,在不得不进行上述操作时,应该使用new为临时变量申请一块额外的内存。这样也会带来新的问题,也即每调用一次函数就会隐藏着用new申请的一块内存,需要在函数外配套使用delete

函数的默认参数

 在声明函数原型时,可以从参数列表自右向左逐个指定形参的默认值。当调用函数时,从最左边填入传入的实参值,对于右边剩下没有填写值的参数,函数将按照形参列表中的默认值代入计算。如:

1
2
3
4
5
// 函数声明
int fun(int a, int b, int c = 0);
// 函数调用
int x = 0, y = 1;
fun(x, y); // 有效的调用,实际参与运算的参数值依次为:0, 1, 0

函数重载(函数多态)

 C++允许函数拥有相同的函数名,前提是他们的函数特征标不同。函数特征标实际上就是函数的参数列表。参数列表中参数类型和数目不同的函数,其函数特征标不同。值得注意的是:

 (1)编译器将类型引用和类型本身视为同一个特征标。

 (2)当存在函数重载时,编译器在匹配函数时并不区分const和非const变量。但仍要注意,将非const变量赋值给const变量时合法的,反之则不合法。

 (3)函数的返回值类型不同并不能构成函数重载,返回值类型可以不同,但是相应的函数特征标也要不同。

函数模板

 函数模板使用通用类型来定义函数,也即在声明和定义函数模板时不比指定该函数的具体类型,而用一个由template关键字定义的通用类型暂时表示。在调用函数模板时,编译器会根据传入参数自动选择具体类型。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
template<class any> 
/* 定义通用类型的格式为
tmeplate<class 通用类型名> // 或
template<typename 通用类型名> // 在本例中,我们起名为any,表示允许传入任何类型
*/
void print(any a, any b){
cout << a << " " << b << endl;
}
int x = 0, y = 1;
char m = 'm', n = 'n';
print(x, y); // [valid]编译器将选择int类型
print(m, n); // [valid]编译器将选择char类型
print(x, m); // [invalid]编译器不允许这种传入方式,传入参数类型必须保持一致

 模板适用于需要多个将同一算法应用并于不同数据类型的函数的场景中。函数模板也可以按照上面的要求进行重载。


转载请注明来源:©Tinshine