2010-03-14 15 views
5

J'écris une classe C++ pour un livre qui contient un nom:alternative à strdup

class Book { 
private: 
    char* nm; 
.......... 
............ 
.......... 
........... 
}; 

Je ne suis pas autorisé à utiliser std::string dans cette mission. Donc ici, je me sers strdup pour copier la valeur du nom du paramètre en nm dans le constructeur:

Book::Book(const char *name, int thickness, int weight) 
    : nm(NULL) 
    , thck(thickness) 
    , wght(weight) 
{ 
    if (name) 
     nm = strdup(name); 
} 

Y at-il une alternative d'obtenir le même résultat sans utiliser strdup, mais en utilisant le mot-clé new à la place?

+2

Généralement, vous utilisez 'std :: string' mais que voulez-vous dire par "sans utiliser la bibliothèque C++ STL"? c'est-à-dire quelles parties de la bibliothèque standard essayez-vous d'éviter (et pourquoi)? –

+0

Pourquoi ne pouvez-vous pas utiliser 'strdup'? Vous demandez un outil pour faire quelque chose en refusant d'utiliser l'idéal. –

+0

c'est une contrainte sur l'affectation ... peut-être que je devrais ajouter un tag de devoirs là ... et oui par STL je veux dire en utilisant la chaîne – aherlambang

Répondre

2

Strictement parlant: La classe string fait partie de la bibliothèque de chaînes. C'est beaucoup plus facile à utiliser, dynamique dans la nature et vous avez moins de soucis lors de la copie/affectation que les chaînes de style C.

L'autre approche consiste à copier manuellement:

class Book { 
    public: 
    Book(const char *name, ...) : nm(0), ... { 
      if (!name) throw "invalid parameter"; 
      nm = new char [ strlen(name) + 1 ]; 
      strcpy(nm, name); 
    } 
    ~Book() { 
      delete [] nm; 
      // ... 
    } 
    Book(Book const& o) : nm(0), ... { 
      if (!name) throw "invalid parameter"; 
      char *p = new char [ strlen(name) + 1 ]; 
      if (p) { 
       strcpy(p, name); 
       delete [] nm; 
       nm = p; 
      } 
    } 
    Book& operator=(Book const& o) { 
      if (this != &o) { 
       char *p = new char [ strlen(name) + 1 ]; 
       if (p) { 
       strcpy(p, name); 
       delete [] nm; 
       nm = p; 
       } 
      } 
      return *this;    
    } 
}; 

Le problème avec cette approche est que vous devrez gérer vous-même la mémoire et mettre en œuvre vous-même tous les Big-trois fonctions membres spéciales (et assurer exception -sécurité autant que vous le pouvez).

+0

Je ne peux pas utiliser la chaîne – aherlambang

+0

Vous ne devez pas vérifier 'if (p)' après 'new', car seulement nothrow new peut retourner un pointeur nul. Vous devez libérer l'ancien tableau dans 'operator ='. Copier-et-échanger est généralement mieux. –

+0

Oui, la seconde était une faute de frappe. Le premier que je voulais mettre en commentaire. (Je suppose que l'OP fonctionne dans un environnement restreint et peut ne pas avoir accès à la gestion des exceptions.) – dirkgently

3

Oui, il existe une alternative.

  • obtenir une taille de chaîne
  • créer un tableau de la même taille que est la chaîne
  • copier le contenu de la chaîne dans ce tableau
  • point de nm à votre tableau alloué

Ou vous pouvez utiliser strdup - btw strdup ne fait pas partie de C++ STL.

0

Vous devez malloc avec strlen et ensuite utiliser strcopy. Stupide devoirs btw.

0
Book::Book(const char *name, int thickness, int weight):nm(NULL), thck(thickness), wght(weight){ 
    if (name) { 
    size_t length = strlen(name); 
    nm = new char[length + 1]; 
    memcpy(nm, name, length + 1); 
    } 
+0

vous avez oublié le nul à la fin de la chaîne via votre utilisation de memcpy – tony

+0

D'oh! Merci - il est corrigé maintenant – JBRWilkinson

4

Pas vraiment une réponse, mais une correction à des années dirkgently qui ne rentrent pas dans un commentaire: vous ne devriez pas écrire tant le code comme il l'a fait.

La copie d'objets en toute sécurité n'est pas quelque chose que vous voulez vous tromper, bien que dans la vraie vie, la meilleure façon d'éviter cela est d'utiliser les classes de bibliothèques appropriées. Cela dit, une chaîne simple de style C est aussi bon exemple que toute autre chose à la pratique avec: « ne jette pas une exception »

class Book { 
    char *nm; 
public: 
    Book(const char *name) : nm(copystr(name)) { /* don't throw an exception! */ } 
    Book(const Book &o) : nm(copystr(o.nm)) { /* Likewise! */ } 
    ~Book() { delete[] nm; } 
    Book& operator=(const Book &o) { 
     // this is called copy-and-swap (CAS). If you absolutely 
     // have to write this kind of resource-managing code, then 
     // you will need this technique, because it's the best 
     // way to provide the strong exception guarantee. 
     Book cp = o; 
     swap(cp); 
     return *this; 
    } 
    /* or you can do this: 
    Book& operator=(Book cp) { 
     swap(cp); 
     return *this; 
    } 
    */ 
    void swap(Book &o) { 
     std::swap(this->nm, o.nm); 
     // also swap other members 
    } 
}; 

char *copystr(const char *name) { 
    if (!name) return 0; 
    char *newname = new char[strlen(name)+1]; 
    std::strcpy(newname, name); 
    return newname; 
} 

Voir la avertissement dans le constructeur? C'est parce que si vous le faites, la chaîne sera divulguée. Si vous avez besoin de plus d'une ressource dans votre classe qui nécessite une libération explicite, les choses deviennent vraiment fastidieuses. La bonne chose à faire est d'écrire une classe juste dans le but de maintenir la chaîne, et une autre dans le but de contenir l'autre ressource, et avoir un membre de chaque type dans votre classe Book. Ensuite, vous n'avez pas à vous soucier des exceptions dans le constructeur, car les membres qui ont été construits sont détruits si le corps du constructeur de la classe conteneur se déclenche. Une fois que vous avez fait cela à quelques reprises, vous serez très désireux d'utiliser les bibliothèques standard et TR1.

Normalement, pour sauver l'effort que vous souhaitez commencer en faisant votre classe non falsifiable, et seulement mettre en œuvre le constructeur de copie et l'opérateur = s'il se trouve que vous avez besoin:

class Book { 
    char *nm; 
public: 
    Book(const char *name) : nm(copystr(name)) { } 
    ~Book() { delete[] nm; } 
private: 
    Book(const Book &o); 
    Book& operator=(const Book &o); 
}; 

Quoi qu'il en soit, strdup est pas grand mystère. Voici quelques implémentations très similaires (toutes deux de GNU), juste en cherchant "strdup.c". La même approche fonctionne généralement pour d'autres fonctions de gestion de chaînes et, en général, pour tout ce qui ne nécessite pas de mécanismes spécifiques à la plate-forme: recherchez "nom_fonction.c" et vous trouverez probablement une implémentation GNU expliquant comment cela fonctionne , et comment vous pouvez faire des choses similaires mais différentes. Dans ce cas, vous commencez avec leur code et remplacez l'appel par malloc et la gestion des erreurs.

http://www.koders.com/c/fidF16762E3999BA95A0B5D87AECB0525BA67CEE45A.aspx

http://cvs.frodo.looijaard.name/viewvc/cgi-bin/viewvc.cgi/public/psiconv/compat/strdup.c?revision=1.1.1.1&view=markup

+0

+1 pour la sécurité d'exception. – msandiford