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?

dreamlax
fonte
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.

Pavel Minaev
fonte
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).

Tyler McHenry
fonte
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
mloskot
fonte
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
John Dibling
fonte
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.

villintehaspam
fonte
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[].)


fonte
1

você sempre pode usar o memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
Gregor Brandt
fonte
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.

Paul Nathan
fonte