Reputation: 113
I've been creating a C++ desktop application that interacts with a local SQLite database. I'm using a repository pattern and storing the data retrieved from the database in an instance of a class (used as a data model). I have implemented this pattern elsewhere in my application and all works fine. However, it seems that in this particular implementation the address of the class instance (the data model) seems to change between construction and destruction.
Here is an example of how my code is setup, using memory addresses that I've logged during debug execution...
Repository Class - Constructor:
Currently, I'm having this repository class maintain control of the Configs
data model class. This could change in the future, but my current thinking is for ease of construction and destruction of this object (or so I thought!).
ConfigsRepo::ConfigsRepo()
{
this->_configs = new Models::Configs(); // Memory address: 0x039089e8
}
Repository Class - Getter Method:
I've left out the database connection and SQL statement strings because they work and are not part of the problem.
Models::Configs& ConfigsRepo::Get()
{
int rc = sqlite3_exec("..", "..", &Sql::Convert, &this->_configs, NULL);
// Check rc == SQLITE_OK, blah blah...
return *this->_configs; // Memory address: 0x03908901
}
Sql::Convert Method:
There is currently only one row and one column in the database table. Data is retrieved fine.
int Sql::Convert(void* ret, int count, char** data, char** columns)
{
// "ret" is a reference to the "Configs" class instance passed
// by the repository.
// Memory address of "ret": 0x03918930 - most likely different
// due to passing by reference.
// Converting retrieved data to a bool and assigning to a
// property of the referenced "Configs" class.
// (if cell equals 1 then 'true', else 'false')
(*(Models::Configs*)ret).SomeProp = atoi(data[0]) == 1;
return 0;
}
Repository Class - Destructor:
ConfigsRepo::~ConfigsRepo()
{
delete this->_configs; // Memory address: 0x03908901 - exception thrown
this->_configs = NULL;
}
As you can see, the memory address of the "Configs" object changes after the Sql::Convert
callback method. When destructing the ConfigsRepo
class (in which I attempt to delete this->_configs
), an exception is thrown: Invalid address specified to RtlValidateHeap
.
I'm not sure why the memory address seems to change, and I can't see the issue.
NOTE: I've come to believe that calling delete
in such a way is not great practice concerning design patterns, and I will be refactoring my code at a later date. I just need it working for now. :)
Thanks in advance!
Upvotes: 0
Views: 60
Reputation: 10655
Looking at sqlite3 documentation on sqlite3_exe(...)
:
The 4th argument to sqlite3_exec() is relayed through to the 1st argument of each callback invocation.
So, on your function:
int rc = sqlite3_exec("..", "..", &Sql::Convert, &this->_configs, NULL);
you are effectively passing a pointer of pointer, i.e, the address of this->configs, which is by itself a pointer of Models::Configs
At callback site, you address it as:
(*(Models::Configs*)ret).SomeProp = atoi(data[0]) == 1;
which is a convoluted way to say:
(Models::Configs*)ret->SomeProp = atoi(data[0]) == 1;
which you can see, you are not undoing what you have done at the call site.
So, I suggest you to change your sqlite3_exec()
too:
int rc = sqlite3_exec("..", "..", &Sql::Convert, this->_configs, NULL);
^^^^^^^^^^^^^^
Aside: You don't need to pass Sql::Convert
as &Sql::Convert
, just as Sql::Convert
Upvotes: 1