2

I am currently filling an vector array of elements like so:

  std::vector<T*> elemArray;

  for (size_t i = 0; i < elemArray.size(); ++i)
  {
    elemArray = new T();
  }

The code has obviously been simplified. Now after asking another question (unrelated to this problem but related to the program) I realized I need an array that has new'd objects (can't be on the stack, will overflow, too many elements) but are contiguous. That is, if I were to receive an element, without the array index, I should be able to find the array index by doing returnedElement - elemArray[0] to get the index of the element in the array.

I hope I have explained the problem, if not, please let me know which parts and I will attempt to clarify.

EDIT: I am not sure why the highest voted answer is not being looked into. I have tried this many times. If I try allocating a vector like that with more than 100,000 (approximately) elements, it always gives me a memory error. Secondly, I require pointers, as is clear from my example. Changing it suddenly to not be pointers will require a large amount of code re-write (although I am willing to do that, but it still does not address the issue that allocating vectors like that with a few million elements does not work.

Community
  • 1
  • 1
Samaursa
  • 16,527
  • 21
  • 89
  • 160
  • If you want the _objects_ to be contiguous and you want 100000 of them then you may be running out of address space. How big are your objects and are you using a 64-bit platform? – CB Bailey Apr 21 '11 at 22:15
  • The program is compiled in 32-bit although my system is 64-bit. The object size is 192 bytes, which really shouldn't be a problem (approx. 20mb of contiguous memory). I have 8gb of system RAM. – Samaursa Apr 21 '11 at 22:18
  • You may be surprised, have you tried mapping the address space of you program while it is running? Also, I don't quite understand your "I require pointers" requirement. If you have a contiguous block of elements it's trivial to construct a pointer to any element. Perhaps you can post some clarifying code? – CB Bailey Apr 21 '11 at 22:30
  • @Charles: Yes, it is quite trivial, but there is a lot of code that is assuming the container is filled with pointers which will need to be changed. – Samaursa Apr 21 '11 at 22:41
  • @Charles: I have not tried mapping the address space, no. I have actually never done that before so I am not sure how to proceed with that. – Samaursa Apr 21 '11 at 22:42
  • Let me see if I've understood. You need a region of memory filled with 100000 contiguous objects and you also need a container of pointers pointing to the contiguously arranged objects? That's fine but you don't need to construct the objects with placement new to fill the container with the correct pointers. – CB Bailey Apr 21 '11 at 22:44
  • 1
    @Samaursa: If you're on windows you could try something like [Address Space Monitor](http://www.hashpling.org/asm/) (other tools are available) which graphical displays the address space and also shows you the size of the largest contiguous region of free space. If you're on linux you can `cat /proc/self/maps` . – CB Bailey Apr 21 '11 at 22:47
  • @Charles: Thanks for the link. And yes I need a region of memory filled with more than 100,000 (it will actually be close to 5 million, but now I am beginning to understand that may not be possible). Again, the reason they need to be contiguous is that I eventually want to know the index of an element in an array by only knowing about its pointer address and these elements cannot have a member variable `index` (which will solve the problem) because I cannot change majority of these elements. – Samaursa Apr 21 '11 at 22:52

7 Answers7

4

A std::vector<> stores its elements in a heap allocated array, it won't store the elements on the stack. So you won't get any stack overflow even if you do it the simple way:

std::vector<T> elemArray;
for (size_t i = 0; i < elemCount; ++i) {
   elemArray.push_back(T(i));
}

&elemArray[0] will be a pointer to a (continuous) array of T objects.

sth
  • 222,467
  • 53
  • 283
  • 367
  • Yup -- looks much more reasonable now. – Jerry Coffin Apr 21 '11 at 17:30
  • I don't get this, doesn't `T(i)` mean constructing T and passing it the argument `i`? – Samaursa Apr 21 '11 at 18:38
  • @Samaursa: Yes, but that of yours depends on how your `T` objects have to be constructed. Do there whatever needs to be done to create your `T` objects there. – sth Apr 21 '11 at 18:56
  • I am getting a bad alloc exception when I allocate 100,000+ elements. It is fine somewhere around 50,000... not sure why. Does not happen with new. Also, my code requires too many changes to make it work with T and not T* so I would prefer to use new. – Samaursa Apr 21 '11 at 21:36
3

If you need the elements to be contiguous, not the pointers, you can just do:

std::vector<T> elemArray(numberOfElements);

The elements themselves won't be on the stack, vector manages the dynamic allocation of memory and as in your example the elements will be value-initialized. (Strictly, copy-initialized from a value-initialized temporary but this should work out the same for objects that it is valid to store in a vector.)

I believe that your index calculation should be: &returnedElement - &elemArray[0] and this will work with a vector. Provided that returnedElement is actually stored in elemArray.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
1

While I also have the same requirements but for a different reason, mainly for being cache hot...(for small number of objects)

    int maxelements = 100000;

    T *obj = new T [100000];        

    vector<T *> vectorofptr;

    for (int i = 0; i < maxelements; i++)
    {
        vectorofptr.push_back(&obj[i]);
    }

or

    int sz = sizeof(T);
    int maxelements = 100000;

    void *base = calloc(maxelements, sz); //need to save base for free()

    vector<T *> vectorofptr;
    int offset = 0;

    for (int i = 0; i < maxelements; i++)
    {
        vectorofptr.push_back((T *) base + offset);
        offset += sz;
    }
1

Your original loop should look something like this: (though it doesn't create objects in contiguous memory).

for (size_t i = 0; i < someSize ; ++i)
{
    elemArray.push_back(new T());
}

And then you should know two basic things here:

  • elemArray.size() returns the number of elements elemArray currently holds. That means, if you use it in the for loop's condition, then your for loop would become an infinite loop, because you're adding elements to it, and so the vector's size would keep on increasing.
  • elemArray is a vector of T*, so you can store only T*, and to populate it, you've to use push_back function.
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • "though it doesn't create objects in contiguous memory" which I require (the other question in the link has a solution that will work for me as long as I can accomplish placing objects in a vector that are continuous and have been `new'd` somehow... otherwise I get a bad alloc exception for elements more than 100,000 [I need 5 million] ) – Samaursa Apr 21 '11 at 21:39
1

Considering your old code caused a stack overflow I think it probably looked like this:

Item items[2000000]; // stack overflow

If that is the case, then you can use the following syntax:

std::vector<Item> items(2000000);

This will allocate (and construct) the items contiguously on the heap.

StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • I need control on when I create them, and require the vector to store pointers... just like if I had int* a[50] which has 50 pointers that I can jump over by sizeof(int) and are guaranteed to be contiguous. – Samaursa Apr 21 '11 at 21:41
0

I know this is an old post but it might be useful intel for any who might need it at some point. Storing pointers in a sequence container is perfectly fine only if you have in mind a few important caveats:

  • the objects your stored pointers point to won't be contiguously aligned in memory since they were allocated (dynamically or not) elsewhere in your program, you will end up with a contiguous array of pointers pointing to data likely scattered across memory.

  • since STL containers work with value copy and not reference copy, your container won't take ownership of allocated data and thus will only delete the pointer objects on destruction and not the objects they point to. This is fine when those objects were not dynamically allocated, otherwise you will need to provide a release mechanism elsewhere on the pointed objects like simply looping through your pointers and delete them individually or using shared pointers which will do the job for you when required ;-)

  • one last but very important thing to remember is that if your container is to be used in a dynamic environment with possible insertions and deletions at random places, you need to make sure to use a stable container so your iterators/pointers to stored elements remain valid after such operations otherwise you will end up with undesirable side effects... This is the case with std::vector which will release and reallocate extra space when inserting new elements and then perform a copy of the shifted elements (unless you insert at the end), thus invalidating element pointers/iterators (some implementations provide stability though, like boost::stable_vector, but the penalty here is that you lose the contiguous property of the container, magic does not exist when programming, life is unfair right? ;-))

Regards

Romain
  • 1
0

Allocate a large chunk of memory and use placement new to construct your vector elements in it:

elemArray[i] = new (GetNextContinuousAddress()) T(); 

(assuming that you really need pointer to indidually new'ed objects in your array and are aware of the reasons why this is not recommended ..)

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
  • This is exactly what I need in this case, but why is this not recommended? – Samaursa Apr 21 '11 at 18:41
  • Having vectors of pointers is not recommended since you are forced to manually delete all elements. For example calling erase() on such a vector would leak the pointer element being removed. That's what smart pointers are typically for - they have destructors that free the objects being pointed at. – Alexander Gessler Apr 21 '11 at 19:18