Devem ser usados ​​arrays em C ++?

95

Uma vez que std::liste std::vectorexiste, há uma razão para usar arrays C tradicionais em C ++, ou eles devem ser evitados, assim como malloc?

Andreas
fonte
18
@Als: Essa questão se refere à diferença entre dois contêineres específicos, enquanto esta pergunta se refere à diferença entre matrizes brutas e contêineres padrão em geral.
Jon Purdy

Respostas:

109

Em C ++ 11, onde std::arrayestá disponível, a resposta é "sim, matrizes devem ser evitadas". Antes do C ++ 11, você pode precisar usar arrays C para alocar arrays no armazenamento automático (ou seja, na pilha).

dasblinkenlight
fonte
3
muitos compiladores ainda não têm suporte para C ++ 11. Você terá que decidir quando é melhor usar um do que o outro, dada a falta de std :: array
Nowayz
4
std :: array é um modelo, que impacta grandes projetos em termos de tempo de construção e possivelmente o tamanho do código, uma vez que para cada combinação de T, N o modelo é instanciado novamente.
zvrba
std :: vector garante o alinhamento dos dados por padrão, então pode ser usado em quase todos os lugares. Com C ++ 11, realmente não há razão para usar arrays C.
Nils
16
Os arrays @Nils também garantem o alinhamento. Além disso, a alocação automática de armazenamento ("a pilha") é muito mais rápida do que a alocação dinâmica de armazenamento. Se eu sei que tenho exatamente 3 elementos [por exemplo, coordenadas de um triângulo], não há razão para usar vetor.
zvrba de
9
@zvrba - Verifique o assembly gerado ao usar matrizes std :: array vs C. Nenhuma diferença.
Nemanja Trifunovic
85

Definitivamente, embora std::arrayem C ++ 11, praticamente apenas para dados estáticos. As matrizes de estilo C têm três vantagens importantes sobre std::vector:

  • Eles não exigem alocação dinâmica. Por esse motivo, os arrays de estilo C devem ser preferidos quando é provável que haja muitos arrays muito pequenos. Diga algo como um ponto de dimensão n:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };

    Normalmente, pode-se imaginar um que dimsserá muito pequeno (2 ou 3), Tum tipo embutido ( double), e que você pode acabar std::vector<Point>com milhões de elementos. Você definitivamente não quer milhões de alocações dinâmicas de 3 duplos.

  • A inicialização estática de suporte. Este é um problema apenas para dados estáticos, onde algo como:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };

    Isso geralmente é preferível a usar um vetor (e std::string), uma vez que evita todos os problemas de ordem de inicialização; os dados são pré-carregados, antes que qualquer código real possa ser executado.

  • Finalmente, relacionado ao acima, o compilador pode calcular o tamanho real do array a partir dos inicializadores. Você não tem que contá-los.

Se você tiver acesso ao C ++ 11, std::arrayresolve os dois primeiros problemas e deve definitivamente ser usado em preferência aos arrays de estilo C no primeiro caso. Ele não aborda o terceiro, entretanto, e ter o compilador dimensionar o array de acordo com o número de inicializadores ainda é uma razão válida para preferir arrays de estilo C.

James Kanze
fonte
11
A inicialização de array no estilo C também elimina a necessidade de repetição. int i[] = { 1, 2, 3 };continua trabalhando com int i[] = { 1, 2, 3, 4 };. array<int, 3>precisa ser alterado manualmente para array<int, 4>.
10
@JoeWreschnig Uma mudança que você pode esquecer facilmente. Se você adicionar um elemento, o compilador deve reclamar, mas se você remover um, acabará com um elemento extra, 0 inicializado no final. Ainda uso arrays de estilo C extensivamente para esse tipo de dados estáticos.
James Kanze
3
A primeira frase não faz sentido.
Konrad Rudolph
4
O terceiro ponto pode ser resolvido elegantemente usando uma make_arrayfunção , semelhante a make_pairetc. Gorjeta para @R. Martinho Fernandes .
Konrad Rudolph
@KonradRudolph: Claro que sim. Andreas pergunta “Devem ser usados ​​arrays em C ++?”, Ao que James responde “Definitivamente, embora com std::arrayem C ++ 11, [eles devam ser usados] praticamente apenas para dados estáticos”.
Jon Purdy
15

Nunca diga "nunca", mas concordo que seu papel é muito diminuído por verdadeiras estruturas de dados do STL.

Eu também diria que o encapsulamento dentro dos objetos deve minimizar o impacto de escolhas como essa. Se a matriz for um membro de dados privado, você pode trocá-la dentro ou fora sem afetar os clientes de sua classe.

duffymo
fonte
11

Trabalhei em sistemas críticos de segurança, onde você não consegue usar a alocação dinâmica de memória. A memória deve estar sempre na pilha. Portanto, neste caso, você usaria matrizes, pois o tamanho é fixo no momento da compilação.

Ed Heal
fonte
8
Antes do C ++ 11, eu teria concordado, mas std::array<T>aloca nas pilhas e basicamente não tem sobrecarga em uma matriz bruta.
111111
5
@ 111111 - Concordo. Mas eu sei de algumas pessoas nessa indústria que ainda não mudaram para C ++ 11
Ed Heal
Eu sei que é por isso que não votei contra você, mas acho que o boost tinha uma versão e é fácil rolar a sua própria também.
111111
6
mas em sistemas críticos de segurança, você não usa novos recursos do compilador (menos testados) e não usa boost.
James Kanze
3
Muitos sistemas críticos de segurança são construídos em compiladores OLD que nem mesmo possuem os recursos de compilador mais recentes, porque a troca de cadeias de ferramentas é um processo lento e caro que requer toneladas de papelada, testes e certificações.
Brian McFarland
6

arrayem c++dá-lhe fixo tamanho rápida alternativa de dinâmica de tamanhostd::vector e std::list. std :: array é uma das adições em c++11. Ele fornece o benefício de contêineres std ao mesmo tempo em que fornece a semântica do tipo agregado de arrays de estilo C.

Então, c++11eu certamente usaria std::array, onde for necessário, mais do que vetor. Mas eu evitaria o array de estilo C no C++03.

Vikas
fonte
4

Normalmente, não , não consigo pensar em uma razão para usar matrizes brutas em vez de, digamos vectors,. Se o código for novo .

Você pode ter que recorrer ao uso de matrizes se suas bibliotecas precisarem ser compatíveis com o código que espera matrizes e ponteiros brutos.

Luchian Grigore
fonte
1
... mas desde C ++ 03 um vetor "realmente tem" um array, que você pode acessar por ponteiro para ler ou escrever. Isso cobre a maioria dos casos de código que esperam ponteiros para matrizes. Só quando o código aloca ou libera o array é que você não pode usar um vetor.
Steve Jessop
@SteveJessop você pode acessar o array interno?
Luchian Grigore
1
@LuchianGrigore: vector.data()em C ++ 11 ou &vector.front()anterior.
Mike Seymour
@Luchian: desde que o vetor não esteja vazio, você pode levar um ponteiro para um elemento (e se estiver vazio, você pode passar um ponteiro nulo e um comprimento de 0 para qualquer função escrita sensatamente que aceite o caso extremo de um buffer de tamanho zero). Praticamente, o único propósito da garantia de contiguidade do vetor adicionada no C ++ 03 era permitir que os vetores fossem usados ​​como buffers por código orientado a ponteiros.
Steve Jessop
1
@SteveJessop E o fato de que muita gente pensava que estava garantido de qualquer maneira, e era considerado preferível não decepcioná-los.
James Kanze
4

Eu sei que muitas pessoas estão apontando std :: array para alocar matrizes na pilha e std :: vector para o heap. Mas nenhum dos dois parece oferecer suporte ao alinhamento não nativo. Se você estiver fazendo qualquer tipo de código numérico no qual deseja usar as instruções SSE ou VPX (exigindo, portanto, um alinhamento de 128 ou 256 bytes, respectivamente), os arrays C ainda parecem ser sua melhor aposta.

gct
fonte
3

Eu diria que os arrays ainda são úteis, se você estiver armazenando uma pequena quantidade estática de dados, por que não.

James82345
fonte
2

A única vantagem de um array (é claro, envolvido em algo que irá gerenciar automaticamente sua desalocação quando necessário) sobre o std::vectorque posso pensar é que vectornão pode passar a propriedade de seus dados, a menos que seu compilador suporte C ++ 11 e mova construtores.

Tadeusz Kopec
fonte
6
"vetor não pode passar a propriedade de seus dados" - sim, pode, em C ++ 03, usando swap.
Steve Jessop
2

Os arrays de estilo C são uma estrutura de dados fundamental, portanto, haverá casos em que será melhor usá-los. Para o caso geral, entretanto, use as estruturas de dados mais avançadas que arredondam os cantos dos dados subjacentes. C ++ permite que você faça coisas muito interessantes e úteis com memória, muitas das quais funcionam com matrizes simples.

James Wynn
fonte
3
Como os arrays de estilo C são mais fundamentais do que std::arrays? Ambos serão, em muitos casos, compilados no mesmo assembly.
esquerda por volta de
1
Mais fundamental porque é mais básico. Você sabe o que um array vai fazer, std :: array pode ter peculiaridades de implementação, já que depende da biblioteca padrão.
James Wynn
1
@JamesWynn Na verdade, não. std::arraytem semântica definida com precisão construída sobre matrizes estáticas.
Konrad Rudolph
1

Você deve usar contêineres STL internamente, mas não deve passar ponteiros para tais contêineres entre módulos diferentes, ou você acabará no inferno da dependência. Exemplo:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

é uma solução muito boa, mas não

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

A razão é que std :: string pode ser implementado de muitas maneiras diferentes, mas uma string de estilo c é sempre uma string de estilo c.

user877329
fonte
Acho que o que você está tentando dizer é: Não vincule códigos-objetos diferentes se diferentes compiladores / implementações da biblioteca padrão foram usados ​​para criá-los. Certamente é verdade. Como isso se relaciona com a pergunta original?
jogojapan
É apenas um conselho quando usar arrays ou contêineres STL. Crie dados usando um contêiner e transmita-os como uma matriz. Para outros dados que são strings, você teria algo como myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329
Mas esses problemas surgem apenas quando você combina diferentes implementações da biblioteca padrão no mesmo projeto. Isso é louco. Em qualquer parte normal do código, é perfeitamente normal passar, digamos, um vetor, por referência (ou, em C ++ 11, movê-lo) para uma função.
jogojapan
1
Acontece que gosto de plug-ins
user877329