
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 = 5
e 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
myArraySize
acordo 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,
myArray
o código real geralmente usa loops for
e / 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 é
myArraySize
sempre 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 myArraySize
nã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 myArray
com 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::array
havia 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::array
há 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::list
lo, 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: