Comment puis-je éviter le besoin de copier des chaînes passées à un constructeur C ++ apr-gcc?
Question
Dans la ArduinoUnit , j'ai fourni un mécanisme permettant de nommer TestSuite. . Un utilisateur de la bibliothèque peut écrire ce qui suit:
TestSuite suite("my test suite");
// ...
suite.run(); // Suite name is used here
Il s'agit de l'utilisation attendue - le nom de TestSuite est un littéral de chaîne. Cependant, pour éviter les bugs difficiles à trouver, je me sens obligé de répondre à différents usages, par exemple:
char* name = (char*) malloc(14);
strcpy(name, "my test suite");
TestSuite suite(name);
free(name);
// ...
suite.run(); // Suite name is used here
En tant que tel, j'ai implémenté TestSuite comme suit:
class TestSuite {
public:
TestSuite(const char* name) {
name_ = (char*) malloc(strlen(name) + 1);
strcpy(name_, name);
}
~TestSuite() {
free(name_);
}
private:
char* name_;
};
Laissant de côté le problème de l'échec de la gestion des échecs d'allocation de mémoire dans le constructeur, je préférerais simplement allouer le pointeur à une variable membre comme celle-ci:
class TestSuite {
public:
TestSuite(const char* name) : name_(name) {
}
private:
const char* name_;
};
Existe-t-il un moyen de modifier l'interface pour la forcer à être utilisée "correctement" afin de pouvoir supprimer l'allocation de mémoire dynamique?
La solution
Et si vous fournissiez deux constructeurs surchargés?
TestSuite(const char* name) ...
TestSuite(char* name) ...
Si appelé avec un const char*
, le constructeur peut alors copier le pointeur en supposant que la chaîne ne disparaîtra pas. Si appelé avec un char*
, le constructeur peut créer une copie de la chaîne entière.
Notez qu'il est toujours possible de subvertir ce mécanisme en transmettant un name
au constructeur lorsque le <=> est en fait alloué de manière dynamique. Toutefois, cela peut suffire à vos fins.
Je dois noter que je n’ai jamais réellement vu cette technique utilisée dans une API, c’était une pensée qui m’est venue à l'esprit en lisant votre question.
Autres conseils
Documentation. Par exemple,
/**
* Test suite constructor.
* @param name test suite name cstring, shared
*/
TestSuite(char const *name) {
// ...
Un pointeur partagé implique que l'objet pointé doit être en vie pendant la durée de vie de cet objet.
Bien, vous pouvez utiliser un std :: string qui se chargera de toute l’allocation de mémoire
class TestSuite {
public:
TestSuite(const std::string &name):name_(name) {
}
~TestSuite() {
}
private:
std::string name_;
};
Modifier : Si c’est l’appel de malloc () que vous souhaitez éviter, vous pouvez le faire:
class TestSuite {
public:
TestSuite(const char *name){
memcpy(name_, name, min(16, strlen(name));
}
private:
char name_[16];
};
Cela gaspillera toutefois un peu de mémoire, ce qui peut poser problème sur les plates-formes intégrées.
Ayez un char name[XYZ]
& nbsp; membre de votre TestSuite (avec un XYZ suffisamment grand pour afficher le nom de manière confortable) et utilisez strncpy
pour copier la chaîne (avec une longueur maximale de XYZ-1).
Pourquoi utilisez-vous char * et malloc quand vous avez la belle classe de chaînes C ++ qui peut prendre un littéral de chaîne ou un char * dans son constructeur?
Pouvez-vous utiliser std :: string? Vous pouvez l’avoir comme std::string name_
et demander à la STL de s’occuper de l’allocation de mémoire pour vous.
class TestSuite {
public:
TestSuite(const char* name) : name_(name) {}
~TestSuite() {}
private:
std::string name_;
};
N'oubliez pas d'inclure <string>
.