Как избавиться от weak_ptrs в контейнере

У меня есть класс, который хранит 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 ++ избавиться от просроченных уязвимостей в контейнере или есть лучший способ хранить их переменное число?

Мой наивный способ — перебирать контейнер каждый раз fill вызывается и удаляет все просроченные уязвимые места. В сценариях, где Example имеет много элементов в контейнере, и часто называют заливку, это кажется очень неэффективным.

5

Решение

Ли 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;
}
1

Другие решения

Поскольку вы пояснили, что вы на самом деле используете 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;
}
}
5

Я бы предпочел использовать пользовательское удаление для 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
По вопросам рекламы [email protected]