HOWTO · C++

Cómo devolver una cadena de una función en C++

Este artículo demuestra cómo devolver una cadena de una función en C++

En esta página

Para una función habitual de C++, devuelve un std::string por valor. Así, quien llama obtiene una cadena independiente y C++ puede aplicar la optimización del valor de retorno cuando sea posible. Usa una referencia solo para devolver un objeto existente cuya vida útil se gestione en otro lugar, y usa un puntero o una cadena de estilo C únicamente cuando la interfaz lo requiera. Los ejemplos siguientes usan C++11 o posterior.

Devolver una cadena por valor

Devolver por valor es la opción predeterminada más clara cuando una función crea o transforma una cadena. Quien llama recibe su propio valor de cadena y puede almacenarlo o modificarlo de forma independiente. Devolver una variable local con nombre suele ser eficiente: el compilador puede construir el resultado directamente en el destino de quien llama mediante la optimización del valor de retorno con nombre. Si no se aplica esa optimización, se puede mover std::string. Ninguno de estos comportamientos requiere añadir std::move a la instrucción de retorno.

Ejecuta este ejemplo:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#include <iostream>
#include <string>

std::string makeGreeting() {
    std::string result = "Hello, C++!";
    return result;
}

int main() {
    std::string greeting = makeGreeting();
    std::cout << greeting << '\n';
}

El tipo de retorno de la función es std::string, y return result; devuelve el valor local. La variable local deja de existir cuando termina la función, pero la cadena devuelta sigue disponible para quien llama. Devolver por valor suele ser más sencillo y seguro que gestionar por separado la propiedad de una cadena asignada dinámicamente.

Devolver una referencia a una cadena existente

Una referencia devuelta es un alias de un objeto que ya existe; no crea una cadena nueva. Puede ser útil cuando una función selecciona o expone una cadena que pertenece a un objeto de vida más larga. Quien llama ve el mismo objeto, por lo que los cambios hechos mediante una referencia no constante afectan al original. Usa una const std::string& si quien llama necesita leer la cadena sin modificarla.

El objeto referenciado debe seguir existiendo durante todos los usos de la referencia devuelta. Nunca devuelvas una referencia a una variable local: esta se destruye al salir de la función y deja a quien llama con una referencia colgante. Una referencia recibida como parámetro solo puede devolverse de forma segura si quien llama mantiene vivo el argumento, como en este ejemplo:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#include <iostream>
#include <string>

const std::string& sameString(const std::string& value) {
    return value;
}

int main() {
    std::string original = "Existing string";
    const std::string& alias = sameString(original);
    std::cout << alias << '\n';
}

Devolver una cadena mediante un puntero

Devolver un puntero puede permitir un acceso opcional o ajustarse a una interfaz existente, pero hace que la vida útil y la propiedad formen parte del contrato. Devuelve la dirección de una cadena existente solo si esta seguirá viva durante todos los usos del puntero. Documenta quién es propietario del objeto y si el puntero puede ser nulo.

No asignes un std::string con new solo para devolverlo. Eso obliga a quien llama a recordar que debe liberarlo con delete y complica las salidas anticipadas y la gestión de errores. Si la cadena es nueva, devuélvela por valor. El ejemplo siguiente apunta a un objeto existente y lo mantiene vivo mientras se usa el puntero:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#include <iostream>
#include <string>

const std::string* findGreeting(const std::string& value) {
    return &value;
}

int main() {
    std::string greeting = "Hello through a pointer";
    const std::string* result = findGreeting(greeting);
    if (result != nullptr) {
        std::cout << *result << '\n';
    }
}

La función no adquiere la propiedad de value. Quien llama no debe conservar el puntero devuelto después de que se destruya la cadena referenciada.

Usa std::move solo cuando sea útil

std::move es una conversión que permite realizar una operación de movimiento; no mueve datos por sí misma. En una instrucción de retorno, escribir return std::move(local); suele ser innecesario. Puede impedir la optimización del valor de retorno con nombre y sustituir potencialmente la construcción directa por un movimiento. Para un resultado local, prefiere return local; y deja que el lenguaje y el compilador elijan la optimización permitida.

Los movimientos explícitos son útiles para transferir desde un objeto que no puede recibir el tratamiento automático de movimiento, como un miembro que debe extraerse de un objeto de vida más larga. Esto modifica el objeto de origen, así que hazlo solo cuando ya no necesites que conserve su estado anterior. No añadas std::move solo para que el retorno parezca más rápido.

Devolver una cadena de estilo C con char*

Una cadena de estilo C es una secuencia de caracteres terminada en nulo, no un std::string. Si una API requiere un puntero a caracteres y el resultado es texto fijo, devuelve const char* que apunte a un literal de cadena. Los literales de cadena tienen duración de almacenamiento estática, por lo que el puntero sigue siendo válido durante todo el programa. Aquí, const char* significa un puntero a caracteres constantes: no se pueden modificar los caracteres a través del puntero, pero el propio puntero no es constante y se puede reasignar. Esto es distinto de char* const, que es un puntero constante a caracteres modificables. No se debe modificar el literal de cadena.

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#include <iostream>

const char* greeting() {
    return "Hello from C-style text";
}

int main() {
    std::cout << greeting() << '\n';
}

No devuelvas un puntero a un arreglo de caracteres local, que deja de existir al retornar la función, ni un puntero a una std::string local. El resultado de c_str() depende de la vida útil del objeto std::string y puede invalidarse al modificar esa cadena. Si quien llama necesita un resultado independiente y modificable, devuelve std::string en su lugar.