2010-07-18 17 views
3

J'ai créé la classe suivante pour une connexion sqlite3:RAII et l'affectation

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

puis on peut initialiser une connexion comme suit

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

Cependant, je voudrais pouvoir partager connexion, le stocker en tant que membre dans les classes, etc., et le problème est avec l'opérateur d'affectation par défaut operator=. Faire quelque chose comme

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

conduirait à deux appels sqlite3_close sur le pointeur de la base de données que chaque variable est hors de portée. Comment surmontez-vous cette difficulté avec RAII lorsque vous devez affecter votre ressource à une variable différente?

Répondre

9

Pour les ressources partagées, vous devrez garder une trace de leurs références, par ex. en utilisant reference counting. Une mise en œuvre est boost::shared_ptr avec un destructeur personnalisé:

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

+1 Pour mentionner le suppresseur personnalisé. – AraK

+1

ressemble à un peu de déchets dans votre réponse: la ligne ', destroy_connection())' ne semble pas appartenir. –

+0

@Ben: Oups, merci - je ne sais pas comment j'ai compris ça. –

3

Mettez la connexion dans un shared_ptr. En assignation, tout ce que vous avez à faire est d'assigner "shared_ptr" pour avoir la propriété partagée de la ressource (connection). Sinon, vous devez implémenter la propriété partagée pour votre classe qui a déjà été faite dans boost et qui est incluse dans C++ 0x.

0

Vous disposez de quatre options de base:

  • Utilisez comptage de référence, probablement par un shared_ptr. C'est le moins efficace, mais c'est la solution la plus générale.
  • Interdire les affectations. Ne permettez pas à l'utilisateur de copier l'objet SQLite. Rendre l'opérateur d'affectation et les constructeurs de copie privés. Ensuite, les utilisateurs devront passer des pointeurs ou des références à la place.
  • Laisser l'affectation "voler" la ressource. C'est ce que fait auto_ptr. a = b entraîne la propriété bb et b étant défini sur une valeur nulle, en tant qu'objet vide et inutilisable.
  • Créez une nouvelle connexion lorsqu'une copie est créée. Cela dépend de l'API sqlite fournissant les fonctionnalités nécessaires. En particulier, les requêtes et autres données spécifiques à la connexion peuvent également être copiées, ce qui peut s'avérer impossible
+1

Vous avez nommé les options, mais vous devriez également essayer de désigner celles qui sont appropriées et pourquoi. –