1

What is the use and explanation of something like this?:

int capacity;
int** number;
this->number = new int*[this->capacity];

I'm studying for an exam and in a test exam they put the requirement of using a pointer-to-pointer object and making a dynamic array from it. There are two classes; Wallet & WalletKeeper. In the solutions they did this in the header-file of WalletKeeper:

private:
    Wallet** wallets;
    int capacity;
    int size;
    /*other stuff below this*/

And in the constructor:

WalletKeeper::WalletKeeper(int capacity)
{
    this->capacity = capacity;
    this->size = 0;
    this->wallets = new Wallet*[this->capacity];
    this->initiate();
}

I understand a basic dynamic array like this:

Wallet * wallets = new Wallet[capacity];

This would mean you make a pointer which points to the place in the memory where this array of Wallets is made, so you can change the content of those memory slots. But why would you ever make a pointer to an array of pointers? What's the use?

Wallet has no array of its own, I would've understood it otherwise because I read this: The correct way to initialize a dynamic pointer to a multidimensional array?

Professors are on vacation until further ado.

Community
  • 1
  • 1
Jack Of Blades
  • 495
  • 7
  • 16
  • Why would you ever make an array of arrays that may contain other arrays? For example, a matrix can be represented as an array of arrays (an array of numbers for each row, the whole matrix is just an array of rows). – ForceBru Aug 16 '16 at 14:55
  • 6
    I'm working on an answer, but before I do, I just want to express how appalled I am that professors still teach c++ like this. They should have been teaching you to just use a `std::vector` or `std::vector>` object, either of which would be infinitely preferable to this error-prone and ill-suited-to-practical-applications code. – Xirema Aug 16 '16 at 14:58
  • 1
    It is an array of Wallet*. This is not "natural" modern C++ and should be done with container classes and raw allocation of memory should generally be avoided where possible. – crashmstr Aug 16 '16 at 15:00
  • @ForceBru Yes, I got that from the link I put up, but in this case there is no array in Wallet. I figure they could've told us to make a matrix with it if they wanted to teach us that. Does this give it some kind of edge in computer performance on large-date scales or why is this useful? – Jack Of Blades Aug 16 '16 at 15:00
  • Your teachers should have talked to you about [member initializer lists](http://en.cppreference.com/w/cpp/language/initializer_list) too... – Bob__ Aug 16 '16 at 15:03

3 Answers3

3

There are many uses of arrays of pointers.

  1. Reordering. Suppose you want to reorder the objects in the array. Dealing with the pointers is much faster than moving the entire object.
  2. Dynamic allocation. You can delete or allocate each object individually.
  3. Reallocation and performance. Suppose you want to increase the size of the array. Reallocating the actual objects may lead to different types of problems (invalidation). Reallocating the array of pointers however is more or less trouble free and also much faster.
Sven Nilsson
  • 1,861
  • 10
  • 11
  • On top of this, pointer arrays have the advantage that you can save a lot of memory by reusing the pointers if several elements in the array should be identical. – Sven Nilsson Aug 22 '16 at 09:03
1

The basic idea is that it allows you to create an "array of arrays". It has a narrow advantage over a matrix in that it allows you to have differently sized sub-arrays, but a disadvantage in that the memory of all objects is no longer contiguous across the entire array.

Wallet ** w_ptr_ptr = new Wallet*[capacity];
for(int i = 0; i < capacity; i++) {
    w_ptr_ptr[i] = new Wallet[i+1];
}

for(int i = 0; i < capacity; i++) {
    for(int j = 0; j < i+1; j++) {
        w_ptr_ptr[i][j] = Wallet(/*...*/);
    }
}

Note that in that code, w_ptr_ptr[0] has a differently sized array than w_ptr_ptr[1].

As I alluded to in my comment though, your professor shouldn't be teaching like this. Because this code requires manual memory cleanup, and doesn't have any capacity to do automatic bounds checking, the code you should be using is:

std::vector<std::vector<Wallet>> wallets;

for(int i = 0; i < capacity; i++) {
    wallets.emplace_back(i+1); //Will automatically create a i+1-sized array.
}

for(int i = 0; i < wallets.size(); i++) { //Note I'm able to query the size here!
    for(int j = 0; j < wallets[i].size(); j++) { //Again I can query the size!
        wallets[i][j] = Wallet(/*...*/);
    }
}
Xirema
  • 19,889
  • 4
  • 32
  • 68
  • Without seeing more code, we cannot assume that it is intended on being an array of arrays of `Wallet` or just an array of `Wallet*`. To me, it makes more sense that they intend `WalletKeeper` to hold `Wallet*`. – crashmstr Aug 16 '16 at 15:09
  • @crashmstr Well, the case is such that there is only a ptr2ptr(Wallet**) pointing to an array of pointers(Wallet*), and they don't point anywhere. Which is why I'm confused, I would have understood it if they were all pointing to an array each. – Jack Of Blades Aug 16 '16 at 15:16
  • The code would then assign or allocate `new Wallet` to the pointers as a later step. A "reason" to do this is so you don't allocate the `Wallet` objects up front. You would need to initialize the array to `nullptr` to be safe though (and can check to see if slot is occupied). – crashmstr Aug 16 '16 at 15:19
  • @crashmstr I would add though (mostly the same thing I've been saying before) that storing objects as `object *` or storing arrays of objects (specifically, not arrays-of-arrays-of-objects) as `object **` is largely a C-construct, not a C++ construct. In C++, you're expected to store objects directly, not as pointers, and if you need a pointer to an object, you should be using a container like `std::unique_ptr` or `std::shared_ptr`. If OP's professors were expecting them to store Wallet objects as `Wallet *` objects, that would double my lowly assessment of their teaching style. – Xirema Aug 16 '16 at 15:24
  • @Xirema yes, exactly. And yes, my assumption is that they are teaching archaic C++ and having them deal with raw arrays and memory allocation and not modern C++. – crashmstr Aug 16 '16 at 15:28
0

A pointer to pointers-objects is common used for matrices implementation. Indeed like pointers to objects implement dynamic array, as you've suggested in your question:

Wallet* wallets = new Wallet[capacity];

makes wallets points the first location of an array to num-capacity Wallet object.

[Wallet-obj] [Wallet-obj] [Wallet-obj] ... [Wallet-obj]
     0           1             2           capacity - 1

Pointer of pointers like:

Wallet** wallets = new Wallet*[capacity];

Create an array of Wallet pointer:

[Wallet-pointer] [Wallet-pointer] [Wallet-pointer] ... [Wallet-pointer]
        0              1                 2                capacity-1

Each pointer in the vector should point to an dynamic array.

I'll try to "draw" a representation:

 [0][wallet-pointer]  ----> [0][wallet obj] [1][wallet obj] ... [capacity-1][wallet obj]
 [1][wallet-pointer]  ----> [0][wallet obj] [1][wallet obj] ... [capacity-1][wallet obj]
 ...
 [capacity-1][wallet-pointer]  ----> [0][wallet obj] [1][wallet obj] ... [capacity-1][wallet obj]

So in order to access to an object you should use something like:

wallets[0][2];

That means access the 1-st pointer in the array of pointer, using the 1 "row" of object access to the 3-rd object of that row.

So as you can image, you have a matrix because you have a n-capacity dynamic array, like n-capacity rows.

Note

When you instantiate an array of pointers, you have to initialize each of them. This is a complete code:

Wallet** wallets = new Wallet*[capacity];

// Now for each pointer you have to allocate a dynamic array of n-elements.
for (size_t i = 0; i < capacity; ++i) {
  wallets[i] = new Wallet[capacity];
}

And the same is for deallocation phase:

// First of all, deallocate each object in each dynamic array:
for (size_t i = 0; i < capacity; ++i) {
  delete[] wallets[i];  // wallets[i] is a dynamic array to deallocate
}

// Finally deallocate the dynamic array of poiter
delete[] wallets;
Community
  • 1
  • 1
BiagioF
  • 9,368
  • 2
  • 26
  • 50