Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get rid of weak_ptrs in a container

I have a class that stores weak_ptrs in a container and later does something if the weak_ptr is not expired:

class Example
{
public:
    void fill(std::shared_ptr<int> thing)
    {
        member.push_back(thing);
    }
    void dosomething() const
    {
        for (const auto& i : member)
            if (!i.expired())
                ;// do something. the weak_ptr will not be locked
    }
private:
    std::vector<std::weak_ptr<int>> member;
};

If Example is an object that lives forever and fill is used regularily, the vector allocates memory for elements continously, but they are never removed after they expired.

Is there any automatic C++ way to get rid of the expired weak_ptrs in the container or is there a better way to store a variable number of them?

My naive way would be to iterate over the container each time fill is called and remove all the expired weak_ptrs. In scenarios where Example has many elements in the container and fill is frequently called this seems to be very inefficient.

like image 418
typ1232 Avatar asked Sep 27 '13 20:09

typ1232


People also ask

How can a weak_ptr be turned into a shared_ptr?

The weak_ptr class template stores a "weak reference" to an object that's already managed by a shared_ptr. To access the object, a weak_ptr can be converted to a shared_ptr using the shared_ptr constructor or the member function lock.

Can a weak_ptr point to a Unique_ptr?

You can implement weak_ptr which works correctly with unique_ptr but only on the same thread - lock method will be unnecessary in this case.

What is weak_ptr?

std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.

Is weak_ptr lock thread safe?

Note that the control block used by std::weak_ptr and std::shared_ptr is thread-safe: different non-atomic std::weak_ptr objects can be accessed using mutable operations, such as operator= or reset , simultaneously by multiple threads, even when these instances are copies or otherwise share the same control block ...


2 Answers

Since you clarified that you are actually using a std::map and not a std::vector, it might be easiest to remove the expired elements on-the-fly in doSomething(). Switch back from a range-based for loop to a normal iterator based design:

void dosomething() const
{
    auto i = member.begin();
    while( i != member.end() ) {
      if( i->expired() ) { i = member.erase( i ); continue; }
      ;// do something. the weak_ptr will not be locked
      ++i;
    }
}
like image 148
Daniel Frey Avatar answered Sep 20 '22 00:09

Daniel Frey


Does the shared_ptr<int> have to be a shared_ptr<int>?

How about a shared_ptr<IntWrapper>?

#include <iostream>
#include <forward_list>
using namespace std;

class IntWrapper {
public:
    int i;

    static forward_list<IntWrapper*>& all() {
        static forward_list<IntWrapper*> intWrappers;
        return intWrappers;
    }
    IntWrapper(int i) : i(i)  {
        all().push_front(this);
    }
    ~IntWrapper() {
        all().remove(this);
    }
};

void DoSomething() {
    for(auto iw : IntWrapper::all()) {
        cout << iw->i << endl;
    }
}

int main(int argc, char *argv[]) {
    shared_ptr<IntWrapper> a = make_shared<IntWrapper>(1);
    shared_ptr<IntWrapper> b = make_shared<IntWrapper>(2);
    shared_ptr<IntWrapper> c = make_shared<IntWrapper>(3);
    DoSomething();
    return 0;
}
like image 24
CuriousGeorge Avatar answered Sep 21 '22 00:09

CuriousGeorge