How to (deep)copy a map from a const object - c++

I have another problem I can't seem to solve..., or find on this site...
I have an object (called DataObject) with a map, declared as follows:
std::map<size_t, DataElement*> dataElements;
Now i have a copy function (used in the copy constructor):
void DataObject::copy(DataObject const &other) {
//here some code to clean up the old data in this object...
//copy all the elements:
size = other.getSize();
for(size_t i = 0; i < size; ++i) {
DataElement* dat = new DataElement(*other.dataElements[i]);
dataElements[i] = dat;
}
}
This doesn't compile, since dataElements[i] is not possible on a const object. How do I make a deep copy of all the elements in the map that is owned by a const object?
I know that the find() function is possible on a const map, but then how do I get to the actual object that I want to copy?

std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin();
while(it != other.dataElements.end())
{
dataElements[it->first] = new DataElement(*(it->second));
++it;
}
I'm almost positive this should work.

You need to use std::transform. This does a copy whilst also performing a function on each element. In your case a deep copy of the value.
This will therefore do as a transformer:
class DeepCopyMapPointer
{
typedef std::map<size_t, DataElement*> map_type;
typedef map_type::value_type value_type;
public:
value_type operator()( const value_type & other ) const
{
return value_type(other.first, new DataElement(*other.second) );
}
};
void DataObject::copy(DataObject const &other)
{
std::transform(other.dataElements.begin(), other.dataElements.end(),
std::inserter( dataElements, dataElements.end() ), DeepCopyMapPointer() );
}
It's not quite that simple because if you do duplicate an element and your insert fails as a result you will get a leak. You could get round that by writing your own inserter instead of std::inserter... a bit tricky but that's your next exercise.

Since your map just has integer keys from 0 to n - 1, just change your container type to a vector, and your current code should work nicely (you'll need to resize the destination container to make sure there's enough room available).
If you need to use map for some reason (existing API?), as you discovered operator[] has only a non-const version.
Instead use a const_iterator approach (upvoted and taken from #PigBen's answer):
std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin();
while(it != other.dataElements.end())
{
dataElements[it->first] = new DataElement(*(it->second));
++it;
}

Don't have much time to answer now so this will be brief. There is a copy-constructor for map, but it won't do a deep copy. You want to use iterators (map.begin(), map.end()). *Iter will give you a pair object, so you can do (*iter).first and/or (*iter).second. (Or something like that... It's been a while...)
Ref: http://www.sgi.com/tech/stl/Map.html

for (auto& kv : other.dataElements) {
dataElements[kv.first] = new DataElement(*kv.second);
}

Just one observation :- You are giving direct access to the dataElements. (other.dataElements). Keep dataElements private and then give method like GetDataElement.

Related

How to insert data into map where one parameter is an object?

I have a class Normal defined as:
class Normal
{
bool value;
float time;
public:
Normal(bool val,float time): value(val),time(time) {}
}
Also, I have declared a map variable as:
map<string,Normal> myMap;
Now I want to insert an data into this map.
Is this way of inserting correct?
Normal temp(true,45.04);
myMap.insert(pair<string,Normal>("one",temp));
or
myMap["one"]=temp;
How should i insert data into the map?
In C++03 :
myMap.insert(std::make_pair(
"one",
Normal(true, 45.04)
));
In C++11 :
m.emplace(std::piecewise_construct,
std::forward_as_tuple("one"),
std::forward_as_tuple(true, 45.04)
);
Both avoid default-constructing a key-value pair inside operator[] and then overwriting it.
Use this code
Normal *temp = new Normal(true,45.9);
mymap.insert(make_pair("one",temp));
avoid shallow copy since pointer is involved.
EDIT: Use insert function to insert data in map. Index is not the best way. specially when u r accessing
See this link for details
In STL maps, is it better to use map::insert than []?
EDIT2: For deletion,use the below code.
for(std::map<string, Normal*>::iterator itr = mymap.begin();it != mymap.end();)
{
if(it->second != NULL)
{
delete (it->second);
(it->second) = NULL;
it=mymap.erase(it);
}
else
{
++it;
}
}

C++ Sorting Objects in a vector based on member boolean

In my program, I have classes I use for handling projectiles in a game.
class Projectile
{
bool IsActive;
bool GetActive();
//....
};
class Game
{
std::vector<Projectile*> ProjectilesToUpdate;
//....
};
Of course, there is more to it than that, however I'm trying to stay relevant to my current problem.
I want to use std::sort to make it so that all projectiles where IsActive == true are at the far beginning and that any projectile which isn't active is at the very end.
How would I go about doing this?
Basically, you want to create a partition:
std::partition(std::begin(ProjectilesToUpdate),
std::end(ProjectilesToUpdate),
[](Projectile const* p) { return p->GetActive(); }
);
As for the subsidiary questions:
I had to remove the "const" part in the code to make it compile.
That's because your GetActive() method should be const:
bool GetActive() const { return IsActive; }
See Meaning of "const" last in a C++ method declaration?
how can I use this to delete every single object (and pointer to object) that is no longer needed?
You could use smart pointers (such as std::shared_ptr) and no longer care about delete. Thus you could use the Erase–remove idiom as follow:
std::vector<std::shared_ptr<Projectile>> ProjectilesToUpdate;
// :
// :
auto it = std::remove_if(
std::begin(ProjectilesToUpdate),
std::end(ProjectilesToUpdate),
[](std::shared_ptr<Projectile> const& p) { return !p->GetActive(); } // mind the negation
);
ProjectilesToUpdate.erase(it, std::end(ProjectilesToUpdate));
Related question: What is a smart pointer and when should I use one?
If you don't want to use smart pointers, you could use the returned iterator which point to the first element of the second group (i.e. the non active ones) and iterate until the end of the array:
auto begin = std::begin(ProjectilesToUpdate);
auto end = std::end(ProjectilesToUpdate);
auto start = std::partition(begin, end,
[](Projectile const* p) { return p->GetActive(); }
);
for (auto it = start; it != end; ++it) {
delete *it;
}
ProjectilesToUpdate.erase(start, end);
Note that I'm not calling erase inside the loop since it invalidates iterators.
And of course, this last solution is more complex than using smart pointers.

how check if std::map key exists for object's property update otherwise insert a new one?

in java, I sometimes do this
Map<String, POJO> objmap = new HashMap<String, POJO>();
POJO obj = null;
if ((obj = objMap.get(key)) == null) {
obj = new POJO();
objMap.put(key, obj);
}
obj.setName("something");
obj.setAddress("yeah");
What is the best practice to do similar thing in c++ with std::map?
to create a obj in map if not exist, then update its properties?
Like this:
void insert_or_update(const K & k, const T & t, std::map<K, T> & m)
{
auto p = m.insert(std::make_pair(k, t));
if (!p.second) p.first->second = t;
}
Or:
m[k] = t;
The latter requires T to be default-constructible and assignable.
In C++17 you can also say:
m.insert_or_assign(k, t);
This has fewer restrictions than the above construction and returns information on whether the insertion took place, as well as the iterator to the element.
You want to use the insert function, it returns an iterator and a boolean regarding whether a new object was inserted:
something like this:
typedef map<int,void*> M;
M m;
auto insertion = m.insert(M::value_type(0,nullptr));
if (insertion.second) {
insertion.first->second = new... (// allocate your item or whatever, this is the iterator to it)
}
You can write objmap[key] = value.
See: http://www.cplusplus.com/reference/map/map/operator[]/
std::map<std::string, POJO> mapStr2Pojo;
mapStr2Pojo["something"].setName("something");
mapStr2Pojo["something"].setAddress("yeah");
std::map<>'s operation[] inserts the object if it doesn't find it.
the insertion operation checks whether each inserted element has a key equivalent to the one of an element already in the container, and if so, the element is not inserted, returning an iterator to this existing element
if ( !myMap.insert( std::make_pair( key, value ) ).second )
{
// Element already present...
}

Inserting values from Vector to Map

i got some issues trying to put the values of my vector in a new map (maMap)
If someone could explain me what contain my ItemIterator or how to do...
map<std::string,Employee*> Entreprise::convertiVectorMap() const
{
map<std::string,Employee*> maMap;
vector<Employee*>::const_iterator ItemIterator;
for(ItemIterator = vector_employe.begin(); ItemIterator != vector_employe.end(); ItemIterator++)
{
maMap.insert(std::pair<string,Employee*>(ItemIterator->getNom(),ItemIterator));
}
}
Your map is of <std::string, Employee*>, but you are trying to add an iterator as the second element of the pair. You need to dereference the iterator to get the Employee pointer.
maMap.insert(std::pair<string,Employee*>((*ItemIterator)->getNom(), *ItemIterator));
Or to save from dereferencing the same iterator twice, you could just use a range based for loop. As #CaptainObvlious mentions, you can also use std::make_pair to add to your map.
for(auto const employee: vector_employe)
{
maMap.insert(std::make_pair(employee->getNom(), employee));
}
You forgot to derefrence your iterator:
maMap.insert(std::pair<string,Employee*>((*ItemIterator)->getNom(),*ItemIterator));
And since everyone asks for a revamped version of your code here we go:
map<std::string,Employee*> Entreprise::convertiVectorMap() const
{
map<std::string,Employee*> maMap;
for(vector<Employee*>::const_iterator ItemIterator = vector_employe.cbegin(),
ItemIteratorEnd = vector_employe.cend();
ItmeIterator != ItemIteratorEnd; ++ItemIterator)
{
Employee* ptr = *ItemIterator;
maMap.insert(std::make_pair(ptr->getNom(),ptr));
}
}
You can also use ranged based for if you're at least in C++11.

How to implement an API for a distributed map in c++?

I am implementing a distributed map in c++ and searching for a good API design.
First and straightforward option is to make it exactly like std::map. Problem is with iterator.
IMap<std::string,Person>::iterator it;
it = map.find("sample");
if(it == map.end() ){
//NULL
}
for(it = map.begin(); it != map.end(); it++){
//iterate
}
In distributed context(at least in the one i am implementing) , there is no begin and end of the map. It is not ordered in anyway, so returning an iterator does not look like an option.
Second option is returning the value class by copy like below:
Person emptyPerson;
Person person = map.get("sample");
if(person == emptyPerson){
//NULL
}
Problem is with that NULL check looks strange. You can first ask if it is available and then get the object, but the requirement is that these operations must be atomic.
Third option is returning pointer:
Person* person = map.get("sample");
if(person == NULL){
//NULL
}
I don't want to do it this way, because it is error prone. User needs to delete the pointer that i created internally.
I am thinking about returning a class that wrapping user object like:
value_reference<std::map, Person> person = map.get("sample");
if(value_reference.hasValue() ){
Person p = value_reference;
}
So what do you think the best approach is?
Do you know any good api similar to requirements my distributed map?
Based on your term "distributed map" I am making the following assumptions:
A subset of the data is available locally, and for the set of data that is not some remote-fetch will need to be performed.
Writes to the returned object should not be automatically persisted in the data store. An explicit update request should be made instead.
If this is true then iterators are not what you want, nor do you want the STL container model. The C++ Iterator concept requires you to implement the pre-increment (++i) operator, and if your data is unordered and spread across multiple nodes, then the request "give me the next entry" does not make sense.
You could create a terrible kludge if you wanted to simulate STL containers and iterators for interoperability reasons: have the map's end() method return a sentinel iterator instance, and have operator++() for your iterators return this same sentinel. Effectively, every iterator would point to "the last element in the map." I would strongly advise against taking this approach unless it becomes necessary, and I don't think it will be.
It sounds like what you want is a simple CRUD model, where updates must be explicitly requested. In that case, your API would look something like:
template <typename TKey, typename TValue>
class IMap<TKey, TValue>
{
public:
void create(TKey const & key, TValue const & value) = 0;
std::unique_ptr<TValue> retrieve(TKey const & key) = 0;
bool update(TKey const & key, TValue const & value) = 0;
bool remove(TKey const & key) = 0;
};
In the retrieve case, you would simply return a null pointer as you suggested. std::unique_ptr<> will ensure that the caller will either delete the allocated object or explicitly take ownership of it.
An alternative to the "return pointer to newly-allocated object" case would be to let the caller pass in a reference, and the method would return true if the value was found in the map. This will, for example, let the caller retrieve an object directly into an array slot or other local structure without the need for an intermediary heap allocation.
bool retrieve(TKey const & key, TValue & value) = 0;
Use of this method would look something like:
Person person;
if (map.retrieve("sample", person)) {
std::cout << "Found person: " << person << std::endl;
} else {
std::cout << "Did not find person." << std::endl;
}
You could provide both overloads too, and the one returning a pointer can be implemented in terms of the other by default:
template <typename TKey, typename TValue>
std::unique_ptr<TValue> IMap<TKey, TValue>::retrieve(TKey const & key)
{
TValue v;
return std::unique_ptr<TValue>(retrieve(key, v) ? new TValue(v) : nullptr);
}
I'd say something like option 3 is best. You could just emulate it using one of the standard smart pointer types introduced in C++11, so you still create a pointer, but the user doesn't have to free it. So something like:
std::unqiue_ptr<Person> person = map.get("sample");
if(person) {
person->makeMeASandwitch();
}