Je rencontre un problème avec un vector. en effet ce dernier a un comportement très étrange que je ne comprends pas. voici le code en question:
void patternsRecoAlignement::Corrector::__buildPatterns() {
for (int i = 0; i < this->sequence->size(); i++) {
this->stars->at(i) = *this->sequence->getStars(i);
patternsRecoAlignement::dataStructures::Pattern* p = nullptr;
for (int j = 0; j < this->stars->at(i).size(); j++) {
std::vector<std::pair<float, float>>* closestStars = this->__findClosestStars(i, j);
std::vector<patternsRecoAlignement::dataStructures::Triangle>* triangles = this->__buildTriangles(closestStars);
p = new patternsRecoAlignement::dataStructures::Pattern(triangles);
this->patterns->at(i).push_back(*p); //ligne a problèmes... patterns est un vector<vector<Pattern>>*
delete closestStars;
}
}
}
quand i =0 et j = 0, tout ce passe bien :
par contre, pour i =0, j =1 c'est le dramme:
Pour je ne sais qu'elle raison, quand la fonction push_back est appelé, et l'object en [0] a été déréférencé. Du coup, je me retrouve avec une violation mémoir, pour j =2
Ton code est tres suspicieux. En particulier ton usage des pointeurs. Je ne serais pas surpris que le problème ne vienne pas de vector et la ligne que tu as indiqué.
Donne le code des structures que tu utilises et des fonctions (sequence, getStars, __findClosestStars, etc)
A mon avis, vire tes pointeurs. Leur utilisation est étrange et on fait jamais de pointeur de vector. Et si tu veux réellement utiliser des pointeurs, utilise unique_ptr.
Evite les noms qui commencent avec __ ou _X (majuscule), c'est réservé dans certains cas.
Evite "this->" partout, c'est lourd a la lecture. Utilise les range for loop et auto pour alleger le code aussi.
Ici n'a pas les sources qui permettraient de voir et encore moins de reproduire le problème, donc c'est difficile de dire pourquoi.
Cependant on voit une tendance à utiliser explicitement des adresses et des déreférencements explicites (opérateur *).
A tout hasard, un truc qui pose souvent problème avec ce genre de notions, c'est qu'un vecteur contient des données qui peuvent déménager quand on ajoute des éléments dans un vecteur (à cause de la réallocation du tableau dynamique sous-jacent au vecteur)
Un exemple : le programme ci-dessous ajoute 0,1,.... 999 dans un vecteur d'entiers. Et regarde si par hasard ça n'aurait pas fait changer l'adresse du premier élément (et des suivants évidemment) :
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vecteur;
int *adresse_premier = nullptr;
for (int i = 0; i < 1000; i++) {
vecteur.push_back(i);
if (adresse_premier != & vecteur[0]) {
std::cout << "déménagement ajout de " << i << std::endl;
adresse_premier = & vecteur[0];
}
}
return EXIT_SUCCESS;
}
Compilation et exécution
$ make
g++ -std=c++20 -Wall -Wextra -pedantic -Werror -Wno-unused -g prog.cc -o prog
$ ./prog
déménagement ajout de 0
déménagement ajout de 1
déménagement ajout de 2
déménagement ajout de 4
déménagement ajout de 8
déménagement ajout de 16
déménagement ajout de 32
déménagement ajout de 64
déménagement ajout de 128
déménagement ajout de 256
déménagement ajout de 512
Exercice : en déduire
le prochain ajout qui provoquera un déménagement
la stratégie de réallocation utilisée pour l'extension du tableau dynamique des vecteurs dans cette implémentation.
Ici, peut être que findClosestStart retourne une adresse d'un truc qui déménage ensuite à cause d'un push_back quelque part, et donc que closestStar est un pointeur contenant une adresse devenue invalide. Ou p ?
Aussi : autant que possible, déclarer les variables seulement au moment où on est capable de leur donner une valeur qui a un sens. Et en général, avec le type auto si il fauit le job (déclarer explicitement un type, c'est indiquer au lecteur que le type auto ne convient pas, et il faudrait expliquer pourquoi). Le pointeur p - si il doit exister - pourrait être déclaré dans la boucle
auto p = new patternsRecoAlignement::dataStructures::Pattern(triangles);
- Edité par michelbillaud 21 janvier 2024 à 19:29:07
Ton code est tres suspicieux. En particulier ton usage des pointeurs. Je ne serais pas surpris que le problème ne vienne pas de vector et la ligne que tu as indiqué.
Donne le code des structures que tu utilises et des fonctions (sequence, getStars, __findClosestStars, etc)
A mon avis, vire tes pointeurs. Leur utilisation est étrange et on fait jamais de pointeur de vector. Et si tu veux réellement utiliser des pointeurs, utilise unique_ptr.
Evite les noms qui commencent avec __ ou _X (majuscule), c'est réservé dans certains cas.
Evite "this->" partout, c'est lourd a la lecture. Utilise les range for loop et auto pour alleger le code aussi.
je peux me passer des pointeurs et je veux bien abandonner leur utilisation. Cependant, si ce n'est pas le cas, le projet sur lequel je travaille est censé pouvoir traiter des images, trouver des étoiles et construire des motifs afin d'aligner automatiquement les images. Autrement dit, un vecteur peut être lourd. Dans le cas où la réponse est non, l'utilisation des pointeurs est une nécessité pour éviter les copies lors du passage d'arguments à une méthode.
J'ai néanmoins fait un test rapide en transformant `vector<vector<pattern>>*` en `vector<vector<pattern>>`, mais le problème reste le même. Pour l'utilisation de `unique_ptr`, je suis d'accord, mais je préfère personnellement gérer la mémoire. C'est plus stimulant, et comme c'est un projet personnel, autant que ce soit formateur :D. L'utilisation de `this` peut paraître lourde, mais je la trouve personnellement plus claire : elle permet de savoir qui est membre de la classe et qui est simplement un objet de portée locale.
Cela ne me dérange pas de partager le code des méthodes (d'ailleurs, le projet est totalement open source et je l'ai mis sur GitHub à l'adresse antoinech13/AstroBase (github.com) ). Cependant, je ne pense vraiment pas que le problème puisse venir d'une autre partie du code. Le code que j'ai partagé ici est une version décomposée du code original. Cela me permet de placer des points d'arrêt un peu partout et de vérifier ce qui pose problème. D'ailleurs, on voit bien, sur les images dans mon premier message, que pour i=0 et j=0, le vecteur stocke bien en [0] un pattern de taille 5 (c'est ce qu'il est censé faire). Cependant, pour i=0 et j=1, ce pattern en [0] est de taille 0. C'est ça le problème que je ne comprends pas.
michelbillaud a écrit:
Ici n'a pas les sources qui permettraient de voir et encore moins de reproduire le problème, donc c'est difficile de dire pourquoi.
Cependant on voit une tendance à utiliser explicitement des adresses et des déreférencements explicites (opérateur *).
Un truc qui pose parfois avec ce genre de notions, c'est qu'un vecteur contient des données qui peuvent déménager quand on ajoute des éléments dans un vecteur (à caause de la réallocation du tableau dynamique sous-jacent au vecteur)
Un exemple : le programme ci-dessous ajoute 0,1,.... 999 dans un vecteur d'entiers. Et regarde si par hasard ça n'aurait pas fait changer l'adresse du premier élément (et des suivants évidemment) :
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vecteur;
int *adresse_premier = nullptr;
for (int i = 0; i < 1000; i++) {
vecteur.push_back(i);
if (adresse_premier != & vecteur[0]) {
std::cout << "déménagement ajout de " << i << std::endl;
adresse_premier = & vecteur[0];
}
}
return EXIT_SUCCESS;
}
Compilation et exécution
$ make
g++ -std=c++20 -Wall -Wextra -pedantic -Werror -Wno-unused -g prog.cc -o prog
$ ./prog
déménagement ajout de 0
déménagement ajout de 1
déménagement ajout de 2
déménagement ajout de 4
déménagement ajout de 8
déménagement ajout de 16
déménagement ajout de 32
déménagement ajout de 64
déménagement ajout de 128
déménagement ajout de 256
déménagement ajout de 512
Exercice : en déduire
le prochain ajout qui provoquera un déménagement
la stratégie de réallocation utilisée pour l'extension du tableau dynamique des vecteurs dans cette implémentation.
- Edité par michelbillaud il y a moins de 30s
Cette petite expérience est intéressante, en effet. Cependant, le problème ne vient pas du fait que l'adresse mémoire de mes objets change, mais plutôt que, pour i=0 et j=0, le vecteur[0] stocke un objet de type "pattern" de taille 5, et que pour i=0 et j=1, le vecteur en question stocke deux objets, mais que vecteur[0] est maintenant un objet "pattern" de taille 0. C'est comme s'il avait détruit l'objet en position 0.
Je comprends que c'est difficile à déboguer. Le problème est que c'est la première fois que cela m'arrive avec des vecteurs, et je ne sais pas si je pourrais créer un exemple simple reproduisant l'erreur. De plus, le projet utilise deux ou trois bibliothèques et peut être un peu compliqué à compiler. Comme mentionné précédemment, le code est disponible ici : antoinech13/AstroBase (github.com). Ma conviction est que cela ne soit pas d'une grande aide.
Je vais essayer de reproduire l'erreur sur un exemple simple, mais je ne suis pas sûr d'y parvenir.
Néanmoins, je partage le code des structures de données Pattern et Triangle. Il n'est pas impossible qu'il contienne quelque chose qui perturbe le comportement du vecteur.
[utilisation de this-> pour savoir qui est membre de la classe]
La solution habituelle (la plus courante en C++ ?) est d'utiliser un préfixe comme my_ ou m_ pour les données membres, ce qui les différencie clairement des paramètres des fonctions..
C'est m_ comme membre, pas comme moche, encore que.
Il y avait aussi _ tout court, en prefixe ou suffixe, mais c'est plutôt déconseillé. Peu visible et en conflit avec autre usage de _
[C'est comme si il avait détruit]
Peut être que quand on ajoute dans un vecteur, et que ça provoque une réallocation, les éléments sont déménagés en appelant un constructeur (de copie ?) , et que celui-ci est défectueux ?
- Edité par michelbillaud 21 janvier 2024 à 21:17:13
Tu n'as pas donné le code des fonctions. Mais quand je vois ca :
return &this->stars->at(0);
Je pencherais pour le problème donné par michelbillaud : un pointeur qui devient invalide.
spartan117du13 a écrit:
l'utilisation des pointeurs est une nécessité pour éviter les copies lors du passage d'arguments à une méthode.
Non, utilise des references (constantes de préférence).
spartan117du13 a écrit:
J'ai néanmoins fait un test rapide en transformant `vector<vector<pattern>>*` en `vector<vector<pattern>>`, mais le problème reste le même.
Ce n'est pas que ce code qui pose problème, mais a priori comment tu utilises en général des pointeurs.
spartan117du13 a écrit:
mais je préfère personnellement gérer la mémoire.
C'est une erreur, mais c'est ton choix.
EDIT : tu es optimiste d'ouvrir une issue sur le GH de microsoft. C'est normalement fait pour rapporter des bugs, pas pour poser des questions de C++. Je pense qu'ils vont tiquer aussi sur les pointeurs de vector et ils vont probablement te répondre d'apprendre le C++ (s'ils répondent).
C'est tout à votre honneur de vouloir gérer la mémoire, ce n'est pas du tout une mauvaise chose ! Mais en effet si c'est pour faire des new/delete à la main, je vous conseille vivement d'utiliser des smart pointers car quitte à faire quelque chose de moisi, autant que ça soit "un peu plus propre" et surtout standard, car si vous voulez travailler sur du C++ plus tard, vous ne verrez personne faire des new/delete dans du code métier (sauf ceux qui ne savent pas ce qu'ils font). D'une manière générale, si vous faites quelque chose qui consiste à faire exactement la même chose que ce que propose le C++, alors utilisez plutôt la fonctionnalité existante, le fait de faire les choses à la main c'est pour justement pouvoir faire mieux mais faire des new/delete ce n'est pas mieux du tout.
Je n'ai pas accès au code source donc je ne sais pas s'il y a moyen de gérer la mémoire de ce projet manuellement tout en proposant quelque chose de substantiellement mieux que ce qui serait fait classiquement. Bien entendu, s'il s'agissait juste de stocker quelques objets dans un vecteur, ça ne serait pas vraiment bénéfique de sortir l'arena (pour ceux qui savent de quoi je parle), il faut que le problème soit un minimum complexe pour commencer à en ressentir les bienfaits.
s'il y a moyen de gérer la mémoire de ce projet manuellement tout en proposant quelque chose de substantiellement mieux que ce qui serait fait classiquement.
On fait d'abord un truc qui marche avant de faire un truc qui marche plus vite. Parce qu'aller plus vite quand ça marche pas, ça permet juste de faire un plus gros splatch contre le mur.
Manipuler toutes ces paires via des indirections va coûter une blinde -- et je ne parle même pas des temps requis pour les allocations et les libérations.
Garde tout dans les mêmes caches, et fais confiance au compilo pour être efficace pour passer et renvoyer des paires par valeurs -- enfin, surtout sur linux où il le fera en plus directement dans les registres.
Donc ouste **tous** tes pointeurs, et le code va très vite se simplifier et il ne restera plus que les éventuels problèmes d'invalidation. Éventuels, car je n'ai pas vérifié ce que mes VDD semblent avoir vu.
NB: le traitement d'image n'est pas exactement une activité critique, j'opte de fait pour la programmation offensive (assertions) plutôt que la défensive (std::vector::at). Tes performances s'en porteront mieux en plus.
car si vous voulez travailler sur du C++ plus tard, vous ne verrez personne faire des new/delete dans du code métier (sauf ceux qui ne savent pas ce qu'ils font).
Tu idéalises vraiment le monde professionnel, huhu
Déclarer des types pour les trucs usuels, genre coord2d au lieu de pair<double, double>
Remplacer
p1.first = p2.first;
p1.second = p2.second;
Par p1 = p2; (en bref, définir et utiliser les abstractions qui correspondent au niveau du problème)
Utiliser des tableaux à 3 éléments plutôt que 3 données, et des boucles
Le dernier, ça éviterait les accidents de copier coller comme les lignes 126-128 (vu par hasard).
Mais avant ça, faire un beau diagramme de classes pour décider si un machin possède un truc, ou fait juste réference à un truc qui est par ailleurs la propriété d'un bidule. C'est un préalable à la bonne utilisation des pointeurs (savoir qui a la responsabilité de faire les delete / choisir le type de smart pointer qui convient)
- Edité par michelbillaud 22 janvier 2024 à 13:16:58
Wow, je n'aurais jamais pensé que cela provoquerait autant de réactions ^^". Merci en tout cas pour toutes vos réponses. J'ai finalement résolu le problème. La solution a été de changer les
array<std::pair<float, float>, 3>*
par des:
array<std::pair<float, float>, 3>
(je parles des structure de données Triangle et Pattern)
Mais j'ai du mal à comprendre. Je saisis l'idée selon laquelle le vecteur va relocaliser la mémoire afin d'optimiser. Par conséquent, je m'attends effectivement à ce qu'un objet de type "Pattern" dans un vecteur de type "vector<Pattern>" change d'adresse. Cependant, je ne vois pas pourquoi les données internes à cet objet doivent également changer d'adresse, et pourquoi le fait de ne plus utiliser de pointeurs mais un objet en dur permet de résoudre ce problème.
Si je peux décrire cela de manière grossière, si je suis dans une maison (ma position dans le vecteur), que je souhaite envoyer une lettre à quelqu'un (l'adresse de cette personne qui est présente sur la lettre correspond donc à mes "array<pair, 3>*"). Si je déménage et que, par conséquent, je change d'adresse, je ne vois pas pourquoi le destinataire de la lettre, lui, change d'adresse.
D'autre part, même si je peux comprendre que parfois c'est un peu excessif d'écrire vector<int>* (et j'en ai très probablement abusé ), je ne vois pas vraiment où est le problème. Certes, le vecteur gère la mémoire en interne et je n'ai pas besoin de m'en soucier, mais écrire vector<int> ou vector<int>* à la fin, dans la mémoire, il y aura de toute façon un vector<int> stocké qui gérera en interne des entiers. La seule différence est que moi je manipule une simple adresse plutôt qu'un objet qui peut être lourd.
Je ne peux malheureusement pas répondre à tous les commentaires, cependant je tiens à vous remercier pour les conseils apportés.
J'ai d'autres questions qui me viennent. Mais avant cela, il faut quand même que je précise certaines choses.
Je comprends l'idée d'éviter d'utiliser les pointeurs et de préférer, dans le cas où c'est nécessaire, l'utilisation des smartPtr. Surtout dans le monde professionnel où l'on cherche à développer vite et bien, et donc à ne pas perdre de temps à gérer d'éventuels problèmes de mémoire. Ma première question, qui est une vraie question, c'est pourquoi coder en C++? Je veux dire par là que de mon point de vue, l'intérêt est justement que le langage soit de bas niveau et qu'il permette d'être précis dans le comportement du code. Si la gestion de mémoire "fait peur" et qu'on lui préfère des outils qui gèrent à notre place (et encore une fois je comprends tout à fait dans le cas du monde professionnel), pourquoi ne pas plutôt coder en Java ou C#? Encore une fois, c'est une vraie question et pas une remarque.
Cela m'amène à un point qui, je pense, est important. Ce projet est un projet personnel et je me suis donné comme objectif d'utiliser les pointeurs de base de C++ et d'essayer d'optimiser au mieux à la fois le temps d'exécution et la mémoire par moi-même (ce qui conduit et conduira bien évidemment à du refactoring constant). Je peux comprendre que des professionnels puissent tiquer en voyant cela, mais cela me permet d'apprendre car c'est réellement ce qui me motive. Par exemple, je ne serais jamais tombé sur ce bug, je n'aurais jamais appris le fonctionnement profond des vecteurs. Alors que maintenant, j'ai commencé à lire le code des vecteurs pour essayer de comprendre comment c'est géré en interne. Aussi, j'apprends bien évidemment à gérer les fuites de mémoire et cela me permet de repenser mon code pour faire en sorte d'optimiser au mieux ma mémoire. Chose que je n'aurais probablement jamais faite si je n'utilisais pas les pointeurs justement (et oui, c'est fastidieux et ça me prend beaucoup de temps).
Je ne prétends pas écrire un code qui suive parfaitement la norme professionnelle, et cela m'amène à ma autre question. Je veux bien qu'on me fasse la réflexion sur le fait que ce n'est pas un code professionnel, mais je ne vois pas en quoi c'est mal d'utiliser les fonctionnalités de C++? Quand je regarde les cours de C++ donnés (même celui sur OpenClassroom), personne n'apprend le C++ "professionnel". Tous les cours apprennent l'utilisation des pointeurs standards et rares sont ceux qui ne feraient que mentionner les shared_ptr et unique_ptr. Encore une fois, dans une volonté d'apprendre, si vous avez des sources ou des cours pour apprendre le C++ "professionnel", je suis tout à fait preneur.
Donc voilà, en conclusion, si vous avez des conseils, des références ou autre pour améliorer mon C++, je suis tout à fait preneur et croyez bien que cela ne tombera pas dans l'oreille d'un sourd
Merci beaucoup!
- Edité par spartan117du13 22 janvier 2024 à 22:16:46
Un std::vector gère un espace mémoire en interne. A l'instanciation, il va allouer un certain espace. Lorsque tu ajoute des éléments, si la place vient à manquer, il va faloir trouver plus de place. Comme on ne peut augmenter la taille d'un espace mémoire, il n'y a pas 36 solutions: 1) Allouer un nouvel espace mémoire plus grand. 2) Deplacer les elements dans le nouvel espace. 3) Detruire l'ancien espace.
Un std::vector gère un espace mémoire en interne. A l'instanciation, il va allouer un certain espace. Lorsque tu ajoute des éléments, si la place vient à manquer, il va faloir trouver plus de place. Comme on ne peut augmenter la taille d'un espace mémoire, il n'y a pas 36 solutions: 1) Allouer un nouvel espace mémoire plus grand. 2) Deplacer les elements dans le nouvel espace. 3) Detruire l'ancien espace.
En effet, j'avais compris ce point. Ce qui me perturbe plutôt est le fait que si j'ai un vector<Pattern> et que Pattern utilise des pointeurs comme arguments, pourquoi les arguments internes de Pattern sont supprimés. Je comprends que les objets Pattern soient réalloués pour agrandir l'espace du vecteur, mais pourquoi en interne de Pattern, la mémoire est libérée alors que lorsque je remplace les pointeurs par les valeurs directes, le vecteur n'a aucun problème pour passer ces données.
Au début, je pensais que le vecteur appelle simplement le destructeur de Pattern pour réallouer et que forcément, si dans mon destructeur je supprime les pointeurs, alors forcément, s'il passe au nouveau Pattern après réallocation les adresses qui ont été libérées plus tôt, ça pose problème. Cependant, mon destructeur de Pattern ne supprime pas les pointeurs. D'où mon interrogation sur ce qui se passe.
D'ailleurs, je dis qu'il les supprime, mais en réalité, je n'en sais rien. Il pourrait aussi changer leurs adresses sans les supprimer (ce qui est pire). Je vais tester ça par curiosité pour voir si les adresses ont changé.
- Edité par spartan117du13 22 janvier 2024 à 23:58:33
Cependant, je ne vois pas pourquoi les données internes à cet objet doivent également changer d'adresse, et pourquoi le fait de ne plus utiliser de pointeurs mais un objet en dur permet de résoudre ce problème.
Sans ton code, on peut pas expliquer ce qui n'allait pas et pourquoi ca fonctionne maintenant.
spartan117du13 a écrit:
(l'adresse de cette personne qui est présente sur la lettre correspond donc à mes "array<pair, 3>*"). Si je déménage et que, par conséquent, je change d'adresse, je ne vois pas pourquoi le destinataire de la lettre, lui, change d'adresse.
L'analogie est bancale, mais si tu envoie un pointeur, tu envoies ton adresse. Donc si tu déménages, c'est plus valide.
spartan117du13 a écrit:
D'autre part, même si je peux comprendre que parfois c'est un peu excessif d'écrire vector<int>* (et j'en ai très probablement abusé ), je ne vois pas vraiment où est le problème. Certes, le vecteur gère la mémoire en interne et je n'ai pas besoin de m'en soucier, mais écrire vector<int> ou vector<int>* à la fin, dans la mémoire, il y aura de toute façon un vector<int> stocké qui gérera en interne des entiers. La seule différence est que moi je manipule une simple adresse plutôt qu'un objet qui peut être lourd.
C'est juste une mauvaise pratique.
spartan117du13 a écrit:
Je comprends l'idée d'éviter d'utiliser les pointeurs et de préférer, dans le cas où c'est nécessaire, l'utilisation des smartPtr. Surtout dans le monde professionnel où l'on cherche à développer vite et bien, et donc à ne pas perdre de temps à gérer d'éventuels problèmes de mémoire.
Quand tu apprends, ton temps est limité. Le temps que tu prends a apprendre quelque chose, c'est du temps que tu ne passe pas a apprendre autre chose.
spartan117du13 a écrit:
c'est pourquoi coder en C++? Je veux dire par là que de mon point de vue, l'intérêt est justement que le langage soit de bas niveau et qu'il permette d'être précis dans le comportement du code. Si la gestion de mémoire "fait peur" et qu'on lui préfère des outils qui gèrent à notre place (et encore une fois je comprends tout à fait dans le cas du monde professionnel), pourquoi ne pas plutôt coder en Java ou C#? Encore une fois, c'est une vraie question et pas une remarque.
Il y a une grosse différence entre "coder en C++" et "apprendre le C++".
Quand tu apprend, tu es obligé de suivre une démarche pédagogique, sinon tu risques de ne pas comprendre les choses. Cela impose souvent de voir les choses dans un certain ordre et apprendre une nouvelle chose que quand tu as un certain niveau de compréhension des prérequis. On voit dans tes codes que tu n'as pas certaines bases de compréhension de ce que tu utilises. Le problème n'est pas forcément d'utiliser des pointeurs, mais que cela bloque ou ralentisse ton apprentissage, en te focalisant sur des bugs triviaux plutôt que sur des choses importantes.
Et même hors apprentissage, il y a une différence entre "permet d'utiliser des pointeurs" et "utiliser des pointeurs partout". L'intérêt du C++ n'est pas de pouvoir faire que du bas niveau ! C'est de pouvoir choisir quand on fait du bas niveau et quand on n'en fait pas. Et donc, la première étape est de savoir quand il faut faut faire ce type d'optimisation.
spartan117du13 a écrit:
je me suis donné comme objectif d'utiliser les pointeurs de base de C++
A priori, c'est une erreur pédagogique d'apprendre les pointeurs maintenant, d'après ce que je vois de ton niveau...
spartan117du13 a écrit:
d'essayer d'optimiser au mieux à la fois le temps d'exécution et la mémoire par moi-même (ce qui conduit et conduira bien évidemment à du refactoring constant).
...parce que apprendre les pointeurs va a l'encontre de ton objectif, justement (optimiser).
Comme tu le dis, tu vas devoir refactoriser souvent. Donc la première chose à apprendre, c'est pas d'optimiser avec des pointeurs, mais de savoir refactoriser. Et faire du code qui est facile a refactoriser. Donc du code correctement conçue, facile a lire, facile a modifier, facile à detecter les erreurs, tester son code, etc.
La seconde chose est de savoir comment optimiser. Tu crois que pour optimiser, il faut utiliser des pointeur, ce qui est faux. Les pertes de performances viennent en premier des choix de structures et d'algos, le respect des caches, ce genre de chose. En partant de cette idée préconçue, tu n'apprend pas a optimiser, tu apprends des détails syntaxiques des langages C/C++.
Un autre point, c'est qu'il faut savoir quoi optimiser. Donc apprend a mesurer les performances d'un code, avoir un démarche logique de recherche de problèmes et de solutions. En partant sur un code directement avec des pointeurs, tu n'optimise pas correctement, parce que tu n'as aucun idée de ce qui est pertinent d'être optimisé ou pas.
spartan117du13 a écrit:
je n'aurais jamais appris le fonctionnement profond des vecteurs.
Tu ne connais pas le fonctionnement profond de std::vector. (EDIT : ta réponse a Deedolith confirme que tu n'as pas compris le fonctionnement de std::vector). Tu en es très loin. Si tu veux comprendre le fonctionnement profond de std::vector, il faut lire la doc technique sur le sujet.
spartan117du13 a écrit:
pour faire en sorte d'optimiser au mieux ma mémoire. Chose que je n'aurais probablement jamais faite si je n'utilisais pas les pointeurs justement (et oui, c'est fastidieux et ça me prend beaucoup de temps).
Ce qui n'est pas le cas. La correction que tu as proposé ne change pas grand chose a l'optimisation de la mémoire. Travailler sur ce bug ne t'a rien appris sur comment on optimise la mémoire. C'est juste un constat : tu pensais apprendre certaines choses en utilisant les pointeurs, mais au final, de ce qu'on voit, tu n'as pas appris ces choses en travaillant sur ce bug. Tu as appris des choses, mais pas ce que tu visais.
spartan117du13 a écrit:
mais je ne vois pas en quoi c'est mal d'utiliser les fonctionnalités de C++?
Il ne faut pas non plus apprendre un C++ "professionnel", mais il faut suivre une certaine démarche pédagogique logique. Le problème n'est pas que tu apprends un C++ non professionnel, mais qu'apprendre des choses de façon non logique peut ralentir ton apprentissage, potentiellement de plusieurs années.
spartan117du13 a écrit:
Quand je regarde les cours de C++ donnés (même celui sur OpenClassroom), personne n'apprend le C++ "professionnel". Tous les cours apprennent l'utilisation des pointeurs standards et rares sont ceux qui ne feraient que mentionner les shared_ptr et unique_ptr.
Oui, c'est un problème.
Le C++ est vieux et la façon de l'apprendre aussi. Beaucoup de cours/livres et de profs sont vieux et donc enseignent comme ils ont appris il y a 30 ans. C'est ce que l'on appelle l'approche historique.
Sauf que cette approche n'a plus de sens dans la majorité des cas (exception par exemple pour l'embarqué - et encore, c'est discutable). Parce que les outils, les besoins, les ordinateurs, tout à changé depuis. Avant, utiliser des pointeurs permettait de comprendre réellement comment fonctionne la mémoire d'un ordinateur, parce qu'il y avait un lien directe entre la gestion de la mémoire par un ordinateur et par le C. Mais c'est plus le cas depuis très longtemps et si on veut apprendre comment fonctionne un ordinateur, la mémoire et comment optimiser, apprendre les pointeurs en premier n'est plus du tout pertinent. Il faut apprendre en premier a avoir un code clair, maintenable, qu'on va pouvoir mesurer, de façon a optimiser correctement. La qualité et la sécurité du code est indispensable pour optimiser correctement.
Regarde par exemple le cours sur Zeste de savoir (la version beta, pas celle en ligne)
Quand on dit "les éléments d'un vecteur peuvent changer d'adresse" (Quand le vecteur est agrandi), ça veut dire que le tableau dynamique qui contient les éléments du vecteur est réalloué (probablement ailleurs) avec une taille plus grande (scoop : le double).
Et si on avait des pointeurs vers ces éléments, ils (les pointeurs) deviennent invalides après la réallocation.
Un contournement possible : remplacer les pointeurs vers ces éléments par des indices (par rapport au vecteur des éléments).
Sur un autre sujet : il faut employer les pointeurs intelligents tant que possible, mais j'ai l'impression qu'on ne peut malheureusement pas les comprendre correctement avant d'avoir saisi ce qu'est un bête pointeur tout cours. Ce qui pose le problème d'apprendre un truc (et donc de le pratiquer) pour devoir s'abstenir de l'utiliser ensuite.
- Edité par michelbillaud 23 janvier 2024 à 0:27:17
il faut employer les pointeurs intelligents tant que possible, mais j'ai l'impression qu'on ne peut malheureusement pas les comprendre correctement avant d'avoir saisi ce qu'est un bête pointeur tout cours.
Je suis pas d'accord. A mon sens, cela semble plus simple d'expliquer comme cela, parce que c'est comme ça qu'on a appris. Pas parce que c'est mieux pédagogiquement.
Un std::vector gère un espace mémoire en interne. A l'instanciation, il va allouer un certain espace. Lorsque tu ajoute des éléments, si la place vient à manquer, il va faloir trouver plus de place. Comme on ne peut augmenter la taille d'un espace mémoire, il n'y a pas 36 solutions: 1) Allouer un nouvel espace mémoire plus grand. 2) Deplacer les elements dans le nouvel espace. 3) Detruire l'ancien espace.
Salut !
Il me semble - mais à confirmer si c'est dans la norme ou pas - que quand tu instancies un vector, il n'y a pas d'allocation.
La première allocation du vector sera faite si tu le construis avec une taille, ou que tu le resize (ou reserve), ou dès que tu push.
Je m'appuie la dessus pour parfois avoir plusieurs vector dans une structure, potentiellement non tous remplis : ceux non remplis ne consomment que la mémoire statique interne, mais n'allouent pas.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Discord NaN. Mon site.
Mon site web de jeux SDL2 entre autres : https://www.ant01.fr