Есть ли безопасный способ использования С++ 11 Smart Pointer и интерфейса для Raw Pointer вместе?
Я хочу использовать С++ 11 Smart Pointers в новых проектах и сталкиваться с проблемой. Многие текущие проекты по-прежнему используют исходные указатели в качестве параметров в своем интерфейсе и не имеют интерфейса для интеллектуальных указателей, например. QMainWindow:: setCentralWidget.
Чтобы сохранить тип согласованным, я должен передать сохраненный указатель из get()
, как этот сегмент:
QMainWindow win;
std::shared_ptr<QWidget> scrollArea{ std::make_shared<QScrollArea>() };
// QScrollArea is a derived class of QWidget.
win.setCentralWidget(scrollArea.get());
Но я не могу убедиться, что другие методы в Qt выполняют оператор delete
на сохраненном указателе scrollArea
.
Может ли это вызвать утечку памяти или другие проблемы, если некоторые методы в Qt делают это?
Я проверил последний С++ Standard CD и ничего не нашел. Кажется, это поведение undefined.
Если это поведение undefined и опасно, существует ли безопасный способ использования интеллектуальных указателей (ов) с интерфейсом для необработанных указателей?
Ответы
Ответ 1
В общем случае такого пути нет. Для каждого "старого" интерфейса, который вы хотите использовать, вы должны прочитать его документацию, чтобы узнать, как он взаимодействует с собственностью (это то, что инкапсулирует std
интеллектуальные указатели). Единственный объект может управляться только одной схемой собственности.
В частности, Qt, безусловно, небезопасно смешивать интеллектуальные указатели и управление Qt. Роль/дочерние отношения Qt между QObject
включает семантику собственности (дети удаляются, когда их родитель), поэтому вы не можете смело смешивать это с любой другой схемой собственности (например, std
умные указатели).
Обратите внимание, что ссылки Qt, на которые вы ссылаетесь, явно указываете, что "QMainWindow
получает право собственности на указатель виджета и удаляет его в соответствующее время".
Ответ 2
К сожалению, если вы используете интерфейс, который использует необработанные указатели, вам нужно будет проконсультироваться с документацией, чтобы определить, принадлежит ли этот метод или не принадлежит владельцу предоставленного указателя.
Если функция переходит в собственность, вы должны вызвать .release()
, чтобы передать право собственности на эту функцию. Если функция не переходит в собственность, вы должны передать объект с помощью .get()
.
Ответ 3
Может ли это вызвать утечку памяти или другие проблемы, если некоторые методы в Qt делают это?
Это не приведет к утечке памяти, так как память после этого будет выпущена. Однако, так как QT и shared_ptr
будут вызывать delete
в этой памяти, вы, вероятно, получите некоторое повреждение кучи (UB в целом).
существует ли безопасный способ использования интеллектуальных указателей (ов) с интерфейсом для необработанных указателей (ов)?
Конечно. Не иметь несвязанных сущностей, управляющих одной и той же памятью. Для этого рекомендуется использовать unique_ptr
вместо shared_ptr
, когда это возможно. С помощью unique_ptr
вы можете вызвать .release()
, чтобы освободить память от элемента управления smartpointer, тем самым предоставив вам возможность управлять QT.
Конечно, вам нужно проверить документацию, чтобы увидеть, когда вам нужно самостоятельно управлять памятью, и когда QT сделает это за вас.
Ответ 4
Я не думаю, что вы должны удалять QWidget.
http://qt-project.org/doc/qt-4.8/qmainwindow.html#setCentralWidget
Примечание. QMainWindow берет на себя ответственность за указатель виджета и удаляет его в соответствующее время.
Если вам нужно использовать интеллектуальные указатели, вы можете использовать weak_ptr, который не будет им владеть или уничтожать.
Ответ 5
Если вы используете интерфейс, который принимает необработанные указатели, у вас уже есть проблема, что вы должны знать, кто несет ответственность за время жизни этих указателей.
Добавление shared_ptr в микс не меняет этого.
Если интерфейс, возможно, удалит объект, вы не сможете безопасно использовать std::shared_ptr
. std::shared_ptr
должен контролировать время жизни своих объектов, и нет никакого пути вокруг этого (без добавления другого уровня косвенности)
Однако вы можете использовать некоторые из std::unique_ptr
. Если интерфейс не удаляет указатель, вы можете безопасно пройти в ptr.get()
. Если интерфейс принимает на себя ответственность за время жизни этого объекта, пройдите в ptr.release()
, и вы сами откажетесь контролировать время жизни.
Всю вы можете получить полезность из интеллектуальных указателей даже с устаревшей кодовой базой, но вы должны быть осторожны.
Ответ 6
Но я не могу проверить, удаляются ли другие методы в операторе выполнения Qt на сохраненном указателе scrollArea.
Если у виджета есть родительский элемент, тогда управление памятью QT освободит этот объект. В этом случае вы не должны использовать интеллектуальный указатель, потому что ваше приложение будет пытаться выпустить его дважды, и это поведение undefined.