close

 

欲架構一 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 。 

最後讀者可試想一下,三維之陣列可怎麼做才可覆載 [] [] [] 。

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 edisonx 的頭像
    edisonx

    Edison.X. Blog

    edisonx 發表在 痞客邦 留言(0) 人氣()