Являются ли строковые литералы, которые мы используем внутри функций, автоматическими переменными? Или они размещены в куче, которую мы должны освободить вручную?
У меня есть ситуация, подобная приведенному ниже коду, в котором я назначаю строковый литерал частному полю класса (помеченному как ОДИН в коде) и получаю его намного позже в моей программе и использую его (помечено как ДВА). Назначаю ли я переменную в стеке полю в ONE? Может ли код ссылаться на висящий указатель, который в этом случае работал, потому что программа была достаточно маленькой?
Я скомпилировал и запустил его, он работал нормально, но у меня странный сбой в моей реальной программе, где я назначаю строковые литералы таким полям класса, как это, и я подозреваю, что это случай, о котором я упоминал выше.
#include <iostream>
using namespace std;
class MemoryLeak
{
private:
char *s;
public:
MemoryLeak() {}
void store()
{
s = "Storing a string"; // ONE
}
char *retrieve()
{
return s;
}
};
int main()
{
MemoryLeak *obj = new MemoryLeak();
obj->store();
cout << obj->retrieve() << endl; // TWO
delete obj;
return 0;
}
Должен ли я объявлять переменную "s" как массив символов вместо указателя? Я планирую использовать std :: string, но мне это просто интересно.
Любые указатели или помощь, как всегда, приветствуются :) Спасибо.





Строковые литералы будут помещены компилятором в сегмент инициализированных данных или текста (кода) вашего двоичного файла, а не в (выделенной во время выполнения) памяти или стеке. Таким образом, вы должны использовать указатель, поскольку вы собираетесь ссылаться на строковый литерал, который компилятор уже создал для вас. Обратите внимание, что изменение этого параметра (которое обычно требует изменения защиты памяти) изменит все способы использования этого литерала.
+1 к const char *. MemoryLeak :: s должен быть const и должен быть const char * MemoryLeak :: retrieve ()
Может быть, причина сбоя в том, что вы не завершили строку нулем?
Эээ, строковые литералы всегда заканчиваются нулем.
Зависит от компилятора, не так ли?
Нет, если вы не имеете дело с компилятором 30-летней давности: P
Изменение строкового литерала является неопределенным поведением и, скорее всего, является причиной сбоя в вашей программе (ISO C++: 2.13.4 / 2). Стандарт допускает преобразование строкового литерала в char* для обратной совместимости с C, и вы должны иметь это преобразование в своем коде только в том случае, если оно вам абсолютно необходимо.
Если вы хотите рассматривать строковый литерал как константу, вы можете изменить тип своего члена на const char *.
Если ваш дизайн требует, чтобы s мог быть изменен, я бы рекомендовал изменить его тип на std::string.
Спасибо, Коди и Ричард.
Я нашел причину ошибки. Это произошло потому, что я выполнял удаление объекта, который уже был удален. Я делал:
if (obj != NULL) delete obj;
Я изменил его на:
if (obj != NULL)
{
delete obj;
obj = NULL;
}
Изучение C++ определенно весело :)
Да - это весело! Что касается обнаруженной вами ошибки, я бы действительно рекомендовал вам использовать интеллектуальные указатели. Выполните поиск по запросам «умный указатель» и «RAII» в StackOverflow, и вы найдете полезную информацию. Умные указатели позаботятся об управлении временем жизни объекта за вас.
удаление должно выполняться в деструкторах. Возможно, в назначении, но даже там copy-to-temp-and-swap обычно безопаснее (деструктор временного файла очищает замененное старое значение)
@ Ричард: Я посмотрю на умные указатели и RAII. @ onebyone.livejournal.com: Спасибо за это. Я удалю проверки NULL перед удалением в моем коде. Но, как заметил Ричард, умные указатели кажутся лучшим выбором, но не раньше, чем я узнаю об этом :) @MSalters: Я не уверен, что понимаю вас. :(
Давайте посмотрим на ваши варианты. Вам также следует сделать несколько вещей:
/*
* Should initialize s to NULL or a valid string in constructor */
MemoryLeak()
{
store();
}
void store()
{
// This does not need to be freed because it is a string literal
// generated by the compiler.
s = "Storing a string"; // ONE
// Note this is allowed for backward compatibility but the string is
// really stored as a const char* and thus unmodifiable. If somebody
// retrieves this C-String and tries to change any of the contents the
// code could potentially crash as this is UNDEFINED Behavior.
// The following does need to be free'd.
// But given the type of s is char* this is more correct.
s = strdup("Storing a string");
// This makes a copy of the string on the heap.
// Because you allocated the memory it is modifiable by anybody
// retrieving it but you also need to explicitly de-allocate it
// with free()
}
Вы используете C-Strings. Их не следует путать с C++ std :: string. C++ std :: string автоматически инициализируется пустой строкой. Любая выделенная память освобождается правильно. Его можно легко вернуть как в изменяемой, так и в неизменяемой версии. Также легко манипулировать (изменение усадки увеличения т.е.). Если вы увеличиваете C-String, вам необходимо перераспределить память и скопировать строку в новую память и т. д. (Это требует очень много времени и может привести к ошибкам).
Чтобы справиться с динамическим распределением вашего объекта, я бы узнал об интеллектуальных указателях. Подробнее об интеллектуальных указателях см. В этой статье. Умные указатели или кто владеет вами, детка
std::auto_ptr<MemoryLeak> obj(new MemoryLeak());
obj->store();
std::cout << obj->retrieve() << std::endl; // TWO
// No need to delete When object goes out of scope it auto deletes the memory.
Его изменение незаконно. Это действительно const char *.