1 / 97

樣版類別-向量和矩陣的定義

23. 樣版類別-向量和矩陣的定義. 向量和矩陣是線性代數和數值分析的基礎。藉由這兩個類別,我們介紹如何建立樣版類別 (template class) 。此外,我們還要在本章中討論動態建立和移除物件的語法。. 樣版類別-向量和矩陣的定義. 23.1  向量 23.2 Vector 樣版類別 23.3  矩陣 23.4 Matrix 樣版類別 23.5  物件陣列的動態創造和刪除 23.6  常犯的錯誤. 23.1 向量. 把 向量 (vector) 定義成物件 的好處 : 1. 將向量視為獨立的單元。

remedy
Download Presentation

樣版類別-向量和矩陣的定義

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 23 樣版類別-向量和矩陣的定義 向量和矩陣是線性代數和數值分析的基礎。藉由這兩個類別,我們介紹如何建立樣版類別 (template class)。此外,我們還要在本章中討論動態建立和移除物件的語法。

  2. 樣版類別-向量和矩陣的定義 • 23.1 向量 • 23.2Vector樣版類別 • 23.3 矩陣 • 23.4Matrix樣版類別 • 23.5 物件陣列的動態創造和刪除 • 23.6 常犯的錯誤

  3. 23.1 向量 把向量(vector)定義成物件的好處: 1. 將向量視為獨立的單元。 2.透過運算子重載,將向量運算以最精簡、最接近數學式的方式表達。 例如,向量間的加法和內積可以分別簡潔地寫成「V1 + V2」和「V1 * V2」。

  4. 使用指標定義向量(1/2) • 考慮線性代數一般的使用習慣,可以犧牲第一個元素不用,而將記憶空間安排為N+1個元素: int N = 5; float* V = new float [N+1]; 圖示如下:

  5. 使用指標定義向量(2/2) • 可以使用「delete」回收不再使用的記憶體 資源。 • delete [] V;

  6. 23.2 Vector樣版類別 • 定義樣版類別 (class template)。以下是一個 簡化的例子: template <class T> //「T」代表一個尚未決定的資料型態 class Vector { private: int Size; T* V; public: Vector(int); // 建構函數不使用Vector <T> Vector(Vector<T>&); // 類別名稱以Vector <T> 表示 ~Vector(); // 解構函數不使用Vector <T> };

  7. 定義樣版類別 (一個以上的未定類別) • 定義樣版類別 (一個以上的未定類別) template <class T1, class T2> class Vector { // ... 其他敘述 };

  8. 以樣版類別Vector定義物件 • 分別用來宣告一個整數向量Vi和一個浮點數向量Vd: Vector <int> Vi; Vector <double> Vd;

  9. 成員函數的定義 在實作樣版類別的成員函數時,必需在每個成員函數的定義前都加上「template <class T>」的宣告。 例如,Vector <T> 的建構函數可以寫成: template <class T> Vector<T>::Vector(int N) { Size = N; V= new T[N+1]; return; }

  10. Vector <T> 的解構函數 template <class T> Vector<T>::~Vector() { delete [] V; }

  11. 加法operator+() 的重載成員函數 template <class T> Vector<T> Vector<T>::operator + (const Vector<T>& V2) { Vector<T> Temp(Size); for (int i=1; i<= Size; i++) Temp.V[i]= V[i]+V2.V[i]; return Temp; }

  12. 完整的Vector樣版類別 樣版類別Vector的完整程式寫成檔案Vector.h。 這個樣版類別的定義中包括了:建構函數 (constructor)複製建構函數 (copy constructor)解構函數 (destructor)指派運算子 (assignment operator, =)「+」、「-」、「*」、「/」等四個運算子 的重載。

  13. 運算子「*」 在程式Vector.h中,運算子「*」被重載了三次: (1) 純量乘以向量。 例如 V1 = 2.5 * V2; (2) 向量乘以純量。 例如 V1 = V2 * 2.5; (3) 向量的內積。 例如 double x = V1 * V2; 其中 (1)「純量乘以向量」只能以friend函數寫成,因為運算子的左側運算元不是Vector物件。

  14. 專門用來發出檢查訊息的函數 (1/2) 例如,做向量加法前,要檢查兩個向量的長度是否相同。 inline void Check(bool ErrorCondition, const string& Message = "Requirement failed") { if (ErrorCondition) { cerr << Message << endl; exit(1); } }

  15. 專門用來發出檢查訊息的函數 (2/2) 將判斷式和錯誤訊息以參數的方式傳遞 // 定義加法運算子 operator + () template <class T> Vector<T> Vector<T>::operator+(const Vector<T>& V2) { Check(Size!=V2.Size, "加法運算錯誤, 長度不相同!"); Vector<T> Temp(Size); for (int i=1; i<= Size; i++) Temp.V[i]= V[i]+V2.V[i]; return Temp; }

  16. 配置新記憶空間的成員函數 • 建構函數,複製建構函數,和指派運算子「=」 都有配置新記憶空間的動作。因此,我們將這個動作 集中寫成inline 函數Create(): template <class T> void Vector<T>::Create(int N) { if(N<1) {Size = 0; V = 0;} else {Size = N; V= new T[N+1];} }

  17. 配置新記憶空間 • 例如,複製建構函數的定義可以比較簡潔地寫成: template <class T> Vector<T>::Vector(Vector<T>& OldV) { Create(OldV.Size); for (int i=1; i<= Size; i++) V[i]= OldV.V[i]; return; }

  18. 指派運算子operator = () • 指派運算子必需重新定義,因為成員內有指標,而且等號左右兩邊的向量不一定一樣長。指派運算子的定義如下: template <class T> Vector<T> Vector<T>::operator=(const Vector<T>& V2) { if (Size!=V2.Size) Create(V2.Size); for (int i=1; i<= Size; i++) V[i] = V2.V[i]; return *this; }

  19. 重載運算子 [] (1/2) • 我們將operator[]() 的返回資料型態設定為參照。如此一來,它才可以是左值lvalue,也可以是右值rvalue,例如: V[2] = 4.8; float x = V[6];

  20. 重載運算子 [] (2/2) • operator []() 的inline成員函數定義如下所示: T& operator[](int i) { Check(i>Size, "索引錯誤, 超過邊界!"); return V[i]; }

  21. 專門用來顯示向量的函數Display() template <class T> void Vector<T>::Display() { for(int i = 1; i <= Size; i++) cout << setiosflags(ios::right) << setiosflags(ios::fixed) << setiosflags(ios::showpoint) << setprecision(4) << setw(12) << V[i]; cout << endl; return; }

  22. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(1/18) // Vector.h #ifndef Vector_H #define Vector_H #include <iostream> #include <iomanip> using namespace std; inline void Check(bool ErrorCondition, const string& Message = "Requirement failed")

  23. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(2/18) { if (ErrorCondition) { cerr << Message << endl; exit(1); } } // =========== Vector 樣版類別 ================================= template <class T> class Vector { // 宣告乘法運算子-1 operator * () friend Vector<T> operator*(const T& , const Vector<T>& ); // 宣告乘法運算子-2 operator * ()

  24. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(3/18) friend Vector<T> operator*(const Vector<T>& , const T& ); private: int Size; T* V; inline void Create(int); public: // 宣告建構函數 Vector(int); Vector(int, const T*);

  25. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(4/18) // 宣告複製建構函數 Vector(Vector<T>&); // 宣告解構函數 ~Vector(); // Operators // 宣告指派運算子 operator = () Vector<T> operator=(const Vector<T>&); // 宣告加法運算子 operator + () Vector<T> operator+(const Vector<T>&); // 宣告減法運算子 operator - ()

  26. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(5/18) Vector<T> operator-(const Vector<T>&); // 宣告乘法運算子-3 operator * () T operator*(const Vector<T>&); // 宣告除法運算子 operator / () Vector<T> operator/(const T&); // 宣告索引運算子 operator []() T& operator[](int i)

  27. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(6/18) { Check(i>Size, "索引錯誤, 超過邊界!"); return V[i]; } // 宣告成員函數 Display() void Display(); };

  28. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(7/18) // 函數 Create() 的定義 template <class T> void Vector<T>::Create(int N) { if(N<1) {Size = 0; V = 0;} else {Size = N; V= new T[N+1];} }

  29. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(8/18) // 建構函數的定義 template <class T> Vector<T>::Vector(int N) { Create(N); return; } // 解構函數的定義 template <class T> Vector<T>::~Vector() { delete [] V; }

  30. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(9/18) // 複製建構函數的定義-1 template <class T> Vector<T>::Vector(int N, const T* OldV) { Create(N); for (int i=1; i<= Size; i++) V[i]= OldV[i-1]; return; }

  31. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(10/18) // 複製建構函數的定義-2 template <class T> Vector<T>::Vector(Vector<T>& OldV) { Create(OldV.Size); for (int i=1; i<= Size; i++) V[i]= OldV.V[i]; return; }

  32. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(11/18) // 定義指派運算子 operator = () template <class T> Vector<T> Vector<T>::operator=(const Vector<T>& V2) { if (Size!=V2.Size) Create(V2.Size); for (int i=1; i<= Size; i++) V[i] = V2.V[i]; return *this; }

  33. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(12/18) // 定義加法運算子 operator + () template <class T> Vector<T> Vector<T>::operator+(const Vector<T>& V2) { Check(Size!=V2.Size,"加法運算錯誤, 長度不相同!"); Vector<T> Temp(Size); for (int i=1; i<= Size; i++) Temp.V[i]= V[i]+V2.V[i]; return Temp; }

  34. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(13/18) // 定義減法運算子 operator - () template <class T> Vector<T> Vector<T>::operator-(const Vector<T>& V2) { Check(Size!=V2.Size,"減法運算錯誤, 長度不相同!"); Vector<T> Temp(Size); for (int i=1; i<= Size; i++) Temp.V[i]= V[i]-V2.V[i]; return Temp; }

  35. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(14/18) // 定義乘法運算子-1 operator * () template <class T> Vector<T> operator*(const T& f, const Vector<T>& V1) { Vector<T> Temp(V1.Size); for(int i=1; i<=V1.Size; i++) Temp.V[i] = f * V1.V[i]; return Temp; }

  36. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(15/18) // 定義乘法運算子-2 operator * () template <class T> Vector<T> operator*(const Vector<T>& V1, const T& f) { Vector<T> Temp(V1.Size); for(int i=1; i<=V1.Size; i++) Temp.V[i] = f * V1.V[i]; return Temp; }

  37. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(16/18) // 定義乘法運算子-3 operator * () template <class T> T Vector<T>::operator*(const Vector<T>& V2) { Check(Size!=V2.Size, "內積運算錯誤, 長度不相同!"); T Product = 0; for (int i=1; i<= Size; i++) Product += V[i]*V2.V[i]; return Product; }

  38. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(17/18) // 定義成員函數 Display() template <class T> void Vector<T>::Display() { for(int i = 1; i <= Size; i++) cout << setiosflags(ios::right) << setiosflags(ios::fixed) << setiosflags(ios::showpoint) << setprecision(4) << setw(12) << V[i]; cout << endl; return; }

  39. 範例程式檔案 Vector.h 定義樣版類別Vector的完整程式(18/18) // 定義除法運算子 operator / () template <class T> Vector<T> Vector<T>::operator/(const T& f) { Vector<T> Temp(Size); for (int i=1; i<= Size; i++) Temp.V[i]= V[i]/f; return Temp; } #endif

  40. 範例程式檔案TestVector.cpp(1/6) • 分別使用Vector<int>和Vector<double>去宣告包含int和double兩種資料型態元素的向量:DV1、DV2、IV1和IV2,並驗證向量除以浮點數、向量間加法和向量內積的正確性。我們也藉由DVb[1]、DVb[2] 和DVb[3] 驗證了索引運算子[]。

  41. 範例程式檔案TestVector.cpp(2/6) // TestVector.cpp #include "Vector.h" int main () { double Ddata1[]={2, 0.5, 4.6}; double Ddata2[]={4, 6.5, 3.8}; int Idata1[]={2, 5, 6}; int Idata2[]={4, 65, 38};

  42. 範例程式檔案TestVector.cpp(3/6) Vector<double> DV1(3, Ddata1); Vector<double> DV2(3, Ddata2), DVa(3), DVb(3); Vector<int> IV1(3, Idata1); Vector<int> IV2(3, Idata2), IVa(3), IVb(3); int Idot; double Ddot; cout << "\n---------------------" << endl; cout << "測試 Vector<double>: " << endl; cout << " DV1 的值是: " << endl;

  43. 範例程式檔案TestVector.cpp(4/6) DV1.Display(); cout << " DV2 的值是: " << endl; DV2.Display(); DVa=DV1+DV2; cout << " (DV1 + DV2) 的值是:" << endl; DVa.Display(); DVb=DV1/2.0; cout << " (DV1/2.0) 的值是: " << endl;

  44. 範例程式檔案TestVector.cpp(5/6) cout << " " << DVb[1] << " " << DVb[2] << " " << DVb[3] << endl; Ddot = DV1*DV2; cout << " DV1 和 DV2 的內積是 : " << Ddot << endl; cout << "\n-------------------- " << endl; cout << "測試 Vector<int>: " << endl; cout << " IV1 的值是: " << endl;

  45. 範例程式檔案TestVector.cpp(6/6) IV1.Display(); cout << " IV2 的值是: " << endl; IV2.Display(); IVa=IV1+IV2; cout << " (IV1 + IV2)的值是:" << endl; IVa.Display(); IVb=IV1/2.0; cout << " (IV1/2.0) 的值是:" << endl; IVb.Display(); Idot = IV1*IV2; cout << " IV1 和 IV2 的內積是 : "<< Idot << endl; return 0;}

  46. 程式執行結果(1/2) ------------------------------------ 測試 Vector<double>: DV1 的值是: 2.0000 0.5000 4.6000 DV2 的值是: 4.0000 6.5000 3.8000 (DV1 + DV2) 的值是: 6.0000 7.0000 8.4000 (DV1/2.0) 的值是: 1.0000 0.2500 2.3000 DV1 和 DV2 的內積是 : 28.7300 -------------------------------------

  47. 程式執行結果(2/2) 測試 Vector<int>: IV1 的值是: 2 5 6 IV2 的值是: 4 65 38 (IV1 + IV2)的值是: 6 70 44 (IV1/2.0) 的值是: 1 2 3 IV1 和 IV2 的內積是 : 561

  48. 23.3 矩陣 • 二維陣列稱為矩陣(matrix)。 • 向量的外積(outer product)也會產生矩陣。 • 矩陣內的單一行或單一列抽離出來也可形成向量。

  49. 使用指標定義矩陣(1/7) • 以動態配置的方式在執期間設定矩陣語法有兩種: (1) 將各列在記憶體中獨立設定 int M = 3; int N = 4; float** A = new float *[M]; for (int i=0; i<M; i++) A[i] = new float [N];

  50. 使用指標定義矩陣(2/7) • 將各列在記憶體中獨立設定 相當於在記憶中動態配置了圖23.3.1中所示的指標和記憶空間: 圖23.3.1 二維陣列的動態記憶體配置

More Related