c++ - Qt 中的内部类

标签 c++ qt inner-classes nested-class

我正在使用带有 VS2013 编译器的 Qt5。我试图将两个类 CUDialDUDial 嵌套在同一个外部类 UDial 中。

UDial 是一个QDialog 类型的类。我希望 CUDialDUDial 都是 QWidget 类型。它与外部类中的 QTabWidget 变量一起使用。

所以,我写的代码如下:

class UDial : public QDialog
{
    Q_OBJECT

    class CUDial : public QWidget
    {
        Q_OBJECT

        // Some variables

     public:

        CUDial(QWidget *parent = 0);
    }wid1;

   class DUDial : public QWidget
   {
        Q_OBJECT

        // Some variables

    public:

        DUDial(QWidget *parent = 0);
   }wid2;

   QTabWidget *tab;
   QDialogButtonBox *box;
   QVBoxLayout *vlay;

public:

   UDial(QWidget *parent = 0);
};

在我实现代码后,我尝试编译并得到以下C2664错误:

error: C2664: 'int QTabWidget::addTab(QWidget *,const QIcon &,const QString &)' : cannot convert argument 1 from 'UDial::CUDial' to 'QWidget *'

No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

我猜这是我的嵌套类的 QWidget 继承的问题。

有什么办法可以解决这个问题吗?

最佳答案

有两个问题:

  1. 一个微不足道的问题:您的代码中其他地方缺少对象取消引用 - 错别字。

  2. 元对象编译器 (moc) 不支持嵌套类。您的代码将编译,但不会通过 moc 步骤,并出现以下错误:

    Error: Meta object features not supported for nested classes

我确实赞扬您没有过早地悲观wid1wid2 的存储。您按值存储它们 - 正如您应该的那样。不过,理想情况下,您应该按值存储所有此类对象——而不仅仅是两个子表盘。它还将使内部类的实例化与其声明分离,使事情更容易阅读。

因此,需要将子表盘类型移出类。您可以将它们放在命名空间中,这样它们就不会污染全局命名空间。

namespace UDial_ {
class CUDial : public QWidget {
   Q_OBJECT
public:
   CUDial(QWidget *parent = 0) : QWidget(parent) {}
};

class DUDial : public QWidget
{
   Q_OBJECT
public:
   DUDial(QWidget *parent = 0) : QWidget(parent) {}
};
}

其次,一旦您确实按值存储对象,就必须在父对象之后声明子对象。这是因为编译器会生成代码以声明的相反顺序销毁它们。子 QObject 必须在父对象之前销毁,否则父对象会错误地尝试删除它们。

我不知道有什么方法可以在不更改 Qt 源代码的情况下强制编译器执行此操作,但您至少可以使用 C++11 value initialization 来表明您的意图。 .

class UDial : public QDialog
{
   Q_OBJECT
   QVBoxLayout vlay { this }
   QTabWidget tab;
   // We make it explicit that both sub-dials are children of tab and must not
   // be declared / initialized before it!
   UDial_::CUDial wid1 { &tab };
   UDial_::DUDial wid2 { &tab };
   QDialogButtonBox box;

public:
   UDial(QWidget *parent = 0);
};

不幸的是,如果我们做错了事,编译器充其量只会发出警告,当然,任何体面的静态分析器都会大声提示。

class UDial : public QDialog
{
   Q_OBJECT
   QVBoxLayout vlay { this };
   UDial_::CUDial wid1 { &tab };
   UDial_::DUDial wid2 { &tab };
   QTabWidget tab; // WRONG, comes after the use above
   QDialogButtonBox box;

public:
   UDial(QWidget *parent = 0);
};

在大多数情况下,这段代码会在启动时崩溃,但这不是我们可以依赖的崩溃——比如说,就好像我们取消引用了一个nullptr。但要注意:编译器可以自由优化 nullptr 的任何取消引用!

构造函数看起来类似于:

UDial::UDial(QWidget * parent) : 
  QDialog(parent),
  box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
{
  vlay.addWidget(&tab);
  vlay.addWidget(&box);
  tab.addTab(&wid1, "Dial 1");
  tab.addTab(&wid2, "Dial 2");
}

如果您使用的是不支持 C++11 值初始化的旧版编译器,则您不能使用大括号初始化语法,并且您必须在构造函数中声明您的意图 - 它更有可能是被不知情的维护者忽略:

UDial::UDial(QWidget * parent) : 
  QDialog(parent),
  vlay(this),
  wid1(&tab), wid2(&tab),
  box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
{ ...

BartoszKP 有一个有效的论点,因为不能可靠地将此检查委托(delegate)给编译时间,所以在堆上显式分配所有子对象并让 Qt 在运行时处理正确的销毁顺序有一些优点。虽然我个人没有遇到任何与此相关的问题,但我非常勤奋并且处于让我和我自己维护我的代码库的特权位置。在没有适当的过程文档来管理以指出此类过程细节的大型项目中,这种方法可能会导致错误。恕我直言,此类错误更多地表明开发过程中存在程序问题,而不是方法的有效性。然后,解决开发过程的缺陷就变成了过早的悲观情绪。由您决定这是否是一个有效的权衡。当我看到 new 分配给原始指针的结果时,我个人感到畏缩 - 它往往会使人们对这种不正确的使用不敏感,因为您没有像 QObject< 这样的良好的内存管理类.

关于c++ - Qt 中的内部类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32126809/

相关文章:

c++ - 别名(或类型定义)参数化类的内部类

java - 与外部类同名的内部类?

c++ - boost asio tcp - 套接字写入数据不同于缓冲区中的数据 - 某处可能存在线程不安全

c++ - 如果我们有基*类,如何访问派生模板类的成员函数

c++ - 根据正则表达式从 QT (C++) 中的 SQLite 数据库中检索记录

c++ - Qt 中的项目未在 TreeView 中排序

c++ - 如何为模板类的 const ref 成员定义 move 赋值运算符

c++ - 如何在 C++ 中编写一个接受可变数量的 char 数组参数的函数?

c++ - 左填充在 QListWidget 中不起作用

java - 在java中的外部类之外创建内部类的实例