I am trying to modify an std::vector through the use of references, the vector lifetime is designed in such a way modifications through the reference always are valid, in other words the the reference's value is never freed in the lifetime of the reference.
Here is the "minimal, reproducible example":
#include <vector>
class B {
public:
B() { m_b = 1; }
int &getInt() { return m_b; }
private:
int m_b;
};
class A {
public:
B &getB() {
m_vec.push_back(B());
return m_vec.back();
}
std::vector<B> getVec() { return m_vec; }
private:
std::vector<B> m_vec;
};
int main() {
A a;
B &b1 = a.getB();
int i = b1.getInt();
B &b2 = a.getB();
b1 = B();
return 0;
}
Here is the valgrind's output:
==22925== Memcheck, a memory error detector
==22925== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22925== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==22925== Command: ./test
==22925==
==22925== Invalid write of size 4
==22925== at 0x1092B5: main (test.cpp:27)
==22925== Address 0x4deec80 is 0 bytes inside a block of size 4 free'd
==22925== at 0x484BB6F: operator delete(void*, unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==22925== by 0x109E28: __gnu_cxx::new_allocator<B>::deallocate(B*, unsigned long) (new_allocator.h:145)
==22925== by 0x109B6F: std::allocator_traits<std::allocator<B> >::deallocate(std::allocator<B>&, B*, unsigned long) (alloc_traits.h:496)
==22925== by 0x1098D9: std::_Vector_base<B, std::allocator<B> >::_M_deallocate(B*, unsigned long) (stl_vector.h:354)
==22925== by 0x109A97: void std::vector<B, std::allocator<B> >::_M_realloc_insert<B>(__gnu_cxx::__normal_iterator<B*, std::vector<B, std::allocator<B> > >, B&&) (vector.tcc:500)
==22925== by 0x1096E5: B& std::vector<B, std::allocator<B> >::emplace_back<B>(B&&) (vector.tcc:121)
==22925== by 0x1094ED: std::vector<B, std::allocator<B> >::push_back(B&&) (stl_vector.h:1204)
==22925== by 0x1093F4: A::getB() (test.cpp:14)
==22925== by 0x10929D: main (test.cpp:26)
==22925== Block was alloc'd at
==22925== at 0x4849013: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==22925== by 0x109FED: __gnu_cxx::new_allocator<B>::allocate(unsigned long, void const*) (new_allocator.h:127)
==22925== by 0x109EAC: std::allocator_traits<std::allocator<B> >::allocate(std::allocator<B>&, unsigned long) (alloc_traits.h:464)
==22925== by 0x109D65: std::_Vector_base<B, std::allocator<B> >::_M_allocate(unsigned long) (stl_vector.h:346)
==22925== by 0x1099C0: void std::vector<B, std::allocator<B> >::_M_realloc_insert<B>(__gnu_cxx::__normal_iterator<B*, std::vector<B, std::allocator<B> > >, B&&) (vector.tcc:440)
==22925== by 0x1096E5: B& std::vector<B, std::allocator<B> >::emplace_back<B>(B&&) (vector.tcc:121)
==22925== by 0x1094ED: std::vector<B, std::allocator<B> >::push_back(B&&) (stl_vector.h:1204)
==22925== by 0x1093F4: A::getB() (test.cpp:14)
==22925== by 0x10927C: main (test.cpp:24)
==22925==
==22925==
==22925== HEAP SUMMARY:
==22925== in use at exit: 0 bytes in 0 blocks
==22925== total heap usage: 3 allocs, 3 frees, 72,716 bytes allocated
==22925==
==22925== All heap blocks were freed -- no leaks are possible
==22925==
==22925== For lists of detected and suppressed errors, rerun with: -s
==22925== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
The question is: why the error is happening in valgrind?
In the original program (Not in the minimal reproducible example), it runs for some miliseconds after the bug and then it crashes printing malloc(): unsorted double linked list corrupted
, is that related to this memory bug?
push_back
all iterators and references to the contents of the vector are (potentially) invalidated. valgrind is correct. You can avoid this by usingreserve
to increase the capacity of the vector. Ifpush_back
does not increase the size beyond the current capacity then iterators and references remain valid.