I’ve been working on implementing a new feature in Couchbase Lite regarding TTL (Time To Live). Basically, it allows you to specify an expiration date for a document, after which it becomes invalid and purged from the database automatically at a later time. I’m in charge of the reference implementation of this feature (meaning that the other two languages we have libraries for will refer to my implementation when implementing theirs). As such, I am also working on some C / C++ code in CBForest to support the ForestDB implementation.
When moving back to C++ in particular, the difference between stack and heap allocation becomes very pertinent. In C#, when you want to create a new instance of a class you must do so with the new operator but in C++ you have another choice. You can simply declare a class like this
If you are familiar with C++, you will know that this creates an instance of ClassName on the stack (using a constructor that takes the parameter stored in bar). When the context of this variable exits, the destructor is automatically called and the memory it uses becomes invalid. This is why it is a bad idea to return a variable allocated this way from a function (it becomes invalid as soon as you return it). However, the alternative was so cumbersome that a new feature was added to C++ called return value optimization. This means that when you return a stack variable, it is copied (or moved, if possible) up to the next stack frame automatically instead of becoming invalid.
However as it turns out the return value of the variable much match the variable being stored in or this falls apart (or so it seems to me). For example, I have a class B which inherits from A and a function that returns B. If I do something like this:
A foo = ReturnsB();
Then the process becomes invalid again, presumably because of where the copy takes place. It seems to me that the flow is this:
- Copy the value for return
- Call the copy constructor to cast to the proper value?
- Delete the original value
In my case this involved freeing shared memory between these two temporary objects and the resulting return value was corrupt. So if you notice odd behavior from your return values, check that the return types match exactly and see if that fixes things.