首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Qt:如何使用Qt的智能指针

Qt:如何使用Qt的智能指针
EN

Stack Overflow用户
提问于 2018-04-04 22:11:25
回答 4查看 2.3K关注 0票数 6

我有“老式”C++编程的经验(也就是说,我关心指针和内存管理)。不过,我确实想利用现代概念。

由于我的应用程序大量使用了Qt,所以我想使用Qt的智能指针。不过,我对一般的智能指针以及它们在Qt中的使用有些困惑。

1.)据我所知,如果我是从QObject派生出来的,我应该坚持Qt的对象树和所有权模型,忘记智能指针。对,是这样?

2.)在C++中,我可以与std::shared_ptrstd::unique_ptr一起度过难关。Qt中的等效智能指针是什么?

假设我有以下代码:

代码语言:javascript
运行
复制
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中的对象

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-04-09 08:34:49

您几乎不得不使用Qt的成语,即拥有GUI对象的原始指针,因为QWidget派生类型将假定子元素的所有权。

在其他地方,您应该尽可能避免使用任何类型的指针。大多数情况下,您可以传递一个引用。如果您需要多态所有权,那么使用std::unique_ptr。在非常罕见的情况下,您有多个独立的生命周期,需要对共享资源拥有所有权,为此您将使用std::shared_ptr

Qt集合类还与现代C++结构交互不良。

代码语言:javascript
运行
复制
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*/ }

你的片段会是

代码语言:javascript
运行
复制
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
}
票数 3
EN

Stack Overflow用户

发布于 2018-04-09 19:17:22

首先,现代C++允许您使用值,Qt支持这一点。因此,默认情况下应该使用QObject,就像它们是不可移动的、不可复制的值一样。实际上,您的代码片段根本不需要显式内存管理。这与对象有一个父对象这一事实完全没有冲突。

代码语言:javascript
运行
复制
#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集合的可行方法及其需求:

  1. std::array -不能返回相同类型、固定大小的所有元素。
  2. std::list -所有相同类型的元素,没有RandomAccessIterator,容器拥有对象
  3. std::deque -所有相同类型的元素都有RandomAccessIterator,但不允许erase,因为对象不是MoveAssignable (当然可以清除/销毁),容器拥有对象
  4. std::vector<std::unique_ptr<BaseClass>> -任何类型的元素,即它是一个多态容器,容器拥有对象。
  5. std::vector<QObject*>QObjectList -不拥有,非跟踪容器。Qt代码中充满了QObjectList = QList<QObject*>
  6. QObject (sic!) -任何QObject类型的元素,容器可选择地拥有对象,容器跟踪对象的生存期,元素指针可用于从容器中删除对象;只有一个容器可以容纳给定的对象,使用裸向量存储对象,因此子对象的添加/删除是O(N)

当存储对象本身,而不是它们的集合时,如果对象的生存期与包含的作用域相同,则最容易将它们保持为值。例如:

代码语言:javascript
运行
复制
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 ++;
     }
  }
};

现在,回答你的问题:

  1. 我应该坚持Qt的对象树和所有权模型吗?在这件事上没有选择:那个模型在那里,它不能被禁用。但它被设计成万无一失,你可以先发制人。QObject所有权模型只确保子代的寿命不会超过父级。它可以防止资源泄漏。你可以在父母去世之前结束孩子们的生命。你也可以自由地拥有没有父母的东西。
  2. Qt中的等效智能指针是什么?无所谓。你在写C++ --使用std::shared_ptrstd::unique_ptr。使用Qt的等价物没有好处,因为Qt5.7:从那个版本开始,Qt需要编译器中的C++11支持,因此必须支持这些指针。
  3. 如何使用智能指针将上述片段转换为版本?没必要这么做。按价值保存对象。也许您需要一个不同的片段,实际上需要使用智能指针。
  4. 我是否应该将函数签名更改为使用智能指针?不是的。您没有激发任何智能指针的使用。
  5. 不适用
票数 3
EN

Stack Overflow用户

发布于 2018-04-09 08:35:30

1.)据我所知,如果我是从QObject派生出来的,我应该坚持Qt的对象树和所有权模型,忘记智能指针。对,是这样?

是。在使用QObjects时,我建议使用它的父-子模型来管理内存。它工作得很好,而且你不能完全避免它,所以请使用它。

2.)在C++中,我可以与std::shared_ptr和std::unique_ptr一起度过难关。Qt中的等效智能指针是什么?

QSharedPointerQScopedPointer,它们有点类似于unique_ptr,但不支持移动语义。现在没有理由使用这些工具,只需使用标准库提供的智能指针(用于管理不从QObject派生的对象的生存期)。

4.)特别是:我是否应该将函数签名更改为使用智能指针?它似乎创建了相当复杂的类型签名(返回类型和传递的参数)。另外,如果某些“遗留”函数调用另一个函数--用原始指针编写函数签名,只使用“内部”函数,会更好吗?

通常-只使用智能指针来管理内存=只有在存在所有权关系时才使用它们。如果您只是将一个实例传递给一个使用它但不接受所有权的函数,那么只传递一个普通的旧指针。

关于你的例子,没有上下文是有点难说的。lsmi都可能是unique_ptr。更好的是,您可以只在堆栈上分配ls。更好的是,避免使用QListstd::vector之类的东西。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49661213

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档