Pointer to list element after removal - c++

I have a list that stores objects.
list<MyObject> l;
I also have a method that returns a pointer to one of those objects, if it exists, or nullptr otherwise.
MyObject* get(int x) {
for (std::list<MyObject>::iterator it = l.begin(); it != l.end(); ++it) {
if (it->X == x) {
return &(*it);
}
}
return nullptr;
}
If I get() a pointer to an element, and while I am using it, it gets erased from another thread, the pointer becomes invalid, and weird things happen :)
What I wanted to know is if there is a way of returning some special kind of pointer in get(), so that if I call erase on an element and that element is still being referenced, its memory won't be released until the pointer to it goes out of scope.
I considered using a reference, but I need the possibility of returning nullptr on get, so I can check from the caller if the return was indeed a valid object.
Can someone suggest a better way of doing this, to avoid these memory issues?

As recommended you should use some smart_pointer to manage the shared ownership.
Some recomendations:
Use always as default, std::vector
If could use C++11 use the standard shared_ptr for shared ownership, if not, use boost version.
Use the algorithm header as much as you can (in this case find_if is the right one).
You should also try to use the algorithm for the search of the specific element. Here is some sample code:
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>
struct MyObject {
int X;
MyObject(int x_value) : X(x_value) {}
};
using element_t = std::shared_ptr<MyObject>;
std::vector<element_t> l{
std::make_shared<MyObject>(3), std::make_shared<MyObject>(4),
std::make_shared<MyObject>(5), std::make_shared<MyObject>(6),
std::make_shared<MyObject>(7), std::make_shared<MyObject>(8)};
element_t get(int x) {
auto it = std::find_if(std::begin(l), std::end(l),
[x](const element_t& elem) { return elem->X == x; });
element_t found;
if (it != std::end(l)) {
found = *it;
}
return found;
}
int main() {
auto f1 = get(6);
if (f1) {
std::cout << "encontrado " << f1->X << std::endl;
} else {
std::cout << "6 no se encontro" << std::endl;
}
auto f2 = get(10);
if (f2) {
std::cout << "encontrado " << f2->X << std::endl;
} else {
std::cout << "10 no se encontro" << std::endl;
}
return 0;
}

Before using smart pointers, you might want to make sure you can spell out the reason why you can't (or don't want to) design a system where your objects have only one owner at a given time.
Smart pointers will avoid invalid data access, but they have all sorts of more or less hidden problems
they cost additional memory, force you to use them and their move semantics everywhere, and might easily become tricky, e.g. if you keep circular references or want an object to return a smart pointer to itself,
std:: containers become basically as useless as when you fill them with any kind of pointers (a vector of pointers is not a vector of objects),
you don't control where the deallocation takes place, so you might have your objects deleted by any task referencing them, possibly a time-critical one,
having no clear idea of who owns what is more often than not a recipe for disaster.
For instance, having one thread decide to delete objects while another grabs some from the same storage without any synchronization is very dangerous indeed. It is a bit as if one thread considered the object invalid while the other would consider it valid.
Does not strike me as the most robust design, but surely you have your reasons.
I think you could start by using unique_ptrs and see if that suits your needs, instead of jumping to shared_ptrs right away.

Related

c++ creating cyclically dependent objects with raw pointers

I'm trying to create a connected graph and to perform certain computations on it. To do that, from each node in this graph, I need to access its neighbors and to access its neighbor's neighbors from its neighbor and so forth. This inevitably creates many (useful) cyclic dependencies.
Below is a simplified example with 3 mutually connected nodes (like the 3 vertices of a triangle), and I'm not sure if this method is a good way to do it, particularly if the clean-up leaves any memory leaks :
#include <iostream>
#include <vector>
class A {
public:
int id;
std::vector<A*> partners;
A(const int &i) : id(i) {
std::cout << id << " created\n";
}
~A() {
std::cout << id << " destroyed\n";
}
};
bool partnerUp(A *a1, A *a2) {
if (!a1 || !a2)
return false;
a1->partners.push_back(a2);
a2->partners.push_back(a1);
std::cout << a1->id << " is now partnered with " << a2->id << "\n";
return true;
}
int main() {
std::vector<A*> vecA;
vecA.push_back(new A(10));
vecA.push_back(new A(20));
vecA.push_back(new A(30));
partnerUp(vecA[0], vecA[1]);
partnerUp(vecA[0], vecA[2]);
partnerUp(vecA[1], vecA[2]);
for (auto& a : vecA) {
delete a;
a = nullptr;
}
vecA.clear();
return 0;
}
I'm also aware that I can use shared_ptr + weak_ptr to complete the task, but smart pointers come with an overhead and I'd love to avoid that whenever possible (I also hate to use .lock() all the time to access the data, but that doesn't really matter). I rewrote the code using smart pointers as follows, and I'd like to know what are the differences between the 2 pieces of code (outputs of the two codes are identical).
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class A {
public:
int id;
vector<weak_ptr<A>> partners;
A(const int &i) : id(i) {
cout << id << " created\n";
}
~A() {
cout << id << " destroyed\n";
}
};
bool partnerUp(shared_ptr<A> a1, shared_ptr<A> a2) {
if (!a1 || !a2)
return false;
a1->partners.push_back(a2);
a2->partners.push_back(a1);
cout << a1->id << " is now partnered with " << a2->id << "\n";
return true;
}
int main() {
vector<shared_ptr<A>> vecA;
vecA.push_back(make_shared<A>(10));
vecA.push_back(make_shared<A>(20));
vecA.push_back(make_shared<A>(30));
partnerUp(vecA[0], vecA[1]);
partnerUp(vecA[0], vecA[2]);
partnerUp(vecA[1], vecA[2]);
return 0;
}
You can prevent memory leaks by using a principle of ownership: At every point, there needs to be an owner who is responsible for freeing the memory.
In the first example, the owner is the main function: It undoes all the allocations.
In the second example, each graph node has shared ownership. Both vecA and the linked nodes share ownership. They are all responsible in the sense that they all call free if necessary.
So in this sense, both versions have a relatively clear ownership. The first version is even using a simpler model. However: The first version has some issues with exception safety. Those are not relevant in this small program, but they will become relevant once this code is embedded into a larger application.
The issues come from transfer of ownership: You perform an allocation via new A. This does not clearly state who the owner is. We then store this into the vector. But the vector itself won't call delete on its elements; it merely call destructors (no-op for a pointer) and deletes its own allocation (the dynamic array/buffer). The main function is the owner, and it frees the allocations only at some point, in the loop at the end. If the main function exits early, for example due to exception, it won't perform its duties as the owner of the allocations - it won't free the memory.
This is where the smart pointers come into play: They clearly state who the owner is, and use RAII to prevent issues with exceptions:
class A {
public:
int id;
vector<A*> partners;
// ...
};
bool partnerUp(A* a1, A* a2) {
// ...
}
int main() {
vector<unique_ptr<A>> vecA;
vecA.push_back(make_unique<A>(10));
vecA.push_back(make_unique<A>(20));
vecA.push_back(make_unique<A>(30));
partnerUp(vecA[0].get(), vecA[1].get());
partnerUp(vecA[0].get(), vecA[2].get());
partnerUp(vecA[1].get(), vecA[2].get());
return 0;
}
The graph can still use raw pointers, since the ownership is now solely the responsibility of the unique_ptr, and those are owned by vecA, and that is owned by main. Main exits, destroys vecA, and this destroys each of its elements, and those destroy the graph nodes.
This is still not ideal, though, because we use one indirection more than necessary. We need to keep the address of the graph nodes stable, since they're being pointed to from the other graph nodes. Hence we should not use vector<A> in main: if we resize that via push_back, this changes the addresses of its elements - the graph nodes - but we might have stored those addresses as graph relations. That is, we can use vector but only as long as we haven't created any links.
We can use deque even after creating links. A deque keeps the addresses of the elements stable during a push_back.
class A {
public:
int id;
vector<A*> partners;
// ...
A(A const&) = delete; // never change the address, since it's important!
// ...
};
bool partnerUp(A* a1, A* a2) {
// ...
}
int main() {
std::deque<A> vecA;
vecA.emplace_back(10);
vecA.emplace_back(20);
vecA.emplace_back(30);
partnerUp(&vecA[0], &vecA[1]);
partnerUp(&vecA[0], &vecA[2]);
partnerUp(&vecA[1], &vecA[2]);
return 0;
}
The actual problem of deletion in a graph is when you don't have a data structure like your vector in main: It is possible to just keep pointers to one or several nodes from which you can reach all other nodes in main. In that case, you need graph traversal algorithms to delete all nodes. This is where it gets more complicated and hence more error prone.
In terms of ownership, here the graph itself would have ownership of its nodes, and main has ownership of just the graph.
int main() {
A* root = new A(10);
partnerUp(root, new A(20));
partnerUp(root, new A(30));
partnerUp(root.partners[0], root.partners[1]);
// now, how to delete all nodes?
return 0;
}
Why would the second approach be recommended?
Because it follows a widespread, simple pattern that reduces the likelyhood of a memory leak. If you always use smart pointers, there'll always be an owner. There's just no opportunity for a bug that drops ownership.
However, with shared pointers, you can form cycles where multiple elements are kept alive because they own each other in a cycle. E.g. A owns B and B owns A.
Therefore, the typical rule-of-thumb recommendations are:
Use a stack object, or if not possible, use a unique_ptr or if not possible, use a shared_ptr.
For multiple elements, use a container<T>, or container<unique_ptr<T>> or container<shared_ptr<T>> in that order.
These are rules of thumb. If you have time to think about it, or some requirements like performance or memory consumption, it can make sense to define a custom ownership model. But then you also need to invest the time to make that safe and test it. So it should really give you a great benefit to be worth all the effort needed to make it safe. I would recommend against assuming that shared_ptr is too slow. This needs to be seen in the context of the application and usually measured. It's just too tricky to get custom ownership concepts right. In one of my examples above, you need to be very careful with resizing the vector, for example.

Invalidate all shared ptrs toward a specific managed object

Is it possible, in C++11, to have an object managed by several std::shared_ptrs. I want to delete the object via one std::shared_ptr and have the other shared_ptrs invalidated (set empty or null), is this possible? If not, what is the best method to inform all other "references" (in a liberal use of the word) that the object is no longer valid?
To do this, the other shared_ptrs have to be replaced with weak_ptrs. The shared_ptr that does the deletion is the one actually manages the lifetime of the object in this scenario. It's worthwhile at this point to figure out if you really need shared ownership semantics. In general, if you find yourself trying to do something the interface doesn't let you do, that's an indication that you need something with different semantics.
Alternatively, if you really can't manage the object's lifetime from one place, you can use shared_ptr<unique_ptr<T>>, but this is more cumbersome (not to mention slower) and is better to avoid. Here you would delete the object by resetting the inner unique_ptr.
Here is a good example of weak_ptr and to be informed when all other "references" is no longer valid.
#include <iostream>
#include <memory>
std::weak_ptr<int> gw;
void f()
{
std::cout << "use_count == " << gw.use_count() << ": ";
if (auto spt = gw.lock())
{ // Has to be copied into a shared_ptr before usage
std::cout << *spt << "\n";
}
else
{
std::cout << "gw is expired\n";
}
}
int main()
{
{
std::shared_ptr<int> sp = std::make_shared<int>(42);
gw = sp;
f();
}
f();
}
Output: use_count == 1: 42 use_count == 0: gw is expired

How to delete raw pointers from a unique_ptr

I'm writing a game framework, I have a vector<unique_ptr<Object>> list and I distribute pointers from that list by calling object.get() and sending that out. Before that I send out references instead of raw pointers but that resulted in other weird problem so I was told this is better. However when I remove a unique_ptr<Object>from the list, the raw pointers remains. I also can't deallocate them manually, I get an exception saying the pointer is not allocated.
So my question would be:
How do I delete raw pointers from removed unique_ptr's?
and is also a more general question:
Am I on the right track structure wise of passing pointers instead of references?
PxlObject* PxlFramework::AddObject(PxlObject* obj)
{
std::unique_ptr<PxlObject> u_ptr(obj);
objects_iterator = objects.insert(objects.end(), std::move(u_ptr));
return obj;
}
void PxlFramework::DeleteObject(PxlObject* obj) {
for(objects_iterator = objects.begin(); objects_iterator != objects.end(); ++objects_iterator)
{
if((*objects_iterator)->get_id() == obj->get_id())
{
//attempting to delete the raw pointer here but it will result in an error
delete obj;
//removing the unique_ptr<PxlObject> from the list here
std::swap((*objects_iterator), objects.back());
objects.pop_back();
break;
}
}
}
The whole point of std::unique_ptr is that it "owns" the object and it manages deletion automatically when the unique_ptr is destroyed. As such, you should not delete either a unique_ptr nor anything that a unique_ptr owns. To avoid this confusion, references are more common. Additionally, you have the oddity that your AddObject returns a pointer to a PxlObject that is not the one just added.
Something like this might be a little cleaner:
template<class Us...>
PxlObject& PxlFramework::AddObject(Us&&... obj)
{
std::unique_ptr<PxlObject> u_ptr(new PxlObject(std::forward<Us>(obj)...));
objects_iterator = objects.insert(objects.end(), std::move(u_ptr));
return **objects_iterator;
}
void PxlFramework::DeleteObject(PxlObject& obj) {
auto finder = [](std::unique_ptr<PxlObject>& p)->bool
{return obj.get_id()==p->get_id();};
auto it = find_if(objects.begin(), objects,end(), finder);
if (it != objects.end())
objects.erase(it);
else
throw ...;
}
You don't need delete the raw pointer directly you can use vector.erase instead. Here you have a simple example:
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
using namespace std;
typedef vector<unique_ptr<int>> ptr_list_t;
void remove_number(int x, ptr_list_t& ptr_list)
{
for (ptr_list_t::iterator it = ptr_list.begin(); it != ptr_list.end(); ++it)
if (*(it->get()) == x) {
ptr_list.erase(it); // Use vector.erase for deleting objects from a vector.
// since it points to a unique_ptr, the object owned by it
// will be destroyed automatically.
break;
}
}
int main()
{
ptr_list_t ptr_list;
// Generating the pointer to numbers. 0 - 9
for (int i = 0; i < 10; i++)
ptr_list.push_back(unique_ptr<int>(new int(i)));
// Remove the number 3.
remove_number(3, ptr_list);
// Printing the list. The number 3 will not appear.
for (ptr_list_t::iterator it = ptr_list.begin(); it != ptr_list.end(); ++it)
cout << *(it->get()) << endl;
return 0;
}
Other thing, I'm agreed with #MooingDuck: you should not delete either a unique_ptr nor anything that a unique_ptr owns. But you sure can. Take a look on unique_ptr.release. This function frees the ownership of the managed object.

Removing all the entries pointing to a deleted object from a map

So here is the code I am dealing with:
class A
{
public:
A(){}
virtual ~A(){}
void Log(){printf("Log A\n");}
};
int main(int argc, char**argv)
{
A* a = new A();
a->Log(); // "Log A"
map<int,A*> m;
m[1] = a;
m[2] = a;
m[3] = a;
m[1]->Log(); // "Log A"
delete a;
a = NULL;
m[1]->Log(); // "Log A"
return 0;
}
Output:
Log A
Log A
Log A
My questions:
Is it only by chance that calling m[1]->Log() does not throw exception after delete a?
What's the best approach to erase all the entries in the map pointing to the deleted instance of A? I mean I want all m.find(1), m.find(2) and m.find(3) to return m.end() after deleting a. Any advice would be appreciated.
Yes and no. Technically it's undefined behavior, but usually (don't rely on this) calling non-virtual methods that don't access members appears to work on most compilers because most compilers implement this call without dereferencing this (which is the invalid part). So, in the standard's opinion, it's by chance. For most compilers, it's intended (or at least a side-effect of how function calls are handled).
Use smart pointers instead. To remove the elements, you can iterate through the map and compare each value to yours. When you reach one, use erase. Iterators are invalidated after erase.
Anything that happens when you dereference a deleted object is undefined behaviour, so even getting an exception could be considered to be "by chance"
The best approach is to couple the deletion of the object that is pointed to with the lifetime of something you have a handle on. So in your case, you could decide that it is best if the object is deleted if the pointer is removed from the map. To achieve this, you could use a map of int to std::unique_ptr<A> instead of one of raw pointers.
Edit: After looking at the requirements in more detail:
Now, since you want to remove elements whose mapped type points to a deleted object, and there is no way to determine whether the memory a pointer points to has been deleted, I see no simple way of removing these entries from the map other than doing it all in one function call. And since std::map et al do not like std::remove_if, one can use a loop:
template <typename T1, typename T2>
void removeEntriesAndDelete(std::map<T1, T2*>& m, T2*& item) {
for (auto i = m.begin(); i != m.end(); ) {
if ( item == i->second) {
m.erase(i++);
} else {
++i;
}
}
delete item;
item=0;
}
int main() {
A* a = new A;
std::map<int,A*> m;
m[1] = a;
m[2] = a;
m[3] = a;
std::cout << std::boolalpha;
std::cout << a << ", " << bool(m[1]) << ", " << bool(m[2]) << ", " << bool(m[3]) <<"\n";
removeEntriesandDelete(m, a);
std::cout << a << ", " << bool(m[1]) << ", " << bool(m[2]) << ", " << bool(m[3]) <<"\n";
}
Generally, objects which enrolled somewhere must notify the object where
they are enrolled. In simple cases like your example code, it's
relatively simple to go through the map, erasing each element which
points to your object, but I assume that your actual use case is less
trivial. The usual solution (in fact, the only one which really works
in practice) involves the observer pattern; when an object saves a
pointer to your object, either directly or in a map or a list or
whatever, it also enrols with your object, requesting notification when
your object is destructed. Your object keeps a list of these observers,
and will notify them in its destructor. Applied to a simple case like
yours, it looks like a lot of unnecessary code, but in the context of
actual applications where this pattern occurs, it's not that much.
It works by luck as you mention.
Either use something like smart pointers as previously mentioned, or encapsulate the map and value handling in a class, where you could have a method that removes the object from the map and deletes the object.

C++: Why is the destructor being called here?

I guess I don't fully understand how destructors work in C++. Here is the sample program I wrote to recreate the issue:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Odp
{
int id;
Odp(int id)
{
this->id = id;
}
~Odp()
{
cout << "Destructing Odp " << id << endl;
}
};
typedef vector<shared_ptr<Odp>> OdpVec;
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
shpoutOdp.reset();
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp.reset(iter->get());
return true;
}
}
return false;
}
int main()
{
OdpVec vec;
vec.push_back(shared_ptr<Odp>(new Odp(0)));
vec.push_back(shared_ptr<Odp>(new Odp(1)));
vec.push_back(shared_ptr<Odp>(new Odp(2)));
shared_ptr<Odp> shOdp;
bool found = findOdpWithID(0, shOdp, vec);
found = findOdpWithID(1, shOdp, vec);
}
Just before main() concludes, the output of this program is:
Destructing Odp 0
Destructing Odp 1
Why does this happen? I'm retaining a reference to each of the Odp instances within the vector. Does it have something to do with passing a shared_ptr by reference?
UPDATE I thought that shared_ptr::reset decremented the ref count, based on MSDN:
The operators all decrement the
reference count for the resource
currently owned by *this
but perhaps I'm misunderstanding it?
UPDATE 2: Looks like this version of findOdpWithID() doesn't cause the destructor to be called:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp = *iter;
return true;
}
}
return false;
}
This line right here is probably what is tripping you up.
shpoutOdp.reset(iter->get());
What you're doing here is getting (through get()) the naked pointer from the smart pointer, which won't have any reference tracking information on it, then telling shpoutOdp to reset itself to point at the naked pointer. When shpoutOdp gets destructed, it's not aware that there is another shared_ptr that points to the same thing, and shpoutOdp proceeds to destroy the thing it's pointed to.
You should just do
shpoutOdp = *iter;
which will maintain the reference count properly. As an aside, reset() does decrement the reference counter (and only destroys if the count hits 0).
So many things that are being used nearly correctly:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
Here the parameter shpoutOdp is a a copy of the input parameter. Not such a big deal considering it is a shared pointer but that is probably not what you were intending. You probably wanted to pass by reference otherwise why pass it to the function in the first place.
shpoutOdp.reset();
Resetting a parameter as it is passed in.
Does this mean it could be dirty (then why have it as an input parameter) it make the function return a shared pointer as a result if you want to pass something out.
Odp& odp = *(iter->get());
Don't use get on shared pointers unless you really need to (and you really if ever need too). Extracting the pointer is not necessary to get at what the pointer points at and makes you more likely to make mistakes because you are handling pointers. The equivalent safe(r) line is:
Odp& odp = *(*iter); // The first * gets a reference to the shared pointer.
// The second star gets a reference to what the shared
//pointer is pointing at
This is where it all goes wrong:
shpoutOdp.reset(iter->get());
You are creating a new shared pointer from a pointer. Unfortunately the pointer is already being managed by another shared pointer. So now you have two shared pointers that think they own the pointer and are going to delete it when they go out of scope (the first one goes out of scope at the end of the function as it is a copy of the input parameter (rather than a reference)). The correct thing to do is just to do an assignment. Then the shared pointers know they are sharing a pointer:
shpoutOdp = *iter; // * converts the iterator into a shared pointer reference
The next line though not totally wrong does assume that the iterators used are random access (which is true for vector).
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
But this makes the code more brittle as a simple change in the typedef OdpVec will break the code without any warning. So to make this more consistent with normal iterator usage, use != when checking against end() and also prefer the pre increment operator:
for (OdpVec::iterator iter = vec.begin(); iter != vec.end(); ++iter)
shared_ptr::reset destroys the contents already in the shared_ptr. If you want to affect only that single shared_ptr reference, simply assign to it.
EDIT: In response to comment, you can fix it by changing the body of your for loop to:
if ((*iter)->id == id)
{
shpoutOdp = *iter;
return true;
}
EDIT2: That all said, why aren't you using std::find_if here?
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm> //for std::find_if
#include <functional> //for std::bind
struct Odp
{
int id;
int GetId()
{
return id;
}
Odp(int id)
{
this->id = id;
}
~Odp()
{
std::cout << "Destructing Odp " << id << std::endl;
}
};
typedef std::vector<shared_ptr<Odp> > OdpVec;
int main()
{
OdpVec vec;
vec.push_back(std::shared_ptr<Odp>(new Odp(0)));
vec.push_back(std::shared_ptr<Odp>(new Odp(1)));
vec.push_back(std::shared_ptr<Odp>(new Odp(2)));
OdpVec::iterator foundOdp = std::find_if(vec.begin(), vec.end(),
std::bind(std::equal_to<int>(), 0, std::bind(&Odp::GetId,_1)));
bool found = foundOdp != vec.end();
}
The nice thing about shared_ptr is that it handles the ref-counting internally. You don't need to manually increment or decrement it ever. (And that is why shared_ptr doesn't allow you to do so either)
When you call reset, it simply sets the current shared_ptr to point to another object (or null). That means that there is now one less reference to the object it pointed to before the reset, so in that sense, the ref counter has been decremented. But it is not a function you should call to decrement the ref counter.
You don't ever need to do that. Just let the shared_ptr go out of scope, and it takes care of decrementing the reference count.
It's an example of RAII in action.
The resource you need to manage (in this case the object pointed to by the shared_ptr) is bound to a stack-allocated object (the shared_ptr itself), so that its lifetime is managed automatically. The shared_ptr's destructor ensures that the pointed-to object is released when appropriate.