欲架構一 class matrix ,達成 [][] 之複載。本文之原始碼省略了所有錯誤之截取 (如 idx 逾界 )。
不保證所提是最好的解決方案,只是筆者所想到的一些解決方案。
在 C++ FAQ 裡曾提到,若要做這事,不建議以足標運算子做,而建議改用 ( ) 括號運算子完成。理由是足標運算子只能塞一個 idx,而括號運算子可以塞很多 idx,以 C++ FAQ 之做法,大致如下。
template<typename T> class Matrix{ private: T **m_arr; size_t m_row, m_col; public: // Constructor Matrix (const size_t row, const size_t col) { m_row = row, m_col = col; m_arr = new T* [m_row]; for(size_t i=0 ; i<row; ++i) m_arr[i] = new T[col]; } // operator(), for write T & operator()(const size_t x, const size_t y) { return m_arr[x][y]; } // operator(), for read const T& operator()(const size_t x, const size_t y ) const { return m_arr[x][y]; } // Destructor ~Matrix() { for(size_t i=0; i<m_row; ++i) delete[] m_arr[i]; delete [] m_arr; } };
在數值分析上較常以一維陣列示之
template<typename T> class Matrix{ private: T *m_arr; size_t m_row, m_col; public: // Constructor Matrix (const size_t row, const size_t col) { m_row = row, m_col = col; m_arr = new T [m_row * m_col]; } // operator(), for write T & operator()(const size_t x, const size_t y) { return m_arr[x*m_col+y]; } // operator(), for read const T& operator()(const size_t x, const size_t y ) const { return m_arr[x*m_col+y]; } // Destructor ~Matrix() { delete [] m_arr; } };
在調用時使用 mat(1,2) 或 mat(1,2) = val 方式進行讀取或寫入。
要達成二個足標方式,則必須借助另一沒有用到之 class 做為輔助,
如新增一 class Row,此處先以二維為例。
template<typename T> class Row{ private: T* m_arr; size_t m_size; public: Row(const size_t size) : m_size(size){ m_arr = new T [size] ; } Row() { m_arr = NULL;} void resize(const size_t size) { if(m_arr) delete [] m_arr; m_size = size; m_arr = new T[ size ]; } T & operator[] (const size_t y){ return m_arr[y]; } const T & operator[] (const size_t y) const{ return m_arr[y]; } ~Row() {delete [] m_arr;} }; template<typename T> class Matrix{ private: Row<T> * m_arr; size_t m_row, m_col; public: // Constructor Matrix (const size_t row, const size_t col) { m_row = row, m_col = col; m_arr = new Row<T> [row] ; for(size_t i=0; i<row; ++i) m_arr[i].resize(col); } // operator[ ], for write Row<T> & operator[](const size_t x) { return m_arr[x]; } // operator(), for read const Row<T> & operator[](const size_t x) const { return Row<T>(m_arr[x]); } // Destructor ~Matrix() { delete [] m_arr; } };
其概念是先實做一維之 vector , class Row,而 class Matrix 再動態配置 Row。
這種方式 class Row 擁有實際資料之管理能力,Matrix 與 Row 相依性較高。
在數值分析裡面,陣列常以一維方式表示
< 這點筆者實在好奇為什麼那麼做 ,唯一想到的理由是確保該陣列資料之連續性 >
若以此架構撰之,則 class Row 便只是為定址作用而已。示例碼如下。
template<typename T> class Row{ private: T* m_addr; public: Row(T* addr) : m_addr(addr){} T & operator[] (const size_t y){ return m_addr[y]; } const T & operator[] (const size_t y) const { return m_addr[y]; } }; template<typename T> class Matrix{ private: size_t m_row, m_col, m_size; T* m_arr; public: // Constructor Matrix (const size_t row, const size_t col) { m_row = row , m_col = col; m_arr = new T [ m_size = m_row * m_col]; } Row<T> operator[](const size_t x){ return Row<T>(m_arr + x * m_col); } const Row<T> & operator[](const size_t x) const{ return Row<T>(m_arr + x * m_col); } // Destructor ~Matrix() {delete [] m_arr; } };
通常 class Row 會設成 Matrix 之 friend class,此處沒那麼做。
另線性代數裡最常用到的列交換,對動態之二維陣列而言,只需把指標交換便可,
這也是筆者寫數值分析較為習慣之方式,故又寫了一份二維之方式。
template<typename T> class Row{ private: T* m_addr; public: Row(T* addr) : m_addr(addr){} T & operator[] (const size_t y){ return m_addr[y]; } const T & operator[] (const size_t y) const { return m_addr[y]; } }; template<typename T> class Matrix{ private: size_t m_row, m_col, m_size; T** m_arr; public: // Constructor Matrix (const size_t row, const size_t col) { m_row = row , m_col = col; m_size = row * col; // new space m_arr = new T*[m_row]; T * trunk = new T[m_size]; for(size_t i=0; i<m_row; ++i){ m_arr[i] = trunk; trunk += m_col; } } Row<T> operator[](const size_t x){ return Row<T>(m_arr[x]); } const Row<T> & operator[](const size_t x) const{ return Row<T>(m_arr + x * m_col); } // Destructor ~Matrix() {delete [] *m_arr, delete[] m_arr; } };
雖是二維方式撰之,但實際上 class Row 本身還是在做定址作用而已,
與讓 class Row 實際上掌有實質資料差異頗大。
關於二維之配置、釋放記憶體部份,
上面考量到碎片化問題,若對於該配置策略不熟,可參考
[HFC] Hidden Features of Array Management in C (I) 。
本篇完整之程式碼,於 此網址 裡面之 Overlad Subscript.rar 。
最後讀者可試想一下,三維之陣列可怎麼做才可覆載 [] [] [] 。
留言列表