Russian Qt Forum
Ноябрь 24, 2024, 20:28 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 4   Вниз
  Печать  
Автор Тема: Причины утечки памяти и способы борьбы с ними  (Прочитано 26161 раз)
8Observer8
Гость
« : Февраль 12, 2014, 18:41 »

Привет!

Как я понял, один из самых действенных способов борьбы с утечками памяти - это использование "умных указателей"

Вот примеры из этой книги: http://www.wrox.com/WileyCDA/WroxTitle/Professional-C-2nd-Edition.productCd-0470932449.html

Код:
void leaky()
{
    Simple* mySimplePtr = new Simple();  // BUG! Memory is never released!
    mySimplePtr->go();
}

Код:
void notLeaky()
{
    shared_ptr<Simple> mySimpleSmartPtr(new Simple());
    mySimpleSmartPtr->go();
}

В первом примере утечка, а во втором - нет (память после выхода из функции notLeaky() сразу освобождается)

А вот в следующем примере утечка памяти:

Код:
void couldBeLeaky()
{
    Simple* mySimplePtr = new Simple();
    mySimplePtr->go();
    delete mySimplePtr;
}

Так как при вызове mySimplePtr->go(); может срабатывать исключение, тогда строка кода: delete mySimplePtr; не будет выполняться.

Нужно ли в Qt везде, где используются указатели использовать shared_ptr?

К примеру, здесь:

Код:
Dialog *w = new Dialog();
Записан
Serr500
Гость
« Ответ #1 : Февраль 12, 2014, 18:57 »

Как я понял, один из самых действенных способов борьбы с утечками памяти - это использование "умных указателей"
Один из самых действенных способов борьбы с утечками памяти - использование Java, C# или другого языка с автоматической сборкой мусора!  Подмигивающий А в C++ самый действенный способ - писать безглючный код. Надо помнить, что исключение - это исключительная ситуация и не злоупотреблять ими.

Ну и фигню же Вы читаете! Да ещё и на английском...

Нужно ли в Qt везде, где используются указатели использовать shared_ptr?

К примеру, здесь:

Код:
Dialog *w = new Dialog();
А Вы подумайте.  Подмигивающий Подсказка: Qt::WA_DeleteOnClose.
Записан
8Observer8
Гость
« Ответ #2 : Февраль 12, 2014, 19:09 »

А в C++ самый действенный способ - писать безглючный код. Надо помнить, что исключение - это исключительная ситуация и не злоупотреблять ими.

Не понял. Мешьше использовать исключения? А как же ошибки обрабатывать?

Если даже полностью отказаться от использования исключений, то это же нас не спасает. Так как многие функции STL могут их выбрасывать. Это я по поводу того что: "не злоупотреблять ими"

Ну и фигню же Вы читаете! Да ещё и на английском...

Я ничего лучше не нашёл. Посоветуете что-нибудь?

Нужно ли в Qt везде, где используются указатели использовать shared_ptr?

К примеру, здесь:

Код:
Dialog *w = new Dialog();
А Вы подумайте.  Подмигивающий Подсказка: Qt::WA_DeleteOnClose.

Я где-то встречал информацию, что объекты, которые наследуют от QObject не нужно удалять. Как Вы это понимаете?
Записан
Bepec
Гость
« Ответ #3 : Февраль 12, 2014, 19:20 »

В Qt присутствует ответственность родителей за детей. Т.е. задавая объекту родителя, вы можете быть уверены что он позаботиться об его удалении при своём удалении.

Самый просто способ в С++ бороться с утечками уже был озвучен - писать безглючный код. Использование sharedPtr конечно хорошо, но...
У меня уже рефлекс - я выделил память, надо освободить. И shared проверить Веселый
Записан
Serr500
Гость
« Ответ #4 : Февраль 12, 2014, 19:37 »

Не понял. Мешьше использовать исключения? А как же ошибки обрабатывать?

Если даже полностью отказаться от использования исключений, то это же нас не спасает. Так как многие функции STL могут их выбрасывать. Это я по поводу того что: "не злоупотреблять ими"
Я для себя вывел такую формулу работы: обрабатывать только те исключения, которые могут возникнуть в коде, который я не контролирую, а сам генерирую исключения только в коде, который предназначен для использования другими людьми и только в том случае, когда без этого обойтись уже нельзя (ну, или в техзадании жёстко прописано). В подавляющем большинстве случаев исключений можно избежать, скрупулёзно выполняя все проверки и передавая каким-либо образом признак ошибки вызвавшему коду. Если мой код выбрасывает исключение, то он уже не может справиться с ошибкой и почти всегда в этом случае приложение проще убить, чем пытаться что-то исправить. Если Вы пишете свой код, то его правильное написание не вызовет исключения. Если же приходится использовать внешние библиотеки, "любящие" исключения, то даже в этом случае можно обойтись без их обработки, поскольку условия возникновения исключений часто точно известны. Например, стопроцентная гарантия от out_of_range - проверка индекса перед вызовом метода.

Я ничего лучше не нашёл. Посоветуете что-нибудь?
Мнэ-э-э... Вспомню - напишу.

Я где-то встречал информацию, что объекты, которые наследуют от QObject не нужно удалять. Как Вы это понимаете?
Это не совсем верно. Точнее, совсем не верно. Нужно, но можно обойтись автоматическим удалением. Например, если объект имеет родителя, то родитель удалит всех детей при своём разрушении. Но если объект родителя не имеет, то нужно грохнуть его самостоятельно.

А вот Вам головоломка. Что будет в следующем коде и почему?
Код:
class QObject2 : public QObject {
    public:
        void work() {
            // Что-то делаем
            deleteLater();
        }
}

// В одной далёкой-далёкой функции...
{
    std::shared_ptr<QObject> Ptr(new QObject2);
    Ptr->work();
    QApplication::processEvents();
}
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #5 : Февраль 12, 2014, 19:42 »

В Qt присутствует ответственность родителей за детей. Т.е. задавая объекту родителя, вы можете быть уверены что он позаботиться об его удалении при своём удалении.
Тут главное не забывать, что удаление произойдет только при удалении родителя, а многие родители живут все время работы программы.
И получается так: объекты создаются, им присваивается "вечный" родитель и все эти объекты живут все время, хотя никому не нужны.
Можно вспомнить объекты QNetworkReply, которые создаются объектом QNetworkAccessManager.

Самый просто способ в С++ бороться с утечками уже был озвучен - писать безглючный код.
Да, это самый простой способ, но лучший способ это писать идеальный код. Плохо, что это практически невозможно. Улыбающийся

У меня уже рефлекс - я выделил память, надо освободить.
А если указатель на объект нужно передать в "свободное плаванье"?
А с shared_ptr таких проблем нет совсем.
Записан
Bepec
Гость
« Ответ #6 : Февраль 12, 2014, 21:37 »

Ну если в плаванье, то там перекладываю ответственность на капитана того корабля Веселый
С Shared нужно тоже учитывать свои нюансы.

Хотя мб у меня это сложилось из-за библиотеки с которой приходилось их использовать. Просто для запуска приложению нужно было держать 8 умных указателей, а при создании объекта ещё по 3 (1 на объект, 1 на нотификатор, 1 на сокет). В результате получался код % на 15 из указателей.

Да и созданные под это классы были уже "неизвлекаемые" из системы. А со свободными указателями гораздо проще Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #7 : Февраль 12, 2014, 22:51 »

Ну если в плаванье, то там перекладываю ответственность на капитана того корабля Веселый
Так если капитан того корабля вы сами? Начинаете придумывать костыли и потому при изменении условий переделывать... и так может продолжаться постоянно.
С умными указателями ничего переделывать не приходится.

С Shared нужно тоже учитывать свои нюансы.
Например.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #8 : Февраль 12, 2014, 23:22 »

Нужно ли в Qt везде, где используются указатели использовать shared_ptr?
Скажу про себя. Во всех своих программах я использую исключительно умные указатели (обычных указателей нет вообще).
Но это не распространяется на программы с использованием Qt. Связано это с тем, что у Qt есть свой "стиль" написания кода и мне приходится его придерживаться, что бы с моим кодом могли работать все те, кто уже работает с Qt. В нем, конечно, есть умные указатели, но не в тех объемах как хотелось бы. Улыбающийся
Записан
OKTA
Гость
« Ответ #9 : Февраль 12, 2014, 23:27 »

А вот Вам головоломка. Что будет в следующем коде и почему?
Код:
class QObject2 : public QObject {
    public:
        void work() {
            // Что-то делаем
            deleteLater();
        }
}

// В одной далёкой-далёкой функции...
{
    std::shared_ptr<QObject> Ptr(new QObject2);
    Ptr->work();
    QApplication::processEvents();
}

Объект не удалится, потому что deleteLater не вызовется из очереди через ProcessEvents?
А за счет shared_ptr удаление произойдет все равно в итоге.
Я так понимаю.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Февраль 13, 2014, 06:03 »

А вот Вам головоломка. Что будет в следующем коде и почему?
Код:
class QObject2 : public QObject {
    public:
        void work() {
            // Что-то делаем
            deleteLater();
        }
}

// В одной далёкой-далёкой функции...
{
    std::shared_ptr<QObject> Ptr(new QObject2);
    Ptr->work();
    QApplication::processEvents();
}

Объект не удалится, потому что deleteLater не вызовется из очереди через ProcessEvents?
А за счет shared_ptr удаление произойдет все равно в итоге.
Я так понимаю.
Того же мнения - deleteLater остается в очереди (кстати хз почему). А когда shared_ptr удалит объект - деструктор QObject вычистит deleteLater из очереди (как и все event'ы посланные ему). Итого - все корректно. И здесь лучше использовать QPointer, тогда хоть тот, хоть этот удалит - голова не болит.

Как я понял, один из самых действенных способов борьбы с утечками памяти - это использование "умных указателей"
Вы ищите "простые" решения для совсем не простых вещей, что в принципе неверно. Да, на простых примерах "умные указатели" выглядят очень привлекательно, но реально это не всегда так. См хотя бы пример выше - зачем мы ввязались в головоломку, была ли в этом реальная необходимость? Не решаем ли мы проблемы что сами себе и создали привлекая умные/могучие средства? Лучше начать с базовых средств языка, и только если возникнет необходимость - кого-то привлечь.

И еще: отладка с "умными указателями" существенно замедляется - часто именно по этой причине я обхожусь простецким delete
Записан
deMax
Хакер
*****
Offline Offline

Сообщений: 600



Просмотр профиля
« Ответ #11 : Февраль 13, 2014, 11:55 »

А в C++ самый действенный способ - писать безглючный код.

Самый простой способ это - CTRL+F, искать по всему проекту "new". И проследить, что все что создано, корректно удаляется(Это не займет очень много времени). Библиотеки проверить тестами - если они текут то подумать как снизить протечку. Для наследников QObject надо смотреть где удаляется родитель и стоит ли заранее удалить объект в нужном месте или пусть его родитель вместе с собой удалит.

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

Еще есть valgrid и тесты.
« Последнее редактирование: Февраль 13, 2014, 11:57 от deMax » Записан
8Observer8
Гость
« Ответ #12 : Февраль 14, 2014, 12:25 »

Всем большое спасибо за ответы! Я позже разберу их.
Записан
Akon
Гость
« Ответ #13 : Февраль 15, 2014, 13:23 »

Цитировать
Скажу про себя. Во всех своих программах я использую исключительно умные указатели (обычных указателей нет вообще).
Но это не распространяется на программы с использованием Qt. Связано это с тем, что у Qt есть свой "стиль" написания кода и мне приходится его придерживаться, что бы с моим кодом могли работать все те, кто уже работает с Qt. В нем, конечно, есть умные указатели, но не в тех объемах как хотелось бы.
Old, допустим ты пишешь библиотеку, в которой есть функция:
Код:
NonQObject* createObject()
{
    NonQObject* result = new NonQObject;
    functionThatCanThrowException();  // может сделать, throw std::runtime_error(), например
    return result;
}

Как ты будешь писать эту функцию?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #14 : Февраль 15, 2014, 13:44 »

Old, допустим ты пишешь библиотеку, в которой есть функция:

Код
C++ (Qt)
QSharedPointer<NonQObject> createObject()
{
   QSharedPointer<NonQObject> result( new NonQObject );
   functionThatCanThrowException();  // может сделать, throw std::runtime_error(), например
   return result;
}
 

Если без Qt, то будет shared_ptr.
Как я уже писал выше, в проектах без Qt у меня все без исключения указатели умные.

ADD: практически для всех указателей я стараюсь делать typedef:
Код
C++ (Qt)
typedef QSharedPointer<NonQObject> NonObjectPtr;
 
NonQObjectPtr createObject()
{
   NonQObjectPtr result( new NonQObject );
   functionThatCanThrowException();  // может сделать, throw std::runtime_error(), например
   return result;
}
 
« Последнее редактирование: Февраль 15, 2014, 13:50 от Old » Записан
Страниц: [1] 2 3 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.097 секунд. Запросов: 23.