Domanda

Durante il debug di crash in un'applicazione con multithreading ho finalmente individuato il problema in questa affermazione:

CSingleLock(&m_criticalSection, TRUE);

Si noti che è la creazione di un anonimo oggetto di CSingleLock categoria e, quindi, la sezione critica in oggetto viene sbloccato subito dopo questa dichiarazione.Questo è, ovviamente, quello che il programmatore ha voluto.Questo errore è stato causato da un semplice errore di battitura.La mia domanda è, c'è qualche modo posso evitare la temporanea oggetto di una classe creata a tempo di compilazione in sè, cioèil tipo di cui sopra di codice dovrebbe generare un errore del compilatore.In generale, penso che quando una classe cerca di fare una sorta di raccolta di risorse, quindi la temporanea oggetto di quella classe, non dovrebbe essere consentito.C'è un modo per farla rispettare?

È stato utile?

Soluzione

Modifica:. Come osserva j_random_hacker, è possibile forzare l'utente a dichiarare un oggetto denominato, al fine di stipulare un blocco

Tuttavia, anche se la creazione di provvisori è stata in qualche modo vietato per la classe, l'utente potrebbe fare un errore simile:

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

// do other stuff, assuming lock is held

In ultima analisi, l'utente deve capire l'impatto di una riga di codice che scrivono. In questo caso, devono sapere che stanno creando un oggetto e devono sapere quanto tempo dura.

Un altro probabile errore:

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

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

Il che porterebbe a chiedere "C'è un modo per fermare l'utente della mia classe dalla loro assegnazione nel mucchio"? Al che la risposta sarebbe la stessa.

In C ++ 0x ci sarà un altro modo per fare tutto questo, utilizzando lambda. Definire una funzione:

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

Questa funzione acquisisce l'uso corretto di CSingleLock. Ora lasciate che gli utenti fanno questo:

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

Questo è molto più difficile per l'utente per avvitare in su. La sintassi sembra strano in un primo momento, ma [&] seguito da un blocco di codice significa "definire una funzione che non prende args, e se mi riferisco a nulla in base al nome, ed è il nome di qualcosa al di fuori (ad esempio, una variabile locale nella contenente funzione) mi permetta di accedervi di riferimento non-const, così posso modificarlo.)

Altri suggerimenti

In primo luogo, Earwicker fa alcuni buoni punti - non è possibile evitare ogni uso improprio accidentale di questo costrutto.

Ma per il vostro caso specifico, questo può infatti essere evitato. Questo perché C ++ fa di una (strana) distinzione per quanto riguarda gli oggetti temporanei:. funzioni gratuiti non possono prendere i riferimenti non const a oggetti temporanei Quindi, al fine di evitare blocchi che Blip dentro e fuori dell'esistenza, basta spostare il codice di blocco fuori del costruttore CSingleLock e in una funzione libera (che si può fare un amico per evitare di esporre interni come metodi):

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

sblocco viene ancora eseguita nel distruttore.

Per usare:

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

Sì, è un po 'più ingombrante di scrivere. Ma ora, il compilatore si lamenterà se provate:

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

Poiché il parametro ref non-const di Lock() non può legarsi ad una temporanea.

Forse sorprendentemente, metodi di classe possono operare su provvisori - è per questo che Lock() deve essere una funzione libera. Se si lascia cadere la specificazione friend e il parametro della funzione nel frammento in alto per fare Lock() un metodo, il compilatore sarà lieto permetterà di scrivere:

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

MS COMPILATORE NOTA: le versioni MSVC ++ fino a Visual Studio .NET 2003 funzioni in modo non corretto consentito di collegarsi ad riferimenti non-const nelle versioni precedenti alla VC ++ 2005. Questo comportamento è stato risolto in VC ++ 2005 e al di sopra .

No, non c'è modo di fare questo.Così facendo pausa di quasi tutto il codice C++ che si basa molto sulla creazione di nameless provvisori.L'unica soluzione per specifiche classi è quello di rendere loro costruttori privati e quindi sempre a costruire il loro tramite una sorta di fabbrica.Ma penso che la cura è peggiore della malattia!

Non penso così.

Anche se non è una cosa sensata da fare - come hai scoperto con il bug - non c'è nulla di "illegale" la dichiarazione. Il compilatore non ha modo di sapere se il valore restituito dal metodo è "vitale" o meno.

Compiler non dovrebbe impedire la creazione di oggetti temporanei, IMHO.

Specialmente casi come un vettore contrazione si ha realmente bisogno oggetto temporaneo da creare.

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

Anche se è po 'difficile, ma ancora la revisione del codice e test di unità dovrebbe prendere questi problemi.

In caso contrario, ecco la soluzione uno dei poveri:

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

E la seguente? Leggermente abusi del preprocessore, ma è abbastanza intelligente che penso che dovrebbe essere incluso:

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

Ora dimenticando di nominare i risultati temporanei in un errore, perché mentre la seguente è valido C ++:

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

Lo stesso codice, ma omettendo il nome, non è:

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

Vedo che in 5 anni nessuno ha messo a punto la soluzione più semplice:

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

E ora solo utilizzare questa macro per la creazione di serrature. Nessuna possibilità di creare provvisori più! Questo ha il vantaggio che la macro può essere facilmente aumentata per eseguire qualsiasi tipo di check-in build di debug, ad esempio la rilevazione inadeguato blocco ricorsivo, di file e la linea della serratura, e molto altro la registrazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top