Citation:
Envoyé par onet Voir le message
Hello,
Voila, la situation initale:
J'ai un struct que je passe en référence à une fonction. Cette fonction à pour but de recevoir ce struct, et d'en faire une copie dans un tableau de struct. Comment je le fais? Je n'y arrive pas!
Code :
struct msg {
int cmd_id;
int lenght;
char * params;
bool urgent;
};
<snip>
Sans succès (avec *message.cmd_id, &message.cmd_id aussi) . Mes bases de C++ étant relativement lointaine, je nage un peu... Si qqun a une piste pour m'aiguiller?
Salut,
Tu devrais déjà prendre l'habitude de préférer en toute circonstance la classe string dispponible dans l'espace de noms std par simple inclusion du fichier d'en-tête <string> pour manipuler tes chaines de caractères...
En effet, cette classe est bien plus sécurisante à l'emploi que les chaines de caractères "C style" (tableau de craractères terminé par un '\0'), surtout lorsqu'il s'agit de manipuler des chaines de caractères de longueur variable ou pour lesquelles il t'es impossible / difficile de déterminer une taille maximale cohérente.
Bien souvent, dans ce genre de situation, tu te retrouve à manipuler, comme tu le fais ici, des char* et à utiliser la gestion dynamique de la mémoire (malloc / new / new[] et free / delete / delete[]) avec tous les risques que cela peut comporter de fuites mémoire, de double libération de la mémoire voire, de dépassement d'index (tout ce qui consiste à essayer de faire entrer N caractères dans un tableau qui ne peut en contenir que... N-1) en devant, de plus, garder une trace du nombre de caractères réellement utilisés et de la taille maximale admise pour la chaine
De plus, si tu as *vraiment* besoin de récupérer un char* correspondant à la chaine std::string (pour le passer en paramètre d'une fonction, par exemple), il t'est toujours possible d'invoquer la fonction membre c_str()
Le tout sans parler du fait que, si tu écris un code proche de
Code :
/* N'essayez pas chez vous */
char *first="hello";
char *second=" world";
char * final = first + second;
tu utilise en fait l'arithmétique des pointeurs et tu ne copie absolument pas le contenu de first ni de second dans final
Citation:
Envoyé par vikki Voir le message
Hello,
Tu pourrais surcharger l'opérateur d'affectation de ta structure:
Code :
struct msg
{
int cmd_id;
std::string params;
bool urgent;
msg & operator=(const msg & right)
{
cmd_id = right.cmd_id;
params = right.params;
urgent = right.urgent;
}
};
A enjoliver ensuite en ajoutant un constructeur par copie.
Dans tous les cas, je te déconseilles d'utiliser l'opérateur d'affectation sans le surcharger en gardant un char* pour params : tu feras une copie de pointeur (chaque params pointera vers le même emplacement mémoire), pas une copie du contenu (à moins que cela ne soit ce que tu souhaite).
Oui, et non...
Oui si l'on se base sur l'exemple de onet qui manipule un pointeur de char, non si on se base sur le tien...
Selon l'exemple de onet, nous en arriverions à quelque chose proche de
Code :
struct msg {
int cmd_id;
int lenght;
char * params;
bool urgent;
/* Après tout, donnons nous l'occasion de construire un msg
* "d'une traite"
*/
msg(int id,int l,char* p, bool u):cmd_id(id),lenght(l),
params(new char[l]), urgent(u)
{
strncpy(params,p,l);
}
/* constructeur par copie */
msg(msg const & m):cmd_id(m.cmd_id),lenght(m.lenght),
params(new char[m.lenght]),urgent(m.urgent)
{
strncpy(params,m.params,lenght);
}
/* opérateur d'affectation */
msg & operator = (msg & rhs)
{
/* utilisons notre constructeur par copie */
msg temp(rhs);
/* intervertissons les membres entre "this" et "temp" */
std::swap(cmd_id,temp.cmd_id);
std::swap(lenght,temp.lenght);
std::swap(params,temp.params);
std::swap(urgent,temp.urgent);
/* temp sera automatiquement détruit (et son destructeur appelé)
* ce qui fait que la mémoire allouée aux pointeur qui apartenait
* avant à this sera libérée
*/
return *this;
}
/* le destructeur */
~msg()
{
delete[] params;
params = 0;
}
};
En effet, la redéfinition du constructeur par copie, de l'opérateur d'affectation et du destructeur est indispensable si leur comportement n'est pas trivial, ce qui est le cas lorsque l'on gère dynamiquement la mémoire et les pointeurs.
Le constructeur par copie devrait donc veiller à allouer dynamiquement la quantité de mémoire adéquate pour params (selon l'exemple), le destructeur devrait veiller à libérer correctement cette mémoire et l'opérateur d'affectation devrait, dans l'idéal, utiliser l'idiôme copy-and-swap
Par contre, dans ton exemple à toi, les comportement du constructeur par copie, de l'opérateur d'affectation et du destructeur sont tout à fait triviaux, du seul fait que l'ensemble des membres disposent eux aussi de constructeurs par copie (ou de pseudo constructeurs par copie, pour les types primitifs), d'opérateur d'affectation et de destructeurs.
Or, il faut savoir que le compilateur fournit d'office un constructeur par copie, un opérateur d'affectation et un destructeur si tu ne les déclares pas toi-même (la nouvelle norme permettra de les déclarer deleted afin d'interdire la copie ou l'affectation), et qu'il présentent exactement le comportement trivial adapté (copie membre à membre pour le constructeur par copie, idiome copy-and-swap pour l'opérateur d'affectation et destruction des membres dans l'ordre inverse de leur déclaration pour le destructeur).
Si ces comportements conviennent à ta structure ou à ta classe, et que tu ne veux pas, par exemple, rendre le destructeur virtuel dans une hiérarchie de classe, tu as bien souvent carrément intérêt à laisser le compilateur les implémenter par lui-même
(Pour rappel, la seule différence qu'il existe, en C++, entre une structure et une classe tient dans la visibilité par défaut de leurs membres et de l'héritage: ils seront private pour les classes et public pour les structures )
Citation:
C'est vrai qu'un std::vector ou une std::list va te faciliter la vie dans ce cas.
Sur ce point, par contre, nous sommes tous catégoriques
Partager