Pregunta

Mientras se depura accidente en una aplicación multiproceso finalmente localicé el problema en esta declaración:

CSingleLock(&m_criticalSection, TRUE);

Tenga en cuenta que es la creación de un objeto no identificado de la clase CSingleLock y por lo tanto el objeto de sección crítica se desbloquea inmediatamente después de esta declaración. Esto obviamente no es lo que quería el codificador. Este error se debe a un simple error de mecanografía. Mi pregunta es, ¿hay alguna manera puedo evitar que el objeto temporal de una clase que se crea en el momento de compilación en sí, es decir el tipo de código anterior debería generar un error de compilación. En general, creo que cada vez que una clase trata de hacer algún tipo de adquisición de recursos, entonces el objeto temporal de esa clase no se debe permitir. ¿Hay alguna manera de hacerla cumplir?

¿Fue útil?

Solución

Editar:. Como notas j_random_hacker, es posible obligar al usuario a declarar un objeto nombrado con el fin de obtener un bloqueo

Sin embargo, incluso si la creación de provisionales de alguna manera estaba prohibido para su clase, a continuación, el usuario podría cometer un error similar:

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

// do other stuff, assuming lock is held

En última instancia, el usuario tiene que entender el impacto de una línea de código que escriben. En este caso, tienen que saber que están creando un objeto y tienen que saber cuánto tiempo dura.

Otra probable error:

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

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

¿Qué se llevaría a preguntar "¿Hay alguna manera de detener el usuario de mi clase a partir de su asignación en el montón"? A lo que la respuesta sería la misma.

En C ++ 0x habrá otra manera de hacer todo esto, mediante el uso de lambdas. Definir una función:

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

Esta función captura el uso correcto de CSingleLock. Ahora permiten a los usuarios hacer esto:

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

Esto es mucho más difícil para el usuario para meter la pata. La sintaxis es raro al principio, pero [Y] seguido de un bloque de código significa "Definir una función que no toma argumentos, y si me refiero a cualquier cosa por su nombre y es el nombre de algo exterior (por ejemplo, una variable local en el que contiene función) me deja acceder a él por referencia no constante, por lo que puedo modificarlo.)

Otros consejos

En primer lugar, Earwicker hace algunos buenos puntos - no se puede evitar que cada mal uso accidental de esta construcción.

Pero para su caso específico, esto puede de hecho ser evitado. Eso es debido a que C ++ hace que una distinción (extraño) en relación con los objetos temporales:. funciones libres no pueden tomar referencias no constante a objetos temporales Por lo tanto, con el fin de evitar bloqueos que blip dentro y fuera de la existencia, sólo hay que mover el código de bloqueo del constructor CSingleLock y en una función gratuita (que se puede hacer un amigo para evitar exponer partes internas como métodos):

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

Desbloqueo todavía se lleva a cabo en el destructor.

Para usar:

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

Sí, es un poco más difícil de manejar para escribir. Pero ahora, el compilador se quejará si se intenta:

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

Debido a que el parámetro de referencia no constante de Lock() no puede obligar a un temporal.

Tal vez sorprendentemente, los métodos de clase puede operar en los temporales - es por eso Lock() tiene que ser una función libre. Si se le cae el especificador friend y el parámetro de la función en el fragmento de la parte superior para hacer Lock() un método, entonces el compilador felizmente le permitirá escribir:

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

MS COMPILER NOTA: versiones MSVC ++ hasta Visual Studio .NET 2003 funciones incorrectamente deja que se una a las referencias no constante en las versiones anteriores a VC ++ 2005. Este comportamiento se ha fijado en VC ++ 2005 y por encima de .

No, no hay manera de hacer esto. Si lo hace, se rompería casi todo el código C ++ que se basa en gran medida en la creación de los temporales sin nombre. Su única solución para las clases específicas es hacer que sus constructores privados y luego siempre construirlas a través de algún tipo de fábrica. Pero creo que el remedio es peor que la enfermedad!

Yo no lo creo.

Si bien no es una cosa sensible a hacer - como se ha averiguado con su error - no hay nada "ilegal" por la declaración. El compilador no tiene forma de saber si el valor de retorno del método es "vital" o no.

Compilador no debe impedir la creación de objetos temporales, en mi humilde opinión.

Especialmente casos como la reducción de un vector que realmente necesitan objeto temporal que se creará.

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

A pesar de que es poco difícil, pero sigue siendo revisión de código y la prueba de la unidad debe atrapar a estos problemas.

Por lo demás, aquí está la solución de un pobre hombre:

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é hay de la siguiente? Ligeramente abusos del preprocesador, pero es lo suficientemente inteligente que yo creo que debe ser incluido:

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

Ahora olvidar nombrar los resultados temporales en un error, porque mientras que la siguiente es válida C ++:

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

El mismo código, pero omitiendo el nombre, no es:

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

veo que en 5 años, nadie ha llegado con la solución más sencilla:

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

Y ahora sólo utilizar esta macro para crear cerraduras. No hay posibilidad de crear temporales más! Esto tiene la ventaja añadida de que la macro puede ser fácilmente aumentada para llevar a cabo cualquier tipo de comprobación en versiones de depuración, por ejemplo, la detección inapropiada de bloqueo recursiva, archivo y la línea de la cerradura, y mucho más la grabación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top