Reputation: 1817
I'll start with a recent problem I had - I had a vector and I took it's iterators, I stored the iterators (begin, end) and then the container got out of scope therefore destroyed. I don't remember any compiler warning. It was a hard debugging, because I got segfault only when I run it on extremely large data set. Is there a way to have compiler errors/warning while using an iterator/any reference to a container item after container modification (enlarges/shrinks)/destruction? Am I missing something? I think it's equally important as the const
qualifier because nobody wants a reference to an item in a messed up container, right?
I was forced to recall the situation above because now I have another homework - to create a specific container. Instead of a reference to an item, that is a certain memory location, I have to return a proxy reference because after assigning to the proxy reference, a portion of the container has to written to disk. But just for info, it perhaps does not matter, it could equally be a normal reference in this question. I want to mark this method returning the proxy reference (or any method returning an iterator) as "locking" which would mean that the container during the lifetime of the reference/iterator would behave effectively as const
. (Though a different const
, allowing modification of individual items, but forbidding modification of container let's say macro properties, e.g. returning a non-const reference by operator[] on this "macro-const" object would be allowed)
How do I do this in c++? Does this concept exist in other languages? Or possibly why it wouldn't work? As I said earlier, I assume this concept is general enough, because first data structures are the building blocks of any program and second because nobody wants a reference to an item in a messed up container.
Upvotes: 1
Views: 128
Reputation: 3638
Generalizing your problem a bit, it is often better to pass/keep a reference or pointer to the actual container instead of iterators, as iterators should be viewed as short-lived -- they are invalidated on many operations (e.g. insert and remove of elements).
To handle lifetime you can pass (or return) a std::shared_ptr
to the object to make sure that it is not deallocated when still in use by another part of the program.
Regarding the debugging, in addition to static analysis as mentioned by @user1118321 you can use the address sanitizer (for clang, compile and link with -fsanitize=address
, which in your case would terminate the program with an error AddressSanitizer: heap-use-after-free
and a quite detailed report on the context where it happened.
Regarding your question about "locking", inspired by the comment about using Rust by @melpomene: you can get semantics similar to borrowing in rust by moving (using std::move()
) the object to another function (thus "locking" it) and then moving it back by returning it. But then you cannot use the object at all until the called function returns ("gives back/unlocks") the object.
Upvotes: 1
Reputation: 26365
Is there a way to have compiler errors/warning while using an iterator/any reference to a container item after container modification (enlarges/shrinks)/destruction?
Yes. It's called a static analyzer. If you're using llvm, the clang static analyzer can help you find some uses of memory after it's been freed. There are similar tools for other compilers. They can tell you other useful things, as well, such as when a variable may be used without being initialized, and things of that nature.
I want to mark this method returning the proxy reference (or any method returning an iterator) as "locking" which would mean that the container during the lifetime of the reference/iterator would behave effectively as const. (Though a different const, allowing modification of individual items, but forbidding modification of container let's say macro properties,
I don't know of a way to do that which will make the compiler give you errors or warnings if the container is modified. But it is possible to lock access to a resource using either semaphores or mutexes. That will stop other code from modifying the container while you're using it. You could also write the container modifying methods so that they throw
an exception if they are called while the resource is locked.
Upvotes: 2