-1

I have a std::vector<std::unique_ptr<Kind>> which I want to clean up while it is being iterated upon, without explicitly calling the destructor of its members (.reset()).

The Kind is a heavy struct and its size increases during the iteration. The next object doesn't need to know about previous objects so I'd like to clean up an iterand when its not needed.

I know vector will clean up in the end, but by then, lots of Kind and their dynamically allocated memory adds up. I'm trying to reduce peak memory to just one element.

I want to avoid reset since other developers may not know about the dynamic allocation, forget calling reset in the end of the loop and cost memory penalty.

I cannot create a copy,

for(std::unique_ptr<Kind> t : store)

I cannot move it like

for(std::unique_ptr<Kind> &&t : store)

Then how do I do it ?

#include <iostream>
#include <vector>

struct Kind{
    char a;
    char *array;
    Kind(const char c): a(c)
    {
    }
    ~Kind(){
      free(array); // internal custom deallocator.
    }
};

int main() {
    std::vector<std::unique_ptr<Kind>> store;
    store.push_back(std::make_unique<Kind>('y'));
    store.push_back(std::make_unique<Kind>('z'));

    for(std::unique_ptr<Kind> &t : store){
        // increase size of Kind.array.
        std::cout << t->a;
        // Use the Kind.array
        // clean up t automatically.
    }
    return 0;

}
puio
  • 1,208
  • 6
  • 19

3 Answers3

3

Example of moving the element out of the vector.

int main() {
    std::vector<std::unique_ptr<Kind>> store;
    store.push_back(std::make_unique<Kind>('y'));
    for(std::unique_ptr<Kind> &t : store){
        auto tmp = std::move(t); // leaving a valid but empty entry in store
        std::cout << tmp->a;
        // clean up t automatically.
        // tmp runs out of scope and cleans up
    }
    return 0;
}

In effect not much different from the reset, but might be relevant for what you actually do in your real program.

Surt
  • 15,501
  • 3
  • 23
  • 39
  • "might be relevant for what you actually do in your real program." thank you understanding this basic thing! – puio Sep 01 '20 at 10:58
3

How to take ownership of an object while looping over std::vector of std::unique_ptr using a range based for loop?

Loop with a reference to the element, and std::move the unique pointer into another. Example:

for(std::unique_ptr<Kind> &t : store){
    std::unique_ptr<Kind> owner = std::move(t);
    // do something with newly owned pointer

I want to clean up

there's no need to keep older structs around

You could deallocate the object by resetting the pointer:

for(std::unique_ptr<Kind> &t : store) {
    // do something
    t.reset();

That said, this is typically unnecessary. They will be automatically be destroyed when the vector goes out of scope.

I'm trying to save some memory here

If you allocate dynamic objects while iterating this may be useful. Otherwise it won't affect peak memory use.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • "Your attempt appears to be futile. The peak memory use is unaffected by early destruction of the elements." see edited Q. array size increases, gets used and I want to delete it automatically before I move on to next object. – puio Sep 01 '20 at 10:47
  • @puio Fair enough, you seem to allocate memory while iterating, so `reset` may be useful. – eerorika Sep 01 '20 at 10:52
  • @puio No. If you allocate millions of structs with single char and store them in a vector, then you have high peak memory use whether you remove those structs one by one or all at once. Instead, what could reduce peak memory use is to not allocate the object until it is used. – eerorika Sep 01 '20 at 12:35
  • aaok.. I misunderstood some things. Thanks – puio Sep 01 '20 at 12:42
0

If you want to make sure the instances are deleted immediately after each iteration and you cannot wait until the entire loop is done, you can write a wrapper that takes care of that and expresses your intent at the same time:

template <typename T>
struct Stealing {
  std::unique_ptr<T> ptr;
  Stealing(std::unique_ptr<T>& ptr) : ptr(std::move(ptr)) {
  }   
  auto operator*() {
    return ptr.operator*();
  }   
  auto operator->() {
    return ptr.operator->();
  }   
}

You can use that in the loop as a drop-in replacement for a unique_ptr as such:

for (Stealing<Kind> t: store) {
  // do what you like with t as if it was a std::unique_ptr
  // when t goes out of scope, so does its member -> Kind gets destroyed
}
bitmask
  • 32,434
  • 14
  • 99
  • 159
  • @puio Just out of curiosity; What exactly did I do to you? – bitmask Sep 01 '20 at 14:20
  • This answer could've been written with the initial form of the question if one had carefully read the title. The constant talking down, stubbornness of sticking to "*just wait for vector to clean up*", downvotes and close votes was not necessary at all. If it was, the other two answerers would've joined you people. See my comment under accepted answer (unless it gets deleted by flags ofc) https://web.archive.org/web/20200901152037/https://stackoverflow.com/questions/63685834/how-to-take-ownership-of-an-object-while-looping-over-stdvector-of-stdunique/63688545?noredirect=1 – puio Sep 01 '20 at 15:19
  • // guess my comment got flags for removal here. I didn't delete it. To reiterate, while this answer is good (I went with good old for loop and moving ith member) , I will not accept or upvote it because I hold grudges. – puio Sep 01 '20 at 15:19
  • @puio I'm sorry that you are so agitated. It can be frustrating if people don't understand what you are trying to express, I've experienced the same. But if a question looks to exhibit the [XY problem](https://meta.stackexchange.com/q/66377/168610) it would be a disservice to OP not to ask for clarification. Please understand that if people ask comments under your question, it is because *they want to help you*. Look I don't care if you don't accept my post, I want you to understand that asking for clarification and even VTCs are useful tools for helping people and none are done out of malice. – bitmask Sep 01 '20 at 15:47