Pergunta

Lembro-me da primeira aprender sobre vetores em STL e depois de algum tempo, eu queria usar um vetor de bools para um de meus projetos.Depois de ver alguns comportamentos estranhos e fazer um pouco de pesquisa, eu aprendi que um vetor de bools não é realmente um vetor de bools.

Há outros erros comuns a evitar em C++?

Foi útil?

Solução

Uma pequena lista poderia ser:

  • Evitar vazamentos de memória através do uso compartilhado de ponteiros para gerenciar a alocação de memória e limpeza
  • Use o Aquisição De Recursos De Inicialização (RAII) expressão idiomática para gerenciar os recursos de limpeza, especialmente na presença de exceções
  • Evite chamar funções virtuais em construtores
  • Empregar minimalista técnicas de codificação onde possível - por exemplo, a declaração de variáveis somente quando necessário, escopo de variáveis, e início do projeto sempre que possível.
  • Verdadeiramente compreender a manipulação de exceção em seu código - tanto no que se refere às exceções lançamento, bem como aqueles lançados por classes que você pode estar usando de forma indireta.Isto é especialmente importante na presença de modelos.

RAII, compartilhada ponteiros e minimalista de codificação não são específicos para C++, mas elas ajudam a evitar problemas que frequentemente surgem quando o desenvolvimento da linguagem.

Alguns excelentes livros sobre este assunto são:

  • Eficaz C++ - Scott Meyers
  • Mais Eficaz Do C++ - Scott Meyers
  • C++ Padrões De Codificação - Sutter & Alexandrescu
  • C++ - Perguntas Mais Frequentes- Cline

A leitura desses livros me ajudou mais do que qualquer outra coisa para evitar o tipo de obstáculos que você está perguntando.

Outras dicas

Armadilhas em ordem decrescente de sua importância

Primeiro de tudo, você deve visitar o premiado C++ FAQ.Tem muitas boas respostas para armadilhas.Se você tiver outras dúvidas, visite ##c++ no irc.freenode.org no IRC.Estamos felizes em ajudá-lo, se possível.Nota os seguintes problemas são originalmente escrito.Eles não são apenas copiados de fontes aleatórias.


delete[] no new, delete no new[]

Solução:Fazer o acima, os rendimentos de comportamento indefinido:Tudo poderia acontecer.Entender seu código e o que ele faz, e sempre delete[] o que você new[], e delete o que você new, e , em seguida, que não vai acontecer.

Exceção:

typedef T type[N]; T * pT = new type; delete[] pT;

Você precisa delete[] mesmo que você new, desde a nova ed uma matriz.Então, se você estiver trabalhando com typedef, tenha especial cuidado.


Chamando uma função virtual em um construtor ou destruidor

Solução:Chamando uma função virtual não chamar a substituir funções em classes derivadas.Chamar um de função virtual pura em um construtor ou desctructor é um comportamento indefinido.


Chamar delete ou delete[] já eliminado do ponteiro

Solução:Atribuir 0 de cada ponteiro que você os exclua.Chamar delete ou delete[] em um nulo ponteiro não faz nada.


Tendo o sizeof de um ponteiro, quando o número de elementos de uma 'matriz' é para ser calculado.

Solução:Passar o número de elementos juntamente com o ponteiro quando você precisa passar uma matriz como um ponteiro para uma função.Use a função proposta aqui se você tomar o sizeof de uma matriz que é suposto ser realmente uma matriz.


Usando uma matriz como se fosse um ponteiro.Assim, usando T ** para um período de dois dimentional matriz.

Solução:Ver aqui para por que eles são diferentes e como você lida com eles.


Escrever para um literal de cadeia de caracteres: char * c = "hello"; *c = 'B';

Solução:Alocar uma matriz é inicializada a partir de dados da seqüência de caracteres literal, em seguida, você pode escrever para ele:

char c[] = "hello"; *c = 'B';

Escrever para um literal de cadeia de caracteres é um comportamento indefinido.De qualquer maneira, acima de conversão a partir de um literal de cadeia de caracteres para char * é preterido.Então, compiladores provavelmente irá avisar se você aumentar o nível de aviso.


A criação de recursos e, em seguida, esquecer para libertá-los quando algo lança.

Solução:O uso de ponteiros inteligentes como std::unique_ptr ou std::shared_ptr como apontado por outras respostas.


A modificação de um objeto duas vezes, como neste exemplo: i = ++i;

Solução:O texto acima foi suposto para atribuir a i o valor de i+1.Mas o que ele faz não está definido.Em vez de incrementar i e atribuindo o resultado, ele muda i no lado direito bem.A alteração de um objeto entre dois seqüência de pontos é um comportamento indefinido.Sequência de pontos de incluir ||, &&, comma-operator, semicolon e entering a function (lista não exaustiva!).Altere o código a seguir para torná-lo comportar-se corretamente: i = i + 1;


Diversos Problemas

Esquecer para liberar fluxos antes de chamar uma função de bloqueio como sleep.

Solução:Liberar o fluxo de transmissão ou std::endl em vez de \n ou ligando para stream.flush();.


Declarar uma função em vez de uma variável.

Solução:O problema surge porque o compilador interpreta, por exemplo,

Type t(other_type(value));

como uma declaração de função de uma função t voltar Type e ter um parâmetro do tipo other_type o que é chamado de value.Você resolvê-lo por colocar entre parênteses o primeiro argumento.Agora você começa uma variável t do tipo Type:

Type t((other_type(value)));

Chamar a função de um objeto que é apenas declarada na atual unidade de tradução (.cpp arquivo).

Solução:A norma não define a ordem de criação livre de objetos (no escopo de namespace), definido através de diferentes unidades de tradução.Chamar uma função membro de um objeto ainda não construído é um comportamento indefinido.Você pode definir a seguinte função no objeto da unidade de tradução e chamá-lo de outros:

House & getTheHouse() { static House h; return h; }

Que iria criar o objeto em demanda e deixá-lo com um totalmente construído objeto no momento em que você chamar funções sobre ele.


A definição de um modelo em um .cpp arquivo, enquanto ele é usado em um diferente .cpp arquivo.

Solução:Quase sempre você irá obter erros como undefined reference to ....Colocar todas as definições de modelo em um cabeçalho, de modo que quando o compilador é usá-los, você já pode produzir o código necessário.


static_cast<Derived*>(base); se a base é um ponteiro para uma classe base virtual de Derived.

Solução:Uma classe base virtual, é uma base que ocorre apenas uma vez, mesmo se ele é herdado mais do que uma vez por diferentes classes indiretamente em uma árvore de herança.Fazer o que está acima não é permitido pela Norma.Use dynamic_cast para fazer isso, e certifique-se de que sua classe base é polimórfica.


dynamic_cast<Derived*>(ptr_to_base); se a base não é polimórfica

Solução:O padrão não permite uma abatido, de um ponteiro ou referência quando o objeto passado não é polimórfica.Ele ou uma de suas classes base tem que ter uma função virtual.


Fazendo a sua função aceitar T const **

Solução:Você pode pensar que é mais seguro do que usar T **, mas, na verdade, vai causar dor de cabeça para as pessoas que querem passar T**:O padrão não permite isso.Ele dá um bom exemplo de por que ele não é permitido:

int main() {
    char const c = ’c’;
    char* pc;
    char const** pcc = &pc; //1: not allowed
    *pcc = &c;
    *pc = ’C’; //2: modifies a const object
}

Sempre aceitar T const* const*; em vez disso.

Outro (fechado) armadilhas discussão sobre C++, para que as pessoas olhando para eles vai encontrá-los, é de Estouro de Pilha pergunta C++ armadilhas.

Alguns devem ter C++ livros que irá ajudá-lo a evitar common C++ armadilhas:

Eficaz C++
Mais Eficaz Do C++
Eficaz STL

A Efetiva STL livro explica o vetor de bools problema :)

Brian tem uma grande lista:Eu acrescentaria "Sempre marcar único argumento construtores explícito (exceto nos raros casos em que você quer automático de fundição)."

Não é realmente uma ponta específica, mas uma diretriz geral:verifique suas fontes.O C++ é uma língua antiga, e tem mudado muito ao longo dos anos.As melhores práticas foram alterados com ele, mas, infelizmente, ainda há um monte de informações lá fora.Tem havido algumas muito boas recomendações de livros no aqui - eu posso segundo a comprar cada um de Scott Meyers C++ livros.Familiarizar-se com Impulso e com a codificação estilos usados no Boost - as pessoas envolvidas com esse projeto estão na vanguarda do C++ design.

Não reinventar a roda.Familiarizar-se com a STL e Impulso, e utilizar as suas instalações, sempre que possível rolar a sua própria.Em particular, a utilização de STL e seqüências de coleções, a menos que você tem uma muito boa razão para não o fazer.Conheça o auto_ptr e o Impulso de ponteiros inteligentes biblioteca muito bem, compreender sob quais circunstâncias cada tipo de ponteiro inteligente destina-se a ser usado e, em seguida, usar ponteiros inteligentes em todos os lugares que você poderia ter usado cru ponteiros.O seu código vai ser tão eficientes e muito menos propenso a vazamentos de memória.

Use static_cast, dynamic_cast, const_cast, e reinterpret_cast em vez de C-estilo de lança.Ao contrário de C-estilo de lança que vai deixar você saber se você está realmente pedindo para um tipo diferente de elenco que você acha que você está pedindo.E eles destacam-se viisually, alertando o leitor de que um elenco está ocorrendo.

A página da web C++ Armadilhas por Scott Wheeler cobre alguns dos principais C++ armadilhas.

Duas dicas que eu desejo que eu não tinha aprendido da maneira mais difícil:

(1) Um monte de saída (tais como printf) são armazenados por padrão.Se você estiver depurando código de bater, e você estiver usando o buffer instruções de depuração, a última saída, você pode ver não realmente a última instrução de impressão encontrados no código.A solução é limpar o buffer após cada impressão de depuração (ou desativar o buffer de todos).

(2) tenha cuidado com inicializações - (a) evitar instâncias de classe como globals / statics;e (b) tentar inicializar todas as suas variáveis de membro para algum valor seguro em um construtor, mesmo se tratando de uma trivial valor como NULO para ponteiros.

Raciocínio:o encomenda global de inicialização de objetos, não é garantido (globals inclui variáveis estáticas), portanto, você pode acabar com o código que parece falhar nondeterministically, pois depende do objeto de X ser inicializado antes do objeto Y.Se você não inicializar explicitamente uma primitiva de tipo de variável, como um membro bool ou a enumeração de uma classe, você vai acabar com valores diferentes de surpreender situações, novamente, o comportamento pode parecer muito não determinístico.

Eu já mencionei algumas vezes, mas Scott Meyers livros Eficaz C++ e Eficaz STL são realmente vale o seu peso em ouro para ajudar com C++.

Venha para pensar sobre isso, Steven Dewhurst do C++ Dicas é também um excelente "das trincheiras" de recursos.Seu item na realização de sua própria exceções e como eles devem ser construídos realmente me ajudou em um projeto.

Usando o C++ como C.Ter uma cria e ciclo de lançamento no código.

Em C++, este não é exceção, seguros e, assim, o lançamento não pode ser executado.Em C++, utilizamos RAII para resolver este problema.

Todos os recursos que ter um manual de criação e lançamento deve ser encapsulado em um objeto, para que essas ações são feitas no construtor/destruidor.

// C Code
void myFunc()
{
    Plop*   plop = createMyPlopResource();

    // Use the plop

    releaseMyPlopResource(plop);
}

Em C++, este deve ser embrulhado em um objeto:

// C++
class PlopResource
{
    public:
        PlopResource()
        {
            mPlop=createMyPlopResource();
            // handle exceptions and errors.
        }
        ~PlopResource()
        {
             releaseMyPlopResource(mPlop);
        }
    private:
        Plop*  mPlop;
 };

void myFunc()
{
    PlopResource  plop;

    // Use the plop
    // Exception safe release on exit.
}

O livro C++ Dicas pode ser útil.

Aqui estão algumas pontuações eu tive a infelicidade de cair.Todos estes têm boas razões que eu só entendi depois de ser mordido por um comportamento que me surpreendeu.

  • virtual funções em construtores não.

  • Não violar o RLL (Uma Regra de Definição), o que o anônimo espaços de nomes são (entre outras coisas).

  • Ordem de inicialização de membros depende da ordem em que elas são declaradas.

    class bar {
        vector<int> vec_;
        unsigned size_; // Note size_ declared *after* vec_
    public:
        bar(unsigned size)
            : size_(size)
            , vec_(size_) // size_ is uninitialized
            {}
    };
    
  • Valores padrão e virtual possuem uma semântica bem diferente.

    class base {
    public:
        virtual foo(int i = 42) { cout << "base " << i; }
    };
    
    class derived : public base {
    public:
        virtual foo(int i = 12) { cout << "derived "<< i; }
    };
    
    derived d;
    base& b = d;
    b.foo(); // Outputs `derived 42`
    

O mais importante armadilhas para o início desenvolvedores é para evitar a confusão entre C e C++.C++ nunca deve ser tratado como um mero melhor C ou C com classes, pois isso remove o seu poder e pode fazer até mesmo perigoso, especialmente quando utilizar a memória como em C).

Confira boost.org.Ele fornece uma série de funcionalidades adicionais, especialmente, a sua ponteiro inteligente implementações.

PRQA ter um excelente e gratuito C++ padrão de codificação baseado nos livros do Scott Meyers, Bjarne Stroustrop e Herb Sutter.Ele traz todas estas informações em um único documento.

  1. Não ler o C++ FAQ Lite.Ele explica muitas más (e boas!) práticas.
  2. Não utilizar Aumentar.Você vai salvar um monte de frustração aproveitando a vantagem de Aumentar sempre que possível.

Tenha cuidado ao usar ponteiros inteligentes e classes de contêiner.

Evitar pseudo-classes e quase classes...Overdesign basicamente.

Esquecer para definir uma classe base do processo de destruição virtual.Isso significa que chamar delete sobre uma Base* não acabar destruindo a derivada parte.

Manter os espaços de nome de reta (incluindo estrutura, classe de espaço de nomes, e usando).Esse é o meu número um de frustração quando o programa não compila.

Para mexer, usar em linha reta ponteiros de um monte.Em vez disso, use RAII para quase tudo, certificando-se claro que o direito de utilização de ponteiros inteligentes.Se você escrever "excluir" em qualquer lugar fora de uma alça ou o tipo de ponteiro de classe, você está muito provavelmente fazendo isso errado.

  • Blizpasta.Que é enorme, eu vejo um monte...

  • Variáveis não inicializadas são um grande erro que os alunos da mina de fazer.Um monte de Java pessoas se esqueça de que, assim dizendo: "int contador" não definir o contador para 0.Desde que você tem para definir as variáveis no h ficheiro (e inicializá-los no construtor/configuração de um objeto), é fácil esquecer.

  • Off por um os erros no for loops / matriz de acesso.

  • Não corretamente a limpeza de código de objeto, quando vodu começa.

  • static_cast abatido em uma classe base virtual

Não realmente...Agora sobre o meu equívoco:Eu pensei que A na seguinte era uma classe base virtual, quando na verdade não é;é, de acordo com 10.3.1, um polimórficos classe.Usando static_cast aqui parece estar bem.

struct B { virtual ~B() {} };

struct D : B { };

Em resumo, sim, esta é uma armadilha perigosa.

Verifique sempre um ponteiro antes de eliminar referência a ele.Em C, você geralmente pode contar com uma falha no ponto de onde você anular a referência de um ponteiro inválido;em C++, você pode criar uma referência inválida, que vai falhar em um ponto distante da origem do problema.

class SomeClass
{
    ...
    void DoSomething()
    {
        ++counter;    // crash here!
    }
    int counter;
};

void Foo(SomeClass & ref)
{
    ...
    ref.DoSomething();    // if DoSomething is virtual, you might crash here
    ...
}

void Bar(SomeClass * ptr)
{
    Foo(*ptr);    // if ptr is NULL, you have created an invalid reference
                  // which probably WILL NOT crash here
}

Esquecer uma & e, assim, a criação de uma cópia em vez de uma referência.

Isso aconteceu comigo duas vezes, de formas diferentes:

  • Um exemplo foi em uma lista de argumentos, o que causou um objeto grande para ser colocada na pilha com o resultado de um estouro de pilha e falha do sistema embarcado.

  • Eu esqueci o & em uma variável de instância, com o efeito de que o objeto foi copiado.Após registrar-se como um ouvinte para a cópia perguntei-me por que eu nunca tive o retorno do objeto original.

De onde, ao invés de difícil, porque a diferença é pequena e difícil de ver, e caso contrário, os objectos e referências são usadas sintaticamente da mesma forma.

A intenção é (x == 10):

if (x = 10) {
    //Do something
}

Eu pensei que eu nunca iria cometer esse erro mesmo, mas eu realmente fiz isso recentemente.

O ensaio/artigo Ponteiros, referências e Valores é muito útil.Fala-se evitar, evitando armadilhas e boas práticas.Você pode navegar em todo o site, que contém dicas de programação, principalmente para C++.

Passei muitos anos fazendo desenvolvimento C++.Eu escrevi uma resumo rápido um dos problemas que eu tive com ele anos atrás.Compatível com os padrões compiladores não são realmente um problema, mas eu suspeito que as outras armadilhas descritas ainda são válidas.

#include <boost/shared_ptr.hpp>
class A {
public:
  void nuke() {
     boost::shared_ptr<A> (this);
  }
};

int main(int argc, char** argv) {
  A a;
  a.nuke();
  return(0);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top