2010-03-14 21 views
5

estoy escribiendo una clase de C++ para un libro que contiene un nombre:alternativa a strdup

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

no se me permite utilizar std::string en esta tarea. Así que aquí estoy usando strdup para copiar el valor del nombre del parámetro en nm en el constructor:

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

¿Hay una alternativa de lograr el mismo resultado sin utilizar strdup, pero usando la palabra clave new en su lugar?

+2

lo general, se podría usar 'std :: string' pero lo que es exactamente lo que quiere decir con "sin usar la biblioteca STL C++"? es decir, ¿qué partes de la biblioteca estándar está tratando de evitar (y por qué)? –

+0

¿Por qué no puedes simplemente usar 'strdup'? Estás pidiendo una herramienta para hacer algo mientras te niegas a usar la ideal. –

+0

es una restricción en la tarea ... tal vez debería agregar una etiqueta de tarea allí ... y sí por STL me refiero al uso de la cadena – aherlambang

Respuesta

2

Estrictamente hablando: La clase string es parte de la biblioteca Strings. Esto es mucho más fácil de usar, de naturaleza dinámica y tiene menos preocupaciones al copiar/asignar que las cadenas estilo C.

El otro enfoque es copiar de forma manual:

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;    
    } 
}; 

El problema con este enfoque es que tendrá que gestionar la memoria de sí mismo y poner en práctica todas las Big-tres funciones miembro especial a sí mismo (y asegurarse de excepción -segura todo lo que puedas).

+0

No puedo usar la cadena – aherlambang

+0

No tiene que marcar 'if (p)' después de 'new', porque solo nothrow new puede devolver un puntero nulo. Tienes que liberar la matriz anterior en 'operator ='. Copiar y cambiar es usualmente mejor. –

+0

Sí, el segundo fue un error tipográfico. El primero que quería poner como un comentario. (Supuse que el OP funciona en un entorno restringido y es posible que no tenga acceso a la gestión de excepciones.) – dirkgently

3

Sí, hay una alternativa.

  • obtener un tamaño de cadena
  • crear una matriz del mismo tamaño que es la cadena
  • copiar el contenido de la cadena en esa matriz
  • punto nm a la matriz asignado

O podría usar strdup - por cierto strdup no es parte de C++ STL.

0

Tienes que malloc con strlen y luego usar strcopy. Estúpida tarea por cierto.

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

ha olvidado el nulo al final de la cadena a través del uso de memcpy – tony

+0

D'oh! Gracias - ahora está arreglado. – JBRWilkinson

4

No es realmente una respuesta, pero una corrección a dirkgently de que no caben en un comentario: que realmente no debería escribir tanto código como lo hizo.

La copia segura de objetos no es algo que desee obtener demasiado mal, aunque en la vida real la mejor manera de evitar eso es, por supuesto, utilizar las clases de biblioteca apropiadas en primer lugar. Dicho esto, un simple cadena tipo C es un ejemplo tan bueno como cualquier otra cosa a la práctica con: "! No lanzan una excepción"

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; 
} 

Véase el advertencia en el constructor? Eso es porque si lo haces, la cadena se filtrará. Si necesita más de un recurso en su clase que requiera una liberación explícita, es cuando las cosas se vuelven realmente tediosas. Lo correcto es escribir una clase con el único propósito de mantener la cadena, y otra con el propósito de contener el otro recurso, y tener un miembro de cada tipo en su clase de Libro. Entonces no tiene que preocuparse por las excepciones en el constructor, porque los miembros que se han construido se destruyen si el cuerpo constructor de la clase contenedora lo arroja. Una vez que haya hecho esto un par de veces, tendrá muchas ganas de usar las bibliotecas estándar y TR1.

Normalmente, para ahorrar esfuerzo empezarías al hacer que su clase no copiable, y sólo implementar el constructor de copia y el operador = si resulta que usted los necesita:

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); 
}; 

De todos modos, no es strdup gran misterio Aquí hay un par de implementaciones muy similares (ambas de GNU), simplemente buscando "strdup.c". El mismo enfoque generalmente funciona para otras funciones de manejo de cadenas, y en general para cualquier cosa que no requiera mecanismos especiales dependientes de la plataforma para implementar: busque "nombre_función.c" y probablemente encontrará una implementación de GNU que explique cómo se hace. y cómo puedes hacer cosas similares pero diferentes. En este caso, comenzaría con su código y reemplazaría la llamada al malloc y el manejo de errores.

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 para seguridad de excepción. – msandiford