Dynamic Array Woes

Started by
15 comments, last by Zahlman 17 years ago
A reference is a C++ language construct. It works as an alias to an existing object, instead of creating a copy from it. Unlike pointers, references cannot be reseated, and there is no such thing as a null reference.

An iterator is a generalization of the pointer concept: each is tied to a value in a sequence, which may be read or modified through it. Iterators may also be incremented or decremented to reach the previous or next elements in the sequence.

To return an iterator, simply use the correct return type. In your case, std::list<SDL_Surface*>::iterator.
Advertisement
Quote:Original post by ConorH
K, confused now. 2 Questions,
What's the diffrence between reference and iterator?
and
How can I return an iterator?
You might check out this site, in particular this section.

An 'iterator' is a generic concept used through the containers and algorithms portion of the standard C++ library. In general, iterators behave somewhat like pointers, although they are not in fact pointers (well, they might be, but as far as the user is concerned they have their own type).

Like pointers, they can be incremented, decremented (in some cases), compared with each other, or dereferenced via the * or -> operators. If you're at all familiar with how to use pointers, you'll find iterator semantics to be similar.

In your example, you have a couple of choices: you could return the iterator corresponding with the newly added object (probably not a terribly good idea), or return a copy of or reference to the object itself.

A list is not a random-access container, so in this case returning an integer value is not terribly useful (you could use distance, advance and so on to emulate this sort of behavior, but that also is not a terribly good idea).

What you should probably do, for now, is have the function return an SDL_Surface*, and just return im.back().

Once you have a better understanding of pointers, iterators, and other aspects of the SC++L, you will most likely want to instead manage your surfaces using smart pointers. boost::shared_ptr offers support for custom deleters, so it can be used with C-style resources such as SDL_Surface.
When I make std::list<SDL_Surface*>::iterator im; I cant use im.push_back(). So, to rephrase, how can I get an integer representation of how many elements are in the list, or do I have to keep count myself?

Sorry if Im not getting this, I've never encountered any of this so far but thanks for the help so far

Edit: Would vectors be better for this sort of thing then?
Quote:Original post by ConorH
When I make std::list<SDL_Surface*>::iterator im; I cant use im.push_back().
push_back() is a member function of list<>, not list<...>::iterator.
Quote:So, to rephrase, how can I get an integer representation of how many elements are in the list, or do I have to keep count myself?
If you just want to know how many elements are in a list, you can use the size() member function. However, this function should be used with care, as it's not guaranteed to be constant time (not AFAIK, at least).

I gather that the broader issue here is resource management, and how to manage (in particular) objects of type SDL_Surface, pass them around, clean them up when you're through with them, and so on.

In terms of safety and correctness, the optimal way to handle this is probably via smart pointers. However, if you're not yet comfortable with the fundamentals of the language, it may not be immediately obvious how or why this should be done.

That's probably as specific as I can be without knowing more about what you're trying to do. If you need further help you might post your cSprite class in its entirety. Perhaps you could also explain what the purpose of the return value of LoadImage() is.
a = (int *)malloc(SIZE_OF_ARRAY*sizeof(int))

and to do it for 2 or 3 dimensional arrays you'd just use a for loop or an embedded for loop.

it's actually not very difficult to dynamically allocate space for arrays.
Everyone hates #1.That's why a lot of idiots complain about WoW, the current president, and why they all loved google so much when it was new.Forget the fact that WoW is the greatest game ever created, our president rocks and the brainless buffons of America care more about how articulate you are than your decision making skills, and that google supports adware, spyware, and communism.
Quote:Original post by sharpnova
a = (int *)malloc(SIZE_OF_ARRAY*sizeof(int))

and to do it for 2 or 3 dimensional arrays you'd just use a for loop or an embedded for loop.

it's actually not very difficult to dynamically allocate space for arrays.
You might give the thread another read. The topic is not memory management in C, but rather resource management and the use of containers and iterators in C++ (or at least that's what it's evolved into).
You know, this would have been a *lot* easier if you had said what you wanted the int to *mean*. Yeah, just use .size() on the list.

Quote:Original post by ConorH
When I make std::list<SDL_Surface*>::iterator im; I cant use im.push_back(). So, to rephrase, how can I get an integer representation of how many elements are in the list, or do I have to keep count myself?


Ok, we're going to begin at the beginning.

A std::list is a thing that will store your elements. It knows how many elements it contains, and keeps them in a particular order. You can ask how many elements there are by calling .size() on the list. It's templated: you can make a std::list<int> which stores ints, or a std::list<SDL_Surface*> which stores pointers to SDL_Surfaces, or basically anything else.

A reference is a language construct: it provides an alias for an existing "thing". So we can make a reference to an SDL_Surface* which is in the list, and that variable effectively becomes another name for that particular SDL_Surface*.

When you use .back() on a container (such as std::list), it gives you a reference to the last element in the container. That is, it gives you that element, not by copying it, but by aliasing it:

std::list<int> foo;foo.push_back(42);int& lastThingInList = foo.back();lastThingInList = 23;// Now '23' is stored in the list instead of '42', because the name// 'lastThingInList' aliases the (single) value stored in the list.int bar = lastThingInList; // the '23' gets assigned to 'bar' as well.// Since 'bar' is not a reference, the value gets copied across.bar = 42; // does not affect the list, because bar is not a reference.


Of course, there's nothing special about references to ints; we can just as easily make a reference to a pointer to an SDL_Surface:

std::list<SDL_Surface*> surfaces;surfaces.push_back(SDL_loadImage(whatever));SDL_Surface* s = surfaces.back();// That's a copy of the pointer value; but the pointed-at surface is NOT copied.SDL_Surface*& s2 = surfaces.back();// That's a reference to the pointer value, i.e. an alias for it.s2 = NULL; // now NULL is stored in the list, but 's' still points at the same// surface, and the surface still exists.


An iterator is a concept created by the standard library - it is not built into the language itself. The idea is that in C++, because you can overload operators, it is possible to make a struct or class that "behaves like" a pointer, by giving it operators that correspond to the operations that are valid on pointers. Thus, pointers themselves are a kind of iterator, but not an especially powerful one.

A std::list<foo>::iterator is an iterator that is designed to "iterate over" the elements of a std::list. As you would expect, dereferencing it (as you would a pointer) gives you some element of the list, according to its current "position". Because it's possible to modify pointed-at things via a pointer, iterators in general support that as well. Therefore, the dereferencing operator is defined to return a reference. (That way, when you subsequently assign, you assign to the pointed-at thing, instead of a copy.) In order to make the list::iterator useful, "incrementing" or "decrementing" it causes it to "point to" the next or previous element of the list, correspondingly. This works even though that element could be anywhere else in memory - something a plain old pointer can't do (incrementing a plain old pointer would make it point at the next element-sized block of memory, and there could be ANYTHING there). It can do this because it's *not* a raw pointer, but an instance of a class that has some knowledge about the std::list's internal structure.

An "end" iterator is an iterator that "points one past the end" of a container. It may not be dereferenced, but it may at least be created and compared to. Oh, and it can be decremented (there's nowhere valid to increment it to). The reason for having such a thing is that the "distance" (number of times you'd have to increment/decrement to get from one to the other) between a "begin" iterator (one that "points to" the initial element) and the corresponding "end" iterator is equal to the number of elements. Standard library containers will give you a begin iterator to themselves if you ask with .begin(), and an end iterator if you ask with .end(). Because these are objects, they have some particular type. For standard library containers, the type name is simply 'iterator', but the class is nested within the container class: thus, a std::list<SDL_Surface*>::iterator is an iterator over a list of pointers to SDL_Surfaces, which is provided to you by the list of pointers to SDL_Surfaces so that you can iterate over it.

In a related way, you can loop ("iterate") over a container until you reach the end iterator, i.e. while your current iterator is not equal to the end iterator: e.g. "for (std::list<SDL_Surface*>::iterator it = mylist.begin(); it != mylist.end(); ++it)". This is actually quite similar to what beginning students are often taught to do with arrays:

int arr[SIZE];for (int i = 0; i < SIZE; ++i) {  arr = 0;}


Of course, this "i" concept is a little messy; what we really want to do is set "each element of arr". So we want to get some kind of handle to "elements of arr". Well, the obvious thing would be to have a pointer... the first element is arr[0], but that's just equivalent to *arr. If we have some pointer that's initialized to arr, and keep incrementing it, then it will iteratively (hmm, key word :) ) point at each element of the array. Now, the first "element" that we *don't* want to access is arr[SIZE], since it doesn't actually exist. That is, we don't want to dereference our pointer when it equals &(arr[SIZE]), which you should know from more basic teaching is equal to just 'arr + SIZE'. So that gives us:

int arr[SIZE];for (int* it = arr; it < arr + SIZE; ++it) {  *it = 0;}


Hopefully you have the idea now. Arrays are the most primitive kind of container, and pointers are the natural iterator type for arrays, because they're the most primitive kind of iterator ;) All that's different is that we normally use '!=' to compare to an end iterator instead of '<', because there is actually no need for iterators to be "ordered" (even if the elements are). We only care if we reach the end; the idea that all the other positions have been "less than" the end is circumstantial.

(Of course, we don't need to write this loop most of the time, either, because the standard library also provides "algorithms" that do this kind of drudge work.)



That said, just to recap, we never have to count up the elements ourselves, because the list is also counting them for us. Just use .size().

This topic is closed to new replies.

Advertisement