2015-12-28 11 views
6

Ho un membro privato di classe chiamato mat [3] [3], e voglio essere in grado di accedere a questo array 3x3 al di fuori della mia classe (solo leggerlo, non cambiare). È possibile scrivere il metodo accessor che restituisce il puntatore al mio array? Come posso fare questo? Si prega di fornire un esempio di codice.Come scrivere accessor per array 2D (membro privato)

Qui è la mia classe:

class myClass { 
private: 
    int mat[3][3]; 
public: 
    return_value get_mat(void); 
}; 

So che posso usare qualcosa di simile

int get_mat(int i, int j); 

per accedere ogni int all'interno della matrice ad uno ad uno, ma non sarebbe inefficiente chiamare l'accessor per ogni membro dell'array?

+0

La versione 'int i, int j' non sarebbe inefficiente. I compilatori sono bravi a gestire questo genere di cose. –

risposta

9

È possibile scrivere il metodo di accesso che restituisce puntatore al mio array? Come posso fare questo?

Ecco un modo:

#include <iostream> 
#include <algorithm> 
#include <iterator> 

class myClass { 
public: 

    const int* operator[](size_t i) const { 
     return mat[i]; 
    } 

    int* operator[](size_t i) { 
     return mat[i]; 
    } 

    int* get_mat() { 
     return &mat[0][0]; 
    } 

    const int* get_mat() const { 
     return &mat[0][0]; 
    } 

private: 
    int mat[3][3]; 
}; 

int main() 
{ 
    using namespace std; 

    myClass m; 
    m[0][1] = 6; 
    cout << m[0][1] << endl; 

    fill(m.get_mat(), m.get_mat() + 9, 11); 
    copy(m.get_mat(), m.get_mat() + 9, ostream_iterator<int>(cout, ", ")); 
    cout << endl; 

    return 0; 
} 

ma non sarebbe inefficiente per chiamare la funzione di accesso per ogni membro della matrice?

Fortunatamente, no. in una versione build il tuo compilatore ottimizzerà tutto ciò molto meglio di quanto tu possa immaginare.

Esprimi elegantemente il tuo intento. Permetti al compilatore di scrivere codice ottimale per te (lo farà).

risultato atteso:

6 
11, 11, 11, 11, 11, 11, 11, 11, 11, 

Come iniziamo a rimpolpare la classe Matrix, ci vorreste probabilmente per iniziare a costruire in alcune misure di sicurezza contro sovraccarichi del buffer (questo codice richiede probabilmente C++ 14). .. uscita

#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include <functional> 
#include <random> 
#include <cassert> 

template<class T, size_t N> 
struct safe_array_ref 
{ 
    constexpr safe_array_ref(T* data) : _data(data) {} 

    constexpr T& operator[](size_t i) const noexcept { 
     assert(i < N); 
     return _data[i]; 
    } 

    constexpr T* begin() const { 
     return _data; 
    } 

    constexpr T* end() const { 
     return _data + N; 
    } 

    constexpr size_t size() const { 
     return N; 
    } 

private: 
    T* _data; 
}; 

class myClass { 
public: 

    auto operator[](size_t i) const { 
     // provide some degree of safety 
     assert(i < extent_1); 
     return safe_array_ref<const int, extent_2>(mat[i]); 
    } 

    auto operator[](size_t i) { 
     // provide some degree of safety 
     assert(i < extent_1); 
     return safe_array_ref<int, extent_2>(mat[i]); 
    } 

    int* get_mat() { 
     return &mat[0][0]; 
    } 

    const int* get_mat() const { 
     return &mat[0][0]; 
    } 

    const int* begin() const { 
     return get_mat(); 
    } 

    const int* end() const { 
     return get_mat() + total_extent; 
    } 

    int* begin() { 
     return get_mat(); 
    } 

    int* end() { 
     return get_mat() + total_extent; 
    } 

    constexpr size_t size() const { 
     return total_extent; 
    } 


private: 
    int mat[3][3]; 

public: 
    constexpr static size_t extent_1 = std::extent<decltype(mat)>::value; 
    constexpr static size_t extent_2 = std::extent<std::remove_extent_t<decltype(mat)>>::value; 
    constexpr static size_t total_extent = extent_1 * extent_2; 
}; 

int main() 
{ 
    using namespace std; 

    myClass m; 
    m[0][1] = 6; 
    cout << m[0][1] << endl; 

    generate(m.begin(), 
      m.end(), 
      bind(uniform_int_distribution<int>(0,99), 
        default_random_engine(random_device()()))); 

    // copy the whole matrix to stdout 
    copy(m.begin(), 
     m.end(), 
     ostream_iterator<int>(cout, ", ")); 
    cout << endl; 

    // copy one row/column of the matrix to stdout   
    copy(m[1].begin(), 
     m[1].end(), 
     ostream_iterator<int>(cout, ", ")); 
    cout << endl; 


    return 0; 
} 

campione:

6 
76, 6, 39, 68, 40, 77, 28, 28, 76, 
68, 40, 77, 
+0

Penso che sia meglio consentire la sintassi 'M [i] [j]' w/o range checking e 'M.at (i, j)' con il controllo dell'intervallo, simile a 'std :: vector'. Spesso il controllo della portata può essere omesso in sicurezza. Inoltre, una matrice C++ corretta dovrebbe lanciare un 'std :: out_of_range' invece di usare' assert() '- l'errore fuori intervallo non è interno alla classe matrix. – Walter

+0

È una questione di gusti. Preferisco la versione originale. (E penso che l'op []/alla differenza sia stato un errore). Infine, op [] non è "senza controllo di intervallo" - è solo un comportamento indefinito. Colpire un assert è un modo perfettamente ragionevole per implementare un comportamento indefinito. (E se è in linea, l'ottimizzatore rimuoverà la maggior parte dei controlli di intervallo) –

+0

@Walter se si compila con il set NDEBUG (una build di rilascio), l'asser compila a un NOP, quindi in produzione non è controllato a distanza ma almeno si ha una possibilità di trovare il bug durante lo sviluppo. Sono assolutamente d'accordo che la versione '.at()' dovrebbe lanciare un range_error. –

0

È possibile scrivere il metodo accessor che restituisce il puntatore al mio array?

È possibile utilizzare questa brutta sintassi per restituire riferimento alla vostra matrice interna

const int (&myClass::as_array())[3][3] const { return mat; } 

che può essere semplificato con typedef:

using int3x3 = int [3][3]; 

const int3x3& myClass::as_array() const { return mat; } 

std::array è una buona alternativa troppo.

ma non sarebbe inefficiente chiamare l'accessore per ogni membro dell'array.

int myClass::get_value(int i, int j) const { return mat[i][j]; } è perfettamente valido, deve essere sottolineato dal compilatore.

Se è necessario accedere a ogni int dall'array, quasi tutti i risultati alternativi nello stesso codice assembler.

L'errore di questo getter è che non è possibile utilizzare la maggior parte dell'algoritmo di stl che funziona con iteratore anziché indice.

Un modo semplice per semplificare iterator consiste nel modificare la dimensione dell'array da [3][3] in [3*3] (e eseguire il calcolo dell'indicizzazione a mano).