У меня есть класс, который хранит 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
имеет много элементов в контейнере, и часто называют заливку, это кажется очень неэффективным.
Ли 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;
}
Поскольку вы пояснили, что вы на самом деле используете 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;
}
}
Я бы предпочел использовать пользовательское удаление для 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();
}