Comment puis-je éviter le besoin de copier des chaînes passées à un constructeur C ++ apr-gcc?

StackOverflow https://stackoverflow.com/questions/1027416

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?

Était-ce utile?

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

Référence

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top