令人迷惑的寫法 一
- 下面的程式想要表達什麼意思呢?
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 部分
- 異常宣告能夠提高程式的可讀性
以上內容參考狄泰軟體學院系列課程,請大家保護原創!