I have objects of type MyClass stored as pairs <std::string, MyClass> in an STL Map. The std::string is a unique name for each MyClass object. I want every MyClass object to be instantiated only ONCE per name and thus destroyed only once at the end in my application. So I try to avoid invocation of copy constructors or default constructors, as they might invoke destruction. A MyClass object refers to some kind of ressource that shall be allocated/freed only once. I tried to use this code to create instances of MyClass, put them in my map and give a pointer to the just created instance back.
MyClass* FooClass::GetItem(std::string name)
{
MyClass* item = GetItemExists(name);
if (item == NULL)
{
item = &(*((this->myMap.insert(std::pair<std::string, MyClass>
(name, MyClass(name)))).first)).second;
}
return item;
}
Creation and insertion works this way. But the destructor of Class MyClass is called 3! times. Even the return item; statement invokes the destructor, as this is a pointer?! I thought this is impossible and must be forced by delete item?!
I thought an alternative is to store pointers MyClass* instead of objects in the map. Or is there a better alternative? I did not use myMap[name] = MyClass(name); to avoid copy/destruction, but I think insert doesnt make it better.
You need to emplace and piecewise construct the inserted element:
item = &(this->myMap.emplace(std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(name)).first->second);
Related
I have one doubt, i have a map which stores class object against some arbitrary strings now when i delete any element from map (using erase/remove api) will the destructor of class object stored in that element be called ?
Also When i insert the class object is my map against a string value, the ctor of class will be called and a copy of object will be created in map. Is my understanding correct here?
Any links explaining these scenarios would be helpful.
Will below code call copy constructor of class Myclass? I tried putting in a cout in MyClass copy ctor but didn't see it in output.
Note : The objects are stored by value in map.
QMap<QString, MyClass> testMap;
MyClass obj;
testMap.insert("test", obj);
testMap.remove("test");
As your objects are stored by value, you will store new instances in the map. That means that a ctor will be called at insertion time. On most insertions, a copy/move ctor will be used, but a different ctor can be selected with the emplace... methods. And the default ctor is used when you create default values in a vector by giving it an initial size or by extending it.
The same, when an object is removed or erased from the map, it is destroyed so its dtor will be called. No special magic about that: the usual rules for object lifetime are applied here.
I have a program that reads lines from a file looking like this:
...
name1 (123)
name2 (345)
...
It then stores each line as an object of MyClass in a map called namelist. The key of namelist is the name of the object the value is the object itself. In the first version of the code namelist was of type map< string,MyClass >, and the objects were created in the loop as MyClass obj; (and ofc all '->' were '.'. But that only created a map of equal objects, why? I've read that you rarely need to use 'new' on objects in c++. Why do i need it here?
ifstream myfile ("input.txt");
map<string,MyClass*> namelist;
string line;
while ( getline(myfile,line) ) {
istringstream iss(line);
string word;
while (iss>>word) {
MyClass* obj = new MyClass;
obj->name = word;
iss>>word;
sscanf(word.c_str(),"(%d)",&obj->number);
namelist.insert( pair<string,MyClass*>(obj->name,obj) );
}
}
myfile.close();
You don't need new. Just do map<string,MyClass> namelist and MyClass obj; (default constructor w/ stack allocation)
new is for allocating objects on the heap and the data must be explicitly deallocated with delete. Instead, simply make your map hold MyClass objects instead of pointers to such objects. The map object will copy the object onto the heap and take care of deallocation for you.
.
You don't need new as std::map already takes care of dynamic allocation.
std::map<std::string, MyClass> namelist;
You don't even need to create a temporary variable first and insert that into the map, which creates an unneccessary copy. Instead, creating the object directly within the map will be more efficient.
The following technique requires MyClass to have a default constructor only. Apart from being more efficient, this technique also enables you to use classes that do not have a copy constructor (e. g. std::ifstream) as map values.
To get the same semantics as std::map::insert() an explicit call to find() is required. If you can live with the fact that duplicate keys overwrite existing objects in the map, you can simplify the code by removing the if.
// Check if the key does not exist yet.
if (namelist.find(word) == namelist.end()){
// Default-construct object within map and get a reference to it.
MyClass& obj = namelist[word];
// Do something with inserted obj...
obj.name = word;
}
If your class has a constructor with one or more parameters, that you would like to call during in-place construction, you need a C++11 feature, namely std::map::emplace().
// Construct MyClass within the map, if the key does not exist yet.
// Pass arguments arg1, arg2, argN to the constructor of MyClass.
auto res = namelist.emplace(arg1, arg2, argN);
if (res.second){ // new object inserted?
// Get a reference to the inserted object.
MyClass& obj = *res.first;
// Do something with inserted obj...
obj.name = word;
}
I have a priority queue inside my class, like this:
class Foo
{
public:
//public methods...
private:
std::priority_queue<Obj, std::vector<Obj>, object_less> foo_queue;
//private methods and members...
}
And I've been using the emplace() method to insert objects inside my priority_queue, like this:
void Foo::add( ... ) {
foo_queue.emplace(var1, var2);
}
And that will call the constructor of Obj(var1,var2) and insert it into the priority queue.
But now, I need to have access to the std::vector<Obj> from outside. From my Obj objects.
Something like creating an Foo object, and changing member that lives inside an object on the priority_queue:
Foo myFoo; // <-- this is where the priority_queue is!
Obj myObj(1); //Creating an object that has some member with value '1'
myFoo.add(myObj); //This will add the object to the priority_queue via emplace (actually it is creating a new object...and not using that one)
myObj.m_member = 2; //HERE WON'T WORK!!! And now I want to change some value on my Obj to '2'. It won't work, because the object that lives inside the priority_queue is different from this one!
So, I was thinking of :
Instead of using the emplace method, use the push (maybe the push won't create a new object)
Changing the priority_queue to, instead of having a vector of objects std::vector<Obj> having a vector of shared pointers, so I can have access to the Obj that it is inside of the priority_queue from outside..as shown above.
QUESTION:
Do you think this is a good idea? I'm a newbie in smart_pointers.. I don't know if there is an easier solution for this.
How can I use a priority_queue with a vector of shared_pointers?
Anyone knows a simple example I can follow?
Something like this:
std::priority_queue<std::shared_ptr<Obj>, std::vector<std::shared_ptr<Obj>>, object_less> foo_queue;
And then hopefully, I can execute:
Foo myFoo;
Obj myObj(1);
myFoo.add(myObj);
myObj.m_member = 2; //<--Now the m_member should be 2 inside the priority_queue.. is this "possible"?
There's nothing wrong with having a priority queue of shared_ptrs. The one thing you'll need to look out for is the comparison. priority_queue needs a comparison, of course, and by default it uses operator <. But that operator, on shared_ptrs, compares the pointers themselves instead of the pointed-to objects. You'll need to pass a custom comparator to the priority queue which operates on the objects themselves. Luckily it looks like you're already using a custom comparator, so the compiler will yell at you if you forget to do that (though the error message may be exceedingly arcane).
One other caveat: If you modify the object in a way that affects its ordering within the priority queue, stuff will go wrong. The only way to do this through the priority_queue interface is to remove the element from the queue, change it, and re-add it.
I have two questions. First I have an std::unordered_map<int,Object*> where Object looks like this:
// Object.hpp
class Object {
public:
Object();
~Object(){};
int Id();
void setId(int i);
private:
int id;
};
This unordered_map is put in a class called DataSet which is used as a container to hold all of these objects. In DataSet I want to be able to properly erase a key/value pair given an integer id from Object.
To do this I tried to create an iterator that finds the key by id, and then deletes the pointer to the iterator, and finally erases the key/value pair. It looks like this:
int DataSet::deleteObject(int id)
{
std::unordered_map<int,Object*>::iterator found_it = objects.find(id);
if(found_it != objects.end()) {
delete *found_it;
objects.erase(found_it);
}
return 1;
}
However, when I compile it I get this error:
dataset.cpp:44:9: error: cannot delete expression of type 'value_type'
(aka 'pair<key_type, mapped_type>')
delete *found_it;
Is there a correct way to erase the Object at that location?
Second, when writing a clear() method, I realize I can't just do objects.clear(), so I was wondering if there was a way to actually clear the unordered_map.
For example when I used a std::vector I was able to do this:
std::vector<Object *>::reverse_iterator object;
for(object = objects.rbegin(); object < objects.rend(); object++)
delete(*object);
objects.clear();
But that won't work now for an unordered_map, so what is the right way to do this? The requirements that I am under make it so I can't change Object, or how the unordered_map is set up.
The first part of the question is quite simple: the iterator reference the value and a given location which is of type std::pair<int const, Object*>. You can't delete such an object. You'll get the pointer using the second part of the object, e.g.:
delete found_it->second;
However, it is actually quite uncommon and error-prone to take care of this kind of maintenance. You are much better off not storing an Object* but rather a std::unique_ptr<Object>: the std::unique_ptr<Object> will automatically release the object upon destruction. That would also simplify removing an element:
int DataSet::deletObject(int id) {
return objects.erase(id);
}
When storing std::unique_ptr<Object> you'll probably need to be a bit more verbose when inserting elements into the container, though, e.g.:
objects.insert(std::make_pair(key, std::unique_ptr<Object>(ptr)));
Given that your Object class doesn't have any virtual functions you could probably just store an Object directly, though:
std::unordered_map<int, Object> objects;
Dereferencing an unordered_map::iterator produces a unordered_map::value_type&, i.e. pair<const Key, Value>&.
You want
delete found_it->second;
objects.erase(found_it);
Stepping back a bit, are you sure you even need std::unordered_map<int,Object*> instead of std::unordered_map<int,Object>? If you use the latter you wouldn't have to worry about deleteing anything.
If you do need the mapped_type be an Object* you should use std::unordered_map<int,std::unique_ptr<Object>>. Again, this makes it unnecessary to manually delete the values before erasing an entry from the map.
Also, if Object is the base class for whatever types you're going to be adding to the map, don't forget that it needs a virtual destructor.
I am using std::map to map string values to MyType *. My map declaration looks like this:
map<string, MyType *> *my_map = new map<string, MyType>;
my_map is a private member variable of one of my classes. My problem is that I am unsure of how to destroy the map. When deleteing the map, I would also like to call delete on all of the MyType * contained in the map. Here is my current destructor:
my_map->erase(my_map->begin(), my_map->end());
delete my_map;
Will this delete the pointers contained in the map, or do I need to iterate through the map to delete each pointer before calling erase?
Pointers merely point. When using raw pointers, you need to know which part of your app owns the resources that the pointers point to. If they are owned by the map, you will need to iterate over the map and call delete on each pointer before the map is destroyed. But if the map just holds pointers to objects that are owned by other parts of your code, you don't need to do anything.
A safer solution is to use shared_ptr to manage object lifetime, which will ensure that the object gets deleted properly when the last shared_ptr is destroyed. You can store shared_ptrs inside the map and if no other shared_ptr instances reference the objects within the map, the objects will be destroyed when the map is destroyed, as desired.
If you use smart pointers instead of raw pointers, everything will be cleaned up for you automatically.
// header:
using MapType = std::map<std::string, std::shared_ptr<MyType>>;
shared_ptr<MapType> my_map;
// usage:
my_map.emplace("foo", std::make_shared<MyType>());
// destructor:
MyClass::~MyClass()
{
// nothing!
}
Will this delete the pointers contained in the map [...]?
No, given the code you have provided, you will leak every member of the map.
As a rule, for every new there must be a matching delete. You have a delete for the map, but none for the elements within.
The most correct solution to this problem is to not use dynamic allocation at all. Just store MyTypes directory, if possible:
map<string, MyType>
... and instead of dynamically allocating the map itself, store that automatically:
map<string,MyType> my_map;
If automatic storage duration is not possible for some reason, then use a smart pointer for the dynamic allocations. Given a C++11 compiler, use unique_ptr (or, rarely, shared_ptr or even weak_ptr) for the elements in the map:
map<string, unique_ptr<MyType>> my_map;
(Given a C++03 compiler, use the Boost equivalents thereof.) Then when my_map is destroyed, all the elements will be deleted.
Baring all of this, if you are in a situation where none of the above will work for you (I would by highly suspect), then you will need to iterate the map youself:
struct deleter
{
template <typename T> operator() (const T& rhs) const
{
delete rhs.second;
}
};
for_each (my_map->begin(), my_map->end(), deleter());
In C++11, this could be made a lambda, something along the line of:
for_each (my_map->begin(), my_map->end(), [](auto item) -> void
{
delete item.second;
});
In modern C++, just make your life easier and use pointers only if strictly required.
You started with this code:
map<string, MyType *> *my_map = new map<string, MyType>;
The first thing you can do is to consider using a std::map instance as data member, instead of a pointer to it.
Then, if MyType is not super-expensive to copy and its instances are only owned by the map, just consider a simple map from string to MyType (instead of MyType*):
// my_map data member - no pointers --> automatically deleted in class destructor
map<string, MyType> my_map;
If you really need a map containing pointers, consider using smart pointers, like std::shared_ptr (available in C++11/14) for shared ownership, or std::unique_ptr for unique non-shared ownership.
(If you target C++98/03, an option is to use boost::shared_ptr. Since there is no move semantics, you can't have unique_ptr, which is heavily based on the move semantics feature.)
e.g.:
// Map containing _smart_ pointers
// --> default destructor is fine (no need for custom delete code)
map<string, shared_ptr<MyType>> my_map;
As you can see, using value semantics (instead of raw pointers), or smart pointers, you can simplify your code and use the automatic destruction provided by C++.