Question

J'ai lu l'article sur les gardes de portée ( générique: Changer la façon dont vous écrivez Exception-Safe code -. Pour toujours ) dans DDJ et je comprends leur utilisation commune

Cependant, l'utilisation courante consiste à instancier une protection de la pile sur la pile particulière pour une opération particulière, par exemple:.

{
    FILE* topSecret = fopen("cia.txt");
    ON_BLOCK_EXIT(std::fclose, topSecret);
    ... use topSecret ...
} // topSecret automagically closed

mais si je veux planifier les opérations de nettoyage dans l'exécution, par exemple quand j'ai une boucle:

{
   vector<FILE*> topSecretFiles;
   for (int i=0; i<numberOfFiles; ++i)
   {
      char filename[256];
      sprintf(filename, "cia%d.txt", i);
      FILE* topSecret = fopen(filename);
      topSecretFiles.push_back(topSecret);
      ON_BLOCK_EXIT(std::fclose, topSecret); // no good
   }
}

De toute évidence, l'exemple ci-dessus ne fonctionnerait pas, car topSecret serait fermé en même temps que le pour portée. Je voudrais un motif de protection de champ où je peux juste la file d'attente aussi facilement les opérations de nettoyage que je RESOLUE à être nécessaire lors de l'exécution . Y at-il quelque chose comme disponible?

Je ne peux pas pousser des objets de garde scope dans une file d'attente standard, faire l'objet original (celui que je fais pression) serait rejeté dans le processus. Que diriez-vous de pousser les gardiens de pile alloué-tas et en utilisant une file d'attente qui supprime ses membres sur dtor? Quelqu'un at-il une approche plus intelligente?

Était-ce utile?

La solution

Il semble que vous n'appréciez pas RAII pour ce qu'elle est. Ces gardes sont bien portée à l'occasion des choses locales ( « scope »), mais vous devriez essayer de les éviter en faveur de ce RAII est vraiment censé faire: encapsuler une ressource dans un objet. Le type de fichier * est vraiment pas bon à cela.

Voici une alternative:

void foo() {
    typedef std::tr1::shared_ptr<FILE> file_sptr;
    vector<file_sptr> bar;
    for (...) {
        file_sptr fsp ( std::fopen(...), std::fclose );
        bar.push_back(fsp);
    }
}

Ou:

void foo() {
    typedef std::tr1::shared_ptr<std::fstream> stream_sptr;
    vector<stream_sptr> bar;
    for (...) {
        file_sptr fsp ( new std::fstream(...) );
        bar.push_back(fsp);
    }
}

Ou dans "C ++ 0x" (future norme C ++):

void foo() {
    vector<std::fstream> bar;
    for (...) {
        // streams will become "movable"
        bar.push_back( std::fstream(...) );
    }
}

Edit: Comme j'aime les types mobiles en C ++ 0x tant et vous a montré un intérêt dans ce: Voici comment vous pouvez utiliser unique_ptr en combinaison avec le fichier * sans ref comptage frais généraux :

struct file_closer {
    void operator()(FILE* f) const { if (f) std::fclose(f); }
};

typedef std::unique_ptr<FILE,file_closer> file_handle;

file_handle source() {
    file_handle fh ( std::fopen(...) );
    return fh;
}

int sink(file_handle fh) {
    return std::fgetc( fh.get() );
}

int main() {
    return sink( source() );
}

(non testé)

Assurez-vous de consulter blog de Dave sur les types de valeurs mobiles efficaces

Autres conseils

Huh, se la garde de portée DDJ est « mobile », pas dans le sens C ++ 0x, mais dans le même sens qu'un auto_ptr est mobile: au cours de la ctor copie, la nouvelle garde « rejette » la vieille garde (comme le ctor copie de auto_ptr appelle l'ancien de :: auto_ptr version ).

Je peux simplement garder un queue<ScopeGuard> et il va travailler:

queue<ScopeGuard> scopeGuards;

// ...

for (...)
{
   // the temporary scopeguard is being neutralized when copied into the queue,
   // so it won't cause a double call of cleanupFunc
   scopeGuards.push_back(MakeScopeGuard(cleanupFunc, arg1));
   // ...
}

Par ailleurs, je vous remercie pour la réponse ci-dessus. Il était instructif et éducatif pour moi de différentes manières.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top