Question

Lors du débogage d'accident dans une application multithread je fini par trouver le problème dans cette déclaration:

CSingleLock(&m_criticalSection, TRUE);

Notez que cela crée un objet sans nom de classe CSingleLock et par conséquent l'objet section critique est déverrouillé immédiatement après cette déclaration. Ceci est évidemment pas ce que le codeur voulait. Cette erreur a été causée par une simple erreur de frappe. Ma question est, est-il someway je peux empêcher l'objet temporaire d'une classe en cours de création au moment de la compilation elle-même à savoir le type de code ci-dessus devrait générer une erreur de compilation. En général, je pense que chaque fois qu'une classe essaie de faire une sorte d'acquisition de ressources alors l'objet temporaire de cette classe ne doit pas être autorisée. Est-il possible de l'appliquer?

Était-ce utile?

La solution

Edit:. Comme notes j_random_hacker, il est possible de forcer l'utilisateur à déclarer un objet nommé afin de prendre un verrou

Cependant, même si la création d'une certaine façon a été interdite temporaires pour votre classe, l'utilisateur peut faire une erreur similaire:

// take out a lock:
if (m_multiThreaded)
{
    CSingleLock c(&m_criticalSection, TRUE);
}

// do other stuff, assuming lock is held

En fin de compte, l'utilisateur doit comprendre l'impact d'une ligne de code qu'ils écrivent. Dans ce cas, ils doivent savoir qu'ils créer un objet et ils doivent savoir combien de temps il dure.

Une autre erreur probable:

 CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE);

 // do other stuff, don't call delete on c...

qui vous amène à nous demander « Est-il possible que je peux arrêter l'utilisateur de ma classe d'allouer sur le tas »? Pour que la réponse serait la même.

En C ++ 0x, il y aura une autre façon de faire tout cela, à l'aide lambdas. Définir une fonction:

template <class TLock, class TLockedOperation>
void WithLock(TLock *lock, const TLockedOperation &op)
{
    CSingleLock c(lock, TRUE);
    op();
}

Cette fonction capture l'utilisation correcte de CSingleLock. Maintenant, permettent aux utilisateurs de faire ceci:

WithLock(&m_criticalSection, 
[&] {
        // do stuff, lock is held in this context.
    });

Ceci est beaucoup plus difficile pour l'utilisateur de bousiller. La syntaxe fait un peu bizarre au début, mais [&] suivi d'un bloc de code signifie « Définir une fonction qui ne prend pas args, et si je me réfère à quoi que ce soit par son nom et il est le nom de quelque chose à l'extérieur (par exemple, une variable locale dans le contenant fonction) me permet d'accéder par référence non-const, donc je peux le modifier.)

Autres conseils

Tout d'abord, Earwicker fait quelques bons points - vous ne pouvez pas empêcher tout mauvais usage accidentel de cette construction.

Mais pour votre cas, cela peut en effet être évité. C'est parce que C ++ ne fait un (étrange) distinction en ce qui concerne les objets temporaires:. fonctions gratuites ne peuvent pas prendre des références non-const à des objets temporaires Ainsi, afin d'éviter les verrous blip dans et hors de l'existence, il suffit de déplacer le code de verrouillage sur le constructeur de CSingleLock et en une fonction libre (que vous pouvez faire un ami pour éviter d'exposer les méthodes internes):

class CSingleLock {
    friend void Lock(CSingleLock& lock) {
        // Perform the actual locking here.
    }
};

Le déverrouillage est toujours réalisée dans le destructor.

Pour utiliser:

CSingleLock myLock(&m_criticalSection, TRUE);
Lock(myLock);

Oui, il est un peu plus difficile à manier à écrire. Mais maintenant, le compilateur se plaindra si vous essayez:

Lock(CSingleLock(&m_criticalSection, TRUE));   // Error! Caught at compile time.

Parce que le paramètre ref non-const de Lock() ne peut pas se lier à un temporaire.

Peut-être étonnamment, les méthodes de classe peut opérer temporaires - c'est pourquoi Lock() doit être une fonction libre. Si vous laissez tomber le spécificateur de friend et le paramètre de fonction dans l'extrait haut pour faire Lock() une méthode, le compilateur vous permettra heureusement d'écrire:

CSingleLock(&m_criticalSection, TRUE).Lock();  // Yikes!

MS COMPILER REMARQUE: MSVC ++ versions jusqu'à Visual Studio .NET 2003 fonctionne correctement autorisés à se lier à des références non-const dans les versions antérieures à VC ++ 2005. Ce comportement a été corrigé dans VC ++ 2005 et au-dessus .

Non, il n'y a aucun moyen de le faire. Cela romprait presque tout le code C ++ qui repose fortement sur la création sans noms temporaires. Votre seule solution pour les classes spécifiques est de rendre leurs constructeurs privés et les construire toujours par une sorte d'usine. Mais je pense que le remède est pire que la maladie!

Je ne pense pas.

Bien que ce n'est pas une chose sensée à faire - que vous avez trouvé votre bug - il n'y a rien « illégal » au sujet de la déclaration. Le compilateur n'a aucun moyen de savoir si la valeur de retour de la méthode est « vital » ou non.

compilateur ne doit pas interdire la création d'objet temporaire, à mon humble avis.

Spécialement des cas comme la réduction d'un vecteur vous avez vraiment besoin objet temporaire à créer.

std::vector<T>(v).swap(v);

Bien qu'il soit peu difficile, mais encore l'examen du code et les tests unitaires doivent prendre ces questions.

Dans le cas contraire, voici une solution pauvre homme:

CSingleLock aLock(&m_criticalSection); //Don't use the second parameter whose default is FALSE

aLock.Lock();  //an explicit lock should take care of your problem

Qu'en est-ce qui suit? Un peu abuse du préprocesseur, mais il est assez intelligent que je pense qu'il devrait être inclus:

class CSingleLock
{
    ...
};
#define CSingleLock class CSingleLock

oublier de nommer les résultats temporaires dans une erreur, car si ce qui suit est valide C ++:

class CSingleLock lock(&m_criticalSection, true); // Compiles just fine!

Le même code, mais en omettant le nom, n'est pas:

class CSingleLock(&m_criticalSection, true); // <-- ERROR!

Je vois que dans 5 ans, personne est venu avec la solution la plus simple:

#define LOCK(x) CSingleLock lock(&x, TRUE);
...
void f() {
   LOCK(m_criticalSection);

Et maintenant utiliser uniquement cette macro pour créer des verrous. Aucune chance de créer plus temporaires! Cela a l'avantage supplémentaire que la macro peut être facilement augmentée pour effectuer toute sorte de vérification de debug, par exemple la détection de verrouillage récursive inappropriée, fichier d'enregistrement et de la ligne de la serrure, et bien plus encore.

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