должен ли я освобождать память для «string * settings = new string [4]» на c в Linux?

#c #linux #pointers #memory-leaks

#c #linux #указатели #утечки памяти

Вопрос:

В настоящее время я отлаживаю некоторые устаревшие программы на c , которые работают в ОС Linux (centos 5). Все эти программы вызывают статическую функцию-член класса, чтобы получить настройку подключения к БД. Класс был закодирован следующим образом

 class DbSetting {
    public:
        static string* getDbSettings();
};

string* DbSetting::getDbSettings() {
    string* settings = new string[4];
    settings[0] = "dbname";
    settings[1] = "server";
    settings[2] = "username";
    settings[3] = "password";
    return settings;
}
  

В каждой программе функция main () будет вызывать эту статическую функцию следующим образом,

 int main(int argc, char* argv[]) {

    string* dbSettings = DbSetting::getDbSettings();

    //dbSettings is used to construct a db connection string

    return 0;
}
  

dbSettings используется для построения строки подключения к БД. Однако он не был «удален» (например, «удалить [] dbSettings»). Мой вопрос в том, что это потенциальная проблема с утечкой памяти? Такое использование существует и во многих других устаревших программах на c . Я в замешательстве по этому поводу. Надеюсь, кто-нибудь может дать мне ответ. Спасибо!

Ответ №1:

Это действительно утечка памяти. Это несколько незначительная проблема, поскольку ваши данные в любом случае должны оставаться в живых до конца программы, и при завершении программы они будут очищены, но это приведет к ложным срабатываниям детекторов утечки памяти, поэтому часто бывает хорошей идеей правильно очистить в любом случае; если вы позволите себеполагайтесь на очистку ОС, когда вам действительно нужно диагностировать утечку памяти, вы обнаружите, что у вас есть сотни ложных срабатываний для сортировки! Также будет очень сложно реорганизовать код, чтобы он был частью более крупной системы, где он может запускаться и останавливаться несколько раз в одном и том же процессе.

Самый простой способ избежать этого — использовать vector :

 std::vector<string> DbSetting::getDbSettings() {
    std::vector<string> settings(4);
    settings[0] = "dbname";
    settings[1] = "server";
    settings[2] = "username";
    settings[3] = "password";
    return settings;
}

int main(int argc, char* argv[]) {

    std::vector<string> dbSettings = DbSetting::getDbSettings();

    //dbSettings is used to construct a db connection string

    return 0;
}
  

vector автоматически очистит память, используемую строковым массивом внутри него, когда он будет уничтожен при возврате из main. Он также имеет ряд очень удобных функций, которых нет в необработанных массивах — например, он отслеживает размер массива и может автоматически изменять размер массива, если вы добавляете новые элементы с помощью push_back .

Комментарии:

1. Если вы можете использовать c 11, array<string,4> будет более подходящим.

2. ИМХО, неплохо переписать getDbSettings, чтобы возвращать std::vector в качестве ссылочного параметра, а не возвращаемого значения. Это уменьшит одно избыточное копирование вектора.

3. @zabulus, когда вы возвращаете объект, созданный в автоматической переменной, подобной этой, в большинстве случаев компилятор может оптимизировать копию (вызывающий объект предоставляет память для размещения результата, а вызываемый объект создает объект в этой области)

4. @bdonlan, эта оптимизация неявная, и это нехорошо

5. @zabulus, это обычно считается хорошим стилем. В частности, C 0x еще больше поощряет это, позволяя (через семантику перемещения ссылки rvalue) что-то близкое к RVO, даже если вы присваиваете существующей переменной.

Ответ №2:

В принципе, ответ — да. Вы должны delete[] использовать строки.

Если этот метод вызывается только из метода main(), по крайней мере, утечка памяти не увеличится, а останется на 4*sizeof(string) уровне объем памяти, зарезервированный для строковых данных, что обычно не является проблемой на практике. ОС позаботится об утечке при выходе. Вы все равно должны delete[] это делать по соображениям стиля и потому, что другие могут использовать такие методы чаще. Если они просто копируют и вставляют такой код, вы скоро получите утечки, которые имеют значение!

Ответ №3:

Было бы лучше, если бы вы передали результат getDbSettings() через ссылочный параметр:

 void DbSetting::getDbSettings(std::vector<std::string> amp;result) {
    result.clear();
    result.push_back("dbname");
    result.push_back("server");
    result.push_back("username");
    result.push_back("password");
}

int main(int argc, char* argv[]) {
  std::vector<std::string> dbSettings;
  DbSetting::getDbSettings(dbSettings);
  return 0;
}
  

Преимущества очевидны: не требуются дорожки выделения памяти. Также для передачи результата обратно из функции используется меньше временных объектов. При возврате std::vector<std::string> временный массив будет скопирован обратно, когда функция завершится (в случае отключенного RVO). Это было бы более чувствительно к большему объему возвращаемых данных, но рекомендуется делать это всегда.

Ответ №4:

Да, это утечка памяти. Всегда рекомендуется использовать delete[] после использования new() . Вы можете использовать интеллектуальный указатель, чтобы избежать утечек памяти.