Antipadrão constante de tamanho de array

A tradução do artigo foi preparada em antecipação ao início do curso “Desenvolvedor C ++. Profissional " .








Quero chamar sua atenção para o antipadrão, que frequentemente encontro no código de alunos no Code Review StackExchange e até mesmo em um número bastante grande de materiais educacionais (!) De outras pessoas. Eles têm uma matriz de, digamos, 5 elementos; e então, como os números mágicos são ruins, eles introduzem uma constante nomeada para representar a cardinalidade de "5".



void example()
{
    constexpr int myArraySize = 5;
    int myArray[myArraySize] = {2, 7, 1, 8, 2};
    ...




Mas a solução é mais ou menos! No código acima, o número cinco é repetido : primeiro no valor myArraySize = 5e depois novamente quando você realmente atribui os elementos myArray. O código acima é tão terrível do ponto de vista de manutenção quanto:



constexpr int messageLength = 45;
const char message[messageLength] =
    "Invalid input. Please enter a valid number.\n";




- que, é claro, nenhum de nós jamais escreverá.



Código repetido não é bom



Observe que em ambos os trechos de código acima, cada vez que você altera o conteúdo da matriz ou o texto da mensagem, você deve atualizar duas linhas de código em vez de uma. Aqui está um exemplo de como um mantenedor pode atualizar este código incorretamente :



   constexpr int myArraySize = 5;
-   int myArray[myArraySize] = {2, 7, 1, 8, 2};
+   int myArray[myArraySize] = {3, 1, 4};




O patch acima parece que muda o conteúdo do array de 2,7,1,8,2 para 3,1,4 , mas não é! Na verdade, ele muda para 3,1,4,0,0 - com preenchimento de zeros - porque o mantenedor se esqueceu de ajustar de myArraySizeacordo com myArray.



Abordagem confiável



Quanto à contagem, os computadores são muito bons nisso. Então deixe o computador contar!



int myArray[] = {2, 7, 1, 8, 2};
constexpr int myArraySize = std::size(myArray);




Agora você pode alterar o conteúdo da matriz, digamos de 2,7,1,8,2 a 3,1,4 , alterando apenas uma linha de código. Você não precisa duplicar a mudança em qualquer lugar.



Ainda mais, myArrayo código real geralmente usa loops fore / ou algoritmos baseados no intervalo do iterador para manipulá-lo , portanto, não precisa de uma variável nomeada para armazenar o tamanho do array.



for (int elt : myArray) {
    use(elt);
}
std::sort(myArray.begin(), myArray.end());
std::ranges::sort(myArray);

// Warning: Unused variable 'myArraySize'




A versão "ruim" desse código é myArraySizesempre usada (na declaração myArray) e, portanto, o programador provavelmente não verá que ele pode ser excluído. Na versão "boa", é fácil para o compilador detectar o que myArraySizenão está sendo usado.



Como fazer isso com std::array?



Às vezes, um programador dá mais um passo em direção ao Lado Negro e escreve:



constexpr int myArraySize = 5;
std::array<int, myArraySize> myArray = {2, 7, 1, 8, 2};




Isso deve ser reescrito como, pelo menos:



std::array<int, 5> myArray = {2, 7, 1, 8, 2};
constexpr int myArraySize = myArray.size();  //  std::size(myArray)




Porém, não há uma maneira fácil de se livrar da contagem manual na primeira linha. CTAD C ++ 17 permite que você escreva



std::array myArray = {2, 7, 1, 8, 2};




mas isso só funciona se você precisar de um array int- não funcionará se você precisar de um array short, por exemplo, ou um array uint32_t.



C ++ 20 nos dá std :: to_array , que nos permite escrever



auto myArray = std::to_array<int>({2, 7, 1, 8, 2});
constexpr int myArraySize = myArray.size();




Observe que isso cria uma matriz C e, em seguida, move (move-constrói) seus elementos para dentro std::array. Todos os nossos exemplos anteriores foram inicializados myArraycom uma lista de inicializadores com colchetes que acionou a inicialização de agregação e instanciou os elementos do array no lugar.



Em qualquer caso, todas essas opções resultam em um grande número de instâncias de template adicionais em comparação com os velhos arrays C (que não requerem instanciação de template). Portanto, eu prefiro fortemente T[]o mais recente std::array<T, N>.



Em C ++ 11 e C ++ 14, std::arrayhavia uma vantagem ergonômica em ser capaz de dizer arr.size(); mas essa vantagem evaporou quando o C ++ 17 nos forneceustd::size(arr)e para matrizes embutidas. Não std::arrayhá mais vantagens ergonômicas. Use-o se quiser toda a semântica de variável de objeto (passe todo o array para uma função! Retorne um array de uma função! Atribua arrays com =! Compare arrays com ==!), Mas caso contrário, recomendo evitar o uso std::array.



Da mesma forma, recomendo evitá- std::listlo, a menos que você queira a estabilidade de seu iterador, colagem rápida, classificação sem substituir elementos, e assim por diante.Eu não estou dizendo que não há lugar para esses tipos em C ++; Só estou dizendo que eles têm um "conjunto muito específico de habilidades" e, se você não usar essas habilidades, provavelmente está pagando caro demais.




Conclusões: não cerque a carroça na frente do cavalo. Na verdade, o carrinho pode nem ser necessário. E se você precisar usar a zebra para fazer o trabalho do cavalo, também não deve cercar a carroça na frente da zebra.





Consulte Mais informação:






All Articles