Appleshell
Appleshell

Reputation: 7388

Why does objects get deleted after being inserted into set?

I have set up this example:

class UsefulClass {
public: 
        int id;
        const bool operator< (const UsefulClass &other) const {
                return this->id > other.id;
        }
        UsefulClass(int _id): id(_id) {
                std::cout << "constructing " << id << std::endl;
        }
        ~UsefulClass() {
                std::cout << "destructing " << id << std::endl;
        }
};
std::set<UsefulClass> set;
void create() {
        UsefulClass object_1(1);
        UsefulClass object_2(2);
        set.insert(object_1);
        set.insert(std::move(object_2));

        std::cout << "create end" << std::endl;
}
int main() {
        create();
        std::cout << "main end" << std::endl;
}

I am expecting that the objects get destructed once when set gets deleted at the end of the program. But the objects get deleted twice:

constructing 1
constructing 2
create end
destructing 2
destructing 1
main end
destructing 1
destructing 2

Why is set.insert creating a copy here?

Upvotes: 2

Views: 233

Answers (5)

Luis Colorado
Luis Colorado

Reputation: 12668

To illustrate whatever everybody has said before me, just create this simple example (it uses a new copy constructor for the set to use and uses a global variable to generate different ids each time a constructor is executed ---it's been tested, so you can put it in a file and compile):

#include <iostream>
#include <string>
#include <set>

using namespace std;

class UsefulClass {
    static int instance;
public:
    int id;
    int i;

    const bool operator<(const UsefulClass &other) const {
        return id < other.id;
    }

    UsefulClass(int i){
        id = instance++;
        this->i = i;
        cout    << "constructing "
            << id
            << ":"
            << this->i
            << endl;
    }

    UsefulClass(const UsefulClass& other) {
        id = instance++;
        i = other.i;
        cout    << "constructing "
            << id
            << ":"
            << i
            << endl;
    }

        ~UsefulClass(){
        cout    << "destructing "
            << id
            << ":"
            << i
            << endl;
    }
};

int UsefulClass::instance = 0;

std::set<UsefulClass> myset;

void create() {
        UsefulClass object_1(1);
        UsefulClass object_2(2);
        myset.insert(object_1);
    /* please, explain what you mean with std::move, or which move
     * have you used for the next statement.  All i have is
     * std::move(... ) for strings, but you have not defined
     * string UsefulClass::operator string();
     */
        myset.insert(/*std::move*/(object_2));

        cout    << "create end"
        << endl;
}

int main() {
        create();
        cout    << "main end"
        << std::endl;
}

so you'll get a different instance id whenever you create a UsefulClass object and you'll see that when inserting into the set they are being copied as new instances. You'll see when each object is being created and when they are being deleted.

$ pru
constructing 0:1
constructing 1:2
constructing 2:1
constructing 3:2
create end
destructing 1:2
destructing 0:1
main end
destructing 2:1
destructing 3:2

Upvotes: 0

Balog Pal
Balog Pal

Reputation: 17163

Rule of 3 applies to your case, if you print from dtor and want meaningful trace you should instrument copy (and maybe move) ctor also.

If you do that output will make sense and things should be properly paired.

Upvotes: 1

Cătălin Pitiș
Cătălin Pitiș

Reputation: 14341

object_1 and object_2 are created on stack and will be destroyed once the create() function ends. They need to be copied in the memory managed by set's allocator.

If you redefine the copy constructor, to trace its execution, you'll notice it is called at both inserts.

Upvotes: 2

aschepler
aschepler

Reputation: 72346

The objects in the set are different from the objects local to create(). The ones in the set are constructed using a copy constructor and move constructor, not the constructor UsefulClass(int), so you don't see their construction. The local objects get destroyed when the function create() returns, and then the objects in the set get destroyed at global cleanup after main ends.

Upvotes: 4

arne
arne

Reputation: 4674

Because your objects get copied on insertion into the set. Hence, when the create() function returns, the two local objects are destroyed. After main ends, the two copies that are in the set are destroyed, leading to the second pair of messages.

Upvotes: 0

Related Questions