0

I've got a program that populates a STL linked list with structs, and I'm trying to pass struct members from a node on the list (one that I'm currently on via an iterator). One of the things I'm trying to accomplish is a function that will calculate a running average. Rather than storing the count and total in the struct, and then computing the average upon output, I'd like to store the count and average in the struct, thus discarding my quantity value after the average has been recalculated. My struct looks like this:

struct mystruct 
{
    string item;
    long avg;
    short count;
} data;

These structs are stored in a list, with an iterator, it, that allows me to move through the list. Would this be the proper way to call my average function, provided I've traversed through the list and it equals the node who's data I want to calculate the average with?

// prior to running the code below, the `count` and `avg` members for the 
// struct at iterator location `it` are both 1 and 100 respectively

long qty = 50;
calc_average(it->count, it->avg, qty);

cout << "The current count is " << it->count << endl; 
  // Outputs 'The current count is 2'
cout << "The current average is " << it->avg << endl; 
  // Outputs 'The current average is 75'


void calc_average(short &count, long &avg, long quant)
{
    avg = ( (avg * count) + quant ) / (count + 1);
    count++;
}

Does this seem correct? I'm trying to implement this using the STL list, but it seems more confusing than just implementing my own linked list class. I think I'm just getting confused with the structure and how the iterators actually work, and what/how things are actually being passed around. Coding is still fairly new to me so much of this is a learning process...

Thanks!

PSUlion01
  • 115
  • 2
  • 10

4 Answers4

3

Assuming that X is the type of the objects in the list, then you can do something like this:

void DoSomething(X& object) {
  object.count, object.avg;
}
void DoSomethingElse(int& count, int& average) {
  ...
}

int main() {
   ...
   for(std::list<X>::iterator it=myList.begin(), end=myList.end(); it != end; ++it) {
     DoSomething(*it); // how I'd do it
     DoSomethingElse(it->count, it->avg); // Equally valid way that you did it
   }
   ...
}

Just remember:

  • container.begin() is the a pointer to the first element
  • container.end() is a pointer to one-past-the-last element
  • *it is a reference to the pointed-to element
  • it != container.end() is how you tell if you are at the end
  • it->x is a member of the pointed-to element
  • removing an object from a container might invalidate outstanding iterators, depending upon several factors.
  • ++it is probably more efficient than it++

EDIT: OP asks:

I'm not iterating through the list and running calc_average on every single node, but rather iterating through the list looking for a specific item value. Once I find the one of interest, I'm calling the calc_average function on that specific node. I just don't need to have the for loop. Instead I would arrive at my desired iterator, and pass that via *it to void DoSomething ?

I think you understand how it works now. You'd have some code to search for the indicated node, and then some other code to invoke your function:

   std::list<X>::iterator it, end;
   for(it=myList.begin(), end=myList.end(); it != end; ++it) {
     // Look for the special node:
     if( it->magicValue == 42 ) {
       // We found it!
       break;
     }
   }

    // Either it is equal to end (boo!) or it points to the special node (yay!)
   if( it == end ) {
      std::cerr << "Could not find special node!\n";
   }
   if( it != end ) {
      DoSomething(*it);
   }
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Thanks, this helps. Let me clarify something though. I'm not iterating through the list and running `calc_average` on every single node, but rather iterating through the list looking for a specific `item` value. Once I find the one of interest, I'm calling the `calc_average` function on that specific node, call it node #5 in my list. As I write this, I think your suggestion still works though, right? I just don't need to have the `for` loop. Instead I would arrive at my desired iterator, and pass that via `*it` to `void DoSomething` ? Will try this later tonight, but this helps a ton! – PSUlion01 Oct 22 '12 at 21:20
  • One more question...if I make a function call like `DoSomething(it)`, will that just pass the pointed-to element by value? I ask because I have another output function where nothing is being written to the struct, but instead I'm just printing member values to the screen. Thx – PSUlion01 Oct 22 '12 at 21:29
  • 1
    `DoSomething(it)` will pass the iterator. `DoSomething(*it)` will pass the pointed-to-element. The question of whether they are passed by value or by reference depends upon the signature of `DoSomething()`: `DoSeomthing(X x)` receives the object by value. `DosSeomthing(X& x)` receives by reference. `DoSomething(std::list::iterator it)` receives the iterator by value. `DoSomething(std::list::iterator& it)` receives the iterator by reference. – Robᵩ Oct 22 '12 at 21:33
  • In the case you describe, I would declare `DoSomething(const X& x) { x.count, x.value, ...}` and invoke it thus: `DoSomething(*it)`. – Robᵩ Oct 22 '12 at 21:35
  • Trying all this out now and it's working great. I had been using `std::list::iterator it` in my code, and while it worked, it was harder to read and I didn't fully understand what it was doing. Your explanation above really helps. Per your last comment, are you suggesting I use that for my output function? Sorry, just not sure which "case" you were referring to. – PSUlion01 Oct 23 '12 at 03:18
  • Sorry one last question... Can you explain why I should use the pre-increment on my iterator `++it` vs post-increment? I see you said it's more efficient, but can you elaborate? I think I found the answer [here](http://stackoverflow.com/questions/24901/is-there-a-performance-difference-between-i-and-i-in-c) – PSUlion01 Oct 23 '12 at 03:19
  • One (more) last question, and this might be a style thing... Is there any reason that I'd want to use `it->item = someValue` instead of `object.item = someValue` when I've iterated to the desired node and want to access/update a member value? It seems like both would work, but what's the best practice? – PSUlion01 Oct 23 '12 at 16:20
  • Give `DoSomething()` the least knowledge required for its mission. If `DoSomething()` only needs to know about objects in isolation, use `DoSomething(X& object) { object.item; }` – Robᵩ Oct 23 '12 at 16:53
1

Think of an iterator as a pointer to an element in the list. So, instead of making the calc_average function act on individual members of mystruct you may want to have it take a reference to a mystruct object and doing something with that.

For instance:

void do_work( mystruct& s )
{
  ++s.count;
}

std::list<mystruct> mylist;

// populate list

std::for_each( mylist.begin(), mylist.end(), do_work );

You can also achieve the same effect with range based for loops

for( auto& elem : mylist ) {
  do_work( elem );
}

I would rewrite the calc_average function as

void calc_average( mystruct& s, long quant )
{
    s.avg = ( (s.avg * s.count) + quant ) / (s.count + 1);
    s.count++;
}

To use it with range based for loops is trivial, but to use it with std::for_each you'll have to use std::bind to bind the quant parameter.

std::for_each( mylist.begin(), mylist.end(), 
  std::bind( calc_average, std::placeholders::_1, qty ) );
Praetorian
  • 106,671
  • 19
  • 240
  • 328
0

Yes, it all seems correct to me. You can think of iterators (especially iterators of standard containers) as just pointers, though in practice they're actually classes that look like pointers. Still, you can think of them as pointers.

user1610015
  • 6,561
  • 2
  • 15
  • 18
0

I am currently working with dynamic content and I found I have a SPRITE structure std::list characters_spr; I declare a character by SPRITE character then I use

characters_spr.push_back(character)

then I use a for loop to iterate over the list

for(SPRITE &character : characters_spr){
   somefunction(character, ....);
}

in the somefunction I have somefunction(SPRITE &spr, ......................)

and when the somefunction is called it updates the character data for the character within the list.

guest
  • 1