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
Related
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.
Reproduce a simple example here to demonstrate my question
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
struct Party {
Party(std::string value) : value_(value) {};
std::string value_;
};
int main() {
std::unordered_map<std::string, std::unique_ptr<Party>> map_;
std::vector<Party> parties_;
parties_.emplace_back("AAA");
parties_.emplace_back("BBB");
parties_.emplace_back("CCC");
std::unique_ptr<Party> ptr = std::make_unique<Party>(parties_.back());
ptr->value_ = "XXX";
for (auto& p : parties_) {
std::cout << p.value_ << std::endl; // print: AAA\nBBB\nCCC
}
}
Essentially, I want to store objects in a vector and have smart pointers pointing to them and to use these pointers to modify the value of the vector's objects.
But it seems like in making a new unique_ptr, parties_.back() actually return a copy of the object instead of the original object.
How do I achieve what I want here? Thank you!
std::make_unique<Party>(something) is essentially equivalent to std::unique_ptr<Party>(new Party(something)), so yes, it is creating new copies of Party objects because you asked for it.
Ultimately here unique_ptr doesn't seem the right choice: if your std::vector is already the sole owner (and manager of lifetime) of the objects, then you don't need to do anything particular, you can just use plain pointers/references. Be advised however that their validity is tied to the references invalidation rules of std::vector - in particular, if it decides to reallocate (which can happen e.g. if you do a push_back) all pointers/references become invalid.
std::unordered_map<std::string, Party*> map_;
std::vector<Party> parties_;
parties_.emplace_back("AAA");
parties_.emplace_back("BBB");
parties_.emplace_back("CCC");
Party *ptr = &parties_.back();
// Notice: if you do parties_.emplace_back("DDD") here
// ptr may become invalid
ptr->value_ = "XXX";
for (auto& p : parties_) {
std::cout << p.value_ << std::endl; // print: AAA\nBBB\nXXX
}
If you want to be isolated from the effects of reallocation, but are ok with std::vector being the owner, and thus dictating the lifetime, of your objects, you can have an std::vector<std::unique_ptr<Party>> (and, again, keep plain pointers/references to them around)
std::unordered_map<std::string, Party*> map_;
std::vector<std::unique_ptr<Party>> parties_;
parties_.emplace_back(std::make_unique<Party>("AAA"));
parties_.emplace_back(std::make_unique<Party>("BBB"));
parties_.emplace_back(std::make_unique<Party>("CCC"));
Party *ptr = parties_.back().get();
// Notice: if you do parties_.emplace_back(std::make_unique<Party>("DDD"));
// ptr will remain valid
ptr->value_ = "XXX";
for (auto& p : parties_) {
std::cout << p.value_ << std::endl; // print: AAA\nBBB\nXXX
}
This makes sure that objects are allocated independently from the vector, but if they are removed from the vector they'll be deleted.
OTOH, if you want shared ownership between the vector and the map, you may want std::shared_ptr (which however doesn't come for free, it has to manage reference counting & co.):
std::unordered_map<std::string, std::shared_ptr<Party>> map_;
std::vector<std::shared_ptr<Party>> parties_;
parties_.emplace_back(std::make_shared<Party>("AAA"));
parties_.emplace_back(std::make_shared<Party>("BBB"));
parties_.emplace_back(std::make_shared<Party>("CCC"));
std::shared_ptr<Party> ptr = parties_.back();
// Notice: if you do parties_.emplace_back(std::make_unique<Party>("DDD"));
// ptr will remain valid, but it will still be valid even after
// parties_.pop_back() (ptr will keep the pointed object alive)
ptr->value_ = "XXX";
for (auto& p : parties_) {
std::cout << p.value_ << std::endl; // print: AAA\nBBB\nXXX
}
This makes sure that the objects' lifetime is not tied to the lifetime of the vector, as any copy of the original std::shared_ptr will (1) point to the same object and (2) keep it alive.
std::make_unique<Party>(parties_.back()) creates a new object, always. It's a wrapper for std::unique_ptr<Party>(new Party(parties_.back())). Note that parties_.back() itself is not copying anything, it returns a reference.
In your code ptr does not need to own the Party its points to: parties_ is already the owner. Just use a reference or a raw pointer:
Party &lastParty = parties_.back();
lastParty.value_ = "XXX";
These two objects
std::unordered_map<std::string, std::unique_ptr<Party>> map_;
std::vector<Party> parties_;
both own the Party instances, as a std::vector always owns its elements, and std::unique_ptr is intended for exclusively owning the pointee. You need to decide which container is supposed to own the parties and manage their lifetimes. Example: std::vector<Party> owns the instances, then you can go with ordinary pointers.
std::unordered_map<std::string, Party*> map_;
std::vector<Party> parties_;
Plain pointers are ok to go with when they don't involve ownership semantics. Once you made this decision, the original question w.r.t. parties_.back() and std::make_unique (which always creates a new instance due to the purpose of std::unique_ptr) is no longer an issue.
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.
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.
I'm a little confused about the best practice for how to do this. Say I have a class that for example allocs some memory. I want it to self destruct like an auto but also put it in a vector for some reason unknown.
#include <iostream>
#include <vector>
class Test {
public:
Test();
Test(int a);
virtual ~Test();
int counter;
Test * otherTest;
};
volatile int count = 0;
Test::Test(int a) {
count++;
counter = count;
std::cout << counter << "Got constructed!\n";
otherTest = new Test();
otherTest->counter = 999;
}
Test::Test() {
count++;
counter = count;
std::cout << counter << "Alloced got constructed!\n";
otherTest = NULL;
}
Test::~Test() {
if(otherTest != 0){
std::cout << otherTest->counter << " 1Got destructed" << counter << "\n";
otherTest->counter = 888;
std::cout << otherTest->counter << " 2Got destructed" << counter << "\n";
}
}
int vectorTest(){
Test a(5);
std::vector<Test> vecTest;
vecTest.push_back(a);
return 1;
}
int main(){
std::cout << "HELLO WORLD\n";
vectorTest();
std::cout << "Prog finished\n";
}
In this case my destructor gets called twice all from counter 1, the alloc' object has already been set to 888 (or in a real case freed leading to bad access to a deleted object). What's the correct case for putting a local variable into a vector, is this some kind of design that would never happen sensibly. The following behaves differently and the destructor is called just once (which makes sense given its an alloc).
int vectorTest(){
//Test a(5);
std::vector<Test> vecTest;
vecTest.push_back(*(new Test(5)));
return 1;
}
How can I make the local variable behave the same leading to just one call to the destructor? Would a local simply never be put in a vector? But aren't vectors preferred over arrays, what if there are a load of local objects I want to initialize separately and place into the vector and pass this to another function without using free/heap memory? I think I'm missing something crucial here. Is this a case for some kind of smart pointer that transfers ownership?
A vector maintains its own storage and copies values into it. Since you did not implement a copy constructor, the default one is used, which just copies the value of the pointer. This pointer is thus deleted twice, once by the local variable destructor and once by the vector. Don't forget the rule of three. You either need to implement the copy and assignment operators, or just use a class that already does this, such as shared_ptr.
Note that this line causes a memory leak, since the object you allocated with new is never deleted:
vecTest.push_back(*(new Test(5)));
In addition to what Dark Falcon wrote: To avoid reallocating when inserting into a vector, you typically implement a swap function to swap local element with a default-constructed one in the vector. The swap would just exchange ownership of the pointer and all will be well. The new c++0x also has move-semantics via rvalue-references to help with this problem.
More than likely, you'd be better off having your vector hold pointers to Test objects instead of Test objects themselves. This is especially true for objects (like this test object) that allocate memory on the heap. If you end up using any algorithm (e.g. std::sort) on the vector, the algorithm will be constantly allocating and deallocating memory (which will slow it down substantially).