令人迷惑的写法 一
- 下面的程序想要表达什么意思呢?
template< class T >
class Test
{
public:
Test(T t) { }
};
template < class T >
void func(T a[], int len)
{
}
编程实验: class 模板初探
#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()
{
Test<string> ts("D.T.Software");
string ta[] = {"D", ".", "T", "."};
func(ta, 4);
Test<int> ti(100);
int ai[] = {1, 2, 3, 4};
func(ai, 4);
return 0;
}
输出:【编译无错误,无警告】
t = D.T.Software
D
.
T
.
t = 100
1
2
3
4
分析:
class 定义的类模板即可以适用于类类型,也可以适用于基础类型(等同于 typename)
历史上的原因 。。。
- 早期的 C++ 直接复用 class 关键字来定义模板
- 但是泛型编程针对的不只是类类型
- class 关键字的复用使得代码出现二义性
typename 诞生的直接原因
- 自定义类类型内部的嵌套类型
- 不同类中的同一个标识符可能导致二义性
- 编译器无法辨识标识符究竟是什么
编程实验: 模板中的二义性
#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>(); // Error
return 0;
}
输出:
test.cpp: In function ‘void test_class() [with T = Test_2]’:
test.cpp:34: instantiated from here
test.cpp:27: error: dependent-name ‘T::TS’ is parsed as a non-type, but instantiation yields a type
test.cpp:27: note: say ‘typename T::TS’ if a type is meant
结论:
T::TS * a; ==> 编译器默认解析 TS 为成员变量
#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()
{
typename T::TS * a; // 解读方式 1 : 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (倾向的解读方式)
// 解读方式 2 : 通过泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作
}
int main()
{
test_class<Test_1>(); // Error
test_class<Test_2>();
return 0;
}
输出:
test.cpp: In function ‘void test_class() [with T = Test_1]’:
test.cpp:33: instantiated from here
test.cpp:27: error: no type named ‘TS’ in ‘class Test_1’
结论:
typename T::TS * a; ==> 明确告诉编译器 typename 后为泛指类型
typename 的作用
- 在模板定义中声明泛指类型
- 明确告诉编译器其后的标识符为类型
令人迷惑的写法 二
下面的程序想要表达什么意思呢?
int func(int i) try
{
return i;
}
catch(...)
{
cout << "Eception..." << endl;
}
int func(int i, int j) throw(int, char)
{
return i + j;
}
- try ... catch 用于分隔正常功能代码与异常功能代码
- try ... catch 可以直接将函数分割为 2 部分
- 函数声明和定义时可以直接指定可能抛出的异常类型
- 异常声明成为函数的一部分可以提高代码可读性
函数异常声明的注意事项
- 函数异常声明是一种与编译器之间的锲约
函数声明异常后就只能抛出声明的异常
- 抛出其它类型异常将导致程序运行终止
- 可以直接通过异常声明定义无异常函数
编程实验: 新的异常写法
#include <iostream>
#include <string>
using namespace std;
int func(int i, int j) throw(int, char)
{
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(i, i) = 10
Exception...
小结
- class 可以用来在模板中定义泛指类型(不推荐)
- typename 可以消除模板中的二义性
- try... catch 可以将函数体分成 2 部分
- 异常声明能够提高程序的可读性
以上内容参考狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。