tags:

views:

179

answers:

5

I have some code which is effectively this:

class dummie_type
{
    public:
    int a;

    void do_stuff()
    {
        // blah
    }
};

class dummie_type dummie[10];

void main()
{
    subroutine();
}

void subroutine()
{
    dummie[3].a = 27; // etc...
    dummie[5].do_stuff();
}

Note that the array of classes is global, and I need it to remain so (its a long story). I need to change this code so that the array of classes is of variable length. I know that this will involve making a global pointer, and then setting that to point to a block of memory that gets malloc'ed or new'ed in main and I know that I will have to change the "." characters to "->" but other than that I keep failing to produce something that my compiler will accept. I'm particularly uncertain about the declaration of a global pointer to an array of classes.

Edit: Sorry I should have said earlier, the array size will be calculated once near the start of main() and will remain unchanged from then on.

+11  A: 

could you just change it so that's it's a vector?

std::vector<dummie_type> dummie;

int main() {

    //populate();
    //showing alternative loop based approach
    populateWithLoop(calc_size());
    subroutine();
 }

void subroutine()
{
    dummie[3].a = 27; // etc...
    dummie[1].do_stuff();
}

void populate() {

   dummie_type a;
   dummie_type b;
   dummie_type c;

   dummie.push_back(a);
   dummie.push_back(b);
   dummie.push_back(c);

   //will print out 3
   std::cout << dummie.size() << std::endl;

   dummie_type d;
   dummie.push_back(d);

   //will print out 4
   std::cout << dummie.size() << std::endl;


}

populateWithLoop(int n) {

   for(int i=0; i<n; i++) {
       dummie_type temp;
       dummie.push_back(temp);
   }
}

You can treat a std::vector pretty much like an array, so any existing code will still continue to work. You get the advantages of a variable length array without the downside's of having to manually manage the memory.

Edit: updated to show Mick how to populate the vector and get it's size.

Glen
excuse my ignorance, but shouldn't there be a line in there which says how big the array is? Say at the start of main() we have int n = calc_size(); then can you add in the line where n gets used to set the size of the array.... or does the use of vector somehow bypass this step?
Mick
He has a comment `//populate dummie somehow`. Here you would do something like `dummie.resize(n);`, or `dummie.push_back(...)`.
GMan
@Mick, I've edited my answer to make it clearer. It that doesn't help then let me know.
Glen
With this edit you've got an error by accessing element `[5]` in the array. :)
GMan
@GMan, oops. yes I do. thanks and fixed.
Glen
Thank you for your efforts, but it seems odd to use code involving separate lines and variables a,b,c and d to illustrate the creation of an array of *variable* length. Surely it would be clearer to do something like variable n = calc_size(); and then use a loop?
Mick
@Mick, there's nothing stopping you using a loop. But you do need to create the items in the array/vector somehow and the example I've shown does that. I'll add something with a loop in a minute.
Glen
It took me a while to work out I needed to do #include <vector>! (this is amongst the first bits of C++ I've ever done). But its now compiled and working. Thanks very much for your help.
Mick
+5  A: 

I know that this will involve making a global pointer, and then setting that to point to a block of memory that gets malloc'ed or new'ed in main and I know that I will have to change the "." characters to "->" but other than that I keep failing to produce something that my compiler will accept. I'm particularly uncertain about the declaration of a global pointer to an array of classes.

That's basically all there is to it. You declare dummie as dummie_type * dummie; and then you do dummie = new dummie_type[size]; in main. And no, you don't need to change . to ->.

sepp2k
duumie_type **dummie;
Arkaitz Jimenez
Why? He didn't say he wanted an array of pointers.
sepp2k
hmm, u are right!
Arkaitz Jimenez
After accepting the vector answer as correct, out of idle curiosity I tried your suggestion and to my surprise it worked perfectly. And its simpler. I wonder why the vector solution has twice as many votes (10:5)?
Mick
Thinking about it more - I guess its because the vector solution has more flexibility, but its actually superfluous flexibility for my needs.
Mick
@Mick, to answer some of your questions. The vector approach doesn't use pointers so it's 'safer' as you don't need to remember to call delete []. vector's manage their own internal buffers, so the chance of a buffer overflow is minimal. (though using the [] operator disables bounds checking, you could use the at() method which will perform bounds checking). vectors grow their internal buffer automatically, so you don't need to. Using a vector means you can use iterators and the std algorithms. Generally, using a std::vector over a bare array is the preferred approach with idioatic C++.
Glen
@Glen: You can use std algorithms with c arrays, too.
sepp2k
+1  A: 

Use an array of POINTERS to objects of your class.

Using std::vectors, it could be something like

typedef dummie_type * dummie_ptr;
typedef vector<dummie_ptr> dummie_array;

Of course, you will have to search through all your code to replace a lot of .s with ->s. But that way, you can resize the array without calling the constructor and destructor a lot of times.

Here's an example

int main(int argc, char* argv[])
{
    dummie_array my_array(5); // initial size

    for (int i = 0; i < 5; ++i)
         my_array[i] = new dummie_type(/* constructor parameters */);

    my_array.resize(8);
    for (int i = 5; i < 8; ++i)
         my_array[i] = new dummie_type(/* constructor parameters */);

    for (int i = 0; i < 8; ++i)
         my_array[i]->do_something(/* member function parameters */);

    for (int i = 0; i < 8; ++i)
         delete my_array[i];

    return 0;
}
Eduardo León
Why pointer to pointer? I'm confused why this is necessary.
GMan
The compiler will almost certainly handle searching the code for you as far as changing `.` to `->` is concerned. Of course if the class has overloaded `operator++`, or anything else that works on a pointer, then it's harder to ensure you've changed everything that needs changing.
Steve Jessop
@GMan: I think Eduardo is assuming that the class might have constructor/destructor/assignment that do a lot of work, and hence a vector of dummie_type would potentially be very painful to resize. Indeed, dummie_type might not even be assignable at all in which case a vector is impossible. Obviously the class in the example code doesn't have any of these problems (its constructor and destructor do absolutely nothing), so a vector of pointers would be a waste of effort in that case.
Steve Jessop
@onebyone: What if the class handles resources, say, allocating them in the constructor and deallocating them in the destructor? The class might be small, the constructor and destructor may have nothing but three instructions each, but using a vector of dummie_type would be DANGEROUS to resize!!!
Eduardo León
@Eduardo: When I say "a lot of work", I don't necessarily mean a lot of lines of code, I mean a lot of time. In this case, the assignment operator and copy constructor should either copy the resources ("lot of work") or be marked private with no implementation ("not assignable"). Otherwise the class is dangerous to exist at all, never mind whether you have a vector of them. One should not work around a completely broken class by avoiding vectors of them - the class should be fixed. So I assume that the questioner's class is not broken in the way you describe.
Steve Jessop
If the assignment operator and the copy constructor copy the resources, it's dangerous to resize the vector as well. :)
Eduardo León
+1  A: 

If you were to implement this as a vector, as Glen has suggested, when you need to do your one-time allocation near the beginning you would reserve(), e.g.:

int slots_i_need = 0;
if (/* ... */)
   slots_i_need = 4096;
else
   // ...
// ...
dummie.reserve(slots_i_need);

A minor efficiency would be to test whether the initial capacity allocated by the compiler is sufficient for your needs. In that case, you wouldn't need to reallocate. A vector's initial capacity is reserved during the first push_back:

// ...
dummie_type a_dummie = new dummie_type();
dummie.push_back(a_dummie);
if (slots_i_need > dummie.capacity())
   dummie.reserve(slots_i_need);
else
   {} // nothing to do.
Buggieboy
A: 
Darin