Solution with shared_ptr
I'm learning C++ and SQLite, so I had this question too. After reading this post, I tried some answers from it. The result is a working example and a small analysis.
- First create a custom deleter for the smart pointer.
- Then, create an empty share_ptr including the custom deleter
- Then, create an empty pointer for the DB handler (
sqlite3 * DB;
)
- Afterwards, open/create the DB.
- Link the raw pointer to the shared one.
- After the shared_ptr goes out of scope, it will delete the raw pointer too.
This is rather inefficient (see conclusion), but is the only way I manged to use smart pointers with sqlite3, so I decided to post this as an answer.
#include <iostream>
#include<sqlite3.h>
#include<memory>
//Custom deleter
auto del_sqlite3 = [](sqlite3* pSqlite)
{
std::cout << "Calling custom deleter." << std::endl;
sqlite3_close_v2(pSqlite);
};
int main()
{
//Uncomment to run
//const char* dir = "C:\\test\\db_dir\\test.db"
openOrCreateDB(dir);
return 0;
}
int openOrCreateDB(const char* dirName)
{
std::shared_ptr<sqlite3> DB(nullptr, del_sqlite3);//custom deleter
auto pDB = DB.get();
{
int exit = sqlite3_open(dirName, &pDB);
DB.reset(pDB);// Replace nullptr with pDB and link
}
return 0;
}
Why smart pointers with sqlite3?
The main reason to use a smart pointer is to automate memory management and avoid memory leaks. So, this happens if we are thinking in allocating memory on the free store, using new
and delete
.
But I failed with all my attempts to allocate a database handler in the free store.
Fail 1: using sqlite3* DB = new sqlite3;
int openOrCreateDB(const char* dirName)
{
sqlite3* DB = new sqlite3;//E0070: Incomplete type not allowed
int exit = sqlite3_open(dirName, &DB);
sqlite3_close(DB);
return 0;
}
Fail 2: using share_ptr
static int openOrCreateDB(const char* dirName)
{
std::shared_ptr<sqlite3> DB(new sqlite3, del_sqlite3);// Incomplete type not allowed
auto pDB = DB.get();
{
int exit = sqlite3_open(dirName, &pDB);
DB.reset(pDB);
}
return 0;
}
Fail 3: using make_shared
I didn't even try. In Meyers' Effective Modern C++, Item 21 it is clear that you can't use make_shared
to construct a smart pointer on the heap with the custom deleter.
Conclusion
Maybe I'm doing something wrong, but it seems that SQLite does not like to allocate database handlers (sqlite3 objects) on the heap. So why use a smart pointer anyway? Even if you allocate the db handler on the stack, smart pointers uses more memory and more lines of code.
The other reason to use smart pointers is to manage ownership. But, in sqlite3, the workflow is quite repetitive: In a routine:
- Create a DB handler.
- Open DB, execute SQL statements, etc.
- Finalize statement
- Finalize DB connection.
So I can't see why should we pass arround a DB handler outside this workflow.
My recommendation is to keep using raw pointers and destroying them with sqlite3_close(sqlite3 * ptr)
.