6

I have a private member of class called mat[3][3], and I want to be able to access this 3x3 array outside of my class (only read it, not change). Is it possible to write accessor method that returns pointer to my array? How can I do this? Please provide code sample.

Here is my class:

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

I know I can use something like

int get_mat(int i, int j);

to access every int inside the array one by one, but wouldn't it be inefficient to call the accessor for every member of the array?

Firmus
  • 195
  • 1
  • 1
  • 8
  • The `int i, int j` version wouldn't be inefficient. Compilers are good at handling this sort of thing. – M.M Dec 29 '15 at 03:19

2 Answers2

9

Is it possible to write accessor method that returns pointer to my array? How can I do this?

Here's one way:

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

but wouldn't it be inefficient to call the accessor for every member of the array?

Happily, no. in a release build your compiler will optimise all that away much better than you can probably imagine.

Express your intent elegantly. Allow the compiler to write optimal code for you (it will).

expected output:

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

As we start to flesh out the matrix class, we'd probably want to start building in some safety measures against buffer overruns (this code probably requires 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;
}

sample output:

6
76, 6, 39, 68, 40, 77, 28, 28, 76,
68, 40, 77,
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • I think it better to allow the syntax `M[i][j]` w/o range checking and `M.at(i,j)` with range checking, similar to `std::vector`. Often range checking can be safely omitted. Also, a proper C++ matrix should throw a `std::out_of_range` instead of using `assert()` -- the out-of-range error is not internal to the matrix class. – Walter Dec 28 '15 at 17:04
  • It's a matter of taste. I prefer the original version. (And I think the op[]/at difference was a mistake). Finally, op[] is not "without range checking" - it's just undefined behaviour. Hitting an assert is a perfectly reasonable way to implement undefined behaviour. (And if it's inline the optimizer will remove most range checks) – Martin Bonner supports Monica Dec 28 '15 at 17:18
  • @Walter if you compile with NDEBUG set (a release build), the assert compiles to a NOP, so in production it's not range-checked but at least you have a chance of finding the bug during development. I absolutely agree that the `.at()` version should throw a range_error. – Richard Hodges Dec 28 '15 at 17:27
  • In the first code sample, it would be simpler to make `mat` public and do away with all of the accessors – M.M Dec 29 '15 at 03:20
0

Is it possible to write accessor method that returns pointer to my array?

you may use this ugly syntax to return reference to your internal array

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

which may be simplified with typedef:

using int3x3 = int [3][3];

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

std::array is a good alternative too.

but wouldn't it be inefficient to call the accessor for every member of the array.

int myClass::get_value(int i, int j) const { return mat[i][j]; } is perfectly valid, should be inlined by compiler.

If you have to access each int from the array, almost all alternative result in same assembler code.

The pitfall of this getter is that you cannot use most algorithm from stl which works with iterator instead of index though.

One easy way to have simpler iterator is to change the dimension of the array from [3][3] into [3*3] (and do the indexing computation by hand).

Jarod42
  • 203,559
  • 14
  • 181
  • 302