gpt4 book ai didi

c++ - Qt 中的内部类

转载 作者:搜寻专家 更新时间:2023-10-31 00:11:54 26 4
gpt4 key购买 nike

我正在使用带有 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com