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!
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 ++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);
}
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 ) );
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.
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.