I have defined a simple class Tutu with two constructors, the default one that do nothing and a 1 parameter one that allocates and initializes a dynamic array. The destructor frees the memory of course. This code is stupid but just a simple example to show my issue.
Then if I create objects like this : Tutu t(5);, it is alright. But if I do it in two steps : Tutu t; t = Tutu(5); , it fails since the destructor is called for reasons I don't understand.
Please help me understand why the destructor is called. And how can I solve this problem.
BTW, I need the Tutu t; t = Tutu(5); form since in the real case, I declare arrays : Tutu t[1000]; for (int i = 0; i < 1000; i++) t[i] = Tutu(i);
#include <iostream>
#include <string>
#include <cstdlib>
class Tutu {
public:
int* mpCommonInts;
int mQtInts;
Tutu() {
cout << "Default constructor for Tutu called" << endl;
}
Tutu(int aQtInts) : mQtInts(aQtInts) {
cout << "Constructor Tutu(" << mQtInts << ") called" << endl;
mpCommonInts = newint[1000];
for (int i = 0; i < mQtInts;i++) mpCommonInts[i] = mQtInts * 10000 + i;
}
~Tutu() {
cout << "Destructor ~Tutu called" << endl;
delete [] mpCommonInts;
}
};
int main(int argc, constchar *argv[]) {
Tutu t;
t = Tutu(5);
cout << "Investigate unexpected call of a Tutu class destructor :";
for (int i = 0; i < t.mQtInts; i++) cout << " " << t.mpCommonInts[i];
cout << endl;
return 0;
}
$ ./Release/test
Default constructor for Tutu called
Constructor Tutu(5) called
Destructor ~Tutu called
Investigate unexpected call of a Tutu class destructor : 50000 50001 50002 50003 50004
Destructor ~Tutu called
*** glibc detected *** ./Release/test: double free or corruption (top): 0x0000000001a6b140 ***
The reason is that on line 29 you copy the pointer. So you have 2 objects the contain the pointer to the same thing. both trying to destroy that thing in their destructor which will cause the crash
There is nothing of course about it. You have an empty constructor as well, which means that an object can exist that doesn't have any "new'ed" memory allocation. What if I do
1 2
Tutu t = new Tutu;
delete t;
Now, the destructor is called, which calls delete on the allocated memory in t. But wait! We didn't allocate any memory! Aaagh!
Either do a simple check before deleting: if (myVariables != nullptr) delete[] myVariables;
Or make a "cleaning" function that deletes all allocated memory, but only if YOU explicitly say so.
The point is that I don't understand why the destructor for
mpCommonInts = 0x1d4d140
is called before the cout.
Well, on line 29 the object right of the assignment operator is created 'on the fly'. It's life ends right after the assignment is done and hence before the 'cout' and so is the destructor called
Why don't you just create t as Tutu t(5); if you don't want copying?
If you really don't want copy constructor and assignment operator to copy the data you would have to have some kind of reference counter to keep track of how may objects that are using the data. If you the data can be changed you probably also want to implement copy on write, that is if the reference counter is greater than 1, copy the data, decrement the reference counter and make the changes to the copied data. This becomes a bit complicated so I doubt it is worth it.
If you decide that copying is fine after all, I would recommend that you used a std::vector to store the data. Then you don't need to define the copy constructor, copy assignment operator or destructor because the defaults are enough.
This message was only to make the code informative when the issue did exist in the first post. As you can see in the last code, I replaced it (possibly you saw my last post before I last EDIT it. Sorry).
With huge data (several tables of millions of struct), I prefer master precisely when things are done.
Well, that's not much of a solution. You've basically said "I need to use an init function after creating my tutu to get it into the right state, and I'm co-opting operator= for that purpose."
Very fragile.
1 2 3 4 5 6 7 8
int main()
{
tutu2 t = tutu2(5) ;
tutu2 u = tutu2(6) ;
cout << "t = Tutu2(5) = "; for (int i = 0; i < t.mQtInts; i++) cout << " " << t.mpCommonInts[i]; cout << endl;
cout << "u = Tutu2(6) = "; for (int i = 0; i < u.mQtInts; i++) cout << " " << u.mpCommonInts[i]; cout << endl;
}
If that doesn't result in a program crash, I will be terribly surprised.
If you don't wish to allow copying, don't allow copying. Make your copy constructor and assignment operator private and don't implement them. Or, use the c++11 way with "= delete" if your compiler supports it.
If you are using C++11 you can make a move constructor and move assignment operator and disable the normal copy constructor and copy assignment operator (as cire mentioned). That way you can do