Qt 在QObject派生类中实例化QWidget派生类

Qt 在QObject派生类中实例化QWidget派生类,qt,memory-management,parent-child,Qt,Memory Management,Parent Child,由于某些原因,如果您试图将QObject派生类作为rparent传递给QWidget派生类,则Qt编译器不会编译 在QObject派生类中为QWidget派生类提供父类的正确方法是什么?我正在考虑以下解决方案: 在QWidget类中使用智能指针,而不是给对象一个父对象。 从QWidget派生而不是QObject在我看来是错误的,因为类不是一个小部件。 将QWidget实例传递给已经有父对象的QObject derviced类,正如我在下面的示例中尝试演示的那样: 如果将QSharedPointe

由于某些原因,如果您试图将QObject派生类作为rparent传递给QWidget派生类,则Qt编译器不会编译

在QObject派生类中为QWidget派生类提供父类的正确方法是什么?我正在考虑以下解决方案:

在QWidget类中使用智能指针,而不是给对象一个父对象。 从QWidget派生而不是QObject在我看来是错误的,因为类不是一个小部件。 将QWidget实例传递给已经有父对象的QObject derviced类,正如我在下面的示例中尝试演示的那样: 如果将QSharedPointer与ErrorMsgDialog一起使用,此测试将崩溃。
有什么建议吗?也许我建议的解决方案不是最好的实践,

< P>动态创建对象的正确存储和生存管理在C++中并不容易。经过长时间的努力,我开始喜欢某些我仍然使用得相当成功的技术:

由作用域管理的局部变量存储和生命周期 非指针成员变量 具有明确所有权共享指针或非所有权弱指针的智能指针。 这样,我几乎完全禁止了源代码中的原始指针,从而使代码维护更方便,调试更少烦人

当然,当外部库(如widget集)发挥作用时,我会绑定到它们的api

关于gtkmm 2.4,上述技术也非常有效。gtkmm提供

可共享对象的智能指针 与子窗口小部件相关的窗口小部件的某种所有权。 当我切换到Qt时,我看到了教程示例中所有的新闻和原始指针,这让我有点害怕。经过一些实验后,我得出结论,我将能够编写功能完整的Qt应用程序,就像我以前在gtkmm中所做的那样——几乎不需要任何新的东西,再次将小部件定义为局部变量,例如直接或间接从QWidget派生的其他类的主变量或成员变量

除此之外,随着时间的推移,我意识到了Qt的一般概念,但我必须承认,我在日常业务中很少依赖它

关于OP的具体问题:

QWidget是从QObject派生的。因此,QoObject的通常所有权原则适用。此外,一个QWidget需要另一个QWidget作为父对象。QWidget可以是任何QObject的父对象,但反之亦然

因此,我建议以下所有权:

主窗口是DataReadController的父窗口 MainWindow是在DataReadController中创建的ErrorMsgDialog的父级。 DataReadController将指向ErrorMsgDialog的指针存储为原始指针。我相信QSharedPointer提供的所有权会与MainWindow的所有权发生冲突

如上所述,在我自己的编程中,我会尝试阻止指针,而使用非指针成员变量。然而,这是一个风格和个人喜好的问题

OP testQParentship.cc的修改示例:

在Windows 10上编译并测试:

$qmake-qt5 testQParentship.pro $make&./testQParentship g++-c-fno保持内联dllexport-D_GNU_SOURCE-pipe-O2-Wall-W-D_REENTRANT-DQT_NO_DEBUG-DQT_WIDGETS-LIB-DQT_GUI_-LIB-DQT_CORE_-LIB-I.-isystem/usr/include/qt5-isystem/usr/include/qt5/QtWidgets-isystem/usr/include/qt5/QtGui-isystem/usr/include/QtGui-isystem/usr/include/QtGui/qtr/isystem/usr/include/include/qtr/qtr/qtr/qtr/qtr g++-o testQParentship.exe testQParentship.o-lQt5Widgets-lQt5Gui-lQt5Core-lGL-lpthread Qt版本:5.9.4 主窗口中的QTimer::singleshot到期后,主窗口关闭。diagnostics的输出说明,当操作系统释放进程内存时,对象树被正确地破坏,而不是丢弃

winMain.close winMain.~main窗口 winMain.\u pCtrlReadData.~DataReadCtrl winMain.\u pCtrlReadData.\u pDlgErrorMsg.~ErrorMsgDlg HBoxLayout.~HBoxLayout 标签。~标签
QWidget需要一个QWidget*作为父对象。虽然QWidget本身是从QObject派生的,但这并没有改变第一个要求。在从QObject派生的类中有一个指向QWidget的指针并没有错。然而,关于所有权,一个QWidget应该属于另一个QWidget,这就是父权制的实际含义。关于您的ErrorMsgDialog,MainWindow w将是一个足够的父窗口。这并不意味着DataReadController不能添加/删除这些实例,或者只是显示/隐藏。@Scheff谢谢。我不确定我是否理解了你最后的两句话。您的意思是“MainWindow w将是一个足够的父窗口”吗?我的示例是为“ErrorMsgDialog”提供父窗口的好方法,因为传递的小部件“parentWidget”是MainWindow w的子窗口?我将向DataReadController添加一个信号,该信号将以消息作为参数发出错误,并将此信号连接到主窗口中显示消息的插槽 在一个对话框中。想想一些Qt类是如何工作的,例如QSerialPort。您有一个类似ErrorOccessed的信号,当您想要显示错误消息时,可以连接到该信号。它本身不显示任何对话框。你应该遵循类似的设计。
#include <QApplication>
#include <QtWidgets>

class ErrorMsgDialog;
class Controller;
class MainWindow;

// This is the QWidget class used in the QObject derived class (DataReadController)
class ErrorMsgDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ErrorMsgDialog(QWidget *parent = nullptr)
        : QDialog(parent)
    {
        errorLbl = new QLabel("An unknown read error occured!");
        QHBoxLayout* layout = new QHBoxLayout;
        layout->addWidget(errorLbl);
        setLayout(layout);
        setWindowTitle("Error!");
        setGeometry(250, 250, 250, 100);
    }
    ~ErrorMsgDialog() { qDebug() << "~ErrorMsgDialog() destructed"; }

private:
    QLabel* errorLbl;
};

// QObject derived class - I want to instatiate Qwidget derived classes here, with this class as parent
class DataReadController
    : public QObject
{
    Q_OBJECT
public:
    DataReadController(QWidget* pw, QObject *parent = nullptr)
        : QObject(parent)
    {
        m_errorMsgDialog = new ErrorMsgDialog(pw);
        //m_errorMsgDialog = QSharedPointer<ErrorMsgDialog>(m_errorMsgDialog);
        //m_dataReader = new DataReader(this); m_dataReader->moveToThread(m_dataReaderThread); connect(....etc

        //simulate that DataReader emits an error msg
        QTimer::singleShot(2000, [&]() {
            onErrorTriggered();
        });
    }

public slots:
    void onNewDataArrived() {}

    // called if reader emits an error message
    void onErrorTriggered() { m_errorMsgDialog->show(); }
private:
    ErrorMsgDialog* m_errorMsgDialog;
    //QSharedPointer<ErrorMsgDialog> m_errorMsgDialog;
    //DataReader* m_dataReader;// DataReader is not shown here, it would be moved to a different thread and provide some data
};

// MainWindow
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        parentWidget = new QWidget(this);   
        m_dataReadController = new DataReadController(parentWidget, this);
        setGeometry(200, 200, 640, 480);

        //Close after 5 seconds.
        QTimer::singleShot(5000, [&]() {
            close();
        });
    }

private:
    QWidget* parentWidget; // QWidget to pass to OBject derive class for parenting QWidgets
    DataReadController* m_dataReadController;
};

// Main
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

#include "main.moc"
#include <QtWidgets>

class Label: public QLabel {
  private:
    const QString _name;
  public:
    Label(const QString &name, const QString &text):
      QLabel(text),
      _name(name)
    { }

    virtual ~Label()
    {
      qDebug() << _name + ".~Label()";
    }
};

class HBoxLayout: public QHBoxLayout {
  private:
    const QString _name;
  public:
    HBoxLayout(const QString &name):
      QHBoxLayout(),
      _name(name)
    { }

    virtual ~HBoxLayout()
    {
      qDebug() << _name + ".~HBoxLayout()";
    }
};

class ErrorMsgDlg: public QDialog {
  private:
    const QString _name;
  public:
    ErrorMsgDlg(const QString &name, QWidget *pQParent):
      QDialog(pQParent),
      _name(name)
    {
      QHBoxLayout *pQBox = new HBoxLayout("HBoxLayout");
      pQBox->addWidget(
        new Label("Label", "An unknown read error occured!"));
      setLayout(pQBox);
      setWindowTitle("Error!");
    }

    virtual ~ErrorMsgDlg()
    {
      qDebug() << _name + ".~ErrorMsgDlg()";
    }
};

class DataReadCtrl: public QObject {
  private:
    const QString _name;
    ErrorMsgDlg *const _pDlgErrorMsg;

  public:
    DataReadCtrl(const QString &name, QWidget *pQParent):
      QObject(pQParent),
      _name(name),
      _pDlgErrorMsg(
        new ErrorMsgDlg(name + "._pDlgErrorMsg", pQParent))
    {
      //simulate that DataReader emits an error msg
      QTimer::singleShot(2000, [&]() {
        onErrorTriggered();
      });
    }

    virtual ~DataReadCtrl()
    {
      qDebug() << _name + ".~DataReadCtrl()";
    }

    void onErrorTriggered()
    {
      _pDlgErrorMsg->show();
    }
};

class MainWindow: public QMainWindow {
  private:
    const QString _name;
    DataReadCtrl *_pCtrlReadData;

  public:
    MainWindow(const char *name):
      QMainWindow(),
      _name(name),
      _pCtrlReadData(nullptr)
    {
      _pCtrlReadData
        = new DataReadCtrl(_name + "._pCtrlReadData", this);
      //Close after 5 seconds.
      QTimer::singleShot(5000, [&]() {
        qDebug() << _name + ".close()";
        close();
      });
    }

    virtual ~MainWindow()
    {
      qDebug() << _name + ".~MainWindow()";
    }
};

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup GUI
  MainWindow winMain("winMain");
  winMain.show();
  // runtime loop
  return app.exec();
}