2

this is my first time using the list STL and i'm not sure if what i'm trying to do is possible. I have class_B which holds a list of class_A, I need a function in class_B that takes an ID, searches the list for an instance with the same ID, and gets a pointer form the list to the instance in that list:

bool class_B::get_pointer(int ID,class_A* pointer2A){

   list<class_A>::iterator i;
   for(i=class_A.begin();i!=class_A.end();i++){
       if((*i).get_id()==ID) {
           \\pointer2A=(i);<---------------this is what I'm trying to do
           return true;
       }
   }
   pointer2A=NULL;
   return false;
}

how do I perform this, is it possible to convert from iterator to instance ?

EDIT:

I'm using this function in a multi-threaded program and I can't return an iterator to the calling function since another thread might delete an element of the list.

Now that I have a pointer to my element(and lets say it's locked so it can't be deleted), and a different thread removed another element and performed a sort on the list, what will happen to the pointer I'm holding ? (I don't know how the list rearranges the elements, is done by copying the elements using a copy c'tor, or by another mean?).

Useless answer was the most helpful in my case (BIG thanks), and yes I should use a reference to the pointer since I'm planing to change it.

Community
  • 1
  • 1
user1426692
  • 83
  • 1
  • 8
  • Note that `(*i).get_id()` is more easily expressed as `i->get_id()`. – chris May 30 '12 at 17:55
  • 1
    Also, in this case, you don't want an "instance", you want to convert an iterator to a pointer. Instead, I would recommend adding a `typedef std::list::iterator A_iterator` to `class_B`, and then making `pointer2A` of type `A_iterator`. – Mooing Duck May 30 '12 at 17:55

3 Answers3

9

You should write this:

pointer2A= &*i;

Here *i returns the object whose address you can get by prepending & as : &*i.

Note that i is not same as &*i. See this topic for more general discussion:


Anyway, I would suggest you to read the pointer itself as:

class_A* class_B::get_pointer(int ID)
{
   //I assume the name of the list is objA, not class_A
   for(list<class_A>::iterator i=objA.begin();i!=objA.end();i++)
   {
       if( i->get_id()==ID) 
       {
           return &*i;
       }
   }
   return NULL; //or nullptr in C++11 
}

Or, in C++11, you can use std::find_if as:

auto it = std::find_if(objA.begin(), 
                       objA.end(), 
                       [&](class_A const &a){ return a->get_id() == ID;});
classA *ptr = NULL;
if ( it != objA.end())
     ptr = &*it; //get the pointer from iterator

Make sure get_id is a const member function.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • In the final example, `std::find_if` returns an iterator, but you initialize `ptr` with it. Won't that fail? – Robᵩ May 30 '12 at 18:35
3
if(i->get_id()==ID) {
    pointer2A=&*i;
    return true;
}

iterators are designed to have similar semantics to pointers, so for example you can write i->get_id() just as if you had a pointer to A.

Similarly, *i yields a reference A&, and &*i converts that back into a pointer - it looks a bit clunky (it would be an identity operation if i were really a pointer), but it's idiomatic.

Note that this won't do what you presumably want anyway - the caller's class_A* pointer2A is passed by value, so only get_pointer's copy of the pointer is modified, and the caller won't see that value. Try this:

bool class_B::get_pointer(int ID, class_A *& pointer2A)
{
    list<class_A>::iterator i;
    for(i=class_A.begin();i!=class_A.end();i++) {
        if(i->get_id()==ID) {
            pointer2A=&*i;
            return true;
        }
    }
    pointer2A=NULL;
    return false;
}   

Now pointer2A is passed by reference, so the caller's copy gets modified inside your function.

BTW, you can read the parameter declaration class_A * & pointer2A right-to-left, as "pointer2A is a reference to a pointer to class_A".

Useless
  • 64,155
  • 6
  • 88
  • 132
1

If you have an iterator, you can get a raw pointer by simply dereferencing the iterator (which gives you a reference), and then taking the address of that (which gives you a pointer). So, in your case:

pointer2A = &*i;

This might seem like an odd, clumsy way to get a pointer, and it is. But you normally don't care about pointers when you are using the collections & iterators from the Std Lib. Iterators are the glue that hold the "STL" together. That's what you should be dealing with, by and large, rather than raw pointers.

The loop you've written above certainly gets the job done that you wish to accomplish, but there are better* ways to accomplish the same goal. (Better is a subjective term.) In particular, the <algorithm> library provides both std::find and std::find_if which do just what they say they do. They find something in a collection. find will find something that is equal to what you're looking for. find_if will find something that matches some criteria that you specify. The latter is the appropriate algorithm to use here, and there are two main ways to use it.

The first, more "traditional" approach is to use a functor:

struct match_id : public std::unary_function<bool, class_A>
{
  match_id(int ID) : id_(id) {};
  bool operator()(const class_A* rhs) const
  {
    if( id_ == rhs->get_id() )
      return true;
    else
      return true;
};

/* ... */

list<class_A>::iterator it = std::find_if(objA.begin(), objA.end(), match_id(ID));

This approach works in C++03 or C++11. Some people don't like it because it is rather verbose. I like it, on the other hand, because the actual buisness logic (the find_if call) is quite succinct and more expressive than an explicit loop.

In C++11, you can use a lambda in place of the functor:

unsigned ID = 42;
std::find_if( objA.begin(), objB.end(), [&ID](const class_A& rhs) -> bool { return rhs.get_id() == ID; } }; 

There's a tradeoff here. On the pro side, you don't have to write 10 or so lines of code for the functor, but on the con side, the lambda syntax is funky and takes a bit of getting used to.

John Dibling
  • 99,718
  • 31
  • 186
  • 324