Saturday, 7 October 2017

In C++, when reassigning an object, why does the constructor fire before the destructor?

I'm a beginner in C++. I'm compiling my code with GCC
9.2.0. Recently I've stumbled upon some weird behaviour which is segfaulting
me.



I have a certain class wrapper for
Hyperscan. I've made it so it basically has the same functionality as Python's re, but
many times faster and multithreaded. I have a class regex which
I usually allocate like



auto
some_regex =
regex();


Now, the what
the constructor does, for those of you unfamiliar with Hyperscan is 2 (actually 3)
things -> first it allocated memory for the regular expression database. This is done
by calling their API, which is in C. Then, if it succeeds, you allocate scratch space.
Now, because my implementation supports multithreading, my class instance holds a member
which is a vector of scratch spaces. Nevertheless, I allocated a scratch space for each
thread I use, also with their API. What I then have are a pointer to the compiled regex
database and a vector of scratch space
pointers.




When deconstructing the
object, I have to free the database and scratch spaces (I also use the provided
functions from the API) if they're allocated. So far so good. The problem arises when I
do something like this:



auto
some_regex = regex();
some_regex =
regex(R"(\s+)");


Do
you see a problem here? Maybe I'm too much of a noob to see it, but it looks good to me.
But nope. It segfaults in the destructor while trying to free the database memory.
Debugging it, I have come across these
actions:



allocate memory for
some_regex
initialize some_regex

allocated new memory for
some_regex
initialize new some_regex
run destructor for the old
some_regex
run destructor for the new
some_regex


Basically,
the destructor for the first instance is run only after the constructor for the second
one fires. What this does is essentially making it so that the first destructor frees up
the data allocated and initialized by the second constructor instead of just cleaning
itself.



Now, things get weirder. I looked into
the addresses at hand, because I started setting the freed up pointers to nullptr - it
turns out that this
happens:




allocate
memory for some_regex
initialize some_regex
allocated new memory for
some_regex
initialize new some_regex
run destructor for the old
some_regex
database pointer is not 0, free it
set database pointer
to nullptr
run destructor for the new some_regex
database pointer is
not 0, free it (BUT THIS IS ALREADY FREED
MEMORY???)
SIGSEGV



I'm
utterly confused. Even after setting the database pointer to null explicitly, the new
one, after the constructor did its job, assigns it a new
address.



This reproduces the
behaviour:



#include

#include


mock::mock()

{

std::cout << "Starting constructor..." << std::endl;

this->test = (int*)malloc(sizeof(int));
*this->test = 0;

std::cout << "Ending constructor..." <<
std::endl;
}

mock::mock(int x)
{

std::cout << "Starting full constructor..." <<
std::endl;

this->test = (int*)malloc(sizeof(int));

*this->test = x;
std::cout << "Ending full constructor..." <<
std::endl;
}

mock::~mock()
{

std::cout << "Starting destructor...";
free(this->test);

this->test = nullptr;

std::cout << "Address of test is "
<< this->test << std::endl;
std::cout << "Ending
destructor..." <<
std::endl;
}


run
it in main like this:



auto
some_mock = mock();
some_mock = mock();
some_mock =
mock();

some_mock =
mock(3);



Expected
result:



Starting
constructor...
Ending constructor...
Starting destructor...Address
of test is 0
Ending destructor...

Starting
constructor...
Ending constructor...
Starting destructor...Address
of test is 0
Ending destructor...
Starting
constructor...
Ending constructor...
Starting destructor...Address
of test is 0
Ending destructor...
Starting full
constructor...
Ending full constructor...

Starting
destructor...Address of test is 0
Ending destructor...
Process
returned 0


Actual
result:



Starting
constructor...
Ending constructor...
Starting
constructor...

Ending constructor...
Starting
destructor...Address of test is 0
Ending destructor...
Starting
constructor...
Ending constructor...
Starting destructor...Address
of test is 0
Ending destructor...
Starting full
constructor...
Ending full constructor...
Starting
destructor...Address of test is 0

Ending
destructor...
*** Error in `/deploy/cmake-build-local-debug/CPP': double free
or corruption (fasttop): 0x0000000002027c40
***
.
.
.
Starting
destructor...
Process finished with exit code 134 (interrupted by signal 6:
SIGABRT)


Is this
intended behaviour? If so, why??? And how can I get around it (aside from using new and
deleting before reassigning)?

No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print ...