#cpp
Демонстрационный код:
MyType getDataFromDB()
{
Driver *driver;
Connection *con;
Statement *stmt;
ResultSet *res;
/* Create a connection */
driver = get_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "login", "pass");
/* Connect to the MySQL test database */
con->setSchema("schema");
stmt = con->createStatement();
MyType resultAnythngAndAnother;
// Первый запрос
res = stmt->executeQuery("SELECT anything");
while (res->next())
{
// перебор данных с запроса "SELECT anything"
}
delete res; // <----- Вопрос #1: Нужно ли делать delete res перед новым использованием
переменной res?
// Другой запрос
res = stmt->executeQuery("SELECT another");
while (res->next())
{
// перебор данных с запроса "SELECT another"
}
delete res; // <----- Вопрос #2: Достаточно ли сделать delete res только тут?
return resultAnythngAndAnother;
}
Вопрос #1: Нужно ли делать delete res перед новым использованием
переменной res?
Вопрос #2: Достаточно ли сделать delete res только после
последнего вызова (когда она уже не будет больше использоваться)?
Ответы
Ответ 1
Правило состоит в том, что простого правила «делайте так и не думайте» нет. У каждого указателя в вашей программе есть владелец — тот, кто ответственен за время жизни объекта. Именно этот владелец должен удалять объект, и обязан знать, когда объект должен быть удалён. Если вы получаете указатель из какой-либо функции, в документации обязано быть написано, передаётся ли владение вам (и значит, вы теперь ответственный за удаление объекта), или оно остаётся за прежним владельцем (и значит, вы не имеете права удалять объект). Если такой информации в документации нет, выясните это у разработчика. Если разработчик недоступен, попробуйте воспользоваться здравым смыслом. Если вы сами разрабатываете функцию, которая возвращает указатель, вы должны решить, передаёте вы владение объектом вместе с указателем или нет, и отобразить этот факт в документации. Для вашего случая с точки зрения здравого смысла каждый stmt->executeQuery выделяет собственную память, которая не принадлежит вам, т. к. она переиспользуется при каждом next(). Скорее всего, однако, вы должны тем или иным образом закрыть Connection, иначе внешний код не будет знать, когда ему можно освободить эту память. Но вообще-то, загляните лучше в документацию. Например, вот эти страницы [1], [2] сообщают: Make sure that you free con, the sql::Connection object, as soon as you do not need it any more. But do not explicitly free driver, the connector object. You must free the sql::Statement, sql::Connection, and sql::ResultSet objects explicitly using delete. То есть, вам передаётся владение (и ответственность за удаление) con, stmt, res, но не driver. А значит, вы обязаны удалить объект по указателю res перед тем, как вы присвоите переменной res новое значение (и потеряете указатель на объект, за который вы ответственны).Ответ 2
У вас переменная объявлена как указатель ResultSet *res;. Указатель - это просто несколько байт, куда помещается адрес некоей выделенной области памяти. Вы выполняете executeQuery, он возвращает адрес созданного им объекта. Вы этот адрес просто кладете в свой указатель. Следующий executeQuery просто создает новый объект и возвращает адрес другой выделенной области памяти. Если вы его просто присвоите переменной res то затрете тот адрес, который там лежит, а область памяти, на которую он указывал, так и останется занятой. Следовательно вам необходимо выполнять delete res после каждого использования. И когда ваша функция завершится потеряются все ее переменные, следовательно перед завершением она по хорошему должна не забыть удалить еще stmt, con и driver. Хотя конечно коннекты к базе будут у вас наверняка в другом месте и вам не потребуется их постоянно создавать и удалять. А вот stmt точно забывать не стоит, он у вас скорее всего будет новый при каждом вызове функции.Ответ 3
По хорошему тону, ответы на вопросы 1 и 2 - нет и нет. Причины: Вы не имеете права удалять память выделенную не Вами - строка res = stmt->executeQuery("SELECT anything"); подразумивает, что выделение происходит в stmt - так и удалить её должна она же! Взависимости от доступа к методу executeQuery можно: вернуть std::unique_ptrили записывать сразу в такой указатель res.reset( stmt->execute() ) при условии, что res уже умный указатель. Если Ваш код имеет место фразе "главное не забыть сделать это" - поверьте вы забудете это: не сегодня, так через месяц. Я уже молчу про сопровождающего код программиста, который понятия не имеет, что он "должен" и кому ). Рекомендации: почитайте про C++ RAII Ознакомьтесь с ссылкой https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-ptr Ответ 4
Оператор delete возвращает память, которая выделена для объекта обратно в кучу, чтобы не было утечек или ошибок.Тем более, Вы работаете с БД. А, вообще есть "золотое правило работы с любыми ресурсами": Заняли ресурс (создали объект); Использовали ресурс; Освободили ресурс. И так при каждом использовании.Ответ 5
В С++ есть фишка связанная с вашим вопросом. А то есть после удаления указателя его можно обнулить и последующее удаление не приведет к ошибке, например: #includeusing namespace std; int main(){ int * i; // создали указатель i = new int; // распеределили память для переменной delete i; // удалили память, теперь i это просто указатель который ни начто не указывает // delete i; - если мы еще раз вызовем удаление то получим ошибку, потому что удалять уже нечего и в этом указателе находится мусорный адресс который указывает ни на что i = 0; // теперь указатель указывает на первый блок памяти delete i; // теперь это стало безопасным благодоря обнулению указателя return 0; } а насчет распределения и удалении памяти то они желательно должны находится в одной зоне видимости чтоб код был простым и не содержал ошибок. но это скорее совет чем инструкция к действию.
Комментариев нет:
Отправить комментарий