how to get stl map to construct/destruct inserted object only once
I have found a very prejudicial fact about stl maps. For some reason I cant get objects being inserted in the map to get constructed/destructed only once.
Example:
struct MyObject{
MyObject(){
cout << "constructor" << endl;
}
~MyObject(){
cout << "destructo开发者_StackOverflowr" << endl;
}
};
int main() {
std::map<int, MyObject> myObjectsMap;
myObjectsMap[0] = MyObject();
return 0;
}
returns:
constructor
destructor
destructor
constructor
destructor
If I do:
typedef std::pair<int, MyObject> MyObjectPair;
myObjectsMap.insert( MyObjectPair(0,MyObject()));
returns:
constructor
destructor
destructor
destructor
I'm inserting Objects responsible for their own memory allocation, so when destructed they'll clean themselves up, being destructed several times is causing me some trouble.
I suggest you add a copy constructor - that's what is being used for the 'missing' constructions I think.
Code:
#include <iostream>
#include <map>
using namespace std;
struct MyObject{
MyObject(){
cout << "no-arg constructor" << endl;
}
MyObject(const MyObject&) {
cout << "const copy constructor" << endl;
}
~MyObject(){
cout << "destructor" << endl;
}
};
int main() {
std::map<int, MyObject> myObjectsMap;
myObjectsMap[0] = MyObject();
return 0;
}
Output:
no-arg constructor
const copy constructor
const copy constructor
destructor
destructor
no-arg constructor
destructor
destructor
std::map
is allowed to make as many copies of your objects as it wishes. This is implementation defined and you have no control over this. The "missing" constructions you notice, by the way, may be for calling the copy-constructor, which you didn't define.
What you can do, however is use a flyweight so constructing an object in fact fetches a existing object from a pool of pre-existing objects, and destructing an object does nothing. The pool never frees its objects, but it always maintains a handle over all of them. This way, your memory usage is large from start to stop, but it doesn't change much throughout the life of the program.
To be able to be used in a standard container your objects must be copyable and assignable. If your objects don't conform to this you may are likely to have problems.
That said, if (as your sample code indicates) you just need a default constructed object inserted in the map you can just use operator[] for its side effect:
// Insert default constructed MyObject at key 0
myObjectsMap[0];
Edit
I'm not quite clear from your question, but if you're unclear about the number of objects constructed and believe there is a constructor/destructor mismatch then note that the compiler will provide a copy constructor which doesn't log to std::cout
as you don't provide a user-declared one.
When you say myObjectsMap[0], you are calling the default constructor for MyObject. This is because there is nothing in [0] yet, and you just accessed it. It's in the manual.
When you hit MyObject(); you are creating a temporary MyObject instance using the default constructor.
Because you allowed the compiler to define your copy constructor, you see more destructor than constructor messages. (There can only be one destructor, but many constructors, unlike building a house.) If you object should never be copied this way, then you likely want to declare a private copy constructor and copy assignment operator.
You're calling the default constructor and the copy constructor twice each with this code:
myObjectsMap[0] = MyObject();
When you do this:
myObjectsMap.insert( MyObjectPair(0,MyObject()));
you call the default constructor once and the copy constructor 3 times.
You should likely use pointers as map values instead of the objects themselves, in particular I suggest looking at shared_ptr.
note: tests were done using GCC 3.4.5 on a Windows NT 5.1 machine.
That's the way map
and the other containers work, you can't get around it. That's why std::auto_ptr
can't be used in a collection, for example.
精彩评论