Começando Programação com C ++ For Dummies

C ++ é cheia de pequenos símbolos, cada um dos quais contribui para o significado de expressões. As regras de gramática C ++ são tão flexíveis que esses símbolos podem ser combinados em combinações quase impenetrável complexos. Expressões na linguagem C mais simples pode ficar tão obtuso que costumava haver um concurso anual para que pudesse escrever o programa mais obscuro e que pudesse compreendê-lo.

Nunca é uma boa idéia para tentar escrever código complexo, mas às vezes você vai se deparar expressões em C ++ que são um pouco desconcertante à primeira vista. Basta usar as seguintes etapas para entendê-los:

  1. Comece com os parênteses mais incorporados.

    Comece a olhar para os exteriores maioria dos parênteses. Dentro daqueles, procure parênteses incorporados. Repita o processo até que você trabalhou seu caminho para o par mais profundo de parênteses. Começar a avaliar que subexpression primeiro usando as seguintes regras. Depois de entender que a expressão, pop de volta para o próximo nível e repita o processo.

  2. Dentro do par de parênteses, avaliar cada operação na ordem de precedência.

    A fim de que os operadores são avaliados é determinada pela prioridade do operador mostrado na tabela. Indireção vem antes da multiplicação que vem antes da adição, assim, a seguir adiciona 1 mais 2 vezes o valor apontado para pelo * ptr.

int i = 1 + 2 * * ptr-
Operadores em ordem de precedência
PrecedênciaOperadorSignificado
1() (Unário)Invocar uma função
2* E -> (unário)Dereference um ponteiro
2- (Unário)Devolve o negativo de seu argumento
3++ (Unário)Incremento
3-- (Unário)diminuição
4* (binário)Multiplicação
4/ (binário)Divisão
4% (binário)Modulo
5+ (binário)Adição
5- (binário)Subtração
6 (binário)E lógico
6!!OU lógico
7=, * =,% =, + =, - = (Especial)tipos de atribuição
  1. Avaliar as operações com a mesma precedência da esquerda para a direita (exceto cessão, que vai para o outro lado).

    A maioria dos operadores da mesma precedência avaliar a partir da esquerda para a direita. Assim, a seguir adiciona 1-2 e adiciona o resultado para 3:

    int i = 1 + 2 + 3 

    A ordem de avaliação de alguns operadores não importa. Por exemplo, a adição funciona da mesma a partir da esquerda para a direita como o faz a partir da direita para a esquerda. A ordem de avaliação faz muita diferença para algumas operações como divisão. As seguintes divisões 8 por 4 e divide o resultado por 2:

    int i = 8/4 / 2-

    A principal excepção a esta regra é atribuição, que é avaliada da direita para a esquerda:

    a = b = c-

    Isso atribui C para B e o resultado para um.

  2. Avaliar subexpressions em nenhuma ordem particular.

    Considere a seguinte expressão:

    int i = f () + g () * h () -

    A multiplicação tem precedência mais alta, de modo que você pode assumir que as funções g () e h () são chamados antes de f (), no entanto, este não é o caso. chamada de função tem a precedência mais alta de todos, para todas as três funções são chamadas quer antes da multiplicação ou a adição é realizada. (Os resultados retornados a partir de G () eh () são multiplicados e, em seguida, adicionados aos resultados retornados a partir de f ().)

    O único momento em que a ordem em que as funções são chamadas faz com que a diferença é, quando a função tem efeitos secundários tais como a abertura de um arquivo ou alterando o valor de uma variável global. Você definitivamente não deve escrever seus programas para que eles dependem deste tipo de efeitos colaterais.

  3. Executar quaisquer conversões de tipo somente quando necessário.

    Você não deve fazer mais conversões de tipo do que o absolutamente necessário. Por exemplo, a seguinte expressão tem pelo menos três e possivelmente quatro conversões de tipo:

    flutuar f = 'a' + 1-

    O caractere 'a' deve ser promovido para um int para realizar a adição. O int é então convertido a um duplo e, em seguida, convertido para baixo para um único flutuador precisão. Lembre-se que toda a aritmética é realizada quer em int ou dupla. Geralmente, você deve evitar a execução de aritmética em tipos de caracteres e evitar precisão única bóia completamente.

5 maneiras de evitar problemas de ponteiro em C ++

Em C ++, uma apontador é uma variável que contém o endereço de um objeto na memória interna do computador. Use estas etapas para evitar problemas com ponteiros em C ++:

  1. Inicializar ponteiros quando declarados.

    Nunca deixe variáveis ​​de ponteiro não inicializado - as coisas não seria muito mau se os ponteiros não inicializados sempre continha valores aleatórios - a grande maioria dos valores aleatórios são valores ponteiro ilegais e fará com que o programa deixe de funcionar assim que eles são usados. O problema é que as variáveis ​​não inicializadas tendem a assumir o valor de outras variáveis ​​de ponteiro, usados ​​anteriormente. Estes problemas são muito difíceis de depurar.

    Se você não sabe o que mais para inicializar um ponteiro para, inicialize-o para nullptr. nullptr é garantido para ser um endereço ilegal.

  2. Zerar os ponteiros depois de usá-los.

    Da mesma forma, sempre zero uma variável ponteiro uma vez que o ponteiro não será mais válida, atribuindo a ela o valor é nullptr. Este é particularmente o caso quando retornar um bloco de memória para a pilha usando Delete sempre zero o ponteiro após o retorno de memória heap.

  3. Alocar memória do heap e devolvê-lo para a pilha ao mesmo "nível" para evitar vazamentos de memória.

    Sempre tentar retornar um bloco de memória para a pilha no mesmo nível de abstração que você alocado-lo. Isso geralmente significa tentar apagar a memória ao mesmo nível de chamadas de função.

  4. Capturar uma exceção para apagar da memória quando necessário.

    Não se esqueça que uma exceção pode ocorrer em quase todo o tempo. Se você pretende capturar a exceção e continuar operacional (em oposição a deixar o programa falhar), certifique-se de que você capturar a exceção e retornar quaisquer blocos de memória para a pilha antes dos ponteiros que apontam para os ir fora do escopo e da memória é perdido.

  5. Certifique-se de que os tipos de corresponder exactamente.

    Sempre certifique-se de que os tipos de ponteiros corresponder ao tipo necessário. Não reformular um ponteiro sem alguma razão específica. Considere o seguinte:

vazio fn (int * p) - vazio myFunc () {char c = 'A'-char * pc = c-fn ((int *) PC) -}

A função acima compila sem reclamar desde o pC ponteiro de caracteres foi reformulado para um int * para coincidir com a declaração de fn (int *) - no entanto, este programa irá quase certamente não funcionar. A função fn () está esperando um ponteiro para um inteiro de 32 bits completo e não um pouco de char rinky-dink 8. Esses tipos de problemas são muito difíceis de resolver.

Como e quando fazer cópias de profundidade em C ++

Classes que alocam recursos em seu construtor deve normalmente incluem um construtor de cópia para criar cópias desses recursos. A inscrição de um novo bloco de memória e copiar o conteúdo do original para este novo bloco é conhecida como a criação de um cópia profunda (Em oposição à cópia superficial padrão). Use as seguintes etapas para determinar como e quando fazer cópias de profundidade em C ++:

  1. Sempre faça uma cópia em profundidade se o construtor aloca recursos.

    Por padrão, C ++ torna os chamados "rasas" membro-a-membro cópias de objetos quando passá-los para funções ou como o resultado de uma atribuição. Você deve substituir os operadores cópia superficial padrão com seu equivalente cópia em profundidade para qualquer classe que aloca recursos no construtor. O recurso mais comum que é alocada é a memória heap que é devolvido pelo novo operador.

  2. Sempre inclua um destrutor para uma classe que aloca recursos.

    Se você criar um construtor que aloca recursos, é necessário criar um processo de destruição que os restaura. Sem exceções.

  3. Sempre declare o destruidor virtual.

    Um erro de principiante comum é esquecer de declarar seu destrutor virtual. O programa será executado muito bem até que algum programador desavisado vem e herda de sua classe. O programa ainda parece funcionar, mas porque o destruidor na classe base não pode ser invocado corretamente, vazamentos de memória de seu programa como uma peneira até que finalmente cai. Este problema é difícil de encontrar.

  4. Sempre inclua um construtor de cópia para uma classe que aloca recursos.

    O construtor de cópia cria uma cópia apropriada do objeto atual de alocação de memória fora do heap e copiar o conteúdo do objeto de origem.

  5. Sempre substituir o operador de atribuição para uma classe que aloca recursos.

    Os programadores devem ser desencorajados de operadores de maior relevo, mas o operador de atribuição é uma exceção. Você deve substituir o operador de atribuição para qualquer classe que aloca recursos no construtor.

    O operador de atribuição deve fazer três coisas:

  1. Certifique-se de que o objeto da mão esquerda e direita não são o mesmo objeto. Em outras palavras, certifique-se de que o programador da aplicação não escrever algo como (a = a). Se forem, não fazer nada.

  2. Invocar o mesmo código que o destruidor sobre o objeto da mão esquerda para voltar seus recursos.

  3. Invocar o mesmo código como um construtor de cópia para fazer uma cópia profunda do objeto mão direita para o objeto da mão esquerda.

  • Se você não pode fazer isso, então excluir o construtor de cópia e operador de atribuição para que o programa não pode fazer cópias de seu objeto.

  • Se você não pode mesmo fazer isso porque seu compilador não suporta o recurso de construtor de exclusão C ++ 2011, criar uma cópia construtor e atribuição operador vazio e declará-los protegido para manter outras classes de usá-los.

  • menu