2009-04-01 9 views
13

El estándar STL no requiere que std :: string se vuelva a contabilizar. Pero, de hecho, la mayoría de las implementaciones de C++ proporcionan cadenas de recuento y de copiado por escritura, lo que le permite pasar cadena por valor como tipo primitivo. Además, estas implementaciones (al menos g ++) usan operaciones atómicas , lo que hace que estas cadenas estén libres de enclavamiento y seguras para el hilo.¿Por qué las cadenas de VC++ no son contadas por referencia?

prueba fácil muestra la semántica copia en escritura:

#include <iostream> 
#include <string> 

using namespace std; 

void foo(string s) 
{ 
    cout<<(void*)s.c_str()<<endl; 
    string ss=s; 
    cout<<(void*)ss.c_str()<<endl; 
    char p=ss[0]; 
    cout<<(void*)ss.c_str()<<endl; 
} 

int main() 
{ 
    string s="coocko"; 
    cout<<(void*)s.c_str()<<endl; 
    foo(s); 
    cout<<(void*)s.c_str()<<endl; 
} 

Sólo dos direcciones se imprimen exactamente después de que se utiliza un miembro no constante.

Probé este código usando el compilador HP, GCC e Intel y obtuve resultados similares - las cadenas funcionan como contenedores de escritura.

Por otro lado, VC++ 2005 muestra claramente que cada cuerda está completamente copiada.

¿Por qué?

Sé que hubo un error en VC++ 6.0 que tenía una implementación no segura para subprocesos de recuento de referencias que causaba cambios de programa aleatorios. ¿Es este el motivo? Ellos simplemente tienen miedo de usar el recuento de ref, incluso si es una práctica común? Prefieren no utilizar el recuento de ref en todo para solucionar el problema?

Gracias

Respuesta

22

creo que cada vez más std::string implementaciones se alejará de refcounting/copia en escritura ya que a menudo es un contra-optimización de código multihilo.

Consulte el artículo de Herb Sutter Optimizations That Aren't (In a Multithreaded World).

+0

creo que Scott Meyers menciona algo similar en uno de sus libros - no puede citar (y ni siquiera seguro) porque yo no los tengo conmigo en el momento. –

+2

+1, pasé 10 minutos buscando esa referencia. Hay tantas preguntas C++ en SO que simplemente requieren un puntero a la escritura de Herb. – JaredPar

+2

En realidad, este artículo muestra claramente que las cadenas COW que utilizan operaciones atómicas tienen casi el mismo rendimiento que las cadenas normales COW. (diferencia del 25%) Así que no es demasiado "sobrecarga", y mucho más para hacer una copia real. – Artyom

5

Tal vez Microsoft determinó que la copia de cadenas no era un gran problema, ya que casi todos los códigos de C++ se usan para pasar por referencia siempre que sea posible. Mantener un recuento de referencias tiene una sobrecarga en el espacio y el tiempo (ignorando el bloqueo) que tal vez decidieron que no valía la pena pagar.

O tal vez no. Si esto le preocupa, debe perfilar su aplicación para determinar si la copia de cadenas es una sobrecarga importante, y si se cambia a una implementación de cadena diferente.

+1

sí, estoy de acuerdo. jsut pasar por ref. ¿Cual es el problema? quiero ahorrar en tipeo? –

1

No es la razón principal, pero vi un montón de código incorrecto en la plataforma win32 que hace algo como const_cast< char* >(str.c_str()).

Quizás Microsoft lo saben y se preocupan por los desarrolladores :)

+0

No había escuchado esto antes, pero apuesto a que tienes razón de que esto podría ser un factor. –

11

El STL real requiere que si se utiliza de referencia contando que la semántica son los mismos que para una versión no contaba referencia. Esto no es trivial para el caso general. (Por eso no debe escribir su clase de cadena).

Debido a la siguiente situación:

std::string x("This is a string"); 
char&   x5 = x[5]; 
std::string y(x); 

x5 = '*'; 

Ver: http://www.sgi.com/tech/stl/string_discussion.html para más detalles

+0

Interesante ... el estándar en realidad tiene una nota que dice específicamente que las cadenas recuentadas están permitidas, pero que las sematics deben ser las mismas que las cadenas no refincadas. (El comentario continúa en el siguiente comentario debido a limitaciones de tamaño) –

+0

El estándar muestra un ejemplo similar al suyo (pero modificando a través de un iterador en lugar de una referencia) y dice que la segunda cadena no debe modificarse, pero no da una indicación de cómo la implementación debería hacer eso (hey es un detalle de implementación, ¿no?). –

+0

-1 Puedes verificar este código en g ++ y ver que funciona perfectamente. De hecho, g ++ tiene cadenas seguras para subprocesos usando ref-counting. Por lo tanto, es definitivamente factible – Artyom

7

según lo declarado por Martin & Michael, copiar al escribir (CP) es a menudo más problemas de lo que vale la pena, para su posterior leyendo ver este excelente artículo por Kelvin Henney sobre Mad COW Disease y creo que fue Andrei Alexandrescu que declaró que Small String Optimization tiene un mejor rendimiento en muchas aplicaciones (pero no puedo encontrar el artículo).

Small String Optimization es donde hace que el objeto de cadena sea más grande y evita las asignaciones de pila para cadenas pequeñas. Una implementación juguete se verá algo como esto:

class string { 
    char *begin_, *end_, *capacity_; 
    char buff_[64]; // pick optimal size (or template argument) 
public: 
    string(const char* str) 
    { 
     size_t len = strlen(str); 
     if (len < sizeof(buff_)) 
     { 
      strcpy(buff_, str); 
      begin_ = buff_; 
      capacity_ = buff_ + sizeof(buff_); 
     } 
     else 
     { 
      begin_ = strdup(str); 
      capacity_ = begin_ + len; 
     } 
     end_ = begin_+len; 
    } 

    ~string() 
    { 
     if (begin_ != buff_) 
      free(begin_); // strdup requires free 
    } 
    // ... 
}; 
Cuestiones relacionadas