【问题标题】:Deletion of objects send by signals, Ownership of objects in signals, Qt删除信号发送的对象,信号中对象的所有权,Qt
【发布时间】:2011-03-12 03:52:00
【问题描述】:

这里,我的信号声明:

signals:
    void mySignal(MyClass *);

以及我是如何使用它的:

MyClass *myObject=new myClass();
emit mySignal(myObject);

我的问题来了:谁负责删除myObject:

  1. 发送方代码,如果在使用 myObject 之前删除了怎么办?悬空指针

  2. 连接信号的槽,如果连接信号的槽没有槽或有多个槽怎么办?内存泄漏或悬空指针

Qt 如何在其内置信号中处理这种情况?它使用内部引用计数吗?

您的最佳做法是什么?

【问题讨论】:

    标签: c++ qt signals-slots qt-signals


    【解决方案1】:

    一句话(好吧,函数名)- deleteLater() :) 所有 QObjects 都有它。它会将对象标记为删除,然后在下一次事件循环更新时发生。

    【讨论】:

      【解决方案2】:

      第一个问题,请使用QPointer

      关于第二个问题,

      如果我理解清楚,即使您发送myObject,您在发出信号的类中仍然有参考 myObject。那么它会是内存泄漏还是悬空指针呢?您仍然可以从发出的类中访问myObject,不是吗?

      希望很清楚..

      编辑:

      从您的 cmets 中,我相信您正在释放/删除插槽中的对象。现在我假设您的问题是,如果(内存释放)插槽被调用一次、两次或根本不调用怎么办。

      您可以为此使用 QPointer。从 Qt 文档中,

      保护指针 (QPointer) 在您需要存储指向其他人拥有的 QObject 的指针时很有用,因此在您仍然持有对它的引用时可能会被销毁。您可以安全地测试指针的有效性。

      Qt 文档本身的示例,

           QPointer<QLabel> label = new QLabel;
           label->setText("&Status:");
           ...
           if (label)
               label->show();
      

      解释是这样的..

      如果同时删除 QLabel,标签变量将保存 0 而不是无效地址,并且最后一行将永远不会执行。 这里 QLabel 将是您的MyClass 和标签是你的myObject。在使用它之前检查 Nullity

      【讨论】:

      • 内存泄漏:当 MyClass *myObject 超出范围并且没有连接插槽时。悬空指针:其中一个槽删除对象,并且有多个槽。
      【解决方案3】:

      您可以根据需要将信号与任意数量的插槽连接,因此您应该确保这些插槽中没有一个能够执行您不希望它们对您的对象执行的操作:

      • 如果您决定将指针作为参数传递,那么您将遇到您所描述的问题,即内存管理 - 这里没有人可以为您工作,因为您必须制定处理分配/删除的策略。有关如何解决此问题的一些想法,请参阅COM 世界中的内存管理规则。
      • 如果您决定将参数作为引用传递,那么您不必担心内存管理,而只需担心插槽会以意想不到的方式修改您的对象。除非必须,否则不要传递指针,而是尽可能使用引用。
      • 如果您决定传递 const 引用,那么根据您的连接类型,QT 将为您传递对象的值(有关详细信息,请参阅 this
      • 避免任何问题并按值传递:)

      有关在信号中传递指针的一些想法,另请参阅question

      【讨论】:

        【解决方案4】:

        在 1):发件人应该小心。当同步发送信号(而不是排队)时,当接收器接收到它时,对象仍然是活动的。如果接收者需要存储它,那么只有 QPointer 会有所帮助,但是 MyClass 需要从 QObject 派生,从上下文中看起来是错误的。 无论如何,这是一个普遍的生命周期问题,不是非常特定于信号/插槽。

        替代方案:使用值类并通过 const 引用发送它。如果 MyClass 可以有子类,则传递一个 const QSharedPointer&

        关于 deleteLater:deleteLater() 在这里没有帮助。这将使排队连接更安全,而对于直接连接则没有区别。 deleteLater() 发挥作用的一种用途是接收者是否需要删除发送者。然后应该总是使用 deleteLater(),这样发送者就可以完成他正在做的事情,否则会崩溃。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多