3 Réponses :
Le code suivant en C ++ n'a aucun avantage:
A* d[3]; for (int i=0;i<3;i++) d[i]=new A(i);
En fait, vous ne devriez pas du tout l'écrire.
Votre solution avec un tableau temporaire déclaré localement est valide, mais a la limitation significative d'exiger que le nombre d'éléments dans le tableau soit une constante de compilation (c'est-à-dire connue à l'avance).
Si vous avez besoin d'un tableau avec un nombre dynamique d'éléments, connu uniquement au moment de l'exécution, vous devez utiliser un std::vector
, qui effectue la new
allocation automatiquement. C'est la manière idiomatique de travailler avec des tableaux alloués dynamiquement en C ++.
Il existe une situation courante supplémentaire où la baie locale n'est pas réalisable. Le cadre de la pile est généralement assez petit et si le tableau est grand et / ou chaque élément utilise une mémoire importante - cette solution entraînera une corruption de la pile
Tableau de pointeurs -> allocation de mémoire lorsque vous créez un objet et que vous l'assignez au pointeur. Tableau de 1000 pointeurs avec 1 élément à l'intérieur = 1x la mémoire.
Tableau d'objets -> allocation de mémoire lorsque vous créez un tableau. Tableau de 1000 avec 1 élément à l'intérieur = 1000x la mémoire.
1) A d[3]
, il crée un tableau intégré (ou tableau C-style, simple, nu ...) qui contient (trois) éléments de type A
, ces éléments vivent sur la pile (selon votre code ).
2) A* d[3];
, il crée un tableau intégré qui contient (trois) pointeurs vers des éléments de type A
(ou ils devraient). Cela ne signifie pas que l'élément pointé est de type A
, c'est la tâche du développeur de créer ces éléments, et vous pouvez le faire de deux manières, en utilisant la pile (comme précédemment):
std::array<std::unique_ptr<A,3> > d;
ou en utilisant le tas (avec new
/ new[]
et delete
/ delete[]
):
std::vector<std::unique_ptr< A> > d(3);
Effets secondaires de la deuxième option; vous pouvez pointer vers un élément déjà détruit (c'est-à-dire que l'élément est hors de portée), d'où une erreur de segment potentielle; vous risquez de perdre le pointeur vers la mémoire allouée (c'est-à-dire que le tableau de pointeurs est hors de portée et que la mémoire allouée n'a pas été libérée), d'où une fuite de mémoire.
Cependant, un avantage est que vous pouvez utiliser l'initialisation paresseuse (vous ne payez pas pour ce que vous n'utilisez pas). Si vous n'avez pas besoin d'un objet A
dans d[2]
(ou pointé par), pourquoi devriez-vous le créer et gaspiller de la mémoire? Et ici, les pointeurs intelligents surgissent, ils vous facilitent la vie en vous permettant d'allouer de la mémoire uniquement lorsque vous en avez besoin, réduisant le risque de défauts de segmentation (il vaut la peine de mentionner std :: low_ptr ), les fuites de mémoire, etc.
std::unique_ptr<A> d[3]; d[0] = std::make_unique<A>(); // Call make_unique when you need it
Mais encore un peu désagréables, les tableaux intégrés sont bons mais peuvent être bien meilleurs dans certaines circonstances. Il est difficile d'ajouter ou de supprimer des éléments, d'itérer, d'obtenir la taille, d'accéder à la queue / tête, etc ... Et c'est pourquoi les conteneurs sont assez utiles, c'est-à-dire std::array
ou std::vector
(où sa taille ne fait pas) doivent être connus au moment de la compilation):
d[0] = new A; ... delete d[0];
A element; ... d[0] = &element;
Merci! mais comme je l'ai dit, je ne peux pas utiliser de conteneurs STL (c'est une affectation uni) donc je pense que je suis obligé d'utiliser un tableau de pointeurs dans ma classe (de type A*
) car mon A
n'est pas constructible par défaut. Donc je peux faire a1[i]=A(0)
si A
est constructible par défaut (puisque a1 [i] aurait déjà été créé) mais si je ne le fais pas (comme dans mon cas), je devrais créer un tableau de pointeurs et donner à chaque élément le retour de new(constructor arguments)
?
Oui, vous pouvez, mais comme indiqué précédemment, vous devrez vous assurer que le tableau de pointeurs et les éléments pointés ont la même portée, afin de pouvoir delete
les éléments avant que le tableau de pointeurs ne soit détruit.
Oui, compris. Mais mon raisonnement quant à la raison pour laquelle je ne peux pas faire a1[i]=...
dans ce cas est-il correct? (que a1 [i] ne peut pas être construit par défaut)
Votre version est meilleure, car elle évite (apparemment) les allocations de tas et les pointeurs inutiles. Malheureusement, le niveau de l'enseignement C ++ n'est pas très élevé.
La première version alloue de la mémoire sur la pile, tandis que la seconde alloue de la mémoire sur le tas. Comprenez-vous la différence entre la pile et le tas?
Concernant le suivi. La meilleure alternative est généralement d'utiliser
std::vector<A> a1;
vector ne nécessite pas que son type soit constructible par défaut.J'ai omis des informations cruciales concernant mon problème, je pense que maintenant c'est plus clair (sans vouloir donner une mauvaise réputation à mon professeur, j'ai mieux décrit la raison pour laquelle je pense que nous ne devrions pas utiliser les objets temporaires)
Aussi @john, malheureusement, je ne suis actuellement pas autorisé à utiliser des conteneurs STL, est-ce que l'EDIT est donc impossible à utiliser? Je ne peux donc utiliser que
A* a1[3]
comme champ dans maclass B
@pol Il n'y a pas d'objets temporaires. Votre compréhension que
A d[3] = {A(0), A(1), A(2)};
construit puis rejette des objets temporaires est incorrect.@pol Vous pouvez utiliser une technique appelée placement nouveau pour contourner les problèmes de ne pas avoir de constructeur par défaut. C'est ce que
std::vector
utilise dans son implémentation. Mais oui, le code édité, exactement tel qu'il est, nécessite un constructeur par défaut.Je ne comprends pas très bien votre avant-dernier commentaire. Les objets A (0), A (1), A2) ne sont-ils pas temporaires? Ils sont copiés dans le tableau (en utilisant le cteur de copie), puis chaque temporaire est détruit mais leurs valeurs vivent dans le tableau. Et quant à votre dernier commentaire, je suis donc "obligé" d'utiliser un tableau de pointeurs dans ce cas? (pas de conteneurs STL comme je l'ai dit) ou y a-t-il un moyen «plus propre»? Merci!