I'm implementing a custom map class similar to the one from std, however I have one problem.In their map, when you do(for instance):
map<string, SomeObject*> container;
SomeObject* object = container["this doesn't exist"];
in the debugger object is 0x0000000, so invalid values from map are always null.However, in my map invalid values are like uninitialized pointers - they have an invalid value like 0xcdcdcdcd, however in Release mode its something else, so I cant rely to check for
if(object == 0xcdcdcdcd)
So I would like to be able to do this:
MyMap<string, SomeObject*> container;
SomeObject* object = container["this doesn't exist"]; //and for this to definetely be nullptr
so I can then do
if(object == nullptr)
DoSomething();
I have a bool ContainsKey(KeyType key); function, but it involves a for loop.Is there some way to ensure what I want in initialization-time for the map, or do I have to make a custom PointerWrapper that will contain a pointer of SomeObject that is set to nullptr in PointerWrapper's constructor?I had a hard time figuring out what is going on in the std headers, they have an enormous amount of macros and typedefs.
Your values (for any type) will be initialized if you explicitly construct the object
SomeObject* object = SomeObject*();
// ^^ Explicit construction
For classes, the default constructor is obviously being called.
For built-in types like ints and pointers (like SomeObject*), they will be zero-initialized (instead of uninitialized)
So, whileyou could use = NULL in your specific pointer example, syntax like this will do The Right Thing for all types.
template < typename Key, typename Value >
void MyMap<Key, Value> add_new_key( const Key &k )
{
std::pair<Key, Value>( k, Value() );
// ^^ Either calls constructor or zero-initializes
// Store the data as you wish...
}
std::map value initializes any new values it creates internally. In the case of scalar types, this means they get initialized to 0.
with std::map when you refer to an entry that doesnt exist it creates one and sets its value to be empty. For a pointer that means setting the value to null;
Related
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'm designing a class for my application that implements a lot of standard shared pointers and usage of standard containers such as std::map and std::vector
It's very specific question to the problem so I just copied a piece of code
from my header for clarification purposes..
here is a snapshot of that declarations from the header:
struct Drag;
std::map<short, std::shared_ptr<Drag>> m_drag;
typedef sigc::signal<void, Drag&> signal_bet;
inline signal_bet signal_right_top();
and here is one of the functions that uses the above declarations and a temporary shared_ptr which is intended to be used not only in this function but until some late time. that means after the function returns a shared pointer should be still alive because it will be assigned at some point to another shared_ptr.
void Table::Field::on_signal_left_top(Drag& drag)
{
m_drag.insert(std::make_pair(drag.id, std::make_shared<Drag>(this))); // THIS!
auto iter = m_drag.find(drag.id);
*iter->second = drag;
iter->second->cx = 0 - iter->second->tx;
iter->second->cy = 0 - iter->second->ty;
invalidate_window();
}
the above function first insert a new shared_ptr and then assigns the values from one object into another,
What I need from your answer is to tell whether is it safe to insert temporary shared_ptr into the map and be sure that it will not be a dangling or what ever bad thing.
According to THIS website the above function is not considered safe because it would much better to write it like so:
void Table::Field::on_signal_left_top(Drag& drag)
{
std::shared_ptr pointer = std::make_shared<Drag>(this);
m_drag.insert(std::make_pair(drag.id, pointer));
auto iter = m_drag.find(drag.id);
*iter->second = drag;
// etc...
}
well one line more in the function.
is it really required to type it like that and why ?
There's no difference between the two functions in regard to the std::shared_ptr, because the std::make_pair function will create a copy of the temporary object before the temporary object is destructed. That copy will in turn be copied into the std::map, and will then itself be destructed, leaving you with a copy-of-a-copy in the map. But because the two other objects have been destructed, the reference count of the object in the map will still be one.
As for handling the return value from insert, it's very simple:
auto result = m_drag.insert(...);
if (!result.second)
{
std::cerr << "Could not insert value\n";
return;
}
auto iter = result.first;
...
The code in the example given is different from your example code, because it is using the new operator instead of std::make_shared. The key part of their advice is here:
Since function arguments are evaluated in unspecified order, it is possible for new int(2) to be evaluated first, g() second, and we may never get to the shared_ptr constructor if g throws an exception.
std::make_shared eliminates this problem - any dynamic memory allocated while constructing an object within std::make_shared will be de-allocated if anything throws. You won't need to worry about temporary std::shared_ptrs in this case.
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);
I'd have to say I'm no expert on using the STL. Here's my problem, I have a class Called LdapClientManager which maintains a number of LDAP clients that are managed by ID. The container holding the LdapClients is declared as a member variable i.e.
typedef std::map<int, LdapClient *> LdapClientMap;
LdapClientMap _ldapClientMap;
The following function fails to compile with the error:
LdapClient * LdapClientManager::getLdapClient(unsigned int templateID)
{
// Do we have an LdapClient
LdapClientMap::const_iterator it = _ldapClientMap.find(templateID);
if (it == std::map::end) {
// no existing client, lets create it
LdapClient * ldapClient = new LdapClient();
if (ldapClient == NULL) {
// TODO: handle out of memory condition
}
_ldapClientMap[templateID] = ldapClient;
return ldapClient;
}
return it->second;
}
Unfortunately I get the following error at compile time, what does it mean. I haven't found a solution in google as yet.
LdapClientManager.cc: In member function LdapClient*
LdapClientManager::getLdapClient(unsigned int)':
LdapClientManager.cc:33:template class std::map' used without template parameters
Replace std::map::end with _ldapClientMap.end().
Also, new never returns 0, it throws an exception if the allocation fails.
Note that the program can be made much shorter.
LdapClient * LdapClientManager::getLdapClient(unsigned int templateID)
{
LdapClient *& value = _ldapClientMap[templateID];
if (value == 0)
value = new LdapClient();
return value;
}
It means exactly what it says it means. std::map is a class template. It is not a class in and of itself. It needs template parameters, like you used when you defined the LdapClientMap type. Later, you say std::map::end, and the compiler says that needs parameters, too.
But you probably meant _ldapClientMap.end(). Each map has its own end; end is not a static function, so you need to call it on an instance. If it were static, you would have needed to provide template parameters, just like when you defined the type: std::map<int, LdapClient*>::end.
std::map::end() is a member function of the container instance and not a universal value, so you'll need to check the result of std::map::find() against _ldapClientMap.end().
Another couple of suggestions to improve the code:
Standard C++ containers have value semantics (they want to store the actual object and not a pointer to the object). If you really need to store pointers to LdapClients instead of the LdapClient objects themselves, I would strongly recommend wrapping them in an appropriate smart pointer like boost::shared_ptr (not std::auto_ptr, which will not work). This way, the automatic memory management of the std::map will still work and destroy the objects as intended. If you don't want to use a smart pointer or put the actual LdapClient object into the container, you will have to manually manage the objects' lifetime and call delete when appropriate to prevent memory leaks. My preference would be to change the type of the map to std::map unless the LdapClient objects are polymorphic.
Unless you are using a very out of date compiler, checking the result of regular new() against 0 or NULL will not yield any new insights as new throws a std::bad_alloc these days when it can't allocated memory for whatever reason.
Instead of using _ldapClientMap[x] = y; to insert a new element, I would use _ldapClientMap.insert(LdapClientMap::value_type(x,y)) as the latter will not overwrite an existing value for key x (which the former will do) and will return 'false' in case the key already exists in the map. That is of course if that is your intention.
LdapClientMap _ldapClientMap;
You should avoid using names with a leading underscore. Technically it is undefined behavior, even if the compiler allows it because by using it you conflict with current or future reserved names.
I have code like this:
class MapIndex
{
private:
typedef std::map<std::string, MapIndex*> Container;
Container mapM;
public:
void add(std::list<std::string>& values)
{
if (values.empty()) // sanity check
return;
std::string s(*(values.begin()));
values.erase(values.begin());
if (values.empty())
return;
MapIndex *&mi = mapM[s]; // <- question about this line
if (!mi)
mi = new MapIndex();
mi->add(values);
}
}
The main concern I have is whether the mapM[s] expression would return reference to NULL pointer if new item is added to the map?
The SGI docs say this: data_type& operator[](const key_type& k)
Returns a reference to the object that is associated with a particular key. If the map does not already contain such an object, operator[] inserts the default object data_type().
So, my question is whether the insertion of default object data_type() will create a NULL pointer, or it could create an invalid pointer pointing somewhere in the memory?
It'll create a NULL (0) pointer, which is an invalid pointer anyway :)
Yes it should be a zero (NULL) pointer as stl containers will default initialise objects when they aren't explicitly stored (ie accessing a non-existant key in a map as you are doing or resizing a vector to a larger size).
C++ Standard, 8.5 paragraph 5 states:
To default-initialize an object of
type T means:
If T is a non-POD class type (clause class), the default
constructor for T is called (and the
initialization is ill-formed if T has
no accessible default constructor)
If T is an array type, each element is default-initialized
Otherwise, the storage for the object iszero-initialized.
You should also note that default initialisation is different to simply ommiting the constructor. When you omit the constructor and simply declare a simple type you will get an indeterminate value.
int a; // not default constructed, will have random data
int b = int(); // will be initialised to zero
UPDATE: I completed my program and that very line I was asking about is causing it to crash sometimes, but at a later stage. The problem is that I'm creating a new object without changing the pointer stored in std::map. What is really needed is either reference or pointer to that pointer.
MapIndex *mi = mapM[s]; // <- question about this line
if (!mi)
mi = new MapIndex();
mi->add(values);
should be changed to:
MapIndex* &mi = mapM[s]; // <- question about this line
if (!mi)
mi = new MapIndex();
mi->add(values);
I'm surprised nobody noticed this.
The expression data_type() value-initializes an object. For a class type with a default constructor, it is invoked; if it doesn’t exist (or is defaulted), such as pointers, the object is zero-initialized.
So yes, you can rely on your map creating a NULL pointer.
Not sure about the crash, but there's definitely a memory leak as this statement:
if (!mi)
mi = new MapIndex();
always returns true, because pointer mi is not a reference to to what mapM is holding for a particular value of s.
I would also avoid using regular pointers and use boost::shared_ptr or some
other pointer that releases memory when destroyed. This allows you to call mapM.clear() or erase(), which should call destructors of keys and values stored in the map. Well, if the value is POD such as your pointer then no destructor is called therefor unless manually deleted, while iterating through a whole map will lead to memory leaks.