Hello dear stackoverflow once again I'm here for help!
The idea: I'm implementing HashTable separate chaining based on sets
The problem: The destructor of set do not really work and I have no idea why
The code of set:
class set {
std::list<std::string>* bucket_array;
size_t bucket_array_size;
size_t set_size;
const double max_load_factor = 3.0;
public:
// Creates an empty set
set() :
bucket_array(new std::list<std::string>[4]),
bucket_array_size(4),
set_size(0)
{
}
};
the main problem is HERE:
set::~set(){
for (size_t i=0;i<=bucket_array->size();i++){
for(auto p = bucket_array[i].begin(); p != bucket_array[i].end(); ++p){
bucket_array[i].erase(p);
}
}
delete[] bucket_array;
}
From list::erase:
References and iterators to the erased elements are invalidated.
So when you write
for(auto p = bucket_array[i].begin(); p != bucket_array[i].end(); ++p){
bucket_array[i].erase(p);
you invalidate p and ++p is UB. You have to use the return value of erase:
for(auto p = bucket_array[i].begin();
p != bucket_array[i].end();
p = bucket_array[i].erase(p)) { }
or even simpler:
bucket_array[i].erase(bucket_array[i].begin(),
bucket_array[i].end());
But why do you erase all the lists manually? delete[] bucket_array; will do it automatically.
Even better would be to use std::vector and then even the delete becomes automatic and you wouldn't need set::~set() at all.
push_back, begin, end are described as concurrent safe in
https://learn.microsoft.com/en-us/cpp/parallel/concrt/reference/concurrent-vector-class?view=vs-2019#push_back
However the below code is asserting. Probably because element is added but not initialized yet.
struct MyData
{
explicit MyData()
{
memset(arr, 0xA5, sizeof arr);
}
std::uint8_t arr[1024];
};
struct MyVec
{
concurrency::concurrent_vector<MyData> v;
};
auto vector_pushback(MyVec &vec) -> void
{
vec.v.push_back(MyData{});
}
auto vector_loop(MyVec &vec) -> void
{
MyData myData;
for (auto it = vec.v.begin(); it != vec.v.end(); ++it)
{
auto res = memcmp(&(it->arr), &(myData.arr), sizeof myData.arr);
assert(res == 0);
}
}
int main()
{
auto vec = MyVec{};
auto th_vec = std::vector<std::thread>{};
for (int i = 0; i < 1000; ++i)
{
th_vec.emplace_back(vector_pushback, std::ref(vec));
th_vec.emplace_back(vector_loop, std::ref(vec));
}
for(auto &th : th_vec)
th.join();
return 0;
}
According to the docs, it should be safe to append to a concurrency::concurrent_vector while iterating over it because the elements are not actually stored contiguously in memory like std::vector:
A concurrent_vector object does not relocate its elements when you append to it or resize it. This enables existing pointers and iterators to remain valid during concurrent operations.
However, looking at the actual implementation of push_back in VS2017, I see the following, which I don't think is thread-safe:
iterator push_back( _Ty &&_Item )
{
size_type _K;
void *_Ptr = _Internal_push_back(sizeof(_Ty), _K);
new (_Ptr) _Ty( std::move(_Item));
return iterator(*this, _K, _Ptr);
}
I have to speculate on _Internal_push_back here, but I'd wager it allocates raw memory for storing the item (and points the last element towards this new node) so that the next line can use emplacement new. I'd imagine that _Internal_push_backis internally thread-safe, however I don't see any synchronization happening before the emplacement new. Meaning the following is possible:
memory is obtained and the node is "present" (yet emplacement new hasn't happend)
the looping thread encounters this node and performs memcmp to discover that they're not equal
emplacement new happens.
There's definitely a race condition here. I can spontaneously reproduce the problem, moreso the more threads I use.
I recommend that you open a ticket with Microsoft support on this one.
Before C++17, deleting all pointers from a map looked like:
for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
{
if (NULL != it->second)
{
delete(it->second);
}
}
m_map_buff_on_attrs.clear();
With C++17, we got:
for (auto it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
{
if (NULL != it->second)
{
delete(it->second);
}
}
Is there a simpler solution?
Yes.
for (auto it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
Since you only use the value *it and no other data from that iterator, a for-range loop would be simpler.
if (NULL != it->second)
{
delete(it->second);
}
Deleting NULL is well-defined and has no effect, this is a test you can skip.
Which gives:
for (auto& p : m_map_buff_on_attrs) {
delete p.second;
}
Manipulating raw pointers is error-prone and makes you write more useless code. If m_map_buff_on_attrs were a map of smart pointers, your code would simply be:
} // m_map_buff_on_attrs goes out of scope and destroys and frees everything.
RAII pattern is your friend:
using TMapBuffOnAttrs = std::map<std::string, std::unique_ptr<Attr>>;
m_map_buff_on_attrs["attrName"] = std::make_unique<Attr>(x, a);
....
// loop is not needed, to delete just do:
m_map_buff_on_attrs.clear();
You can use for_each with lambda function.
Here is a way:
std::for_each(m_map_buff_on_attrs.begin(), m_map_buff_on_attrs.end(),
[](auto &item) {delete item.second;});
Yes, use smartpointers. And simply 'clear()' the container...
Otherwise this is just "C code written in C++17"
How can i loop thru a stl::List and store the value of one of the objects for use later in the function?
Particle *closestParticle;
for(list<Particle>::iterator p1 = mParticles.begin(); p1 != mParticles.end(); ++p1 )
{
// Extra stuff removed
closestParticle = p1; // fails to compile (edit from comments)
}
Either
Particle *closestParticle;
for(list<Particle>::iterator it=mParticles.begin(); it!=mParticles.end(); ++it)
{
// Extra stuff removed
closestParticle = &*it;
}
or
list<Particle>::iterator closestParticle;
for(list<Particle>::iterator it=mParticles.begin(); it!=mParticles.end(); ++it )
{
// Extra stuff removed
closestParticle = it;
}
or
inline list<Particle>::iterator findClosestParticle(list<Particle>& pl)
{
for(list<Particle>::iterator it=pl.begin(); it!=pl.end(); ++it )
{
// Extra stuff removed
return it;
}
return pl.end();
}
or
template< typename It >
inline It findClosestParticle(It begin, It end)
{
while(begin != end )
{
// Extra stuff removed
return begin;
++begin;
}
return end;
}
These are sorted in increasing personal preference. :)
For a list, the only way to invalidate an iterator is to erase it. So I suspect you're calling list.erase(p1) at some point in the loop. You need to make a copy of the iterator, move p1 back one, and then erase the copy.
EDIT: Oh wait, did you mean it doesn't compile? If so, see #sbi's answer. But you really need to word your question in a good way. What is your compile error? Or does it fail at run-time? In this case, however, I believe you mean a compile error.
What is the shortest chunk of C++ you can come up with to safely clean up a std::vector or std::list of pointers? (assuming you have to call delete on the pointers?)
list<Foo*> foo_list;
I'd rather not use Boost or wrap my pointers with smart pointers.
For std::list<T*> use:
while(!foo.empty()) delete foo.front(), foo.pop_front();
For std::vector<T*> use:
while(!bar.empty()) delete bar.back(), bar.pop_back();
Not sure why i took front instead of back for std::list above. I guess it's the feeling that it's faster. But actually both are constant time :). Anyway wrap it into a function and have fun:
template<typename Container>
void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); }
Since we are throwing down the gauntlet here... "Shortest chunk of C++"
static bool deleteAll( Foo * theElement ) { delete theElement; return true; }
foo_list . remove_if ( deleteAll );
I think we can trust the folks who came up with STL to have efficient algorithms. Why reinvent the wheel?
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it)
{
delete *it;
}
foo_list.clear();
If you allow C++11, you can do a very short version of Douglas Leeder's answer:
for(auto &it:foo_list) delete it; foo_list.clear();
It's really dangerous to rely on code outside of the container to delete your pointers. What happens when the container is destroyed due to a thrown exception, for example?
I know you said you don't like boost, but please consider the boost pointer containers.
template< typename T >
struct delete_ptr : public std::unary_function<T,bool>
{
bool operator()(T*pT) const { delete pT; return true; }
};
std::for_each(foo_list.begin(), foo_list.end(), delete_ptr<Foo>());
I'm not sure that the functor approach wins for brevity here.
for( list<Foo*>::iterator i = foo_list.begin(); i != foo_list.end(); ++i )
delete *i;
I'd usually advise against this, though. Wrapping the pointers in smart pointers or using a specialist pointer container is, in general, going to be more robust. There are lots of ways that items can be removed from a list ( various flavours of erase, clear, destruction of the list, assignment via an iterator into the list, etc. ). Can you guarantee to catch them all?
The following hack deletes the pointers when your list goes out of scope using RAII or if you call list::clear().
template <typename T>
class Deleter {
public:
Deleter(T* pointer) : pointer_(pointer) { }
Deleter(const Deleter& deleter) {
Deleter* d = const_cast<Deleter*>(&deleter);
pointer_ = d->pointer_;
d->pointer_ = 0;
}
~Deleter() { delete pointer_; }
T* pointer_;
};
Example:
std::list<Deleter<Foo> > foo_list;
foo_list.push_back(new Foo());
foo_list.clear();
At least for a list, iterating and deleting, then calling clear at the end is a bit inneficient since it involves traversing the list twice, when you really only have to do it once. Here is a little better way:
for (list<Foo*>::iterator i = foo_list.begin(), e = foo_list.end(); i != e; )
{
list<Foo*>::iterator tmp(i++);
delete *tmp;
foo_list.erase(tmp);
}
That said, your compiler may be smart enough to loop combine the two anyways, depending on how list::clear is implemented.
Actually, I believe the STD library provides a direct method of managing memory in the form of the allocator class
You can extend the basic allocator's deallocate() method to automatically delete the members of any container.
I /think/ this is the type of thing it's intended for.
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); it++)
{
delete *it;
}
foo_list.clear();
There's a small reason why you would not want to do this - you're effectively iterating over the list twice.
std::list<>::clear is linear in complexity; it removes and destroys one element at a time within a loop.
Taking the above into consideration the simplest to read solution in my opinion is:
while(!foo_list.empty())
{
delete foo_list.front();
foo_list.pop_front();
}
Since C++11:
std::vector<Type*> v;
...
std::for_each(v.begin(), v.end(), std::default_delete<Type>());
Or, if you are writing templated code and want to avoid specifying a concrete type:
std::for_each(v.begin(), v.end(),
std::default_delete<std::remove_pointer<decltype(v)::value_type>::type>());
Which (since C++14) can be shortened as:
std::for_each(v.begin(), v.end(),
std::default_delete<std::remove_pointer_t<decltype(v)::value_type>>());
void remove(Foo* foo) { delete foo; }
....
for_each( foo_list.begin(), foo_list.end(), remove );
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i)
delete *i;
foo_list.clear();
This seems cleanest imo, but your c++ version must support this type of iteration (I believe anything including or ahead of c++0x will work):
for (Object *i : container) delete i;
container.clear();