Pergunta

Vem fazendo Java para o número de anos, então não foram rastreamento C ++. Tem finalmente cláusula foi adicionada para C ++ manipulação de exceção na definição de linguagem?

Existe um idioma preferido que tentam imita do Java / finally?

Am também incomodado que C ++ não tem um tipo de super final para todas as exceções possíveis que poderiam ser lançadas -. Como classe Throwable de Java

Eu posso escrever:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

ADENDO EDIT:

acabei aceitando a resposta que teve a maioria se votos, ou seja, o uso destruidores para fazer a limpeza. Claro, de minhas próprias observações, é claro I Não concordo inteiramente com isso. No entanto, C ++ é o que é e assim em a aplicação esforço que tenho em mente, eu estou indo para mais ou menos se esforçam a aderir a comunidade comum prática. Vou usar classes de modelo para recursos envoltório que ainda não têm um destruidor de classe (isto é, a biblioteca C recursos), concedendo, assim, neles semântica destructor.

NOVO ADENDO EDIT:

Hmm, em vez de finalmente , em seguida, um fecho apresentam talvez? Um encerramento combinadas com abordagem ScopeGuard (veja um dos respostas abaixo) seria uma maneira de realizar a limpeza com arbitrária ações e acesso à limpeza contexto escopo externo do código. Cleanup poderia ser feito na forma linguagem que é visto na programação Ruby, onde eles fornecem blocos de limpeza quando um recurso está sendo aberto. não é um recurso de fechamento sendo considerado para C ++?

Foi útil?

Solução

Ao fazer uso eficaz de destruidores. Quando uma exceção é lançada em um bloco try, qualquer objeto criado dentro dela será destruída imediatamente (e, portanto, seu destruidor chamado).

Isto é diferente de Java, onde você não tem idéia de quando finalizador de um objeto será chamado.

Atualizar : diretamente da boca do cavalo: Por que não C ++ fornecer uma construção "finalmente"?

Outras dicas

My $ .02. Eu tenho sido programação em linguagens gerenciadas como C # e Java há anos, mas foi forçado a fazer a troca de C ++ para fins de velocidade. No começo eu não conseguia acreditar como eu tinha para escrever a assinatura do método duas vezes no arquivo de cabeçalho e, em seguida, o arquivo cpp, e eu não gosto de como não houve finalmente bloquear, e nenhuma coleta de lixo significava rastreamento vazamentos de memória em todos os lugares - Puxa eu não gosto nada disso!

No entanto, como eu disse que foi forçado a usar C ++. Então, eu fui forçado a aprender a sério, e agora eu finalmente entendi todas as expressões idiomáticas de programação como RAII e eu recebo todas as sutilezas da linguagem e tal. Levei um tempo, mas agora vejo o quão diferente de uma linguagem que é comparado com C # ou Java.

Esses dias eu acho que C ++ é a melhor linguagem que existe! Sim, eu posso entender que há um pouco mais o que eu chamo de 'joio' às vezes (coisas aparentemente desnecessário para escrever), mas depois realmente usando a linguagem sério, eu mudei de idéia sobre isso completamente.

Eu costumava ter vazamentos de memória o tempo todo. Eu costumava escrever todo o meu código para o arquivo .h porque eu odiava a separação de código, eu não conseguia entender por que eles fariam isso! E eu costumava sempre acabar com cíclica estúpido incluem dependências, e montes mais. Eu realmente estava pendurado em C # ou Java, para mim C ++ foi um grande passo para baixo. Estes dias eu entendo. Eu quase nunca tem vazamentos de memória, eu desfrutar da separação de interface e implementação, e eu não tenho problemas com dependências de ciclo mais.

E eu não perca o bloco finally também. Para ser honesto, minha opinião é que esses programadores C ++ que você fala sobre a escrita de ações de limpeza utilizadas na blocos catch apenas me parece eles são apenas maus programadores C ++. Quero dizer, ele não se parece com qualquer um dos outros programadores C ++ neste segmento estão tendo nenhum dos problemas que você menciona. RAII realmente faz finalmente redundante, e se alguma coisa, é menos trabalho. Você escreve um destruidor e, em seguida, você nunca tem que escrever outro, finalmente, sempre! Bem, pelo menos para esse tipo.

Com respeito, que eu acho que está acontecendo é que você está apenas usado para Java agora, assim como eu tinha sido.

resposta

C ++ 's é RAII: destrutor do objeto será executado quando saem do escopo. Se por um retorno, por uma exceção ou o que quer. Se você tratar a exceção em outro lugar, você pode ter certeza todos os objetos da função chamada para baixo para o manipulador será devidamente destruído por ter o seu destruidor chamado. Eles vão limpar para você.

Leia http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

Sem finalmente não foi adicionado ao C ++, nem é provável que nunca ser adicionado.

O construtor usos forma de C ++ / destructor torna a necessidade de finalmente desnecessário.
Se você estiver usando catch (...) para a limpeza, então você não estiver usando C ++ corretamente. O código de limpeza devem estar todos no processo de destruição.

Embora não seja um requisito para usá-lo C ++ tem um std :: exceção.
Forçar os desenvolvedores a derivar de uma classe específica de exceção uso vai contra a mantê-lo simples filosofia do C ++. É também por isso que não necessitam de todas as classes de derivar de Object.

Leia: suporte O C ++ 'finalmente'? (E o que é esse 'RAII' Eu continuo ouvindo sobre?)

O uso de, finalmente, é mais propenso a erros do que destruidores para fazer a limpeza.
Isso é porque você está forçando o usuário do objeto para fazer a limpeza em vez do designer / implementador da classe.

Ok, eu tenho que adicionar em uma resposta aos pontos que você fez em um post resposta em separado: (Seria muito mais conveniente se você tivesse editado esta na pergunta original, por isso não acabar na parte inferior abaixo as respostas para isso.

Se toda a limpeza sempre é feito em destruidores então não haveria necessidade ser qualquer código de limpeza, em um prendedor bloco - C ++ ainda tem blocos de captura quando ações de limpeza feito. Na verdade, tem um bloco de catch (...) onde é só é possível fazer ações de limpeza (Bem, certamente não pode obter em qualquer informações de exceção para fazer qualquer logging).

captura tem um propósito completamente separado, e como programador Java você deve estar ciente disso. A cláusula finalmente é para ações de limpeza "incondicionais". Não importa como o bloco é encerrado, isso deve ser feito. Prendedor é para limpeza condicional. Se este tipo de exceção é lançada, é preciso executar algumas ações extras.

A limpeza em uma vontade bloco finally ter feito se houve uma exceção lançada ou não - que é o que se quer sempre acontecer quando código de limpeza existe.

Really? Se queremos que ele sempre acontecer para este tipo (por exemplo, nós sempre queremos fechar uma conexão de banco de dados quando estamos a fazer com ele), então por que não podemos defini-lo uma vez ? No próprio tipo? Fazer a conexão com o banco em si perto, ao invés de ter que colocar um try / finally em torno de cada utilização única dele?

Esse é o ponto em destruidores. Eles garantem que cada tipo é capaz de cuidar de sua própria limpeza, cada vez que é usado, sem o chamador ter que pensar sobre isso.

Os desenvolvedores de C ++ de um dia ter sido atormentado com a necessidade de repetir a limpeza ações que aparecem em blocos catch em o fluxo de código que ocorre mediante Sair com êxito da bloco try. Java e programadores C # apenas fazê-lo uma vez no bloco finally.

No. programadores C ++ nunca foram atormentado por isso. programadores C tem. E C programadores que perceberam que c ++ teve aulas, e então chamou-se programadores C ++ têm.

Programa I em C ++ e C # diariamente, e eu sinto que estou atormentado por C # 's insistência ridícula que eu deve fornecer uma cláusula finally (ou um bloco using) Toda vez que eu usar uma conexão de banco de dados ou qualquer outra coisa que deve ser limpo -se.

C ++ permite-me especificar uma vez por todas que "sempre que estamos a fazer com este tipo, ele deve realizar essas ações". Eu não arriscar esquecer de memória liberação. Eu não arriscar esquecer de identificadores de arquivo estreitas, tomadas ou conexões de banco de dados. Porque a minha memória, meus punhos, tomadas e ligações db fazer por si próprios.

Como pode nunca ser preferível a ter que escrever duplicado código de limpeza cada vez que você usar um tipo? Se você precisar para embrulhar o tipo porque ele não tem um destruidor em si, você tem duas opções fáceis:

  • Procure uma adequada C ++ biblioteca que fornece este destructor (dica: Boost)
  • Use boost :: shared_ptr para envolvê-lo, e fornecê-lo com um functor personalizado em tempo de execução, especificando a limpeza a ser feito.

Quando você escreve servidor de aplicativos software como servidores de aplicativos Java EE Glassfish, JBoss, etc., você quer ser capaz de capturar e log excepção informações - em oposição a deixá-lo cair no chão. Ou pior queda em o tempo de execução e causar uma ungraceful saída abrupta do servidor de aplicativos. É por isso que é muito desejável ter uma classe base global para qualquer possível exceção. E C ++ tem apenas tal classe. std :: exceção.

fizeram C ++ desde os dias Cfront e Java / C # maior parte desta década. É claro, para ver há apenas uma enorme lacuna cultura em como fundamentalmente coisas semelhantes são abordados.

Não, você nunca fiz C ++. Você já fez Cfront, ou C com classes. Nãot C ++. Há uma enorme diferença. Sair chamando o coxo respostas, e você pode aprender algo sobre a língua que você achava que sabia. ;)

funções de limpeza, eles mesmos, são completamente coxo. Eles têm baixa coesão, na medida em que são esperados para realizar uma série de atividades relacionadas apenas quando eles acontecem. Eles têm acoplamento de alta, em que eles precisam ter seus internos modificados quando as funções que realmente fazer alguma coisa são alteradas. Devido a isso, eles são propensos a erros.

O try ... finalmente construção é uma estrutura para funções de limpeza. É uma maneira encorajou-linguagem para escrever código ruim. Além disso, uma vez que incentiva a escrever o mesmo código de limpeza mais e mais, que põe em causa o princípio de DRY.

forma A C ++ é muito preferível para esses fins. O código de limpeza para um recurso está escrito precisamente uma vez, no processo de destruição. É no mesmo lugar que o resto do código para esse recurso e, portanto, tem uma boa coesão. O código de limpeza não tem que ser colocado em módulos independentes, e, portanto Isso reduz o acoplamento. Está escrito precisamente uma vez, quando bem desenhado.

Além disso, a maneira como C ++ é muito mais uniforme. C ++, com os acréscimos de ponteiro inteligente, lida com todos os tipos de recursos da mesma maneira, enquanto Java memória alças bem e fornece construções inadequadas para liberar outros recursos.

Há uma abundância de problemas com C ++, mas este não é um deles. Existem maneiras em que Java é melhor do que C ++, mas este não é um deles.

Java seria muito melhor com uma maneira de implementar RAII em vez de try ... finalmente.

Para evitar ter que definir uma classe wrapper para cada recurso releasable, você pode estar interessado em ScopeGuard ( http : //www.ddj.com/cpp/184403758 ) que permite criar "produtos de limpeza" on the fly.

Por exemplo:

FILE* fp = SomeExternalFunction();
// Will automatically call fclose(fp) when going out of scope
ScopeGuard file_guard = MakeGuard(fclose, fp);

Um exemplo de como é difícil de usar, finalmente corretamente.

Abrir e fechar dois arquivos.
Onde você quer garantia de que o arquivo é fechado corretamente.
Esperando o GC não é uma opção que os arquivos podem ser reutilizado.

Em C ++

void foo()
{
    std::ifstream    data("plop");
    std::ofstream    output("plep");

    // DO STUFF
    // Files closed auto-magically
}

Em uma linguagem sem destruidores, mas tem uma cláusula finalmente.

void foo()
{
    File            data("plop");
    File            output("plep");

    try
    {
        // DO STUFF
    }
    finally
    {
        // Must guarantee that both files are closed.
        try {data.close();}  catch(Throwable e){/*Ignore*/}
        try {output.close();}catch(Throwable e){/*Ignore*/}
    }
}

Este é um exemplo simples e já o código está ficando complicado. Aqui estamos apenas tentando organizar 2 recursos simples. Mas como o número de recursos que precisam ser gerenciados aumenta e / ou sua complexidade aumenta o uso de um bloco, finalmente, torna-se mais difícil e mais difícil de usar corretamente na presença de exceções.

O uso de finalmente se move a responsabilidade por uso correto para o usuário de um objeto. Usando mecanismo de construtor / destruidor fornecido pelo C ++ você mover a responsabilidade da utilização correcta para o designer / implementador da classe. Esta é inheritanly mais seguro como o designer só precisa fazê-lo corretamente uma vez no nível de classe (em vez de ter diferentes usuários tentar e fazê-lo corretamente de maneiras diferentes).

Usando C ++ 11 com sua lambda expressões , eu tenho recentemente começou a utilizar o seguinte código para finally imitar:

class FinallyGuard {
private:
  std::function<void()> f_;
public:
  FinallyGuard(std::function<void()> f) : f_(f) { }
  ~FinallyGuard() { f_(); }
};

void foo() {
  // Code before the try/finally goes here
  { // Open a new scope for the try/finally
    FinallyGuard signalEndGuard([&]{
      // Code for the finally block goes here
    });
    // Code for the try block goes here
  } // End scope, will call destructor of FinallyGuard
  // Code after the try/finally goes here
}

O FinallyGuard é um objecto que é construído com um argumento da função semelhante pode ser chamado, de preferência uma expressão lambda. Ela simplesmente lembrar que a função até à sua destruição é chamado, o que é o caso quando o objecto sai do escopo, quer devido ao fluxo de controlo normal ou devido a pilha de desenrolamento durante o processamento de excepções. Em ambos os casos, o destruidor vai chamar a função, executando assim o código em questão.

É um pouco estranho que você tem que escrever o código para o finally antes o código para o bloco try, mas para além de que ele realmente se sente muito como um try genuína / finally de Java . Eu acho que não se deve abusar desta para situações em que um objeto com a sua própria destruição adequada seria mais apropriado, mas há casos em que eu considero esta abordagem acima mais adequado. Eu discuti um tal cenário em esta questão .

Tanto quanto eu entendo as coisas, std::function<void()> vai usar algum engano ponteiro e pelo menos uma chamada de função virtual para executar seu tipo apagamento , então haverá um desempenho sobrecarga . Não use essa técnica dentro de um loop onde o desempenho é crítico. Nesses casos, um objeto especializada cuja destructor faz uma coisa só seria mais apropriado.

destruidores C ++ fazer finally redundante. Você pode obter o mesmo efeito, movendo o código de limpeza de, finalmente, para destruidores correspondente.

Eu acho que você está perdendo o ponto do que catch (...) pode fazer.

Você diz no seu exemplo "infelizmente, não pode examinar a exceção". Bem, você não tem informações sobre o tipo de exceção. Você nem sei se é um tipo polimórfico assim mesmo se você teve algum tipo de referência untyped para ele, você não poderia mesmo tentar com segurança um dynamic_cast.

Se você sabe sobre certas exceções ou hierarquias de exceção que você pode fazer algo com o então este é o lugar para blocos catch com tipos explicitamente chamado.

catch (...) muitas vezes não é útil em C ++. Ele pode ser usado em locais que devem garantir que eles não jogue, ou único lance certas exceções contratados. Se você estiver usando catch (...) para limpeza, então há uma boa chance de que seu código não é robustamente segura exceção em qualquer caso.

Como mencionado em outras respostas, se você estiver usando objetos locais para gerir os recursos (RAII), então ele pode ser surpreendente e esclarecer como os blocos captura poucos você precisa, muitas vezes - se você não precisa fazer nada localmente, com uma exceção -. até mesmo o bloco try pode ser redundante como você deixar as exceções fluir para o código do cliente que podem responder a elas, enquanto ainda garantindo há problemas de recursos

Para responder à sua pergunta original, se você precisar de algum pedaço de código seja executado no final de um bloco, exceção ou nenhuma exceção, em seguida, uma receita seria.

class LocalFinallyReplacement {
    ~LocalFinallyReplacement() { /* Finally code goes here */ }
};
// ...
{ // some function...
    LocalFinallyReplacement lfr; // must be a named object

    // do something
}

Note como podemos completamente acabar com try, catch e throw.

Se você teve dados na função que foi originalmente declarada fora do bloco try que você precisava de acesso no bloco "finalmente", então você pode precisar de acrescentar que para o construtor da classe auxiliar e armazená-lo até que o processo de destruição . No entanto, neste momento eu iria reconsiderar seriamente se o problema poderia ser resolvido alterando o desenho do recurso local manipulação de objetos, uma vez que implicaria algo errado no projeto.

Não completetely offtopic.

Caldeira chapeamento DB Resource Cleanup em Java

Modo de sarcasmo: Não é o Java idioma maravilhoso

?

Já fiz muita classe design e design template invólucro em C ++ ao longo desses 15 anos e fez tudo isso forma como o C ++ em termos de destruidores limpeza. Cada projeto, no entanto, também invariavelmente envolveram o uso de bibliotecas C que forneceu recursos com a abri-lo, usá-lo, fechá-lo modelo de uso. Um try / finally significaria tal recurso só pode ser consumido onde ele precisa ser - de uma forma completamente robusto - e ser feito com ele. A abordagem menos tédio para programar essa situação. Poderia lidar com toda a outro estado acontecendo durante a lógica de que a limpeza sem ter que ser com escopo afastado em alguns destructor wrapper.

Eu fiz a maioria do meu C ++ codificação no Windows assim pode sempre recorrer ao uso de __try da Microsoft / __, finalmente, para tais situações. (O tratamento de exceção estruturada tem algumas habilidades poderosas para interagir com exceções.) Infelizmente, não se parece com a linguagem C já ratificado qualquer exceção portátil manipulação construções.

Isso não foi a solução ideal, no entanto, porque não era fácil de misturar código C e C ++ em um bloco try, onde quer estilo de exceção pode ficar jogado. Um bloco finalmente adicionado ao C ++ teria sido útil para aquelas situações e permitir a portabilidade.

Quanto à sua adendo-edit, fechamentos sim estão sendo consideradas para C ++ 0x. Eles podem ser usados ??com RAII escopo guardas para fornecer uma solução fácil de usar, verificação o Pizer weblog . Eles também podem ser usados ??para imitar try-finally, consulte esta resposta ; mas isso é realmente uma boa ideia? .

Pensei em adicionar a minha própria solução para isso - uma espécie de invólucro ponteiro inteligente para quando você tem que lidar com tipos não-RAII

.

Usado como este:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

Então aqui está a implementação de finaliser:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

... e aqui está Releaser:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

Eu tenho alguns tipos diferentes de liberador como esta, incluindo um para free () e um para CloseHandle ().

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