13b3ebbe14
把川大 wiki「02.Qt方向」课程代码整理为 CMake 管理的可编译运行项目。
Part1 C/C++ 强化 (p01/, 144 目标, host gcc/g++ 构建, 全绿):
- 修复全部编译失败:补 <cstring>/<fstream>/<iomanip>/<vector> 等缺失头
(common_fix + infer_stl_includes 自动检测注入)、修正 pause() 壳注入
(改检测已转换后的 pause 调用)、namespace 提升出函数体、const 成员函数
正确性、dedupe 不再破坏函数重载集、K&R 隐式 int / 动态异常规范等
C++17 移除特性的可编译等价改写。
- 目录采用深缩写 p01/ch04/s03/ (原 part1_cpp/ch04_class_object/03_ctor_dtor)。
Part3 Qt 桌面 (p03/, 29 目标, 隔离 Qt 5.14.2 构建, 全绿):
- gen_part3.py: Qt 外壳模板 (main+QApplication+show, QTimer 自动退出便于
offscreen 验证)、AUTOMOC + .moc include、rewrite_* 处理散文混入/重载/
资源依赖、生成占位 png/gif + .qrc + rundata 样例数据。
- 修正 QLable 笔误、全角分号、.→->、Windows 路径、QApplication 阻塞事件
循环改为 QCoreApplication 等。
任务1 概念/版本验证:
- tools/std_probe.py: 跨 -std= 编译探针 (--pedantic-errors 让已废除特性硬失败)。
- tools/demo_versions/: K&R 隐式 int、三目左值、void* 转换、enum 赋整、
const 经指针、动态异常规范 共 6 个跨版本演示。
- docs/VERSION_NOTES.md: 每条「原文说法→C17/C++17 是否成立→何版本成立→已验证」。
文档: 顶层 README、docs/SOURCE_PAGES.md (页面→目录映射)、
docs/ERRATA.md (Part1 修正)、docs/ERRATA_part3.md (Part3 修正)。
另修复 tools/run.sh / run_qt.sh 的 ${1:?...} 引号语法 bug。
隔离 Qt 验证: ldd 0 系统 Qt 引用; 173/173 目标全绿; p03 offscreen 运行通过。
1 line
9.4 KiB
JSON
1 line
9.4 KiB
JSON
{"id":"58954525","type":"page","status":"current","title":"3 第一个Qt小程序","version":{"by":{"type":"known","username":"ethanliu","userKey":"2c90931b6cfb654f016d5c69becc0001","profilePicture":{"path":"/images/icons/profilepics/default.svg","width":48,"height":48,"isDefault":true},"displayName":"刘浩","_links":{"self":"https://wiki.suncaper.net/rest/api/user?key=2c90931b6cfb654f016d5c69becc0001"},"_expandable":{"status":""}},"when":"2022-07-09T01:51:25.000Z","message":"","number":1,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954525/version/1"},"_expandable":{"content":"/rest/api/content/58954525"}},"body":{"storage":{"value":"<h1><ac:structured-macro ac:name=\"anchor\" ac:schema-version=\"1\" ac:macro-id=\"74670046-5cec-4252-8448-10045d3870e2\"><ac:parameter ac:name=\"\">_Toc471841995</ac:parameter></ac:structured-macro>3.1 按钮的创建</h1><p>在Qt程序中,最常用的控件之一就是按钮了,首先我们来看下如何创建一个按钮</p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"7b38173f-fb72-477f-91b3-3f3c291e7e43\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[QPushButton * btn = new QPushButton; 头文件 #include <QPushButton>\n\n //设置父亲\n btn->setParent(this);\n //设置文字\n btn->setText(\"德玛西亚\");\n //移动位置\n btn->move(100,100);\n\n //第二种创建\n QPushButton * btn2 = new QPushButton(\"孙悟空\",this);\n //重新指定窗口大小\n this->resize(600,400);\n\n //设置窗口标题\n this->setWindowTitle(\"第一个项目\");\n\n //限制窗口大小\n this->setFixedSize(600,400);\n\n]]></ac:plain-text-body></ac:structured-macro><p><br class=\"atl-forced-newline\" />上面代码中,一个按钮其实就是一个<span style=\"color: rgb(128,0,128);\">QPushButton</span>类下的对象,如果只是创建出对象,是无法显示到窗口中的,所以我们需要依赖一个父窗口,也就是指定一个父亲利用setParent函数即可,如果想设置按钮上显示的文字利用setText,移动按钮位置用move<br />对于窗口而言,我们可以修改左上角窗口的标题setWindowTitle,重新指定窗口大小:resize,或者设置固定的窗口大小setFixedSize;</p><h1><ac:structured-macro ac:name=\"anchor\" ac:schema-version=\"1\" ac:macro-id=\"0d6f0c3a-835f-47af-99cb-0e1f281a81f5\"><ac:parameter ac:name=\"\">_Toc471841996</ac:parameter></ac:structured-macro>3.2<ac:structured-macro ac:name=\"anchor\" ac:schema-version=\"1\" ac:macro-id=\"b62ffcf5-6cd2-4a9b-891b-373745cbe342\"><ac:parameter ac:name=\"\">_Toc466556776</ac:parameter></ac:structured-macro> 对象模型(对象树)</h1><p>在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。</p><ul><li>QObject是以对象树的形式组织起来的。<ul><li>当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。</li></ul></li></ul><p>这相当于,<span style=\"color: rgb(255,0,0);\"><strong>在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。</strong></span></p><ul><li><ul><li><span style=\"color: rgb(255,0,0);\"><strong>当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)</strong></span></li></ul></li></ul><p>这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。</p><ul><li>QWidget是能够在屏幕上显示的一切组件的父类。<ul><li><span style=\"color: rgb(255,0,0);\"><strong>QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件</strong></span>。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。</li><li>当然,<span style=\"color: rgb(255,0,0);\"><strong>我们也可以自己删除子对象,它们会自动从其父对象列表中删除。</strong></span>比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。</li></ul></li></ul><p>Qt 引入对象树的概念,在一定程度上解决了内存问题。</p><ul><li>当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。</li><li>任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。</li></ul><p>如果QObject在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:</p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"392f2981-2274-4cc9-8421-05704391405f\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[{\n QWidget window;\n QPushButton quit(\"Quit\", &window);\n}\n\n]]></ac:plain-text-body></ac:structured-macro><p><br />作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,<strong>局部对象的析构顺序应该按照其创建顺序的相反过程</strong>。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。<br />但是,如果我们使用下面的代码:</p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"5fbc2b0a-f09c-49df-9804-b82bc3e269e9\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[{\n QPushButton quit(\"Quit\");\n QWidget window;\n quit.setParent(&window);\n}\n\n]]></ac:plain-text-body></ac:structured-macro><p><br />情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。<br />由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。</p><h1><ac:structured-macro ac:name=\"anchor\" ac:schema-version=\"1\" ac:macro-id=\"3a418358-d5ef-418d-a50b-dcf28acd21d9\"><ac:parameter ac:name=\"\">_Toc471841997</ac:parameter></ac:structured-macro>3.3<ac:structured-macro ac:name=\"anchor\" ac:schema-version=\"1\" ac:macro-id=\"5334eb26-31e4-4ca7-9b84-6c1aa77dd0ee\"><ac:parameter ac:name=\"\">_Toc466556773</ac:parameter></ac:structured-macro> Qt窗口坐标体系</h1><p><ac:structured-macro ac:name=\"anchor\" ac:schema-version=\"1\" ac:macro-id=\"a2c8f308-83d8-4750-9b8b-8c01068b1df5\"><ac:parameter ac:name=\"\">_Toc466556774</ac:parameter></ac:structured-macro>坐标体系:<br />以左上角为原点(0,0),X向右增加,Y向下增加。<br /><ac:image ac:height=\"243\" ac:width=\"349\"><ri:attachment ri:filename=\"worddavc788bf9a24622102b4528a77878aa30c.png\" /></ac:image><br />对于嵌套窗口,其坐标是<span style=\"color: rgb(255,0,0);\"><strong>相对于父窗口</strong></span>来说的。</p>","representation":"storage","_expandable":{"content":"/rest/api/content/58954525"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":19},"_links":{"webui":"/pages/viewpage.action?pageId=58954525","edit":"/pages/resumedraft.action?draftId=58954525","tinyui":"/x/HZODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954525"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954525/child","restrictions":"/rest/api/content/58954525/restriction/byOperation","history":"/rest/api/content/58954525/history","ancestors":"","descendants":"/rest/api/content/58954525/descendant","space":"/rest/api/space/2CT"}} |