#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() . Вы можете использовать интеллектуальный указатель, чтобы избежать утечек памяти.