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
10 KiB
JSON
1 line
10 KiB
JSON
{"id":"58954456","type":"page","status":"current","title":"04.C++面向对象模型初探","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:24.000Z","message":"","number":1,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954456/version/1"},"_expandable":{"content":"/rest/api/content/58954456"}},"body":{"storage":{"value":"<ac:layout><ac:layout-section ac:type=\"two_right_sidebar\"><ac:layout-cell><h2><span>4.4 C++</span>面向对象模型初探</h2><h3><span>4.4.1 </span>成员变量和函数的存储</h3><p>在<span>c</span>语言中,“分开来声明的,也就是说,语言本身并没有支持“数据”和“函数”之间的关联性我们把这种程序方法称为“程序性的”,由一组“分布在各个以功能为导航的函数中”的算法驱动,它们处理的是共同的外部数据。</p><p><span> </span></p><p>c++实现了“封装”,那么数据<span>(</span>成员属性<span>)</span>和操作<span>(</span>成员函数<span>)</span>是什么样的呢?</p><p>“数据”和“处理数据的操作<span>(</span>函数<span>)</span>”是分开存储的。</p><ul><li>c++中的<strong>非静态数据成员</strong>直接内含在类对象中,就像<span>c struct</span>一样。</li><li>成员函数<span>(member function)</span>虽然内含在<span>class</span>声明之内,却不出现在对象中。</li><li>每一个非内联成员函数<span>(non-inline member function)</span>只会诞生一份函数实例<span>.</span></li></ul><p><br /></p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"78bef3f0-a75f-4cc8-9b5e-9cf5db57006a\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[class MyClass01{\npublic:\n\tint mA;\n};\n\nclass MyClass02{\npublic:\n\tint mA;\n\tstatic int sB;\n};\n\nclass MyClass03{\npublic:\n\tvoid printMyClass(){\n\t\tcout << \"hello world!\" << endl;\n\t}\npublic:\n\tint mA;\n\tstatic int sB;\n};\n\nclass MyClass04{\npublic:\n\tvoid printMyClass(){\n\t\tcout << \"hello world!\" << endl;\n\t}\n\tstatic void ShowMyClass(){\n\t\tcout << \"hello world!\" << endl;\n\t}\npublic:\n\tint mA;\n\tstatic int sB;\n};\n\nint main(){\n\n\tMyClass01 mclass01;\n\tMyClass02 mclass02;\n\tMyClass03 mclass03;\n\tMyClass04 mclass04;\n\n\tcout << \"MyClass01:\" << sizeof(mclass01) << endl; //4\n\t//静态数据成员并不保存在类对象中\n\tcout << \"MyClass02:\" << sizeof(mclass02) << endl; //4\n\t//非静态成员函数不保存在类对象中\n\tcout << \"MyClass03:\" << sizeof(mclass03) << endl; //4\n\t//静态成员函数也不保存在类对象中\n\tcout << \"MyClass04:\" << sizeof(mclass04) << endl; //4\n\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><h3><span>4.4.2 this</span>指针</h3><h4><span>4.4.2.1 this</span>指针工作原理</h4><p>通过上例我们知道,<span>c++</span>的数据和操作也是分开存储,并且每一个非内联成员函数<span>(non-inline member function)</span>只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码</p><p>那么问题是:这一块代码是如何区分那个对象调用自己的呢?</p><p>c++通过提供特殊的对象指针,<span>this</span>指针,解决上述问题。<span>this</span>指针指向被调用的成员函数所属的对象。</p><p> <span> c++</span>规定,<span>this</span>指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针<span>this</span>,用以保存这个对象的地址,也就是说虽然我们没有写上<span>this</span>指针,编译器在编译的时候也是会加上的。因此<span>this</span>也称为“指向本对象的指针”,<span>this</span>指针并不是对象的一部分,不会影响<span>sizeof(</span>对象<span>)</span>的结果。</p><p> <span>this</span>指针是<span>C++</span>实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每一个对象都拥有自己的函数成员。一般情况下,并不写<span>this</span>,而是让系统进行默认设置。</p><p><strong>this指针永远指向当前对象</strong>。</p><p>成员函数通过<span>this</span>指针即可知道操作的是那个对象的数据。This指针是一种隐含指针,它隐含于每个类的非静态成员函数中。<span>This</span>指针无需定义,直接使用即可。</p><table><colgroup><col /></colgroup><tbody><tr><td><p>注意:静态成员函数内部没有<span>this</span>指针,静态成员函数不能操作非静态成员变量。</p></td></tr></tbody></table><h4><span>4.4.2.2 this</span>指针的使用</h4><ul><li>当形参和成员变量同名时,可用<span>this</span>指针来区分</li><li>在类的非静态成员函数中返回对象本身,可使用<span>return *this.</span></li></ul><p><br /></p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"a92483f9-9a9b-4828-bda3-e6101d3448ce\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[class Person{\npublic:\n\t//1. 当形参名和成员变量名一样时,this指针可用来区分\n\tPerson(string name,int age){\n\t\t//name = name;\n\t\t//age = age; //输出错误\n\t\tthis->name = name;\n\t\tthis->age = age;\n\t}\n\t//2. 返回对象本身的引用\n\t//重载赋值操作符\n\t//其实也是两个参数,其中隐藏了一个this指针\n\tPerson PersonPlusPerson(Person& person){\n\t\tstring newname = this->name + person.name;\n\t\tint newage = this->age + person.age;\n\t\tPerson newperson(newname, newage);\n\t\treturn newperson;\n\t}\n\tvoid ShowPerson(){\n\t\tcout << \"Name:\" << name << \" Age:\" << age << endl;\n\t}\npublic:\n\tstring name;\n\tint age;\n};\n\n//3. 成员函数和全局函数(Perosn对象相加)\nPerson PersonPlusPerson(Person& p1,Person& p2){\n\tstring newname = p1.name + p2.name;\n\tint newage = p1.age + p2.age;\n\tPerson newperson(newname,newage);\n\treturn newperson;\n}\n\nint main(){\n\n\tPerson person(\"John\",100);\n\tperson.ShowPerson();\n\n\tcout << \"---------\" << endl;\n\tPerson person1(\"John\",20);\n\tPerson person2(\"001\", 10);\n\t//1.全局函数实现两个对象相加\n\tPerson person3 = PersonPlusPerson(person1, person2);\n\tperson1.ShowPerson();\n\tperson2.ShowPerson();\n\tperson3.ShowPerson();\n\t//2. 成员函数实现两个对象相加\n\tPerson person4 = person1.PersonPlusPerson(person2);\n\tperson4.ShowPerson();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><h4><span>4.4.2.3 const</span>修饰成员函数</h4><ul><li>用<span>const</span>修饰的成员函数时,const修饰<span>this</span>指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量</li><li>当成员变量类型符前用<span>mutable</span><span>修饰时例外。</span></li></ul><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"12aee074-d30f-4c5b-87b8-d4ba3434c335\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[//const修饰成员函数\nclass Person{\npublic:\n\tPerson(){\n\t\tthis->mAge = 0;\n\t\tthis->mID = 0;\n\t}\n\t//在函数括号后面加上const,修饰成员变量不可修改,除了mutable变量\n\tvoid sonmeOperate() const{\n\t\t//this->mAge = 200; //mAge不可修改\n\t\tthis->mID = 10;\n\t}\n\tvoid ShowPerson(){\n\t\tcout << \"ID:\" << mID << \" mAge:\" << mAge << endl;\n\t}\nprivate:\n\tint mAge;\n\tmutable int mID;\n};\n\nint main(){\n\n\tPerson person;\n\tperson.sonmeOperate();\n\tperson.ShowPerson();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><h4><span>4.4.2.4 const</span>修饰对象<span>(</span>常对象<span>)</span></h4><ul><li>常对象只能调用<span>const</span>的成员函数</li><li>常对象可访问<span> const </span>或非<span> const </span>数据成员,不能修改,除非成员用<span>mutable</span>修饰</li></ul><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"48a15323-9302-4b63-8e3f-3c3beaa1a140\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[class Person{\npublic:\n\tPerson(){\n\t\tthis->mAge = 0;\n\t\tthis->mID = 0;\n\t}\n\tvoid ChangePerson() const{\n\t\tmAge = 100;\n\t\tmID = 100;\n\t}\n\tvoid ShowPerson(){\n this->mAge = 1000;\n\t\tcout << \"ID:\" << this->mID << \" Age:\" << this->mAge << endl;\n\t}\n\npublic:\n\tint mAge;\n\tmutable int mID;\n};\n\nvoid test(){\t\n\tconst Person person;\n\t//1. 可访问数据成员\n\tcout << \"Age:\" << person.mAge << endl;\n\t//person.mAge = 300; //不可修改\n\tperson.mID = 1001; //但是可以修改mutable修饰的成员变量\n\t//2. 只能访问const修饰的函数\n\t//person.ShowPerson();\n\tperson.ChangePerson();\n}]]></ac:plain-text-body></ac:structured-macro></ac:layout-cell><ac:layout-cell><p><br /></p></ac:layout-cell></ac:layout-section></ac:layout>","representation":"storage","_expandable":{"content":"/rest/api/content/58954456"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954456","edit":"/pages/resumedraft.action?draftId=58954456","tinyui":"/x/2JKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954456"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954456/child","restrictions":"/rest/api/content/58954456/restriction/byOperation","history":"/rest/api/content/58954456/history","ancestors":"","descendants":"/rest/api/content/58954456/descendant","space":"/rest/api/space/2CT"}} |