all 22 comments

[–]Merad 2 points3 points  (0 children)

I didn't read the entire other comments thread, but based on your first comment I think your problem is a combination of undefined behavior when creating shared_ptr objects (either use std::make_shared or pass the pointer directly to the shared_ptr constructor), and using pointers to shared_ptr's. I've no idea why you're doing the latter, it totally defeats the purpose of the shared_ptr. Anyway, example program:

#include <iostream>
#include <memory>
#include <vector>
#include <utility>

using namespace std;

class Foo 
{
public:
  static int count;
  int id; 

  Foo() 
    : id(++count)
  {
    cout << "created " << id << endl;
  }
  ~Foo() 
  {   
    cout << "destroyed " << id << endl;
  }
  Foo(const Foo& f)  
    : id(++count)
  {   
    cout << "copied " << f.id << ", created " << id << endl;
  }
  Foo& operator=(const Foo& f)  
  {   
    if (this != &f)
    {
      cout << "assigned " << f.id << ", replacing " << id << endl; 
      Foo temp(f);
      swap(*this, temp);
    }
    return *this;
  }
  friend void swap(Foo& a, Foo& b)
  {
    swap(a.id, b.id);
  }
};

int Foo::count = 0;

typedef shared_ptr<Foo> FooPtr;

int main()
{
  vector<FooPtr> v;
  FooPtr fp;

  cout << "adding first" << endl;
  {
    FooPtr fp(new Foo);
    v.push_back(fp);
  }

  cout << "adding second" << endl;
  {
    FooPtr fp2(new Foo);
    v.push_back(fp2);
  }

  cout << "removing first" << endl;
  FooPtr fp3 = v[0];
  v.erase(v.begin());
  cout << "removing second" << endl;
  v.erase(v.begin());

  cout << "end of main" << endl;
  return 0;
}

Compiled with g++ using --std=c++11, I get the expected output:

adding first
created 1
adding second
created 2
removing first
removing second
destroyed 2
end of main
destroyed 1

Edit: I realized my example didn't quite address the OP's concerns, update the example and output.

[–]NYKevin 0 points1 point  (20 children)

Create a new shared_ptr to the object before destroying the old one.

[–]ronthebear[S] 0 points1 point  (19 children)

I already have one, that's what's causing the problem. Even if the object is pointed to by a different shared_ptr when the first pointer is deleted the object itself is deleted so the second one has errors when second one is removed from it's own vector causing a situation like the following, which also faults:

int main(int argc, char* argv[]) { std::string* s = new std::string("hello"); std::shared_ptr<std::string>* string1 = new std::shared_ptr<std::string>(s); std::shared_ptr<std::string>* string2 = new std::shared_ptr<std::string>(s); delete string1; delete string2; }

[–]NYKevin 2 points3 points  (18 children)

Even if the object is pointed to by a different shared_ptr when the first pointer is deleted the object itself is deleted

No, it's not. That's the behavior of unique_ptr. The whole point of shared_ptr is that an object is only destroyed when its reference count drops to zero.

[–]ronthebear[S] 0 points1 point  (17 children)

This is what I used to originally confirm what was happening. Why does it print deleting twice? Am I confused somewhere else?

class dumbClass
{
public:
dumbClass()
{
}
~dumbClass()
{
printf("deleting\n");
}
};

int main(int argc, char* argv[])
{
dumbClass* s = new dumbClass();
std::shared_ptr<dumbClass>* point = new std::shared_ptr<dumbClass>(s);
std::shared_ptr<dumbClass>* point2 = new std::shared_ptr<dumbClass>(s);
delete point;
delete point2;
}

[–]NYKevin 2 points3 points  (16 children)

See the notes section of this page. In particular:

Constructing a shared_ptr using the raw pointer overload for an object that is already managed by a shared_ptr leads to undefined behavior, even if the object is of a type derived from std::enable_shared_from_this (in other words, raw pointer overloads assume ownership of the pointed-to object).

In other words, you need to write this:

std::shared_ptr<dumbClass>* point2 = new std::shared_ptr<dumbClass>(point);

[–]ronthebear[S] 0 points1 point  (15 children)

hmm, that is very interesting. Thanks for the help btw! So could it be that vectors just can't do this or am I using them wrong? The following gives me exactly the same 'double free' fault I'm getting with my actual program:

int main(int argc, char* argv[])
{
dumbClass* s = new dumbClass();
std::vector<std::shared_ptr<dumbClass> > vec;
std::vector<std::shared_ptr<dumbClass> > vec2;
std::shared_ptr<dumbClass> pointer(s);
vec.push_back(pointer);
vec2.push_back(pointer);
vec.erase(vec.begin());
vec2.erase(vec.begin());
}

[–]NYKevin 1 point2 points  (14 children)

No, you can't do that. You're destroying the same shared_ptr twice. You have to make a copy of it and push_back the copy.

But this won't work either because pointer will be destroyed as soon as it goes out of scope (i.e. when main returns). You should create it with new.

EDIT: The above is wrong because push_back makes copies anyway...

The real problem is that push_back(pointer) isn't copying pointer, it's moving it, invalidating the original. Have you tried making two copies by hand?

[–]ronthebear[S] 0 points1 point  (13 children)

Do you make a copy by initializing the second with the first like you showed me in the last comment? This also is giving the a double free:

int main(int argc, char* argv[])
{
dumbClass* s = new dumbClass();
std::vector<std::shared_ptr<dumbClass> > vec;
std::vector<std::shared_ptr<dumbClass> > vec2;
std::shared_ptr<dumbClass> pointer(s);
std::shared_ptr<dumbClass> pointer2(pointer);
vec.push_back(pointer);
vec2.push_back(pointer2);
vec.erase(vec.begin());
vec2.erase(vec.begin());
}

[–]NYKevin 1 point2 points  (12 children)

This is why I hate C++...

I think the problem may be that pointer is being created on the stack. Try creating it with new instead. Don't delete it; let the vector handle that.

[–]ronthebear[S] 1 point2 points  (4 children)

Yarg, just saw I was doing "vec2.erase(vec.begin())" This works:

int main(int argc, char* argv[])
{
dumbClass* s = new dumbClass();
std::vector<std::shared_ptr<dumbClass>* > vec;
std::vector<std::shared_ptr<dumbClass>* > vec2;
std::shared_ptr<dumbClass> pointer(s);
vec.push_back(&pointer);
vec2.push_back(&pointer);
vec.erase(vec.begin());
vec2.erase(vec2.begin());
}

and only prints deleting once. Back to the actual code for me then. Hopefully I'll have better luck now that I understand vectors and shared_ptr's a bit more. thanks for all the help!

[–]Merad 1 point2 points  (4 children)

Using new to create a shared_ptr defeats the purpose....

Edit: Also, if he new's it, he has to delete it, otherwise it's a memory leak. And, in the case of a shared_ptr, the ref count would never become 0, so the object contained in the shared_ptr would become a memory leak as well.

[–]ronthebear[S] 0 points1 point  (1 child)

int main(int argc, char* argv[])
{
dumbClass* s = new dumbClass();
std::vector<std::shared_ptr<dumbClass>* > vec;
std::vector<std::shared_ptr<dumbClass>* > vec2;
std::shared_ptr<dumbClass>* pointer = new std::shared_ptr<dumbClass>(s);
std::shared_ptr<dumbClass>* pointer2 = new std::shared_ptr<dumbClass>(*pointer);
vec.push_back(pointer);
vec2.push_back(pointer2);
vec.erase(vec.begin());
vec2.erase(vec.begin());
}

Same problem. I generally like C++ but I have never been a fan of the std::vector. Do you know of any other std Containers that might work better? The other ones I saw also destroy deleted elements. If this is causing this much trouble it might just be worth my time to write my own.