矩陣就不用再解釋了,寫成泛型主要是為了幾個方便:
1、方便在棧上分配空間。由于維度在編譯期已知,所以可以做到在棧上分配空間。當然如果這個對象是new出來的,自然是在堆上分配,這里說的是在棧上分配這個對象時,矩陣元素所占用的空間也在棧上分配。
2、方便在編譯期檢查非法的矩陣運算。C++模板的強大推導能力可以在編譯期推導出結果矩陣的維度。
3、泛型類在方法內聯上具有優勢。
這個矩陣類為了能夠直接從數組賦值,使用了一個ArrayPorxy類(可參考《Imperfect C++》)。
代碼如下:
template?
<
class
?T,?
int
?D1,?
int
?D2
=
1
>
class
?ArrayProxy
{
????T
*
?data;
public
:
????ArrayProxy(T?(
&
value)[D1][D2])
????????:?data(
&
value[
0
][
0
])
????{
????}
????ArrayProxy(T?(
&
value)[D1
*
D2])
????????:?data(value)
????{
????}
????T
*
?getData()?
const
????{
????????
return
?data;
????}
};
這個只是簡單的實現。
因為我基本上不使用這個矩陣類,所以只完成幾個簡單功能:
1、從數組賦值:
int a[][3] = {{1,2,3}, {4,5,6}};
Matrix<int, 2, 3> m1(a);
或
int a[] = {1,2,3, 4,5,6};
Matrix<int, 2, 3> m1(a);
Matrix<int, 3, 2> m2(a);
Matrix<int, 6, 1> m3(a);
Matrix<int, 1, 6> m4(a);
2、矩陣乘法:
Matrix<int, 2, 3> m1;
Matrix<int, 2, 4> m2;
// m1 * m2? <== 編譯錯誤,維度不匹配
Matrix<int, 3, 5> m3;
Matrix<int, 2, 5> m4 = m1 * m3; // <== 合法
// m3 * m1; // <== 編譯錯誤,維度不匹配
源碼如下:
template?<class?T,?int?R,?int?C>
class?Matrix
{
????T?matrix[R][C];
public:
????//?Big?three
????Matrix(void)
????{
????????memset(matrix,?0,?sizeof(matrix));
????}
????Matrix(const?Matrix&?rhs)
????{
????????memcpy(matrix,?rhs.matrix,?sizeof(matrix));
????}
????Matrix&?operator?=(const?Matrix&?rhs)
????{
????????memcpy(matrix,?rhs.matrix,?sizeof(matrix));
????????return?*this;
????}
public:
????Matrix(const?ArrayProxy<T,R,C>&?arr)
????{
????????memcpy(matrix,?arr.getData(),?sizeof(matrix));
????}
????~Matrix(void)
????{
????}
public:
????T?get(int?r,?int?c)?const
????{
????????assert(c?<?C?&&?c?>=?0?&&?r?<?R?&&?r?>=?0);
????????return?matrix[r][c];
????}
????void?set(int?r,?int?c,?T?v)
????{
????????assert(c?<?C?&&?c?>=?0?&&?r?<?R?&&?r?>=?0);
????????matrix[r][c]?=?v;
????}
????int?getCols?()?const
????{
????????return?C;
????}
????int?getRows?()?const
????{
????????return?R;
????}
????bool?operator?==?(const?Matrix&?rhs)?const
????{
????????return?memcmp(matrix,?rhs.matrix,?sizeof(matrix))?==?0;
????}
????bool?operator?!=?(const?Matrix&?rhs)?const
????{
????????return?!(*this?==?rhs);
????}
};
template?<class?T,?int?R,?int?C,?int?C1>
Matrix<T,R,C1>?operator*?(const?Matrix<T,R,C>&?lhs,?const?Matrix<T,C,C1>&?rhs)
{
????Matrix<T,R,C1>?result;
????for?(int?r=0;?r<R;?++r)
????{
????????for?(int?c=0;?c<C1;?++c)
????????{
????????????int?value?=?0;
????????????for?(int?i=0;?i<C;?++i)
????????????{
????????????????value?+=?lhs.get(r,i)?*?rhs.get(i,c);
????????????}
????????????result.set(r,c,value);
????????}
????}
????return?result;
} 測試代碼:
int?main()
{
????{
????????//?測試初始化
????????Matrix<int,?3,?4>?m1;
????????Matrix<int,?3,?4>?m2(m1);
????????Matrix<int,?3,?4>?m3?=?m1;
????????Matrix<int,?3,?4>?m4;
????????m4?=?m1;
????????for?(int?i=0;?i<3;?i++)
????????????for?(int?j=0;?j<4;?j++)
????????????{
????????????????assert?(m1.get(i,?j)?==?0);
????????????????assert?(m2.get(i,?j)?==?0);
????????????????assert?(m3.get(i,?j)?==?0);
????????????????assert?(m4.get(i,?j)?==?0);
????????????}
????????int?a[]?=?{1,2,3,4,?5,6,7,8,?9,10,11,12};
????????Matrix<int,?3,?4>?m5(a);
????????int?b[3][4]?=?{?{1,2,3,4},
????????????????????????{5,6,7,8},
????????????????????????{9,10,11,12}};
????????Matrix<int,?3,?4>?m6(b);
????????Matrix<int,?3,?4>?m7(m5);
????????Matrix<int,?3,?4>?m8?=?m5;
????????Matrix<int,?3,?4>?m9;
????????m9?=?m5;
????????for?(int?i=0;?i<3;?i++)
????????????for?(int?j=0;?j<4;?j++)
????????????{
????????????????assert?(m5.get(i,?j)?==?i*4+j+1);
????????????????assert?(m6.get(i,?j)?==?i*4+j+1);
????????????????assert?(m7.get(i,?j)?==?i*4+j+1);
????????????????assert?(m8.get(i,?j)?==?i*4+j+1);
????????????????assert?(m9.get(i,?j)?==?i*4+j+1);
????????????}
????????//?維數不匹配,編譯錯誤
????????//?Matrix<int,?4,?5>?m10?=?m9;
????????int?c[][2]?=?{{1,2},?{2,3}};
????????//?數組大小不匹配,編譯錯誤
????????//Matrix<int,?3,?4>?m10(c);
????????int?d[]?=?{1,2};
????????//?數組大小不匹配,編譯錯誤
????????//Matrix<int,?3,?4>?m11(d);
????????//?乘法維數不合適,無法相乘
????????//m1?*?m2;
????????Matrix<int,4,3>?m12;
????????//?匹配,可以相乘
????????Matrix<int,?3,?3>?m13?=?m1?*?m12;
????????Matrix<int,?8,?3>?m14;
????????//?無法相乘
????????//Matrix<int,?3,?3>?m15?=?m1?*?m14;
????????//?可以相乘
????????Matrix<int,?8,?4>?m15?=?m14?*?m1;
????}
????{
????????//?檢查點乘
????????int?a[2][5]?=?{{1,2,3,4,5},?{6,7,8,9,10}};
????????Matrix<int,?2,?5>?m1(a);
????????int?b[5][3]?=?{{1,2,3},?{4,5,6},?{7,8,9},?{10,11,12},?{13,14,15}};
????????Matrix<int,?5,?3>?m2(b);
????????int?c[2][3]?=?{{135,150,165},?{310,350,390}};
????????Matrix<int,?2,?3>?m3(c);
????????Matrix<int,?2,?3>?m4?=?m1?*?m2;
????????assert(m4?==?m3);
????????cout?<<?m4.get(0,0)?<<?endl;
????}
????return?0;
}
補充:
1、加法、減法只需要2個矩陣維度相同即可。
template?<class?T,?class?R,?class?C>
Matrix<T,R,C>?operator+?(const?Matrix<T,R,C>&?lhs,?const?Matrix<T,R,C>&?rhs)
{
???//?
} 2、由于1x1的矩陣可以看成一個標量,矩陣與標量運算結果維數與原矩陣相同,可以重載來實現。
template?<class?T,?class?R,?class?C>
Matrix<T,R,C>?operator*?(const?Matrix<T,R,C>&?lhs,?const?Matrix<T,1,1>&?rhs)
{
????//?
} 3、由于類型泛化,可能某些合理的運算無法進行,比如float型矩陣,與一個int型標量運算等。這些最好是借助類型萃取等手段,推導出運算以后的類型。(c++0x中包含自動獲取運算結果類型的關鍵字typeof,等幾年就可以用了:)。GCC編譯器中已有實現,不過似乎有BUG)。
4、其它。泛型實現可能會有一些考慮不周的地方,強類型有強類型的好處,不過必須要有完整的泛型算法支撐,否則難以使用。也可以把泛型矩陣類從一個普通矩陣類派生,這樣更容易寫出通用算法,不過在實現上可能要借助于運行期多態,對于矩陣類來說并不合適。
5、其它。。前面說C++的模板相當強大,D語言模板到目前為止似乎已經完全實現了C++模板的功能,還增加了一些比如字符串值參模板等特性,比C++模板功能更多。在代碼編寫上,由于可以編寫靜態判斷語句(編譯期)以及靜態斷言,編寫模板比C++更容易。有時間可以試試用它寫個矩陣類,純粹是興趣,這些東西真的很難用到,現成的庫也挺多。
6、其它。。。c++0x要提供“template typedef”,也就是可以這樣定義:
template <int R, int C> typedef Matrix<int, R, C> MatrixInt;? // 定義類型,維度不定
template <class T> typedef Matrix<T, 4, 4> Matrix4x4; // 定義維度,類型不定
由此可以出定義行向量、列向量、標量等,當然實際使用起來可能沒那么舒服了。