11

I'm pretty new to C++ so this may be a trivial question:

My class has a private member variable that is an array. I need to return that array, but I'm not sure how to do that properly.

class X {
    // ...
    private: double m_Array[9];
    public: double* GetArray() const { return m_Array; }
};

Is there any problem with this code? This returns a pointer to the class member, right? - so if I fetch that array from an instance of this class and modify it (from outside the class), the original class member array will get changed as well? If that is the case, how do I return a copy of the array instead?

Niko
  • 26,516
  • 9
  • 93
  • 110
  • 2
    consider `std::vector< double >` if the array size is not fixed (in your example it seems fixed, so no worries). Added benefit: you can return a reference to the vector. – hochl Apr 10 '12 at 11:09
  • 2
    Usually with objects you encapsulate data and behaviour. Is there a reason `X` can't do what you want? [Tell, don't ask](http://pragprog.com/articles/tell-dont-ask). – Peter Wood Apr 10 '12 at 11:57

3 Answers3

11

This returns a pointer to the class member, right?

Almost - it returns a pointer to the first element of the array.

if I fetch that array from an instance of this class and modify it (from outside the class), the original class member array will get changed as well?

That's correct.

If that is the case, how do I return a copy of the array instead?

The easiest way to achieve that is to use std::array or std::vector instead. You should return a const reference to it - then the caller avoid the cost of copying when it's not needed. Example:

class X {
    std::array<double, 9> array;
public:
    std::array<double, 9> const & GetArray() const {return array;}
};

X x;
double d = x.GetArray()[5]; // read-only access, no copy

auto array = x.GetArray();  // get a copy
array[5] = 42;              // modify the copy

Alternatively, if the array has a fixed size (as it does in your example), you could return an array contained in a structure - which is what std::array is. Otherwise you could return a pointer (preferably a smart pointer, to avoid memory leaks) to a newly allocated array - which is more or less what std::vector is.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Yes, you would have to 'clone' it, or in the case of an array, copy the contents into a new array. –  Apr 10 '12 at 11:09
  • +1, but isn't it better to create somekind of 'array view' instead where a struct is returned that implements operator[], as I would assume that array copying would use alot of memory and time – Yet Another Geek Apr 10 '12 at 11:11
  • @YetAnotherGeek: The question asks how to return a copy of the array, so it can be modified without affecting the original. You can't do that without copying it. – Mike Seymour Apr 10 '12 at 11:12
  • @MikeSeymour I just assumed that he didn't want changes from outside the class, but yes if it has to be modified then copying is inevitable – Yet Another Geek Apr 10 '12 at 11:13
  • You do **not** want to return a classical smart pointer here; the data is a member of the class, and will be destructed when the class instance is destructed. Regardless. Calling a destructor on it or trying to free it will result in undefined behavior. – James Kanze Apr 10 '12 at 11:17
  • @JamesKanze: I'm not sure what you mean by a "classical" smart pointer, but I suggested returning a pointer to a *newly allocated* array, not the class member. – Mike Seymour Apr 10 '12 at 11:24
  • Thank you, this is very much what I was looking for. But one short follow up question about your example code: Why is the ampersand in `const &` necessary? – Niko Apr 10 '12 at 11:27
  • 2
    @Niko: Returning by `const` reference, rather than by value, allows you to read the array without the cost of copying it, and only copy it if you need to modify it. – Mike Seymour Apr 10 '12 at 11:29
  • @MikeSeymour OK. That does make sense, sort of. Although IMHO, a better solution would be to return an `std::vector` or an `std::array`. – James Kanze Apr 10 '12 at 11:37
  • @JamesKanze: Indeed, that's why I suggested that first. Perhaps it was a mistake to complicate my answer by mentioning alternatives. – Mike Seymour Apr 10 '12 at 11:42
  • 1
    @MikeSeymour In this case, probably. First, it's possible that others will misread it as I did, and think you meant a smart pointer to the member. And second, the usual smart pointers don't do an array delete, which would be necessary here. Boost has some that do, but I'm willing to bet that most people who wouldn't know what to do without having read your answer aren't aware of them, or the necessity of using them. (The "standard" smart pointer for transfering ownership, for those of us who can't be guaranteed a fully compiliant C++11, is `auto_ptr`, which doesn't work with arrays.) – James Kanze Apr 10 '12 at 12:57
2

In order to return a copy a new array of double would need to dynamically allocated, populate with the current values of m_Array and returned to the caller (a pointer to the first element to be specific). The caller would be responsible for delete[]ing the returned array. As it stands, the caller has no knowledge of the number of elements in the returned array.

As this is C++, suggest changing to a std::vector<double> instead: this would handle the copying for you and provide the caller with information regarding the number of doubles.

hmjd
  • 120,187
  • 20
  • 207
  • 252
2

Yes, when you change the array you will also change the class member.

I would recommend you to use std::vector instead of c-style array in c++. It will make your life way easier and will make the code easier to read. If there is still a reason for you to insist on using array, you need to allocate a new array and return a pointer to it. Note that this will force you to deal with dynamic memory and take care to free it:

public:
  double* GetArray() {
    double* res = new double[9];
    for (int i = 0; i < 9; ++i) {
      res[i] = m_Array[i];
    }
    return res;
  }

You can also use memcpy to copy the elements of the array.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176