Destructor call when an object is removed/erased from a map - c++

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.

Related

What is the correct way to create an object and push it in a vector?

What is the correct way to add an object to a vector ? It seems that a vector takes a copy of the object, rather than the actual object...
For example:
class MyClass{
private:
std::vector<Texture>_textures;
public:
void addATexture(int textureWidth,int textureHeight){
Texture tex(textureWidth,textureHeight);
_textures.push_back(tex);//A copy of tex is pushed into in..
} // ---> At this point, tex is destroyed...
}
What is the correct way to put objects in the vector, without copies ?
If you are using C++11 or later, you may want to use emplace_back to create an object in place:
_textures.emplace_back(textureWidth, textureHeight);
If you are concerned that your object is copied when inserted into a std::vector, you may likewise be concerned that objects that are already in the vector are also copied when the vector is reallocated. You can prevent that undesired behavior in one of the following ways:
If you know the size of your collection beforehand and can postpone the creation of the object to be-inserted till right-before-insertion, then reserve() the vector and use its emplace_back() method.
Otherwise, ensure that your class provides a move constructor and an equivalent of a move-assignment operator (i.e. a proper move-assignment operator or an assignment operator that takes its argument by value):
// CAVEAT: this will compile even if Texture is not a movable type (as long
// CAVEAT: as it is copyable)
std::vector<Texture> _textures;
Texture tex(textureWidth,textureHeight);
_textures.push_back(std::move(tex));
// ^^^^^^^^^
Or store your objects in std::vector indirectly, i.e. through pointers (or, better, through std::unique_ptr):
std::vector<std::unique_ptr<Texture>> _textures;
_textures.push_back(std::make_unique<Texture>(textureWidth,textureHeight));
Using c++11, you can benefit move constructor for costly objects:
_textures.push_back(Texture(textureWidth,textureHeight));
Because the object you construct is a temporary one, it's move constructor will be called.
Another way is to call emplace_back instead of push_back:
_textures.emplace_back(textureWidth,textureHeight);
Calling push_back will cost a constructor and a move, but emplace_back will have only one constructor.
However, sometimes it's OK to have a copy. Compiler will optimize out the code if possible (But don't rely on it).
In this you can use vector of pointer to the object.
class MyClass{
private:
std::vector<Texture *> _textures;
public:
void addATexture(int textureWidth,int textureHeight){
Texture * tex = new Texture(textureWidth,textureHeight);
_textures.push_back(tex);`enter code here`
}
}
But remember you will have to de-allocate memory from all the entries of the vector.

How do I copy an instance of a class with a nested class containing a pointer member to the outer class?

Novice C++ programmer here. Let's say I have a class Outer with a nested class Inner. Inner contains a pointer member, set during construction, to Outer. Outer contains a function AddNewInner() that creates a new Inner pointing to itself and adds it to a vector.
class Outer {
public:
class Inner {
public:
Inner(Outer* outerParent) : mOuterParent(outerParent) {}
Outer* mOuterParent;
}
void AddNewInner() {
Inner newInner(this);
mInnersVec.push_back(newInner);
}
vector<Inner> mInnersVec;
}
This works fine when creating a new instance of Outer and calling AddNewInner() to add Inners to the vector. However, I have run into an issue when I try to create a copy of an instance of Outer: The Outer copy's vector of Inners do not point to the copy (itself), they still point to the original Outer.
Outer outerA;
outerA.AddNewInner();
Outer* ptrA = outerA.mInnersVec[0].mOuterParent; // this points to outerA, good!
Outer outerB = outerA;
Outer* ptrB = outerB.mInnersVec[0].mOuterParent; // this still points to outerA, bad!
I need the vector of Inners in the copy to point to the copy, not the original. What's the best way of accomplishing this, or perhaps is there an alternate way to do the same thing?
Correct, this is expected behaviour. When you create a copy of an object in C++, the compiler uses the copy constructor. If you haven't written your own copy constructor for a class, then it uses the compiler-generated copy constructor, which simply runs the (possibly generated) copy constructor for each member in turn.
So when copying an Outer, the sequence of events goes like this:
Compiler runs the (generated) copy constructor for Outer
This runs the copy constructor for std::vector. This constructor assigns the storage for the new vector, and then runs the copy constructor for each element in turn
So the (generated) copy constructor for Inner runs for each element, which just copies the member pointer (still pointing to the original Outer).
In order to update the Inner elements when copying an Outer, you need to write a custom copy constructor for Outer which updates the pointers as you'd like. Something like so:
Outer::Outer(const Outer& other)
: mInnersVec(other.mInnersVec) // Do initial vector copy
{
// Update vector elements
for (auto& elem : mInnersVec) {
elem.mOuterParent = this;
}
}
Note that whenever you write a custom copy constructor, you almost always need to write a custom assignment operator too. I'd recommend reading up on copying and assignment in your favourite C++ textbook :-).
You need to utilize a custom copy constructor/assignment operator for your class. This will allow you to make a deep copy of your Outer* mOuterParent variable; probably creating a new one.
When you copy a pointer, you are copying a variable which points to a specific address space in memory. Copying the pointer only gives you the ability to access the same 'true variable' through two 'accessor variables'.
In your example the Outer* mOuterParent variable of a particular outer object will always point to the same instantiation of an outer class pointed to by that pointer no matter how many copies of that particular object you make.

C++ inserting object with constructor into map

I was trying to methods to insert into a map, inserting a pair into a map works fine, the code looks like this:
type insert(){
map<int,MyClass> myMap;
MyClass myObj(parameter);
myMap.insert(pair<int,MyClass>(0,myObj));
...
return myMap;
}
However, I decide to use the other way, basically:
map[key] = value;
And, it look like this:
type insert(){
map<int,MyClass> myMap;
MyClass myObj(parameter);
myMap[i] = myObj;
....
return myMap;
}
compiler will give an error saying: "no matching function for call to myObj::myObj(), candidates are: " and it gives my self defined constructor.
My guess is that when using the indexing way to cast to a map, if I were to pass in an object, then it will automatically call its default constructor with no parameter. But since I have already got a self defined constructor with parameter, it will therefore give an error. Therefore, I tried creating a map in which the value is an object, and the object has only default constructor. This way, compiler is not complaining.
My problem is I don't find any document confirming my idea. If it is right, why does the map with value of object made to call the default constructor rather than existing constructor?
The problem is that std::map::operator[] will actually return a reference to the object on the given index and if there is non, default construct one. Your assignment takes place after acquiring a reference.
Use std::map::emplace to directly construct an object in place:
myMap.emplace(i, parameter);
What happens when you call
myMap[i] = myObj;
is that, if there is no element with key i, one is inserted with a value initialized (which for a user defined type means default constructed) mapped type. You then assign to it the value on the RHS of the assignment. The first part requires that the type be default constructable.
std::map requires your type must have a no-argument constructor. See here for a discussion of why.

STL map insertion copy constructor

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);

returning vector by value with multiple function nesting in C++

For some reason, I want to return an object of my::Vector (which is basically a wrapper class that internally use STL vector for actual storage plus do provide some extra functions). I return vector by value as the function creates a vector locally every time.
my::Vector<int> calcOnCPU()
{
my::Vector<int> v....
return v;
}
Now I can have multiple nesting of function calls (considering a library design), so in short something like following:
my::Vector<int> calc()
{
if(...)
return calcOnCPU();
}
AFAIK, returning by value would invoke copy constructor of my::Vector class which is something:
Vector<int>::Vector(const Vector& c)
{
....
m_vec = c.m_vec; // where m_vec is std::vector<int>
}
Few questions:
1) In copy constructor, is it invoking copy constructor of std::vector? or assignment operator and Just to confirm, std::vector creates deep copy (meaning copies all elements considering basic integer type).
2) With nesting of calcOnCPU() in calc() each returning Vector of int: 2 or 1 copies of Vector will be created? How could I avoid multiple copies in case of such simple method nesting? Inline functions or there exist another way?
UPDATE 1: It became apparent to me that I need to keep my own copy constructor as there are some custom requirements. However, I did a simple test in main function:
int main() {
...
my::Vector v = calc();
std::cout<<v;
}
I put some prints using "std::cerr" in my copy constructor to see when it gets called. Interestingly, it is not called even once for above program (atleast nothing gets printed). Is it copy ellision optimization? I am using GNU C++ compiler (g++) v4.6.3 on Linux.
In copy constructor, is it invoking copy constructor of std::vector? or assignment operator
In your case, it's creating an empty std::vector, then copy-assigning it. Using an initialiser list would copy-construct it directly, which is neater and possibly more efficient:
Vector<int>::Vector(const Vector& c) : m_vec(c.m_vec) {
....
}
Just to confirm, std::vector creates deep copy
Yes, copying a std::vector will allocate a new block of memory and copy all the elements into that.
With nesting of calcOnCPU() in calc() each returning Vector of int: 2 or 1 copies of Vector will be created?
That's up to the compiler. It should apply the "return value optimisation" (a special case of copy elision), in which case it won't create a local object and return a copy, but will create it directly in the space allocated for the returned object. There are some cases where this can't be done - if you have multiple return statements that might return one of several local objects, for example.
Also, a modern compiler will also support move semantics where, even if the copy can't be elided, the vector's contents will be moved to the returned object rather than copied; that is, they will be transferred quickly by setting the vector's internal pointers, with no memory allocation or copying of elements. However, since you're wrapping the vector in your own class, and you've declared a copy constructor, you'll have to give that class a move constructor in order for that to work - which you can only do if you're using C++11.
How could I avoid multiple copies in case of such simple method nesting? Inline functions or there exist another way?
Make sure the structure of your function is simple enough for copy elision to work. If you can, give your class a move constructor that moves the vector (or remove the copy constructor, and the assignment operator if there is one, to allow one to be implicitly generated). Inlining is unlikely to make a difference.