Não permitindo a criação dos objetos temporários
Pergunta
Durante a depuração acidente em um aplicativo com vários segmentos Eu finalmente localizado o problema nesta declaração:
CSingleLock(&m_criticalSection, TRUE);
Observe que ele está criando um objeto sem nome da classe CSingleLock e, portanto, o objeto seção crítica fica desbloqueado imediatamente após esta declaração. Isso obviamente não é o que o codificador queria. Este erro foi causado por um simples erro de digitação. A minha pergunta é, existe alguma maneira eu pode impedir que o objeto temporário de uma classe que está sendo criado no próprio tempo de compilação ou seja, o tipo de código acima deve gerar um erro do compilador. Em geral, eu acho que sempre que uma classe tenta fazer algum tipo de aquisição de recursos, em seguida, o objeto temporário dessa classe não deve ser permitido. Existe alguma maneira de aplicá-la?
Solução
Editar:. Como observa j_random_hacker, é possível forçar o usuário a declarar um objeto nomeado, a fim de tirar um bloqueio
No entanto, mesmo se a criação de temporários foi de alguma forma banido por sua classe, em seguida, o usuário pode cometer um erro semelhante:
// take out a lock:
if (m_multiThreaded)
{
CSingleLock c(&m_criticalSection, TRUE);
}
// do other stuff, assuming lock is held
Em última análise, o usuário tem que entender o impacto de uma linha de código que eles escrevem. Neste caso, eles têm de saber que eles estão criando um objeto e eles têm de saber quanto tempo dura.
Outro erro provável:
CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE);
// do other stuff, don't call delete on c...
O que levaria a perguntar: "Existe alguma maneira eu posso parar o usuário da minha classe de alocação na pilha"? Para que a resposta seria a mesma.
Em C ++ 0x haverá outra maneira de fazer tudo isso, usando lambdas. Definir uma função:
template <class TLock, class TLockedOperation>
void WithLock(TLock *lock, const TLockedOperation &op)
{
CSingleLock c(lock, TRUE);
op();
}
Essa função captura o uso correto de CSingleLock. Agora vamos aos usuários fazer isso:
WithLock(&m_criticalSection,
[&] {
// do stuff, lock is held in this context.
});
Este é muito mais difícil para o usuário a estragar. A sintaxe parece estranho no início, mas [&] seguido por um meio bloco de código "Definir uma função que não recebe argumentos, e se me refiro a qualquer coisa pelo nome e é o nome de algo fora (por exemplo, uma variável local na contendo função) deixe-me acessá-lo por referência não-const, para que eu possa modificá-lo.)
Outras dicas
Em primeiro lugar, Earwicker faz alguns bons pontos - você não pode impedir todos os mau uso acidental de esta construção.
Mas, para o seu caso específico, isso pode na verdade ser evitado. Isso porque C ++ faz um (estranho) distinção sobre objetos temporários: funções grátis não pode ter referências não-const para objetos temporários Assim, a fim de fechaduras evitar que blip dentro e fora de existência, apenas movimento. o código de bloqueio para fora do construtor CSingleLock
e em uma função livre (que você pode fazer um amigo para evitar a exposição internos como métodos):
class CSingleLock {
friend void Lock(CSingleLock& lock) {
// Perform the actual locking here.
}
};
Desbloqueio ainda é realizada no processo de destruição.
Para usar:
CSingleLock myLock(&m_criticalSection, TRUE);
Lock(myLock);
Sim, é um pouco mais complicado de escrever. Mas agora, o compilador vai reclamar se você tentar:
Lock(CSingleLock(&m_criticalSection, TRUE)); // Error! Caught at compile time.
Como o parâmetro ref não-const de Lock()
não pode vincular a um temporário.
Talvez surpreendentemente, métodos de classe pode operar em temporários - é por isso necessidades Lock()
ser uma função livre. Se você deixar cair o especificador friend
eo parâmetro de função no topo trecho para fazer Lock()
um método, em seguida, o compilador terá todo o prazer permitem que você escreva:
CSingleLock(&m_criticalSection, TRUE).Lock(); // Yikes!
MS COMPILADOR NOTA: MSVC ++ versões até Visual Studio .NET 2003 funções incorretamente deixa-se ligar a referências não-const em versões anteriores ao VC ++ 2005. Este comportamento foi corrigido no VC ++ 2005 e acima .
Não, não há nenhuma maneira de fazer isso. Fazer isso seria quebrar quase todo o código C ++ que depende fortemente na criação de temporários sem nome. Sua única solução para classes específicas é fazer com que seus construtores privados e, em seguida, sempre construí-las através de algum tipo de fábrica. Mas eu acho que a cura é pior do que a doença!
Eu não penso assim.
Enquanto não é uma coisa sensata a fazer - como você descobriu com o seu erro - não há nada de "ilegal" sobre a declaração. O compilador não tem como saber se o valor de retorno do método é "vital" ou não.
Compiler não deve impedir a criação do objeto temporário, IMHO.
casos gosto especialmente encolhendo um vetor você realmente precisa objeto temporário a ser criado.
std::vector<T>(v).swap(v);
Embora seja pouco difícil, mas ainda revisão de código e testes unitários devem pegar estas questões.
Caso contrário, aqui é solução de um pobre homem:
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 sobre o seguinte? Ligeiramente abusos do pré-processador, mas é o suficiente inteligente que eu acho que deveria ser incluído:
class CSingleLock
{
...
};
#define CSingleLock class CSingleLock
Agora, esquecendo-se de citar os resultados temporários em um erro, porque, enquanto o seguinte é válido C ++:
class CSingleLock lock(&m_criticalSection, true); // Compiles just fine!
O mesmo código, mas omitindo o nome, não é:
class CSingleLock(&m_criticalSection, true); // <-- ERROR!
Eu vejo que em 5 anos ninguém veio acima com a solução mais simples:
#define LOCK(x) CSingleLock lock(&x, TRUE);
...
void f() {
LOCK(m_criticalSection);
E agora só usar essa macro para a criação de bloqueios. Sem chance para criar temporários mais! Isto tem a vantagem adicional de que a macro pode ser facilmente aumentada para realizar qualquer tipo de verificação em compilações de depuração, por exemplo detectar inapropriado recursiva de bloqueio, a gravação de arquivo e linha do bloqueio, e muito mais.