C ++ удаляет указатель на функцию перед концом области видимости - PullRequest
0 голосов
/ 15 декабря 2018

Если у меня есть функция:

std::string returnString() {
    return "Hello, World!";
}

, вызов:

std::string hello = returnString();
std::cout << hello << std::endl;

производит Hello, World!.

Однако, если я пытаюсь:

const char* hello = returnString().c_str();

и попробуйте распечатать с помощью:

for (const char* p = hello; *p; ++p ) {
    std::cout << *p;
}
std::cout << std::endl;

Это дает мне ошибку, говорящую Invalid read of size 1, что означает, что p равно NULL.

Что вызывает такое поведение?

Спасибо за помощь.

1 Ответ

0 голосов
/ 15 декабря 2018

(Примечание: здесь я примыкаю к ​​некоторым деталям. Посмотрите Оптимизацию возвращаемого значения и Скопируйте Elision, если вы хотите знать исключения из правил, которые я здесь упоминаю. Не меняйте поведение, которое я описываю в этомхотя ответьте).

Когда вы возвращаете объект из функции, возвращаемый объект уничтожается в конце строки, в которой была вызвана функция.Это всегда дело.Номинально вы скопируете или переместите этот объект в другой объект в локальной области видимости, как в последнем фрагменте:

std::string hello = returnString();

В этой строке returnString возвращает один std::string объект, hello - этоСоздайте перемещение из возвращенного объекта, а затем исходный объект будет уничтожен.

Если вы рассмотрите немного другую линию, вот тогда возникают проблемы:

const char* hello = returnString().c_str();

В этом случае returnString возвращает объект std::string, вы сохраняете указатель на массив char, принадлежащий этому объекту std::string, затем оригинальный объект std::string уничтожается, принимая массив char s, на который у вас есть указательit.


std::string сохраняет владение массивом char s, на который указывает указатель, возвращаемый c_str.std::string удаляет принадлежащий ему массив, когда выходит из области видимости, что означает, что время жизни указанного массива связано с временем жизни объекта std::string.

Вы можете думать о std::stringвыглядит примерно так:

class string
{
public:
    string(const char* str)
        : ptr_(new char[strlen(str) + 1])
    {
        strcpy(ptr_, str);
    }

    ~string()
    {
        delete[] ptr_;
    }

    const char* c_str()
    {
        return ptr_;
    }

    // other members

private:
    const char* ptr_;
};

Реальный std::string немного сложнее, но основная идея та же.Когда объект std::string создан, он выделяет массив char s для хранения строковых данных, а когда объект std::string уничтожается, он удаляет этот массив.

Метод c_str простовозвращает указатель на внутренний массив std::string char.Тот факт, что у вас есть указатель на этот массив, не означает, что он не будет удален после смерти объекта std::string, это просто означает, что у вас есть указатель на некоторую память, которой вы больше не владеете.

...