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?

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top