我有“老式”C++编程的经验(也就是说,我关心指针和内存管理)。不过,我确实想利用现代概念。
由于我的应用程序大量使用了Qt,所以我想使用Qt的智能指针。不过,我对一般的智能指针以及它们在Qt中的使用有些困惑。
1.)据我所知,如果我是从QObject
派生出来的,我应该坚持Qt的对象树和所有权模型,忘记智能指针。对,是这样?
2.)在C++中,我可以与std::shared_ptr
和std::unique_ptr
一起度过难关。Qt中的等效智能指针是什么?
假设我有以下代码:
QList<MyObject *> * foobar(MyOtherObject *ptr) {
// do some stuff with MyOtherObject
QList<MyObject* > ls = new QList<MyObject*>();
for(int i=0;i<10;i++) {
MyObject* mi = new MyObject();
...
ls.insert(mi);
}
return ls;
}
int main() {
MyOtherObject* foo = new MyOtherObject();
QList<MyObject*> *bar = foobar(foo);
// do stuff
// and don't care about cleaning up?!
}
3.)如何使用智能指针将上述片段转换为版本?
4.)特别是:我是否应该将函数签名更改为使用智能指针?它似乎创建了相当复杂的类型签名(返回类型和传递的参数)。另外,如果某些“遗留”函数调用另一个函数--用原始指针编写函数签名,只使用“内部”函数,会更好吗?
5.)在函数ls
中,什么智能指针应该取代foobar
?应该用于mi
的指针类型是什么,即存储在QList
中的对象
发布于 2018-04-09 08:34:49
您几乎不得不使用Qt
的成语,即拥有GUI对象的原始指针,因为QWidget
派生类型将假定子元素的所有权。
在其他地方,您应该尽可能避免使用任何类型的指针。大多数情况下,您可以传递一个引用。如果您需要多态所有权,那么使用std::unique_ptr
。在非常罕见的情况下,您有多个独立的生命周期,需要对共享资源拥有所有权,为此您将使用std::shared_ptr
。
Qt集合类还与现代C++结构交互不良。
extern QList<Foo> getFoos();
for (const Foo & foo : getFoos())
{ /*foo is a dangling reference here*/ }
for (const Foo & foo : std::as_const(getFoos()))
{ /*This is safe*/ }
你的片段会是
std::vector<std::unique_ptr<MyObject>> foobar(MyOtherObject & obj) {
// do some stuff with MyOtherObject
std::vector<std::unique_ptr<MyObject>> ls;
for(int i=0;i<10;i++)
{
ls.emplace_back(std::make_unique<MyObject>());
...
}
return ls;
}
int main() {
MyOtherObject foo = MyOtherObject;
auto bar = foobar(foo);
// do stuff
// destructors do the cleanup automatically
}
发布于 2018-04-09 19:17:22
首先,现代C++允许您使用值,Qt支持这一点。因此,默认情况下应该使用QObject
,就像它们是不可移动的、不可复制的值一样。实际上,您的代码片段根本不需要显式内存管理。这与对象有一个父对象这一事实完全没有冲突。
#include <QObject>
#include <list>
using MyObject = QObject;
using MyOtherObject = QObject;
std::list<MyObject> makeObjects(MyOtherObject *other, QObject *parent = {}) {
std::list<MyObject> list;
for (int i = 0; i < 10; ++i) {
#if __cplusplus >= 201703L // C++17 allows more concise code
auto &obj = list.emplace_back(parent);
#else
auto &obj = (list.emplace_back(parent), list.back());
#endif
//...
}
return list;
}
int main() {
MyOtherObject other;
auto objects = makeObjects(&other, &other);
//...
objects.erase(objects.begin()); // the container manages lifetimes
//
}
C++有严格的对象销毁顺序,objects
保证在other
之前销毁。因此,当other.~QObject()
运行时,没有MyObject
子级,因此双重删除没有问题。
通常,以下是存储QObject
集合的可行方法及其需求:
std::array
-不能返回相同类型、固定大小的所有元素。std::list
-所有相同类型的元素,没有RandomAccessIterator
,容器拥有对象std::deque
-所有相同类型的元素都有RandomAccessIterator
,但不允许erase
,因为对象不是MoveAssignable
(当然可以清除/销毁),容器拥有对象std::vector<std::unique_ptr<BaseClass>>
-任何类型的元素,即它是一个多态容器,容器拥有对象。std::vector<QObject*>
或QObjectList
-不拥有,非跟踪容器。Qt代码中充满了QObjectList = QList<QObject*>
。QObject
(sic!) -任何QObject
类型的元素,容器可选择地拥有对象,容器跟踪对象的生存期,元素指针可用于从容器中删除对象;只有一个容器可以容纳给定的对象,使用裸向量存储对象,因此子对象的添加/删除是O(N)
。当存储对象本身,而不是它们的集合时,如果对象的生存期与包含的作用域相同,则最容易将它们保持为值。例如:
class ButtonGrid : public QWidget {
static constexpr int const N = 3;
QGridLayout m_gridLayout{this};
QLabel m_label;
std::array<QPushButton, N*N> m_buttons;
public:
ButtonGrid(QWidget *parent = {}) : QWidget{parent} {
int r = 0, c = 0;
m_gridLayout.addWidget(&m_label, r, c, 1, N);
r ++;
for (auto &b : m_buttons) {
m_gridLayout.addWidget(&b, r, c);
c ++;
if (c == N)
c = 0, r ++;
}
}
};
现在,回答你的问题:
QObject
所有权模型只确保子代的寿命不会超过父级。它可以防止资源泄漏。你可以在父母去世之前结束孩子们的生命。你也可以自由地拥有没有父母的东西。std::shared_ptr
和std::unique_ptr
。使用Qt的等价物没有好处,因为Qt5.7:从那个版本开始,Qt需要编译器中的C++11支持,因此必须支持这些指针。发布于 2018-04-09 08:35:30
1.)据我所知,如果我是从QObject派生出来的,我应该坚持Qt的对象树和所有权模型,忘记智能指针。对,是这样?
是。在使用QObjects
时,我建议使用它的父-子模型来管理内存。它工作得很好,而且你不能完全避免它,所以请使用它。
2.)在C++中,我可以与std::shared_ptr和std::unique_ptr一起度过难关。Qt中的等效智能指针是什么?
有QSharedPointer
和QScopedPointer
,它们有点类似于unique_ptr
,但不支持移动语义。现在没有理由使用这些工具,只需使用标准库提供的智能指针(用于管理不从QObject
派生的对象的生存期)。
4.)特别是:我是否应该将函数签名更改为使用智能指针?它似乎创建了相当复杂的类型签名(返回类型和传递的参数)。另外,如果某些“遗留”函数调用另一个函数--用原始指针编写函数签名,只使用“内部”函数,会更好吗?
通常-只使用智能指针来管理内存=只有在存在所有权关系时才使用它们。如果您只是将一个实例传递给一个使用它但不接受所有权的函数,那么只传递一个普通的旧指针。
关于你的例子,没有上下文是有点难说的。ls
和mi
都可能是unique_ptr
。更好的是,您可以只在堆栈上分配ls
。更好的是,避免使用QList
和std::vector
之类的东西。
https://stackoverflow.com/questions/49661213
复制相似问题