今天我们来看看一些在程序中迷惑的写法,下面的程序想要表达什么意思呢?
#include <iostream> #include <string> using namespace std; template < class T > class Test { public: Test(T t) { cout << "t = " << t << endl; } }; template < class T > void func(T a[], int len) { for(int i=0; i<len; i++) { cout << a[i] << endl; } } int main() { int a[5] = {1, 2, 3, 4, 5}; Test<int> t(5); cout << endl; func(a, 5); return 0; }
我们在类模板中用 class 来声明,但是我们在 main 函数中是用 int 类型来使用的,下来我们看看这样的写法在 C++ 编译器中支持吗?
我们看到编译器编译通过并且运行成功。那么我们不是用的 typename 来声明的泛指类型的嘛,这怎么用 class 了呢。这是因为历史上的一些原因,早期的 C++ 直接复用 class 关键字来定义模板,但是泛型编程针对的不只是类类型,class 关键字的复用使得代码出现二义性。下来我们来看看具有二义性的代码
#include <iostream> #include <string> using namespace std; int a = 0; class Test_1 { public: static const int TS = 1; }; class Test_2 { public: struct TS { int value; }; }; template < class T > void test_class() { T::TS * a; // 这便出现了二义性 // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式) // 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作 } int main() { test_class<Test_1>(); test_class<Test_2>(); return 0; }
我们通过在函数模板指定泛指类型为 Test_1,便是第 2 种解读方式,泛指类型为 Test_2 时,便是第 1 种解读方式。那么在这块是不是就出现了二义性呢?我们看看编译结果
我们看到编译器直接报错了,如果是一种类型的话,便让我们在前前面加上 typename,那么我们在模板中加上 typename,看看
编译器又报错了,它说 Test_1 中没有这种类型,肯定没有啊,只有一个成员变量而已。我们注释掉 Test_1 看看
编译通过了。那么 typename 诞生的原因是什么呢?用来自定义类类型内部的嵌套类型,不同类中的同一个标识符可能导致二义性,编译器无法直接辨识标识符究竟是什么。typename 的作用有两个:1、在模板定义中声明泛指类型;2、明确告诉编译器其后的标识符为类型。
那么下面的程序想要表达什么意思呢?
try...catch 用于分隔正常功能代码与异常处理代码,try...catch 可以直接将函数实分隔为两部分。函数声明和定义时可以直接指定可能抛出的异常类型,异常声明成为函数的一部分可以提高代码可读性。那么函数异常声明时有几个注意事项:1、函数异常声明是一种与编译器之间的契约;2、函数声明异常后就只能抛出声明的异常,抛出其他异常将导致程序运行终止,可以直接通过异常声明定义无异常函数。
下来我们还是以代码为例来进行说明
#include <iostream> #include <string> using namespace std; int func(int i, int j) throw(int) { if( (0 < j) && (j < 10) ) { return (i + j); } else { throw '0'; } } void test(int i) try { cout << "func(i, i) = " << func(i, i) << endl; } catch(int i) { cout << "Exception: " << i << endl; } catch(...) { cout << "Exception..." << endl; } int main() { test(5); test(10); return 0; }
我们在 func 函数中异常只声明了 int,而抛出了字符型异常。我们来看看会导致程序运行终止嘛?
确实导致了程序的运行终止了,注意我们的程序中还有 catch(...) 都没能捕捉到 char 类型的异常。那么我们在声明的异常中加上 char 再来看看
程序正常的运行结束了。通过今天对一些迷惑写法的学习,总结如下:1、class 可以用来在模板中定义泛指类型(不推荐);2、typename 是可以消除模板中的二义性;3、try...catch 可以将函数体分成 2 部分;4、异常声明能够提供程序的可读性。
欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083。