D R
D R

Reputation: 22500

RAII and assignment

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?

Upvotes: 3

Views: 714

Answers (3)

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 248199

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

Upvotes: 0

Georg Fritzsche
Georg Fritzsche

Reputation: 99074

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);
}

Upvotes: 9

Khaled Alshaya
Khaled Alshaya

Reputation: 96889

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.

Upvotes: 3

Related Questions