如何摆脱容器中的weak_ptrs

时间:2013-09-27 20:35:29

标签: c++ c++11 weak-ptr

我有一个将weak_ptrs存储在容器中的类,如果weak_ptr没有过期,我会做一些事情:

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

如果Example是一个永远存在且永久使用fill的对象,则该向量会连续为元素分配内存,但它们在过期后永远不会被删除。

是否有任何自动C ++方法可以摆脱容器中过期的weak_ptrs,或者有更好的方法来存储可变数量的它们吗?

我天真的方式是每次调用fill时迭代容器并删除所有过期的weak_ptrs。在Example容器中有许多元素并且频繁调用填充的情况下,这似乎效率很低。

3 个答案:

答案 0 :(得分:5)

由于您澄清说您实际上使用的是std::map而不是std::vector,因此在doSomething()中即时删除已过期的元素可能最为简单。从基于范围的for循环切换回基于正常迭代器的设计:

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

答案 1 :(得分:2)

我宁愿为shared_ptr使用自定义删除器。但这意味着要更改Example类的接口。使用自定义删除器的优点是无需检查集合中的过期对象。该集合由自定义删除程序直接维护。

快速实施:

#include <memory>
#include <iostream>
#include <set>

template <typename Container>
// requires Container to be an associative container type with key type
// a raw pointer type
class Deleter {
    Container* c;
public:
    Deleter(Container& c) : c(&c) {}
    using key_type = typename Container::key_type;
    void operator()(key_type ptr) {
        c->erase(ptr);
        delete ptr;
    }
};

class Example {
public:
    // cannot change the custom deleter of an existing shared_ptr
    // so i changed the interface here to take a unique_ptr instead
    std::shared_ptr<int> fill(std::unique_ptr<int> thing) {
        std::shared_ptr<int> managed_thing(thing.release(), Deleter<containter_type>(member));
        member.insert(managed_thing.get());
        return managed_thing;
    }

    void dosomething() const {
        // we don't need to check for expired pointers
        for (const auto & i : member)
            std::cout << *i << ", ";

        std::cout << std::endl;
    }

    using containter_type =  std::set<int*>;
private:
    containter_type member;
};

int main()
{
    Example example;
    auto one = example.fill(std::unique_ptr<int>(new int(1)));
    auto two = example.fill(std::unique_ptr<int>(new int(2)));
    auto three = example.fill(std::unique_ptr<int>(new int(3)));
    example.dosomething();
    three.reset();
    example.dosomething();
}

答案 2 :(得分:1)

shared_ptr<int>必须是shared_ptr<int>吗?

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