I understand that in the C++ realm it is advocated to use smart pointers. I have a simple program as below.
/* main.cpp */
#include <iostream>
#include <memory>
using namespace std;
/* SQLite */
#include "sqlite3.h"
int main(int argc, char** argv)
{
// unique_ptr<sqlite3> db = nullptr; // Got error with this
shared_ptr<sqlite3> db = nullptr;
cout << "Database" << endl;
return 0;
}
When I compile with unique_ptr line got an error message:
error C2027: use of undefined type 'sqlite3'
error C2338: can't delete an incomplete type
When I compile with shared_ptr line it is successful. From several questions and answers my understanding is that unique_ptr should be preferred as I do not intended to have objects sharing resources. What is the best solution in this case? Use shared_ptr or go back to the old approach of bare pointers (new/delete)?
The general approach is in @SomeProgrammerDudes's answer (accept it). But to address your concerns I'm posting this.
You shouldn't go back to raw new and delete. Neither because sqlite3
is an opaque type nor because the overhead of std::shared_ptr
. You use, as the other answer specified, a std::unique_tr
.
The only difference is how you setup the custom deleter. For std::unique_ptr
it's part of the type definition, not a run-time parameter. So you need to do something like this:
struct sqlite3_deleter {
void operator()(sqlite3* sql) {
sqlite3_close_v2(sql);
}
};
using unique_sqlite3 = std::unique_ptr<sqlite3, sqlite3_deleter>;
sqlite3
is an opaque structure (much like FILE
from C). All you have is its declaration, not its definition. That means you can't use it in a std::unique_ptr
directly without a custom deleter.
#include <memory>
#include <stdexcept>
/* sqlite 3 interface */
struct sqlite3 {};
extern void sqlite3_close(sqlite3*);
extern int sqlite3_open(sqlite3**);
/* our boilerplate */
struct closer
{
void operator()(sqlite3* p) const
{
sqlite3_close(p);
}
};
using sqlite3_ptr = std::unique_ptr<sqlite3, closer>;
/* handy maker function */
sqlite3_ptr make_sqlite()
{
sqlite3* buffer = nullptr;
int err = sqlite3_open(&buffer);
if (err) {
throw std::runtime_error("failed to open sqlite");
}
return sqlite3_ptr(buffer);
}
int main()
{
auto mysqlite = make_sqlite();
}
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.
sqlite3 * DB;
)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;
}
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.
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;
}
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;
}
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 .
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:
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)
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.