简体   繁体   中英

How do I pass struct members stored in a list to a function by reference?

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
  • 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);
   }

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM