Подтвердить что ты не робот

Как вернуть std::string.c_str()

У меня есть метод, который возвращает указатель константы char. Он использует std::string и, наконец, возвращает указатель c_str() char.

const char * returnCharPtr()
{
    std::string someString;

    // some processing!.

    return someString.c_str();
}

У меня есть сообщение от инструмента "СКОРОСТЬ", что вышеприведенное не является хорошим использованием. Я googled и обнаружил, что вернувшийся указатель char был бы признан недействительным, как только someString встретит его уничтожение.

Учитывая это, как решить эту проблему? Как правильно вернуть указатель char?

Возврат std::string разрешит эту проблему. Но я хочу знать, есть ли другие способы сделать это.

4b9b3361

Ответ 1

Что происходит в этом коде:

const char * returnCharPtr()
{
    std::string someString("something");
    return someString.c_str();
}
  • создается экземпляр std::string - это объект с автоматической продолжительностью хранения
  • возвращается указатель на внутреннюю память этой строки
  • объект someString разрушен и внутренняя память очищена.
  • вызывающая сторона этой функции получает висячий указатель (недопустимый указатель), который дает undefined поведение

Лучшее решение: вернуть объект:

std::string returnString()
{
    std::string someString("something");
    return someString;
}

Ответ 2

В C++ самое простое - просто вернуть std::string (что также эффективно благодаря таким оптимизациям, как RVO и C++ 11 семантика перемещения):

std::string returnSomeString()
{
    std::string someString;

    // some processing...

    return someString;
}

Если вам действительно нужен необработанный указатель C char*, вы всегда можете вызвать .c_str() для возвращаемого значения, например

// void SomeLegacyFunction(const char * psz)

// .c_str() called on the returned string, to get the 'const char*' 
SomeLegacyFunction( returnSomeString().c_str() );

Если вы действительно хотите вернуть указатель char* из функции, вы можете динамически распределять строковую память в куче (например, используя new[]) и возвращать указатель на это:

// NOTE: The caller owns the returned pointer, 
// and must free the string using delete[] !!!
const char* returnSomeString()
{
    std::string someString;

    // some processing...

    // Dynamically allocate memory for the returned string
    char* ptr = new char[someString.size() + 1]; // +1 for terminating NUL

    // Copy source string in dynamically allocated string buffer
    strcpy(ptr, someString.c_str());

    // Return the pointer to the dynamically allocated buffer
    return ptr;
}

Альтернативой является предоставление указателя буфера назначения и размера буфера (чтобы избежать переполнения буфера!) В качестве параметров функции:

void returnSomeString(char* destination, size_t destinationSize)
{
    std::string someString;

    // some processing...

    // Copy string to destination buffer.
    // Use some safe string copy function to avoid buffer overruns.
    strcpy_s(destination, destinationSize, someString.c_str());
}

Ответ 3

Поскольку этот вопрос отмечен C, сделайте следующее:

#define _POSIX_C_SOURCE 200809L
#include <string.h>

const char * returnCharPtr()
{
  std::string someString;

  // some processing!.

  return strdup(someString.c_str()); /* Dynamically create a copy on the heap. */
}

Не забудьте free() вернуть функцию, если она больше не используется.

Ответ 4

Хорошо, ПОЛНОСТЬЮ верна. Причина, по которой ваш текущий подход не срабатывает, заключается в том, что экземпляр std::string, созданный внутри функции, будет действителен только до тех пор, пока эта функция будет запущена. Когда ваша программа покинет область действия, будет вызван деструктор std::string, и это будет конец вашей строки.

Но если вам нужна C-строка, как насчет...

const char * returnCharPtr()
{
    std::string someString;

    // some processing!.

    char * new_string = new char[someString.length() + 1];

    std::strcpy(new:string, someString.c_str());

    return new_string;
}

Но подождите... это почти так же, как возврат std::string, не так ли?

std::string returnCharPtr()
{
    std::string someString;

    // some processing!.

    return new_string;
}

Это скопирует вашу строку в новую за пределами области действия. Он работает, но создает новую копию строки.

Благодаря оптимизации возвращаемого значения это не создаст копию (спасибо за все исправления!).

Итак, еще один параметр - передать параметр в качестве аргумента, поэтому вы обрабатываете строку в функции, но не создаете новую копию.

void returnCharPtr(std::string & someString)
{
    // some processing!.
}

Или, опять же, если вы хотите C-Strings, вам нужно следить за длиной вашей строки:

void returnCharPtr(char*& someString, int n) // a reference to pointer, params by ref
{
    // some processing!.
}

Ответ 5

Лучшим способом было бы вернуть std::string, который автоматически выполняет управление памятью. Если, с другой стороны, вы действительно возвращали const char*, который указывает на некоторую память, выделенную вами из returnCharPtr, тогда она должна быть явно освобождена кем-то другим.

Оставайтесь с std::string.

Ответ 6

Вы можете передать указатель на свою строку и напрямую обработать метод (т.е. вообще избегать возврата)

void returnCharPtr(char* someString)
{    
    // some processing!
    if(someString[0] == 'A')
       someString++;
}

Ответ 7

Ваши варианты:

Возврат std::string

Передайте буфер в returnCharPtr(), который будет содержать новый буфер символов. Для этого вам необходимо проверить, что предоставленный буфер достаточно велик, чтобы удерживать строку.

Создайте новый массив char внутри returnCharPtr(), скопируйте буфер в новый и верните указатель на него. Это требует, чтобы вызывающий пользователь явно вызывал delete [] на то, что они явно не создавали с помощью new, или сразу же помещал его в класс интеллектуальных указателей. Это решение было бы улучшено, если бы вы вернули смарт-указатель, но на самом деле просто имеет смысл возвращать std::string напрямую.

Выберите первый; return std::string. Это, безусловно, упрощенный и безопасный вариант.

Ответ 8

Проблема заключается в том, что someString уничтожается в конце функции, а функция возвращает указатель на несуществующие данные.

Не возвращайте .c_str() строки, которая может быть уничтожена, прежде чем использовать возвращаемый указатель char.

Вместо...

const char* function()
{
    std::string someString;
    // some processing!
    return someString.c_str();
}

//...

useCharPtr(function());

использование

std::string function()
{
    std::string someString;
    // some processing!
    return someString;
}

//...

useCharPtr(function().c_str());

Ответ 9

Если у вас есть свобода изменить возвращаемое значение returnCharPtr, измените его на std::string. Это будет самый чистый метод для возврата строки. Если вы не можете, вам нужно выделить память для возвращаемой строки, скопировать ее из std::string и вернуть указатель на выделенную память. Вы также должны убедиться, что вы удаляете память в вызывающей функции. Поскольку вызывающий абонент будет отвечать за освобождение памяти, я бы изменил возвращаемое значение на char*.

char* returnCharPtr() 
{
    std::string someString;

    // some processing!.

    char* cp = new char[someString.length()+1];
    strcpy(cp, someString.c_str());
    return cp;
}

Ответ 10

Решение, которое не было вызвано в других ответах.

Если ваш метод является членом класса, например:

class A {
public:
    const char *method();
};

И если экземпляр класса будет жить за пределами полезности указателя, вы можете сделать:

class A {
public: 
    const char *method() {
        string ret = "abc";
        cache.push_back(std::move(ret));
        return cache.last().c_str();
    }
private:
    vector<string> cache; //std::deque would be more appropriate but is less known
}

Таким образом, указатели будут действительны до разрушения A.

Если функция не является частью класса, она все равно может использовать класс для хранения данных (например, переменную static функции или экземпляр внешнего класса, на который можно ссылаться по всему миру, или даже static член класса). Механизмы могут быть сделаны для удаления данных через некоторое время, чтобы не сохранять их навсегда.