3

does the function set::insert saves a pointer to the element or a copy of it. meaning, can I do the following code, or I have to make sure that the pointers are not deleted?

int *a;
*a=new int(1);
set<int> _set;
_set.insert (*a);
delete a;
*a=new int(2);
_set.insert (*a);
delete a;

I gave the example with int, but my real program uses classes that I created.

hivert
  • 10,579
  • 3
  • 31
  • 56
SIMEL
  • 8,745
  • 28
  • 84
  • 130

4 Answers4

14

All STL containers store a copy of the inserted data. Look here in section "Description" in the third paragraph: A Container (and std::set models a Container) owns its elements. And for more details look at the following footnote [1]. In particular for the std::set look here under the section "Type requirements". The Key must be Assignable.

Apart from that you can test this easily:

struct tester {
  tester(int value) : value(value) { }
  tester(const tester& t) : value(t.value) {
    std::cout << "Copy construction!" << std::endl;
  }
  int value;
};

// In order to use tester with a set:
bool operator < (const tester& t, const tester& t2) {
  return t.value < t2.value;
}

int main() {
    tester t(2);

    std::vector<tester> v;
    v.push_back(t);

    std::set<tester> s;
    s.insert(t);
}

You'll always see Copy construction!.

If you really want to store something like a reference to an object you either can store pointers to these objects:

tester* t = new tester(10);
{
    std::set<tester*> s;
    s.insert(t);
    // do something awesome with s
} // here s goes out of scope just as well the contained objects
  // i.e. the *pointers* to tester objects. The referenced objects
  // still exist and thus we must delete them at the end of the day:
delete t;

But in this case you have to take care of deleting the objects correctly and this is sometimes very difficult. For example exceptions can change the path of execution dramatically and you never reach the right delete.

Or you can use smart pointers like boost::shared_ptr:

{
    std::set< boost::shared_ptr<tester> > s;
    s.insert(boost::shared_ptr<tester>(new tester(20)));
    // do something awesome with your set
} // here s goes out of scope and destructs all its contents,
  // i.e. the smart_ptr<tester> objects. But this doesn't mean
  // the referenced objects will be deleted.

Now the smart pointers takes care for you and delete their referenced objects at the right time. If you copied one of the inserted smart pointers and transfered it somewhere else the commonly referenced object won't be delete until the last smart pointer referencing this object goes out of scope.

Oh and by the way: Never use std::auto_ptrs as elements in the standard containers. Their strange copy semantics aren't compatible with the way the containers are storing and managing their data and how the standard algorithms are manipulating them. I'm sure there are many questions here on StackOverflow concerning this precarious issue.

phlipsy
  • 2,899
  • 1
  • 21
  • 37
  • Comprehensive answer! Perhaps, worth mentioning about Boost Pointer Container. `boost::ptr_set< tester > s;` `s.insert( new tester(20) );` – Oleg Svechkarenko Feb 25 '11 at 23:36
1

std::set will copy the element you insert.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • I can't find a reference to back this up in the standard. There's the signature of insert and there's table 69, but neither of these say anything about `std::set` being obligated to copy the element. I'm sure `std::set` _has_ to copy the element for correctness, but I haven't yet found a reference to back this up. :-S – wilhelmtell Feb 25 '11 at 21:49
  • That said, you didn't post you "real" code so I don't know if it's analogous, but don't use the free store if you don't have to. It's lots of work, it's expensive (yes, the free store isn't free) and it bears pitfalls. – wilhelmtell Feb 25 '11 at 21:54
0

You are saving pointers into the set.

The object pointed at by the pointer is not copied.
Thus after calling delete the pointer in the set is invalid.

Note: You probably want to just save integers.

int a(1);
set<int>  s;
s.insert(a); // pushes 1 into the set
s.insert(2); // pushes 2 into the set.

Couple of other notes:

  • Be careful with underscores at the beginning of identifier names.
  • Use smart pointers to hold pointers.

Ptr:

 std::auto_ptr<int>  a(new int(1));
 set<int*>           s;
 s.insert(a.release());

 // Note. Set now holds a RAW pointer that you should delete before the set goes away.
 //       Or convert into a boost::ptr_set<int> so it takes ownership of the pointer.
Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 1
    Just as an extra note, DO NOT put `auto_ptr`'s into an STL container :) (As the example shows, call release when placing in container) – pstrjds Feb 25 '11 at 21:49
  • He _is_ saving integers. He's dereferencing the pointer upon insert. You're spot on though concerning pointers, and given his confusion with his `new` calls, that may be the question he was actually trying to ask. – Karl Bielefeldt Feb 25 '11 at 21:49
  • How does the `auto_ptr` help here? – wilhelmtell Feb 25 '11 at 21:50
  • 1
    I don't like RAW pointers. If the creation of s fails (because of some exception) my a object will be correctly deleted. – Martin York Feb 26 '11 at 01:56
  • Agreed in principle about the raw pointers. Although, if you fail creating a simple set on the stack, you probably have bigger fish to fry than a 4 byte memory leak. – Karl Bielefeldt Feb 26 '11 at 09:16
  • @Karl Bielefeldt: If you stick to your principles by habit. Then bad things will not happen as often. – Martin York Feb 26 '11 at 10:10
0
int *a;     
*a=new int(1);

This code is wrong because you try to use the value stored at address a which is a garbage.

And, every stl containers copy elements unless you use move semantics with insert() and push_back() taking rvalue references in C++0x.

young
  • 2,163
  • 12
  • 19