如何让Qt删除QObjects?

4
据我理解,以下代码是创建QObject的完美方法。
QLabel *label = new QLabel("label");
QWidget window;
label->setParent(&window);
window.show();

我正在阅读到 "父对象拥有新创建的对象并最终调用delete" 或者 "复合对象拥有子对象,所以只要完成了父子关系,你就可以确保当父对象销毁时,子QObject也会被销毁"(来自如何删除Qt对象?最佳的存储QObjects方法是什么?)。请问有人能告诉我Qt如何"拥有"一个QObject吗?更准确地说:对于Qt(它是一个库,有自己的运行时),如何对我用不同运行时创建的指针调用operator delete?为什么不会崩溃?
编辑:
我加上这个参考资料,因为整个问题的重点就在这里:
"运行在DLL中的代码可能正在使用不同的C++运行时库,这意味着堆的布局将是不同的。DLL可能在完全不同的堆上运行。
在主程序上调用delete一个由DLL分配的指针(反之亦然)将导致(在最好情况下)立即崩溃或(在最坏情况下)内存损坏,需要一段时间来跟踪。" (来自C++在不同库之间混合使用new/delete?)

4
你可能过度努力了。:-) Qt 代码将执行 delete label;,就像你的代码一样可以执行。没有区别,也没有魔法涉及。 - Bo Persson
但是Qt的delete运算符与我的不同,因为库会有自己的new/delete运算符,不应混合使用。在DLL中运行的代码可能使用不同的C++运行时库,这意味着堆的布局将不同。DLL可能会使用完全不同的堆。 - user815129
问题的新部分涉及其他内容。如果您随意混合DLL,例如一些Debug模式和一些Release模式,或者使用不同的编译器,那么您肯定会遇到问题。但是,如果所有内容都使用相同的编译器和设置进行编译,则所有内容将使用相同的语言运行时。然后就不会有问题了。 - Bo Persson
你必须使用与你正在使用的编译器相同的Qt工具包,因此(至少在Windows上),我们有不同的工具包代表相同版本的Qt,但是使用不同的编译器编译... - Mike
我曾经错误地认为,即使使用相同的编译器、版本或配置,也可能存在多个堆管理。谢谢。 - user815129
2个回答

4

让源代码说话,而不是简单的回答。这里是QObject内部的操作:

for (int i = 0; i < children.count(); ++i) {
    currentChildBeingDeleted = children.at(i);
    children[i] = 0;
    delete currentChildBeingDeleted;
}
children.clear();

上面代码中的函数在析构函数中被调用。父类存储其所有子类的指针。当调用父类的析构函数时,它会删除其所有子类。这是递归的,因为调用对象上的delete会调用其析构函数。
为什么Qt可以安全地删除而不用考虑运行库?
实际上,由于名称重整,导出C++接口几乎强制您使用相同版本的编译器和运行库。这在非常粗略的方式上允许Qt假定delete是安全的。

1
我更新了问题以使其更清晰。谢谢。 - user815129
有人能解释一下这行代码children[i] = 0;是做什么的吗?这和children[i] = nullptr;是一样的吗?如果已经调用了children.clear(),为什么还需要这行代码呢? - Dan

1

针对Qt(它是一个库并具有自己的运行时)

你的假设是错误的。首先,操作系统中不存在“运行时”。 “运行时”是一个信息性术语。

现代操作系统有像:二进制可执行文件、共享库(也称为动态链接库或DLL)、静态库、进程、线程等等。

在这种情况下,您的可执行文件和Qt共享库被加载到同一个进程中,这意味着它们之间共享内存。这意味着您的可执行文件可以看到Qt内存,反之亦然。


我更新了问题以使其更清晰。谢谢。 - user815129
2
@user815129 对于 C 接口来说,这是正确的。但是,如果通过 C++ 创建接口,则必须具有相同的运行时,因为名称修饰可能会因同一编译器的不同版本而有所不同。 - bartop
你考虑过你的代码和 Qt 使用相同的标准库的情况吗? - dimm
由于我没有从源代码编译qt,也没有包含qt源代码,所以我认为我不能确定...当然,我的一些假设是错误的,因为给出的示例是绝对正确的。 - user815129

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接