Como inicializar memória com novo operador em C ++?


176

Estou apenas começando a entrar em C ++ e quero pegar alguns bons hábitos. Se eu acabei de alocar uma matriz de tipos intcom o newoperador, como posso inicializá-los todos para 0 sem repetir todos eles? Devo apenas usar memset? Existe uma maneira "C ++" de fazer isso?


19
Se você deseja adquirir um bom hábito de C ++, evite usar matrizes diretamente e use vetor. O vetor inicializará todos os itens, independentemente do tipo, e você não precisará se lembrar de chamar o operador delete [].
Brianegge

@ brianegge: E se eu precisar passar uma matriz para uma função C externa, posso apenas dar o vetor?
dreamlax

12
Você pode passar &vector[0].
jamesdlin

Obviamente, quando você passa matrizes para funções C, normalmente é necessário especificar o ponteiro para o primeiro elemento, & vector [0] como @jamesdlin disse, e o tamanho da matriz, fornecido neste caso por vector.size ().
Trebor rude

Relacionado (solicita tipos que não são de matriz): stackoverflow.com/questions/7546620/…
Aconcagua

Respostas:


392

É um recurso surpreendentemente pouco conhecido do C ++ (como evidenciado pelo fato de que ninguém deu isso como resposta ainda), mas na verdade possui uma sintaxe especial para inicializar um array com valor:

new int[10]();

Observe que você deve usar os parênteses vazios - não é possível, por exemplo, usar (0)ou qualquer outra coisa (é por isso que isso é útil apenas para inicialização de valor).

Isso é explicitamente permitido pela ISO C ++ 03 5.3.4 [expr.new] / 15, que diz:

Uma nova expressão que cria um objeto do tipo Tinicializa esse objeto da seguinte maneira:

...

  • Se o novo inicializador for do formato (), o item será inicializado por valor (8.5);

e não restringe os tipos para os quais isso é permitido, enquanto o (expression-list)formulário é explicitamente restrito por outras regras na mesma seção, de forma que não permita tipos de matriz.


1
Embora eu concorde que isso seja pouco conhecido, não posso (inteiramente) concordar que é realmente muito surpreendente - foi adicionado no C ++ 03, que a maioria das pessoas parece ter quase ignorado (já que essa foi uma das poucas coisas novas acrescentou).
Jerry Coffin

2
@ Jerry: Devo admitir que ainda não sabia (provavelmente porque quando cheguei à leitura do padrão, já era C ++ 03). Dito isto, é notável que todas as implementações que conheço suportem isso (acho que é porque é tão trivial de implementar).
Pavel Minaev

2
Sim, é bastante trivial de implementar. Quanto a ser novo, tudo "inicialização valor" era novo em C ++ 03.
Jerry Coffin

34
Em C ++ 11 você pode usar initializtion uniforme, bem como: new int[10] {}. Você também pode fornecer valores para inicializar com:new int[10] {1,2,3}
bames53 13/08

Por favor, não confunda inicializado por padrão com inicializado por valor: ambos estão claramente definidos no padrão e são diferentes inicializações.
Deduplicator

25

Supondo que você realmente deseja uma matriz e não um std :: vector, a "maneira C ++" seria esta

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable

std::fill_n(array, n, 0); 

Mas lembre-se de que, na verdade, isso ainda é apenas um loop que atribui cada elemento a 0 (realmente não há outra maneira de fazer isso, exceto uma arquitetura especial com suporte no nível do hardware).


Eu não me importo se o loop for implementado sob uma função, eu só queria saber se eu mesmo deveria implementar esse loop. Obrigado pela dica.
dreamlax

4
Você pode se surpreender. Eu fui. No meu STL (GCC e Dinkumware), std :: copy na verdade se transforma em um memcpy se ele detectar que está sendo chamado com tipos incorporados. Eu não ficaria surpreso se std :: fill_n usasse o memset.
Brian Neal

2
Não. Use 'Value-Initialization' para definir todos os membros como 0.
Martin York

24

Há um número de métodos para alocar uma matriz do tipo intrínseco e todos esses métodos estão corretos, apesar de qual escolher, depende ...

Inicialização manual de todos os elementos em loop

int* p = new int[10];
for (int i = 0; i < 10; i++)
{
    p[i] = 0;
}

Usando a std::memsetfunção de<cstring>

int* p = new int[10];
std::memset(p, 0, sizeof(int) * 10);

Usando o std::fill_nalgoritmo de<algorithm>

int* p = new int[10];
std::fill_n(p, 10, 0);

Usando std::vectorcontêiner

std::vector<int> v(10); // elements zero'ed

Se C ++ 0x estiver disponível, usando os recursos da lista do inicializador

int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime

1
deve ser o vetor <int> Se você adicionou p = new int [10] (), você tinha uma lista completa.
Karsten

@mloskot, no primeiro caso em que você inicializou uma matriz usando "new", como a passagem por referência acontecerá? Se eu usei int array[SIZE] ={1,2,3,4,5,6,7};notação, posso usar a void rotateArray(int (& input)[SIZE], unsigned int k);minha declaração de função, o que seria ao usar a primeira convenção? alguma sugestão?
Anu

1
Receio que o exemplo std::memsetesteja errado - você passa 10, parece esperar número de bytes - consulte en.cppreference.com/w/cpp/string/byte/memset . (Acho que isso mostra muito bem por isso deve-se evitar tal construção de baixo nível quando possível.)
Suma

@Suma Grande captura! Fixo. Este parece ser um candidato a um bug de uma década :-) Sim, eu concordo com o seu comentário.
mloskot

7

Se a memória que você está alocando for uma classe com um construtor que faz algo útil, o operador new chamará esse construtor e deixará seu objeto inicializado.

Mas se você estiver alocando um POD ou algo que não tenha um construtor que inicialize o estado do objeto, não poderá alocar memória e inicializá-la com o operador new em uma operação. No entanto, você tem várias opções:

1) Use uma variável de pilha. Você pode alocar e inicializar por padrão em uma etapa, assim:

int vals[100] = {0};  // first element is a matter of style

2) use memset(). Observe que, se o objeto que você está alocando não for um POD , definir incorretamente é uma má idéia. Um exemplo específico é que, se você configurar uma classe que possui funções virtuais, você explodirá a tabela e deixará seu objeto em um estado inutilizável.

3) Muitos sistemas operacionais têm chamadas que fazem o que você deseja - aloque em uma pilha e inicialize os dados em algo. Um exemplo do Windows seriaVirtualAlloc()

4) Geralmente é a melhor opção. Evite ter que gerenciar a memória você mesmo. Você pode usar contêineres STL para fazer praticamente tudo o que faria com a memória bruta, incluindo alocar e inicializar tudo de uma só vez:

std::vector<int> myInts(100, 0);  // creates a vector of 100 ints, all set to zero

6

Sim existe:

std::vector<int> vec(SIZE, 0);

Use um vetor em vez de uma matriz alocada dinamicamente. Os benefícios incluem não ter que se preocupar em excluir explicitamente a matriz (ela é excluída quando o vetor fica fora do escopo) e também que a memória é excluída automaticamente, mesmo se houver uma exceção lançada.

Edit: Para evitar mais downvotes drive-by de pessoas que não se preocupam em ler os comentários abaixo, devo deixar mais claro que essa resposta não diz que vetor é sempre a resposta certa. Mas com certeza é uma maneira mais C ++ do que "manualmente", certificando-se de excluir uma matriz.

Agora, com o C ++ 11, também existe o std :: array que modela uma matriz de tamanho constante (vetor vs capaz de crescer). Também existe std :: unique_ptr que gerencia uma matriz alocada dinamicamente (que pode ser combinada com a inicialização conforme respondido em outras respostas a esta pergunta). Qualquer uma dessas maneiras é mais C ++ do que manipular manualmente o ponteiro para a matriz, IMHO.


11
isso realmente não responde à pergunta que foi feita.
John Knoeller

1
Devo sempre usar em std::vectorvez de matrizes alocadas dinamicamente? Quais são os benefícios de usar uma matriz sobre um vetor e vice-versa?
dreamlax

1
@ John Knoller: O OP perguntou sobre uma maneira C ++ de fazer isso, eu diria que o vetor é a maneira c ++ de fazer isso. Obviamente, você está certo de que pode haver situações que ainda exigiriam uma matriz simples e, sem saber a situação do OP, essa pode ser uma. Eu acho que não, pois parece plausível que o OP não saiba sobre vetores.
Villintehaspam

1
@illintehaspam: Embora essa solução não responda à minha pergunta, é o caminho que vou seguir. Tyler McHenry responde à minha pergunta mais diretamente e deve ajudar especialmente as pessoas que não podem - por qualquer motivo - usar std::vector.
dreamlax

2
@illintehaspam: Não, não é uma maneira C ++ de fazer isso. É a maneira Java de fazer isso. Permanecer vectorem qualquer lugar, independentemente do contexto, é chamado "Escrevendo código Java em C ++".
AnT

2

std::fillé uma maneira. Leva dois iteradores e um valor para preencher a região. Esse, ou o loop for, (suponho) seria a maneira mais C ++.

Para definir uma matriz de tipos inteiros primitivos como 0 especificamente, memseté bom, embora possa levantar sobrancelhas. Considere também calloc, embora seja um pouco inconveniente usar o C ++ por causa do elenco.

Da minha parte, eu quase sempre uso um loop.

(Não gosto de adivinhar as intenções das pessoas, mas é verdade que std::vectortodas as coisas são iguais, preferível a usar new[].)


1

você sempre pode usar o memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));

Entendo que posso usar memset, mas não tinha certeza se essa era a maneira C ++ de abordar o problema.
dreamlax

1
Não é realmente o 'jeito C ++', mas também não são matrizes brutas.
Pete Kirkham

1
@ gbrandt: O que quer dizer que não funciona muito bem em C ou C ++. Ele funciona para a maioria dos valores do tipo é char ou char não assinado. Funciona para a maioria dos tipos de valor é 0 (pelo menos na maioria das implementações). Caso contrário, geralmente é inútil.
Jerry Coffin

1
10 * sizeof( *myArray )é mais documentado e à prova de alterações do que 10 * sizeof( int ).
Kevin Reid

1
De qualquer forma, o OP tem uma matriz bruta e o memset é a maneira mais rápida e fácil de zerar essa matriz.
Gregor Brandt

-1

Normalmente, para listas dinâmicas de itens, você usa a std::vector.

Geralmente, eu uso o memset ou um loop para alocação dinâmica de memória bruta, dependendo de quão variável eu prevejo que essa área de código seja no futuro.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.