2015-12-28 4 views
6

Ich habe ein privates Mitglied der Klasse namens mat [3] [3], und ich möchte auf diese 3x3-Array außerhalb meiner Klasse zugreifen können (nur lesen, nicht ändern). Ist es möglich, eine Accessor-Methode zu schreiben, die den Zeiger auf mein Array zurückgibt? Wie kann ich das machen? Bitte geben Sie ein Codebeispiel an.Wie schreibe ich Accessor für 2D-Array (private Mitglied)

Hier ist meine Klasse:

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

Ich weiß, dass ich so etwas wie

int get_mat(int i, int j); 

zu jedem Zugriff int innerhalb des Arrays einzeln verwenden kann, aber es wäre nicht ineffizient zu nennen der Accessor für jedes Mitglied des Arrays?

+0

Die 'int i, int j'-Version wäre nicht ineffizient. Compiler sind gut im Umgang mit solchen Dingen. –

Antwort

9

Ist es möglich, eine Accessor-Methode zu schreiben, die den Zeiger auf mein Array zurückgibt? Wie kann ich das machen?

Hier ist eine Art und Weise:

#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; 
} 

aber wäre es nicht ineffizient sein für jedes Mitglied des Arrays den Accessor zu nennen?

Glücklicherweise, nein. In einem Release-Build wird Ihr Compiler alles besser optimieren als Sie sich vorstellen können.

Drücken Sie Ihre Absicht elegant aus. Erlauben Sie dem Compiler, optimalen Code für Sie zu schreiben (es wird).

erwartete Ausgabe:

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

Als wir die Matrix-Klasse zu konkretisieren beginnen, würden wir wahrscheinlich wollen den Bau in einigen Sicherheitsmaßnahmen gegen Pufferüberläufe starten (dieser Code wahrscheinlich erfordert C++ 14). ..

#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; 
} 

Beispielausgabe:

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

Ich denke es ist besser, die Syntax 'M [i] [j]' ohne Bereichsüberprüfung und 'M.at (i, j)' mit Bereichsüberprüfung zuzulassen, ähnlich wie 'std :: vector'. Oft kann die Bereichsüberprüfung sicher weggelassen werden. Außerdem sollte eine richtige C++ - Matrix einen 'std :: out_of_range' werfen, anstatt' assert() 'zu verwenden - der Fehler außerhalb des Bereichs liegt nicht innerhalb der Matrix-Klasse. – Walter

+0

Es ist eine Frage des Geschmacks. Ich bevorzuge die Originalversion. (Und ich denke, die Op []/at-Differenz war ein Fehler). Schließlich ist op [] nicht "ohne Bereichsüberprüfung" - es ist nur undefiniertes Verhalten. Eine Assertion zu treffen ist eine völlig sinnvolle Möglichkeit, undefiniertes Verhalten zu implementieren. (Und wenn es inline ist, wird das Optimierungsprogramm die meisten Bereichsüberprüfungen entfernen) –

+0

@Walter Wenn Sie mit dem NDEBUG-Set (einem Release-Build) kompilieren, kompiliert das Assert zu einem NOP, so dass es in der Produktion nicht range-checked ist, aber zumindest haben Sie eine Chance den Fehler während der Entwicklung zu finden. Ich stimme absolut zu, dass die '.at()' Version einen range_error werfen sollte. –

0

Ist es möglich, eine Accessor-Methode zu schreiben, die den Zeiger auf mein Array zurückgibt?

können Sie diese hässliche Syntax Bezug auf Ihre interne Array zurückzukehren

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

, die mit typedef vereinfacht werden kann:

using int3x3 = int [3][3]; 

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

std::array auch eine gute Alternative ist.

aber wäre es nicht ineffizient, den Accessor für jedes Mitglied des Arrays aufzurufen.

int myClass::get_value(int i, int j) const { return mat[i][j]; } ist perfekt gültig, sollte vom Compiler inlined sein.

Wenn Sie auf jedes int vom Array zugreifen müssen, resultieren fast alle Alternativen in demselben Assemblercode.

Die Fallstricke dieser Getter ist, dass Sie nicht die meisten Algorithmus von Stl, die mit Iterator statt Index obwohl funktioniert.

Eine einfache Möglichkeit, einfacher zu haben iterator ist, die Dimension des Arrays von [3][3] in [3*3] zu ändern (und die Indizierung Berechnung von Hand).

Verwandte Themen