1

令人迷惑的写法 一

  • 下面的程序想要表达什么意思呢?
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 部分
  • 异常声明能够提高程序的可读性

以上内容参考狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧