Pregunta

Estoy tratando de utilizar std::string en lugar de char* siempre que sea posible, pero me preocupa que me puede ser degradante demasiado el rendimiento.Es esta una buena manera de devolver cadenas (ninguna comprobación de errores, por razones de brevedad)?

std::string linux_settings_provider::get_home_folder() {
    return std::string(getenv("HOME"));
}

También, una pregunta relacionada:al aceptar las cadenas como parámetros, debería recibir como const std::string& o const char*?

Gracias.

¿Fue útil?

Solución

Devuelve la cadena.

Creo que la mejor abstracción vale la pena. Hasta que pueda medir una diferencia de rendimiento significativa, diría que es una microoptimización que solo existe en su imaginación.

Tomó muchos años obtener una buena abstracción de cadena en C ++. No creo que Bjarne Stroustroup, tan famoso por su conservador & "; Solo pague por lo que usa &"; dictamen, habría permitido un obvio asesino en el lenguaje. Una mayor abstracción es buena.

Otros consejos

Devuelve la cadena, como todo el mundo dice.

al aceptar las cadenas como parámetros, debería recibir como const std::string& o const char*?

Yo diría que tomar cualquier const parámetros por referencia, a menos que cualquiera de ellos está suficientemente ligeras como para tomar por valor, o en aquellos casos donde se necesita un puntero nulo a ser una entrada válida que significa "ninguna de las anteriores".Esta política no es específico para cadenas.

No const parámetros de referencia son discutibles, porque desde el código de llamada (sin un buen IDE), no se puede ver de inmediato si son pasados por valor o por referencia, y la diferencia es importante.Por lo que el código puede ser confuso.Para const params, que no es de aplicación.La gente que lee el código de llamada generalmente puede simplemente asumir que no es su problema, por lo que sólo necesitan ocasionalmente para comprobar la firma.

En el caso de que usted va a tomar una copia del argumento, en función de su política general debe tomar el argumento por valor.Entonces usted ya tiene una copia que usted puede usar, y si a usted le han copiado en alguna ubicación específica (como un miembro de datos), entonces usted puede mover (en C++11) o swap (en C++03) para llegar allí.Esto le da el compilador de la mejor oportunidad para optimizar los casos donde la persona que llama, pasa un objeto temporal.

Para string en particular, esto se refiere al caso donde la función toma un std::string por su valor, y el autor de la llamada especifica como argumento una expresión literal de cadena o un char* apuntando a una nul cadena terminada.Si usted tomó un const std::string& y copiado en el de la función, lo que resultaría en la construcción de dos cadenas.

El costo de copiar cadenas por valor varía según la implementación de STL con la que esté trabajando:

  • std :: string bajo MSVC usa la optimización de cadena corta, de modo que las cadenas cortas (< 16 caracteres iirc) no requieren ninguna asignación de memoria (se almacenan dentro de la cadena std :: ), mientras que los más largos requieren una asignación de montón cada vez que se copia la cadena.

  • std :: string bajo GCC usa una implementación contada de referencia: al construir un std :: string a partir de un char *, se realiza una asignación de montón cada vez, pero al pasar por valor a una función, un recuento de referencia simplemente se incrementa, evitando la asignación de memoria.

En general, es mejor olvidarse de lo anterior y devolver std :: strings por valor, a menos que lo haga miles de veces por segundo.

re: paso de parámetros, tenga en cuenta que hay un costo de ir de char * - > std :: string, pero no de ir de std :: string - > char *. En general, esto significa que es mejor aceptar una referencia constante a un std :: string. Sin embargo, la mejor justificación para aceptar un const std :: string & Amp; como argumento es que la persona que llama no tiene que tener un código adicional para verificar vs. nulo.

Parece una buena idea.

Si esto no es parte de un software en tiempo real (como un juego) sino una aplicación normal, debería estar más que bien.

Recuerde, " La optimización prematura es la raíz de todo mal "

La naturaleza humana es preocuparse por el rendimiento, especialmente cuando el lenguaje de programación admite la optimización de bajo nivel. Sin embargo, lo que no debemos olvidar como programadores es que el rendimiento del programa es solo una de las muchas cosas que podemos optimizar y admirar. Además de la velocidad del programa, podemos encontrar belleza en nuestro propio rendimiento. Podemos minimizar nuestros esfuerzos al tratar de lograr el máximo rendimiento visual y la interactividad de la interfaz de usuario. ¿Crees que podría ser más motivación que preocuparse por los bits y los ciclos a largo plazo ... Entonces sí, devuelve string: s. Minimizan el tamaño de su código y sus esfuerzos, y hacen que la cantidad de trabajo que realiza sea menos deprimente.

En su caso, la Optimización del valor de retorno tendrá lugar para que std :: string no se copie.

Tenga cuidado cuando cruce los límites del módulo.

Entonces es mejor devolver tipos primitivos ya que los tipos C ++ no son necesariamente compatibles con binarios, incluso en diferentes versiones del mismo compilador.

Estoy de acuerdo con los otros carteles, que debería usar cadena.

Pero sepa que, dependiendo de cuán agresivamente optimice su compilador los temporarios, probablemente tendrá algo de sobrecarga adicional (sobre el uso de una matriz dinámica de caracteres). (Nota: la buena noticia es que en C ++ 0a, el uso prudente de las referencias rvalue no requerirá optimizaciones del compilador para comprar eficiencia aquí, y los programadores podrán hacer algunas garantías de rendimiento adicionales sobre su código sin depender de la calidad de el compilador.)

En su situación, ¿vale la pena la sobrecarga adicional para introducir la administración manual de memoria? La mayoría de los programadores razonables no estarían de acuerdo, pero si su aplicación termina teniendo problemas de rendimiento, el siguiente paso sería perfilar su aplicación; por lo tanto, si introduce complejidad, solo lo hace una vez que tenga buena evidencia de que es necesario mejorar eficiencia general.

Alguien mencionó que la optimización del valor de retorno (RVO) es irrelevante aquí, no estoy de acuerdo.

El texto estándar (C ++ 03) en esto lee (12.2):

[Comenzar cita estándar]

  

Los temporales del tipo de clase se crean en varios contextos: vincular un valor r a una referencia (8.5.3), devolver un valor r (6.6.3), una conversión que crea un valor r (4.1, 5.2.9, 5.2.11 , 5.4), lanzando una excepción (15.1), ingresando un controlador (15.3), y en algunas inicializaciones (8.5). [Nota: la vida útil de los objetos de excepción se describe en 15.1. ] Incluso cuando se evita la creación del objeto temporal (12.8), toda la semántica   las restricciones deben respetarse como si se hubiera creado el objeto temporal. [Ejemplo: incluso si no se llama al constructor de la copia, se cumplirán todas las restricciones semánticas, como la accesibilidad (cláusula 11). ]

 [Example:  
struct X {
  X(int);
  X(const X&);
  ˜X();
};

X f(X);

void g()
{
  X a(1);
  X b = f(X(2));
  a = f(a);
}
  

Aquí, una implementación podría usar un temporal en el que construir X (2) antes de pasarlo a f () usando X & # 8217; s copy-constructor; alternativamente, X (2) podría construirse en el espacio utilizado para contener el argumento. Además, se puede usar un temporal para contener el resultado de f (X (2)) antes de copiarlo en b usando X & # 8217; s copyconstructor; alternativamente, el resultado de f () & # 8217; s podría construirse en b. Por otro lado, la expresión a = f (a) requiere un temporal para el argumento a o el resultado de f (a) para evitar el alias indeseado de   a. ]

[Fin de la cotización estándar]

Esencialmente, el texto anterior dice que posiblemente puede confiar en RVO en situaciones de inicialización, pero no en situaciones de asignación. La razón es que, cuando está inicializando un objeto, no hay forma de que lo que lo está inicializando pueda ser alias al objeto en sí mismo (por lo que nunca hace una autocomprobación en un constructor de copias), pero cuando lo hace una tarea, podría.

No hay nada sobre su código, que inherentemente prohíbe RVO, pero lea la documentación de su compilador para asegurarse de que realmente puede confiar en él, si realmente lo necesita.

Estoy de acuerdo con duffymo. Primero debe hacer una aplicación de trabajo comprensible y luego, si es necesario, atacar la optimización. Es en este punto que tendrá una idea de dónde están los principales cuellos de botella y podrá administrar su tiempo de manera más eficiente para crear una aplicación más rápida.

Estoy de acuerdo con @duffymo. No optimices hasta que hayas medido, esto es doblemente cierto cuando haces micro optimizaciones. Y siempre: mida antes y después que haya optimizado, para ver si realmente cambió las cosas a mejor.

Devuelve la cadena, no es una gran pérdida en términos de rendimiento, pero seguramente facilitará tu trabajo después.

Además, siempre puedes alinear la función, pero la mayoría del optimizador la arreglará de todos modos.

Si pasa una cadena referenciada y trabaja en esa cadena, no necesita devolver nada. ;)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top