views:

95

answers:

3

I created the following class for an sqlite3 connection:

class SqliteConnection
{
public:
    sqlite3* native;

    SqliteConnection (std::string path){
        sqlite3_open_v2 (path.c_str(), &native, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
    }

    ~SqliteConnection (){
        sqlite3_close(native);
    }
}

and then one can initialize a connection as follows

SqliteConnection conn("./database.db");

However, I would like to be able to share this connection, store it as a member in classes, etc., and the trouble is with the default assignment operator operator=. Doing something like

SqliteConnection conn("./database.db");
SqliteConnection conn1 = conn;

would lead to two sqlite3_close calls on the database pointer as each variable goes out of scope. How do you overcome this difficulty with RAII when you need to assign your resource to a different variable?

+3  A: 

Put the connection in a shared_ptr. On assignment all what you have to do is to assign "shared_ptr"s to have shared ownership of the resource(connection). Otherwise you have to implement shared ownership for your class which already has been done in boost and is included in C++0x.

AraK
+8  A: 

For shared resources you will have to keep track of wether references to them exist, e.g. using reference counting. One implementation is boost::shared_ptr with a custom deleter:

class SqliteConnection {
    boost::shared_ptr<sqlite3> native;
public:
    SqliteConnection(const std::string& path) 
      : native(init_connection(path), &destroy_connection)
    {}
    // ...
};

sqlite3* init_connection(const std::string& path) {
    // ...
    return ptr;
}

void destroy_connection(sqlite3* p) {
    sqlite3_close(p);
}
Georg Fritzsche
+1 For mentioning the custom deleter.
AraK
looks like some extra garbage in your answer: the line `, destroy_connection())` doesn't seem to belong.
Ben Voigt
@Ben: Oops, thanks - don't know how i got that in there.
Georg Fritzsche
A: 

You have four basic options:

  • Use reference counting, probably through a shared_ptr. This is the least efficient, but it's the most general solution.
  • Prohibit assignments. Don't allow the user to copy the SQLite object. Make the assignment operator and copy constructors private. Then users will have to pass pointers or references around instead.
  • Let assignment "steal" the resource. This is what auto_ptr does. a = b results in a taking ownership of b's connection, and b being set to a null value, as an empty, unusable object.
  • Create a new connection when a copy is created. This depends on the sqlite API providing the necessary functionality. In particular, queries and other connection-specific data may have to be copied as well, which might be impractical
jalf
You have named the options but you should also try and point which ones are appropriate and why.
Martin York