char * vs std :: string en c ++ [cerrado]

81

¿Cuándo debo usar std::stringy cuándo debo usar char*para administrar matrices de chars en C ++?

Parece que debería usarlo char*si el rendimiento (velocidad) es crucial y está dispuesto a aceptar un negocio arriesgado debido a la gestión de la memoria.

¿Hay otros escenarios a considerar?

jww
fuente

Respuestas:

56

Puede pasar std :: strings por referencia si son grandes para evitar la copia, o un puntero a la instancia, por lo que no veo ninguna ventaja real en el uso de punteros char.

Utilizo std :: string / wstring para más o menos todo lo que es texto real. char *Sin embargo, es útil para otros tipos de datos y puede estar seguro de que se desasignará como debería. De lo contrario, std :: vector es el camino a seguir.

Probablemente haya excepciones a todo esto.

Skurmedel
fuente
8
¿Existe una diferencia de rendimiento antes de los dos?
vtd-xml-author
3
@ vtd-xml-author: Quizás algunos. La recta char *casi no tiene gastos generales. std::stringNo sé exactamente qué gastos generales tiene, es probable que dependa de la implementación. Difícilmente espero que la sobrecarga sea mucho mayor que la de un puntero de char. Dado que no poseo una copia del estándar, no puedo detallar ninguna garantía hecha por el estándar. Es probable que cualquier diferencia de rendimiento varíe según las operaciones a realizar. std::string::sizepodría almacenar el tamaño junto a los datos del carácter y, por lo tanto, ser más rápido que strlen.
Skurmedel
2
¿Por qué no usar std :: string para datos que no son de texto? No tienen terminación nula, por lo que debería poder almacenar todo lo que desee allí.
Casey Rodarmor
1
@rodarmor Usted puede almacenar cualquier cosa que desee, aunque es un toque arriesgado como cadena está diseñado para cadenas de caracteres terminadas en cero. Debe tener cuidado de utilizar solo operaciones binarias seguras, por ejemplo, append(const string&)y en append(const char*, size_t)lugar de operator+=().
boycy
6
¿Estás seguro? Sé que muchas operaciones supondrán que un char * es una cadena terminada en nulo, pero no puedo pensar en ninguna que asuma que un std :: string no contiene nulos.
Casey Rodarmor
57

Mi punto de vista es:

  • Nunca use char * si no llama al código "C".
  • Utilice siempre std :: string: es más fácil, es más amigable, está optimizado, es estándar, evitará que tenga errores, ha sido verificado y probado que funciona.
Gal Goldman
fuente
13

Uso de cadenas sin procesar

Sí, a veces realmente puedes hacer esto. Al usar const char *, las matrices de char asignadas en la pila y los literales de cadena, puede hacerlo de tal manera que no haya ninguna asignación de memoria.

Escribir dicho código requiere a menudo más pensamiento y cuidado que usar cadenas o vectores, pero con las técnicas adecuadas se puede hacer. Con las técnicas adecuadas, el código puede ser seguro, pero siempre debe asegurarse de que, al copiar en char [], tenga algunas garantías sobre la longitud de la cadena que se está copiando, o verifique y maneje cadenas de gran tamaño con elegancia. No hacerlo es lo que le dio a la familia de funciones strcpy la reputación de ser insegura.

Cómo las plantillas pueden ayudar a escribir búferes de caracteres seguros

En cuanto a la seguridad de los búferes char [], las plantillas pueden ayudar, ya que pueden crear una encapsulación para manejar el tamaño del búfer por usted. Microsoft implementa, por ejemplo, plantillas como ésta para proporcionar reemplazos seguros para strcpy. El ejemplo aquí se extrae de mi propio código, el código real tiene muchos más métodos, pero esto debería ser suficiente para transmitir la idea básica:

template <int Size>
class BString
{
  char _data[Size];

  public:
  BString()
  {
    _data[0]=0;
    // note: last character will always stay zero
    // if not, overflow occurred
    // all constructors should contain last element initialization
    // so that it can be verified during destruction
    _data[Size-1]=0;
  }
  const BString &operator = (const char *src)
  {
    strncpy(_data,src,Size-1);
    return *this;
  }

  operator const char *() const {return _data;}
};

//! overloads that make conversion of C code easier 
template <int Size>
inline const BString<Size> & strcpy(BString<Size> &dst, const char *src)
{
  return dst = src;
}
Suma
fuente
1
+1 para "Al usar const char *, las matrices de char asignadas en la pila y los literales de cadena, puede hacerlo de tal manera que no haya ninguna asignación de memoria". La gente olvida que la "asignación" de la pila es mucho más rápida que la pila.
NoSenseEtAl
char*las cadenas no siempre están en la pila. char *str = (char*)malloc(1024); str[1024] = 0;
Cole Johnson
@ColeJohnson No estoy afirmando eso, solo quiero decir que si desea que su cadena se asigne en la pila, debe usar const char * junto con los literales de cadena, no std :: string.
Suma
9

Una ocasión que DEBE usar char*y no std::stringes cuando necesita constantes de cadena estáticas. La razón de esto es que no tiene ningún control sobre los módulos de orden que inicializan sus variables estáticas, y otro objeto global de un módulo diferente puede referirse a su cadena antes de que se inicialice. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variables

std::string pros:

  • administra la memoria por usted (la cadena puede crecer y la implementación le asignará un búfer más grande)
  • Interfaz de programación de nivel superior, funciona bien con el resto de STL.

std::stringcontras: - dos instancias de cadenas STL distintas no pueden compartir el mismo búfer subyacente. Entonces, si pasa por valor, siempre obtendrá una nueva copia. - Hay una penalización de rendimiento, pero yo diría que, a menos que sus requisitos sean especiales, es insignificante.

thesamet
fuente
En realidad, las implementaciones de STL a menudo implementan la semántica de copia en escritura para std :: string, por lo que pasarlas por valor no cuesta mucho. Aún así, es mejor no confiar en eso y, en general, es mejor pasar una referencia a constante de todos modos.
1
Algunas implementaciones de std :: string abandonaron la implementación de COW. Además, no es tan trivial como parece proporcionar una clase segura para subprocesos (POSIX) compatible con el estándar. Ver groups.google.fr/group/ifi.test.boa/browse_frm/thread/… o groups.google.fr/group/comp.programming.threads/browse_frm/…
Luc Hermitte
8

Debe considerar su uso char*en los siguientes casos:

  • Esta matriz se pasará en parámetro.
  • Usted sabe de antemano el tamaño máximo de su matriz (lo sabe O lo impone).
  • No hará ninguna transformación en esta matriz.

En realidad, en C ++, a char*menudo se usan para palabras pequeñas fijas, como opciones, nombre de archivo, etc.

Jérôme
fuente
3
La matriz no se pasa, un puntero a la matriz. Eso es lo que es un puntero: un puntero a un objeto.
Cole Johnson
5

Cuándo usar un std :: string de c ++:

  • las cadenas, en general, son más seguras que char *. Normalmente, cuando estás haciendo cosas con char * tienes que comprobar las cosas para asegurarte de que estén bien, en la clase de cadenas todo esto se hace por ti.
  • Por lo general, cuando use char *, tendrá que liberar la memoria que asignó, no tiene que hacer eso con la cadena, ya que liberará su búfer interno cuando se destruya.
  • Las cadenas funcionan bien con stringstream de c ++, la E / S formateada es muy fácil.

Cuando usar char *

  • El uso de char * le da más control sobre lo que sucede "detrás" de escena, lo que significa que puede ajustar la interpretación si lo necesita.
user88637
fuente
3

Utilice (const) char * como parámetros si está escribiendo una biblioteca. Las implementaciones de std :: string difieren entre diferentes compiladores.

Nemanja Trifunovic
fuente
Si está escribiendo una biblioteca en C ++, el diseño de std :: string no es lo único de lo que debe preocuparse. Hay una serie de posibles incompatibilidades entre dos implementaciones. Use bibliotecas en C ++ solo si están disponibles en el código fuente o compiladas para el compilador exacto que está usando. Las bibliotecas C suelen ser más portátiles, pero en ese caso no tiene std :: string de todos modos.
David Thornley
Es cierto que std :: string no es el único problema, pero es demasiado concluir "Use bibliotecas en C ++ solo si están disponibles en el código fuente o compiladas para el compilador exacto que está usando". Hay sistemas de componentes que funcionan bien con diferentes compiladores (COM, por ejemplo) y es posible exponer una interfaz C a una biblioteca que está escrita internamente con C ++ (API Win32, por ejemplo)
Nemanja Trifunovic
2

Si desea utilizar bibliotecas C, tendrá que lidiar con cadenas C. Lo mismo se aplica si desea exponer su API a C.

n0rd
fuente
2

Puede esperar que la mayoría de las operaciones en un std :: string (como por ejemplo find) estén lo más optimizadas posible, por lo que es probable que funcionen al menos tan bien como una contraparte de C pura.

También vale la pena señalar que los iteradores std :: string a menudo se asignan a punteros en la matriz de caracteres subyacente. Entonces, cualquier algoritmo que diseñe sobre iteradores es esencialmente idéntico al mismo algoritmo sobre char * en términos de rendimiento.

Las cosas a tener en cuenta son, por ejemplo operator[], que la mayoría de las implementaciones de STL no realizan la verificación de límites y deberían traducir esto a la misma operación en la matriz de caracteres subyacente. AFAIK STLPort opcionalmente puede realizar la verificación de límites, momento en el que este operador sería un poco más lento.

Entonces, ¿qué te aporta el uso de std :: string? Le absuelve de la gestión manual de la memoria; cambiar el tamaño de la matriz se vuelve más fácil y, por lo general, debe pensar menos en liberar memoria.

Si le preocupa el rendimiento al cambiar el tamaño de una cadena, hay una reservefunción que puede resultarle útil.


fuente
1

si está utilizando la matriz de caracteres en texto similar, etc., use std :: string más flexible y fácil de usar. ¿Si lo usa para algo más como el almacenamiento de datos? use matrices (prefiera vectores)

RvdK
fuente
1

Incluso cuando el rendimiento es crucial, es mejor usarlo vector<char>: permite la asignación de memoria por adelantado (método reserve ()) y lo ayudará a evitar pérdidas de memoria. El uso de vector :: operator [] genera una sobrecarga, pero siempre puede extraer la dirección del búfer e indexarlo exactamente como si fuera un char *.

diente filoso
fuente
Pero sería bueno usar algún tipo de funcionalidad de cadena típica y tener solo la opción de especificar la política para el almacenamiento. Para eso, vea el enlace en mi respuesta.
Anónimo
No es realmente cierto. Si considera que el vector se asignará en el espacio de memoria contiguo, la reasignación (para aumentar el tamaño del vector) no será eficiente en absoluto, ya que implica la copia del fragmento anterior.
Jérôme
Entendí mal tu respuesta, ya que usas el vector en lugar de char *, no en lugar de string ... En este caso, estoy de acuerdo.
Jérôme
No debería haber una sobrecarga en el uso del operador []. Véase, por ejemplo, stackoverflow.com/questions/381621/…
Luc Hermitte
-1

AFAIK internamente, la mayoría de std :: string implementan copia en escritura, semántica contada de referencia para evitar gastos generales, incluso si las cadenas no se pasan por referencia.

piotr
fuente
5
Esto ya no es cierto, porque la copia en escritura causa serios problemas de escalabilidad en entornos multiproceso.
Suma
Es cierto para la implementación de STL de GCC al menos.