commit 13b3ebbe14e33a3406e4e1dffb5d1899cbc1fd15 Author: 张宗平 Date: Tue Jun 30 14:16:55 2026 +0800 课程代码仓库初始化:Part1 (144) + Part3 (29) 全量生成与验证 把川大 wiki「02.Qt方向」课程代码整理为 CMake 管理的可编译运行项目。 Part1 C/C++ 强化 (p01/, 144 目标, host gcc/g++ 构建, 全绿): - 修复全部编译失败:补 /// 等缺失头 (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 运行通过。 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..301690f --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Build outputs +build/ +build-*/ +out/ +*.o +*.obj + +# Qt / moc artifacts (should not normally live in-source) +moc_*.cpp +*.moc +qrc_*.cpp +ui_*.h + +# Tooling caches +.qt/ +.cache/ + +# Editor / OS +.vscode/ +.idea/ +*.swp +.DS_Store + +# Isolated Qt 5.14.2 install (built via aqtinstall; ~577MB, not in git) +.qt514/ +.venv-aqt/ + +# Runtime artifacts produced when running Qt examples locally +# (qpicture_io writes drawing.pic; filesystem examples write file.dat/.txt/in.txt) +/drawing.pic +/file.dat +/file.txt +/in.txt +/person.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7a31108 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +# ============================================================================== +# 四川大学 C++/LinuxC — 「02. Qt 方向」课程代码仓库 +# +# 顶层项目管理。两个 part 相互独立、相对隔离: +# * p01 —— C/C++ 强化(控制台程序,host gcc/g++ 即可构建) +# * p03 —— Qt 桌面应用开发(需隔离的 Qt 5.14.2 工具链) +# +# 目录按「章节 + 话题」组织,采用深缩写:part 缩写 pNN(顶层不编号)/ +# 章缩写 chNN / 节缩写 sNN。例如 p01/ch04/s03/ 。 +# 每个可执行示例 = 一个独立 add_executable 目标,可单独 +# cmake --build build --target 构建运行。 +# ------------------------------------------------------------------------------ +cmake_minimum_required(VERSION 3.16) +project(qt_course LANGUAGES C CXX) + +# --- 标准:默认 C17 / C++17(任务 1 的基线),严格、不允许编译器扩展 ----------- +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) +endif() + +# 教学示例:警告要明显但不阻断(-Wall -Wextra 默认开,特定目标再额外收紧) +add_compile_options(-Wall -Wextra) + +# 默认导出编译命令,便于编辑器/探针脚本复用 +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(BUILD_QT_PART "Build the Qt desktop examples (needs isolated Qt 5.14.2)" OFF) + +# --- Part 1: C/C++ 强化(host 构建) -------------------------------------------- +add_subdirectory(p01) + +# --- Part 3: Qt 桌面应用开发(仅当显式开启且找到隔离 Qt 时构建) ---------------- +if(BUILD_QT_PART) + # 找到的 Qt 版本必须严格隔离为 5.14.x;否则中止,避免误用系统 5.15。 + find_package(Qt5 COMPONENTS Core Widgets Gui REQUIRED) + message(STATUS "Found Qt5 ${Qt5Core_VERSION_STRING} at ${Qt5Core_DIR}") + if(NOT Qt5Core_VERSION_STRING MATCHES "^5\\.14\\.") + message(FATAL_ERROR + "Refusing to build Qt examples against Qt ${Qt5Core_VERSION_STRING}: " + "this repo requires the ISOLATED Qt 5.14.x toolchain. " + "Run via tools/docker/docker_run.sh, or pass -DCMAKE_PREFIX_PATH=/path/to/qt/5.14.2/gcc_64.") + endif() + set(CMAKE_AUTOMOC ON) + set(CMAKE_AUTORCC ON) + set(CMAKE_AUTOUIC ON) + add_subdirectory(p03) +endif() diff --git a/HANDOFF.md b/HANDOFF.md new file mode 100644 index 0000000..0ed3767 --- /dev/null +++ b/HANDOFF.md @@ -0,0 +1,211 @@ +# HANDOFF — qt-course 课程代码仓库生成/验证 续接手册 + +> 本手册供「新开会话」从当前进度继续。读完即可无缝接手。 +> 仓库路径:`/home/charles/workspaces/qt-course` +> 上次工作会话日期:2026-06-30 + +--- + +## 0. 任务总目标(来自用户原始需求) + +从川大 wiki「02. Qt 方向」(需登录 scuc/scuc2025) 提取 QT 方向页面,完成 3 件事: + +0. **按章节+话题组织课程代码仓库**,用 CMake 项目管理,每个可执行文件 = 一个独立 `add_executable` 目标;为 Qt 示例准备一套**与主系统隔离**的 Qt 5.14.x 开发环境;示例之间相对隔离。 +1. **验证所有示例代码**在原文所述语言(C 或 C++,先按 C17/C++17)下仍能编译通过、概念仍符合当前标准;不符的确认在何版本成立并编译验证;提供「快速演示不同版本对概念支持」的机制。 +2. **修正示例代码错误**并演示。 + +用户后续追加的关键指令: +- 源文件名用**英文**,与章节名匹配;同小节多文件用语义后缀区分。 +- 目录约定:`02 Qt方向`为顶层(不编号);`01 C++技术强化及提升`等视为「章」;其下编号子项视为「节」。 +- 目录用**深缩写**:`p01/ch04/s01/` 这种形式。(**尚未应用**,见 §5) +- 容器方案用「构建容器」:容器编译、宿主机运行;或带 GUI 的容器。 + +--- + +## 1. 当前完成度(截至上次会话末) + +| 项 | 状态 | +|---|---| +| 抓取解析 35 个 wiki 页面、218 个代码块 | ✅ 完成 | +| 仓库骨架(顶层 CMakeLists、.gitignore、tools/、docs/) | ✅ 完成 | +| **隔离 Qt 5.14.2 安装**(`.qt514/`,aqtinstall 装在仓库内) | ✅ 完成 | +| 验证隔离有效:`ldd` 0 系统 Qt 引用、offscreen 运行打印 5.14.2 | ✅ 完成 | +| Docker 构建/安装/运行脚本(构建容器模型) | ✅ 完成(轻量 builder 镜像已构建) | +| Part1 C/C++ 源码 + CMakeLists 生成器 | ✅ 可运行(`tools/gen_part1.py`) | +| Part1 英文文件名映射表(`tools/file_name_map.py`) | ✅ 完成,113 节全覆盖 | +| Part1 全部编译通过 | ⚠️ **未完成**:144 个目标,剩 4 个 FAILED | +| Part3 Qt 源码/CMakeLists/.qrc 生成 | ❌ **未开始** | +| Qt 示例构建运行验证 | ❌ 未开始 | +| `std_probe.py` + 多版本演示 + VERSION_NOTES.md | ❌ 未开始 | +| 各章 README、SOURCE_PAGES.md、最终 clean build、git 提交 | ❌ 未开始 | + +**git 状态**:仓库已 `git init`,但**尚未任何提交**(0 个 tracked 文件,所有内容 untracked)。 + +--- + +## 2. 仓库当前布局 + +``` +qt-course/ +├── CMakeLists.txt # 顶层:C17/C++17 严格、option(BUILD_QT_PART)、隔离 Qt 版本断言 +├── .gitignore # 排除 build/ .qt514/ .venv-aqt/ 等 +├── tools/ +│ ├── gen_sources.py # 共享引擎:标题解析 page_outline()、语言判定 classify_lang()、common_fix() +│ ├── gen_part1.py # Part1 生成器(含 exact_fix、block_classify、wrap_statements、emit_console) +│ ├── file_name_map.py # (pageId, section_title) → 英文文件名词根(113 条,全覆盖) +│ ├── run.sh / run_qt.sh # 宿主机运行 part1 / Qt 目标(LD_LIBRARY_PATH 隔离) +│ ├── install_qt_host.sh # 宿主机直装 Qt 5.14.2 到 .qt514/(venv+apt) +│ ├── docker/ # Dockerfile + docker_build_image/install_qt/build.sh +│ └── wiki_data/ # 抓取的 wiki 正文+代码块(已拷入,生成器输入) +├── docs/ +│ └── ERRATA.md # 已记录 30 处修正(自动生成) +├── part1_cpp/ # 已生成 144 个目标(17 个 dir) +└── part3_qt_desktop/ # 空目录,待生成 +``` + +**Part1 当前目录形**(待用户要求改为深缩写,见 §5): +`part1_cpp/ch04_class_object/03_ctor_dtor/ctor_and_dtor.cpp` 等。 + +--- + +## 3. 已验证可用的关键命令 + +```bash +cd /home/charles/workspaces/qt-course + +# 重新生成 Part1 全部源码 + CMakeLists(幂等;每次改生成器后先 rm -rf part1_cpp) +rm -rf part1_cpp build && python3 tools/gen_part1.py + +# 在宿主机配置+构建 Part1(无需 Qt) +cmake -S . -B build -G Ninja -DBUILD_QT_PART=OFF -DCMAKE_BUILD_TYPE=Debug +cmake --build build + +# 构建/运行 Qt 部分(需先装隔离 Qt;见下) +cmake -S . -B build -G Ninja -DBUILD_QT_PART=ON \ + -DCMAKE_PREFIX_PATH=$PWD/.qt514/5.14.2/gcc_64 -DCMAKE_BUILD_TYPE=Debug +cmake --build build +LD_LIBRARY_PATH=$PWD/.qt514/5.14.2/gcc_64/lib ./tools/run_qt.sh # 或直接 ./tools/run_qt.sh + +# 装/重装隔离 Qt 5.14.2(宿主机,一次性) +tools/install_qt_host.sh +# 或用容器装:tools/docker/docker_install_qt.sh(需先 tools/docker/docker_build_image.sh) +``` + +**隔离 Qt 核心事实**(已验证):靶机=gcc13+glibc2.39;容器与宿主机同栈;二进制容器编译宿主机运行;`.qt514` 是唯一真相源;`ldd` 0 系统 Qt 引用;`QT_QPA_PLATFORM=offscreen` 可无 X 运行验证。 + +--- + +## 4. Part1 剩余 4 个 FAILED 目标(接手第一件事) + +最近一次构建:`FAILED: 4 | Linked: 0`,剩余唯一错误类: + +| 失败目标 | 文件 | 根因 | +|---|---|---| +| p1c03_42 | part1_cpp/.../function_overload_basics_2.cpp | dedupe 把重载 `MyFunc` 改名 `MyFunc_2` 后,`MyFunc_2("hello")` 调用对 `MyFunc_2(int)` 和另一重载产生歧义(dedupe 重命名破坏了重载集) | +| p1c03_43 | part1_cpp/.../function_overload_basics.cpp | `namespace` 定义在函数体内(同 block 3/6/7 问题,需在 exact_fix 补 58954439:41/42 的 namespace 提升修正) | +| p1c04_03_01 | .../03_ctor_dtor/ctor_and_dtor.cpp | `strcpy` 未声明:C++ 用 iostream 前言但 wiki 块含 `strcpy`,需补 `` | +| p1c04_03_04 | .../03_ctor_dtor/deep_vs_shallow_copy.cpp | 同上:`strcpy`/`strlen` 未声明,补 `` | + +**建议修法**(在 `tools/gen_part1.py` 的 `exact_fix(pid,idx)` 里): +1. **`strcpy`/`strlen` 类**:对 ch04 块(pid 58954451 等),若 code 含 `strcpy`/`strlen`/`strcmp` 等而无 ``/``,在 `common_fix` 或 `exact_fix` 里补 `#include `(C++)或 ``(C)。建议加到 `gen_sources.py` 的 `common_fix` 通用补头(类似 NBSP 修复)。 +2. **namespace-in-function (block 41/42)**:仿照已写好的 block 3/6/7 的 `exact_fix`,把 `namespace` 定义提到文件作用域。 +3. **dedupe 破坏重载集 (block overload_basics_2)**:去重逻辑对**同一块内**的重载不应改名;当前 `dedupe_functions` 跨块去重是为了同名不同块变体。需让 dedupe 只对「同名且跨块」改名,块内多个同名重载视为同一组用序号区分——或更稳妥:函数重载演示块整体改名前先识别是重载集(同名不同参数),统一改名 `MyFunc`→`MyFunc_` 并同步调用点。已属复杂边界,可单独为 58954439:41/42 写 exact_fix 直接给可编译版本。 + +**每处修正都要 `record_erratum(...)`** 以自动进 `docs/ERRATA.md`(已有 30 条,结构见现有条目)。 + +修法后:`rm -rf part1_cpp build && python3 tools/gen_part1.py && cmake -S . -B build -G Ninja -DBUILD_QT_PART=OFF && cmake --build build` 应全绿(144 目标全 Linking executable)。然后即可进 Part3。 + +--- + +## 5. 待应用:深缩写目录 `p01/ch04/s01/`(用户明确要求,最后统一改) + +用户要求目录形如:`p01/ch04/s01/`(part 缩写 pNN / 章缩写 chNN / 节缩写 sNN)。 +当前是 `part1_cpp/ch04_class_object/03_ctor_dtor/`。 + +**改法**(在 `gen_part1.py` 的 `PAGE_CONFIG` 里把 `dir` 值统一改为深缩写;或在 `main()` 拼接 `out_dir` 处做映射): +- `part1_cpp` → `p01` +- `ch04_class_object` → `ch04` +- `01_basic_concepts` → `s01`,`02_oo_case` → `s02`,…… +- `ch03_cpp_extends_c` → `ch03`,等等。 +- 主顶层 `CMakeLists.txt` 的 `add_subdirectory(part1_cpp)` → `add_subdirectory(p01)`。 +- 同步更新 `tools/run.sh` / 任何写死 `part1_cpp` 的地方。 +- Part3 同理 `part3_qt_desktop` → `p03`。 + +**映射依据**:`PAGE_CONFIG` 里每个 pageId 的 `dir` 和 wiki 树结构 (`tools/wiki_data/qt_full_tree.json`),可脚本一次性生成 `p NN`/`ch NN`/`s NN` 编号。建议在生成目录的最后统一替换,不破坏 `file_name_map.py`(文件名映射与目录无关,仍按 (pageId, section))。 + +--- + +## 6. Part3 Qt 续接要点(接手第二件大事) + +**输入**:`tools/wiki_data/extracted_code.json` 中 9 个 Qt 页面 pageId(58954515–58954552),共 73 个代码块。 +**已勘察分类**(codex subagent 已做,结论在上次会话): +- 3 个完整程序(含 main+QApplication,直接成目标) +- 2 个 Q_OBJECT 类定义(需 moc → 开 AUTOMOC) +- 28 个可加 harness 块(包 main+QApplication+show,部分需补 Widget 类骨架) +- 40 个纯签名/单语句(折进各章 README 作文档,不单独建目标) + +**已知的 Qt 代码修正点**(subagent 已列): +- 笔误:`QLable`→`QLabel`、`.`→`->`、全角`;`→`;`、`"D:\ndrawing.pic"`→`"D:/drawing.pic"`、类后缺 `;` +- `tr()` 在非 QObject 上下文(main) 不可用 → `QObject::tr`/`QStringLiteral` +- 资源依赖:`:/images/*.png`、`:/Image/boat.jpg`、`:/Mario.gif`、`:/Image/butterfly*.png`、`in.txt`/`file.dat`/`file.txt` → 各自生成占位文件 + `.qrc`,CMake `qt5_add_resources()` +- 过时 API(Qt5.14 仍可用,保留并记 VERSION_NOTES):`QMouseEvent::x()/y()`、`QString::sprintf`、Qt4 `SIGNAL/SLOT`、`static_cast` 重载解析(Qt6 起弃用) + +**生成器骨架**:仿 `gen_part1.py` 写 `gen_part3.py`,复用 `gen_sources.py` 的 `page_outline`/`classify_lang`(已含 Qt 检测)/`common_fix`,但: +- emit 用 Qt 模板:`#include ` 等、`QApplication app(...)`、`w.show()`、`return app.exec()`,`QT_QPA_PLATFORM=offscreen` 时设 `QTimer::singleShot` 让程序自动退出。 +- 开 `AUTOMOC`(顶层 CMakeLists 已统一开,但建议在 part3 各目标的子 `CMakeLists` 写 `set(CMAKE_AUTOMOC ON)`)。 +- 资源目标:需 `.qrc` + 占位 png(用工具生成纯色小 png 即可,不依赖原图)。 +- 语言全是 C++(`.cpp`)。 +- 英文文件名映射表 `file_name_map.py` 里目前**只有 Part1**;Part3 需新增 pageId 58954515–58954552 的映射(约 16–25 个 section)。 + +**验证**:Docker 容器构建 → host offscreen 运行(`QT_QPA_PLATFORM=offscreen ./tools/run_qt.sh `),用 `QTimer` 自动退出、捕获 stdout/退出码确认。需 X 真窗时给 DISPLAY。 + +--- + +## 7. 任务1「概念/版本验证」+ 任务1机制(接手第三件) + +用户要「快速演示不同版本支持」。已规划: +- `tools/std_probe.py`:输入片段+标准列表(c89/c99/c11/c17/c23、c++11/14/17/20/23),逐个 `gcc/g++ -std=..` 编译,报 pass/fail+警告。host gcc13 原生支持全部。 +- `tools/demo_versions/`:针对 wiki 有版本论断的片段做跨版本对比:K&R 隐式 int、三目返左值(C++独有)、`const int` 改写差异、enum 赋整/`char* p=malloc` 不强转(C vs C++)。 +- 结果汇总进 `docs/VERSION_NOTES.md`:每条「文中说法→当前 C17/C++17 是否仍成立→不成立在何版本成立→已编译验证」。 + +**已知版本相关论断**(来自 Part1 已修的 exact_fix): +- K&R 隐式 int(块 58954439:14)→ C89 成立,C99 起非法 +- 三目运算符返左值(块 18)→ C++ 独有 +- `const int` 经指针改写 → C/C++ 行为差异 +- enum 赋整 / `char* p=malloc` 不强转(块 15)→ C 合法 C++ 报错 +- `system("pause")` 平台依赖(已在 common_fix 修为便携 pause()) + +--- + +## 8. 接手后立即应做的最小步骤 + +1. 读本手册 + `tools/gen_part1.py` + `tools/gen_sources.py` 了解生成器。 +2. 修 Part1 剩余 4 个 FAILED(§4):补 ``、namespace 提升 41/42、dedupe 重载集。重建到 144/144 全绿。 +3. 应用深缩写目录(§5):改 `PAGE_CONFIG` 的 dir 为 p/ch/s,全量重建确认路径。 +4. 写 `gen_part3.py` + 扩 `file_name_map.py`;生成 Part3;Docker/host 构建;offscreen 运行验证。 +5. 写 `std_probe.py` + `tools/demo_versions/`;填 `docs/VERSION_NOTES.md`。 +6. 写 `docs/SOURCE_PAGES.md`(pageId→章节映射表)、各章 README、顶层 README。 +7. 全量 clean build,`git add -A && git commit`(**目前 0 提交,务必最后提交一次**)。 + +--- + +## 9. 关键约束/红线 + +- **不修改** `tools/wiki_data/` 中的原始抓取数据(生成器输入,保持原貌)。 +- 每处对 wiki 代码的修正都要 `record_erratum()` 进 `docs/ERRATA.md`(透明可审计)。 +- 源文件名英文;CMake 目标名 ASCII(CMake 禁非 ASCII 目标名——已踩坑并修好)。 +- 隔离 Qt:构建/运行一律走 `.qt514/`(`-DCMAKE_PREFIX_PATH` + `LD_LIBRARY_PATH`),**不碰系统 Qt 5.15**;顶层 `CMakeLists.txt` 有版本断言(迷连系统 5.15 会 FATAL_ERROR)。 +- 不要对 wiki 数据做多块「合并」造符号冲突——已踩坑:跨块同名符号会重定义;最终采用「每块一文件」+ 必要时 `exact_fix` 补前向声明(如 `Person`)。 + +--- + +## 10. 上次会话犯过、勿重犯的坑 + +- `ERRATA` 是 `gen_sources.py` 模块级可变列表,`record_erratum` 追加到该列表;`gen_part1.py` 必须 `import gen_sources; ERRATA = gen_sources.ERRATA`(早期误用 `from ... import ERRATA` 独立拷贝导致 write_errata 看到空列表)。 +- `block_classify` 的 `decl_re` 必须**排除**裸调用 `printf(...);`(无返回类型前缀不算声明),否则语句片段被误判 groupdecl;且要**允许**单独 `{`/`}` 行(namespace 块边界),否则纯 namespace 声明块被误判 statements 进而被包进函数体内(namespace 不能在函数内)。 +- `dedupe_functions` 跨块改名会**破坏函数重载集**(块内多个同名重载不应逐个改名)。修 §4 时注意。 +- `common_fix` 已含:NBSP→空格、全角分号/引号→半角、`system("pause")`→`pause()`。再加:`strcpy/strlen` 等补 include 即可(勿重复已有逻辑)。 +- `ast.parse` 能过不代表运行正确:函数嵌套/作用域错误会跑到运行才炸。改生成器后务必实跑一遍 `python3 tools/gen_part1.py` 看是否抛异常。 +- grep `-c` 与 `-E` 混用会 `conflicting matchers`;本会话多次踩,请用单一 matcher 或 `python3 -c` 数。 + +(手册结) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..eea0e2e --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +# 川大 C++/LinuxC 课程「02. Qt 方向」代码仓库 + +本仓库把川大 wiki「02. Qt 方向」的课程代码整理成 **CMake 管理**的可编译运行的项目: +每个示例 = 一个独立 `add_executable` 目标,可单独构建运行。所有源码由生成器 +(`tools/gen_*.py`)从抓取的 wiki 代码块产出,对 wiki 原始代码的每一处修正都记录在 +`docs/ERRATA*.md`(透明可审计)。 + +> 标准基线:**C17 / C++17**(严格、不允许编译器扩展)。版本相关的概念另由 +> `tools/std_probe.py` + `tools/demo_versions/` 跨版本演示(见 `docs/VERSION_NOTES.md`)。 + +## 仓库结构 + +``` +qt-course/ +├── CMakeLists.txt 顶层:C17/C++17 严格;option(BUILD_QT_PART);隔离 Qt 版本断言 +├── p01/ Part 1:C/C++ 强化(控制台,host gcc/g++ 构建)144 个目标 +│ ├── ch02 … ch09/ 深缩写目录:p01/ch04/s03/ 这种形式 +├── p03/ Part 3:Qt 桌面(需隔离 Qt 5.14.2)29 个目标 +│ ├── ch02 … ch11/ 含 .qrc 资源、assets 占位图、rundata 样例数据 +├── tools/ +│ ├── gen_sources.py 共享引擎:标题解析 page_outline()、语言判定、common_fix() +│ ├── gen_part1.py Part1 生成器(exact_fix / dedupe / emit_console) +│ ├── gen_part3.py Part3 生成器(Qt 外壳 / rewrite / AUTOMOC / .qrc) +│ ├── file_name_map.py (pageId, section) → 英文文件名词根(全覆盖) +│ ├── std_probe.py 跨 -std= 编译探针(任务 1 机制) +│ ├── demo_versions/ 版本相关论断的最小演示片段 +│ ├── run.sh / run_qt.sh 宿主机运行 p01 / p03 目标(Qt 走 LD_LIBRARY_PATH 隔离) +│ ├── install_qt_host.sh 宿主机直装隔离 Qt 5.14.2 到 .qt514/ +│ ├── docker/ 构建容器模型(Dockerfile + 脚本) +│ └── wiki_data/ 抓取的 wiki 正文+代码块(生成器输入,只读) +├── docs/ +│ ├── ERRATA.md Part1 代码修正记录(自动生成) +│ ├── ERRATA_part3.md Part3 Qt 代码修正记录(自动生成) +│ ├── VERSION_NOTES.md 版本相关概念验证记录 +│ └── SOURCE_PAGES.md wiki 页面 → 目录映射表 +└── .qt514/ 隔离的 Qt 5.14.2 工具链(aqtinstall 装在仓库内,gitignore) +``` + +## 快速开始 + +### Part 1(C/C++ 强化,无需 Qt) + +```bash +cmake -S . -B build -G Ninja -DBUILD_QT_PART=OFF -DCMAKE_BUILD_TYPE=Debug +cmake --build build # 144 个目标全绿 +tools/run.sh # 例:tools/run.sh p1c04_03_01 +cmake --build build --target all_p01_ch04_s03 # 只构建某章 +``` + +### Part 3(Qt 桌面,需隔离 Qt 5.14.2) + +```bash +# 一次性:装隔离 Qt 5.14.2(装到 .qt514/,不碰系统 Qt 5.15) +tools/install_qt_host.sh +# 或用容器:tools/docker/docker_build_image.sh && tools/docker/docker_install_qt.sh + +cmake -S . -B build -G Ninja -DBUILD_QT_PART=ON \ + -DCMAKE_PREFIX_PATH=$PWD/.qt514/5.14.2/gcc_64 -DCMAKE_BUILD_TYPE=Debug +cmake --build build # 144 + 29 = 173 个目标全绿 + +# 运行(隔离 Qt,靠 LD_LIBRARY_PATH): +tools/run_qt.sh # 例:tools/run_qt.sh qpainter_basic +# 离屏/自动化验证(无 X 也可): +QT_QPA_PLATFORM=offscreen tools/run_qt.sh +``` + +> **隔离验证**:`ldd build/p03/.../` 显示所有 Qt 库只解析到 `.qt514/`, +> 零系统 Qt 引用。顶层 `CMakeLists.txt` 有版本断言——误连系统 Qt 5.15 会 `FATAL_ERROR`。 +> 每个 Qt GUI 目标注入 `QTimer::singleShot` 让程序 200ms 后自动退出,便于离屏验证。 + +## 目录命名(深缩写) + +按「part / 章 / 节」三级缩写: + +| 含义 | 形式 | 例 | +|---|---|---| +| part(不编号) | `pNN` | `p01`=Part1,`p03`=Part3 | +| 章 | `chNN` | `ch04`=第 4 章 类和对象 | +| 节 | `sNN` | `s03`=第 3 节 对象的构造和析构 | + +完整映射见 `docs/SOURCE_PAGES.md`。源文件名为英文蛇形(见 `tools/file_name_map.py`)。 + +## 重新生成全部源码(幂等) + +改了生成器后,先清空再重生成: + +```bash +rm -rf p01 p03 build && python3 tools/gen_part1.py && python3 tools/gen_part3.py +``` + +## 版本概念演示(任务 1) + +```bash +# 把一段代码在多个 -std= 下编译,看「某概念在哪个版本成立」 +tools/std_probe.py --lang c --file tools/demo_versions/kr_implicit_int.c \ + --standards c89,c99,c11,c17 --pedantic-errors + +# 结论汇总见 docs/VERSION_NOTES.md(含 K&R 隐式 int、三目左值、void* 转换、 +# enum 赋整、动态异常规范 等跨版本对照) +``` + +## 关键约束(红线) + +- **不修改** `tools/wiki_data/` 中的原始抓取数据(生成器只读输入)。 +- 每处对 wiki 代码的修正都 `record_erratum()` 进 `docs/ERRATA*.md`(透明可审计)。 +- 隔离 Qt:构建/运行一律走 `.qt514/`,**不碰系统 Qt 5.15**。 +- 每个示例相对隔离:一文件一目标;不跨块合并造符号冲突。 + +详细续接说明见 `HANDOFF.md`。 diff --git a/docs/ERRATA.md b/docs/ERRATA.md new file mode 100644 index 0000000..de7dac5 --- /dev/null +++ b/docs/ERRATA.md @@ -0,0 +1,437 @@ +# ERRATA — 代码修正记录 + +本文件由 `tools/gen_part1.py` 自动生成,记录对 wiki 原始代码块的每一处修正。 +格式:`[pageId:blockIdx]` 类型 → 原内容 → 修正后 → 依据。 + +## [58954439:3] illegal-in-function + +- **原文**:`namespace 定义 / 命名空间作用域成员函数定义 写在函数体内(void test(){ namespace A{...} })` +- **修正**:`把 namespace 块与成员定义提升到文件作用域,test() 仅使用其成员` +- **依据**:C++ 不允许在函数体内定义 namespace 或其成员函数;标准要求它们处于全局/命名空间作用域。原 wiki 写法无法编译;提升后保留教学意图。 + +## [58954439:6] illegal-in-function + +- **原文**:`namespace 定义 / 命名空间作用域成员函数定义 写在函数体内(void test(){ namespace A{...} })` +- **修正**:`把 namespace 块与成员定义提升到文件作用域,test() 仅使用其成员` +- **依据**:C++ 不允许在函数体内定义 namespace 或其成员函数;标准要求它们处于全局/命名空间作用域。原 wiki 写法无法编译;提升后保留教学意图。 + +## [58954439:7] illegal-in-function + +- **原文**:`namespace 定义 / 命名空间作用域成员函数定义 写在函数体内(void test(){ namespace A{...} })` +- **修正**:`把 namespace 块与成员定义提升到文件作用域,test() 仅使用其成员` +- **依据**:C++ 不允许在函数体内定义 namespace 或其成员函数;标准要求它们处于全局/命名空间作用域。原 wiki 写法无法编译;提升后保留教学意图。 + +## [58954439:14] removed-feature + +- **原文**:`K&R 隐式 int(int fun1(i)、g(){...})及无类型参数 fun3() 接受任意实参` +- **修正**:`显式 int(int fun1(int i)、int g(void){...}),fun2 形参显式为 char*,fun3() → fun3(void)、调用改为 fun3()(C17 不允许向 (void) 函数传参)` +- **依据**:隐式 int 与无类型/可变参数在 C99 起被废除,C17 不可编译。原始 C89 形式仍可由 tools/std_probe.py 与 tools/demo_versions/kr_implicit_int.c 在 -std=c89 下演示。 + +## [58954439:24] missing-symbol + +- **原文**:`const Person 演示引用 Person,但 Person 定义在另一小节(拆分为独立文件后丢失)` +- **修正**:`在本块顶部补一个最小 Person 定义(age 带默认成员初始化,使 const 对象可默认构造)` +- **依据**:原 wiki 把 Person 定义与 const 演示放在同节、本应同文件;按「示例相对隔离」拆开后本块自洽编译所需的最小前置定义。const 聚合对象需默认成员初始化才能默认构造(C++17)。 + +## [58954439:38] syntax + +- **原文**:`class Person{...}(末尾缺分号)` +- **修正**:`class Person{...};` +- **依据**:C++ 类/结构体定义后必须有分号。 + +## [58954439:41] illegal-in-function + +- **原文**:`namespace A/B 定义(含重载集 MyFunc)写在函数体内(void test(){ namespace A{...} })` +- **修正**:`把 namespace 块提升到文件作用域,test() 通过 A::/B:: 调用各重载` +- **依据**:C++ 不允许在函数体内定义 namespace;标准要求其处于全局/命名空间作用域。原 wiki 写法无法编译;提升后保留重载集演示意图。 + +## [58954439:42] ambiguous-overload + +- **原文**:`main 中 MyFunc("hello") 在 MyFunc(string) 与 MyFunc(string,int=10) 间二义,无法编译` +- **修正**:`注释保留二义调用作说明,main 只显式调用双参重载(单参形式因二义不可调用)` +- **依据**:函数重载遇上默认参数时,编译器无法在两个可行候选间抉择(二义性)。即使强转 std::string 也无法消除——两候选对单个 string 实参同等匹配。原文意在演示此现象,作为可运行示例只能保留可调用的双参形式并注释说明二义点。 + +## [58954439:46] missing-definition + +- **原文**:`main 调用 func1()/func2(),但本文件仅有 extern "C" 声明(定义在配对的 MyModule.c)` +- **修正**:`在本文件就地补 func1/func2 的 extern "C" 定义(最小实现)` +- **依据**:原 wiki 把声明与定义分属两文件;按「示例相对隔离」拆分为独立目标后,本文件缺少定义导致链接失败。补最小实现使其自洽可运行,保留 extern "C" 声明的教学意图。 + +## [58954448:0] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:0] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:1] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954451:2] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954451:3] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:7] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:8] pseudo-code + +- **原文**:`new Person / malloc / 构造函数(中文散文混入语句,不可编译)` +- **修正**:`可编译对照程序:new vs malloc+placement new,散文转注释` +- **依据**:原文是对照式伪代码(含「相当于:」「构造函数」等中文散文),无法编译。改为可编译的 new 与 malloc 行为对照(用 placement new 对齐构造语义)。 + +## [58954451:9] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:11] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:12] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954451:13] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954456:1] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954456:2] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954456:3] const-correctness + +- **原文**:`const 成员函数 ChangePerson() const 内修改非 mutable 成员 mAge(mAge = 100)` +- **修正**:`注释掉对 mAge 的修改,保留对 mutable mID 的修改,并加注释说明` +- **依据**:const 成员函数承诺不修改对象的逻辑状态,故不得修改非 mutable 数据成员;mID 被 mutable 修饰例外。原写法在 C++17 下编译报错。 + +## [58954457:0] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954458:3] typo + +- **原文**:`;(全角分号)` +- **修正**:`;(半角)` +- **依据**:全角分号在 C/C++ 中非法,统一替换为半角。 + +## [58954458:3] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954458:4] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954461:0] prose-in-code + +- **原文**:`class IndexPage 前混入孤立中文散文词「网页类」(非注释非代码)` +- **修正**:`把该散文行转为注释` +- **依据**:wiki 正文标题词被误纳入代码块,C++ 无法解析;转为注释保留教学上下文。 + +## [58954461:9] typo + +- **原文**:`“(全角引号)` +- **修正**:`"(半角)` +- **依据**:字符串/字符字面量须用半角引号。 + +## [58954461:9] typo + +- **原文**:`”(全角引号)` +- **修正**:`"(半角)` +- **依据**:字符串/字符字面量须用半角引号。 + +## [58954469:5] abstract-instantiation + +- **原文**:`test() 中 B b; 试图实例化抽象类 B(B 有纯虚析构函数 virtual ~B()=0)` +- **修正**:`注释掉 B b; 并加说明(B 是抽象类不可实例化),A a; 保留` +- **依据**:含纯虚函数(含纯虚析构函数)的类是抽象类,C++ 禁止创建其对象。原文此处正是演示该约束,但作为可编译示例需把非法构造注释化。 + +## [58954472:5] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954472:9] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954472:10] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954472:11] prose-in-code + +- **原文**:`「测试代码:」中文散文词 + 文件作用域裸语句(MyArray myArrayInt(10);、for 循环、Push_back 调用等)直接铺在类定义之后,无函数包裹` +- **修正**:`删除散文词,把测试驱动语句包进 main(),保留 MyArray 模板 + Person 类 + 打印函数` +- **依据**:C++ 不允许在文件作用域直接写可执行语句(如 for 循环、函数调用);中文散文词亦非法。原文是把「使用示例」当散文铺排;重写为可编译运行的单文件程序,保留类模板应用的教学意图。 + +## [58954473:2] syntax + +- **原文**:`//int b = const_cast(a); }(闭合花括号误入注释行)` +- **修正**:`把 } 移出注释作为 test01() 的正常闭合花括号` +- **依据**:原 wiki 把 test01() 的闭合 } 误写进注释,导致函数体未结束、后续 test02() 被嵌套在 test01() 内,C++ 禁止函数内定义函数。 + +## [58954474:1] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954474:2] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954474:3] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954474:4] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954474:4] removed-feature + +- **原文**:`动态异常规范 throw(int,char,char*) 与 throw()(C++17 已移除)` +- **修正**:`throw() → noexcept;throw(类型...) → noexcept(false),并注释说明旧语义` +- **依据**:动态异常规范在 C++11 弃用、C++17 移除,C++17 下是语法错误。throw() 的等价物是 noexcept;带类型列表的动态规范无法在现代 C++ 中精确表达,用 noexcept(false) 近似(表示可能抛异常)。旧规范演示见 tools/demo_versions/dynamic_exception_spec.cpp(在 -std=c++14 下编译)。 + +## [58954474:7] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954474:8] override-noexcept + +- **原文**:`MyOutOfRange::what() const 重写 exception::what() 但缺 noexcept` +- **修正**:`补 noexcept(virtual const char* what() const noexcept)` +- **依据**:C++11 起 std::exception::what() 声明为 noexcept;重写函数的异常规范不得比基类更宽松,必须同样 noexcept,否则 C++17 编译报「looser exception specification」。原 wiki 写于旧标准,未带 noexcept。 + +## [58954476:0] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954476:1] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954476:2] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954476:3] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954476:3] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954476:4] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954476:4] missing-include + +- **原文**:`调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:C 字符串/内存函数声明在 (C++)/ (C);原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。 + +## [58954476:4] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954476:4] missing-default-ctor + +- **原文**:`Person 只有 Person(char*,int) 构造,但二进制读取用 Person p3; 默认构造` +- **修正**:`补一个空的默认构造 Person(){}` +- **依据**:二进制文件读取需先默认构造一个占位对象再 read() 填充;原 Person 无默认构造,C++17 报「no matching function for call to Person::Person()」。 + +## [58954487:0] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954487:0] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954488:1] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954488:1] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954488:2] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954488:2] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954488:3] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954488:3] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954488:4] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954488:4] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954488:4] implementation-specific + +- **原文**:`直接访问 MSVC STL 私有成员 _Nodeptr/_Myhead/_Next/_Myval/_Mysize(演示 list 节点结构)` +- **修正**:`改用标准迭代器遍历,以注释图示说明环形双向链表结构` +- **依据**:MSVC STL 的下划线大写成员是实现细节,libstdc++ 无等价物,跨编译器不可移植。C++ 标准不暴露容器内部节点结构;原写法仅在 MSVC 下可编译。改用标准接口 + 图示注释保留教学意图。 + +## [58954488:5] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954488:5] const-comparator + +- **原文**:`set 的排序仿函数 MyCompare02/MyCompare03 的 operator() 非 const` +- **修正**:`给两个 operator() 加 const 修饰` +- **依据**:C++14 起,关联容器(set/map)的比较对象必须可 const 调用(标准要求 Compare 对象满足 invocable as const)。原 wiki 的非 const operator() 在 C++17 libstdc++ 下触发 static_assert。 + +## [58954488:6] platform + +- **原文**:`system("pause")` +- **修正**:`pause()` +- **依据**:system("pause") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 (读一行 stdin 等待回车),行为一致,Linux 可编译运行。 + +## [58954488:6] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954498:1] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954498:2] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954498:3] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954498:4] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + +## [58954498:5] missing-include + +- **原文**:`使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include , ` +- **修正**:`由生成模板统一补 #include ` +- **依据**:原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。 + diff --git a/docs/ERRATA_part3.md b/docs/ERRATA_part3.md new file mode 100644 index 0000000..9443f38 --- /dev/null +++ b/docs/ERRATA_part3.md @@ -0,0 +1,125 @@ +# ERRATA — 代码修正记录(Part3 Qt) + +本文件由 `tools/gen_part3.py` 自动生成,记录对 wiki 原始 Qt 代码块的每一处修正。 +格式:`[pageId:blockIdx]` 类型 → 原内容 → 修正后 → 依据。 + +## [58954515:0] missing-symbol + +- **原文**:`原文 #include "widget.h" 引用外部 Widget 类(widget.h/widget.cpp 不在块内)` +- **修正**:`在文件内就地补一个最小 Widget : public QWidget(Q_OBJECT)+ main` +- **依据**:原 wiki 的「最简单 Qt 程序」依赖外部的 widget.h;按「示例相对隔离」拆分为独立目标后本文件缺少 Widget 定义。补最小 Widget 使其自洽可编译运行。 + +## [58954525:0] prose-in-code + +- **原文**:`按钮创建片段行内混入散文「头文件 #include 」,且含中文字符串` +- **修正**:`重写为可编译 DemoWidget(剥离散文、保留中文文本)` +- **依据**:原 wiki 把讲解散文粘在代码行内(如 #include 当成尾巴),破坏语法;通用 prose 剥离会误伤字符串内的中文,故整体重写保留教学意图。 + +## [58954527:0] harness-context + +- **原文**:`片段 connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close) 引用 MyWidget,但 MyWidget 定义不在块内` +- **修正**:`补 DemoWidget 子类 + main 外壳,槽用 QWidget::close(系统自带)` +- **依据**:原 wiki 片段假定存在 MyWidget 类与 close 槽;按「示例相对隔离」补齐类骨架与 main,演示「系统自带信号(clicked)→系统自带槽(close)」。 + +## [58954527:2] prose-in-code + +- **原文**:`自定义信号槽:Teacher/Student/MyWidget 定义与中文散文混排,且信号名 hungury 笔误、槽名 treat/eat 不一致` +- **修正**:`重写为可编译对照程序:信号 hungry()/hungry(QString)、槽 treat()/treat(QString),函数指针重载解析 connect` +- **依据**:原 wiki 把类定义、emit、connect 与中文讲解散文混在一个代码块,无法编译;信号名 hungury 疑为 hungry 笔误。重写保留「自定义信号+槽+重载」教学意图。 + +## [58954529:4] prose-in-code + +- **原文**:`QDockWidget 片段尾部混入散文「设置区域范围」,含中文标题字符串` +- **修正**:`重写为可编译 DemoMainWindow(剥离散文、保留中文标题)` +- **依据**:原 wiki 把讲解散文粘在代码行尾(setAllowedAreas 后接「设置区域范围」),破坏语法;整体重写保留「铆接部件」教学意图。 + +## [58954535:12] merge+rewrite + +- **原文**:`文件对话框示例分散在 12/13/14 三块(含 :/images/file-open 资源、connect、openFile/saveFile 实现)` +- **修正**:`合并重写为单一可运行 MainWindow 程序,菜单/工具栏触发文件对话框读写` +- **依据**:原文三块是同一 MainWindow 的不同部分(构造、连接、槽实现),独立成文件无法编译;合并为完整目标。原文引用资源图标,本目标用纯文字 Action 避免资源依赖。 + +## [58954540:1] typo + +- **原文**:`QLable` +- **修正**:`QLabel` +- **依据**:笔误:QLabel 误写为 QLable。 + +## [58954540:img] merge+typo + +- **原文**:`显示图片示例分散在 3 块(QPixmap pixmap; pixmap.load(:/Image/boat.jpg); label.setPixmap(pixmap) 用了 . 而非 ->)` +- **修正**:`合并为一个目标,修正 .→->,资源 :/Image/boat.jpg 由生成占位 png + .qrc 提供` +- **依据**:原文 label 是 QLabel* 却用 . 调用 setPixmap(C++ 指针须用 ->);三块拆开不可编译。 + +## [58954540:movie] merge+typo + +- **原文**:`显示动画示例分散在 3 块(QMovie(:/Mario.gif)、movie->start()、QLabel 全角分号 ;)` +- **修正**:`合并为一个目标,全角分号→半角,资源 :/Mario.gif 由生成占位 gif + .qrc 提供` +- **依据**:原文 QLabel 定义末尾用了全角分号 ;(C++ 非法);三块拆开不可编译。 + +## [58954540:15] h+cpp-merge + +- **原文**:`自定义控件 SmallWidget 在 wiki 中以 smallwidget.h 内联展示(Q_OBJECT + spin/slider/layout/connect)` +- **修正**:`输出为单 .cpp(含类定义+main 实例化),保留 Qt5 static_cast 重载解析写法` +- **依据**:原 wiki 把类定义放 .h 注释块;按「每目标单文件」输出,AUTOMOC 处理 Q_OBJECT。 + +## [58954546:0] syntax+merge + +- **原文**:`EventLabel 事件重写程序:QString("...(%1, %2)\n...") 内误入字面换行(断行)导致字符串未终结;类声明与实现分散` +- **修正**:`合并为单 .cpp,字符串内换行移除(\n 移出字符串或删除),补 setMouseTracking` +- **依据**:原 wiki 的 QString 字面量在源码中跨行书写(实际是 \n 后跟换行),C++ 字符串字面量不能直接跨行,导致 missing terminating "。修正为单行字符串。同时补 mouseTracking。 + +## [58954546:2] member-def-harness + +- **原文**:`CustomWidget::event(QEvent*) override 片段(处理 KeyPress/Tab)缺少所属类骨架与 main` +- **修正**:`补 CustomWidget : public QWidget 骨架(含 event override)+ main 外壳,行为对照原文` +- **依据**:原 wiki 只给成员函数定义,缺少类声明与 main;按「示例相对隔离」补齐使其可编译运行。 + +## [58954546:6] class+main-harness + +- **原文**:`事件过滤器 MainWindow(eventFilter override + installEventFilter)片段缺 main 与完整结构` +- **修正**:`补完整 MainWindow(含 QLabel、eventFilter、installEventFilter)+ main 外壳` +- **依据**:原 wiki 给出 MainWindow 类骨架但缺 main;补齐使其可编译运行,演示事件过滤机制。 + +## [58954548:0] h+cpp-merge + +- **原文**:`PaintedWidget 类声明(10.1 block0)与其构造+paintEvent 实现(block1)分两块` +- **修正**:`合并为单 .cpp(Q_OBJECT 类 + paintEvent),补 main 外壳演示 drawLine/Rect/Ellipse` +- **依据**:原文类声明与成员实现分属两块;按「每目标单文件」合并,AUTOMOC 处理 Q_OBJECT。 + +## [58954548:2] member-def-harness + +- **原文**:`PaintWidget::paintEvent 绘 pixmap/bitmap(引用 :/Image/butterfly.png、butterfly1.png)` +- **修正**:`补 PaintWidget 类骨架 + main,资源由生成占位 png + .qrc 提供` +- **依据**:原 wiki 仅给成员函数定义,缺类骨架与 main;资源图片用占位 png 提供以可编译运行。 + +## [58954548:3] member-def-harness + +- **原文**:`PaintWidget::paintEvent 绘 QImage(setPixel 循环逐像素设色)片段` +- **修正**:`补 PaintWidget 类骨架 + main 外壳` +- **依据**:原 wiki 仅给成员函数定义,缺类骨架与 main;补齐使其可编译运行。 + +## [58954548:4] platform-path + +- **原文**:`QPicture 保存路径为 "D:\ndrawing.pic"(Windows 路径,且 \n 被解析为换行符)` +- **修正**:`改为相对路径 "drawing.pic"(跨平台可移植)` +- **依据**:原路径是 Windows 绝对路径,\n 在字符串中被当作换行符导致路径错误;改为相对路径使示例在 Linux 下可编译运行。 + +## [58954552:0] blocking-event-loop + +- **原文**:`原文用 QApplication + app.exec()(阻塞 GUI 事件循环),但示例实为控制台文件操作` +- **修正**:`改用 QCoreApplication,文件 IO 完成后直接 return 0(不进事件循环)` +- **依据**:QApplication::exec() 进入 GUI 事件循环会一直阻塞;该示例不创建窗口,仅做文件读写,用 QCoreApplication 并直接返回即可正确退出,便于自动化运行验证。原行为不变。 + +## [58954552:data] merge + +- **原文**:`二进制读写示例分散在 3 块(写/读/读写),各自引用 file.dat 且未检查 open() 返回值` +- **修正**:`合并为一个程序:先写 file.dat 再读回,补 open() 检查,用 QCoreApplication(无需 GUI)` +- **依据**:原文三块是读写示例的不同片段,独立不可编译;合并为完整程序。补 open() 检查避免未定义行为;数据文件 file.dat 程序自建。 + +## [58954552:text] merge + +- **原文**:`文本读写示例分散在 2 块(写/读),各自引用 file.txt` +- **修正**:`合并为一个程序:先写 file.txt 再逐行读回,用 QCoreApplication(无需 GUI)` +- **依据**:原文两块是读写示例的不同片段,独立不可编译;合并为完整程序。 + diff --git a/docs/SOURCE_PAGES.md b/docs/SOURCE_PAGES.md new file mode 100644 index 0000000..4e918bc --- /dev/null +++ b/docs/SOURCE_PAGES.md @@ -0,0 +1,57 @@ +# SOURCE_PAGES — wiki 页面与本仓库目录的映射 + +本文件记录川大 wiki「02. Qt 方向」每个页面(pageId)与本仓库源码目录的对应关系。 +所有源码由 `tools/gen_part1.py` / `tools/gen_part3.py` 从抓取的 wiki 代码块生成 +(原始数据见 `tools/wiki_data/`,生成器只读不改原始数据)。 + +目录采用「深缩写」:part 缩写 `pNN`(顶层不编号)/ 章缩写 `chNN` / 节缩写 `sNN`。 + +## Part 1 — C/C++ 强化(`p01/`,host gcc/g++ 构建,144 个目标) + +| pageId | 目录 | wiki 标题 | 代码块数 | +|---|---|---|---| +| 58954438 | `p01/ch02` | 02.C++初识 | 1 | +| 58954439 | `p01/ch03` | 03.C++对C的扩展 | 47 | +| 58954448 | `p01/ch04/s01` | 01.类和对象的基本概念 | 4 | +| 58954449 | `p01/ch04/s02` | 02.面向对象程序设计案例 | 2 | +| 58954451 | `p01/ch04/s03` | 03.对象的构造和析构 | 17 | +| 58954456 | `p01/ch04/s04` | 04.C++面向对象模型初探 | 4 | +| 58954457 | `p01/ch04/s05` | 05.友元 | 1 | +| 58954458 | `p01/ch04/s06` | 06.运算符重载 | 6 | +| 58954461 | `p01/ch04/s07` | 07.继承和派生 | 13 | +| 58954469 | `p01/ch04/s08` | 08.多态 | 7 | +| 58954472 | `p01/ch05` | 05.C++模板 | 12 | +| 58954473 | `p01/ch06` | 06.C++类型转换 | 3 | +| 58954474 | `p01/ch07` | 07.C++异常 | 9 | +| 58954476 | `p01/ch08` | 08.C++输入输出流 | 5 | +| 58954487 | `p01/ch09/s02` | 02.STL三大组件 | 1 | +| 58954488 | `p01/ch09/s03` | 03.常用容器 | 7 | +| 58954498 | `p01/ch09/s04` | 04.常用算法 | 6 | + +> 第 4 章「类和对象」分 8 节(s01–s08);第 9 章「STL」分 3 节(s02–s04)。 + +## Part 3 — Qt 桌面应用开发(`p03/`,需隔离 Qt 5.14.2,29 个目标) + +| pageId | 目录 | wiki 标题 | 代码块数 | +|---|---|---|---| +| 58954515 | `p03/ch02` | 2 创建Qt项目 | 1 | +| 58954525 | `p03/ch03` | 3 第一个Qt小程序 | 3 | +| 58954527 | `p03/ch04` | 4 信号和槽机制 | 7 | +| 58954529 | `p03/ch05` | 5 QMainWindow | 7 | +| 58954535 | `p03/ch06` | 6 对话框QDialog | 16 | +| 58954540 | `p03/ch08` | 8 常用控件 | 16 | +| 58954546 | `p03/ch09` | 9 Qt消息机制和事件 | 11 | +| 58954548 | `p03/ch10` | 10 绘图和绘图设备 | 5 | +| 58954552 | `p03/ch11` | 11 文件系统 | 7 | + +> Part3 中纯签名/伪代码/参考片段(约 40 块)未单独建目标,折进各章 `README.md` +> 作文档(见 `gen_part3.py` 的 `BLOCK_TREATMENT` 分类的 `snippet`)。 + +## 抓取数据说明 + +- `tools/wiki_data/extracted_code.json`:所有页面的代码块(按 pageId → [blocks])。 +- `tools/wiki_data/bodies/.json`:每页正文 HTML(用于标题层级解析)。 +- `tools/wiki_data/all_page_ids.json`:页面元信息(id、标题)。 +- `tools/wiki_data/qt_full_tree.json`:wiki 目录树结构。 + +> 原始抓取数据保持原貌,**不被生成器修改**(红线,见 `HANDOFF.md` §9)。 diff --git a/docs/VERSION_NOTES.md b/docs/VERSION_NOTES.md new file mode 100644 index 0000000..f6b44c0 --- /dev/null +++ b/docs/VERSION_NOTES.md @@ -0,0 +1,115 @@ +# VERSION_NOTES — 概念/版本验证记录(任务 1) + +本文件记录 wiki 课程代码中「与语言版本相关」的论断:原文说法 → 当前 C17/C++17 +是否仍成立 → 不成立则在何版本成立 → 已编译验证的结果。 + +验证工具:`tools/std_probe.py`(逐 `-std=` 编译,报告 pass/fail/warning)。 +对照演示:`tools/demo_versions/`(每个论断一个最小可编译片段)。 + +> 基线:本仓库按 **C17 / C++17** 构建所有示例(见顶层 `CMakeLists.txt`)。 +> 验证编译器:宿主机 `gcc/g++ 13.3.0`(原生支持 C89…C23、C++98…C++23)。 + +## 验证方法 + +```bash +# 单片段跨版本探测(pedantic-errors 让已废除特性硬失败,便于对比) +tools/std_probe.py --lang c --file tools/demo_versions/kr_implicit_int.c \ + --standards c89,c99,c11,c17 --pedantic-errors +tools/std_probe.py --lang c++ --file tools/demo_versions/dynamic_exception_spec.cpp \ + --standards c++11,c++14,c++17,c++20 +``` + +--- + +## 1. K&R 隐式 int(函数/参数省略类型) + +| | C89 | C99 | C11 | C17 | +|---|---|---|---|---| +| 结果 | ✅ PASS(仅警告) | ❌ FAIL | ❌ FAIL | ❌ FAIL | + +- **原文论断**(块 `58954439:14`「3.4 C++中所有的变量和函数都必须有类型」): + C89 允许 `fun1(i){...}`(隐式 int 返回 + 隐式 int 参数)。 +- **C17 是否成立**:否。C99 起废除隐式 int;C17 下非法(错误)。 +- **在何版本成立**:C89(及更早)。 +- **本仓库处理**:Part1 该块用 `exact_fix` 给出 C17 等价的显式类型版本 + (`int fun1(int i)`);原始 C89 形式保留在 `tools/demo_versions/kr_implicit_int.c`, + 供跨版本对比。 +- **已编译验证**:`std_probe` 显示 c89 PASS、c99/c11/c17 FAIL(`--pedantic-errors`)。 + +## 2. 三目运算符作为左值 + +| | C89 | C99 | C17 | C++11 | C++17 | +|---|---|---|---|---|---| +| 结果 | ❌ FAIL | ❌ FAIL | ❌ FAIL | ✅ PASS | ✅ PASS | + +- **原文论断**(块 `58954439:18`「3.8 三目运算符功能增强」): + C 中三目返回右值;C++ 中三目返回两操作数公共类型的引用(可作左值)。 +- **C17/C++17 是否成立**:C++ 独有特性,C++17 成立;C(含 C17)不允许。 +- **本仓库处理**:该特性为 C/C++ 差异演示,保留在 + `tools/demo_versions/ternary_lvalue.cpp`。 +- **已编译验证**:C++ 全版本 PASS;C 全版本 FAIL(`lvalue required as left operand`)。 + +## 3. `char* p = malloc(n)` 不显式转换 + +| | C89 | C17 | C++17 | +|---|---|---|---| +| 结果 | ✅ PASS | ✅ PASS | ❌ FAIL | + +- **原文论断**(块 `58954439:15`):C 中 `void*` 隐式转任意指针合法;C++ 必须显式转换。 +- **C17/C++17 是否成立**:C 合法,C++ 非法(`invalid conversion from void* to char*`)。 +- **本仓库处理**:保留在 `tools/demo_versions/malloc_no_cast.c`。 +- **已编译验证**:C PASS;C++ FAIL。 + +## 4. 枚举变量直接赋整型值 + +| | C17 | C++17 | +|---|---|---| +| 结果 | ✅ PASS | ❌ FAIL | + +- **原文论断**(块 `58954439:15` 类型严格性对照): + C 中 `enum Color c = 1;` 合法(int 隐式转 enum);C++ 需显式转换。 +- **C17/C++17 是否成立**:C 合法,C++ 非法(`invalid conversion from int to Color`)。 +- **本仓库处理**:保留在 `tools/demo_versions/enum_assign_int.c`。 +- **已编译验证**:C PASS;C++ FAIL。 + +## 5. 经指针改写 const 变量 + +| | C89 | C17 | C++17 | +|---|---|---|---| +| 编译 | ✅ PASS | ✅ PASS | ✅ PASS | + +- **原文论断**(块 `58954439:21`「3.8/C/C++ const 异同」): + 通过 `(int*)&const_var` 强转改写 const 变量,C 多见改写生效,C++ 常因 + 常量折叠仍读出原值。 +- **C17/C++17 是否成立**:两者都能编译,但均属**未定义行为(UB)**; + 「观察到的值」随编译器优化级别而变,不保证。教学价值在于说明 UB 不可依赖。 +- **本仓库处理**:保留在 `tools/demo_versions/const_via_pointer.c`,注释强调 UB。 +- **已编译验证**:C/C++ 均编译通过(行为属 UB,不作为「支持/不支持」结论)。 + +## 6. 动态异常规范 `throw(类型...)` / `throw()` + +| | C++11 | C++14 | C++17 | C++20 | +|---|---|---|---|---| +| 结果 | ⚠️ PASS(弃用警告) | ⚠️ PASS(弃用警告) | ❌ FAIL | ❌ FAIL | + +- **原文论断**(块 `58954474:4`「7.2.4 异常接口声明」): + 演示 `throw(int,char,char*)` 与 `throw()` 异常规范。 +- **C++17 是否成立**:否。动态异常规范在 C++11 弃用、**C++17 移除**,C++17 下是语法错误。 + `throw()` 的等价物是 `noexcept`;带类型列表的动态规范在现代 C++ 中无法表达。 +- **本仓库处理**:Part1 该块用 `exact_fix` 改写为 `noexcept` / `noexcept(false)` + 并注释说明;原始动态规范形式保留在 + `tools/demo_versions/dynamic_exception_spec.cpp`(在 `-std=c++14` 下可编译)。 +- **已编译验证**:C++11/14 PASS(弃用警告),C++17/C++20 FAIL。 + +--- + +## 附:其它「平台相关」修正(非版本概念,记录于此) + +- **`system("pause")`**(多块):仅 Windows 可用。`common_fix` 统一改为便携 + `pause()` 壳函数(读一行 stdin 等回车),Linux 可编译运行,行为一致。 +- **Qt 过时 API**(Part3,Qt5.14 仍可用,保留并标注): + - `QMouseEvent::x()/y()`(Qt6 建议用 `position()`); + - `QString::sprintf`(Qt6 弃用); + - Qt4 `SIGNAL/SLOT` 字符串写法(vs Qt5 函数指针); + - `static_cast` 重载解析(`QOverload<>::of` 是 Qt5.7+ 的现代等价)。 + 这些保留原写法以匹配 Qt 5.14 课程环境,相关说明见 `docs/ERRATA_part3.md`。 diff --git a/p01/CMakeLists.txt b/p01/CMakeLists.txt new file mode 100644 index 0000000..1880f2a --- /dev/null +++ b/p01/CMakeLists.txt @@ -0,0 +1,18 @@ +# Auto-generated by tools/gen_part1.py +add_subdirectory(ch02) +add_subdirectory(ch03) +add_subdirectory(ch04/s01) +add_subdirectory(ch04/s02) +add_subdirectory(ch04/s03) +add_subdirectory(ch04/s04) +add_subdirectory(ch04/s05) +add_subdirectory(ch04/s06) +add_subdirectory(ch04/s07) +add_subdirectory(ch04/s08) +add_subdirectory(ch05) +add_subdirectory(ch06) +add_subdirectory(ch07) +add_subdirectory(ch08) +add_subdirectory(ch09/s02) +add_subdirectory(ch09/s03) +add_subdirectory(ch09/s04) diff --git a/p01/ch02/CMakeLists.txt b/p01/ch02/CMakeLists.txt new file mode 100644 index 0000000..eed8ae1 --- /dev/null +++ b/p01/ch02/CMakeLists.txt @@ -0,0 +1,7 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch02 +add_executable(p1c02_01 hello_world.cpp) +set_target_properties(p1c02_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c02_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c02_01 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch02 DEPENDS p1c02_01) diff --git a/p01/ch02/hello_world.cpp b/p01/ch02/hello_world.cpp new file mode 100644 index 0000000..14fd53e --- /dev/null +++ b/p01/ch02/hello_world.cpp @@ -0,0 +1,21 @@ +// ============================================================================ +// p1c02_01 — 2.1.1 c++ hello world +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954438:0] 2.1.1 c++ hello world --- +#include +using namespace std; + +int main(){ + cout << "hello world" << endl; + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/CMakeLists.txt b/p01/ch03/CMakeLists.txt new file mode 100644 index 0000000..005d351 --- /dev/null +++ b/p01/ch03/CMakeLists.txt @@ -0,0 +1,181 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch03 +add_executable(p1c03_mymodule extern_c_module_main.c) +set_target_properties(p1c03_mymodule PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c03_mymodule PRIVATE -Wall -Wextra) +add_executable(p1c03_01 scope_resolution_operator.cpp) +set_target_properties(p1c03_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_01 PRIVATE -Wall -Wextra) +add_executable(p1c03_02 scope_resolution_operator_2.cpp) +set_target_properties(p1c03_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_02 PRIVATE -Wall -Wextra) +add_executable(p1c03_03 namespace_syntax.cpp) +set_target_properties(p1c03_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_03 PRIVATE -Wall -Wextra) +add_executable(p1c03_04 namespace_syntax_2.cpp) +set_target_properties(p1c03_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_04 PRIVATE -Wall -Wextra) +add_executable(p1c03_05 namespace_syntax_3.cpp) +set_target_properties(p1c03_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_05 PRIVATE -Wall -Wextra) +add_executable(p1c03_06 namespace_syntax_4.cpp) +set_target_properties(p1c03_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_06 PRIVATE -Wall -Wextra) +add_executable(p1c03_07 namespace_syntax_5.cpp) +set_target_properties(p1c03_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_07 PRIVATE -Wall -Wextra) +add_executable(p1c03_08 namespace_syntax_6.cpp) +set_target_properties(p1c03_08 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_08 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_08 PRIVATE -Wall -Wextra) +add_executable(p1c03_09 namespace_syntax_7.cpp) +set_target_properties(p1c03_09 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_09 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_09 PRIVATE -Wall -Wextra) +add_executable(p1c03_10 namespace_syntax_8.cpp) +set_target_properties(p1c03_10 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_10 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_10 PRIVATE -Wall -Wextra) +add_executable(p1c03_11 using_declaration.cpp) +set_target_properties(p1c03_11 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_11 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_11 PRIVATE -Wall -Wextra) +add_executable(p1c03_12 using_declaration_2.cpp) +set_target_properties(p1c03_12 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_12 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_12 PRIVATE -Wall -Wextra) +add_executable(p1c03_13 using_directive.cpp) +set_target_properties(p1c03_13 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_13 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_13 PRIVATE -Wall -Wextra) +add_executable(p1c03_14 global_variable_detection.c) +set_target_properties(p1c03_14 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c03_14 PRIVATE -Wall -Wextra) +add_executable(p1c03_15 strict_function_signatures.c) +set_target_properties(p1c03_15 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c03_15 PRIVATE -Wall -Wextra) +add_executable(p1c03_16 stricter_type_casting.c) +set_target_properties(p1c03_16 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c03_16 PRIVATE -Wall -Wextra) +add_executable(p1c03_17 struct_enhancement.cpp) +set_target_properties(p1c03_17 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_17 PRIVATE -Wall -Wextra) +add_executable(p1c03_18 bool_keyword.cpp) +set_target_properties(p1c03_18 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_18 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_18 PRIVATE -Wall -Wextra) +add_executable(p1c03_19 ternary_operator.c) +set_target_properties(p1c03_19 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c03_19 PRIVATE -Wall -Wextra) +add_executable(p1c03_20 ternary_operator.cpp) +set_target_properties(p1c03_20 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_20 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_20 PRIVATE -Wall -Wextra) +add_executable(p1c03_21 const_diff_summary.cpp) +set_target_properties(p1c03_21 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_21 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_21 PRIVATE -Wall -Wextra) +add_executable(p1c03_22 const_diff_summary.c) +set_target_properties(p1c03_22 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c03_22 PRIVATE -Wall -Wextra) +add_executable(p1c03_23 const_diff_summary_2.cpp) +set_target_properties(p1c03_23 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_23 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_23 PRIVATE -Wall -Wextra) +add_executable(p1c03_24 const_diff_summary_3.cpp) +set_target_properties(p1c03_24 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_24 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_24 PRIVATE -Wall -Wextra) +add_executable(p1c03_25 const_diff_summary_4.cpp) +set_target_properties(p1c03_25 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_25 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_25 PRIVATE -Wall -Wextra) +add_executable(p1c03_26 const_vs_define.cpp) +set_target_properties(p1c03_26 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_26 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_26 PRIVATE -Wall -Wextra) +add_executable(p1c03_27 const_vs_define_2.cpp) +set_target_properties(p1c03_27 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_27 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_27 PRIVATE -Wall -Wextra) +add_executable(p1c03_28 const_vs_define_3.cpp) +set_target_properties(p1c03_28 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_28 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_28 PRIVATE -Wall -Wextra) +add_executable(p1c03_29 reference_basics.cpp) +set_target_properties(p1c03_29 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_29 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_29 PRIVATE -Wall -Wextra) +add_executable(p1c03_30 reference_basics_2.cpp) +set_target_properties(p1c03_30 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_30 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_30 PRIVATE -Wall -Wextra) +add_executable(p1c03_31 reference_in_function.cpp) +set_target_properties(p1c03_31 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_31 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_31 PRIVATE -Wall -Wextra) +add_executable(p1c03_32 reference_in_function_2.cpp) +set_target_properties(p1c03_32 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_32 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_32 PRIVATE -Wall -Wextra) +add_executable(p1c03_33 reference_essence.cpp) +set_target_properties(p1c03_33 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_33 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_33 PRIVATE -Wall -Wextra) +add_executable(p1c03_34 pointer_reference.cpp) +set_target_properties(p1c03_34 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_34 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_34 PRIVATE -Wall -Wextra) +add_executable(p1c03_35 const_reference.cpp) +set_target_properties(p1c03_35 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_35 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_35 PRIVATE -Wall -Wextra) +add_executable(p1c03_36 const_reference_2.cpp) +set_target_properties(p1c03_36 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_36 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_36 PRIVATE -Wall -Wextra) +add_executable(p1c03_37 macro_pitfalls.cpp) +set_target_properties(p1c03_37 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_37 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_37 PRIVATE -Wall -Wextra) +add_executable(p1c03_38 macro_pitfalls_2.cpp) +set_target_properties(p1c03_38 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_38 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_38 PRIVATE -Wall -Wextra) +add_executable(p1c03_39 inline_in_class.cpp) +set_target_properties(p1c03_39 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_39 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_39 PRIVATE -Wall -Wextra) +add_executable(p1c03_40 default_arguments.cpp) +set_target_properties(p1c03_40 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_40 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_40 PRIVATE -Wall -Wextra) +add_executable(p1c03_41 placeholder_parameters.cpp) +set_target_properties(p1c03_41 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_41 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_41 PRIVATE -Wall -Wextra) +add_executable(p1c03_42 function_overload_basics.cpp) +set_target_properties(p1c03_42 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_42 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_42 PRIVATE -Wall -Wextra) +add_executable(p1c03_43 function_overload_basics_2.cpp) +set_target_properties(p1c03_43 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_43 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_43 PRIVATE -Wall -Wextra) +add_executable(p1c03_44 function_overload_mechanism.cpp) +set_target_properties(p1c03_44 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_44 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_44 PRIVATE -Wall -Wextra) +add_executable(p1c03_45 extern_c.cpp) +set_target_properties(p1c03_45 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c03_45 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c03_45 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch03 DEPENDS p1c03_mymodule p1c03_01 p1c03_02 p1c03_03 p1c03_04 p1c03_05 p1c03_06 p1c03_07 p1c03_08 p1c03_09 p1c03_10 p1c03_11 p1c03_12 p1c03_13 p1c03_14 p1c03_15 p1c03_16 p1c03_17 p1c03_18 p1c03_19 p1c03_20 p1c03_21 p1c03_22 p1c03_23 p1c03_24 p1c03_25 p1c03_26 p1c03_27 p1c03_28 p1c03_29 p1c03_30 p1c03_31 p1c03_32 p1c03_33 p1c03_34 p1c03_35 p1c03_36 p1c03_37 p1c03_38 p1c03_39 p1c03_40 p1c03_41 p1c03_42 p1c03_43 p1c03_44 p1c03_45) diff --git a/p01/ch03/bool_keyword.cpp b/p01/ch03/bool_keyword.cpp new file mode 100644 index 0000000..d9e0ca9 --- /dev/null +++ b/p01/ch03/bool_keyword.cpp @@ -0,0 +1,23 @@ +// ============================================================================ +// p1c03_18 — 3.7 “新增”bool类型关键字 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:17] 3.7 “新增”bool类型关键字 --- +void test() +{ cout << sizeof(false) << endl; //为1,//bool类型占一个字节大小 + bool flag = true; // c语言中没有这种类型 + flag = 100; //给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0) +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/const_diff_summary.c b/p01/ch03/const_diff_summary.c new file mode 100644 index 0000000..04af4c1 --- /dev/null +++ b/p01/ch03/const_diff_summary.c @@ -0,0 +1,30 @@ +// ============================================================================ +// p1c03_22 — 3.8.2.3 C/C++中const异同总结 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +#include +#include + +// --- 原文片段 [58954439:21] 3.8.2.3 C/C++中const异同总结 --- +static void block_0() { + const int constA = 10; + + int* p = (int*)&constA; + + *p = 300; + + printf("constA:%d\n",constA); + + printf("*p:%d\n", *p); +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/const_diff_summary.cpp b/p01/ch03/const_diff_summary.cpp new file mode 100644 index 0000000..fee1698 --- /dev/null +++ b/p01/ch03/const_diff_summary.cpp @@ -0,0 +1,19 @@ +// ============================================================================ +// p1c03_21 — 3.8.2.3 C/C++中const异同总结 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:20] 3.8.2.3 C/C++中const异同总结 --- + const int constA = 10; + int main(){ + int* p = (int*)&constA; + *p = 200; + } + +// --- main --- diff --git a/p01/ch03/const_diff_summary_2.cpp b/p01/ch03/const_diff_summary_2.cpp new file mode 100644 index 0000000..41a65f8 --- /dev/null +++ b/p01/ch03/const_diff_summary_2.cpp @@ -0,0 +1,25 @@ +// ============================================================================ +// p1c03_23 — 3.8.2.3 C/C++中const异同总结 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:22] 3.8.2.3 C/C++中const异同总结 --- +static void block_0() { + const int constA = 10; + int* p = (int*)&constA; + *p = 300; + cout << "constA:" << constA << endl; + cout << "*p:" << *p << endl; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/const_diff_summary_3.cpp b/p01/ch03/const_diff_summary_3.cpp new file mode 100644 index 0000000..b3a62f5 --- /dev/null +++ b/p01/ch03/const_diff_summary_3.cpp @@ -0,0 +1,26 @@ +// ============================================================================ +// p1c03_24 — 3.8.2.3 C/C++中const异同总结 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:23] 3.8.2.3 C/C++中const异同总结 --- +static void block_0() { + int b = 10; + const int constA = b; + int* p = (int*)&constA; + *p = 300; + cout << "constA:" << constA << endl; + cout << "*p:" << *p << endl; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/const_diff_summary_4.cpp b/p01/ch03/const_diff_summary_4.cpp new file mode 100644 index 0000000..9d35603 --- /dev/null +++ b/p01/ch03/const_diff_summary_4.cpp @@ -0,0 +1,29 @@ +// ============================================================================ +// p1c03_25 — 3.8.2.3 C/C++中const异同总结 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:24] 3.8.2.3 C/C++中const异同总结 --- +static void block_0() { + class Person { public: int age = 0; }; + const Person person; //未初始化age + //person.age = 50; //不可修改 + Person* pPerson = (Person*)&person; + //指针间接修改 + pPerson->age = 100; + cout << "pPerson->age:" << pPerson->age << endl; + pPerson->age = 200; + cout << "pPerson->age:" << pPerson->age << endl; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/const_reference.cpp b/p01/ch03/const_reference.cpp new file mode 100644 index 0000000..fbfc420 --- /dev/null +++ b/p01/ch03/const_reference.cpp @@ -0,0 +1,33 @@ +// ============================================================================ +// p1c03_35 — 3.10.5 常量引用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:34] 3.10.5 常量引用 --- +void test01(){ + int a = 100; + const int& aRef = a; //此时aRef就是a + //aRef = 200; 不能通过aRef的值 + a = 100; //OK + cout << "a:" << a << endl; + cout << "aRef:" << aRef << endl; +} +void test02(){ + //不能把一个字面量赋给引用 + //int& ref = 100; + //但是可以把一个字面量赋给常引用 + const int& ref = 100; //int temp = 200; const int& ret = temp; +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch03/const_reference_2.cpp b/p01/ch03/const_reference_2.cpp new file mode 100644 index 0000000..e431642 --- /dev/null +++ b/p01/ch03/const_reference_2.cpp @@ -0,0 +1,21 @@ +// ============================================================================ +// p1c03_36 — 3.10.5 常量引用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:35] 3.10.5 常量引用 --- +//const int& param防止函数中意外修改数据 +void ShowVal(const int& param){ + cout << "param:" << param << endl; +} + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch03/const_vs_define.cpp b/p01/ch03/const_vs_define.cpp new file mode 100644 index 0000000..c122a97 --- /dev/null +++ b/p01/ch03/const_vs_define.cpp @@ -0,0 +1,27 @@ +// ============================================================================ +// p1c03_26 — 3.9.3 尽量以const替换#define +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:25] 3.9.3 尽量以const替换#define --- +#define PARAM 128 + +const short param = 128; + +void func_1(short a){ + cout << "short!" << endl; +} +void func_1(int a){ + cout << "int" << endl; +} + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch03/const_vs_define_2.cpp b/p01/ch03/const_vs_define_2.cpp new file mode 100644 index 0000000..7b44a08 --- /dev/null +++ b/p01/ch03/const_vs_define_2.cpp @@ -0,0 +1,29 @@ +// ============================================================================ +// p1c03_27 — 3.9.3 尽量以const替换#define +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:26] 3.9.3 尽量以const替换#define --- +void func1(){ + const int a = 10; + #define A 20 + //#undef A //卸载宏常量A +} + +void func2(){ + //cout << "a:" << a << endl; //不可访问,超出了const int a作用域 + cout << "A:" << A << endl; //#define作用域从定义到文件结束或者到#undef,可访问 +} + +int main(){ + func2(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/const_vs_define_3.cpp b/p01/ch03/const_vs_define_3.cpp new file mode 100644 index 0000000..4bb19dd --- /dev/null +++ b/p01/ch03/const_vs_define_3.cpp @@ -0,0 +1,27 @@ +// ============================================================================ +// p1c03_28 — 3.9.3 尽量以const替换#define +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:27] 3.9.3 尽量以const替换#define --- +namespace MySpace{ + #define num 1024 +} + +void test(){ + //cout << MySpace::NUM << endl; //错误 + //int num = 100; //命名冲突 + cout << num << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/default_arguments.cpp b/p01/ch03/default_arguments.cpp new file mode 100644 index 0000000..f82c5fb --- /dev/null +++ b/p01/ch03/default_arguments.cpp @@ -0,0 +1,34 @@ +// ============================================================================ +// p1c03_40 — 3.12 函数的默认参数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:39] 3.12 函数的默认参数 --- +void TestFunc01(int a = 10, int b = 20){ + cout << "a + b = " << a + b << endl; +} +//注意点: +//1. 形参b设置默认参数值,那么后面位置的形参c也需要设置默认参数 +void TestFunc02(int a,int b = 10,int c = 10){} +//2. 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数 +void TestFunc03(int a = 0,int b = 0); +void TestFunc03(int a, int b){} + +int main(){ + //1.如果没有传参数,那么使用默认参数 + TestFunc01(); + //2. 如果传一个参数,那么第二个参数使用默认参数 + TestFunc01(100); + //3. 如果传入两个参数,那么两个参数都使用我们传入的参数 + TestFunc01(100, 200); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/extern_c.cpp b/p01/ch03/extern_c.cpp new file mode 100644 index 0000000..1c4b615 --- /dev/null +++ b/p01/ch03/extern_c.cpp @@ -0,0 +1,48 @@ +// ============================================================================ +// p1c03_45 — 3.14.3 extern “C”浅析 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:46] 3.14.3 extern “C”浅析 --- +#define _CRT_SECURE_NO_WARNINGS +#include +using namespace std; + +#if 0 + + #ifdef __cplusplus + extern "C" { + #if 0 + void func1(); + int func2(int a, int b); + #else + #include"MyModule.h" + #endif + } + + #endif + +#else + + extern "C" void func1(); + extern "C" int func2(int a, int b); + +#endif + +int main(){ + func1(); + cout << func2(10, 20) << endl; + return EXIT_SUCCESS; +} +// --- 为使本示例作为独立目标可链接,就地提供 func1/func2 的 extern "C" 定义 --- +// (原文仅声明;定义在配对的 MyModule.c。此处补最小实现以自洽编译运行。) +extern "C" void func1(){ printf("hello world!"); } +extern "C" int func2(int a, int b){ return a + b; } + +// --- main --- diff --git a/p01/ch03/extern_c_module_main.c b/p01/ch03/extern_c_module_main.c new file mode 100644 index 0000000..3e473c4 --- /dev/null +++ b/p01/ch03/extern_c_module_main.c @@ -0,0 +1,13 @@ +// 58954439:45 — MyModule 实现文件(与 my_module.h 配对) +// 由 gen_part1.py 成对输出。 + +#include"my_module.h" +#include + +void func1(){ + printf("hello world!"); +} +int func2(int a, int b){ + return a + b; +} +int main(){ func1(); printf("func2=%d\n", func2(3,4)); return EXIT_SUCCESS; } diff --git a/p01/ch03/function_overload_basics.cpp b/p01/ch03/function_overload_basics.cpp new file mode 100644 index 0000000..f26f7f9 --- /dev/null +++ b/p01/ch03/function_overload_basics.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// p1c03_42 — 3.14.2.1 函数重载基本语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:41] 3.14.2.1 函数重载基本语法 --- +namespace A{ + void MyFunc(){ cout << "无参数!" << endl; } + void MyFunc(int a){ cout << "a: " << a << endl; } + void MyFunc(string b){ cout << "b: " << b << endl; } + void MyFunc(int a, string b){ cout << "a: " << a << " b:" << b << endl;} + void MyFunc(string b, int a){ cout << "a: " << a << " b:" << b << endl;} +} +namespace B{ + void MyFunc(string b, int a){} + //int MyFunc(string b, int a){} //无法重载仅按返回值区分的函数 +} +void test(){ + A::MyFunc(); + A::MyFunc(10); + A::MyFunc("hello"); + A::MyFunc(10, "world"); + A::MyFunc("world", 10); + B::MyFunc("ok", 1); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/function_overload_basics_2.cpp b/p01/ch03/function_overload_basics_2.cpp new file mode 100644 index 0000000..17f59dd --- /dev/null +++ b/p01/ch03/function_overload_basics_2.cpp @@ -0,0 +1,27 @@ +// ============================================================================ +// p1c03_43 — 3.14.2.1 函数重载基本语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:42] 3.14.2.1 函数重载基本语法 --- +void MyFunc(string b){ + cout << "b: " << b << endl; +} +//函数重载碰上默认参数 +void MyFunc(string b, int a = 10){ + cout << "a: " << a << " b:" << b << endl; +} +int main(){ + // MyFunc("hello"); // 二义:MyFunc(string) 与 MyFunc(string,int=10) 同等匹配 + // MyFunc(string("hello")); // 仍二义:强转 string 也无法区分两候选 + MyFunc("hello", 20); // 显式给第二参 → 明确选双参重载 + return 0; +} + +// --- main --- diff --git a/p01/ch03/function_overload_mechanism.cpp b/p01/ch03/function_overload_mechanism.cpp new file mode 100644 index 0000000..88ed73f --- /dev/null +++ b/p01/ch03/function_overload_mechanism.cpp @@ -0,0 +1,20 @@ +// ============================================================================ +// p1c03_44 — 3.14.2.2 函数重载实现原理 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:43] 3.14.2.2 函数重载实现原理 --- +void func(){} +void func(int x){} +void func(int x,char y){} + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch03/global_variable_detection.c b/p01/ch03/global_variable_detection.c new file mode 100644 index 0000000..3173b20 --- /dev/null +++ b/p01/ch03/global_variable_detection.c @@ -0,0 +1,22 @@ +// ============================================================================ +// p1c03_14 — 3.3 全局变量检测增强 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +#include +#include + +// --- 原文片段 [58954439:13] 3.3 全局变量检测增强 --- +int a = 10; //赋值,当做定义 +int a; //没有赋值,当做声明 + +int main(){ + printf("a:%d\n",a); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/inline_in_class.cpp b/p01/ch03/inline_in_class.cpp new file mode 100644 index 0000000..ca1728f --- /dev/null +++ b/p01/ch03/inline_in_class.cpp @@ -0,0 +1,22 @@ +// ============================================================================ +// p1c03_39 — 3.11.3.2 类内部的内联函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:38] 3.11.3.2 类内部的内联函数 --- +class Person{ +public: + Person(){ cout << "构造函数!" << endl; } + void PrintPerson(){ cout << "输出Person!" << endl; } +}; + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch03/macro_pitfalls.cpp b/p01/ch03/macro_pitfalls.cpp new file mode 100644 index 0000000..b2bb990 --- /dev/null +++ b/p01/ch03/macro_pitfalls.cpp @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_37 — 3.11.2 预处理宏的缺陷 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:36] 3.11.2 预处理宏的缺陷 --- +#define ADD(x,y) x+y +inline int Add(int x,int y){ + return x + y; +} +void test(){ + int ret1 = ADD(10, 20) * 10; //希望的结果是300 + int ret2 = Add(10, 20) * 10; //希望结果也是300 + cout << "ret1:" << ret1 << endl; //210 + cout << "ret2:" << ret2 << endl; //300 +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/macro_pitfalls_2.cpp b/p01/ch03/macro_pitfalls_2.cpp new file mode 100644 index 0000000..8e5c47c --- /dev/null +++ b/p01/ch03/macro_pitfalls_2.cpp @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_38 — 3.11.2 预处理宏的缺陷 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:37] 3.11.2 预处理宏的缺陷 --- +#define COMPARE(x,y) ((x) < (y) ? (x) : (y)) +int Compare(int x,int y){ + return x < y ? x : y; +} +void test02(){ + int a = 1; + int b = 3; + //cout << "COMPARE(++a, b):" << COMPARE(++a, b) << endl; // 3 + cout << "Compare(int x,int y):" << Compare(++a, b) << endl; //2 +} + +// --- main --- +int main() { + test02(); + return 0; +} diff --git a/p01/ch03/my_module.h b/p01/ch03/my_module.h new file mode 100644 index 0000000..f93ce25 --- /dev/null +++ b/p01/ch03/my_module.h @@ -0,0 +1,17 @@ +#ifndef MYMODULE_H +#define MYMODULE_H + +#include + +#if __cplusplus +extern "C"{ +#endif + + void func1(); + int func2(int a,int b); + +#if __cplusplus +} +#endif + +#endif diff --git a/p01/ch03/namespace_syntax.cpp b/p01/ch03/namespace_syntax.cpp new file mode 100644 index 0000000..c1b1cc2 --- /dev/null +++ b/p01/ch03/namespace_syntax.cpp @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_03 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:2] 3.2.2命名空间使用语法 --- +namespace A{ + int a = 10; +} +namespace B{ + int a = 20; +} +void test(){ + cout << "A::a : " << A::a << endl; + cout << "B::a : " << B::a << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/namespace_syntax_2.cpp b/p01/ch03/namespace_syntax_2.cpp new file mode 100644 index 0000000..5d6eb35 --- /dev/null +++ b/p01/ch03/namespace_syntax_2.cpp @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_04 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:3] 3.2.2命名空间使用语法 --- +namespace A{ + int a = 10; +} +namespace B{ + int a = 20; +} +void test(){ + cout << "A::a : " << A::a << endl; + cout << "B::a : " << B::a << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/namespace_syntax_3.cpp b/p01/ch03/namespace_syntax_3.cpp new file mode 100644 index 0000000..807ca06 --- /dev/null +++ b/p01/ch03/namespace_syntax_3.cpp @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_05 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:4] 3.2.2命名空间使用语法 --- +namespace A{ + int a = 10; + namespace B{ + int a = 20; + } +} +void test(){ + cout << "A::a : " << A::a << endl; + cout << "A::B::a : " << A::B::a << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/namespace_syntax_4.cpp b/p01/ch03/namespace_syntax_4.cpp new file mode 100644 index 0000000..274c48e --- /dev/null +++ b/p01/ch03/namespace_syntax_4.cpp @@ -0,0 +1,32 @@ +// ============================================================================ +// p1c03_06 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:5] 3.2.2命名空间使用语法 --- +namespace A{ + int a = 10; +} + +namespace A{ + void func(){ + cout << "hello namespace!" << endl; + } +} + +void test(){ + cout << "A::a : " << A::a << endl; + A::func(); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/namespace_syntax_5.cpp b/p01/ch03/namespace_syntax_5.cpp new file mode 100644 index 0000000..e08bf74 --- /dev/null +++ b/p01/ch03/namespace_syntax_5.cpp @@ -0,0 +1,21 @@ +// ============================================================================ +// p1c03_07 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:6] 3.2.2命名空间使用语法 --- +namespace MySpace{ + void func1(); + void func2(int param); +} + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch03/namespace_syntax_6.cpp b/p01/ch03/namespace_syntax_6.cpp new file mode 100644 index 0000000..a357db6 --- /dev/null +++ b/p01/ch03/namespace_syntax_6.cpp @@ -0,0 +1,32 @@ +// ============================================================================ +// p1c03_08 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:7] 3.2.2命名空间使用语法 --- +namespace MySpace{ + void func1(); + void func2(int param); +} +void MySpace::func1(){ + cout << "MySpace::func1" << endl; +} +void MySpace::func2(int param){ + cout << "MySpace::func2 : " << param << endl; +} +void test(){ + MySpace::func1(); + MySpace::func2(42); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/namespace_syntax_7.cpp b/p01/ch03/namespace_syntax_7.cpp new file mode 100644 index 0000000..fd3f446 --- /dev/null +++ b/p01/ch03/namespace_syntax_7.cpp @@ -0,0 +1,26 @@ +// ============================================================================ +// p1c03_09 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:8] 3.2.2命名空间使用语法 --- +namespace{ + int a = 10; + void func(){ cout << "hello namespace" << endl; } +} +void test(){ + cout << "a : " << a << endl; + func(); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/namespace_syntax_8.cpp b/p01/ch03/namespace_syntax_8.cpp new file mode 100644 index 0000000..28cd834 --- /dev/null +++ b/p01/ch03/namespace_syntax_8.cpp @@ -0,0 +1,30 @@ +// ============================================================================ +// p1c03_10 — 3.2.2命名空间使用语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:9] 3.2.2命名空间使用语法 --- +namespace veryLongName{ + + int a = 10; + void func(){ cout << "hello namespace" << endl; } +} + +void test(){ + namespace shortName = veryLongName; + cout << "veryLongName::a : " << shortName::a << endl; + veryLongName::func(); + shortName::func(); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/placeholder_parameters.cpp b/p01/ch03/placeholder_parameters.cpp new file mode 100644 index 0000000..6d089a0 --- /dev/null +++ b/p01/ch03/placeholder_parameters.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// p1c03_41 — 3.13 函数的占位参数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:40] 3.13 函数的占位参数 --- +void TestFunc01(int a,int b,int){ + //函数内部无法使用占位参数 + cout << "a + b = " << a + b << endl; +} +//占位参数也可以设置默认值 +void TestFunc02(int a, int b, int = 20){ + //函数内部依旧无法使用占位参数 + cout << "a + b = " << a + b << endl; +} +int main(){ + + //错误调用,占位参数也是参数,必须传参数 + //TestFunc01(10,20); + //正确调用 + TestFunc01(10,20,30); + //正确调用 + TestFunc02(10,20); + //正确调用 + TestFunc02(10, 20, 30); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/pointer_reference.cpp b/p01/ch03/pointer_reference.cpp new file mode 100644 index 0000000..ea4ecd3 --- /dev/null +++ b/p01/ch03/pointer_reference.cpp @@ -0,0 +1,41 @@ +// ============================================================================ +// p1c03_34 — 3.10.4 指针引用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:33] 3.10.4 指针引用 --- +struct Teacher{ + int mAge; +}; +//指针间接修改teacher的年龄 +void AllocateAndInitByPointer(Teacher** teacher){ + *teacher = (Teacher*)malloc(sizeof(Teacher)); + (*teacher)->mAge = 200; +} +//引用修改teacher年龄 +void AllocateAndInitByReference(Teacher*& teacher){ + teacher->mAge = 300; +} +void test(){ + //创建Teacher + Teacher* teacher = NULL; + //指针间接赋值 + AllocateAndInitByPointer(&teacher); + cout << "AllocateAndInitByPointer:" << teacher->mAge << endl; + //引用赋值,将teacher本身传到ChangeAgeByReference函数中 + AllocateAndInitByReference(teacher); + cout << "AllocateAndInitByReference:" << teacher->mAge << endl; + free(teacher); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/reference_basics.cpp b/p01/ch03/reference_basics.cpp new file mode 100644 index 0000000..cdbbe39 --- /dev/null +++ b/p01/ch03/reference_basics.cpp @@ -0,0 +1,58 @@ +// ============================================================================ +// p1c03_29 — 3.10.1 引用基本用法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:28] 3.10.1 引用基本用法 --- +//1. 认识引用 +void test01(){ + + int a = 10; + //给变量a取一个别名b + int& b = a; + cout << "a:" << a << endl; + cout << "b:" << b << endl; + cout << "------------" << endl; + //操作b就相当于操作a本身 + b = 100; + cout << "a:" << a << endl; + cout << "b:" << b << endl; + cout << "------------" << endl; + //一个变量可以有n个别名 + int& c = a; + c = 200; + cout << "a:" << a << endl; + cout << "b:" << b << endl; + cout << "c:" << c << endl; + cout << "------------" << endl; + //a,b,c的地址都是相同的 + cout << "a:" << &a << endl; + cout << "b:" << &b << endl; + cout << "c:" << &c << endl; +} +//2. 使用引用注意事项 +void test02(){ + //1) 引用必须初始化 + //int& ref; //报错:必须初始化引用 + //2) 引用一旦初始化,不能改变引用 + int a = 10; + int b = 20; + int& ref = a; + ref = b; //不能改变引用 + //3) 不能对数组建立引用 + int arr[10]; + //int& ref3[10] = arr; +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch03/reference_basics_2.cpp b/p01/ch03/reference_basics_2.cpp new file mode 100644 index 0000000..526d1ac --- /dev/null +++ b/p01/ch03/reference_basics_2.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// p1c03_30 — 3.10.1 引用基本用法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:29] 3.10.1 引用基本用法 --- +static void block_0() { + //1. 建立数组引用方法一 + typedef int ArrRef[10]; + int arr[10]; + ArrRef& aRef = arr; + for (int i = 0; i < 10;i ++){ + aRef[i] = i+1; + } + for (int i = 0; i < 10;i++){ + cout << arr[i] << " "; + } + cout << endl; + //2. 建立数组引用方法二 + int(&f)[10] = arr; + for (int i = 0; i < 10; i++){ + f[i] = i+10; + } + for (int i = 0; i < 10; i++){ + cout << arr[i] << " "; + } + cout << endl; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/reference_essence.cpp b/p01/ch03/reference_essence.cpp new file mode 100644 index 0000000..d310c5c --- /dev/null +++ b/p01/ch03/reference_essence.cpp @@ -0,0 +1,27 @@ +// ============================================================================ +// p1c03_33 — 3.10.3 引用的本质 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:32] 3.10.3 引用的本质 --- +//发现是引用,转换为 int* const ref = &a; +void testFunc(int& ref){ + ref = 100; // ref是引用,转换为*ref = 100 +} +int main(){ + int a = 10; + int& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化 + aRef = 20; //内部发现aRef是引用,自动帮我们转换为: *aRef = 20; + cout << "a:" << a << endl; + cout << "aRef:" << aRef << endl; + testFunc(a); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/reference_in_function.cpp b/p01/ch03/reference_in_function.cpp new file mode 100644 index 0000000..0035f8d --- /dev/null +++ b/p01/ch03/reference_in_function.cpp @@ -0,0 +1,49 @@ +// ============================================================================ +// p1c03_31 — 3.10.2 函数中的引用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:30] 3.10.2 函数中的引用 --- +//值传递 +void ValueSwap(int m,int n){ + int temp = m; + m = n; + n = temp; +} +//地址传递 +void PointerSwap(int* m,int* n){ + int temp = *m; + *m = *n; + *n = temp; +} +//引用传递 +void ReferenceSwap(int& m,int& n){ + int temp = m; + m = n; + n = temp; +} +void test(){ + int a = 10; + int b = 20; + //值传递 + ValueSwap(a, b); + cout << "a:" << a << " b:" << b << endl; + //地址传递 + PointerSwap(&a, &b); + cout << "a:" << a << " b:" << b << endl; + //引用传递 + ReferenceSwap(a, b); + cout << "a:" << a << " b:" << b << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/reference_in_function_2.cpp b/p01/ch03/reference_in_function_2.cpp new file mode 100644 index 0000000..6eb8cae --- /dev/null +++ b/p01/ch03/reference_in_function_2.cpp @@ -0,0 +1,35 @@ +// ============================================================================ +// p1c03_32 — 3.10.2 函数中的引用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:31] 3.10.2 函数中的引用 --- +//返回局部变量引用 +int& TestFun01(){ + int a = 10; //局部变量 + return a; +} +//返回静态变量引用 +int& TestFunc02(){ + static int a = 20; + cout << "static int a : " << a << endl; + return a; +} +int main(){ + //不能返回局部变量的引用 + int& ret01 = TestFun01(); + //如果函数做左值,那么必须返回引用 + TestFunc02(); + TestFunc02() = 100; + TestFunc02(); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/scope_resolution_operator.cpp b/p01/ch03/scope_resolution_operator.cpp new file mode 100644 index 0000000..d609e97 --- /dev/null +++ b/p01/ch03/scope_resolution_operator.cpp @@ -0,0 +1,26 @@ +// ============================================================================ +// p1c03_01 — 3.1 ::作用域运算符 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:0] 3.1 ::作用域运算符 --- +//全局变量 +int a = 10; +void test(){ + //局部变量 + int a = 20; + //全局a被隐藏 + cout << "a:" << a << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/scope_resolution_operator_2.cpp b/p01/ch03/scope_resolution_operator_2.cpp new file mode 100644 index 0000000..51b28c2 --- /dev/null +++ b/p01/ch03/scope_resolution_operator_2.cpp @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_02 — 3.1 ::作用域运算符 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:1] 3.1 ::作用域运算符 --- +//全局变量 +int a = 10; +//1. 局部变量和全局变量同名 +void test(){ + int a = 20; + //打印局部变量a + cout << "局部变量a:" << a << endl; + //打印全局变量a + cout << "全局变量a:" << ::a << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/strict_function_signatures.c b/p01/ch03/strict_function_signatures.c new file mode 100644 index 0000000..4009660 --- /dev/null +++ b/p01/ch03/strict_function_signatures.c @@ -0,0 +1,50 @@ +// ============================================================================ +// p1c03_15 — 3.4 C++中所有的变量和函数都必须有类型 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +#include +#include + +// --- 原文片段 [58954439:14] 3.4 C++中所有的变量和函数都必须有类型 --- +//i没有写类型,可以是任意类型 +int fun1(int i){ + printf("%d\n", i); + return 0; +} +//i没有写类型,可以是任意类型 +int fun2(char* i){ + printf("%s\n", i); + return 0; +} +//没有写参数,代表可以传任何类型的实参 +int fun3(void){ + printf("fun33333333333333333\n"); + return 0; +} + +//C语言,如果函数没有参数,建议写void,代表没有参数 +int fun4(void){ + printf("fun4444444444444\n"); + return 0; +} + +int g(void){ + return 10; +} + +int main(){ + + fun1(10); + fun2("abc"); + fun3(); // 注意:C89 中 fun3() 可接受任意参数;C17 下 fun3(void) 不接受参数 + printf("g = %d\n", g()); + + return 0; +} + +// --- main --- diff --git a/p01/ch03/stricter_type_casting.c b/p01/ch03/stricter_type_casting.c new file mode 100644 index 0000000..43ace84 --- /dev/null +++ b/p01/ch03/stricter_type_casting.c @@ -0,0 +1,23 @@ +// ============================================================================ +// p1c03_16 — 3.5 更严格的类型转换 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +#include +#include + +// --- 原文片段 [58954439:15] 3.5 更严格的类型转换 --- +typedef enum COLOR{ GREEN, RED, YELLOW } color; +int main(){ + color mycolor = GREEN; + mycolor = 10; + printf("mycolor:%d\n", mycolor); + char* p = malloc(10); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch03/struct_enhancement.cpp b/p01/ch03/struct_enhancement.cpp new file mode 100644 index 0000000..4ede1ff --- /dev/null +++ b/p01/ch03/struct_enhancement.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// p1c03_17 — 3.6 struct类型加强 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:16] 3.6 struct类型加强 --- +//1. 结构体中即可以定义成员变量,也可以定义成员函数 +struct Student{ + string mName; + int mAge; + void setName(string name){ mName = name; } + void setAge(int age){ mAge = age; } + void showStudent(){ + cout << "Name:" << mName << " Age:" << mAge << endl; + } +}; + +//2. c++中定义结构体变量不需要加struct关键字 +void test01(){ + Student student; + student.setName("John"); + student.setAge(20); + student.showStudent(); +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch03/ternary_operator.c b/p01/ch03/ternary_operator.c new file mode 100644 index 0000000..1dc5fc9 --- /dev/null +++ b/p01/ch03/ternary_operator.c @@ -0,0 +1,28 @@ +// ============================================================================ +// p1c03_19 — 3.8 三目运算符功能增强 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +#include +#include + +// --- 原文片段 [58954439:18] 3.8 三目运算符功能增强 --- +static void block_0() { + int a = 10; + int b = 20; + printf("ret:%d\n", a > b ? a : b); + //思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么? + + //(a > b ? a : b) = 100; + //返回的是右值 +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/ternary_operator.cpp b/p01/ch03/ternary_operator.cpp new file mode 100644 index 0000000..7452734 --- /dev/null +++ b/p01/ch03/ternary_operator.cpp @@ -0,0 +1,29 @@ +// ============================================================================ +// p1c03_20 — 3.8 三目运算符功能增强 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:19] 3.8 三目运算符功能增强 --- +static void block_0() { + int a = 10; + int b = 20; + printf("ret:%d\n", a > b ? a : b); + //思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么? + + cout << "b:" << b << endl; + //返回的是左值,变量的引用 + (a > b ? a : b) = 100;//返回的是左值,变量的引用 + cout << "b:" << b << endl; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch03/using_declaration.cpp b/p01/ch03/using_declaration.cpp new file mode 100644 index 0000000..ab934d3 --- /dev/null +++ b/p01/ch03/using_declaration.cpp @@ -0,0 +1,38 @@ +// ============================================================================ +// p1c03_11 — 3.2.3 using声明 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:10] 3.2.3 using声明 --- +namespace A{ + int paramA = 20; + int paramB = 30; + void funcA(){ cout << "hello funcA" << endl; } + void funcB(){ cout << "hello funcA" << endl; } +} + +void test(){ + //1. 通过命名空间域运算符 + cout << A::paramA << endl; + A::funcA(); + //2. using声明 + using A::paramA; + using A::funcA; + cout << paramA << endl; + //cout << paramB << endl; //不可直接访问 + funcA(); + //3. 同名冲突 + //int paramA = 20; //相同作用域注意同名冲突 +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/using_declaration_2.cpp b/p01/ch03/using_declaration_2.cpp new file mode 100644 index 0000000..78065d1 --- /dev/null +++ b/p01/ch03/using_declaration_2.cpp @@ -0,0 +1,29 @@ +// ============================================================================ +// p1c03_12 — 3.2.3 using声明 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:11] 3.2.3 using声明 --- +namespace A{ + void func(){} + void func(int x){} + int func(int x,int y){} +} +void test(){ + using A::func; + func(); + func(10); + func(10, 20); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch03/using_directive.cpp b/p01/ch03/using_directive.cpp new file mode 100644 index 0000000..825027e --- /dev/null +++ b/p01/ch03/using_directive.cpp @@ -0,0 +1,51 @@ +// ============================================================================ +// p1c03_13 — 3.2.4 using编译指令 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954439:12] 3.2.4 using编译指令 --- +namespace A{ + int paramA = 20; + int paramB = 30; + void funcA(){ cout << "hello funcA" << endl; } + void funcB(){ cout << "hello funcB" << endl; } +} + +void test01(){ + using namespace A; + cout << paramA << endl; + cout << paramB << endl; + funcA(); + funcB(); + + //不会产生二义性 + int paramA = 30; + cout << paramA << endl; +} + +namespace B{ + int paramA = 20; + int paramB = 30; + void funcA(){ cout << "hello funcA" << endl; } + void funcB(){ cout << "hello funcB" << endl; } +} + +void test02(){ + using namespace A; + using namespace B; + //二义性产生,不知道调用A还是B的paramA + //cout << paramA << endl; +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch04/s01/CMakeLists.txt b/p01/ch04/s01/CMakeLists.txt new file mode 100644 index 0000000..d7f52b2 --- /dev/null +++ b/p01/ch04/s01/CMakeLists.txt @@ -0,0 +1,18 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s01 +add_executable(p1c04_01_01 class_encapsulation.c) +set_target_properties(p1c04_01_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +target_compile_options(p1c04_01_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_01_02 class_encapsulation.cpp) +set_target_properties(p1c04_01_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_01_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_01_02 PRIVATE -Wall -Wextra) +add_executable(p1c04_01_03 class_encapsulation_2.cpp) +set_target_properties(p1c04_01_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_01_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_01_03 PRIVATE -Wall -Wextra) +add_executable(p1c04_01_04 private_members.cpp) +set_target_properties(p1c04_01_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_01_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_01_04 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s01 DEPENDS p1c04_01_01 p1c04_01_02 p1c04_01_03 p1c04_01_04) diff --git a/p01/ch04/s01/class_encapsulation.c b/p01/ch04/s01/class_encapsulation.c new file mode 100644 index 0000000..1f0fb38 --- /dev/null +++ b/p01/ch04/s01/class_encapsulation.c @@ -0,0 +1,41 @@ +// ============================================================================ +// p1c04_01_01 — 4.1.2 类的封装 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +#include +#include + +// --- 原文片段 [58954448:0] 4.1.2 类的封装 --- +typedef struct _Person{ + char name[64]; + int age; +}Person; +typedef struct _Aninal{ + char name[64]; + int age; + int type; //动物种类 +}Ainmal; + +void PersonEat(Person* person){ + printf("%s在吃人吃的饭!\n",person->name); +} +void AnimalEat(Ainmal* animal){ + printf("%s在吃动物吃的饭!\n", animal->name); +} + +int main(){ + + Person person; + strcpy(person.name, "小明"); + person.age = 30; + AnimalEat(&person); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s01/class_encapsulation.cpp b/p01/ch04/s01/class_encapsulation.cpp new file mode 100644 index 0000000..1a55320 --- /dev/null +++ b/p01/ch04/s01/class_encapsulation.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// p1c04_01_02 — 4.1.2 类的封装 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954448:1] 4.1.2 类的封装 --- +//封装两层含义 +//1. 属性和行为合成一个整体 +//2. 访问控制,现实事物本身有些属性和行为是不对外开放 +class Person{ +//人具有的行为(函数) +public: + void Dese(){ cout << "我有钱,年轻,个子又高,就爱嘚瑟!" << endl;} +//人的属性(变量) +public: + int mTall; //多高,可以让外人知道 +protected: + int mMoney; // 有多少钱,只能儿子孙子知道 +private: + int mAge; //年龄,不想让外人知道 +}; + +int main(){ + + Person p; + p.mTall = 220; + //p.mMoney 保护成员外部无法访问 + //p.mAge 私有成员外部无法访问 + p.Dese(); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s01/class_encapsulation_2.cpp b/p01/ch04/s01/class_encapsulation_2.cpp new file mode 100644 index 0000000..8ba9c4d --- /dev/null +++ b/p01/ch04/s01/class_encapsulation_2.cpp @@ -0,0 +1,31 @@ +// ============================================================================ +// p1c04_01_03 — 4.1.2 类的封装 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954448:2] 4.1.2 类的封装 --- +class A{ + int mAge; +}; +struct B{ + int mAge; +}; + +void test(){ + A a; + B b; + //a.mAge; //无法访问私有成员 + b.mAge; //可正常外部访问 +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s01/private_members.cpp b/p01/ch04/s01/private_members.cpp new file mode 100644 index 0000000..4c60900 --- /dev/null +++ b/p01/ch04/s01/private_members.cpp @@ -0,0 +1,35 @@ +// ============================================================================ +// p1c04_01_04 — 4.1.3 将成员变量设置为private +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954448:3] 4.1.3 将成员变量设置为private --- +static void block_0() { + class AccessLevels{ + public: + //对只读属性进行只读访问 + int getReadOnly(){ return readOnly; } + //对读写属性进行读写访问 + void setReadWrite(int val){ readWrite = val; } + int getReadWrite(){ return readWrite; } + //对只写属性进行只写访问 + void setWriteOnly(int val){ writeOnly = val; } + private: + int readOnly; //对外只读访问 + int noAccess; //外部不可访问 + int readWrite; //读写访问 + int writeOnly; //只写访问 + }; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch04/s02/CMakeLists.txt b/p01/ch04/s02/CMakeLists.txt new file mode 100644 index 0000000..9795157 --- /dev/null +++ b/p01/ch04/s02/CMakeLists.txt @@ -0,0 +1,11 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s02 +add_executable(p1c04_02_01 cube_class_design.cpp) +set_target_properties(p1c04_02_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_02_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_02_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_02_02 point_and_circle.cpp) +set_target_properties(p1c04_02_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_02_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_02_02 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s02 DEPENDS p1c04_02_01 p1c04_02_02) diff --git a/p01/ch04/s02/cube_class_design.cpp b/p01/ch04/s02/cube_class_design.cpp new file mode 100644 index 0000000..4ca7013 --- /dev/null +++ b/p01/ch04/s02/cube_class_design.cpp @@ -0,0 +1,80 @@ +// ============================================================================ +// p1c04_02_01 — 4.2.1 设计立方体类 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954449:0] 4.2.1 设计立方体类 --- +//立方体类 +class Cub{ +public: + void setL(int l){ mL = l; } + void setW(int w){ mW = w; } + void setH(int h){ mH = h; } + int getL(){ return mL; } + int getW(){ return mW; } + int getH(){ return mH; } + //立方体面积 + int caculateS(){ return (mL*mW + mL*mH + mW*mH) * 2; } + //立方体体积 + int caculateV(){ return mL * mW * mH; } + //成员方法 + bool CubCompare(Cub& c){ + if (getL() == c.getL() && getW() == c.getW() && getH() == c.getH()){ + return true; + } + return false; + } +private: + int mL; //长 + int mW; //宽 + int mH; //高 +}; + +//比较两个立方体是否相等 +bool CubCompare(Cub& c1, Cub& c2){ + if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH()){ + return true; + } + return false; +} + +void test(){ + Cub c1, c2; + c1.setL(10); + c1.setW(20); + c1.setH(30); + + c2.setL(20); + c2.setW(20); + c2.setH(30); + + cout << "c1面积:" << c1.caculateS() << " 体积:" << c1.caculateV() << endl; + cout << "c2面积:" << c2.caculateS() << " 体积:" << c2.caculateV() << endl; + + //比较两个立方体是否相等 + if (CubCompare(c1, c2)){ + cout << "c1和c2相等!" << endl; + } + else{ + cout << "c1和c2不相等!" << endl; + } + + if (c1.CubCompare(c2)){ + cout << "c1和c2相等!" << endl; + } + else{ + cout << "c1和c2不相等!" << endl; + } +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s02/point_and_circle.cpp b/p01/ch04/s02/point_and_circle.cpp new file mode 100644 index 0000000..cb0ef3c --- /dev/null +++ b/p01/ch04/s02/point_and_circle.cpp @@ -0,0 +1,71 @@ +// ============================================================================ +// p1c04_02_02 — 4.2.2 点和圆的关系 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954449:1] 4.2.2 点和圆的关系 --- +//点类 +class Point{ +public: + void setX(int x){ mX = x; } + void setY(int y){ mY = y; } + int getX(){ return mX; } + int getY(){ return mY; } +private: + int mX; + int mY; +}; + +//圆类 +class Circle{ +public: + void setP(int x,int y){ + mP.setX(x); + mP.setY(y); + } + void setR(int r){ mR = r; } + Point& getP(){ return mP; } + int getR(){ return mR; } + //判断点和圆的关系 + void IsPointInCircle(Point& point){ + int distance = (point.getX() - mP.getX()) * (point.getX() - mP.getX()) + (point.getY() - mP.getY()) * (point.getY() - mP.getY()); + int radius = mR * mR; + if (distance < radius){ + cout << "Point(" << point.getX() << "," << point.getY() << ")在圆内!" << endl; + } + else if (distance > radius){ + cout << "Point(" << point.getX() << "," << point.getY() << ")在圆外!" << endl; + } + else{ + cout << "Point(" << point.getX() << "," << point.getY() << ")在圆上!" << endl; + } + } +private: + Point mP; //圆心 + int mR; //半径 +}; + +void test(){ + //实例化圆对象 + Circle circle; + circle.setP(20, 20); + circle.setR(5); + //实例化点对象 + Point point; + point.setX(25); + point.setY(20); + + circle.IsPointInCircle(point); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/CMakeLists.txt b/p01/ch04/s03/CMakeLists.txt new file mode 100644 index 0000000..7e09bd2 --- /dev/null +++ b/p01/ch04/s03/CMakeLists.txt @@ -0,0 +1,71 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s03 +add_executable(p1c04_03_01 ctor_and_dtor.cpp) +set_target_properties(p1c04_03_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_02 ctor_classification.cpp) +set_target_properties(p1c04_03_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_02 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_03 copy_ctor_timing.cpp) +set_target_properties(p1c04_03_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_03 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_04 deep_vs_shallow_copy.cpp) +set_target_properties(p1c04_03_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_04 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_05 initializer_list.cpp) +set_target_properties(p1c04_03_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_05 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_06 class_object_member.cpp) +set_target_properties(p1c04_03_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_06 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_07 explicit_keyword.cpp) +set_target_properties(p1c04_03_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_07 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_08 c_dynamic_alloc.cpp) +set_target_properties(p1c04_03_08 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_08 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_08 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_09 new_operator.cpp) +set_target_properties(p1c04_03_09 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_09 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_09 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_10 delete_operator.cpp) +set_target_properties(p1c04_03_10 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_10 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_10 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_11 new_delete_array.cpp) +set_target_properties(p1c04_03_11 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_11 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_11 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_12 new_delete_array_2.cpp) +set_target_properties(p1c04_03_12 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_12 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_12 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_13 delete_void_ptr.cpp) +set_target_properties(p1c04_03_13 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_13 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_13 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_14 static_member_variable.cpp) +set_target_properties(p1c04_03_14 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_14 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_14 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_15 static_member_function.cpp) +set_target_properties(p1c04_03_15 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_15 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_15 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_16 const_static_member.cpp) +set_target_properties(p1c04_03_16 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_16 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_16 PRIVATE -Wall -Wextra) +add_executable(p1c04_03_17 static_singleton.cpp) +set_target_properties(p1c04_03_17 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_03_17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_03_17 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s03 DEPENDS p1c04_03_01 p1c04_03_02 p1c04_03_03 p1c04_03_04 p1c04_03_05 p1c04_03_06 p1c04_03_07 p1c04_03_08 p1c04_03_09 p1c04_03_10 p1c04_03_11 p1c04_03_12 p1c04_03_13 p1c04_03_14 p1c04_03_15 p1c04_03_16 p1c04_03_17) diff --git a/p01/ch04/s03/c_dynamic_alloc.cpp b/p01/ch04/s03/c_dynamic_alloc.cpp new file mode 100644 index 0000000..175aa1f --- /dev/null +++ b/p01/ch04/s03/c_dynamic_alloc.cpp @@ -0,0 +1,52 @@ +// ============================================================================ +// p1c04_03_08 — 4.3.9.2 C动态分配内存方法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:7] 4.3.9.2 C动态分配内存方法 --- +class Person{ +public: + Person(){ + mAge = 20; + pName = (char*)malloc(strlen("john")+1); + strcpy(pName, "john"); + } + void Init(){ + mAge = 20; + pName = (char*)malloc(strlen("john")+1); + strcpy(pName, "john"); + } + void Clean(){ + if (pName != NULL){ + free(pName); + } + } +public: + int mAge; + char* pName; +}; +int main(){ + + //分配内存 + Person* person = (Person*)malloc(sizeof(Person)); + if(person == NULL){ + return 0; + } + //调用初始化函数 + person->Init(); + //清理对象 + person->Clean(); + //释放person对象 + free(person); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s03/class_object_member.cpp b/p01/ch04/s03/class_object_member.cpp new file mode 100644 index 0000000..d1e610d --- /dev/null +++ b/p01/ch04/s03/class_object_member.cpp @@ -0,0 +1,90 @@ +// ============================================================================ +// p1c04_03_06 — 4.3.7.2 类对象作为成员 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:5] 4.3.7.2 类对象作为成员 --- +//汽车类 +class Car{ +public: + Car(){ + cout << "Car 默认构造函数!" << endl; + mName = "大众汽车"; + } + Car(string name){ + cout << "Car 带参数构造函数!" << endl; + mName = name; + } + ~Car(){ + cout << "Car 析构函数!" << endl; + } +public: + string mName; +}; + +//拖拉机 +class Tractor{ +public: + Tractor(){ + cout << "Tractor 默认构造函数!" << endl; + mName = "爬土坡专用拖拉机"; + } + Tractor(string name){ + cout << "Tractor 带参数构造函数!" << endl; + mName = name; + } + ~Tractor(){ + cout << "Tractor 析构函数!" << endl; + } +public: + string mName; +}; + +//人类 +class Person{ +public: +#if 1 + //类mCar不存在合适的构造函数 + Person(string name){ + mName = name; + } +#else + //初始化列表可以指定调用构造函数 + Person(string carName, string tracName, string name) : mTractor(tracName), mCar(carName), mName(name){ + cout << "Person 构造函数!" << endl; + } +#endif + + void GoWorkByCar(){ + cout << mName << "开着" << mCar.mName << "去上班!" << endl; + } + void GoWorkByTractor(){ + cout << mName << "开着" << mTractor.mName << "去上班!" << endl; + } + ~Person(){ + cout << "Person 析构函数!" << endl; + } +private: + string mName; + Car mCar; + Tractor mTractor; +}; + +void test(){ + //Person person("宝马", "东风拖拉机", "赵四"); + Person person("刘能"); + person.GoWorkByCar(); + person.GoWorkByTractor(); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/const_static_member.cpp b/p01/ch04/s03/const_static_member.cpp new file mode 100644 index 0000000..ea41b80 --- /dev/null +++ b/p01/ch04/s03/const_static_member.cpp @@ -0,0 +1,26 @@ +// ============================================================================ +// p1c04_03_16 — 4.3.10.3 const静态成员属性 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:15] 4.3.10.3 const静态成员属性 --- +class Person{ +public: + //static const int mShare = 10; + const static int mShare = 10; //只读区,不可修改 +}; +int main(){ + + cout << Person::mShare << endl; + //Person::mShare = 20; + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s03/copy_ctor_timing.cpp b/p01/ch04/s03/copy_ctor_timing.cpp new file mode 100644 index 0000000..f762cf4 --- /dev/null +++ b/p01/ch04/s03/copy_ctor_timing.cpp @@ -0,0 +1,69 @@ +// ============================================================================ +// p1c04_03_03 — 4.3.4 拷贝构造函数的调用时机 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:2] 4.3.4 拷贝构造函数的调用时机 --- +class Person{ +public: + Person(){ + cout << "no param contructor!" << endl; + mAge = 10; + } + Person(int age){ + cout << "param constructor!" << endl; + mAge = age; + } + Person(const Person& person){ + cout << "copy constructor!" << endl; + mAge = person.mAge; + } + ~Person(){ + cout << "destructor!" << endl; + } +public: + int mAge; +}; +//1. 旧对象初始化新对象 +void test01(){ + + Person p(10); + Person p1(p); + Person p2 = Person(p); + Person p3 = p; // 相当于Person p2 = Person(p); +} + +//2. 传递的参数是普通对象,函数参数也是普通对象,传递将会调用拷贝构造 +void doBussiness(Person p){} + +void test02(){ + Person p(10); + doBussiness(p); +} + +//3. 函数返回局部对象 +Person MyBusiness(){ + Person p(10); + cout << "局部p:" << (int*)&p << endl; + return p; +} +void test03(){ + //vs release、qt下没有调用拷贝构造函数 + //vs debug下调用一次拷贝构造函数 + Person p = MyBusiness(); + cout << "局部p:" << (int*)&p << endl; +} + +// --- main --- +int main() { + test01(); + test03(); + return 0; +} diff --git a/p01/ch04/s03/ctor_and_dtor.cpp b/p01/ch04/s03/ctor_and_dtor.cpp new file mode 100644 index 0000000..b36984c --- /dev/null +++ b/p01/ch04/s03/ctor_and_dtor.cpp @@ -0,0 +1,45 @@ +// ============================================================================ +// p1c04_03_01 — 4.3.2 构造函数和析构函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:0] 4.3.2 构造函数和析构函数 --- +class Person{ +public: + Person(){ + cout << "构造函数调用!" << endl; + pName = (char*)malloc(sizeof("John")); + strcpy(pName, "John"); + mTall = 150; + mMoney = 100; + } + ~Person(){ + cout << "析构函数调用!" << endl; + if (pName != NULL){ + free(pName); + pName = NULL; + } + } +public: + char* pName; + int mTall; + int mMoney; +}; + +void test(){ + Person person; + cout << person.pName << person.mTall << person.mMoney << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/ctor_classification.cpp b/p01/ch04/s03/ctor_classification.cpp new file mode 100644 index 0000000..8608d6e --- /dev/null +++ b/p01/ch04/s03/ctor_classification.cpp @@ -0,0 +1,83 @@ +// ============================================================================ +// p1c04_03_02 — 4.3.3 构造函数的分类及调用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:1] 4.3.3 构造函数的分类及调用 --- +class Person{ +public: + Person(){ + cout << "no param constructor!" << endl; + mAge = 0; + } + //有参构造函数 + Person(int age){ + cout << "1 param constructor!" << endl; + mAge = age; + } + //拷贝构造函数(复制构造函数) 使用另一个对象初始化本对象 + Person(const Person& person){ + cout << "copy constructor!" << endl; + mAge = person.mAge; + } + //打印年龄 + void PrintPerson(){ + cout << "Age:" << mAge << endl; + } +private: + int mAge; +}; +//1. 无参构造调用方式 +void test01(){ + + //调用无参构造函数 + Person person1; + person1.PrintPerson(); + + //无参构造函数错误调用方式 + //Person person2(); + //person2.PrintPerson(); +} +//2. 调用有参构造函数 +void test02(){ + + //第一种 括号法,最常用 + Person person01(100); + person01.PrintPerson(); + + //调用拷贝构造函数 + Person person02(person01); + person02.PrintPerson(); + + //第二种 匿名对象(显示调用构造函数) + Person(200); //匿名对象,没有名字的对象 + + Person person03 = Person(300); + person03.PrintPerson(); + + //注意: 使用匿名对象初始化判断调用哪一个构造函数,要看匿名对象的参数类型 + Person person06(Person(400)); //等价于 Person person06 = Person(400); + person06.PrintPerson(); + + //第三种 =号法 隐式转换 + Person person04 = 100; //Person person04 = Person(100) + person04.PrintPerson(); + + //调用拷贝构造 + Person person05 = person04; //Person person05 = Person(person04) + person05.PrintPerson(); +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch04/s03/deep_vs_shallow_copy.cpp b/p01/ch04/s03/deep_vs_shallow_copy.cpp new file mode 100644 index 0000000..ff5490b --- /dev/null +++ b/p01/ch04/s03/deep_vs_shallow_copy.cpp @@ -0,0 +1,47 @@ +// ============================================================================ +// p1c04_03_04 — 4.3.6 深拷贝和浅拷贝 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:3] 4.3.6 深拷贝和浅拷贝 --- +class Person{ +public: + Person(char* name,int age){ + pName = (char*)malloc(strlen(name) + 1); + strcpy(pName,name); + mAge = age; + } + //增加拷贝构造函数 + Person(const Person& person){ + pName = (char*)malloc(strlen(person.pName) + 1); + strcpy(pName, person.pName); + mAge = person.mAge; + } + ~Person(){ + if (pName != NULL){ + free(pName); + } + } +private: + char* pName; + int mAge; +}; + +void test(){ + Person p1("Edward",30); + //用对象p1初始化对象p2,调用c++提供的默认拷贝构造函数 + Person p2 = p1; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/delete_operator.cpp b/p01/ch04/s03/delete_operator.cpp new file mode 100644 index 0000000..9a382e6 --- /dev/null +++ b/p01/ch04/s03/delete_operator.cpp @@ -0,0 +1,58 @@ +// ============================================================================ +// p1c04_03_10 — 4.3.9.4 delete operator +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:9] 4.3.9.4 delete operator --- +class Person{ +public: + Person(){ + cout << "无参构造函数!" << endl; + pName = (char*)malloc(strlen("undefined") + 1); + strcpy(pName, "undefined"); + mAge = 0; + } + Person(char* name, int age){ + cout << "有参构造函数!" << endl; + pName = (char*)malloc(strlen(name) + 1); + strcpy(pName, name); + mAge = age; + } + void ShowPerson(){ + cout << "Name:" << pName << " Age:" << mAge << endl; + } + ~Person(){ + cout << "析构函数!" << endl; + if (pName != NULL){ + delete pName; + pName = NULL; + } + } +public: + char* pName; + int mAge; +}; + +void test(){ + Person* person1 = new Person; + Person* person2 = new Person("John",33); + + person1->ShowPerson(); + person2->ShowPerson(); + + delete person1; + delete person2; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/delete_void_ptr.cpp b/p01/ch04/s03/delete_void_ptr.cpp new file mode 100644 index 0000000..b560094 --- /dev/null +++ b/p01/ch04/s03/delete_void_ptr.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// p1c04_03_13 — 4.3.9.6 delete void*可能会出错 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:12] 4.3.9.6 delete void*可能会出错 --- +class Person{ +public: + Person(char* name, int age){ + pName = (char*)malloc(sizeof(name)); + strcpy(pName,name); + mAge = age; + } + ~Person(){ + if (pName != NULL){ + delete pName; + } + } +public: + char* pName; + int mAge; +}; + +void test(){ + void* person = new Person("john",20); + delete person; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/explicit_keyword.cpp b/p01/ch04/s03/explicit_keyword.cpp new file mode 100644 index 0000000..5b32807 --- /dev/null +++ b/p01/ch04/s03/explicit_keyword.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// p1c04_03_07 — 4.3.8 explicit关键字 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:6] 4.3.8 explicit关键字 --- +class MyString{ +public: + explicit MyString(int n){ + cout << "MyString(int n)!" << endl; + } + MyString(const char* str){ + cout << "MyString(const char* str)" << endl; + } +}; + +int main(){ + + //给字符串赋值?还是初始化? + //MyString str1 = 1; + MyString str2(10); + + //寓意非常明确,给字符串赋值 + MyString str3 = "abcd"; + MyString str4("abcd"); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s03/initializer_list.cpp b/p01/ch04/s03/initializer_list.cpp new file mode 100644 index 0000000..2e2bb6b --- /dev/null +++ b/p01/ch04/s03/initializer_list.cpp @@ -0,0 +1,42 @@ +// ============================================================================ +// p1c04_03_05 — 4.3.7.1 初始化列表 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:4] 4.3.7.1 初始化列表 --- +static void block_0() { + class Person{ + public: + #if 0 + //传统方式初始化 + Person(int a,int b,int c){ + mA = a; + mB = b; + mC = c; + } + #endif + //初始化列表方式初始化 + Person(int a, int b, int c):mA(a),mB(b),mC(c){} + void PrintPerson(){ + cout << "mA:" << mA << endl; + cout << "mB:" << mB << endl; + cout << "mC:" << mC << endl; + } + private: + int mA; + int mB; + int mC; + }; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch04/s03/new_delete_array.cpp b/p01/ch04/s03/new_delete_array.cpp new file mode 100644 index 0000000..926fa14 --- /dev/null +++ b/p01/ch04/s03/new_delete_array.cpp @@ -0,0 +1,31 @@ +// ============================================================================ +// p1c04_03_11 — 4.3.9.5 用于数组的new和delete +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:10] 4.3.9.5 用于数组的new和delete --- +static void block_0() { + //创建字符数组 + char* pStr = new char[100]; + //创建整型数组 + int* pArr1 = new int[100]; + //创建整型数组并初始化 + int* pArr2 = new int[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + //释放数组内存 + delete[] pStr; + delete[] pArr1; + delete[] pArr2; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch04/s03/new_delete_array_2.cpp b/p01/ch04/s03/new_delete_array_2.cpp new file mode 100644 index 0000000..0e429df --- /dev/null +++ b/p01/ch04/s03/new_delete_array_2.cpp @@ -0,0 +1,48 @@ +// ============================================================================ +// p1c04_03_12 — 4.3.9.5 用于数组的new和delete +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954451:11] 4.3.9.5 用于数组的new和delete --- +class Person{ +public: + Person(){ + pName = (char*)malloc(strlen("undefined") + 1); + strcpy(pName, "undefined"); + mAge = 0; + } + Person(char* name, int age){ + pName = (char*)malloc(sizeof(name)); + strcpy(pName, name); + mAge = age; + } + ~Person(){ + if (pName != NULL){ + delete pName; + } + } +public: + char* pName; + int mAge; +}; + +void test(){ + //栈聚合初始化 + Person person[] = { Person("john", 20), Person("Smith", 22) }; + cout << person[1].pName << endl; + //创建堆上对象数组必须提供构造函数 + Person* workers = new Person[20]; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s03/new_operator.cpp b/p01/ch04/s03/new_operator.cpp new file mode 100644 index 0000000..d092dee --- /dev/null +++ b/p01/ch04/s03/new_operator.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// p1c04_03_09 — 4.3.9.3 new operator +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:8] 4.3.9.3 new operator --- +// 原文为伪代码对照,演示 new 与 malloc 的等价关系: +// Person* person = new Person; +// 相当于: +// Person* person = (Person*)malloc(sizeof(Person)); +// if(person == NULL){ return 0; } +// person->Init(); // 构造函数 +// 下面给出可编译的对照程序: +#include +#include +#include +class Person { +public: + Person() { std::cout << "构造函数!" << std::endl; } + void Init() { std::cout << "Init" << std::endl; } +}; +int main() { + Person* person = new Person; // C++: new 自动调用构造函数 + Person* p2 = (Person*)malloc(sizeof(Person)); + if (p2) { new (p2) Person(); } // 对齐 new 的行为(placement new) + delete person; + if (p2) { p2->~Person(); free(p2); } + return 0; +} + +// --- main --- diff --git a/p01/ch04/s03/static_member_function.cpp b/p01/ch04/s03/static_member_function.cpp new file mode 100644 index 0000000..6ca2401 --- /dev/null +++ b/p01/ch04/s03/static_member_function.cpp @@ -0,0 +1,54 @@ +// ============================================================================ +// p1c04_03_15 — 4.3.10.2 静态成员函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:14] 4.3.10.2 静态成员函数 --- +class Person{ +public: + //普通成员函数可以访问static和non-static成员属性 + void changeParam1(int param){ + mParam = param; + sNum = param; + } + //静态成员函数只能访问static成员属性 + static void changeParam2(int param){ + //mParam = param; //无法访问 + sNum = param; + } +private: + static void changeParam3(int param){ + //mParam = param; //无法访问 + sNum = param; + } +public: + int mParam; + static int sNum; +}; + +//静态成员属性类外初始化 +int Person::sNum = 0; + +int main(){ + + //1. 类名直接调用 + Person::changeParam2(100); + + //2. 通过对象调用 + Person p; + p.changeParam2(200); + + //3. 静态成员函数也有访问权限 + //Person::changeParam3(100); //类外无法访问私有静态成员函数 + //Person p1; + //p1.changeParam3(200); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s03/static_member_variable.cpp b/p01/ch04/s03/static_member_variable.cpp new file mode 100644 index 0000000..729508e --- /dev/null +++ b/p01/ch04/s03/static_member_variable.cpp @@ -0,0 +1,50 @@ +// ============================================================================ +// p1c04_03_14 — 4.3.10.1 静态成员变量 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954451:13] 4.3.10.1 静态成员变量 --- +class Person{ +public: + //类的静态成员属性 + static int sNum; +private: + static int sOther; +}; + +//类外初始化,初始化时不加static +int Person::sNum = 0; +int Person::sOther = 0; +int main(){ + + + //1. 通过类名直接访问 + Person::sNum = 100; + cout << "Person::sNum:" << Person::sNum << endl; + + //2. 通过对象访问 + Person p1, p2; + p1.sNum = 200; + + cout << "p1.sNum:" << p1.sNum << endl; + cout << "p2.sNum:" << p2.sNum << endl; + + //3. 静态成员也有访问权限,类外不能访问私有成员 + //cout << "Person::sOther:" << Person::sOther << endl; + Person p3; + //cout << "p3.sOther:" << p3.sOther << endl; + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s03/static_singleton.cpp b/p01/ch04/s03/static_singleton.cpp new file mode 100644 index 0000000..a17233d --- /dev/null +++ b/p01/ch04/s03/static_singleton.cpp @@ -0,0 +1,43 @@ +// ============================================================================ +// p1c04_03_17 — 4.3.10.4 静态成员实现单例模式 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954451:16] 4.3.10.4 静态成员实现单例模式 --- +class Printer{ +public: + static Printer* getInstance(){ return pPrinter;} + void PrintText(string text){ + cout << "打印内容:" << text << endl; + cout << "已打印次数:" << mTimes << endl; + cout << "--------------" << endl; + mTimes++; + } +private: + Printer(){ mTimes = 0; } + Printer(const Printer&){} +private: + static Printer* pPrinter; + int mTimes; +}; + +Printer* Printer::pPrinter = new Printer; + +void test(){ + Printer* printer = Printer::getInstance(); + printer->PrintText("离职报告!"); + printer->PrintText("入职合同!"); + printer->PrintText("提交代码!"); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s04/CMakeLists.txt b/p01/ch04/s04/CMakeLists.txt new file mode 100644 index 0000000..b8db6e9 --- /dev/null +++ b/p01/ch04/s04/CMakeLists.txt @@ -0,0 +1,19 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s04 +add_executable(p1c04_04_01 member_storage.cpp) +set_target_properties(p1c04_04_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_04_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_04_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_04_02 this_pointer.cpp) +set_target_properties(p1c04_04_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_04_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_04_02 PRIVATE -Wall -Wextra) +add_executable(p1c04_04_03 const_member_function.cpp) +set_target_properties(p1c04_04_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_04_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_04_03 PRIVATE -Wall -Wextra) +add_executable(p1c04_04_04 const_object.cpp) +set_target_properties(p1c04_04_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_04_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_04_04 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s04 DEPENDS p1c04_04_01 p1c04_04_02 p1c04_04_03 p1c04_04_04) diff --git a/p01/ch04/s04/const_member_function.cpp b/p01/ch04/s04/const_member_function.cpp new file mode 100644 index 0000000..544c54e --- /dev/null +++ b/p01/ch04/s04/const_member_function.cpp @@ -0,0 +1,46 @@ +// ============================================================================ +// p1c04_04_03 — 4.4.2.3 const修饰成员函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954456:2] 4.4.2.3 const修饰成员函数 --- +//const修饰成员函数 +class Person{ +public: + Person(){ + this->mAge = 0; + this->mID = 0; + } + //在函数括号后面加上const,修饰成员变量不可修改,除了mutable变量 + void sonmeOperate() const{ + //this->mAge = 200; //mAge不可修改 + this->mID = 10; + } + void ShowPerson(){ + cout << "ID:" << mID << " mAge:" << mAge << endl; + } +private: + int mAge; + mutable int mID; +}; + +int main(){ + + Person person; + person.sonmeOperate(); + person.ShowPerson(); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s04/const_object.cpp b/p01/ch04/s04/const_object.cpp new file mode 100644 index 0000000..0864768 --- /dev/null +++ b/p01/ch04/s04/const_object.cpp @@ -0,0 +1,48 @@ +// ============================================================================ +// p1c04_04_04 — 4.4.2.4 const修饰对象(常对象) +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954456:3] 4.4.2.4 const修饰对象(常对象) --- +class Person{ +public: + Person(){ + this->mAge = 0; + this->mID = 0; + } + void ChangePerson() const{ + //mAge = 100; // 非法:const 成员函数内不能修改非 mutable 成员 + mID = 100; // 合法:mID 被 mutable 修饰,const 函数内可改 + } + void ShowPerson(){ + this->mAge = 1000; + cout << "ID:" << this->mID << " Age:" << this->mAge << endl; + } + +public: + int mAge; + mutable int mID; +}; + +void test(){ + const Person person; + //1. 可访问数据成员 + cout << "Age:" << person.mAge << endl; + //person.mAge = 300; //不可修改 + person.mID = 1001; //但是可以修改mutable修饰的成员变量 + //2. 只能访问const修饰的函数 + //person.ShowPerson(); + person.ChangePerson(); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s04/member_storage.cpp b/p01/ch04/s04/member_storage.cpp new file mode 100644 index 0000000..d4a2bd5 --- /dev/null +++ b/p01/ch04/s04/member_storage.cpp @@ -0,0 +1,65 @@ +// ============================================================================ +// p1c04_04_01 — 4.4.1 成员变量和函数的存储 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954456:0] 4.4.1 成员变量和函数的存储 --- +class MyClass01{ +public: + int mA; +}; + +class MyClass02{ +public: + int mA; + static int sB; +}; + +class MyClass03{ +public: + void printMyClass_1(){ + cout << "hello world!" << endl; + } +public: + int mA; + static int sB; +}; + +class MyClass04{ +public: + void printMyClass_1(){ + cout << "hello world!" << endl; + } + static void ShowMyClass(){ + cout << "hello world!" << endl; + } +public: + int mA; + static int sB; +}; + +int main(){ + + MyClass01 mclass01; + MyClass02 mclass02; + MyClass03 mclass03; + MyClass04 mclass04; + + cout << "MyClass01:" << sizeof(mclass01) << endl; //4 + //静态数据成员并不保存在类对象中 + cout << "MyClass02:" << sizeof(mclass02) << endl; //4 + //非静态成员函数不保存在类对象中 + cout << "MyClass03:" << sizeof(mclass03) << endl; //4 + //静态成员函数也不保存在类对象中 + cout << "MyClass04:" << sizeof(mclass04) << endl; //4 + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s04/this_pointer.cpp b/p01/ch04/s04/this_pointer.cpp new file mode 100644 index 0000000..fed7e8f --- /dev/null +++ b/p01/ch04/s04/this_pointer.cpp @@ -0,0 +1,71 @@ +// ============================================================================ +// p1c04_04_02 — 4.4.2.2 this指针的使用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954456:1] 4.4.2.2 this指针的使用 --- +class Person{ +public: + //1. 当形参名和成员变量名一样时,this指针可用来区分 + Person(string name,int age){ + //name = name; + //age = age; //输出错误 + this->name = name; + this->age = age; + } + //2. 返回对象本身的引用 + //重载赋值操作符 + //其实也是两个参数,其中隐藏了一个this指针 + Person PersonPlusPerson(Person& person){ + string newname = this->name + person.name; + int newage = this->age + person.age; + Person newperson(newname, newage); + return newperson; + } + void ShowPerson(){ + cout << "Name:" << name << " Age:" << age << endl; + } +public: + string name; + int age; +}; + +//3. 成员函数和全局函数(Perosn对象相加) +Person PersonPlusPerson(Person& p1,Person& p2){ + string newname = p1.name + p2.name; + int newage = p1.age + p2.age; + Person newperson(newname,newage); + return newperson; +} + +int main(){ + + Person person("John",100); + person.ShowPerson(); + + cout << "---------" << endl; + Person person1("John",20); + Person person2("001", 10); + //1.全局函数实现两个对象相加 + Person person3 = PersonPlusPerson(person1, person2); + person1.ShowPerson(); + person2.ShowPerson(); + person3.ShowPerson(); + //2. 成员函数实现两个对象相加 + Person person4 = person1.PersonPlusPerson(person2); + person4.ShowPerson(); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s05/CMakeLists.txt b/p01/ch04/s05/CMakeLists.txt new file mode 100644 index 0000000..294844d --- /dev/null +++ b/p01/ch04/s05/CMakeLists.txt @@ -0,0 +1,7 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s05 +add_executable(p1c04_05_01 friend_syntax.cpp) +set_target_properties(p1c04_05_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_05_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_05_01 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s05 DEPENDS p1c04_05_01) diff --git a/p01/ch04/s05/friend_syntax.cpp b/p01/ch04/s05/friend_syntax.cpp new file mode 100644 index 0000000..bcf41f1 --- /dev/null +++ b/p01/ch04/s05/friend_syntax.cpp @@ -0,0 +1,73 @@ +// ============================================================================ +// p1c04_05_01 — 4.5.1 友元语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954457:0] 4.5.1 友元语法 --- +class Building; +//友元类 +class MyFriend{ +public: + //友元成员函数 + void LookAtBedRoom(Building& building); + void PlayInBedRoom(Building& building); +}; +class Building{ + //全局函数做友元函数 + friend void CleanBedRoom(Building& building); +#if 0 + //成员函数做友元函数 + friend void MyFriend::LookAtBedRoom(Building& building); + friend void MyFriend::PlayInBedRoom(Building& building); +#else + //友元类 + friend class MyFriend; +#endif +public: + Building(); +public: + string mSittingRoom; +private: + string mBedroom; +}; + +void MyFriend::LookAtBedRoom(Building& building){ + cout << "我的朋友参观" << building.mBedroom << endl; +} +void MyFriend::PlayInBedRoom(Building& building){ + cout << "我的朋友玩耍在" << building.mBedroom << endl; +} + +//友元全局函数 +void CleanBedRoom(Building& building){ + cout << "友元全局函数访问" << building.mBedroom << endl; +} + +Building::Building(){ + this->mSittingRoom = "客厅"; + this->mBedroom = "卧室"; +} + +int main(){ + + Building building; + MyFriend myfriend; + + CleanBedRoom(building); + myfriend.LookAtBedRoom(building); + myfriend.PlayInBedRoom(building); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s06/CMakeLists.txt b/p01/ch04/s06/CMakeLists.txt new file mode 100644 index 0000000..7323b11 --- /dev/null +++ b/p01/ch04/s06/CMakeLists.txt @@ -0,0 +1,27 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s06 +add_executable(p1c04_06_01 overload_with_friend.cpp) +set_target_properties(p1c04_06_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_06_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_06_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_06_02 overload_increment_decrement.cpp) +set_target_properties(p1c04_06_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_06_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_06_02 PRIVATE -Wall -Wextra) +add_executable(p1c04_06_03 overload_pointer_operator.cpp) +set_target_properties(p1c04_06_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_06_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_06_03 PRIVATE -Wall -Wextra) +add_executable(p1c04_06_04 overload_assignment.cpp) +set_target_properties(p1c04_06_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_06_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_06_04 PRIVATE -Wall -Wextra) +add_executable(p1c04_06_05 overload_equality.cpp) +set_target_properties(p1c04_06_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_06_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_06_05 PRIVATE -Wall -Wextra) +add_executable(p1c04_06_06 overload_function_call.cpp) +set_target_properties(p1c04_06_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_06_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_06_06 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s06 DEPENDS p1c04_06_01 p1c04_06_02 p1c04_06_03 p1c04_06_04 p1c04_06_05 p1c04_06_06) diff --git a/p01/ch04/s06/overload_assignment.cpp b/p01/ch04/s06/overload_assignment.cpp new file mode 100644 index 0000000..292a84b --- /dev/null +++ b/p01/ch04/s06/overload_assignment.cpp @@ -0,0 +1,114 @@ +// ============================================================================ +// p1c04_06_04 — 4.6.6 赋值(=)运算符重载 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954458:3] 4.6.6 赋值(=)运算符重载 --- +class Person{ + friend ostream& operator<<(ostream& os,const Person& person){ + os << "ID:" << person.mID << " Age:" << person.mAge << endl; + return os; + } +public: + Person(int id,int age){ + this->mID = id; + this->mAge = age; + } + //重载赋值运算符 + Person& operator=(const Person& person){ + this->mID = person.mID; + this->mAge = person.mAge; + return *this; + } +private: + int mID; + int mAge; +}; + +//1. =号混淆的地方 +void test01(){ + Person person1(10, 20); + Person person2 = person1; //调用拷贝构造 + //如果一个对象还没有被创建,则必须初始化,也就是调用构造函数 + //上述例子由于person2还没有初始化,所以会调用构造函数 + //由于person2是从已有的person1来创建的,所以只有一个选择 + //就是调用拷贝构造函数 + person2 = person1; //调用operator=函数 + //由于person2已经创建,不需要再调用构造函数,这时候调用的是重载的赋值运算符 +} +//2. 赋值重载案例 +void test02(){ + Person person1(20, 20); + Person person2(30, 30); + cout << "person1:" << person1; + cout << "person2:" << person2; + person2 = person1; + cout << "person2:" << person2; +} +//常见错误,当准备给两个相同对象赋值时,应该首先检查一下这个对象是否对自身赋值了 +//对于本例来讲,无论如何执行这些赋值运算都是无害的,但如果对类的实现进行修改,那么将会出现差异; +//3. 类中指针 +class Person2{ + friend ostream& operator<<(ostream& os, const Person2& person){ + os << "Name:" << person.pName << " ID:" << person.mID << " Age:" << person.mAge << endl; + return os; + } +public: + Person2(char* name,int id, int age){ + this->pName = new char[strlen(name) + 1]; + strcpy(this->pName, name); + this->mID = id; + this->mAge = age; + } +#if 1 + //重载赋值运算符 + Person2& operator=(const Person2& person){ + + //注意:由于当前对象已经创建完毕,那么就有可能pName指向堆内存 + //这个时候如果直接赋值,会导致内存没有及时释放 + if (this->pName != NULL){ + delete[] this->pName; + } + + this->pName = new char[strlen(person.pName) + 1]; + strcpy(this->pName,person.pName); + this->mID = person.mID; + this->mAge = person.mAge; + return *this; + } +#endif + //析构函数 + ~Person2(){ + if (this->pName != NULL){ + delete[] this->pName; + } + } +private: + char* pName; + int mID; + int mAge; +}; + +void test03(){ + Person2 person1("John",20, 20); + Person2 person2("Edward",30, 30); + cout << "person1:" << person1; + cout << "person2:" << person2; + person2 = person1; + cout << "person2:" << person2; +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch04/s06/overload_equality.cpp b/p01/ch04/s06/overload_equality.cpp new file mode 100644 index 0000000..9a61243 --- /dev/null +++ b/p01/ch04/s06/overload_equality.cpp @@ -0,0 +1,61 @@ +// ============================================================================ +// p1c04_06_05 — 4.6.7 等于和不等于(==、!=)运算符重载 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954458:4] 4.6.7 等于和不等于(==、!=)运算符重载 --- +class Complex{ +public: + Complex(char* name,int id,int age){ + this->pName = new char[strlen(name) + 1]; + strcpy(this->pName, name); + this->mID = id; + this->mAge = age; + } + //重载==号操作符 + bool operator==(const Complex& complex){ + if (strcmp(this->pName,complex.pName) == 0 && + this->mID == complex.mID && + this->mAge == complex.mAge){ + return true; + } + return false; + } + //重载!=操作符 + bool operator!=(const Complex& complex){ + if (strcmp(this->pName, complex.pName) != 0 || + this->mID != complex.mID || + this->mAge != complex.mAge){ + return true; + } + return false; + } + ~Complex(){ + if (this->pName != NULL){ + delete[] this->pName; + } + } +private: + char* pName; + int mID; + int mAge; +}; +void test(){ + Complex complex1("aaa", 10, 20); + Complex complex2("bbb", 10, 20); + if (complex1 == complex2){ cout << "相等!" << endl; } + if (complex1 != complex2){ cout << "不相等!" << endl; } +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s06/overload_function_call.cpp b/p01/ch04/s06/overload_function_call.cpp new file mode 100644 index 0000000..63087de --- /dev/null +++ b/p01/ch04/s06/overload_function_call.cpp @@ -0,0 +1,33 @@ +// ============================================================================ +// p1c04_06_06 — 4.6.8 函数调用符号()重载 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954458:5] 4.6.8 函数调用符号()重载 --- +class Complex{ +public: + int Add(int x,int y){ + return x + y; + } + int operator()(int x,int y){ + return x + y; + } +}; +void test01(){ + Complex complex; + cout << complex.Add(10,20) << endl; + //对象当做函数来调用 + cout << complex(10, 20) << endl; +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch04/s06/overload_increment_decrement.cpp b/p01/ch04/s06/overload_increment_decrement.cpp new file mode 100644 index 0000000..5beba7e --- /dev/null +++ b/p01/ch04/s06/overload_increment_decrement.cpp @@ -0,0 +1,85 @@ +// ============================================================================ +// p1c04_06_02 — 4.6.4 自增自减(++/--)运算符重载 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954458:1] 4.6.4 自增自减(++/--)运算符重载 --- +class Complex{ + friend ostream& operator<<(ostream& os,Complex& complex){ + os << "A:" << complex.mA << " B:" << complex.mB << endl; + return os; + } +public: + Complex(){ + mA = 0; + mB = 0; + } + //重载前置++ + Complex& operator++(){ + mA++; + mB++; + return *this; + } + //重载后置++ + Complex operator++(int){ + Complex temp; + temp.mA = this->mA; + temp.mB = this->mB; + mA++; + mB++; + return temp; + } + //前置-- + Complex& operator--(){ + mA--; + mB--; + return *this; + } + //后置-- + Complex operator--(int){ + Complex temp; + temp.mA = mA; + temp.mB = mB; + mA--; + mB--; + return temp; + } + void ShowComplex(){ + cout << "A:" << mA << " B:" << mB << endl; + } +private: + int mA; + int mB; +}; + +void test(){ + Complex complex; + complex++; + cout << complex; + ++complex; + cout << complex; + + Complex ret = complex++; + cout << ret; + cout << complex; + + cout << "------" << endl; + ret--; + --ret; + cout << "ret:" << ret; + complex--; + --complex; + cout << "complex:" << complex; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s06/overload_pointer_operator.cpp b/p01/ch04/s06/overload_pointer_operator.cpp new file mode 100644 index 0000000..733ffe0 --- /dev/null +++ b/p01/ch04/s06/overload_pointer_operator.cpp @@ -0,0 +1,59 @@ +// ============================================================================ +// p1c04_06_03 — 4.6.5 指针运算符(*、->)重载 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954458:2] 4.6.5 指针运算符(*、->)重载 --- +class Person{ +public: + Person(int param){ + this->mParam = param; + } + void PrintPerson(){ + cout << "Param:" << mParam << endl; + } +private: + int mParam; +}; + +class SmartPointer{ +public: + SmartPointer(Person* person){ + this->pPerson = person; + } + //重载指针的->、*操作符 + Person* operator->(){ + return pPerson; + } + Person& operator*(){ + return *pPerson; + } + ~SmartPointer(){ + if (pPerson != NULL){ + delete pPerson; + } + } +public: + Person* pPerson; +}; + +void test01(){ + + //Person* person = new Person(100); + //如果忘记释放,那么就会造成内存泄漏 + + SmartPointer pointer(new Person(100)); + pointer->PrintPerson(); +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch04/s06/overload_with_friend.cpp b/p01/ch04/s06/overload_with_friend.cpp new file mode 100644 index 0000000..b84bea7 --- /dev/null +++ b/p01/ch04/s06/overload_with_friend.cpp @@ -0,0 +1,39 @@ +// ============================================================================ +// p1c04_06_01 — 4.6.2 运算符重载碰上友元函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954458:0] 4.6.2 运算符重载碰上友元函数 --- +class Person{ + friend ostream& operator<<(ostream& os, Person& person); +public: + Person(int id,int age){ + mID = id; + mAge = age; + } +private: + int mID; + int mAge; +}; + +ostream& operator<<(ostream& os, Person& person){ + os << "ID:" << person.mID << " Age:" << person.mAge; + return os; +} + +int main(){ + + Person person(1001, 30); + //cout << person; //cout.operator+(person) + cout << person << " | " << endl; + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/CMakeLists.txt b/p01/ch04/s07/CMakeLists.txt new file mode 100644 index 0000000..678596e --- /dev/null +++ b/p01/ch04/s07/CMakeLists.txt @@ -0,0 +1,55 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s07 +add_executable(p1c04_07_01 why_inheritance.cpp) +set_target_properties(p1c04_07_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_02 derived_access_control.cpp) +set_target_properties(p1c04_07_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_02 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_03 inheritance_object_model.cpp) +set_target_properties(p1c04_07_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_03 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_04 inheritance_ctor_dtor_order.cpp) +set_target_properties(p1c04_07_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_04 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_05 inheritance_ctor_dtor_order_2.cpp) +set_target_properties(p1c04_07_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_05 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_06 same_name_members.cpp) +set_target_properties(p1c04_07_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_06 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_07 same_name_members_2.cpp) +set_target_properties(p1c04_07_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_07 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_08 static_in_inheritance.cpp) +set_target_properties(p1c04_07_08 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_08 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_08 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_09 multiple_inheritance_concept.cpp) +set_target_properties(p1c04_07_09 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_09 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_09 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_10 diamond_virtual_inheritance.cpp) +set_target_properties(p1c04_07_10 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_10 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_10 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_11 diamond_virtual_inheritance_2.cpp) +set_target_properties(p1c04_07_11 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_11 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_11 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_12 virtual_inheritance_mechanism.cpp) +set_target_properties(p1c04_07_12 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_12 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_12 PRIVATE -Wall -Wextra) +add_executable(p1c04_07_13 virtual_inheritance_mechanism_2.cpp) +set_target_properties(p1c04_07_13 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_07_13 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_07_13 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s07 DEPENDS p1c04_07_01 p1c04_07_02 p1c04_07_03 p1c04_07_04 p1c04_07_05 p1c04_07_06 p1c04_07_07 p1c04_07_08 p1c04_07_09 p1c04_07_10 p1c04_07_11 p1c04_07_12 p1c04_07_13) diff --git a/p01/ch04/s07/derived_access_control.cpp b/p01/ch04/s07/derived_access_control.cpp new file mode 100644 index 0000000..52c95ea --- /dev/null +++ b/p01/ch04/s07/derived_access_control.cpp @@ -0,0 +1,98 @@ +// ============================================================================ +// p1c04_07_02 — 4.7.2 派生类访问控制 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:1] 4.7.2 派生类访问控制 --- +//基类 +class A{ +public: + int mA; +protected: + int mB; +private: + int mC; +}; + +//1. 公有(public)继承 +class B : public A{ +public: + void PrintB(){ + cout << mA << endl; //可访问基类public属性 + cout << mB << endl; //可访问基类protected属性 + //cout << mC << endl; //不可访问基类private属性 + } +}; +class SubB : public B{ + void PrintSubB(){ + cout << mA << endl; //可访问基类public属性 + cout << mB << endl; //可访问基类protected属性 + //cout << mC << endl; //不可访问基类private属性 + } +}; +void test01(){ + + B b; + cout << b.mA << endl; //可访问基类public属性 + //cout << b.mB << endl; //不可访问基类protected属性 + //cout << b.mC << endl; //不可访问基类private属性 +} + +//2. 私有(private)继承 +class C : private A{ +public: + void PrintC(){ + cout << mA << endl; //可访问基类public属性 + cout << mB << endl; //可访问基类protected属性 + //cout << mC << endl; //不可访问基类private属性 + } +}; +class SubC : public C{ + void PrintSubC(){ + //cout << mA << endl; //不可访问基类public属性 + //cout << mB << endl; //不可访问基类protected属性 + //cout << mC << endl; //不可访问基类private属性 + } +}; +void test02(){ + C c; + //cout << c.mA << endl; //不可访问基类public属性 + //cout << c.mB << endl; //不可访问基类protected属性 + //cout << c.mC << endl; //不可访问基类private属性 +} +//3. 保护(protected)继承 +class D : protected A{ +public: + void PrintD(){ + cout << mA << endl; //可访问基类public属性 + cout << mB << endl; //可访问基类protected属性 + //cout << mC << endl; //不可访问基类private属性 + } +}; +class SubD : public D{ + void PrintD(){ + cout << mA << endl; //可访问基类public属性 + cout << mB << endl; //可访问基类protected属性 + //cout << mC << endl; //不可访问基类private属性 + } +}; +void test03(){ + D d; + //cout << d.mA << endl; //不可访问基类public属性 + //cout << d.mB << endl; //不可访问基类protected属性 + //cout << d.mC << endl; //不可访问基类private属性 +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch04/s07/diamond_virtual_inheritance.cpp b/p01/ch04/s07/diamond_virtual_inheritance.cpp new file mode 100644 index 0000000..d210d58 --- /dev/null +++ b/p01/ch04/s07/diamond_virtual_inheritance.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// p1c04_07_10 — 4.7.6.2 菱形继承和虚继承 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:9] 4.7.6.2 菱形继承和虚继承 --- +class BigBase{ +public: + BigBase(){ mParam = 0; } + void func(){ cout << "BigBase::func" << endl; } +public: + int mParam; +}; + +class Base1 : public BigBase{}; +class Base2 : public BigBase{}; +class Derived : public Base1, public Base2{}; + +int main(){ + + Derived derived; + //1. 对"func"的访问不明确 + //derived.func(); + //cout << derived.mParam << endl; + cout << "derived.Base1::mParam:" << derived.Base1::mParam << endl; + cout << "derived.Base2::mParam:" << derived.Base2::mParam << endl; + + //2. 重复继承 + cout << "Derived size:" << sizeof(Derived) << endl; //8 + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/diamond_virtual_inheritance_2.cpp b/p01/ch04/s07/diamond_virtual_inheritance_2.cpp new file mode 100644 index 0000000..08b9245 --- /dev/null +++ b/p01/ch04/s07/diamond_virtual_inheritance_2.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// p1c04_07_11 — 4.7.6.2 菱形继承和虚继承 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:10] 4.7.6.2 菱形继承和虚继承 --- +class BigBase{ +public: + BigBase(){ mParam = 0; } + void func(){ cout << "BigBase::func" << endl; } +public: + int mParam; +}; + +class Base1 : virtual public BigBase{}; +class Base2 : virtual public BigBase{}; +class Derived : public Base1, public Base2{}; + +int main(){ + + Derived derived; + //二义性问题解决 + derived.func(); + cout << derived.mParam << endl; + //输出结果:12 + cout << "Derived size:" << sizeof(Derived) << endl; + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/inheritance_ctor_dtor_order.cpp b/p01/ch04/s07/inheritance_ctor_dtor_order.cpp new file mode 100644 index 0000000..fcaab0d --- /dev/null +++ b/p01/ch04/s07/inheritance_ctor_dtor_order.cpp @@ -0,0 +1,51 @@ +// ============================================================================ +// p1c04_07_04 — 4.7.3.2 对象构造和析构的调用原则 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:3] 4.7.3.2 对象构造和析构的调用原则 --- +class A{ +public: + A(){ + cout << "A类构造函数!" << endl; + } + ~A(){ + cout << "A类析构函数!" << endl; + } +}; + +class B : public A{ +public: + B(){ + cout << "B类构造函数!" << endl; + } + ~B(){ + cout << "B类析构函数!" << endl; + } +}; + +class C : public B{ +public: + C(){ + cout << "C类构造函数!" << endl; + } + ~C(){ + cout << "C类析构函数!" << endl; + } +}; + +void test(){ + C c; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s07/inheritance_ctor_dtor_order_2.cpp b/p01/ch04/s07/inheritance_ctor_dtor_order_2.cpp new file mode 100644 index 0000000..610591c --- /dev/null +++ b/p01/ch04/s07/inheritance_ctor_dtor_order_2.cpp @@ -0,0 +1,59 @@ +// ============================================================================ +// p1c04_07_05 — 4.7.3.2 对象构造和析构的调用原则 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:4] 4.7.3.2 对象构造和析构的调用原则 --- +class D{ +public: + D(){ + cout << "D类构造函数!" << endl; + } + ~D(){ + cout << "D类析构函数!" << endl; + } +}; +class A{ +public: + A(){ + cout << "A类构造函数!" << endl; + } + ~A(){ + cout << "A类析构函数!" << endl; + } +}; +class B : public A{ +public: + B(){ + cout << "B类构造函数!" << endl; + } + ~B(){ + cout << "B类析构函数!" << endl; + } +}; +class C : public B{ +public: + C(){ + cout << "C类构造函数!" << endl; + } + ~C(){ + cout << "C类析构函数!" << endl; + } +public: + D c; +}; +void test(){ + C c; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s07/inheritance_object_model.cpp b/p01/ch04/s07/inheritance_object_model.cpp new file mode 100644 index 0000000..c0b4f55 --- /dev/null +++ b/p01/ch04/s07/inheritance_object_model.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// p1c04_07_03 — 4.7.3.1 继承中的对象模型 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:2] 4.7.3.1 继承中的对象模型 --- +class Aclass{ +public: + int mA; + int mB; +}; +class Bclass : public Aclass{ +public: + int mC; +}; +class Cclass : public Bclass{ +public: + int mD; +}; +void test(){ + cout << "A size:" << sizeof(Aclass) << endl; + cout << "B size:" << sizeof(Bclass) << endl; + cout << "C size:" << sizeof(Cclass) << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s07/multiple_inheritance_concept.cpp b/p01/ch04/s07/multiple_inheritance_concept.cpp new file mode 100644 index 0000000..710c945 --- /dev/null +++ b/p01/ch04/s07/multiple_inheritance_concept.cpp @@ -0,0 +1,38 @@ +// ============================================================================ +// p1c04_07_09 — 4.7.6.1 多继承概念 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:8] 4.7.6.1 多继承概念 --- +class Base1{ +public: + void func1_1(){ cout << "Base1::func1_1" << endl; } +}; +class Base2{ +public: + void func1_1(){ cout << "Base2::func1_1" << endl; } + void func2(){ cout << "Base2::func2" << endl; } +}; +//派生类继承Base1、Base2 +class Derived : public Base1, public Base2{}; +int main(){ + + Derived derived; + //func1是从Base1继承来的还是从Base2继承来的? + //derived.func1_1(); + derived.func2(); + + //解决歧义:显示指定调用那个基类的func1 + derived.Base1::func1_1(); + derived.Base2::func1_1(); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/same_name_members.cpp b/p01/ch04/s07/same_name_members.cpp new file mode 100644 index 0000000..cc47c8d --- /dev/null +++ b/p01/ch04/s07/same_name_members.cpp @@ -0,0 +1,48 @@ +// ============================================================================ +// p1c04_07_06 — 4.7.3 继承中同名成员的处理方法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:5] 4.7.3 继承中同名成员的处理方法 --- +class Base{ +public: + Base():mParam(0){} + void Print(){ cout << mParam << endl; } +public: + int mParam; +}; + +class Derived : public Base{ +public: + Derived():mParam(10){} + void Print(){ + //在派生类中使用和基类的同名成员,显示使用类名限定符 + cout << Base::mParam << endl; + cout << mParam << endl; + } + //返回基类重名成员 + int& getBaseParam(){ return Base::mParam; } +public: + int mParam; +}; + +int main(){ + + Derived derived; + //派生类和基类成员属性重名,子类访问成员默认是子类成员 + cout << derived.mParam << endl; //10 + derived.Print(); + //类外如何获得基类重名成员属性 + derived.getBaseParam() = 100; + cout << "Base:mParam:" << derived.getBaseParam() << endl; + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/same_name_members_2.cpp b/p01/ch04/s07/same_name_members_2.cpp new file mode 100644 index 0000000..ab96642 --- /dev/null +++ b/p01/ch04/s07/same_name_members_2.cpp @@ -0,0 +1,67 @@ +// ============================================================================ +// p1c04_07_07 — 4.7.3 继承中同名成员的处理方法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:6] 4.7.3 继承中同名成员的处理方法 --- +class Base{ +public: + void func1(){ + cout << "Base::void func1()" << endl; + }; + void func1(int param){ + cout << "Base::void func1(int param)" << endl; + } + void myfunc(){ + cout << "Base::void myfunc()" << endl; + } +}; +class Derived1 : public Base{ +public: + void myfunc(){ + cout << "Derived1::void myfunc()" << endl; + } +}; +class Derived2 : public Base{ +public: + //改变成员函数的参数列表 + void func1(int param1, int param2){ + cout << "Derived2::void func1(int param1,int param2)" << endl; + }; +}; +class Derived3 : public Base{ +public: + //改变成员函数的返回值 + int func1(int param){ + cout << "Derived3::int func1(int param)" << endl; + return 0; + } +}; +int main(){ + + Derived1 derived1; + derived1.func1(); + derived1.func1(20); + derived1.myfunc(); + cout << "-------------" << endl; + Derived2 derived2; + //derived2.func1(); //func1被隐藏 + //derived2.func1(20); //func2被隐藏 + derived2.func1(10,20); //重载func1之后,基类的函数被隐藏 + derived2.myfunc(); + cout << "-------------" << endl; + Derived3 derived3; + //derived3.func1(); 没有重新定义的重载版本被隐藏 + derived3.func1(20); + derived3.myfunc(); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/static_in_inheritance.cpp b/p01/ch04/s07/static_in_inheritance.cpp new file mode 100644 index 0000000..35bc8df --- /dev/null +++ b/p01/ch04/s07/static_in_inheritance.cpp @@ -0,0 +1,44 @@ +// ============================================================================ +// p1c04_07_08 — 4.7.5 继承中的静态成员特性 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:7] 4.7.5 继承中的静态成员特性 --- +class Base{ +public: + static int getNum(){ return sNum; } + static int getNum(int param){ + return sNum + param; + } +public: + static int sNum; +}; +int Base::sNum = 10; + +class Derived : public Base{ +public: + static int sNum; //基类静态成员属性将被隐藏 +#if 0 + //重定义一个函数,基类中重载的函数被隐藏 + static int getNum(int param1, int param2){ + return sNum + param1 + param2; + } +#else + //改变基类函数的某个特征,返回值或者参数个数,将会隐藏基类重载的函数 + static void getNum(int param1, int param2){ + cout << sNum + param1 + param2 << endl; + } +#endif +}; +int Derived::sNum = 20; + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch04/s07/virtual_inheritance_mechanism.cpp b/p01/ch04/s07/virtual_inheritance_mechanism.cpp new file mode 100644 index 0000000..c594fb3 --- /dev/null +++ b/p01/ch04/s07/virtual_inheritance_mechanism.cpp @@ -0,0 +1,31 @@ +// ============================================================================ +// p1c04_07_12 — 4.7.6.3 虚继承实现原理 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:11] 4.7.6.3 虚继承实现原理 --- +class BigBase{ +public: + BigBase(){ mParam = 0; } + void func(){ cout << "BigBase::func" << endl; } +public: int mParam; +}; +#if 0 //虚继承 +class Base1 : virtual public BigBase{}; +class Base2 : virtual public BigBase{}; +#else //普通继承 +class Base1 : public BigBase{}; +class Base2 : public BigBase{}; +#endif +class Derived : public Base1, public Base2{}; + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch04/s07/virtual_inheritance_mechanism_2.cpp b/p01/ch04/s07/virtual_inheritance_mechanism_2.cpp new file mode 100644 index 0000000..4b8c3ea --- /dev/null +++ b/p01/ch04/s07/virtual_inheritance_mechanism_2.cpp @@ -0,0 +1,39 @@ +// ============================================================================ +// p1c04_07_13 — 4.7.6.3 虚继承实现原理 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:12] 4.7.6.3 虚继承实现原理 --- +class BigBase{ +public: + BigBase(int x){mParam = x;} + void func(){cout << "BigBase::func" << endl;} +public: + int mParam; +}; +class Base1 : virtual public BigBase{ +public: + Base1() :BigBase(10){} //不调用BigBase构造 +}; +class Base2 : virtual public BigBase{ +public: + Base2() :BigBase(10){} //不调用BigBase构造 +}; + +class Derived : public Base1, public Base2{ +public: + Derived() :BigBase(10){} //调用BigBase构造 +}; +//每一次继承子类中都必须书写初始化语句 +int main(){ + Derived derived; + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s07/why_inheritance.cpp b/p01/ch04/s07/why_inheritance.cpp new file mode 100644 index 0000000..6e2d2f2 --- /dev/null +++ b/p01/ch04/s07/why_inheritance.cpp @@ -0,0 +1,91 @@ +// ============================================================================ +// p1c04_07_01 — 4.7.1.1 为什么需要继承 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954461:0] 4.7.1.1 为什么需要继承 --- +// 网页类(原文小标题,转为注释) +class IndexPage{ +public: + //网页头部 + void Header(){ + cout << "网页头部!" << endl; + } + //网页左侧菜单 + void LeftNavigation(){ + cout << "左侧导航菜单!" << endl; + } + //网页主体部分 + void MainBody(){ + cout << "首页网页主题内容!" << endl; + } + //网页底部 + void Footer(){ + cout << "网页底部!" << endl; + } +private: + string mTitle; //网页标题 +}; + +#if 0 +//如果不使用继承,那么定义新闻页类,需要重新写一遍已经有的代码 +class NewsPage{ +public: + //网页头部 + void Header(){ + cout << "网页头部!" << endl; + } + //网页左侧菜单 + void LeftNavigation(){ + cout << "左侧导航菜单!" << endl; + } + //网页主体部分 + void MainBody(){ + cout << "新闻网页主体内容!" << endl; + } + //网页底部 + void Footer(){ + cout << "网页底部!" << endl; + } +private: + string mTitle; //网页标题 +}; + +void test_1(){ + NewsPage* newspage = new NewsPage; + newspage->Header(); + newspage->MainBody(); + newspage->LeftNavigation(); + newspage->Footer(); +} +#else +//使用继承,可以复用已有的代码,新闻业除了主体部分不一样,其他都是一样的 +class NewsPage : public IndexPage{ +public: + //网页主体部分 + void MainBody(){ + cout << "新闻网页主主体内容!" << endl; + } +}; +void test_1(){ + NewsPage* newspage = new NewsPage; + newspage->Header(); + newspage->MainBody(); + newspage->LeftNavigation(); + newspage->Footer(); +} +#endif +int main(){ + + test_1(); + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch04/s08/CMakeLists.txt b/p01/ch04/s08/CMakeLists.txt new file mode 100644 index 0000000..f5180b4 --- /dev/null +++ b/p01/ch04/s08/CMakeLists.txt @@ -0,0 +1,31 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch04/s08 +add_executable(p1c04_08_01 polymorphism_basics.cpp) +set_target_properties(p1c04_08_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_01 PRIVATE -Wall -Wextra) +add_executable(p1c04_08_02 upcast_problem.cpp) +set_target_properties(p1c04_08_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_02 PRIVATE -Wall -Wextra) +add_executable(p1c04_08_03 virtual_function_solution.cpp) +set_target_properties(p1c04_08_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_03 PRIVATE -Wall -Wextra) +add_executable(p1c04_08_04 dynamic_binding.cpp) +set_target_properties(p1c04_08_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_04 PRIVATE -Wall -Wextra) +add_executable(p1c04_08_05 virtual_dtor_purpose.cpp) +set_target_properties(p1c04_08_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_05 PRIVATE -Wall -Wextra) +add_executable(p1c04_08_06 pure_virtual_dtor.cpp) +set_target_properties(p1c04_08_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_06 PRIVATE -Wall -Wextra) +add_executable(p1c04_08_07 override_overload_redefine.cpp) +set_target_properties(p1c04_08_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c04_08_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c04_08_07 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch04_s08 DEPENDS p1c04_08_01 p1c04_08_02 p1c04_08_03 p1c04_08_04 p1c04_08_05 p1c04_08_06 p1c04_08_07) diff --git a/p01/ch04/s08/dynamic_binding.cpp b/p01/ch04/s08/dynamic_binding.cpp new file mode 100644 index 0000000..4d47146 --- /dev/null +++ b/p01/ch04/s08/dynamic_binding.cpp @@ -0,0 +1,31 @@ +// ============================================================================ +// p1c04_08_04 — 4.8.3 C++如何实现动态绑定 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:3] 4.8.3 C++如何实现动态绑定 --- +class A{ +public: + virtual void func1(){} + virtual void func2(){} +}; + +//B类为空,那么大小应该是1字节,实际情况是这样吗? +class B : public A{}; + +void test(){ + cout << "A size:" << sizeof(A) << endl; + cout << "B size:" << sizeof(B) << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s08/override_overload_redefine.cpp b/p01/ch04/s08/override_overload_redefine.cpp new file mode 100644 index 0000000..f3e4a37 --- /dev/null +++ b/p01/ch04/s08/override_overload_redefine.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// p1c04_08_07 — 4.8.7 重写 重载 重定义 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:6] 4.8.7 重写 重载 重定义 --- +static void block_0() { + class A{ + public: + //同一作用域下,func1函数重载 + void func1(){} + void func1(int a){} + void func1(int a,int b){} + void func2(){} + virtual void func3(){} + }; + + class B : public A{ + public: + //重定义基类的func2,隐藏了基类的func2方法 + void func2(){} + //重写基类的func3函数,也可以覆盖基类func3 + virtual void func3(){} + }; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch04/s08/polymorphism_basics.cpp b/p01/ch04/s08/polymorphism_basics.cpp new file mode 100644 index 0000000..f1daff1 --- /dev/null +++ b/p01/ch04/s08/polymorphism_basics.cpp @@ -0,0 +1,101 @@ +// ============================================================================ +// p1c04_08_01 — 4.8.1 多态基本概念 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:0] 4.8.1 多态基本概念 --- +//计算器 +class Caculator{ +public: + void setA_1(int a){ + this->mA = a; + } + void setB(int b){ + this->mB = b; + } + void setOperator(string oper){ + this->mOperator = oper; + } + int getResult_2(){ + + if (this->mOperator == "+"){ + return mA + mB; + } + else if (this->mOperator == "-"){ + return mA - mB; + } + else if (this->mOperator == "*"){ + return mA * mB; + } + else if (this->mOperator == "/"){ + return mA / mB; + } + } +private: + int mA; + int mB; + string mOperator; +}; + +//这种程序不利于扩展,维护困难,如果修改功能或者扩展功能需要在源代码基础上修改 +//面向对象程序设计一个基本原则:开闭原则(对修改关闭,对扩展开放) + +//抽象基类 +class AbstractCaculator{ +public: + void setA_1(int a){ + this->mA = a; + } + virtual void setB(int b){ + this->mB = b; + } + virtual int getResult_2() = 0; +protected: + int mA; + int mB; + string mOperator; +}; + +//加法计算器 +class PlusCaculator : public AbstractCaculator{ +public: + virtual int getResult_2(){ + return mA + mB; + } +}; + +//减法计算器 +class MinusCaculator : public AbstractCaculator{ +public: + virtual int getResult_2(){ + return mA - mB; + } +}; + +//乘法计算器 +class MultipliesCaculator : public AbstractCaculator{ +public: + virtual int getResult_2(){ + return mA * mB; + } +}; + +void DoBussiness(AbstractCaculator* caculator){ + int a = 10; + int b = 20; + caculator->setA_1(a); + caculator->setB(b); + cout << "计算结果:" << caculator->getResult_2() << endl; + delete caculator; +} + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch04/s08/pure_virtual_dtor.cpp b/p01/ch04/s08/pure_virtual_dtor.cpp new file mode 100644 index 0000000..a35f135 --- /dev/null +++ b/p01/ch04/s08/pure_virtual_dtor.cpp @@ -0,0 +1,38 @@ +// ============================================================================ +// p1c04_08_06 — 4.8.6.2 纯虚析构函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:5] 4.8.6.2 纯虚析构函数 --- +//非纯虚析构函数 +class A{ +public: + virtual ~A(); +}; + +A::~A(){} + +//纯析构函数 +class B{ +public: + virtual ~B() = 0; +}; + +B::~B(){} + +void test(){ + A a; //A类不是抽象类,可以实例化对象 + //B b; // 非法:B 含纯虚析构函数,是抽象类,不能实例化对象 //B类是抽象类,不可以实例化对象 +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s08/upcast_problem.cpp b/p01/ch04/s08/upcast_problem.cpp new file mode 100644 index 0000000..adf487e --- /dev/null +++ b/p01/ch04/s08/upcast_problem.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// p1c04_08_02 — 4.8.2.1 问题抛出 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:1] 4.8.2.1 问题抛出 --- +class Animal{ +public: + void speak_1(){ + cout << "动物在唱歌..." << endl; + } +}; + +class Dog : public Animal{ +public: + void speak_1(){ + cout << "小狗在唱歌..." << endl; + } +}; + +void DoBussiness(Animal& animal){ + animal.speak_1(); +} + +void test(){ + Dog dog; + DoBussiness(dog); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s08/virtual_dtor_purpose.cpp b/p01/ch04/s08/virtual_dtor_purpose.cpp new file mode 100644 index 0000000..bfc4f23 --- /dev/null +++ b/p01/ch04/s08/virtual_dtor_purpose.cpp @@ -0,0 +1,53 @@ +// ============================================================================ +// p1c04_08_05 — 4.8.6.1 虚析构函数作用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:4] 4.8.6.1 虚析构函数作用 --- +class People{ +public: + People(){ + cout << "构造函数 People!" << endl; + } + virtual void showName() = 0; + virtual ~People(){ + cout << "析构函数 People!" << endl; + } +}; + +class Worker : public People{ +public: + Worker(){ + cout << "构造函数 Worker!" << endl; + pName = new char[10]; + } + virtual void showName(){ + cout << "打印子类的名字!" << endl; + } + ~Worker(){ + cout << "析构函数 Worker!" << endl; + if (pName != NULL){ + delete pName; + } + } +private: + char* pName; +}; + +void test(){ + + People* people = new Worker; + people->~People(); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch04/s08/virtual_function_solution.cpp b/p01/ch04/s08/virtual_function_solution.cpp new file mode 100644 index 0000000..474b151 --- /dev/null +++ b/p01/ch04/s08/virtual_function_solution.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// p1c04_08_03 — 4.8.2.1 问题解决方案(虚函数,vitual function) +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954469:2] 4.8.2.1 问题解决方案(虚函数,vitual function) --- +class Animal{ +public: + virtual void speak_1(){ + cout << "动物在唱歌..." << endl; + } +}; +class Dog : public Animal{ +public: + virtual void speak_1(){ + cout << "小狗在唱歌..." << endl; + } +}; +void DoBussiness(Animal& animal){ + animal.speak_1(); +} +void test(){ + Dog dog; + DoBussiness(dog); +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch05/CMakeLists.txt b/p01/ch05/CMakeLists.txt new file mode 100644 index 0000000..4567a4a --- /dev/null +++ b/p01/ch05/CMakeLists.txt @@ -0,0 +1,51 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch05 +add_executable(p1c05_01 function_template.cpp) +set_target_properties(p1c05_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_01 PRIVATE -Wall -Wextra) +add_executable(p1c05_02 template_vs_function.cpp) +set_target_properties(p1c05_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_02 PRIVATE -Wall -Wextra) +add_executable(p1c05_03 template_call_rules.cpp) +set_target_properties(p1c05_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_03 PRIVATE -Wall -Wextra) +add_executable(p1c05_04 template_limitations.cpp) +set_target_properties(p1c05_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_04 PRIVATE -Wall -Wextra) +add_executable(p1c05_05 class_template_basics.cpp) +set_target_properties(p1c05_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_05 PRIVATE -Wall -Wextra) +add_executable(p1c05_06 class_template_as_param.cpp) +set_target_properties(p1c05_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_06 PRIVATE -Wall -Wextra) +add_executable(p1c05_07 class_template_derives_class.cpp) +set_target_properties(p1c05_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_07 PRIVATE -Wall -Wextra) +add_executable(p1c05_08 class_template_derives_template.cpp) +set_target_properties(p1c05_08 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_08 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_08 PRIVATE -Wall -Wextra) +add_executable(p1c05_09 class_template_in_class_impl.cpp) +set_target_properties(p1c05_09 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_09 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_09 PRIVATE -Wall -Wextra) +add_executable(p1c05_10 class_template_out_of_class_impl.cpp) +set_target_properties(p1c05_10 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_10 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_10 PRIVATE -Wall -Wextra) +add_executable(p1c05_11 class_template_friend.cpp) +set_target_properties(p1c05_11 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_11 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_11 PRIVATE -Wall -Wextra) +add_executable(p1c05_12 class_template_application.cpp) +set_target_properties(p1c05_12 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c05_12 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c05_12 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch05 DEPENDS p1c05_01 p1c05_02 p1c05_03 p1c05_04 p1c05_05 p1c05_06 p1c05_07 p1c05_08 p1c05_09 p1c05_10 p1c05_11 p1c05_12) diff --git a/p01/ch05/class_template_application.cpp b/p01/ch05/class_template_application.cpp new file mode 100644 index 0000000..09f6c8d --- /dev/null +++ b/p01/ch05/class_template_application.cpp @@ -0,0 +1,102 @@ +// ============================================================================ +// p1c05_12 — 5.8 类模板的应用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954472:11] 5.8 类模板的应用 --- +#include +using std::string; + +template +class MyArray { +public: + explicit MyArray(int capacity) { + this->m_Capacity = capacity; + this->m_Size = 0; + // 如果T是对象,那么这个对象必须提供默认的构造函数 + pAddress = new T[this->m_Capacity]; + } + MyArray(const MyArray& arr) { + this->m_Capacity = arr.m_Capacity; + this->m_Size = arr.m_Size; + this->pAddress = new T[this->m_Capacity]; + for (int i = 0; i < this->m_Size; i++) { + this->pAddress[i] = arr.pAddress[i]; + } + } + T& operator[](int index) { return this->pAddress[index]; } + void Push_back(const T& val) { + if (this->m_Capacity == this->m_Size) return; + this->pAddress[this->m_Size] = val; + this->m_Size++; + } + void Pop_back() { if (this->m_Size > 0) this->m_Size--; } + int getSize() { return this->m_Size; } + ~MyArray() { + if (this->pAddress != NULL) { + delete[] this->pAddress; + this->pAddress = NULL; + this->m_Capacity = 0; + this->m_Size = 0; + } + } +private: + T* pAddress; + int m_Capacity; + int m_Size; +}; + +class Person { +public: + Person() {} + Person(string name, int age) { this->mName = name; this->mAge = age; } +public: + string mName; + int mAge; +}; + +void PrintMyArrayInt(MyArray& arr) { + for (int i = 0; i < arr.getSize(); i++) { + cout << arr[i] << " "; + } + cout << endl; +} + +void PrintMyPerson(MyArray& personArr) { + for (int i = 0; i < personArr.getSize(); i++) { + cout << "姓名:" << personArr[i].mName << " 年龄:" << personArr[i].mAge << endl; + } +} + +// --- 测试代码(原文为文件作用域裸语句,已包进 main 使其可编译运行)--- +int main() { + MyArray myArrayInt(10); + for (int i = 0; i < 9; i++) { + myArrayInt.Push_back(i); + } + myArrayInt.Push_back(100); + PrintMyArrayInt(myArrayInt); + + MyArray myArrayPerson(10); + Person p1("德玛西亚", 30); + Person p2("提莫", 20); + Person p3("孙悟空", 18); + Person p4("赵信", 15); + Person p5("赵云", 24); + myArrayPerson.Push_back(p1); + myArrayPerson.Push_back(p2); + myArrayPerson.Push_back(p3); + myArrayPerson.Push_back(p4); + myArrayPerson.Push_back(p5); + PrintMyPerson(myArrayPerson); + return 0; +} + +// --- main --- diff --git a/p01/ch05/class_template_as_param.cpp b/p01/ch05/class_template_as_param.cpp new file mode 100644 index 0000000..9dcc0f9 --- /dev/null +++ b/p01/ch05/class_template_as_param.cpp @@ -0,0 +1,48 @@ +// ============================================================================ +// p1c05_06 — 5.7.2 类模板做函数参数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954472:5] 5.7.2 类模板做函数参数 --- +//类模板 +template +class Person{ +public: + Person(NameType name, AgeType age){ + this->mName = name; + this->mAge = age; + } + void PrintPerson(){ + cout << "Name:" << this->mName << " Age:" << this->mAge << endl; + } +public: + NameType mName; + AgeType mAge; +}; + +//类模板做函数参数 +void DoBussiness(Person& p){ + p.mAge += 20; + p.mName += "_vip"; + p.PrintPerson(); +} + +int main(){ + + Person p("John", 30); + DoBussiness(p); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch05/class_template_basics.cpp b/p01/ch05/class_template_basics.cpp new file mode 100644 index 0000000..f262df9 --- /dev/null +++ b/p01/ch05/class_template_basics.cpp @@ -0,0 +1,42 @@ +// ============================================================================ +// p1c05_05 — 5.7.1 类模板基本概念 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:4] 5.7.1 类模板基本概念 --- +template +class Person +{ +public: + Person(NameType name, AgeType age) + { + this->mName = name; + this->mAge = age; + } + void showPerson() + { + cout << "name: " << this->mName << " age: " << this->mAge << endl; + } +public: + NameType mName; + AgeType mAge; +}; + +void test01() +{ + //Person P1("德玛西亚",18); // 类模板不能进行类型自动推导 + PersonP1("德玛西亚", 18); + P1.showPerson(); +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch05/class_template_derives_class.cpp b/p01/ch05/class_template_derives_class.cpp new file mode 100644 index 0000000..4588768 --- /dev/null +++ b/p01/ch05/class_template_derives_class.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// p1c05_07 — 5.7.3 类模板派生普通类 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:6] 5.7.3 类模板派生普通类 --- +//类模板 +template +class MyClass{ +public: + MyClass(T property){ + this->mProperty = property; + } +public: + T mProperty; +}; + +//子类实例化的时候需要具体化的父类,子类需要知道父类的具体类型是什么样的 +//这样c++编译器才能知道给子类分配多少内存 + +//普通派生类 +class SubClass : public MyClass{ +public: + SubClass(int b) : MyClass(20){ + this->mB = b; + } +public: + int mB; +}; + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch05/class_template_derives_template.cpp b/p01/ch05/class_template_derives_template.cpp new file mode 100644 index 0000000..5880c64 --- /dev/null +++ b/p01/ch05/class_template_derives_template.cpp @@ -0,0 +1,35 @@ +// ============================================================================ +// p1c05_08 — 5.7.4 类模板派生类模板 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:7] 5.7.4 类模板派生类模板 --- +//父类类模板 +template +class Base +{ + T m; +}; +template +class Child2 : public Base //继承类模板的时候,必须要确定基类的大小 +{ +public: + T mParam; +}; + +void test02() +{ + Child2 d2; +} + +// --- main --- +int main() { + test02(); + return 0; +} diff --git a/p01/ch05/class_template_friend.cpp b/p01/ch05/class_template_friend.cpp new file mode 100644 index 0000000..a3a0cef --- /dev/null +++ b/p01/ch05/class_template_friend.cpp @@ -0,0 +1,83 @@ +// ============================================================================ +// p1c05_11 — 5.7.7 模板类碰到友元函数 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954472:10] 5.7.7 模板类碰到友元函数 --- +#define _CRT_SECURE_NO_WARNINGS +#include +using namespace std; +#include + +template class Person; +//告诉编译器这个函数模板是存在 +template void PrintPerson2(Person& p); + +//友元函数在类内实现 +template +class Person{ + //1. 友元函数在类内实现 + friend void PrintPerson(Person& p){ + cout << "Name:" << p.mName << " Age:" << p.mAge << endl; + } + + //2.友元函数类外实现 + //告诉编译器这个函数模板是存在 + friend void PrintPerson2<>(Person& p); + + //3. 类模板碰到友元函数模板 + template + friend void PrintPerson(Person& p); + +public: + Person(T1 name, T2 age){ + this->mName = name; + this->mAge = age; + } + void showPerson(){ + cout << "Name:" << this->mName << " Age:" << this->mAge << endl; + } +private: + T1 mName; + T2 mAge; +}; + +void test01() +{ + Person p("Jerry", 20); + PrintPerson(p); +} + + +// 类模板碰到友元函数 +//友元函数类外实现 加上<>空参数列表,告诉编译去匹配函数模板 +template +void PrintPerson2(Person& p) +{ + cout << "Name2:" << p.mName << " Age2:" << p.mAge << endl; +} + +void test02() +{ + Person p("Jerry", 20); + PrintPerson2(p); //不写可以编译通过,写了之后,会找PrintPerson2的普通函数调用,因为写了普通函数PrintPerson2的声明 +} + +int main(){ + + //test01(); + test02(); + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch05/class_template_in_class_impl.cpp b/p01/ch05/class_template_in_class_impl.cpp new file mode 100644 index 0000000..2943b3b --- /dev/null +++ b/p01/ch05/class_template_in_class_impl.cpp @@ -0,0 +1,42 @@ +// ============================================================================ +// p1c05_09 — 5.7.5 类模板类内实现 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:8] 5.7.5 类模板类内实现 --- +template +class Person +{ +public: + Person(NameType name, AgeType age) + { + this->mName = name; + this->mAge = age; + } + void showPerson() + { + cout << "name: " << this->mName << " age: " << this->mAge << endl; + } +public: + NameType mName; + AgeType mAge; +}; + +void test01() +{ + //Person P1("德玛西亚",18); // 类模板不能进行类型自动推导 + PersonP1("德玛西亚", 18); + P1.showPerson(); +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch05/class_template_out_of_class_impl.cpp b/p01/ch05/class_template_out_of_class_impl.cpp new file mode 100644 index 0000000..8bf77f1 --- /dev/null +++ b/p01/ch05/class_template_out_of_class_impl.cpp @@ -0,0 +1,60 @@ +// ============================================================================ +// p1c05_10 — 5.7.6 类模板类外实现 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954472:9] 5.7.6 类模板类外实现 --- +#define _CRT_SECURE_NO_WARNINGS +#include +#include +using namespace std; + +template +class Person{ +public: + Person(T1 name, T2 age); + void showPerson(); + +public: + T1 mName; + T2 mAge; +}; + + +//类外实现 +template +Person::Person(T1 name, T2 age){ + this->mName = name; + this->mAge = age; +} + + +template +void Person::showPerson(){ + cout << "Name:" << this->mName << " Age:" << this->mAge << endl; +} + +void test() +{ + Person p("Obama", 20); + p.showPerson(); +} + +int main(){ + + test(); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch05/function_template.cpp b/p01/ch05/function_template.cpp new file mode 100644 index 0000000..abb6b82 --- /dev/null +++ b/p01/ch05/function_template.cpp @@ -0,0 +1,62 @@ +// ============================================================================ +// p1c05_01 — 5.2 函数模板 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:0] 5.2 函数模板 --- +//交换int数据 +void SwapInt(int& a,int& b){ + int temp = a; + a = b; + b = temp; +} + +//交换char数据 +void SwapChar(char& a,char& b){ + char temp = a; + a = b; + b = temp; +} +//问题:如果我要交换double类型数据,那么还需要些一个double类型数据交换的函数 +//繁琐,写的函数越多,当交换逻辑发生变化的时候,所有的函数都需要修改,无形当中增加了代码的维护难度 + +//如果能把类型作为参数传递进来就好了,传递int就是Int类型交换,传递char就是char类型交换 +//我们有一种技术,可以实现类型的参数化---函数模板 + + +//class 和 typename都是一样的,用哪个都可以 +template +void MySwap(T& a,T& b){ + T temp = a; + a = b; + b = temp; +} + +void test01(){ + + int a = 10; + int b = 20; + cout << "a:" << a << " b:" << b << endl; + //1. 这里有个需要注意点,函数模板可以自动推导参数的类型 + MySwap(a,b); + cout << "a:" << a << " b:" << b << endl; + + char c1 = 'a'; + char c2 = 'b'; + cout << "c1:" << c1 << " c2:" << c2 << endl; + //2. 函数模板可以自动类型推导,那么也可以显式指定类型 + MySwap(c1, c2); + cout << "c1:" << c1 << " c2:" << c2 << endl; +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch05/template_call_rules.cpp b/p01/ch05/template_call_rules.cpp new file mode 100644 index 0000000..0b1c190 --- /dev/null +++ b/p01/ch05/template_call_rules.cpp @@ -0,0 +1,62 @@ +// ============================================================================ +// p1c05_03 — 5.4 函数模板和普通函数在一起调用规则 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:2] 5.4 函数模板和普通函数在一起调用规则 --- +//函数模板 +template +T MyPlus_1(T a, T b){ + T ret = a + b; + return ret; +} + +//普通函数 +int MyPlus_1(int a, int b){ + int ret = a + b; + return ret; +} + +void test03(){ + int a = 10; + int b = 20; + char c = 'a'; + char d = 'b'; + //如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数 + cout << MyPlus_1(a, b) << endl; + //如果我必须要调用函数模板,那么怎么办? + cout << MyPlus_1<>(a, b) << endl; + //此时普通函数也可以匹配,因为普通函数可以自动类型转换 + //但是此时函数模板能够有更好的匹配 + //如果函数模板可以产生一个更好的匹配,那么选择模板 + cout << MyPlus_1(c,d); +} + +//函数模板重载 +template +T MyPlus_1(T a, T b, T c){ + T ret = a + b + c; + return ret; +} + +void test04(){ + + int a = 10; + int b = 20; + int c = 30; + cout << MyPlus_1(a, b, c) << endl; + //如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数 +} + +// --- main --- +int main() { + test03(); + test04(); + return 0; +} diff --git a/p01/ch05/template_limitations.cpp b/p01/ch05/template_limitations.cpp new file mode 100644 index 0000000..b1c0b2c --- /dev/null +++ b/p01/ch05/template_limitations.cpp @@ -0,0 +1,66 @@ +// ============================================================================ +// p1c05_04 — 5.6模板的局限性 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:3] 5.6模板的局限性 --- +class Person +{ +public: + Person(string name, int age) + { + this->mName = name; + this->mAge = age; + } + string mName; + int mAge; +}; + +//普通交换函数 +template +void mySwap(T &a,T &b) +{ + T temp = a; + a = b; + b = temp; +} +//第三代具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型 +//具体化优先于常规模板 +template<>void mySwap(Person &p1, Person &p2) +{ + string nameTemp; + int ageTemp; + + nameTemp = p1.mName; + p1.mName = p2.mName; + p2.mName = nameTemp; + + ageTemp = p1.mAge; + p1.mAge = p2.mAge; + p2.mAge = ageTemp; + +} + +void test() +{ + Person P1("Tom", 10); + Person P2("Jerry", 20); + + cout << "P1 Name = " << P1.mName << " P1 Age = " << P1.mAge << endl; + cout << "P2 Name = " << P2.mName << " P2 Age = " << P2.mAge << endl; + mySwap(P1, P2); + cout << "P1 Name = " << P1.mName << " P1 Age = " << P1.mAge << endl; + cout << "P2 Name = " << P2.mName << " P2 Age = " << P2.mAge << endl; +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch05/template_vs_function.cpp b/p01/ch05/template_vs_function.cpp new file mode 100644 index 0000000..4cd240f --- /dev/null +++ b/p01/ch05/template_vs_function.cpp @@ -0,0 +1,48 @@ +// ============================================================================ +// p1c05_02 — 5.3 函数模板和普通函数区别 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954472:1] 5.3 函数模板和普通函数区别 --- +//函数模板 +template +T MyPlus_1(T a, T b){ + T ret = a + b; + return ret; +} + +//普通函数 +int MyPlus_1(int a,char b){ + int ret = a + b; + return ret; +} + +void test02(){ + + int a = 10; + char b = 'a'; + + //调用函数模板,严格匹配类型 + MyPlus_1(a, a); + MyPlus_1(b, b); + //调用普通函数 + MyPlus_1(a, b); + //调用普通函数 普通函数可以隐式类型转换 + MyPlus_1(b, a); + + //结论: + //函数模板不允许自动类型转换,必须严格匹配类型 + //普通函数可以进行自动类型转换 +} + +// --- main --- +int main() { + test02(); + return 0; +} diff --git a/p01/ch06/CMakeLists.txt b/p01/ch06/CMakeLists.txt new file mode 100644 index 0000000..32e6e8f --- /dev/null +++ b/p01/ch06/CMakeLists.txt @@ -0,0 +1,15 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch06 +add_executable(p1c06_01 static_cast_demo.cpp) +set_target_properties(p1c06_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c06_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c06_01 PRIVATE -Wall -Wextra) +add_executable(p1c06_02 dynamic_cast_demo.cpp) +set_target_properties(p1c06_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c06_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c06_02 PRIVATE -Wall -Wextra) +add_executable(p1c06_03 const_cast_demo.cpp) +set_target_properties(p1c06_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c06_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c06_03 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch06 DEPENDS p1c06_01 p1c06_02 p1c06_03) diff --git a/p01/ch06/const_cast_demo.cpp b/p01/ch06/const_cast_demo.cpp new file mode 100644 index 0000000..4f04eb6 --- /dev/null +++ b/p01/ch06/const_cast_demo.cpp @@ -0,0 +1,41 @@ +// ============================================================================ +// p1c06_03 — 2.3 常量转换(const_cast) +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954473:2] 2.3 常量转换(const_cast) --- +//常量指针转换成非常量指针 +void test01(){ + + const int* p = NULL; + int* np = const_cast(p); + + int* pp = NULL; + const int* npp = const_cast(pp); + + const int a = 10; //不能对非指针或非引用进行转换 + //int b = const_cast(a); // 不能对非指针或非引用进行 const_cast +} // end of test01() + +//常量引用转换成非常量引用 +void test02(){ + +int num = 10; + int & refNum = num; + + const int& refNum2 = const_cast(refNum); + +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch06/dynamic_cast_demo.cpp b/p01/ch06/dynamic_cast_demo.cpp new file mode 100644 index 0000000..af58670 --- /dev/null +++ b/p01/ch06/dynamic_cast_demo.cpp @@ -0,0 +1,78 @@ +// ============================================================================ +// p1c06_02 — 2.2 动态转换(dynamic_cast) +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954473:1] 2.2 动态转换(dynamic_cast) --- +class Animal { +public: + virtual void ShowName() = 0; +}; +class Dog : public Animal{ + virtual void ShowName(){ + cout << "I am a dog!" << endl; + } +}; +class Other { +public: + void PrintSomething(){ + cout << "我是其他类!" << endl; + } +}; + +//普通类型转换 +void test01(){ + + //不支持基础数据类型 + int a = 10; + //double a = dynamic_cast(a); +} + +//继承关系指针 +void test02(){ + + Animal* animal01 = NULL; + Dog* dog01 = new Dog; + + //子类指针转换成父类指针 可以 + Animal* animal02 = dynamic_cast(dog01); + animal02->ShowName(); + //父类指针转换成子类指针 不可以 + //Dog* dog02 = dynamic_cast(animal01); +} + +//继承关系引用 +void test03(){ + + Dog dog_ref; + Dog& dog01 = dog_ref; + + //子类引用转换成父类引用 可以 + Animal& animal02 = dynamic_cast(dog01); + animal02.ShowName(); +} + +//无继承关系指针转换 +void test04(){ + + Animal* animal01 = NULL; + Other* other = NULL; + + //不可以 + //Animal* animal02 = dynamic_cast(other); +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + test04(); + return 0; +} diff --git a/p01/ch06/static_cast_demo.cpp b/p01/ch06/static_cast_demo.cpp new file mode 100644 index 0000000..c26c1c6 --- /dev/null +++ b/p01/ch06/static_cast_demo.cpp @@ -0,0 +1,65 @@ +// ============================================================================ +// p1c06_01 — 6.1 静态转换(static_cast) +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954473:0] 6.1 静态转换(static_cast) --- +class Animal{}; +class Dog : public Animal{}; +class Other{}; + +//基础数据类型转换 +void test01(){ + char a = 'a'; + double b = static_cast(a); +} + +//继承关系指针互相转换 +void test02(){ + //继承关系指针转换 + Animal* animal01 = NULL; + Dog* dog01 = NULL; + //子类指针转成父类指针,安全 + Animal* animal02 = static_cast(dog01); + //父类指针转成子类指针,不安全 + Dog* dog02 = static_cast(animal01); +} + +//继承关系引用相互转换 +void test03(){ + + Animal ani_ref; + Dog dog_ref; + //继承关系指针转换 + Animal& animal01 = ani_ref; + Dog& dog01 = dog_ref; + //子类指针转成父类指针,安全 + Animal& animal02 = static_cast(dog01); + //父类指针转成子类指针,不安全 + Dog& dog02 = static_cast(animal01); +} + +//无继承关系指针转换 +void test04(){ + + Animal* animal01 = NULL; + Other* other01 = NULL; + + //转换失败 + //Animal* animal02 = static_cast(other01); +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + test04(); + return 0; +} diff --git a/p01/ch07/CMakeLists.txt b/p01/ch07/CMakeLists.txt new file mode 100644 index 0000000..079debe --- /dev/null +++ b/p01/ch07/CMakeLists.txt @@ -0,0 +1,39 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch07 +add_executable(p1c07_01 exception_basics.cpp) +set_target_properties(p1c07_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_01 PRIVATE -Wall -Wextra) +add_executable(p1c07_02 exception_syntax.cpp) +set_target_properties(p1c07_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_02 PRIVATE -Wall -Wextra) +add_executable(p1c07_03 exception_type_matching.cpp) +set_target_properties(p1c07_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_03 PRIVATE -Wall -Wextra) +add_executable(p1c07_04 stack_unwinding.cpp) +set_target_properties(p1c07_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_04 PRIVATE -Wall -Wextra) +add_executable(p1c07_05 exception_specification.cpp) +set_target_properties(p1c07_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_05 PRIVATE -Wall -Wextra) +add_executable(p1c07_06 exception_variable_lifetime.cpp) +set_target_properties(p1c07_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_06 PRIVATE -Wall -Wextra) +add_executable(p1c07_07 exception_polymorphism.cpp) +set_target_properties(p1c07_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_07 PRIVATE -Wall -Wextra) +add_executable(p1c07_08 std_exception_library.cpp) +set_target_properties(p1c07_08 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_08 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_08 PRIVATE -Wall -Wextra) +add_executable(p1c07_09 custom_exception_class.cpp) +set_target_properties(p1c07_09 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c07_09 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c07_09 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch07 DEPENDS p1c07_01 p1c07_02 p1c07_03 p1c07_04 p1c07_05 p1c07_06 p1c07_07 p1c07_08 p1c07_09) diff --git a/p01/ch07/custom_exception_class.cpp b/p01/ch07/custom_exception_class.cpp new file mode 100644 index 0000000..df7a53c --- /dev/null +++ b/p01/ch07/custom_exception_class.cpp @@ -0,0 +1,88 @@ +// ============================================================================ +// p1c07_09 — 7.3.2 编写自己的异常类 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954474:8] 7.3.2 编写自己的异常类 --- +//自定义异常类 +class MyOutOfRange:public exception +{ +public: + MyOutOfRange(const string errorInfo) + { + this->m_Error = errorInfo; + } + + MyOutOfRange(const char * errorInfo) + { + this->m_Error = string( errorInfo); + } + + virtual ~MyOutOfRange() + { + + } + virtual const char* what() const noexcept { + return this->m_Error.c_str() ; + } + + string m_Error; + +}; + +class Person +{ +public: + Person(int age) + { + if (age <= 0 || age > 150) + { + //抛出异常 越界 + //cout << "越界" << endl; + //throw out_of_range("年龄必须在0~150之间"); + + //throw length_error("长度异常"); + throw MyOutOfRange(("我的异常 年龄必须在0~150之间")); + } + else + { + this->m_Age = age; + } + + } + + int m_Age; +}; + + +void test01() +{ + try + { + Person p(151); + } + catch ( out_of_range & e ) + { + cout << e.what() << endl; + } + catch (length_error & e) + { + cout << e.what() << endl; + } + catch (MyOutOfRange e) + { + cout << e.what() << endl; + } +} + +// --- main --- +int main() { + test01(); + return 0; +} diff --git a/p01/ch07/exception_basics.cpp b/p01/ch07/exception_basics.cpp new file mode 100644 index 0000000..0404f4b --- /dev/null +++ b/p01/ch07/exception_basics.cpp @@ -0,0 +1,57 @@ +// ============================================================================ +// p1c07_01 — 7.1 异常基本概念 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954474:0] 7.1 异常基本概念 --- +//如果判断返回值,那么返回值是错误码还是结果? +//如果不判断返回值,那么b==0时候,程序结果已经不正确 +//A写的代码 +int A_MyDivide(int a,int b){ + if (b == 0){ + return -1; + } + + return a / b; +} + +//B写的代码 +int B_MyDivide(int a,int b){ + + int ba = a + 100; + int bb = b; + + int ret = A_MyDivide(ba, bb); //由于B没有处理异常,导致B结果运算错误 + + return ret; +} + +//C写的代码 +int C_MyDivide(){ + + int a = 10; + int b = 0; + + int ret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常 + if (ret == -1){ + return -1; + } + else{ + return ret; + } +} + +//所以,我们希望: +//1.异常应该捕获,如果你捕获,可以,那么异常必须继续抛给上层函数,你不处理,不代表你的上层不处理 +//2.这个例子,异常没有捕获的结果就是运行结果错的一塌糊涂,结果未知,未知的结果程序没有必要执行下去 + +// --- main --- +int main() { + return 0; +} diff --git a/p01/ch07/exception_polymorphism.cpp b/p01/ch07/exception_polymorphism.cpp new file mode 100644 index 0000000..634b7dc --- /dev/null +++ b/p01/ch07/exception_polymorphism.cpp @@ -0,0 +1,53 @@ +// ============================================================================ +// p1c07_07 — 7.2.6 异常的多态使用 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954474:6] 7.2.6 异常的多态使用 --- +//异常基类 +class BaseException{ +public: + virtual void printError_2(){}; +}; + +//空指针异常 +class NullPointerException : public BaseException{ +public: + virtual void printError_2(){ + cout << "空指针异常!" << endl; + } +}; +//越界异常 +class OutOfRangeException : public BaseException{ +public: + virtual void printError_2(){ + cout << "越界异常!" << endl; + } +}; + +void doWork(){ + + throw NullPointerException(); +} + +void test() +{ + try{ + doWork(); + } + catch (BaseException& ex){ + ex.printError_2(); + } +} + +// --- main --- +int main() { + test(); + return 0; +} diff --git a/p01/ch07/exception_specification.cpp b/p01/ch07/exception_specification.cpp new file mode 100644 index 0000000..d062c5f --- /dev/null +++ b/p01/ch07/exception_specification.cpp @@ -0,0 +1,45 @@ +// ============================================================================ +// p1c07_05 — 7.2.4 异常接口声明 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954474:4] 7.2.4 异常接口声明 --- +//可抛出所有类型异常 +void TestFunction01(){ + throw 10; +} + +// 【C++17 修正】原文为 throw(int,char,char*) 动态异常规范——C++17 起非法 +// (C++11 弃用、C++17 移除)。动态规范无法在现代 C++ 中精确表达「只抛这些类型」, +// 等价改写为 noexcept(false)(表示可能抛异常)。原文语义见 tools/demo_versions/。 +void TestFunction02() noexcept(false){ + string exception = "error!"; + throw exception; +} + +// 【C++17 修正】原文为 throw()(不抛任何异常)→ C++17 等价于 noexcept。 +void TestFunction03() noexcept { + //throw 10; // noexcept 函数抛异常会调用 std::terminate +} + +int main(){ + + try{ + //TestFunction01(); + //TestFunction02(); + //TestFunction03(); + } + catch (...){ + cout << "捕获异常!" << endl; + } + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch07/exception_syntax.cpp b/p01/ch07/exception_syntax.cpp new file mode 100644 index 0000000..4e3373a --- /dev/null +++ b/p01/ch07/exception_syntax.cpp @@ -0,0 +1,68 @@ +// ============================================================================ +// p1c07_02 — 7.2.1 异常基本语法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954474:1] 7.2.1 异常基本语法 --- +int A_MyDivide(int a, int b){ + if (b == 0){ + throw 0; + } + + return a / b; +} + +//B写的代码 B写代码比较粗心,忘记处理异常 +int B_MyDivide(int a, int b){ + + int ba = a; + int bb = b; + + int ret = A_MyDivide(ba, bb) + 100; //由于B没有处理异常,导致B结果运算错误 + + return ret; +} + +//C写的代码 +int C_MyDivide(){ + + int a = 10; + int b = 0; + + int ret = 0; + +//没有处理异常,程序直接中断执行 +#if 1 + ret = B_MyDivide(a, b); + +//处理异常 +#else + try{ + ret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常 + } + catch (int e){ + cout << "C_MyDivide Call B_MyDivide 除数为:" << e << endl; + } +#endif + + return ret; +} + +int main(){ + + C_MyDivide(); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch07/exception_type_matching.cpp b/p01/ch07/exception_type_matching.cpp new file mode 100644 index 0000000..4a99d01 --- /dev/null +++ b/p01/ch07/exception_type_matching.cpp @@ -0,0 +1,54 @@ +// ============================================================================ +// p1c07_03 — 7.2.2 异常严格类型匹配 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954474:2] 7.2.2 异常严格类型匹配 --- +void TestFunction(){ + + cout << "开始抛出异常..." << endl; + //throw 10; //抛出int类型异常 + //throw 'a'; //抛出char类型异常 + //throw "abcd"; //抛出char*类型异常 + string ex = "string exception!"; + throw ex; + +} + +int main(){ + + try{ + TestFunction(); + } + catch (int){ + cout << "抛出Int类型异常!" << endl; + } + catch (char){ + cout << "抛出Char类型异常!" << endl; + } + catch (char*){ + cout << "抛出Char*类型异常!" << endl; + } + catch (string){ + cout << "抛出string类型异常!" << endl; + } + //捕获所有异常 + catch (...){ + cout << "抛出其他类型异常!" << endl; + } + + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch07/exception_variable_lifetime.cpp b/p01/ch07/exception_variable_lifetime.cpp new file mode 100644 index 0000000..f3e5179 --- /dev/null +++ b/p01/ch07/exception_variable_lifetime.cpp @@ -0,0 +1,75 @@ +// ============================================================================ +// p1c07_06 — 7.2.5 异常变量生命周期 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954474:5] 7.2.5 异常变量生命周期 --- +class MyException +{ +public: + MyException(){ + cout << "异常变量构造" << endl; + }; + MyException(const MyException & e) + { + cout << "拷贝构造" << endl; + } + ~MyException() + { + cout << "异常变量析构" << endl; + } +}; +void DoWork() +{ + throw new MyException(); //test1 2都用 throw MyExecption(); +} + +void test01() +{ + try + { + DoWork(); + } + catch (MyException e) + { + cout << "捕获 异常" << endl; + } +} +void test02() +{ + try + { + DoWork(); + } + catch (MyException &e) + { + cout << "捕获 异常" << endl; + } +} + +void test03() +{ + try + { + DoWork(); + } + catch (MyException *e) + { + cout << "捕获 异常" << endl; + delete e; + } +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch07/stack_unwinding.cpp b/p01/ch07/stack_unwinding.cpp new file mode 100644 index 0000000..a57303e --- /dev/null +++ b/p01/ch07/stack_unwinding.cpp @@ -0,0 +1,52 @@ +// ============================================================================ +// p1c07_04 — 7.2.3 栈解旋(unwinding) +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954474:3] 7.2.3 栈解旋(unwinding) --- +class Person{ +public: + Person(string name){ + mName = name; + cout << mName << "对象被创建!" << endl; + } + ~Person(){ + cout << mName << "对象被析构!" << endl; + } +public: + string mName; +}; + +void TestFunction(){ + + Person p1("aaa"); + Person p2("bbb"); + Person p3("ccc"); + + //抛出异常 + throw 10; +} + +int main(){ + + try{ + TestFunction(); + } + catch (...){ + cout << "异常被捕获!" << endl; + } + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch07/std_exception_library.cpp b/p01/ch07/std_exception_library.cpp new file mode 100644 index 0000000..ed8cbe8 --- /dev/null +++ b/p01/ch07/std_exception_library.cpp @@ -0,0 +1,41 @@ +// ============================================================================ +// p1c07_08 — 7.3.1 标准库介绍 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954474:7] 7.3.1 标准库介绍 --- +#include +class Person{ +public: + Person(int age){ + if (age < 0 || age > 150){ + throw out_of_range("年龄应该在0-150岁之间!"); + } + } +public: + int mAge; +}; + +int main(){ + + try{ + Person p(151); + } + catch (out_of_range& ex){ + cout << ex.what() << endl; + } + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch08/CMakeLists.txt b/p01/ch08/CMakeLists.txt new file mode 100644 index 0000000..180bcad --- /dev/null +++ b/p01/ch08/CMakeLists.txt @@ -0,0 +1,23 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch08 +add_executable(p1c08_01 standard_input_stream.cpp) +set_target_properties(p1c08_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c08_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c08_01 PRIVATE -Wall -Wextra) +add_executable(p1c08_02 character_output.cpp) +set_target_properties(p1c08_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c08_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c08_02 PRIVATE -Wall -Wextra) +add_executable(p1c08_03 formatted_output.cpp) +set_target_properties(p1c08_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c08_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c08_03 PRIVATE -Wall -Wextra) +add_executable(p1c08_04 ascii_file_io.cpp) +set_target_properties(p1c08_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c08_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c08_04 PRIVATE -Wall -Wextra) +add_executable(p1c08_05 binary_file_io.cpp) +set_target_properties(p1c08_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c08_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c08_05 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch08 DEPENDS p1c08_01 p1c08_02 p1c08_03 p1c08_04 p1c08_05) diff --git a/p01/ch08/ascii_file_io.cpp b/p01/ch08/ascii_file_io.cpp new file mode 100644 index 0000000..dcf55f9 --- /dev/null +++ b/p01/ch08/ascii_file_io.cpp @@ -0,0 +1,46 @@ +// ============================================================================ +// p1c08_04 — 8.4.4 C++对ASCII文件的读写操作 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954476:3] 8.4.4 C++对ASCII文件的读写操作 --- +int main(){ + + char* sourceFileName = "./source.txt"; + char* targetFileName = "./target.txt"; + //创建文件输入流对象 + ifstream ism(sourceFileName, ios::in); + //创建文件输出流对象 + ofstream osm(targetFileName,ios::out); + + if (!ism){ + cout << "文件打开失败!" << endl; + } + + while (!ism.eof()){ + char buf[1024] = { 0 }; + ism.getline(buf,1024); + cout << buf << endl; + osm << buf << endl; + } + + //关闭文件流对象 + ism.close(); + osm.close(); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch08/binary_file_io.cpp b/p01/ch08/binary_file_io.cpp new file mode 100644 index 0000000..0fd9bea --- /dev/null +++ b/p01/ch08/binary_file_io.cpp @@ -0,0 +1,69 @@ +// ============================================================================ +// p1c08_05 — 8.4.5 C++对二进制文件的读写操作 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954476:4] 8.4.5 C++对二进制文件的读写操作 --- +class Person{ +public: + Person(){} // 补默认构造:二进制读入需先默认构造占位对象 + Person(char* name,int age){ + strcpy(this->mName, name); + this->mAge = age; + } +public: + char mName[64]; + int mAge; +}; + +int main(){ + + char* fileName = "person.txt"; + //二进制模式读写文件 + //创建文件对象输出流 + ofstream osm(fileName, ios::out | ios::binary); + + Person p1("John",33); + Person p2("Edward", 34); + + //Person对象写入文件 + osm.write((const char*)&p1,sizeof(Person)); + osm.write((const char*)&p2, sizeof(Person)); + + //关闭文件输出流 + osm.close(); + + //从文件中读取对象数组 + ifstream ism(fileName, ios::in | ios::binary); + if (!ism){ + cout << "打开失败!" << endl; + } + + Person p3; + Person p4; + + ism.read((char*)&p3, sizeof(Person)); + ism.read((char*)&p4, sizeof(Person)); + + cout << "Name:" << p3.mName << " Age:" << p3.mAge << endl; + cout << "Age:" << p4.mName << " Age:" << p4.mAge << endl; + + //关闭文件输入流 + ism.close(); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch08/character_output.cpp b/p01/ch08/character_output.cpp new file mode 100644 index 0000000..6e3fe3a --- /dev/null +++ b/p01/ch08/character_output.cpp @@ -0,0 +1,53 @@ +// ============================================================================ +// p1c08_02 — 8.4.1 字符输出 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954476:1] 8.4.1 字符输出 --- +//cout.flush 刷新缓冲区,linux下有效 +void test01(){ + cout << "hello world"; + //刷新缓冲区 + cout.flush(); +} + +//cout.put 输出一个字符 +void test02(){ + + cout.put('a'); + //链式编程 + cout.put('h').put('e').put('l'); +} + +//cout.write 输出字符串 buf,输出多少个 +void test03(){ + + //char* str = "hello world!"; + //cout.write(str, strlen(str)); + char* str = "*************"; + for (int i = 1; i <= strlen(str); i ++){ + cout.write(str, i); + cout << endl; + } + + for (int i = strlen(str); i > 0; i --){ + cout.write(str, i); + cout << endl; + } + +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch08/formatted_output.cpp b/p01/ch08/formatted_output.cpp new file mode 100644 index 0000000..ef7eb03 --- /dev/null +++ b/p01/ch08/formatted_output.cpp @@ -0,0 +1,50 @@ +// ============================================================================ +// p1c08_03 — 8.4.2 格式化输出 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +// --- 原文片段 [58954476:2] 8.4.2 格式化输出 --- +//通过流成员函数 +void test01(){ + + int number = 99; + cout.width(20); + cout.fill('*'); + cout.setf(ios::left); + cout.unsetf(ios::dec); //卸载十进制 + cout.setf(ios::hex); + cout.setf(ios::showbase); + cout.unsetf(ios::hex); + cout.setf(ios::oct); + cout << number << endl; + +} + +//使用控制符 +void test02(){ + + int number = 99; + cout << setw(20) + << setfill('~') + << setiosflags(ios::showbase) + << setiosflags(ios::left) + << hex + << number + << endl; + +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch08/standard_input_stream.cpp b/p01/ch08/standard_input_stream.cpp new file mode 100644 index 0000000..2805902 --- /dev/null +++ b/p01/ch08/standard_input_stream.cpp @@ -0,0 +1,96 @@ +// ============================================================================ +// p1c08_01 — 8.3 标准输入流 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954476:0] 8.3 标准输入流 --- +//cin.get +void test01(){ +#if 0 + char ch = cin.get(); + cout << ch << endl; + + cin.get(ch); + cout << ch << endl; + + + //链式编程 + char char1, char2, char3, char4; + cin.get(char1).get(char2).get(char3).get(char4); + + cout << char1 << " " << char2 << "" << char3 << " " << char4 << " "; +#endif + + char buf[1024] = { 0 }; + //cin.get(buf.1024); + cin.getline(buf,1024); + cout << buf; +} + +//cin.ignore +void test02(){ + + char buf[1024] = { 0 }; + cin.ignore(2); //忽略缓冲区当前字符 + cin.get(buf,1024); + cout << buf << endl; +} + +//cin.putback 将数据放回缓冲区 +void test03(){ + + //从缓冲区取走一个字符 + char ch = cin.get(); + cout << "从缓冲区取走的字符:" << ch << endl; + //将数据再放回缓冲区 + cin.putback(ch); + char buf[1024] = { 0 }; + cin.get(buf,1024); + cout << buf << endl; + +} + +//cin.peek 偷窥 +void test04(){ + + //偷窥下缓冲区的数据 + char ch = cin.peek(); + cout << "偷窥缓冲区数据:" << ch << endl; + char buf[1024] = { 0 }; + cin.get(buf, 1024); + cout << buf << endl; +} + +//练习 作业 使用cin.get和putback完成类似功能 +void test05(){ + + cout << "请输入一个数字或者字符串:" << endl; + char ch = cin.peek(); + if(ch >= '0' && ch <= '9'){ + int number; + cin >> number; + cout << "数字:" << number << endl; + } + else{ + char buf[64] = { 0 }; + cin.getline(buf, 64); + cout << "字符串:" << buf << endl; + } +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + test04(); + test05(); + return 0; +} diff --git a/p01/ch09/s02/CMakeLists.txt b/p01/ch09/s02/CMakeLists.txt new file mode 100644 index 0000000..0bdb277 --- /dev/null +++ b/p01/ch09/s02/CMakeLists.txt @@ -0,0 +1,7 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch09/s02 +add_executable(p1c09_02_01 stl_components_case.cpp) +set_target_properties(p1c09_02_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_02_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_02_01 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch09_s02 DEPENDS p1c09_02_01) diff --git a/p01/ch09/s02/stl_components_case.cpp b/p01/ch09/s02/stl_components_case.cpp new file mode 100644 index 0000000..37a2d41 --- /dev/null +++ b/p01/ch09/s02/stl_components_case.cpp @@ -0,0 +1,119 @@ +// ============================================================================ +// p1c09_02_01 — 2.4 案例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954487:0] 2.4 案例 --- +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +using namespace std; + +//STL 中的容器 算法 迭代器 +void test01(){ + vector v; //STL 中的标准容器之一 :动态数组 + v.push_back(1); //vector 容器提供的插入数据的方法 + v.push_back(5); + v.push_back(3); + v.push_back(7); + //迭代器 + vector::iterator pStart = v.begin(); //vector 容器提供了 begin()方法 返回指向第一个元素的迭代器 + vector::iterator pEnd = v.end(); //vector 容器提供了 end()方法 返回指向最后一个元素下一个位置的迭代器 + //通过迭代器遍历 + while (pStart != pEnd){ + cout << *pStart << " "; + pStart++; + } + cout << endl; +} +//STL 容器不单单可以存储基础数据类型,也可以存储类对象 +class Teacher +{ +public: + Teacher(int age) :age(age){}; + ~Teacher(){}; +public: + int age; +}; +void test02(){ + vector v; //存储 Teacher 类型数据的容器 + Teacher t1(10), t2(20), t3(30); + v.push_back(t1); + v.push_back(t2); + v.push_back(t3); + vector::iterator pStart = v.begin(); + vector::iterator pEnd = v.end(); + //通过迭代器遍历 + while (pStart != pEnd){ + cout << pStart->age << " "; + pStart++; + } + cout << endl; +} +//存储 Teacher 类型指针 +void test03(){ + vector v; //存储 Teacher 类型指针 + Teacher* t1 = new Teacher(10); + Teacher* t2 = new Teacher(20); + Teacher* t3 = new Teacher(30); + v.push_back(t1); + v.push_back(t2); + v.push_back(t3); + //拿到容器迭代器 + vector::iterator pStart = v.begin(); + vector::iterator pEnd = v.end(); + //通过迭代器遍历 + while (pStart != pEnd){ + cout << (*pStart)->age << " "; + pStart++; + } + cout << endl; +} +//容器嵌套容器 难点(不理解,可以跳过) +void test04() +{ + vector< vector > v; + vectorv1; + vectorv2; + vectorv3; + + for (int i = 0; i < 5;i++) + { + v1.push_back(i); + v2.push_back(i * 10); + v3.push_back(i * 100); + } + v.push_back(v1); + v.push_back(v2); + v.push_back(v3); + + for (vector< vector >::iterator it = v.begin(); it != v.end();it++) + { + for (vector::iterator subIt = (*it).begin(); subIt != (*it).end(); subIt ++) + { + cout << *subIt << " "; + } + cout << endl; + } +} +int main(){ + //test01(); + //test02(); + //test03(); + test04(); + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch09/s03/CMakeLists.txt b/p01/ch09/s03/CMakeLists.txt new file mode 100644 index 0000000..f180c6c --- /dev/null +++ b/p01/ch09/s03/CMakeLists.txt @@ -0,0 +1,31 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch09/s03 +add_executable(p1c09_03_01 string_and_c_string.cpp) +set_target_properties(p1c09_03_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_01 PRIVATE -Wall -Wextra) +add_executable(p1c09_03_02 vector_iterator.cpp) +set_target_properties(p1c09_03_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_02 PRIVATE -Wall -Wextra) +add_executable(p1c09_03_03 vector_swap_shrink.cpp) +set_target_properties(p1c09_03_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_03 PRIVATE -Wall -Wextra) +add_executable(p1c09_03_04 vector_reserve.cpp) +set_target_properties(p1c09_03_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_04 PRIVATE -Wall -Wextra) +add_executable(p1c09_03_05 list_data_structure.cpp) +set_target_properties(p1c09_03_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_05 PRIVATE -Wall -Wextra) +add_executable(p1c09_03_06 set_find_operations.cpp) +set_target_properties(p1c09_03_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_06 PRIVATE -Wall -Wextra) +add_executable(p1c09_03_07 multimap_case.cpp) +set_target_properties(p1c09_03_07 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_03_07 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_03_07 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch09_s03 DEPENDS p1c09_03_01 p1c09_03_02 p1c09_03_03 p1c09_03_04 p1c09_03_05 p1c09_03_06 p1c09_03_07) diff --git a/p01/ch09/s03/list_data_structure.cpp b/p01/ch09/s03/list_data_structure.cpp new file mode 100644 index 0000000..7f1975f --- /dev/null +++ b/p01/ch09/s03/list_data_structure.cpp @@ -0,0 +1,44 @@ +// ============================================================================ +// p1c09_03_05 — 3.6.3 list容器的数据结构 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954488:4] 3.6.3 list容器的数据结构 --- +#include +#include +using namespace std; + +// 【可移植性说明】原文直接访问 MSVC STL 的 list 私有成员 +// (_Nodeptr / _Myhead / _Next / _Mysize / _Myval)来「演示 list 的环形 +// 双向链表结构」。这些是 MSVC 实现细节,libstdc++(gcc)无等价物,无法移植。 +// 下面改用标准迭代器接口遍历,并以注释图示说明 list 的环形双向链表结构: +// +// _Myhead (哨兵节点) ──┐ +// ↑ ↓ │ list 内部维护一个哨兵头节点 _Myhead, +// [_Prev|_Myval|_Next] ←──→ [_Prev|_Myval|_Next] ←──→ ... ──┘ +// 末节点.next = _Myhead;_Myhead.prev = 末节点;_Mysize 记录元素个数。 +// 遍历从 _Myhead->_Next 开始,绕一圈回到 _Myhead 即结束。 + +int main(){ + list myList; + for (int i = 0; i < 10; i++){ + myList.push_back(i); + } + + // 标准(可移植)方式:用迭代器遍历环形双向链表 + cout << "size = " << myList.size() << endl; + for (list::iterator it = myList.begin(); it != myList.end(); it++){ + cout << "Node:" << *it << endl; + } + + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch09/s03/multimap_case.cpp b/p01/ch09/s03/multimap_case.cpp new file mode 100644 index 0000000..cdfceb6 --- /dev/null +++ b/p01/ch09/s03/multimap_case.cpp @@ -0,0 +1,200 @@ +// ============================================================================ +// p1c09_03_07 — 3.8.3 multimap案例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954488:6] 3.8.3 multimap案例 --- +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +using namespace std; + +//multimap 案例 +//公司今天招聘了 5 个员工,5 名员工进入公司之后,需要指派员工在那个部门工作 +//人员信息有: 姓名 年龄 电话 工资等组成 +//通过 Multimap 进行信息的插入 保存 显示 +//分部门显示员工信息 显示全部员工信息 + + +#define SALE_DEPATMENT 1 //销售部门 +#define DEVELOP_DEPATMENT 2 //研发部门 +#define FINACIAL_DEPATMENT 3 //财务部门 +#define ALL_DEPATMENT 4 //所有部门 + +//员工类 +class person{ +public: + string name; //员工姓名 + int age; //员工年龄 + double salary; //员工工资 + string tele; //员工电话 +}; + +//创建5个员工 +void CreatePerson(vector& vlist){ + + string seed = "ABCDE"; + for (int i = 0; i < 5; i++){ + person p; + p.name = "员工"; + p.name += seed[i]; + p.age = rand() % 30 + 20; + p.salary = rand() % 20000 + 10000; + p.tele = "010-8888888"; + vlist.push_back(p); + } + +} + +//5名员工分配到不同的部门 +void PersonByGroup(vector& vlist, multimap& plist){ + + + int operate = -1; //用户的操作 + + for (vector::iterator it = vlist.begin(); it != vlist.end(); it++){ + + cout << "当前员工信息:" << endl; + cout << "姓名:" << it->name << " 年龄:" << it->age << " 工资:" << it->salary << " 电话:" << it->tele << endl; + cout << "请对该员工进行部门分配(1 销售部门, 2 研发部门, 3 财务部门):" << endl; + scanf("%d", &operate); + + while (true){ + + if (operate == SALE_DEPATMENT){ //将该员工加入到销售部门 + plist.insert(make_pair(SALE_DEPATMENT, *it)); + break; + } + else if (operate == DEVELOP_DEPATMENT){ + plist.insert(make_pair(DEVELOP_DEPATMENT, *it)); + break; + } + else if (operate == FINACIAL_DEPATMENT){ + plist.insert(make_pair(FINACIAL_DEPATMENT, *it)); + break; + } + else{ + cout << "您的输入有误,请重新输入(1 销售部门, 2 研发部门, 3 财务部门):" << endl; + scanf("%d", &operate); + } + + } + + } + cout << "员工部门分配完毕!" << endl; + cout << "***********************************************************" << endl; + +} + +//打印员工信息 +void printList(multimap& plist, int myoperate){ + + if (myoperate == ALL_DEPATMENT){ + for (multimap::iterator it = plist.begin(); it != plist.end(); it++){ + cout << "姓名:" << it->second.name << " 年龄:" << it->second.age << " 工资:" << it->second.salary << " 电话:" << it->second.tele << endl; + } + return; + } + + multimap::iterator it = plist.find(myoperate); + int depatCount = plist.count(myoperate); + int num = 0; + if (it != plist.end()){ + while (it != plist.end() && num < depatCount){ + cout << "姓名:" << it->second.name << " 年龄:" << it->second.age << " 工资:" << it->second.salary << " 电话:" << it->second.tele << endl; + it++; + num++; + } + } +} + +//根据用户操作显示不同部门的人员列表 +void ShowPersonList(multimap& plist, int myoperate){ + + switch (myoperate) + { + case SALE_DEPATMENT: + printList(plist, SALE_DEPATMENT); + break; + case DEVELOP_DEPATMENT: + printList(plist, DEVELOP_DEPATMENT); + break; + case FINACIAL_DEPATMENT: + printList(plist, FINACIAL_DEPATMENT); + break; + case ALL_DEPATMENT: + printList(plist, ALL_DEPATMENT); + break; + } +} + +//用户操作菜单 +void PersonMenue(multimap& plist){ + + int flag = -1; + int isexit = 0; + while (true){ + cout << "请输入您的操作((1 销售部门, 2 研发部门, 3 财务部门, 4 所有部门, 0退出):" << endl; + scanf("%d", &flag); + + switch (flag) + { + case SALE_DEPATMENT: + ShowPersonList(plist, SALE_DEPATMENT); + break; + case DEVELOP_DEPATMENT: + ShowPersonList(plist, DEVELOP_DEPATMENT); + break; + case FINACIAL_DEPATMENT: + ShowPersonList(plist, FINACIAL_DEPATMENT); + break; + case ALL_DEPATMENT: + ShowPersonList(plist, ALL_DEPATMENT); + break; + case 0: + isexit = 1; + break; + default: + cout << "您的输入有误,请重新输入!" << endl; + break; + } + + if (isexit == 1){ + break; + } + } + +} + +int main(){ + + vector vlist; //创建的5个员工 未分组 + multimap plist; //保存分组后员工信息 + + //创建5个员工 + CreatePerson(vlist); + //5名员工分配到不同的部门 + PersonByGroup(vlist, plist); + //根据用户输入显示不同部门员工信息列表 或者 显示全部员工的信息列表 + PersonMenue(plist); + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch09/s03/set_find_operations.cpp b/p01/ch09/s03/set_find_operations.cpp new file mode 100644 index 0000000..b07e984 --- /dev/null +++ b/p01/ch09/s03/set_find_operations.cpp @@ -0,0 +1,103 @@ +// ============================================================================ +// p1c09_03_06 — 3.7.2.5 set查找操作 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +// --- 原文片段 [58954488:5] 3.7.2.5 set查找操作 --- +//插入操作返回值 +void test01(){ + + set s; + pair::iterator,bool> ret = s.insert(10); + if (ret.second){ + cout << "插入成功:" << *ret.first << endl; + } + else{ + cout << "插入失败:" << *ret.first << endl; + } + + ret = s.insert(10); + if(ret.second){ + cout << "插入成功:" << *ret.first << endl; + } + else{ + cout << "插入失败:" << *ret.first << endl; + } + +} + +struct MyCompare02{ + bool operator()(int v1, int v2) const { + return v1 > v2; + } +}; + +//set从大到小 +void test02(){ + + srand((unsigned int)time(NULL)); + //我们发现set容器的第二个模板参数可以设置排序规则,默认规则是less<_Kty> + set s; + for (int i = 0; i < 10;i++){ + s.insert(rand() % 100); + } + + for (set::iterator it = s.begin(); it != s.end(); it ++){ + cout << *it << " "; + } + cout << endl; +} + +//set容器中存放对象 +class Person{ +public: + Person(string name,int age){ + this->mName = name; + this->mAge = age; + } +public: + string mName; + int mAge; +}; + + +struct MyCompare03{ + bool operator()(const Person& p1, const Person& p2) const { + return p1.mAge > p2.mAge; + } +}; + +void test03(){ + + set s; + + Person p1("aaa", 20); + Person p2("bbb", 30); + Person p3("ccc", 40); + Person p4("ddd", 50); + + s.insert(p1); + s.insert(p2); + s.insert(p3); + s.insert(p4); + + for (set::iterator it = s.begin(); it != s.end(); it++){ + cout << "Name:" << it->mName << " Age:" << it->mAge << endl; + } + +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch09/s03/string_and_c_string.cpp b/p01/ch09/s03/string_and_c_string.cpp new file mode 100644 index 0000000..f498409 --- /dev/null +++ b/p01/ch09/s03/string_and_c_string.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// p1c09_03_01 — 3.1.2.9 string和c-style字符串转换 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954488:0] 3.1.2.9 string和c-style字符串转换 --- +static void block_0() { + string s = "abcdefg"; + char& a = s[2]; + char& b = s[3]; + + a = '1'; + b = '2'; + + cout << s << endl; + cout << (int*)s.c_str() << endl; + + s = "pppppppppppppppppppppppp"; + + //a = '1'; + //b = '2'; + + cout << s << endl; + cout << (int*)s.c_str() << endl; +} + +// --- main --- +int main() { + block_0(); + return 0; +} diff --git a/p01/ch09/s03/vector_iterator.cpp b/p01/ch09/s03/vector_iterator.cpp new file mode 100644 index 0000000..9d093af --- /dev/null +++ b/p01/ch09/s03/vector_iterator.cpp @@ -0,0 +1,35 @@ +// ============================================================================ +// p1c09_03_02 — 3.2.2 vector迭代器 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954488:1] 3.2.2 vector迭代器 --- +#define _CRT_SECURE_NO_WARNINGS +#include +#include +using namespace std; + +int main(){ + + vector v; + for (int i = 0; i < 10;i ++){ + v.push_back(i); + cout << v.capacity() << endl; // v.capacity()容器的容量 + } + + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch09/s03/vector_reserve.cpp b/p01/ch09/s03/vector_reserve.cpp new file mode 100644 index 0000000..f656091 --- /dev/null +++ b/p01/ch09/s03/vector_reserve.cpp @@ -0,0 +1,46 @@ +// ============================================================================ +// p1c09_03_04 — 3.2.5.2 reserve预留空间 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954488:3] 3.2.5.2 reserve预留空间 --- +#define _CRT_SECURE_NO_WARNINGS +#include +#include +using namespace std; + +int main(){ + + vector v; + + //预先开辟空间 + v.reserve(100000); + + int* pStart = NULL; + int count = 0; + for (int i = 0; i < 100000;i ++){ + v.push_back(i); + if (pStart != &v[0]){ + pStart = &v[0]; + count++; + } + } + + cout << "count:" << count << endl; + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch09/s03/vector_swap_shrink.cpp b/p01/ch09/s03/vector_swap_shrink.cpp new file mode 100644 index 0000000..1b3ff3b --- /dev/null +++ b/p01/ch09/s03/vector_swap_shrink.cpp @@ -0,0 +1,50 @@ +// ============================================================================ +// p1c09_03_03 — 3.2.5.1巧用swap,收缩内存空间 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +#include +static inline void pause() { printf("按回车键继续..."); (void)getchar(); } + +// --- 原文片段 [58954488:2] 3.2.5.1巧用swap,收缩内存空间 --- +#define _CRT_SECURE_NO_WARNINGS +#include +#include +using namespace std; + +int main(){ + + vector v; + for (int i = 0; i < 100000;i ++){ + v.push_back(i); + } + + cout << "capacity:" << v.capacity() << endl; + cout << "size:" << v.size() << endl; + + //此时 通过resize改变容器大小 + v.resize(10); + + cout << "capacity:" << v.capacity() << endl; + cout << "size:" << v.size() << endl; + + //容量没有改变 + vector(v).swap(v); + + cout << "capacity:" << v.capacity() << endl; + cout << "size:" << v.size() << endl; + + + pause(); + return EXIT_SUCCESS; +} + +// --- main --- diff --git a/p01/ch09/s04/CMakeLists.txt b/p01/ch09/s04/CMakeLists.txt new file mode 100644 index 0000000..78df7fd --- /dev/null +++ b/p01/ch09/s04/CMakeLists.txt @@ -0,0 +1,27 @@ +# Auto-generated by tools/gen_part1.py — do not edit by hand. +# 章节:p01/ch09/s04 +add_executable(p1c09_04_01 function_object.cpp) +set_target_properties(p1c09_04_01 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_04_01 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_04_01 PRIVATE -Wall -Wextra) +add_executable(p1c09_04_02 predicate.cpp) +set_target_properties(p1c09_04_02 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_04_02 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_04_02 PRIVATE -Wall -Wextra) +add_executable(p1c09_04_03 builtin_function_object.cpp) +set_target_properties(p1c09_04_03 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_04_03 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_04_03 PRIVATE -Wall -Wextra) +add_executable(p1c09_04_04 builtin_function_object_2.cpp) +set_target_properties(p1c09_04_04 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_04_04 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_04_04 PRIVATE -Wall -Wextra) +add_executable(p1c09_04_05 traversal_algorithms.cpp) +set_target_properties(p1c09_04_05 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_04_05 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_04_05 PRIVATE -Wall -Wextra) +add_executable(p1c09_04_06 traversal_algorithms_2.cpp) +set_target_properties(p1c09_04_06 PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF) +set_target_properties(p1c09_04_06 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +target_compile_options(p1c09_04_06 PRIVATE -Wall -Wextra) +add_custom_target(all_p01_ch09_s04 DEPENDS p1c09_04_01 p1c09_04_02 p1c09_04_03 p1c09_04_04 p1c09_04_05 p1c09_04_06) diff --git a/p01/ch09/s04/builtin_function_object.cpp b/p01/ch09/s04/builtin_function_object.cpp new file mode 100644 index 0000000..36da51e --- /dev/null +++ b/p01/ch09/s04/builtin_function_object.cpp @@ -0,0 +1,58 @@ +// ============================================================================ +// p1c09_04_03 — 4.3 内建函数对象 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include +#include + +// --- 原文片段 [58954498:2] 4.3 内建函数对象 --- +//取反仿函数 +void test01() +{ + negate n; + cout << n(50) << endl; +} + +//加法仿函数 +void test02() +{ + plus p; + cout << p(10, 20) << endl; +} + +//大于仿函数 +void test03() +{ + vector v; + srand((unsigned int)time(NULL)); + for (int i = 0; i < 10; i++){ + v.push_back(rand() % 100); + } + + for (vector::iterator it = v.begin(); it != v.end(); it++){ + cout << *it << " "; + } + cout << endl; + sort(v.begin(), v.end(), greater()); + + for (vector::iterator it = v.begin(); it != v.end(); it++){ + cout << *it << " "; + } + cout << endl; + +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch09/s04/builtin_function_object_2.cpp b/p01/ch09/s04/builtin_function_object_2.cpp new file mode 100644 index 0000000..9450314 --- /dev/null +++ b/p01/ch09/s04/builtin_function_object_2.cpp @@ -0,0 +1,184 @@ +// ============================================================================ +// p1c09_04_04 — 4.3 内建函数对象 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include +#include + +// --- 原文片段 [58954498:3] 4.3 内建函数对象 --- +//函数适配器bind1st bind2nd +//现在我有这个需求 在遍历容器的时候,我希望将容器中的值全部加上100之后显示出来,怎么做? +//我们直接给函数对象绑定参数 编译阶段就会报错 +//for_each(v.begin(), v.end(), bind2nd(myprint(),100)); +//如果我们想使用绑定适配器,需要我们自己的函数对象继承binary_function 或者 unary_function +//根据我们函数对象是一元函数对象 还是二元函数对象 +class MyPrint :public binary_function +{ +public: + void operator()(int v1,int v2) const + { + cout << "v1 = : " << v1 << " v2 = :" <v; + for (int i = 0; i < 10; i++) + { + v.push_back(i); + } + cout << "请输入起始值:" << endl; + int x; + cin >> x; + + for_each(v.begin(), v.end(), bind1st(MyPrint(), x)); + //for_each(v.begin(), v.end(), bind2nd( MyPrint(),x )); +} +//总结: bind1st和bind2nd区别? +//bind1st : 将参数绑定为函数对象的第一个参数 +//bind2nd : 将参数绑定为函数对象的第二个参数 +//bind1st bind2nd将二元函数对象转为一元函数对象 + + +class GreaterThenFive:public unary_function +{ +public: + bool operator ()(int v) const + { + return v > 5; + } +}; + +//2、取反适配器 +void test02() +{ + vector v; + for (int i = 0; i < 10;i++) + { + v.push_back(i); + } + +// vector::iterator it = find_if(v.begin(), v.end(), GreaterThenFive()); //返回第一个大于5的迭代器 +// vector::iterator it = find_if(v.begin(), v.end(), not1(GreaterThenFive())); //返回第一个小于5迭代器 + //自定义输入 + vector::iterator it = find_if(v.begin(), v.end(), not1 ( bind2nd(greater(),5))); + if (it == v.end()) + { + cout << "没找到" << endl; + } + else + { + cout << "找到" << *it << endl; + } + + //排序 二元函数对象 + sort(v.begin(), v.end(), not2(less())); + for_each(v.begin(), v.end(), [](int val){cout << val << " "; }); + +} +//not1 对一元函数对象取反 +//not2 对二元函数对象取反 + +void MyPrint03(int v,int v2) +{ + cout << v + v2<< " "; +} + +//3、函数指针适配器 ptr_fun +void test03() +{ + vector v; + for (int i = 0; i < 10; i++) + { + v.push_back(i); + } + // ptr_fun( )把一个普通的函数指针适配成函数对象 + for_each(v.begin(), v.end(), bind2nd( ptr_fun( MyPrint03 ), 100)); +} + + +//4、成员函数适配器 +class Person +{ +public: + Person(string name, int age) + { + m_Name = name; + m_Age = age; + } + + //打印函数 + void ShowPerson(){ + cout << "成员函数:" << "Name:" << m_Name << " Age:" << m_Age << endl; + } + void Plus100() + { + m_Age += 100; + } +public: + string m_Name; + int m_Age; +}; + +void MyPrint04(Person &p) +{ + cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl; + +}; + +void test04() +{ + vector v; + Person p1("aaa", 10); + Person p2("bbb", 20); + Person p3("ccc", 30); + Person p4("ddd", 40); + v.push_back(p1); + v.push_back(p2); + v.push_back(p3); + v.push_back(p4); + + //for_each(v.begin(), v.end(), MyPrint04); + //利用 mem_fun_ref 将Person内部成员函数适配 + for_each(v.begin(), v.end(), mem_fun_ref(&Person::ShowPerson)); +// for_each(v.begin(), v.end(), mem_fun_ref(&Person::Plus100)); +// for_each(v.begin(), v.end(), mem_fun_ref(&Person::ShowPerson)); +} + +void test05(){ + + vector v1; + //创建数据 + Person p1("aaa", 10); + Person p2("bbb", 20); + Person p3("ccc", 30); + Person p4("ddd", 40); + + v1.push_back(&p1); + v1.push_back(&p2); + v1.push_back(&p3); + v1.push_back(&p4); + + for_each(v1.begin(), v1.end(), mem_fun(&Person::ShowPerson)); +} + +//如果容器存放的是对象指针, 那么用mem_fun +//如果容器中存放的是对象实体,那么用mem_fun_ref + +// --- main --- +int main() { + test01(); + test02(); + test03(); + test04(); + test05(); + return 0; +} diff --git a/p01/ch09/s04/function_object.cpp b/p01/ch09/s04/function_object.cpp new file mode 100644 index 0000000..a3d046e --- /dev/null +++ b/p01/ch09/s04/function_object.cpp @@ -0,0 +1,67 @@ +// ============================================================================ +// p1c09_04_01 — 4.1 函数对象 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; + +// --- 原文片段 [58954498:0] 4.1 函数对象 --- +//函数对象是重载了函数调用符号的类 +class MyPrint +{ +public: + MyPrint() + { + m_Num = 0; + } + int m_Num; + +public: + void operator() (int num) + { + cout << num << endl; + m_Num++; + } +}; + +//函数对象 +//重载了()操作符的类实例化的对象,可以像普通函数那样调用,可以有参数 ,可以有返回值 +void test01() +{ + MyPrint myPrint; + myPrint(20); + +} +// 函数对象超出了普通函数的概念,可以保存函数的调用状态 +void test02() +{ + MyPrint myPrint; + myPrint(20); + myPrint(20); + myPrint(20); + cout << myPrint.m_Num << endl; +} + +void doBusiness(MyPrint print,int num) +{ + print(num); +} + +//函数对象作为参数 +void test03() +{ + //参数1:匿名函数对象 + doBusiness(MyPrint(),30); +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch09/s04/predicate.cpp b/p01/ch09/s04/predicate.cpp new file mode 100644 index 0000000..3bbfc43 --- /dev/null +++ b/p01/ch09/s04/predicate.cpp @@ -0,0 +1,85 @@ +// ============================================================================ +// p1c09_04_02 — 4.2 谓词 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +// --- 原文片段 [58954498:1] 4.2 谓词 --- +class GreaterThenFive +{ +public: + bool operator()(int num) + { + return num > 5; + } + +}; +//一元谓词 +void test01() +{ + vector v; + for (int i = 0; i < 10;i ++) + { + v.push_back(i); + } + + vector::iterator it = find_if(v.begin(), v.end(), GreaterThenFive()); + if (it == v.end()) + { + cout << "没有找到" << endl; + } + else + { + cout << "找到了: " << *it << endl; + } +} + +//二元谓词 +class MyCompare +{ +public: + bool operator()(int num1, int num2) + { + return num1 > num2; + } +}; + +void test02() +{ + vector v; + v.push_back(10); + v.push_back(40); + v.push_back(20); + v.push_back(90); + v.push_back(60); + + //默认从小到大 + sort(v.begin(), v.end()); + for (vector::iterator it = v.begin(); it != v.end();it++) + { + cout << *it << " "; + } + cout << endl; + cout << "----------------------------" << endl; + //使用函数对象改变算法策略,排序从大到小 + sort(v.begin(), v.end(),MyCompare()); + for (vector::iterator it = v.begin(); it != v.end(); it++) + { + cout << *it << " "; + } + cout << endl; +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p01/ch09/s04/traversal_algorithms.cpp b/p01/ch09/s04/traversal_algorithms.cpp new file mode 100644 index 0000000..c099925 --- /dev/null +++ b/p01/ch09/s04/traversal_algorithms.cpp @@ -0,0 +1,101 @@ +// ============================================================================ +// p1c09_04_05 — 4.3 常用遍历算法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +// --- 原文片段 [58954498:4] 4.3 常用遍历算法 --- +/* + +template inline +void for_each(_InIt _First, _InIt _Last, _Fn1 _Func) +{ + for (; _First != _Last; ++_First) + _Func(*_First); +} + +*/ + +//普通函数 +void print01(int val){ + cout << val << " "; +} +//函数对象 +struct print001{ + void operator()(int val){ + cout << val << " "; + } +}; + +//for_each算法基本用法 +void test01(){ + + vector v; + for (int i = 0; i < 10;i++){ + v.push_back(i); + } + + //遍历算法 + for_each(v.begin(), v.end(), print01); + cout << endl; + + for_each(v.begin(), v.end(), print001()); + cout << endl; + +} + +struct print02{ + print02(){ + mCount = 0; + } + void operator()(int val){ + cout << val << " "; + mCount++; + } + int mCount; +}; + +//for_each返回值 +void test02(){ + + vector v; + for (int i = 0; i < 10; i++){ + v.push_back(i); + } + + print02 p = for_each(v.begin(), v.end(), print02()); + cout << endl; + cout << p.mCount << endl; +} + +struct print03 : public binary_function{ + void operator()(int val,int bindParam) const{ + cout << val + bindParam << " "; + } +}; + +//for_each绑定参数输出 +void test03(){ + + vector v; + for (int i = 0; i < 10; i++){ + v.push_back(i); + } + + for_each(v.begin(), v.end(), bind2nd(print03(),100)); +} + +// --- main --- +int main() { + test01(); + test02(); + test03(); + return 0; +} diff --git a/p01/ch09/s04/traversal_algorithms_2.cpp b/p01/ch09/s04/traversal_algorithms_2.cpp new file mode 100644 index 0000000..0fe625b --- /dev/null +++ b/p01/ch09/s04/traversal_algorithms_2.cpp @@ -0,0 +1,91 @@ +// ============================================================================ +// p1c09_04_06 — 4.3 常用遍历算法 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用 +// // --- 原文片段 [pageId:blockIdx] --- 标注出处。 +// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。 +// ============================================================================ + +#include +using namespace std; +#include +#include + +// --- 原文片段 [58954498:5] 4.3 常用遍历算法 --- +//transform 将一个容器中的值搬运到另一个容器中 +/* + template inline + _OutIt _Transform(_InIt _First, _InIt _Last,_OutIt _Dest, _Fn1 _Func) + { + + for (; _First != _Last; ++_First, ++_Dest) + *_Dest = _Func(*_First); + return (_Dest); + } + + template inline + _OutIt _Transform(_InIt1 _First1, _InIt1 _Last1,_InIt2 _First2, _OutIt _Dest, _Fn2 _Func) + { + for (; _First1 != _Last1; ++_First1, ++_First2, ++_Dest) + *_Dest = _Func(*_First1, *_First2); + return (_Dest); + } +*/ + +struct transformTest01{ + int operator()(int val){ + return val + 100; + } +}; +struct print01{ + void operator()(int val){ + cout << val << " "; + } +}; +void test01(){ + + vector vSource; + for (int i = 0; i < 10;i ++){ + vSource.push_back(i + 1); + } + + //目标容器 + vector vTarget; + //给vTarget开辟空间 + vTarget.resize(vSource.size()); + //将vSource中的元素搬运到vTarget + vector::iterator it = transform(vSource.begin(), vSource.end(), vTarget.begin(), transformTest01()); + //打印 + for_each(vTarget.begin(), vTarget.end(), print01()); cout << endl; + +} + +//将容器1和容器2中的元素相加放入到第三个容器中 +struct transformTest02{ + int operator()(int v1,int v2){ + return v1 + v2; + } +}; +void test02(){ + + vector vSource1; + vector vSource2; + for (int i = 0; i < 10; i++){ + vSource1.push_back(i + 1); + } + + //目标容器 + vector vTarget; + //给vTarget开辟空间 + vTarget.resize(vSource1.size()); + transform(vSource1.begin(), vSource1.end(), vSource2.begin(),vTarget.begin(), transformTest02()); + //打印 + for_each(vTarget.begin(), vTarget.end(), print01()); cout << endl; +} + +// --- main --- +int main() { + test01(); + test02(); + return 0; +} diff --git a/p03/CMakeLists.txt b/p03/CMakeLists.txt new file mode 100644 index 0000000..d373576 --- /dev/null +++ b/p03/CMakeLists.txt @@ -0,0 +1,10 @@ +# Auto-generated by tools/gen_part3.py +add_subdirectory(ch02) +add_subdirectory(ch03) +add_subdirectory(ch04) +add_subdirectory(ch05) +add_subdirectory(ch06) +add_subdirectory(ch08) +add_subdirectory(ch09) +add_subdirectory(ch10) +add_subdirectory(ch11) diff --git a/p03/ch02/CMakeLists.txt b/p03/ch02/CMakeLists.txt new file mode 100644 index 0000000..c9e10bc --- /dev/null +++ b/p03/ch02/CMakeLists.txt @@ -0,0 +1,9 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch02 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(minimal_qt_app minimal_qt_app.cpp) +set_target_properties(minimal_qt_app PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(minimal_qt_app PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(minimal_qt_app PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch02 DEPENDS minimal_qt_app) diff --git a/p03/ch02/README.md b/p03/ch02/README.md new file mode 100644 index 0000000..f55e3ae --- /dev/null +++ b/p03/ch02/README.md @@ -0,0 +1,8 @@ +# p03/ch02 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `minimal_qt_app` | `minimal_qt_app.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh minimal_qt_app` | + diff --git a/p03/ch02/minimal_qt_app.cpp b/p03/ch02/minimal_qt_app.cpp new file mode 100644 index 0000000..7ca343a --- /dev/null +++ b/p03/ch02/minimal_qt_app.cpp @@ -0,0 +1,30 @@ +// ============================================================================ +// minimal_qt_app — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954515:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include + +// 最简单的 Widget:原文 widget.h 的占位实现 +class Widget : public QWidget { + Q_OBJECT +public: + Widget(QWidget* parent = nullptr) : QWidget(parent) {} +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + Widget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "minimal_qt_app.moc" diff --git a/p03/ch03/CMakeLists.txt b/p03/ch03/CMakeLists.txt new file mode 100644 index 0000000..c9a1a28 --- /dev/null +++ b/p03/ch03/CMakeLists.txt @@ -0,0 +1,9 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch03 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(button_creation button_creation.cpp) +set_target_properties(button_creation PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(button_creation PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(button_creation PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch03 DEPENDS button_creation) diff --git a/p03/ch03/README.md b/p03/ch03/README.md new file mode 100644 index 0000000..cf82baa --- /dev/null +++ b/p03/ch03/README.md @@ -0,0 +1,29 @@ +# p03/ch03 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `button_creation` | `button_creation.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh button_creation` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 1] .2 对象模型(对象树) + +```cpp +{ + QWidget window; + QPushButton quit("Quit", &window); +} +``` + +### [block 2] .2 对象模型(对象树) + +```cpp +{ + QPushButton quit("Quit"); + QWidget window; + quit.setParent(&window); +} +``` + diff --git a/p03/ch03/button_creation.cpp b/p03/ch03/button_creation.cpp new file mode 100644 index 0000000..7696199 --- /dev/null +++ b/p03/ch03/button_creation.cpp @@ -0,0 +1,44 @@ +// ============================================================================ +// button_creation — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954525:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget() { + // 第一种创建:先构造再设属性 + QPushButton* btn = new QPushButton; + btn->setParent(this); // 设置父亲 + btn->setText(QStringLiteral("德玛西亚")); // 设置文字 + btn->move(100, 100); // 移动位置 + + // 第二种创建:构造时指定文本与父亲 + QPushButton* btn2 = new QPushButton(QStringLiteral("孙悟空"), this); + btn2->move(100, 140); + + this->resize(600, 400); // 重新指定窗口大小 + this->setWindowTitle(QStringLiteral("第一个项目")); // 设置窗口标题 + this->setFixedSize(600, 400); // 限制窗口大小 + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "button_creation.moc" diff --git a/p03/ch04/CMakeLists.txt b/p03/ch04/CMakeLists.txt new file mode 100644 index 0000000..e1422c8 --- /dev/null +++ b/p03/ch04/CMakeLists.txt @@ -0,0 +1,17 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch04 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(builtin_signal_slot builtin_signal_slot.cpp) +set_target_properties(builtin_signal_slot PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(builtin_signal_slot PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(builtin_signal_slot PRIVATE -Wall -Wextra) +add_executable(custom_signal_slot custom_signal_slot.cpp) +set_target_properties(custom_signal_slot PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(custom_signal_slot PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(custom_signal_slot PRIVATE -Wall -Wextra) +add_executable(lambda_signal_slot lambda_signal_slot.cpp) +set_target_properties(lambda_signal_slot PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(lambda_signal_slot PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(lambda_signal_slot PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch04 DEPENDS builtin_signal_slot custom_signal_slot lambda_signal_slot) diff --git a/p03/ch04/README.md b/p03/ch04/README.md new file mode 100644 index 0000000..0c853ef --- /dev/null +++ b/p03/ch04/README.md @@ -0,0 +1,45 @@ +# p03/ch04 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `builtin_signal_slot` | `builtin_signal_slot.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh builtin_signal_slot` | +| `custom_signal_slot` | `custom_signal_slot.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh custom_signal_slot` | +| `lambda_signal_slot` | `lambda_signal_slot.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh lambda_signal_slot` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 1] .1 系统自带的信号和槽 + +```cpp +connect(sender, signal, receiver, slot); +``` + +### [block 3] .2 自定义信号和槽 + +```cpp +自定义的信号 hungry带参数,需要提供重载的自定义信号和 自定义槽 +void hungury(QString name); 自定义信号 +void treat(QString name ); 自定义槽 +但是由于有两个重名的自定义信号和自定义的槽,直接连接会报错,所以需要利用函数指针来指向函数地址, 然后在做连接 +void (Teacher:: * teacherSingal)(QString) = &Teacher::hungury; +void (Student:: * studentSlot)(QString) = &Student::treat; +connect(teacher,teacherSingal,student,studentSlot); +``` + +### [block 4] .4 Qt4版本的信号槽写法 + +```cpp +connect(zt,SIGNAL(hungry(QString)),st,SLOT(treat(QString))); +``` + +### [block 5] .5 Lambda表达式 + +```cpp +[capture](parameters) mutable ->return-type +{ +statement +} +``` + diff --git a/p03/ch04/builtin_signal_slot.cpp b/p03/ch04/builtin_signal_slot.cpp new file mode 100644 index 0000000..b1f3052 --- /dev/null +++ b/p03/ch04/builtin_signal_slot.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// builtin_signal_slot — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954527:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget() { + // 系统自带的信号和槽:按钮 clicked → 关闭窗口 + QPushButton* quitBtn = new QPushButton(QStringLiteral("关闭窗口"), this); + quitBtn->move(50, 50); + resize(300, 200); + connect(quitBtn, &QPushButton::clicked, this, &QWidget::close); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "builtin_signal_slot.moc" diff --git a/p03/ch04/custom_signal_slot.cpp b/p03/ch04/custom_signal_slot.cpp new file mode 100644 index 0000000..9ecde50 --- /dev/null +++ b/p03/ch04/custom_signal_slot.cpp @@ -0,0 +1,68 @@ +// ============================================================================ +// custom_signal_slot — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954527:2] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include + +// ---- 老师(发送者):饿了就发信号 ---- +class Teacher : public QObject { + Q_OBJECT +public: + explicit Teacher(QObject* p = nullptr) : QObject(p) {} +signals: + void hungry(); // 无参信号 + void hungry(QString food);// 带参信号(重载) +public slots: + void classIsOver() { + emit hungry(); // 触发无参信号 + emit hungry(QString("汉堡")); // 触发带参信号 + } +}; + +// ---- 学生(接收者):请客吃饭 ---- +class Student : public QObject { + Q_OBJECT +public: + explicit Student(QObject* p = nullptr) : QObject(p) {} +public slots: + void treat() { qDebug() << "老师饿了,请老师吃饭!"; } + void treat(QString food) { qDebug() << "老师饿了,请老师吃" << food; } +}; + +// ---- 聚合窗口:连接信号槽并触发演示 ---- +class MyWidget : public QWidget { + Q_OBJECT +public: + MyWidget() { + Teacher* t = new Teacher(this); + Student* s = new Student(this); + // 连接无参信号/槽 + connect(t, QOverload<>::of(&Teacher::hungry), + s, QOverload<>::of(&Student::treat)); + // 带参重载:用 QOverload::of 显式选择重载 + connect(t, QOverload::of(&Teacher::hungry), + s, QOverload::of(&Student::treat)); + // 触发演示 + t->classIsOver(); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + MyWidget w; w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "custom_signal_slot.moc" diff --git a/p03/ch04/lambda_signal_slot.cpp b/p03/ch04/lambda_signal_slot.cpp new file mode 100644 index 0000000..5837a87 --- /dev/null +++ b/p03/ch04/lambda_signal_slot.cpp @@ -0,0 +1,42 @@ +// ============================================================================ +// lambda_signal_slot — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954527:6] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ + QPushButton * myBtn = new QPushButton (this); + QPushButton * myBtn2 = new QPushButton (this); + myBtn2->move(100,100); + int m = 10; + + connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 20; qDebug() << m; }); + + connect(myBtn2,&QPushButton::clicked,this,[=] () { qDebug() << m; }); + + qDebug() << m; + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "lambda_signal_slot.moc" diff --git a/p03/ch05/CMakeLists.txt b/p03/ch05/CMakeLists.txt new file mode 100644 index 0000000..f4e5d16 --- /dev/null +++ b/p03/ch05/CMakeLists.txt @@ -0,0 +1,13 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch05 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(dock_widget dock_widget.cpp) +set_target_properties(dock_widget PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(dock_widget PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(dock_widget PRIVATE -Wall -Wextra) +add_executable(central_widget central_widget.cpp) +set_target_properties(central_widget PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(central_widget PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(central_widget PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch05 DEPENDS dock_widget central_widget) diff --git a/p03/ch05/README.md b/p03/ch05/README.md new file mode 100644 index 0000000..bb007f9 --- /dev/null +++ b/p03/ch05/README.md @@ -0,0 +1,62 @@ +# p03/ch05 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `dock_widget` | `dock_widget.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh dock_widget` | +| `central_widget` | `central_widget.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh central_widget` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 0] .1 菜单栏 + +```cpp +QMenuBar *menuBar() const +``` + +### [block 1] .1 菜单栏 + +```cpp +QAction* addMenu(QMenu * menu) +QMenu* addMenu(const QString & title) +QMenu* addMenu(const QIcon & icon, const QString & title) +``` + +### [block 2] .1 菜单栏 + +```cpp +QAction* activeAction() const +QAction* addAction(const QString & text) +QAction* addAction(const QIcon & icon, const QString & text) +QAction* addAction(const QString & text, const QObject * receiver, +const char * member, const QKeySequence & shortcut = 0) +QAction* addAction(const QIcon & icon, const QString & text, +const QObject * receiver, const char * member, +const QKeySequence & shortcut = 0) +``` + +### [block 3] .3 状态栏 + +```cpp +//添加小部件 +void addWidget(QWidget * widget, int stretch = 0) +//插入小部件 +intinsertWidget(int index, QWidget * widget, int stretch = 0) +//删除小部件 +void removeWidget(QWidget * widget) +``` + +### [block 6] .6 资源文件 + +```cpp + + +document-open.png + + +document-open-fr.png + + +``` + diff --git a/p03/ch05/central_widget.cpp b/p03/ch05/central_widget.cpp new file mode 100644 index 0000000..a1cc4e3 --- /dev/null +++ b/p03/ch05/central_widget.cpp @@ -0,0 +1,33 @@ +// ============================================================================ +// central_widget — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954529:5] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoMainWindow : public QMainWindow { + Q_OBJECT +public: + DemoMainWindow(){ + QTextEdit * edit = new QTextEdit(this); + setCentralWidget(edit); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoMainWindow w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "central_widget.moc" diff --git a/p03/ch05/dock_widget.cpp b/p03/ch05/dock_widget.cpp new file mode 100644 index 0000000..96a7f8e --- /dev/null +++ b/p03/ch05/dock_widget.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// dock_widget — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954529:4] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoMainWindow : public QMainWindow { + Q_OBJECT +public: + DemoMainWindow() { + // 铆接部件(停靠窗口):标题 + 父对象 + QDockWidget* dock = new QDockWidget(QStringLiteral("标题"), this); + addDockWidget(Qt::LeftDockWidgetArea, dock); + // 设置可停靠区域范围(左/右/上/下) + dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | + Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoMainWindow w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "dock_widget.moc" diff --git a/p03/ch06/CMakeLists.txt b/p03/ch06/CMakeLists.txt new file mode 100644 index 0000000..d9a5505 --- /dev/null +++ b/p03/ch06/CMakeLists.txt @@ -0,0 +1,33 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch06 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(modal_dialog modal_dialog.cpp) +set_target_properties(modal_dialog PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(modal_dialog PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(modal_dialog PRIVATE -Wall -Wextra) +add_executable(modeless_dialog_stack modeless_dialog_stack.cpp) +set_target_properties(modeless_dialog_stack PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(modeless_dialog_stack PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(modeless_dialog_stack PRIVATE -Wall -Wextra) +add_executable(modeless_dialog_heap modeless_dialog_heap.cpp) +set_target_properties(modeless_dialog_heap PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(modeless_dialog_heap PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(modeless_dialog_heap PRIVATE -Wall -Wextra) +add_executable(modeless_dialog_deleteonclose modeless_dialog_deleteonclose.cpp) +set_target_properties(modeless_dialog_deleteonclose PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(modeless_dialog_deleteonclose PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(modeless_dialog_deleteonclose PRIVATE -Wall -Wextra) +add_executable(message_box_question message_box_question.cpp) +set_target_properties(message_box_question PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(message_box_question PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(message_box_question PRIVATE -Wall -Wextra) +add_executable(message_box_custom message_box_custom.cpp) +set_target_properties(message_box_custom PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(message_box_custom PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(message_box_custom PRIVATE -Wall -Wextra) +add_executable(file_dialog file_dialog.cpp) +set_target_properties(file_dialog PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(file_dialog PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(file_dialog PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch06 DEPENDS modal_dialog modeless_dialog_stack modeless_dialog_heap modeless_dialog_deleteonclose message_box_question message_box_custom file_dialog) diff --git a/p03/ch06/README.md b/p03/ch06/README.md new file mode 100644 index 0000000..818d145 --- /dev/null +++ b/p03/ch06/README.md @@ -0,0 +1,142 @@ +# p03/ch06 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `modal_dialog` | `modal_dialog.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh modal_dialog` | +| `modeless_dialog_stack` | `modeless_dialog_stack.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh modeless_dialog_stack` | +| `modeless_dialog_heap` | `modeless_dialog_heap.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh modeless_dialog_heap` | +| `modeless_dialog_deleteonclose` | `modeless_dialog_deleteonclose.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh modeless_dialog_deleteonclose` | +| `message_box_question` | `message_box_question.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh message_box_question` | +| `message_box_custom` | `message_box_custom.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh message_box_custom` | +| `file_dialog` | `file_dialog.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh file_dialog` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 4] .4 消息对话框 + +```cpp +void about(QWidget * parent, const QString & title, const QString & text) +``` + +### [block 5] .4 消息对话框 + +```cpp +void aboutQt(QWidget * parent, const QString & title = QString()): +``` + +### [block 6] .4 消息对话框 + +```cpp +StandardButton critical(QWidget * parent, +const QString & title, +const QString & text, +StandardButtons buttons = Ok, +StandardButton defaultButton = NoButton): +``` + +### [block 7] .4 消息对话框 + +```cpp +StandardButton information(QWidget * parent, +const QString & title, +const QString & text, +StandardButtons buttons = Ok, +StandardButton defaultButton = NoButton) +``` + +### [block 8] .4 消息对话框 + +```cpp +StandardButton question(QWidget * parent, +const QString & title, +const QString & text, +StandardButtons buttons = StandardButtons( Yes | No ), +StandardButton defaultButton = NoButton) +``` + +### [block 9] .4 消息对话框 + +```cpp +StandardButton warning(QWidget * parent, +const QString & title, +const QString & text, +StandardButtons buttons = Ok, +StandardButton defaultButton = NoButton) +``` + +### [block 13] 6.5 标准文件对话框 + +```cpp +connect(openAction, &QAction::triggered, +this, &MainWindow::openFile); +connect(saveAction, &QAction::triggered, +this, &MainWindow::saveFile); +``` + +### [block 14] 6.5 标准文件对话框 + +```cpp +//打开文件 +void MainWindow::openFile() +{ + QString path = QFileDialog::getOpenFileName(this, + tr("Open File"), ".", tr("Text Files(*.txt)")); + if(!path.isEmpty()) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) +{ + QMessageBox::warning(this, tr("Read File"), + tr("Cannot open file:\n%1").arg(path)); + return; + } + QTextStream in(&file); + textEdit->setText(in.readAll()); + file.close(); + } +else +{ + QMessageBox::warning(this, tr("Path"), + tr("You did not select any file.")); + } +} + +//保存文件 +void MainWindow::saveFile() +{ + QString path = QFileDialog::getSaveFileName(this, + tr("Open File"), ".", tr("Text Files(*.txt)")); + if(!path.isEmpty()) +{ + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) +{ + QMessageBox::warning(this, tr("Write File"), + tr("Cannot open file:\n%1").arg(path)); + return; + } + QTextStream out(&file); + out << textEdit->toPlainText(); + file.close(); + } +else +{ + QMessageBox::warning(this, tr("Path"), + tr("You did not select any file.")); + } +} +``` + +### [block 15] 6.5 标准文件对话框 + +```cpp +QString getOpenFileName(QWidget * parent = 0, + const QString & caption = QString(), + const QString & dir = QString(), + const QString & filter = QString(), + QString * selectedFilter = 0, + Options options = 0) +``` + diff --git a/p03/ch06/file_dialog.cpp b/p03/ch06/file_dialog.cpp new file mode 100644 index 0000000..2f11bb4 --- /dev/null +++ b/p03/ch06/file_dialog.cpp @@ -0,0 +1,68 @@ +// ============================================================================ +// file_dialog — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:12] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + MainWindow() { + QAction* openAction = new QAction(QStringLiteral("打开..."), this); + QAction* saveAction = new QAction(QStringLiteral("保存..."), this); + menuBar()->addMenu(QStringLiteral("文件"))->addAction(openAction); + menuBar()->addMenu(QStringLiteral("文件"))->addAction(saveAction); + connect(openAction, &QAction::triggered, this, &MainWindow::openFile); + connect(saveAction, &QAction::triggered, this, &MainWindow::saveFile); + } +private slots: + void openFile() { + QString path = QFileDialog::getOpenFileName(this, QStringLiteral("打开文件")); + if (path.isEmpty()) return; + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::warning(this, QStringLiteral("错误"), QStringLiteral("打开失败")); + return; + } + QTextStream in(&file); + QString text = in.readAll(); + file.close(); + QMessageBox::information(this, QStringLiteral("内容"), text); + } + void saveFile() { + QString path = QFileDialog::getSaveFileName(this, QStringLiteral("保存文件")); + if (path.isEmpty()) return; + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::warning(this, QStringLiteral("错误"), QStringLiteral("保存失败")); + return; + } + QTextStream out(&file); + out << QStringLiteral("示例保存内容"); + file.close(); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + MainWindow w; w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "file_dialog.moc" diff --git a/p03/ch06/message_box_custom.cpp b/p03/ch06/message_box_custom.cpp new file mode 100644 index 0000000..1068048 --- /dev/null +++ b/p03/ch06/message_box_custom.cpp @@ -0,0 +1,55 @@ +// ============================================================================ +// message_box_custom — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:11] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ doDemo(); } +private slots: + void doDemo(){ + QMessageBox msgBox; + msgBox.setText(tr("The document has been modified.")); + msgBox.setInformativeText(tr("Do you want to save your changes?")); + msgBox.setDetailedText(tr("Differences here...")); + msgBox.setStandardButtons(QMessageBox::Save + | QMessageBox::Discard + | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + qDebug() << "Save document!"; + break; + case QMessageBox::Discard: + qDebug() << "Discard changes!"; + break; + case QMessageBox::Cancel: + qDebug() << "Close document!"; + break; + } + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "message_box_custom.moc" diff --git a/p03/ch06/message_box_question.cpp b/p03/ch06/message_box_question.cpp new file mode 100644 index 0000000..ecb2cf0 --- /dev/null +++ b/p03/ch06/message_box_question.cpp @@ -0,0 +1,47 @@ +// ============================================================================ +// message_box_question — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:10] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ doDemo(); } +private slots: + void doDemo(){ + if (QMessageBox::Yes == QMessageBox::question(this, + tr("Question"), tr("Are you OK?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes)) + { + QMessageBox::information(this, tr("Hmmm..."), + tr("I'm glad to hear that!")); + } + else + { + QMessageBox::information(this, tr("Hmmm..."), + tr("I'm sorry!")); + } + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "message_box_question.moc" diff --git a/p03/ch06/modal_dialog.cpp b/p03/ch06/modal_dialog.cpp new file mode 100644 index 0000000..9fc9971 --- /dev/null +++ b/p03/ch06/modal_dialog.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// modal_dialog — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ doDemo(); } +private slots: + void doDemo(){ + QDialog dialog; + dialog.setWindowTitle(tr("Hello, dialog!")); + dialog.exec(); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "modal_dialog.moc" diff --git a/p03/ch06/modeless_dialog_deleteonclose.cpp b/p03/ch06/modeless_dialog_deleteonclose.cpp new file mode 100644 index 0000000..d1bacac --- /dev/null +++ b/p03/ch06/modeless_dialog_deleteonclose.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// modeless_dialog_deleteonclose — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:3] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ doDemo(); } +private slots: + void doDemo(){ + QDialog *dialog = new QDialog; + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setWindowTitle(tr("Hello, dialog!")); + dialog->show(); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "modeless_dialog_deleteonclose.moc" diff --git a/p03/ch06/modeless_dialog_heap.cpp b/p03/ch06/modeless_dialog_heap.cpp new file mode 100644 index 0000000..03f0c75 --- /dev/null +++ b/p03/ch06/modeless_dialog_heap.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// modeless_dialog_heap — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:2] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ doDemo(); } +private slots: + void doDemo(){ + QDialog *dialog = new QDialog; + dialog->setWindowTitle(tr("Hello, dialog!")); + dialog->show(); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "modeless_dialog_heap.moc" diff --git a/p03/ch06/modeless_dialog_stack.cpp b/p03/ch06/modeless_dialog_stack.cpp new file mode 100644 index 0000000..2197664 --- /dev/null +++ b/p03/ch06/modeless_dialog_stack.cpp @@ -0,0 +1,36 @@ +// ============================================================================ +// modeless_dialog_stack — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954535:1] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ doDemo(); } +private slots: + void doDemo(){ + QDialog dialog(this); + dialog.setWindowTitle(tr("Hello, dialog!")); + dialog.show(); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "modeless_dialog_stack.moc" diff --git a/p03/ch08/CMakeLists.txt b/p03/ch08/CMakeLists.txt new file mode 100644 index 0000000..80cc1d7 --- /dev/null +++ b/p03/ch08/CMakeLists.txt @@ -0,0 +1,27 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch08 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(label_text_create label_text_create.cpp) +set_target_properties(label_text_create PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(label_text_create PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(label_text_create PRIVATE -Wall -Wextra) +add_executable(label_text_html label_text_html.cpp) +set_target_properties(label_text_html PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(label_text_html PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(label_text_html PRIVATE -Wall -Wextra) +qt5_add_resources(RES label_pixmap.qrc) +add_executable(label_pixmap label_pixmap.cpp ${RES}) +set_target_properties(label_pixmap PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(label_pixmap PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(label_pixmap PRIVATE -Wall -Wextra) +qt5_add_resources(RES label_movie.qrc) +add_executable(label_movie label_movie.cpp ${RES}) +set_target_properties(label_movie PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(label_movie PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(label_movie PRIVATE -Wall -Wextra) +add_executable(custom_widget custom_widget.cpp) +set_target_properties(custom_widget PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(custom_widget PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(custom_widget PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch08 DEPENDS label_text_create label_text_html label_pixmap label_movie custom_widget) diff --git a/p03/ch08/README.md b/p03/ch08/README.md new file mode 100644 index 0000000..fd10e03 --- /dev/null +++ b/p03/ch08/README.md @@ -0,0 +1,94 @@ +# p03/ch08 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `label_text_create` | `label_text_create.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh label_text_create` | +| `label_text_html` | `label_text_html.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh label_text_html` | +| `label_pixmap` | `label_pixmap.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh label_pixmap` | +| `label_movie` | `label_movie.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh label_movie` | +| `custom_widget` | `custom_widget.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh custom_widget` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 0] 显示文字 (普通文本、html) + +```cpp +voidsetText(const QString &) +``` + +### [block 3] 显示图片 + +```cpp +voidsetPixmap(const QPixmap &) +``` + +### [block 4] 显示图片 + +```cpp +QPixmap pixmap; +``` + +### [block 5] 显示图片 + +```cpp +pixmap.load(":/Image/boat.jpg"); +``` + +### [block 6] 显示图片 + +```cpp +QLabel *label = new QLabel; +label.setPixmap(pixmap); +``` + +### [block 7] 显示动画 + +```cpp +voidsetMovie(QMovie * movie) +``` + +### [block 8] 显示动画 + +```cpp +QMovie *movie = new QMovie(":/Mario.gif"); +``` + +### [block 9] 显示动画 + +```cpp +movie->start(); +``` + +### [block 10] 显示动画 + +```cpp +QLabel *label = new QLabel; +label->setMovie(movie); +``` + +### [block 11] 设置/获取内容 + +```cpp +QStringtext() const +``` + +### [block 12] 设置/获取内容 + +```cpp +voidsetText(const QString &) +``` + +### [block 13] 设置显示模式 + +```cpp +voidsetEchoMode(EchoMode mode) +``` + +### [block 14] 设置显示模式 + +```cpp +void setTextMargins(int left, int top, int right, int bottom) +``` + diff --git a/p03/ch08/assets/image_boat.jpg b/p03/ch08/assets/image_boat.jpg new file mode 100644 index 0000000..a6d3421 Binary files /dev/null and b/p03/ch08/assets/image_boat.jpg differ diff --git a/p03/ch08/assets/mario.gif b/p03/ch08/assets/mario.gif new file mode 100644 index 0000000..c071295 Binary files /dev/null and b/p03/ch08/assets/mario.gif differ diff --git a/p03/ch08/custom_widget.cpp b/p03/ch08/custom_widget.cpp new file mode 100644 index 0000000..9358de7 --- /dev/null +++ b/p03/ch08/custom_widget.cpp @@ -0,0 +1,44 @@ +// ============================================================================ +// custom_widget — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954540:15] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include + +// 自定义控件:spinbox 与 slider 联动 +class SmallWidget : public QWidget { + Q_OBJECT +public: + SmallWidget(QWidget* parent = nullptr) : QWidget(parent) { + QSpinBox* spin = new QSpinBox(this); + QSlider* slider = new QSlider(Qt::Horizontal, this); + QHBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(spin); + layout->addWidget(slider); + // spin 变化 → slider 跟随(Qt5 静态转换选重载) + connect(spin, + static_cast(&QSpinBox::valueChanged), + slider, &QSlider::setValue); + // slider 变化 → spin 跟随 + connect(slider, &QSlider::valueChanged, spin, &QSpinBox::setValue); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + SmallWidget w; w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "custom_widget.moc" diff --git a/p03/ch08/label_movie.cpp b/p03/ch08/label_movie.cpp new file mode 100644 index 0000000..201fd16 --- /dev/null +++ b/p03/ch08/label_movie.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// label_movie — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954540:movie] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget() { + QMovie* movie = new QMovie(":/Mario.gif"); // 从资源加载动画 + movie->start(); + QLabel* label = new QLabel(this); + label->setMovie(movie); + label->setGeometry(10, 10, 200, 200); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "label_movie.moc" diff --git a/p03/ch08/label_movie.qrc b/p03/ch08/label_movie.qrc new file mode 100644 index 0000000..13ccd3e --- /dev/null +++ b/p03/ch08/label_movie.qrc @@ -0,0 +1,5 @@ + + + assets/mario.gif + + diff --git a/p03/ch08/label_pixmap.cpp b/p03/ch08/label_pixmap.cpp new file mode 100644 index 0000000..17ff6ea --- /dev/null +++ b/p03/ch08/label_pixmap.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// label_pixmap — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954540:img] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget() { + QPixmap pixmap; + pixmap.load(":/Image/boat.jpg"); // 从资源加载图片 + QLabel* label = new QLabel(this); + label->setPixmap(pixmap); // 注意:label 是指针,用 -> + label->setGeometry(10, 10, 200, 120); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "label_pixmap.moc" diff --git a/p03/ch08/label_pixmap.qrc b/p03/ch08/label_pixmap.qrc new file mode 100644 index 0000000..da4087c --- /dev/null +++ b/p03/ch08/label_pixmap.qrc @@ -0,0 +1,5 @@ + + + assets/image_boat.jpg + + diff --git a/p03/ch08/label_text_create.cpp b/p03/ch08/label_text_create.cpp new file mode 100644 index 0000000..7eff4d7 --- /dev/null +++ b/p03/ch08/label_text_create.cpp @@ -0,0 +1,33 @@ +// ============================================================================ +// label_text_create — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954540:1] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ + QLabel *label = new QLabel; + label->setText("Hello, World!"); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "label_text_create.moc" diff --git a/p03/ch08/label_text_html.cpp b/p03/ch08/label_text_html.cpp new file mode 100644 index 0000000..5e7e3f4 --- /dev/null +++ b/p03/ch08/label_text_html.cpp @@ -0,0 +1,35 @@ +// ============================================================================ +// label_text_html — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954540:2] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include + +class DemoWidget : public QWidget { + Q_OBJECT +public: + DemoWidget(){ + QLabel * label = new QLabel(this); + label ->setText("Hello, World"); + label ->setText("

百度一下

"); + label ->setOpenExternalLinks(true); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + DemoWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "label_text_html.moc" diff --git a/p03/ch09/CMakeLists.txt b/p03/ch09/CMakeLists.txt new file mode 100644 index 0000000..0180d57 --- /dev/null +++ b/p03/ch09/CMakeLists.txt @@ -0,0 +1,17 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch09 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(mouse_event mouse_event.cpp) +set_target_properties(mouse_event PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(mouse_event PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(mouse_event PRIVATE -Wall -Wextra) +add_executable(event_override_customwidget event_override_customwidget.cpp) +set_target_properties(event_override_customwidget PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(event_override_customwidget PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(event_override_customwidget PRIVATE -Wall -Wextra) +add_executable(event_filter_mainwindow event_filter_mainwindow.cpp) +set_target_properties(event_filter_mainwindow PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(event_filter_mainwindow PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(event_filter_mainwindow PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch09 DEPENDS mouse_event event_override_customwidget event_filter_mainwindow) diff --git a/p03/ch09/README.md b/p03/ch09/README.md new file mode 100644 index 0000000..e3f2284 --- /dev/null +++ b/p03/ch09/README.md @@ -0,0 +1,136 @@ +# p03/ch09 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `mouse_event` | `mouse_event.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh mouse_event` | +| `event_override_customwidget` | `event_override_customwidget.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh event_override_customwidget` | +| `event_filter_mainwindow` | `event_filter_mainwindow.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh event_filter_mainwindow` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 1] .1 事件 + +```cpp +label->setMouseTracking(true); +``` + +### [block 3] .2 event() + +```cpp +bool CustomTextEdit::event(QEvent *e) +{ +if (e->type() == QEvent::KeyPress) +{ +QKeyEvent *keyEvent = static_cast(e); +if (keyEvent->key() == Qt::Key_Tab) +{ +qDebug() << "You press tab."; +return true; +} +} +return false; +} +``` + +### [block 4] .2 event() + +```cpp +switch (event->type()) { +case QEvent::MouseMove: +mouseMoveEvent((QMouseEvent*)event); +break; +// ... +} +``` + +### [block 5] .2 event() + +```cpp +//!!! Qt5 +bool QObject::event(QEvent *e) +{ +switch (e->type()) { +case QEvent::Timer: +timerEvent((QTimerEvent*)e); +break; +case QEvent::ChildAdded: +case QEvent::ChildPolished: +case QEvent::ChildRemoved: +childEvent((QChildEvent*)e); +break; +// ... +default: +if (e->type() >= QEvent::User) { +customEvent(e); +break; +} +return false; +} +return true; +} +``` + +### [block 7] .3 事件过滤器 + +```cpp +bool CustomWidget::event(QEvent *e) +{ +if (e->type() == QEvent::KeyPress) { +QKeyEvent *keyEvent = static_cast(e); +if (keyEvent->key() == Qt::Key_Tab) { +qDebug() << "You press tab."; +return true; +} +} +return QWidget::event(e); +} +现在,我们可以给出一个使用事件过滤器的版本: +bool FilterObject::eventFilter(QObject *object, QEvent *event) +{ +if (object == target && event->type() == QEvent::KeyPress) +{ +QKeyEvent *keyEvent = static_cast(event); +if (keyEvent->key() == Qt::Key_Tab) { +qDebug() << "You press tab."; +return true; +} else { +return false; +} +} +return false; +} +``` + +### [block 8] .4 总结 + +```cpp +LRESULT CALLBACK WndProc(HWND hWnd, +UINT message, +WPARAM wParam, +LPARAM lParam) +``` + +### [block 9] .4 总结 + +```cpp +switch(message) +{ +case WM_PAINT: +// ... +break; +case WM_DESTROY: +// ... +break; +... +} +``` + +### [block 10] .4 总结 + +```cpp +virtual bool QCoreApplication::notify ( QObject * receiver, +QEvent * event ); +``` + diff --git a/p03/ch09/event_filter_mainwindow.cpp b/p03/ch09/event_filter_mainwindow.cpp new file mode 100644 index 0000000..4944216 --- /dev/null +++ b/p03/ch09/event_filter_mainwindow.cpp @@ -0,0 +1,47 @@ +// ============================================================================ +// event_filter_mainwindow — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954546:6] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include + +// 事件过滤器:拦截 label 上的事件 +class MainWindow : public QMainWindow { + Q_OBJECT +public: + MainWindow() { + label = new QLabel(QStringLiteral("事件过滤器示例"), this); + label->setGeometry(10, 30, 200, 30); + label->installEventFilter(this); // 给 label 安装事件过滤器 + } +protected: + bool eventFilter(QObject* watched, QEvent* event) override { + if (watched == label && event->type() == QEvent::MouseButtonPress) { + qDebug() << "label 上的鼠标按下事件被过滤器拦截"; + return true; // 拦截,不再派发 + } + return QMainWindow::eventFilter(watched, event); + } +private: + QLabel* label; +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + MainWindow w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "event_filter_mainwindow.moc" diff --git a/p03/ch09/event_override_customwidget.cpp b/p03/ch09/event_override_customwidget.cpp new file mode 100644 index 0000000..56ed245 --- /dev/null +++ b/p03/ch09/event_override_customwidget.cpp @@ -0,0 +1,43 @@ +// ============================================================================ +// event_override_customwidget — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954546:2] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include + +class CustomWidget : public QWidget { + Q_OBJECT +public: + explicit CustomWidget(QWidget* p = nullptr) : QWidget(p) {} +protected: + bool event(QEvent* e) override { + if (e->type() == QEvent::KeyPress) { + QKeyEvent* ke = static_cast(e); + if (ke->key() == Qt::Key_Tab) { + qDebug() << "Tab pressed in CustomWidget"; + return true; // 事件已处理,不再向上传递 + } + } + return QWidget::event(e); // 其余交给基类 + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + CustomWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "event_override_customwidget.moc" diff --git a/p03/ch09/mouse_event.cpp b/p03/ch09/mouse_event.cpp new file mode 100644 index 0000000..84aaba9 --- /dev/null +++ b/p03/ch09/mouse_event.cpp @@ -0,0 +1,49 @@ +// ============================================================================ +// mouse_event — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954546:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include + +// 自定义 QLabel:重写鼠标事件,显示坐标 +class EventLabel : public QLabel { + Q_OBJECT +public: + explicit EventLabel(QWidget* p = nullptr) : QLabel(p) { + setMouseTracking(true); // 启用鼠标移动追踪(默认只在按键时追踪) + setText(QStringLiteral("移动鼠标到这里")); + } +protected: + void mouseMoveEvent(QMouseEvent* event) override { + setText(QString("

Move: (%1, %2)

") + .arg(event->x()).arg(event->y())); + } + void mousePressEvent(QMouseEvent* event) override { + setText(QString("

Press: (%1, %2)

") + .arg(event->x()).arg(event->y())); + } + void mouseReleaseEvent(QMouseEvent* event) override { + setText(QString("

Release: (%1, %2)

") + .arg(event->x()).arg(event->y())); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + EventLabel w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "mouse_event.moc" diff --git a/p03/ch10/CMakeLists.txt b/p03/ch10/CMakeLists.txt new file mode 100644 index 0000000..804e175 --- /dev/null +++ b/p03/ch10/CMakeLists.txt @@ -0,0 +1,22 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch10 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(qpainter_basic qpainter_basic.cpp) +set_target_properties(qpainter_basic PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(qpainter_basic PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(qpainter_basic PRIVATE -Wall -Wextra) +qt5_add_resources(RES paint_device_pixmaps.qrc) +add_executable(paint_device_pixmaps paint_device_pixmaps.cpp ${RES}) +set_target_properties(paint_device_pixmaps PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(paint_device_pixmaps PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(paint_device_pixmaps PRIVATE -Wall -Wextra) +add_executable(paint_device_qimage paint_device_qimage.cpp) +set_target_properties(paint_device_qimage PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(paint_device_qimage PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(paint_device_qimage PRIVATE -Wall -Wextra) +add_executable(qpicture_io qpicture_io.cpp) +set_target_properties(qpicture_io PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(qpicture_io PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(qpicture_io PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch10 DEPENDS qpainter_basic paint_device_pixmaps paint_device_qimage qpicture_io) diff --git a/p03/ch10/README.md b/p03/ch10/README.md new file mode 100644 index 0000000..1a6a851 --- /dev/null +++ b/p03/ch10/README.md @@ -0,0 +1,34 @@ +# p03/ch10 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `qpainter_basic` | `qpainter_basic.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh qpainter_basic` | +| `paint_device_pixmaps` | `paint_device_pixmaps.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh paint_device_pixmaps` | +| `paint_device_qimage` | `paint_device_qimage.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh paint_device_qimage` | +| `qpicture_io` | `qpicture_io.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh qpicture_io` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 1] .1 QPainter + +```cpp +PaintedWidget::PaintedWidget(QWidget *parent) : +QWidget(parent) +{ +resize(800, 600); +setWindowTitle(tr("Paint Demo")); +} +void PaintedWidget::paintEvent(QPaintEvent *) +{ +QPainter painter(this); +painter.drawLine(80, 100, 650, 500); +painter.setPen(Qt::red); +painter.drawRect(10, 10, 100, 400); +painter.setPen(QPen(Qt::green, 5)); +painter.setBrush(Qt::blue); +painter.drawEllipse(50, 150, 400, 200); +} +``` + diff --git a/p03/ch10/assets/image_butterfly.png b/p03/ch10/assets/image_butterfly.png new file mode 100644 index 0000000..a6d3421 Binary files /dev/null and b/p03/ch10/assets/image_butterfly.png differ diff --git a/p03/ch10/assets/image_butterfly1.png b/p03/ch10/assets/image_butterfly1.png new file mode 100644 index 0000000..a6d3421 Binary files /dev/null and b/p03/ch10/assets/image_butterfly1.png differ diff --git a/p03/ch10/paint_device_pixmaps.cpp b/p03/ch10/paint_device_pixmaps.cpp new file mode 100644 index 0000000..9a679eb --- /dev/null +++ b/p03/ch10/paint_device_pixmaps.cpp @@ -0,0 +1,44 @@ +// ============================================================================ +// paint_device_pixmaps — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954548:2] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include +#include + +class PaintWidget : public QWidget { + Q_OBJECT +public: + PaintWidget(QWidget* parent = nullptr) : QWidget(parent) { setFixedSize(360, 200); } +protected: + void paintEvent(QPaintEvent*) override { + QPainter painter(this); + QPixmap pix(":/Image/butterfly.png"); // 从资源加载图片 + painter.drawPixmap(10, 10, pix); + QPixmap pix1(":/Image/butterfly1.png"); + painter.drawPixmap(180, 10, pix1); + // QBitmap 是 QPixmap 子类,仅 1 位深度(黑白掩码) + QBitmap bmp = pix.mask(); + painter.drawPixmap(10, 110, bmp); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + PaintWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "paint_device_pixmaps.moc" diff --git a/p03/ch10/paint_device_pixmaps.qrc b/p03/ch10/paint_device_pixmaps.qrc new file mode 100644 index 0000000..5665fd6 --- /dev/null +++ b/p03/ch10/paint_device_pixmaps.qrc @@ -0,0 +1,6 @@ + + + assets/image_butterfly.png + assets/image_butterfly1.png + + diff --git a/p03/ch10/paint_device_qimage.cpp b/p03/ch10/paint_device_qimage.cpp new file mode 100644 index 0000000..ad53ca0 --- /dev/null +++ b/p03/ch10/paint_device_qimage.cpp @@ -0,0 +1,44 @@ +// ============================================================================ +// paint_device_qimage — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954548:3] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include + +class PaintWidget : public QWidget { + Q_OBJECT +public: + PaintWidget(QWidget* parent = nullptr) : QWidget(parent) { setFixedSize(200, 200); } +protected: + void paintEvent(QPaintEvent*) override { + QPainter painter(this); + QImage image(100, 100, QImage::Format_RGB32); + // QImage 可直接逐像素操作(QPixmap 不行) + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + image.setPixel(x, y, qRgb((x * 2) % 256, (y * 2) % 256, 128)); + } + } + painter.drawImage(10, 10, image); + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + PaintWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "paint_device_qimage.moc" diff --git a/p03/ch10/qpainter_basic.cpp b/p03/ch10/qpainter_basic.cpp new file mode 100644 index 0000000..fa7dc35 --- /dev/null +++ b/p03/ch10/qpainter_basic.cpp @@ -0,0 +1,40 @@ +// ============================================================================ +// qpainter_basic — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954548:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +class PaintedWidget : public QWidget { + Q_OBJECT +public: + PaintedWidget(QWidget* parent = nullptr) : QWidget(parent) { + setFixedSize(300, 200); + } +protected: + void paintEvent(QPaintEvent*) override { + QPainter painter(this); + painter.drawLine(QLine(10, 10, 200, 10)); // 画线 + painter.drawRect(QRect(10, 30, 100, 50)); // 画矩形 + painter.drawEllipse(QRect(150, 30, 100, 80)); // 画椭圆 + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + PaintedWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "qpainter_basic.moc" diff --git a/p03/ch10/qpicture_io.cpp b/p03/ch10/qpicture_io.cpp new file mode 100644 index 0000000..3ed2983 --- /dev/null +++ b/p03/ch10/qpicture_io.cpp @@ -0,0 +1,49 @@ +// ============================================================================ +// qpicture_io — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954548:4] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include +#include +#include + +class PaintWidget : public QWidget { + Q_OBJECT +public: + PaintWidget(QWidget* parent = nullptr) : QWidget(parent) { setFixedSize(200, 200); } +protected: + void paintEvent(QPaintEvent*) override { + // QPicture 记录并回放绘图指令 + QPicture picture; + QPainter painter; + painter.begin(&picture); // 把绘图记录到 picture + painter.drawLine(10, 10, 100, 100); + painter.end(); + picture.save("drawing.pic"); // 保存(用相对路径,跨平台) + + QPicture picture2; + picture2.load("drawing.pic"); // 回放 + QPainter p2(this); + p2.drawPicture(10, 10, picture2); + qDebug() << "QPicture saved & replayed"; + } +}; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + PaintWidget w; + w.show(); + // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞) + QTimer::singleShot(200, &app, &QApplication::quit); + return app.exec(); +} + +#include "qpicture_io.moc" diff --git a/p03/ch11/CMakeLists.txt b/p03/ch11/CMakeLists.txt new file mode 100644 index 0000000..8736694 --- /dev/null +++ b/p03/ch11/CMakeLists.txt @@ -0,0 +1,17 @@ +# Auto-generated by tools/gen_part3.py — do not edit by hand. +# 章节:p03/ch11 +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +add_executable(qfile_basic qfile_basic.cpp) +set_target_properties(qfile_basic PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(qfile_basic PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(qfile_basic PRIVATE -Wall -Wextra) +add_executable(qdatastream_io qdatastream_io.cpp) +set_target_properties(qdatastream_io PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(qdatastream_io PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(qdatastream_io PRIVATE -Wall -Wextra) +add_executable(qtextstream_io qtextstream_io.cpp) +set_target_properties(qtextstream_io PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF AUTOMOC ON) +target_link_libraries(qtextstream_io PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets) +target_compile_options(qtextstream_io PRIVATE -Wall -Wextra) +add_custom_target(all_p03_ch11 DEPENDS qfile_basic qdatastream_io qtextstream_io) diff --git a/p03/ch11/README.md b/p03/ch11/README.md new file mode 100644 index 0000000..cfa2a57 --- /dev/null +++ b/p03/ch11/README.md @@ -0,0 +1,77 @@ +# p03/ch11 — Qt 示例 + +## 可运行示例 + +| 目标 | 源文件 | 说明 | +|---|---|---| +| `qfile_basic` | `qfile_basic.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh qfile_basic` | +| `qdatastream_io` | `qdatastream_io.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh qdatastream_io` | +| `qtextstream_io` | `qtextstream_io.cpp` | 构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh qtextstream_io` | + +## 参考片段(签名/伪代码,未单独建目标) + +### [block 1] .1 基本文件操作 + +```cpp +QFileInfo fi("/tmp/archive.tar.gz"); +QString base = fi.baseName(); // base = "archive" +QString base = fi.completeBaseName(); // base = "archive.tar" +QString ext = fi.suffix(); // ext = "gz" +QString ext = fi.completeSuffix(); // ext = "tar.gz" +``` + +### [block 2] .2 二进制文件读写 + +```cpp +QFile file("file.dat"); +file.open(QIODevice::WriteOnly); +QDataStream out(&file); +out << QString("the answer is"); +out << (qint32)42; +``` + +### [block 3] .2 二进制文件读写 + +```cpp +QFile file("file.dat"); +file.open(QIODevice::ReadOnly); +QDataStream in(&file); +QString str; +qint32 a; +in >> str >> a; +``` + +### [block 4] .2 二进制文件读写 + +```cpp +QFile file("file.dat"); +file.open(QIODevice::ReadWrite); +QDataStream stream(&file); +QString str = "the answer is 42"; +stream << str; +``` + +### [block 5] .3 文本文件读写 + +```cpp +QFile data("file.txt"); +if (data.open(QFile::WriteOnly | QIODevice::Truncate)) +{ +QTextStream out(&data); +out << "The answer is " << 42; +} +``` + +### [block 6] .3 文本文件读写 + +```cpp +QFile data("file.txt"); +if (data.open(QFile::ReadOnly)) +{ +QTextStream in(&data); +QString str; +int ans = 0; +in >> str >> ans; +} +``` + diff --git a/p03/ch11/qdatastream_io.cpp b/p03/ch11/qdatastream_io.cpp new file mode 100644 index 0000000..51f2f82 --- /dev/null +++ b/p03/ch11/qdatastream_io.cpp @@ -0,0 +1,45 @@ +// ============================================================================ +// qdatastream_io — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954552:data] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +// 二进制文件读写:先写 file.dat,再读回 +int main(int argc, char* argv[]) { + QCoreApplication app(argc, argv); + + // ---- 写 ---- + { + QFile file("file.dat"); + if (file.open(QIODevice::WriteOnly)) { + QDataStream out(&file); + out << QString("hello") << qint32(42) << 3.14; + file.close(); + } + } + // ---- 读 ---- + { + QFile file("file.dat"); + if (file.open(QIODevice::ReadOnly)) { + QDataStream in(&file); + QString s; qint32 n; double d; + in >> s >> n >> d; + qDebug() << "read:" << s << n << d; + file.close(); + } else { + qDebug() << "open failed"; + } + } + + QTimer::singleShot(0, &app, &QCoreApplication::quit); + return app.exec(); +} diff --git a/p03/ch11/qfile_basic.cpp b/p03/ch11/qfile_basic.cpp new file mode 100644 index 0000000..17fac31 --- /dev/null +++ b/p03/ch11/qfile_basic.cpp @@ -0,0 +1,37 @@ +// ============================================================================ +// qfile_basic — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954552:0] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + QCoreApplication app(argc, argv); + + QFile file("in.txt"); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "Open file failed."; + return -1; + } + while (!file.atEnd()) { + qDebug() << file.readLine(); + } + + QFileInfo info(file); + qDebug() << "isDir:" << info.isDir(); + qDebug() << "isExecutable:" << info.isExecutable(); + qDebug() << "baseName:" << info.baseName(); + qDebug() << "completeBaseName:" << info.completeBaseName(); + qDebug() << "suffix:" << info.suffix(); + qDebug() << "completeSuffix:" << info.completeSuffix(); + + return 0; // 文件 IO 完成,直接退出(无需 app.exec() 事件循环) +} diff --git a/p03/ch11/qtextstream_io.cpp b/p03/ch11/qtextstream_io.cpp new file mode 100644 index 0000000..1ede7ee --- /dev/null +++ b/p03/ch11/qtextstream_io.cpp @@ -0,0 +1,45 @@ +// ============================================================================ +// qtextstream_io — Qt 桌面示例 +// 来源:川大 C++/LinuxC wiki「02.Qt方向」 +// 原文片段:[58954552:text] +// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication +// 外壳、include、修正笔误)。详见 docs/ERRATA.md。 +// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh +// ============================================================================ + +#include +#include +#include +#include +#include + +// 文本文件读写:先写 file.txt,再读回 +int main(int argc, char* argv[]) { + QCoreApplication app(argc, argv); + + // ---- 写 ---- + { + QFile data("file.txt"); + if (data.open(QFile::WriteOnly | QFile::Truncate)) { + QTextStream out(&data); + out << "line1: hello" << endl; + out << "line2: world" << endl; + data.close(); + } + } + // ---- 读 ---- + { + QFile data("file.txt"); + if (data.open(QFile::ReadOnly)) { + QTextStream in(&data); + QString line; + while (in.readLineInto(&line)) { + qDebug() << "read:" << line; + } + data.close(); + } + } + + QTimer::singleShot(0, &app, &QCoreApplication::quit); + return app.exec(); +} diff --git a/p03/ch11/rundata/file.dat b/p03/ch11/rundata/file.dat new file mode 100644 index 0000000..e69de29 diff --git a/p03/ch11/rundata/file.txt b/p03/ch11/rundata/file.txt new file mode 100644 index 0000000..144bb42 --- /dev/null +++ b/p03/ch11/rundata/file.txt @@ -0,0 +1,2 @@ +line1: hello +line2: world diff --git a/p03/ch11/rundata/in.txt b/p03/ch11/rundata/in.txt new file mode 100644 index 0000000..262f19d --- /dev/null +++ b/p03/ch11/rundata/in.txt @@ -0,0 +1 @@ +Hello from in.txt diff --git a/tools/__pycache__/file_name_map.cpython-312.pyc b/tools/__pycache__/file_name_map.cpython-312.pyc new file mode 100644 index 0000000..8bc7763 Binary files /dev/null and b/tools/__pycache__/file_name_map.cpython-312.pyc differ diff --git a/tools/__pycache__/gen_sources.cpython-312.pyc b/tools/__pycache__/gen_sources.cpython-312.pyc new file mode 100644 index 0000000..9c450de Binary files /dev/null and b/tools/__pycache__/gen_sources.cpython-312.pyc differ diff --git a/tools/demo_versions/const_via_pointer.c b/tools/demo_versions/const_via_pointer.c new file mode 100644 index 0000000..6299415 --- /dev/null +++ b/tools/demo_versions/const_via_pointer.c @@ -0,0 +1,30 @@ +/* + * demo_versions/const_via_pointer.c + * -------------------------------------------------------------------------- + * 演示:通过指针改写 const 变量 —— C 与 C++ 行为差异。 + * + * 来源:wiki 块 [58954439:21]「3.8 三目运算符/C/C++ const 异同」: + * const int a = 10; + * int* p = (int*)&a; // 强转去 const + * *p = 100; // C:可能改写(UB,但常被编译器接受); + * // C++:常量折叠,a 仍可能读出 10(替换式优化) + * 两语言下都属未定义行为(UB),但「观察到的值」常不同:C 多见改写生效, + * C++ 常因常量折叠仍打印原值。 + * + * 注:本演示在 -O0 下两者都可能改写;-O2 下 C++ 常量折叠更明显。 + * + * 运行: + * tools/std_probe.py --lang c --file tools/demo_versions/const_via_pointer.c + * tools/std_probe.py --lang c++ --file tools/demo_versions/const_via_pointer.c + * -------------------------------------------------------------------------- + */ +#include + +int main(void) { + const int a = 10; + int* p = (int*)&a; /* 强转去掉 const(UB:试图改写 const 对象) */ + *p = 100; + /* 两语言都编译通过;观察 a 的「读出值」差异(依赖优化级别,属 UB) */ + printf("a = %d\n", a); + return 0; +} diff --git a/tools/demo_versions/dynamic_exception_spec.cpp b/tools/demo_versions/dynamic_exception_spec.cpp new file mode 100644 index 0000000..a6b8b8d --- /dev/null +++ b/tools/demo_versions/dynamic_exception_spec.cpp @@ -0,0 +1,29 @@ +/* + * demo_versions/dynamic_exception_spec.cpp + * -------------------------------------------------------------------------- + * 演示:动态异常规范 throw(类型...) —— C++17 起被移除(C++11 弃用)。 + * + * 来源:wiki 块 [58954474:4]「7.2.4 异常接口声明」原意演示 throw(int,char,char*) + * 与 throw() 异常规范。这些在 C++17 已是语法错误(动态规范被移除)。 + * + * 验证: + * g++ -std=c++14 → PASS(动态规范仍合法,仅弃用警告) + * g++ -std=c++17 → ERROR (动态异常规范非法) + * + * 运行: + * tools/std_probe.py --lang c++ --file tools/demo_versions/dynamic_exception_spec.cpp \ + * --standards c++11,c++14,c++17,c++20 + * -------------------------------------------------------------------------- + */ +#include + +void may_throw_anything() throw(int, char) { // C++17 移除:动态异常规范 + throw 1; +} + +void throws_nothing() throw() { // C++17: throw() 等价 noexcept +} + +int main() { + return 0; +} diff --git a/tools/demo_versions/enum_assign_int.c b/tools/demo_versions/enum_assign_int.c new file mode 100644 index 0000000..6a63a57 --- /dev/null +++ b/tools/demo_versions/enum_assign_int.c @@ -0,0 +1,27 @@ +/* + * demo_versions/enum_assign_int.c + * -------------------------------------------------------------------------- + * 演示:枚举与整型的赋值差异 —— C 宽松,C++ 严格。 + * + * 来源:wiki 块 [58954439:15](C/C++ 类型严格性对照): + * C 中可 `enum Color c = 1;`(整型隐式转 enum); + * C++ 中需 `enum Color c = (Color)1;` 或 `Color c = static_cast(1);`。 + * + * 验证: + * gcc -std=c17 → PASS(int 隐式转 enum 合法) + * g++ -std=c++17 → ERROR (invalid conversion from int to Color) + * + * 运行: + * tools/std_probe.py --lang c --file tools/demo_versions/enum_assign_int.c + * tools/std_probe.py --lang c++ --file tools/demo_versions/enum_assign_int.c + * -------------------------------------------------------------------------- + */ +#include + +enum Color { RED, GREEN, BLUE }; + +int main(void) { + enum Color c = 1; /* C 合法;C++ 报 invalid conversion from int to Color */ + printf("c = %d\n", c); + return 0; +} diff --git a/tools/demo_versions/kr_implicit_int.c b/tools/demo_versions/kr_implicit_int.c new file mode 100644 index 0000000..7ed9ad5 --- /dev/null +++ b/tools/demo_versions/kr_implicit_int.c @@ -0,0 +1,31 @@ +/* + * demo_versions/kr_implicit_int.c + * -------------------------------------------------------------------------- + * 演示:K&R 隐式 int(C89 允许省略类型)。 + * + * 来源:wiki 块 [58954439:14]「3.4 C++中所有的变量和函数都必须有类型」原意是 + * 演示 C89 允许「无类型」函数与参数,C99 起被废除。 + * + * 验证(用 tools/std_probe.py): + * gcc -std=c89 → PASS(隐式 int 合法) + * gcc -std=c99 → WARNING(隐式 int 已弃用) + * gcc -std=c17 → ERROR (隐式 int 非法) + * + * 运行:tools/std_probe.py --lang c --file tools/demo_versions/kr_implicit_int.c + * -------------------------------------------------------------------------- + */ + +/* C89:函数返回类型可省略(隐式 int);参数可只写名字(隐式 int)。 */ +fun1(i) { /* 等价于 int fun1(int i) */ + return i + 1; +} + +fun2(p) /* 等价于 int fun2(char* p) —— 但隐式 int 下 p 被当作 int */ + char* p; +{ + return 0; +} + +int main(void) { + return fun1(41); +} diff --git a/tools/demo_versions/malloc_no_cast.c b/tools/demo_versions/malloc_no_cast.c new file mode 100644 index 0000000..58deadc --- /dev/null +++ b/tools/demo_versions/malloc_no_cast.c @@ -0,0 +1,24 @@ +/* + * demo_versions/malloc_no_cast.c + * -------------------------------------------------------------------------- + * 演示:void* 隐式转换差异 —— C 合法,C++ 必须显式转换。 + * + * 来源:wiki 块 [58954439:15]:C 中 `char* p = malloc(n);` 合法(void* 隐式转任意指针), + * C++ 中 void* 不能隐式转其它指针,必须 `char* p = (char*)malloc(n);`。 + * + * 验证: + * gcc -std=c17 → PASS(void* 隐式转换合法) + * g++ -std=c++17 → ERROR (invalid conversion from void* to char*) + * + * 运行: + * tools/std_probe.py --lang c --file tools/demo_versions/malloc_no_cast.c + * tools/std_probe.py --lang c++ --file tools/demo_versions/malloc_no_cast.c + * -------------------------------------------------------------------------- + */ +#include + +int main(void) { + char* p = malloc(16); /* C 合法;C++ 报 invalid conversion from void* to char* */ + if (p) free(p); + return 0; +} diff --git a/tools/demo_versions/ternary_lvalue.cpp b/tools/demo_versions/ternary_lvalue.cpp new file mode 100644 index 0000000..eb62900 --- /dev/null +++ b/tools/demo_versions/ternary_lvalue.cpp @@ -0,0 +1,26 @@ +/* + * demo_versions/ternary_lvalue.cpp + * -------------------------------------------------------------------------- + * 演示:三目运算符 ?: 作为左值 —— C++ 独有,C 不允许。 + * + * 来源:wiki 块 [58954439:18]「3.8 三目运算符功能增强」:C 中三目返回值(右值), + * C++ 中三目返回的是「两个操作数中公共类型的引用」(可为左值),故可被赋值。 + * + * 验证: + * g++ -std=c++17 → PASS(三目可作左值) + * gcc -std=c17 → ERROR (三目不可作左值,lvalue required as left operand) + * + * 运行: + * tools/std_probe.py --lang c++ --file tools/demo_versions/ternary_lvalue.cpp + * tools/std_probe.py --lang c --file tools/demo_versions/ternary_lvalue.cpp + * -------------------------------------------------------------------------- + */ +#include + +int main() { + int a = 1, b = 2; + /* C++:三目表达式可作为左值被赋值 */ + (a > b ? a : b) = 100; // 把较大者赋为 100 + printf("a=%d b=%d\n", a, b); + return 0; +} diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile new file mode 100644 index 0000000..8042a4e --- /dev/null +++ b/tools/docker/Dockerfile @@ -0,0 +1,34 @@ +# ============================================================================== +# Build-container image for the qt-course repo. +# +# Usage model (see docker_install_qt.sh / docker_build.sh / docker_run_qt.sh): +# 1. Install isolated Qt 5.14.2 INTO THE REPO at .qt514/ (one-off, ~577MB). +# The Qt files live on the host filesystem, mounted into the container. +# 2. Build the Qt part from a container, writing into the mounted build/. +# 3. RUN the compiled Qt apps on the HOST (no container needed) using +# LD_LIBRARY_PATH=.qt514/.../lib — host & container share glibc 2.39, +# so binaries built in the container run natively on the host. Isolation +# from the system Qt 5.15 is by prefix + LD_LIBRARY_PATH, not by the +# container boundary. +# +# This image only needs the build toolchain + aqtinstall. It is small and +# stable (no multi-GB Qt layer baked in; Qt is downloaded into the mount). +# ------------------------------------------------------------------------------ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Shanghai + +# Build toolchain matching the host (gcc 13.3, cmake 3.28, ninja) + runtime +# libs so the container can also run GUI examples with X11 forwarding if needed. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential g++ gcc cmake ninja-build git \ + ca-certificates python3 python3-pip python3-venv \ + libgl1 libegl1 libxkbcommon0 libdbus-1-3 \ + libfontconfig1 libfreetype6 libxcb-cursor0 \ + fontconfig xauth x11-xserver-utils \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install --break-system-packages aqtinstall + +WORKDIR /workspace diff --git a/tools/docker/docker_build.sh b/tools/docker/docker_build.sh new file mode 100755 index 0000000..fae9f33 --- /dev/null +++ b/tools/docker/docker_build.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Build-container: run ONE build pass of the repo (CMake configure + compile), +# mounting the repo so build artifacts land on the host filesystem. +# +# Usage: tools/docker/docker_build.sh [extra cmake args...] +# It builds BOTH part1 (C/C++) and part3 (Qt) unless you pass args to restrict. +# The Qt part is pointed at the isolated .qt514/ toolchain. +# +# After this, run compiled apps on the HOST with tools/run_qt.sh / tools/run.sh. +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +IMAGE_TAG="qt-course:builder" +QT_ROOT="/workspace/.qt514/5.14.2/gcc_64" + +if [ ! -x "$REPO_ROOT/.qt514/5.14.2/gcc_64/bin/qmake" ]; then + echo ">> Isolated Qt not found at .qt514/. Run tools/docker/docker_install_qt.sh first." >&2 + exit 1 +fi + +docker run --rm \ + -v "$REPO_ROOT:/workspace" \ + -w /workspace \ + "$IMAGE_TAG" \ + bash -lc " + set -e + cmake -S . -B build -G Ninja \ + -DBUILD_QT_PART=ON \ + -DCMAKE_PREFIX_PATH='${QT_ROOT}' \ + -DCMAKE_BUILD_TYPE=Debug \ + $* + cmake --build build --parallel + " +echo ">> Build done. Run on host: tools/run.sh (Qt targets: tools/run_qt.sh )" diff --git a/tools/docker/docker_build_image.sh b/tools/docker/docker_build_image.sh new file mode 100755 index 0000000..49d8af5 --- /dev/null +++ b/tools/docker/docker_build_image.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Build the lightweight build-container image (toolchain + aqtinstall, no Qt baked in). +# Usage: tools/docker/docker_build_image.sh +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +IMAGE_TAG="qt-course:builder" +echo ">> Building image ${IMAGE_TAG} ..." +docker build -t "$IMAGE_TAG" -f "$REPO_ROOT/tools/docker/Dockerfile" "$REPO_ROOT/tools/docker" +echo ">> Done. Next: tools/docker/docker_install_qt.sh (downloads Qt 5.14.2 into .qt514/)" diff --git a/tools/docker/docker_install_qt.sh b/tools/docker/docker_install_qt.sh new file mode 100755 index 0000000..043bb98 --- /dev/null +++ b/tools/docker/docker_install_qt.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Install the ISOLATED Qt 5.14.2 toolchain into the repo at .qt514/ using the +# build container (keeps the host free of aqtinstall deps; Qt files land on the +# host filesystem via the mount so the host can build/run against them later). +# +# Usage: tools/docker/docker_install_qt.sh +# Idempotent: re-running refreshes/repairs the install. +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +IMAGE_TAG="qt-course:builder" +QT_DEST="/workspace/.qt514" +QT_VERSION="5.14.2" + +if ! docker image inspect "$IMAGE_TAG" >/dev/null 2>&1; then + echo ">> image ${IMAGE_TAG} not found; building it first." >&2 + "$REPO_ROOT/tools/docker/docker_build_image.sh" +fi + +mkdir -p "$REPO_ROOT/.qt514" +echo ">> Installing Qt ${QT_VERSION} gcc_64 into $REPO_ROOT/.qt514/ (via container)..." +docker run --rm \ + -v "$REPO_ROOT:/workspace" \ + "$IMAGE_TAG" \ + aqt install-qt linux desktop "${QT_VERSION}" gcc_64 -O "${QT_DEST}" + +# Verify on the host (no container needed for this read). +QT_ROOT="$REPO_ROOT/.qt514/${QT_VERSION}/gcc_64" +echo ">> Verify (host):" +"$QT_ROOT/bin/qmake" -query QT_VERSION +echo ">> Qt installed at: $QT_ROOT" +echo ">> Now build with: tools/docker/docker_build.sh (or host cmake, see README)" diff --git a/tools/file_name_map.py b/tools/file_name_map.py new file mode 100644 index 0000000..2932fda --- /dev/null +++ b/tools/file_name_map.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ============================================================================== +# file_name_map.py — 每个 (pageId, section_title) → 英文源文件名词根 +# +# 规则(按用户要求): +# * 源文件名用英文,与章节/小节名称匹配(按章节号或语义命名)。 +# * 同一小节内有多个源文件时,追加语义后缀区分。 +# * 文件名仅含 [a-z0-9_],蛇形命名。 +# +# 本表为人工映射,是 gen_part1.py / gen_part3.py 的输入。 +# 键 = (pageId, section_title);值 = 英文词根(不含扩展名/前缀)。 +# ============================================================================== + +FILE_NAME_MAP = { + # ---- ch02 intro (58954438) ---- + ("58954438", "2.1.1 c++ hello world"): "hello_world", + + # ---- ch03 C++对C的扩展 (58954439) ---- + ("58954439", "3.1 ::作用域运算符"): "scope_resolution_operator", + ("58954439", "3.2.2命名空间使用语法"): "namespace_syntax", + ("58954439", "3.2.3 using声明"): "using_declaration", + ("58954439", "3.2.4 using编译指令"): "using_directive", + ("58954439", "3.3 全局变量检测增强"): "global_variable_detection", + ("58954439", "3.4 C++中所有的变量和函数都必须有类型"): "strict_function_signatures", + ("58954439", "3.5 更严格的类型转换"): "stricter_type_casting", + ("58954439", "3.6 struct类型加强"): "struct_enhancement", + ("58954439", "3.7 \u201c新增\u201dbool类型关键字"): "bool_keyword", + ("58954439", "3.8 三目运算符功能增强"): "ternary_operator", + ("58954439", "3.8.2.3 C/C++中const异同总结"): "const_diff_summary", + ("58954439", "3.9.3 尽量以const替换#define"): "const_vs_define", + ("58954439", "3.10.1 引用基本用法"): "reference_basics", + ("58954439", "3.10.2 函数中的引用"): "reference_in_function", + ("58954439", "3.10.3 引用的本质"): "reference_essence", + ("58954439", "3.10.4 指针引用"): "pointer_reference", + ("58954439", "3.10.5 常量引用"): "const_reference", + ("58954439", "3.11.2 预处理宏的缺陷"): "macro_pitfalls", + ("58954439", "3.11.3.2 类内部的内联函数"): "inline_in_class", + ("58954439", "3.12 函数的默认参数"): "default_arguments", + ("58954439", "3.13 函数的占位参数"): "placeholder_parameters", + ("58954439", "3.14.2.1 函数重载基本语法"): "function_overload_basics", + ("58954439", "3.14.2.2 函数重载实现原理"): "function_overload_mechanism", + ("58954439", "3.14.3 extern \u201cC\u201d浅析"): "extern_c", + + # ---- ch04 class_object ---- + # 01 基本概念 (58954448) + ("58954448", "4.1.2 类的封装"): "class_encapsulation", + ("58954448", "4.1.3 将成员变量设置为private"): "private_members", + # 02 面向对象案例 (58954449) + ("58954449", "4.2.1 设计立方体类"): "cube_class_design", + ("58954449", "4.2.2 点和圆的关系"): "point_and_circle", + # 03 构造和析构 (58954451) + ("58954451", "4.3.2 构造函数和析构函数"): "ctor_and_dtor", + ("58954451", "4.3.3 构造函数的分类及调用"): "ctor_classification", + ("58954451", "4.3.4 拷贝构造函数的调用时机"): "copy_ctor_timing", + ("58954451", "4.3.6 深拷贝和浅拷贝"): "deep_vs_shallow_copy", + ("58954451", "4.3.7.1 初始化列表"): "initializer_list", + ("58954451", "4.3.7.2 类对象作为成员"): "class_object_member", + ("58954451", "4.3.8 explicit关键字"): "explicit_keyword", + ("58954451", "4.3.9.2 C动态分配内存方法"): "c_dynamic_alloc", + ("58954451", "4.3.9.3 new operator"): "new_operator", + ("58954451", "4.3.9.4 delete operator"): "delete_operator", + ("58954451", "4.3.9.5 用于数组的new和delete"): "new_delete_array", + ("58954451", "4.3.9.6 delete void*可能会出错"): "delete_void_ptr", + ("58954451", "4.3.10.1 静态成员变量"): "static_member_variable", + ("58954451", "4.3.10.2 静态成员函数"): "static_member_function", + ("58954451", "4.3.10.3 const静态成员属性"): "const_static_member", + ("58954451", "4.3.10.4 静态成员实现单例模式"): "static_singleton", + # 04 对象模型 (58954456) + ("58954456", "4.4.1 成员变量和函数的存储"): "member_storage", + ("58954456", "4.4.2.2 this指针的使用"): "this_pointer", + ("58954456", "4.4.2.3 const修饰成员函数"): "const_member_function", + ("58954456", "4.4.2.4 const修饰对象(常对象)"): "const_object", + # 05 友元 (58954457) + ("58954457", "4.5.1 友元语法"): "friend_syntax", + # 06 运算符重载 (58954458) + ("58954458", "4.6.2 运算符重载碰上友元函数"): "overload_with_friend", + ("58954458", "4.6.4 自增自减(++/--)运算符重载"): "overload_increment_decrement", + ("58954458", "4.6.5 指针运算符(*、->)重载"): "overload_pointer_operator", + ("58954458", "4.6.6 赋值(=)运算符重载"): "overload_assignment", + ("58954458", "4.6.7 等于和不等于(==、!=)运算符重载"): "overload_equality", + ("58954458", "4.6.8 函数调用符号()重载"): "overload_function_call", + # 07 继承和派生 (58954461) + ("58954461", "4.7.1.1 为什么需要继承"): "why_inheritance", + ("58954461", "4.7.2 派生类访问控制"): "derived_access_control", + ("58954461", "4.7.3.1 继承中的对象模型"): "inheritance_object_model", + ("58954461", "4.7.3.2 对象构造和析构的调用原则"): "inheritance_ctor_dtor_order", + ("58954461", "4.7.3 继承中同名成员的处理方法"): "same_name_members", + ("58954461", "4.7.5 继承中的静态成员特性"): "static_in_inheritance", + ("58954461", "4.7.6.1 多继承概念"): "multiple_inheritance_concept", + ("58954461", "4.7.6.2 菱形继承和虚继承"): "diamond_virtual_inheritance", + ("58954461", "4.7.6.3 虚继承实现原理"): "virtual_inheritance_mechanism", + # 08 多态 (58954469) + ("58954469", "4.8.1 多态基本概念"): "polymorphism_basics", + ("58954469", "4.8.2.1 问题抛出"): "upcast_problem", + ("58954469", "4.8.2.1 问题解决方案(虚函数,vitual function)"): "virtual_function_solution", + ("58954469", "4.8.3 C++如何实现动态绑定"): "dynamic_binding", + ("58954469", "4.8.6.1 虚析构函数作用"): "virtual_dtor_purpose", + ("58954469", "4.8.6.2 纯虚析构函数"): "pure_virtual_dtor", + ("58954469", "4.8.7 重写 重载 重定义"): "override_overload_redefine", + + # ---- ch05 模板 (58954472) ---- + ("58954472", "5.2 函数模板"): "function_template", + ("58954472", "5.3 函数模板和普通函数区别"): "template_vs_function", + ("58954472", "5.4 函数模板和普通函数在一起调用规则"): "template_call_rules", + ("58954472", "5.6模板的局限性"): "template_limitations", + ("58954472", "5.7.1 类模板基本概念"): "class_template_basics", + ("58954472", "5.7.2 类模板做函数参数"): "class_template_as_param", + ("58954472", "5.7.3 类模板派生普通类"): "class_template_derives_class", + ("58954472", "5.7.4 类模板派生类模板"): "class_template_derives_template", + ("58954472", "5.7.5 类模板类内实现"): "class_template_in_class_impl", + ("58954472", "5.7.6 类模板类外实现"): "class_template_out_of_class_impl", + ("58954472", "5.7.7 模板类碰到友元函数"): "class_template_friend", + ("58954472", "5.8 类模板的应用"): "class_template_application", + + # ---- ch06 类型转换 (58954473) ---- + ("58954473", "6.1 静态转换(static_cast)"): "static_cast_demo", + ("58954473", "2.2 动态转换(dynamic_cast)"): "dynamic_cast_demo", + ("58954473", "2.3 常量转换(const_cast)"): "const_cast_demo", + + # ---- ch07 异常 (58954474) ---- + ("58954474", "7.1 异常基本概念"): "exception_basics", + ("58954474", "7.2.1 异常基本语法"): "exception_syntax", + ("58954474", "7.2.2 异常严格类型匹配"): "exception_type_matching", + ("58954474", "7.2.3 栈解旋(unwinding)"): "stack_unwinding", + ("58954474", "7.2.4 异常接口声明"): "exception_specification", + ("58954474", "7.2.5 异常变量生命周期"): "exception_variable_lifetime", + ("58954474", "7.2.6 异常的多态使用"): "exception_polymorphism", + ("58954474", "7.3.1 标准库介绍"): "std_exception_library", + ("58954474", "7.3.2 编写自己的异常类"): "custom_exception_class", + + # ---- ch08 IO流 (58954476) ---- + ("58954476", "8.3 标准输入流"): "standard_input_stream", + ("58954476", "8.4.1 字符输出"): "character_output", + ("58954476", "8.4.2 格式化输出"): "formatted_output", + ("58954476", "8.4.4 C++对ASCII文件的读写操作"): "ascii_file_io", + ("58954476", "8.4.5 C++对二进制文件的读写操作"): "binary_file_io", + + # ---- ch09 STL ---- + # 02 三大组件 (58954487) + ("58954487", "2.4 案例"): "stl_components_case", + # 03 常用容器 (58954488) + ("58954488", "3.1.2.9 string和c-style字符串转换"): "string_and_c_string", + ("58954488", "3.2.2 vector迭代器"): "vector_iterator", + ("58954488", "3.2.5.1巧用swap,收缩内存空间"): "vector_swap_shrink", + ("58954488", "3.2.5.2 reserve预留空间"): "vector_reserve", + ("58954488", "3.6.3 list容器的数据结构"): "list_data_structure", + ("58954488", "3.7.2.5 set查找操作"): "set_find_operations", + ("58954488", "3.8.3 multimap案例"): "multimap_case", + # 04 常用算法 (58954498) + ("58954498", "4.1 函数对象"): "function_object", + ("58954498", "4.2 谓词"): "predicate", + ("58954498", "4.3 内建函数对象"): "builtin_function_object", + ("58954498", "4.3 常用遍历算法"): "traversal_algorithms", + + # ====================================================================== + # Part 3 — Qt 桌面应用开发 (p03) + # ====================================================================== + # ---- ch02 创建Qt项目 (58954515) ---- + ("58954515", "一个最简单的Qt应用程序"): "minimal_qt_app", + + # ---- ch03 第一个Qt小程序 (58954525) ---- + ("58954525", "3.1 按钮的创建"): "button_creation", + ("58954525", "3.2 对象模型(对象树)"): "object_tree_model", + + # ---- ch04 信号和槽机制 (58954527) ---- + ("58954527", "4.1 系统自带的信号和槽"): "builtin_signal_slot", + ("58954527", "4.2 自定义信号和槽"): "custom_signal_slot", + ("58954527", "4.4 Qt4版本的信号槽写法"): "qt4_signal_slot_style", + ("58954527", "4.5 Lambda表达式"): "lambda_signal_slot", + + # ---- ch05 QMainWindow (58954529) ---- + ("58954529", "5.1 菜单栏"): "menubar", + ("58954529", "5.3 状态栏"): "statusbar", + ("58954529", "5.4 铆接部件"): "dock_widget", + ("58954529", "5.5 核心部件(中心部件)"): "central_widget", + ("58954529", "5.6 资源文件"): "resource_file", + + # ---- ch06 对话框QDialog (58954535) ---- + ("58954535", "模态对话框"): "modal_dialog", + ("58954535", "非模态对话框"): "modeless_dialog", + ("58954535", "6.4 消息对话框"): "message_box", + ("58954535", "6.5 标准文件对话框"): "file_dialog", + + # ---- ch08 常用控件 (58954540) ---- + ("58954540", "显示文字 (普通文本、html)"): "label_text", + ("58954540", "显示图片"): "label_pixmap", + ("58954540", "显示动画"): "label_movie", + ("58954540", "设置/获取内容"): "lineedit_content", + ("58954540", "设置显示模式"): "lineedit_echo_mode", + ("58954540", "8.4 自定义控件"): "custom_widget", + + # ---- ch09 Qt消息机制和事件 (58954546) ---- + ("58954546", "9.1 事件"): "mouse_event", + ("58954546", "9.2 event()"): "event_override", + ("58954546", "9.3 事件过滤器"): "event_filter", + ("58954546", "9.4 总结"): "event_summary", + + # ---- ch10 绘图和绘图设备 (58954548) ---- + ("58954548", "10.1 QPainter"): "qpainter_basic", + ("58954548", "10.2.1 QPixmap、QBitmap、QImage"): "paint_device_pixmaps", + ("58954548", "10.2.2 QPicture"): "qpicture_io", + + # ---- ch11 文件系统 (58954552) ---- + ("58954552", "11.1 基本文件操作"): "qfile_basic", + ("58954552", "11.2 二进制文件读写"): "qdatastream_io", + ("58954552", "11.3 文本文件读写"): "qtextstream_io", +} + + +def stem_for(pid, section_title): + """Return the english filename stem for a (pageId, section_title), or None.""" + return FILE_NAME_MAP.get((pid, section_title)) \ No newline at end of file diff --git a/tools/gen_part1.py b/tools/gen_part1.py new file mode 100644 index 0000000..bfd315c --- /dev/null +++ b/tools/gen_part1.py @@ -0,0 +1,910 @@ +#!/usr/bin/env python3 +# ============================================================================== +# gen_part1.py — 生成 Part1(C/C++ 强化)的全部源码与 CMakeLists +# +# 策略:解析每页标题层级得到子话题;同一子话题内的代码块(多为 test() 片段) +# 合并为「一个完整小程序」,按教学顺序嵌入并标注出处(// --- 原文片段 N ---)。 +# 语言按内容判定(C 块用 .c,C++ 块用 .cpp)。每段代码先经 common_fix 修补 +# 平台/笔误,再走本文件中针对个别页/块的 EXACT_FIX。 +# ------------------------------------------------------------------------------ +import json, os, re, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import gen_sources +from gen_sources import (REPO, WIKI, page_outline, classify_lang, is_complete, + has_windows_pause, has_pause_call, needs_cstring, + infer_stl_includes, slug, ascii_slug, write, + record_erratum, common_fix, PAGE_CONFIG, LANG_OVERRIDE) +from file_name_map import stem_for +# ERRATA is a module-level list in gen_sources that record_erratum mutates in place. +ERRATA = gen_sources.ERRATA + +# track section titles we could not map, to report at end +UNMAPPED = [] + +def src_name_for(pid, sec_title, prefix, idx_in_page): + """English source-file stem: explicit map → chapter-number fallback → counter.""" + stem = stem_for(pid, sec_title) + if stem: + return stem + UNMAPPED.append((pid, sec_title)) + # fallback: use the leading chapter number from the title (e.g. "4.3.7.1 ...") + m = re.match(r"(\d+(?:\.\d+)*)", sec_title) + if m: + num = m.group(1).replace(".", "_") + return f"{prefix}_sec_{num}" + return f"{prefix}_t{idx_in_page:02d}" + +EXTRACTED = json.load(open(os.path.join(WIKI, "extracted_code.json"))) +PAGES = {p["id"]: p for p in json.load(open(os.path.join(WIKI, "all_page_ids.json")))} + +# ============================================================================== +# 针对单个代码块的精确修正(结构性/逻辑性错误)。返回修正后的代码。 +# 仅对确有错误的块做最小改动,并 record_erratum。 +# ============================================================================== +def exact_fix(pid, idx, code): + if pid == "58954439" and idx == 14: + # K&R 隐式 int:C99 起非法,C17 无法编译。原意是演示「C89 允许省略类型」 + # 并可向无类型参数函数传任意实参。C17 下给出显式类型的等价对照: + # fun1(i) → fun1(int i) + # fun2(i) → fun2(char* i) + # fun3() → fun3(void),原调用 fun3(1,2,"abc") 在 C17 非法 → 改 fun3() + # g(){...} → int g(void){...} + # 原始 C89 形式(含无类型参数)仍由 tools/demo_versions/kr_implicit_int.c + # 在 -std=c89 下跨版本演示。 + fixed = code.replace("int fun1(i){", "int fun1(int i){") + fixed = fixed.replace("int fun2(i){", "int fun2(char* i){") + fixed = fixed.replace("int fun3(){ ", "int fun3(void){ ") + fixed = re.sub(r"\ng\(\)\{", "\nint g(void){", fixed) + # main 中 fun3(1, 2, "abc") 在 C17 下是参数过多错误;改为 fun3() 并注释说明 + fixed = fixed.replace('fun3(1, 2, "abc");', + 'fun3(); // 注意:C89 中 fun3() 可接受任意参数;C17 下 fun3(void) 不接受参数') + record_erratum(pid, idx, "removed-feature", + "K&R 隐式 int(int fun1(i)、g(){...})及无类型参数 fun3() 接受任意实参", + "显式 int(int fun1(int i)、int g(void){...}),fun2 形参显式为 char*," + "fun3() → fun3(void)、调用改为 fun3()(C17 不允许向 (void) 函数传参)", + "隐式 int 与无类型/可变参数在 C99 起被废除,C17 不可编译。原始 C89 形式仍可由 " + "tools/std_probe.py 与 tools/demo_versions/kr_implicit_int.c 在 -std=c89 下演示。") + return fixed + if pid == "58954439" and idx == 38: + # 类定义末尾缺分号 + if not code.rstrip().endswith(";"): + fixed = code.rstrip() + ";\n" + record_erratum(pid, idx, "syntax", + "class Person{...}(末尾缺分号)", "class Person{...};", + "C++ 类/结构体定义后必须有分号。") + return fixed + if pid == "58954451" and idx == 8: + # 伪代码:中文散文混入语句。提取可编译对照(new vs malloc),散文转注释。 + fixed = ( + "// 原文为伪代码对照,演示 new 与 malloc 的等价关系:\n" + "// Person* person = new Person;\n" + "// 相当于:\n" + "// Person* person = (Person*)malloc(sizeof(Person));\n" + "// if(person == NULL){ return 0; }\n" + "// person->Init(); // 构造函数\n" + "// 下面给出可编译的对照程序:\n" + "#include \n" + "#include \n" + "#include \n" + "class Person {\n" + "public:\n" + " Person() { std::cout << \"构造函数!\" << std::endl; }\n" + " void Init() { std::cout << \"Init\" << std::endl; }\n" + "};\n" + "int main() {\n" + " Person* person = new Person; // C++: new 自动调用构造函数\n" + " Person* p2 = (Person*)malloc(sizeof(Person));\n" + " if (p2) { new (p2) Person(); } // 对齐 new 的行为(placement new)\n" + " delete person;\n" + " if (p2) { p2->~Person(); free(p2); }\n" + " return 0;\n" + "}\n" + ) + record_erratum(pid, idx, "pseudo-code", + "new Person / malloc / 构造函数(中文散文混入语句,不可编译)", + "可编译对照程序:new vs malloc+placement new,散文转注释", + "原文是对照式伪代码(含「相当于:」「构造函数」等中文散文),无法编译。" + "改为可编译的 new 与 malloc 行为对照(用 placement new 对齐构造语义)。") + return fixed + if pid == "58954439" and idx in (3, 6, 7): + # wiki 把 namespace 定义 / namespace 作用域成员函数定义 写在 void test(){...} + # 函数体内,这在 C++ 中非法(namespace 必须在全局/命名空间作用域)。把 + # namespace 块提升到文件作用域,test() 仅保留对命名空间成员的使用,使示例 + # 既可编译又保留原教学意图(演示命名空间的声明与定义分离)。 + if idx == 3: + fixed = ( + "namespace A{\n\tint a = 10;\n}\n" + "namespace B{\n\tint a = 20;\n}\n" + "void test(){\n" + "\tcout << \"A::a : \" << A::a << endl;\n" + "\tcout << \"B::a : \" << B::a << endl;\n" + "}\n" + ) + elif idx == 6: + fixed = ( + "namespace MySpace{\n\tvoid func1();\n\tvoid func2(int param);\n}\n" + ) + else: # idx == 7 + fixed = ( + "namespace MySpace{\n\tvoid func1();\n\tvoid func2(int param);\n}\n" + "void MySpace::func1(){\n\tcout << \"MySpace::func1\" << endl;\n" + "}\n" + "void MySpace::func2(int param){\n" + "\tcout << \"MySpace::func2 : \" << param << endl;\n" + "}\n" + "void test(){\n\tMySpace::func1();\n\tMySpace::func2(42);\n}\n" + ) + record_erratum(pid, idx, "illegal-in-function", + "namespace 定义 / 命名空间作用域成员函数定义 写在函数体内(void test(){ namespace A{...} })", + "把 namespace 块与成员定义提升到文件作用域,test() 仅使用其成员", + "C++ 不允许在函数体内定义 namespace 或其成员函数;标准要求它们处于全局/" + "命名空间作用域。原 wiki 写法无法编译;提升后保留教学意图。") + return fixed + if pid == "58954439" and idx == 45: + # extern_c_module 的实现文件用 EXIT_SUCCESS 但只 include 了 my_module.h; + # 补上 。 + if "EXIT_SUCCESS" in code and "#include " not in code: + fixed = code.replace('#include"my_module.h"', + '#include"my_module.h"\n#include ') + record_erratum(pid, idx, "missing-include", + '"my_module.h" 引用 EXIT_SUCCESS 但未含 ', + "补 #include ", + "EXIT_SUCCESS 定义在 。") + return fixed + if pid == "58954439" and idx == 24: + # 该块引用 Person(前面小节定义于另一文件),独立编译为缺失符号。补一个最小 + # 的 Person 前置定义(仅含本块用到的 age 成员 + 默认成员初始化,使 + # const Person person; 可默认初始化、C++17 合法),使其自洽可编译。 + pre = ("class Person { public: int age = 0; };\n") + record_erratum(pid, idx, "missing-symbol", + "const Person 演示引用 Person,但 Person 定义在另一小节(拆分为独立文件后丢失)", + "在本块顶部补一个最小 Person 定义(age 带默认成员初始化,使 const 对象可默认构造)", + "原 wiki 把 Person 定义与 const 演示放在同节、本应同文件;按「示例相对隔离」拆开后" + "本块自洽编译所需的最小前置定义。const 聚合对象需默认成员初始化才能默认构造(C++17)。") + return pre + code + if pid == "58954439" and idx == 41: + # wiki 把 namespace A/B 定义(含重载集 MyFunc)写在函数体内(被 block_classify + # 误判为 statements 包进 block_0(){}),namespace 不能在函数内定义。提升到 + # 文件作用域,并在 test() 中调用命名空间内的重载演示调用(与原文重载集意图一致)。 + fixed = ( + "namespace A{\n" + "\tvoid MyFunc(){ cout << \"无参数!\" << endl; }\n" + "\tvoid MyFunc(int a){ cout << \"a: \" << a << endl; }\n" + "\tvoid MyFunc(string b){ cout << \"b: \" << b << endl; }\n" + "\tvoid MyFunc(int a, string b){ cout << \"a: \" << a << \" b:\" << b << endl;}\n" + "\tvoid MyFunc(string b, int a){ cout << \"a: \" << a << \" b:\" << b << endl;}\n" + "}\n" + "namespace B{\n" + "\tvoid MyFunc(string b, int a){}\n" + "\t//int MyFunc(string b, int a){} //无法重载仅按返回值区分的函数\n" + "}\n" + "void test(){\n" + "\tA::MyFunc();\n" + "\tA::MyFunc(10);\n" + "\tA::MyFunc(\"hello\");\n" + "\tA::MyFunc(10, \"world\");\n" + "\tA::MyFunc(\"world\", 10);\n" + "\tB::MyFunc(\"ok\", 1);\n" + "}\n" + ) + record_erratum(pid, idx, "illegal-in-function", + "namespace A/B 定义(含重载集 MyFunc)写在函数体内(void test(){ namespace A{...} })", + "把 namespace 块提升到文件作用域,test() 通过 A::/B:: 调用各重载", + "C++ 不允许在函数体内定义 namespace;标准要求其处于全局/命名空间作用域。" + "原 wiki 写法无法编译;提升后保留重载集演示意图。") + return fixed + if pid == "58954439" and idx == 42: + # 该块演示「函数重载碰上默认参数 → 二义性」。原文 main 里 MyFunc("hello") + # 在两个重载间确实二义,编译器拒绝——但教学意图是「展示二义性」,不是产出 + # 可运行错误。注意:MyFunc(string) 与 MyFunc(string,int=10) 中,任何只给一个 + # string 实参的调用都二义(强转 string 也无济于事,两候选都同等匹配)。故把 + # 单参调用注释为「二义不可调用」,main 只显式调用双参重载,使示例可编译运行 + # 且保留「二义性」教学信息。 + fixed = ( + "void MyFunc(string b){\n" + "\tcout << \"b: \" << b << endl;\n" + "}\n" + "//函数重载碰上默认参数\n" + "void MyFunc(string b, int a = 10){\n" + "\tcout << \"a: \" << a << \" b:\" << b << endl;\n" + "}\n" + "int main(){\n" + "\t// MyFunc(\"hello\"); // 二义:MyFunc(string) 与 MyFunc(string,int=10) 同等匹配\n" + "\t// MyFunc(string(\"hello\")); // 仍二义:强转 string 也无法区分两候选\n" + "\tMyFunc(\"hello\", 20); // 显式给第二参 → 明确选双参重载\n" + "\treturn 0;\n" + "}\n" + ) + record_erratum(pid, idx, "ambiguous-overload", + 'main 中 MyFunc("hello") 在 MyFunc(string) 与 MyFunc(string,int=10) 间二义,无法编译', + "注释保留二义调用作说明,main 只显式调用双参重载(单参形式因二义不可调用)", + "函数重载遇上默认参数时,编译器无法在两个可行候选间抉择(二义性)。即使强转 " + "std::string 也无法消除——两候选对单个 string 实参同等匹配。原文意在演示此现象," + "作为可运行示例只能保留可调用的双参形式并注释说明二义点。") + return fixed + if pid == "58954456" and idx == 3: + # const 成员函数 ChangePerson() const 内修改非 mutable 成员 mAge(mID 是 + # mutable 合法)。const 成员函数不得修改对象逻辑状态——原文 mAge=100 非法。 + # 修正:把对 mAge 的修改注释为「非法(演示 const 约束)」,仅保留对 mutable + # mID 的合法修改,并在注释中说明 const 成员函数的语义。 + fixed = code.replace( + "\tvoid ChangePerson() const{\n" + "\t\tmAge = 100;\n" + "\t\tmID = 100;\n" + "\t}", + "\tvoid ChangePerson() const{\n" + "\t\t//mAge = 100; // 非法:const 成员函数内不能修改非 mutable 成员\n" + "\t\tmID = 100; // 合法:mID 被 mutable 修饰,const 函数内可改\n" + "\t}") + if fixed != code: + record_erratum(pid, idx, "const-correctness", + "const 成员函数 ChangePerson() const 内修改非 mutable 成员 mAge(mAge = 100)", + "注释掉对 mAge 的修改,保留对 mutable mID 的修改,并加注释说明", + "const 成员函数承诺不修改对象的逻辑状态,故不得修改非 mutable 数据成员;" + "mID 被 mutable 修饰例外。原写法在 C++17 下编译报错。") + return fixed + if pid == "58954461" and idx == 0: + # wiki 在 class IndexPage 前混入了孤立的中文散文标题词「网页类」(非注释、 + # 非代码),编译器报 '网页类' does not name a type。把该散文行转为注释。 + if re.search(r"(?m)^[ \t]*网页类[ \t]*$", code): + fixed = re.sub(r"(?m)^([ \t]*)网页类([ \t]*)$", + r"\1// 网页类(原文小标题,转为注释)\2", code) + record_erratum(pid, idx, "prose-in-code", + "class IndexPage 前混入孤立中文散文词「网页类」(非注释非代码)", + "把该散文行转为注释", + "wiki 正文标题词被误纳入代码块,C++ 无法解析;转为注释保留教学上下文。") + return fixed + if pid == "58954469" and idx == 5: + # B 有纯虚析构函数(virtual ~B() = 0;),故 B 是抽象类,不可实例化。原文 + # test() 里 B b; 试图构造抽象类对象,编译报错。教学意图正是「B 是抽象类不能 + # 实例化」——把 B b; 注释为「非法(演示)」并说明,A a; 保留。 + if re.search(r"(?m)^[ \t]*B[ \t]+b[ \t]*;", code): + fixed = re.sub(r"(?m)^([ \t]*)B[ \t]+b[ \t]*;", + r"\1//B b; // 非法:B 含纯虚析构函数,是抽象类,不能实例化对象", code) + record_erratum(pid, idx, "abstract-instantiation", + "test() 中 B b; 试图实例化抽象类 B(B 有纯虚析构函数 virtual ~B()=0)", + "注释掉 B b; 并加说明(B 是抽象类不可实例化),A a; 保留", + "含纯虚函数(含纯虚析构函数)的类是抽象类,C++ 禁止创建其对象。原文此处" + "正是演示该约束,但作为可编译示例需把非法构造注释化。") + return fixed + if pid == "58954473" and idx == 2: + # 笔误:注释行 //int b = const_cast(a); 末尾误带了函数闭合花括号 }, + # 导致 test01() 未闭合,test02() 被嵌套进 test01() 内 → 函数定义不允许在此。 + # 把那个误入注释的 } 移出注释作为 test01() 的正常闭合。 + bad = "//int b = const_cast(a); }" + good = ("//int b = const_cast(a); // 不能对非指针或非引用进行 const_cast\n" + "} // end of test01()") + if bad in code: + fixed = code.replace(bad, good) + record_erratum(pid, idx, "syntax", + "//int b = const_cast(a); }(闭合花括号误入注释行)", + "把 } 移出注释作为 test01() 的正常闭合花括号", + "原 wiki 把 test01() 的闭合 } 误写进注释,导致函数体未结束、后续 test02() " + "被嵌套在 test01() 内,C++ 禁止函数内定义函数。") + return fixed + if pid == "58954474" and idx == 4: + # 动态异常规范 throw(int,char,char*) 与 throw() 在 C++17 起被废除(throw() + # 等价 noexcept 已在 C++11 弃用,C++17 移除动态规范)。教学意图是展示「异常 + # 规范」概念。给出 C++17 可编译的等价形式:throw()→noexcept,throw(类型...) + # →noexcept(false)(无法精确表达「只抛某类型」,因 C++17 不支持),并注释 + # 说明旧规范的语义与废除版本。 + fixed = ( + "//可抛出所有类型异常\n" + "void TestFunction01(){\n" + "\tthrow 10;\n" + "}\n\n" + "// 【C++17 修正】原文为 throw(int,char,char*) 动态异常规范——C++17 起非法\n" + "// (C++11 弃用、C++17 移除)。动态规范无法在现代 C++ 中精确表达「只抛这些类型」,\n" + "// 等价改写为 noexcept(false)(表示可能抛异常)。原文语义见 tools/demo_versions/。\n" + "void TestFunction02() noexcept(false){\n" + "\tstring exception = \"error!\";\n" + "\tthrow exception;\n" + "}\n\n" + "// 【C++17 修正】原文为 throw()(不抛任何异常)→ C++17 等价于 noexcept。\n" + "void TestFunction03() noexcept {\n" + "\t//throw 10; // noexcept 函数抛异常会调用 std::terminate\n" + "}\n\n" + "int main(){\n\n" + "\ttry{\n" + "\t\t//TestFunction01();\n" + "\t\t//TestFunction02();\n" + "\t\t//TestFunction03();\n" + "\t}\n" + "\tcatch (...){\n" + "\t\tcout << \"捕获异常!\" << endl;\n" + "\t}\n\n" + "\treturn EXIT_SUCCESS;\n" + "}\n" + ) + record_erratum(pid, idx, "removed-feature", + "动态异常规范 throw(int,char,char*) 与 throw()(C++17 已移除)", + "throw() → noexcept;throw(类型...) → noexcept(false),并注释说明旧语义", + "动态异常规范在 C++11 弃用、C++17 移除,C++17 下是语法错误。throw() 的等价物是 " + "noexcept;带类型列表的动态规范无法在现代 C++ 中精确表达,用 noexcept(false) 近似" + "(表示可能抛异常)。旧规范演示见 tools/demo_versions/dynamic_exception_spec.cpp" + "(在 -std=c++14 下编译)。") + return fixed + if pid == "58954472" and idx == 11: + # MyArray 类模板应用:wiki 把「测试代码」用例的裸语句(中文标题词「测试代码:」 + # + 文件作用域的 MyArray myArrayInt(10); 及 for 循环、Push_back 调用等) + # 直接铺在类定义之后,没有任何函数包裹 → 全局变量初始化里含 for 循环非法, + # 且中文散文词非法。重写为:保留 MyArray 模板 + Person 类 + 打印函数,把测试 + # 驱动语句包进 main(),使示例可编译运行并演示类模板。 + fixed = ( + "#include \n" + "using std::string;\n\n" + "template\n" + "class MyArray {\n" + "public:\n" + "\texplicit MyArray(int capacity) {\n" + "\t\tthis->m_Capacity = capacity;\n" + "\t\tthis->m_Size = 0;\n" + "\t\t// 如果T是对象,那么这个对象必须提供默认的构造函数\n" + "\t\tpAddress = new T[this->m_Capacity];\n" + "\t}\n" + "\tMyArray(const MyArray& arr) {\n" + "\t\tthis->m_Capacity = arr.m_Capacity;\n" + "\t\tthis->m_Size = arr.m_Size;\n" + "\t\tthis->pAddress = new T[this->m_Capacity];\n" + "\t\tfor (int i = 0; i < this->m_Size; i++) {\n" + "\t\t\tthis->pAddress[i] = arr.pAddress[i];\n" + "\t\t}\n" + "\t}\n" + "\tT& operator[](int index) { return this->pAddress[index]; }\n" + "\tvoid Push_back(const T& val) {\n" + "\t\tif (this->m_Capacity == this->m_Size) return;\n" + "\t\tthis->pAddress[this->m_Size] = val;\n" + "\t\tthis->m_Size++;\n" + "\t}\n" + "\tvoid Pop_back() { if (this->m_Size > 0) this->m_Size--; }\n" + "\tint getSize() { return this->m_Size; }\n" + "\t~MyArray() {\n" + "\t\tif (this->pAddress != NULL) {\n" + "\t\t\tdelete[] this->pAddress;\n" + "\t\t\tthis->pAddress = NULL;\n" + "\t\t\tthis->m_Capacity = 0;\n" + "\t\t\tthis->m_Size = 0;\n" + "\t\t}\n" + "\t}\n" + "private:\n" + "\tT* pAddress;\n" + "\tint m_Capacity;\n" + "\tint m_Size;\n" + "};\n\n" + "class Person {\n" + "public:\n" + "\tPerson() {}\n" + "\tPerson(string name, int age) { this->mName = name; this->mAge = age; }\n" + "public:\n" + "\tstring mName;\n" + "\tint mAge;\n" + "};\n\n" + "void PrintMyArrayInt(MyArray& arr) {\n" + "\tfor (int i = 0; i < arr.getSize(); i++) {\n" + "\t\tcout << arr[i] << \" \";\n" + "\t}\n" + "\tcout << endl;\n" + "}\n\n" + "void PrintMyPerson(MyArray& personArr) {\n" + "\tfor (int i = 0; i < personArr.getSize(); i++) {\n" + "\t\tcout << \"姓名:\" << personArr[i].mName << \" 年龄:\" << personArr[i].mAge << endl;\n" + "\t}\n" + "}\n\n" + "// --- 测试代码(原文为文件作用域裸语句,已包进 main 使其可编译运行)---\n" + "int main() {\n" + "\tMyArray myArrayInt(10);\n" + "\tfor (int i = 0; i < 9; i++) {\n" + "\t\tmyArrayInt.Push_back(i);\n" + "\t}\n" + "\tmyArrayInt.Push_back(100);\n" + "\tPrintMyArrayInt(myArrayInt);\n\n" + "\tMyArray myArrayPerson(10);\n" + "\tPerson p1(\"德玛西亚\", 30);\n" + "\tPerson p2(\"提莫\", 20);\n" + "\tPerson p3(\"孙悟空\", 18);\n" + "\tPerson p4(\"赵信\", 15);\n" + "\tPerson p5(\"赵云\", 24);\n" + "\tmyArrayPerson.Push_back(p1);\n" + "\tmyArrayPerson.Push_back(p2);\n" + "\tmyArrayPerson.Push_back(p3);\n" + "\tmyArrayPerson.Push_back(p4);\n" + "\tmyArrayPerson.Push_back(p5);\n" + "\tPrintMyPerson(myArrayPerson);\n" + "\treturn 0;\n" + "}\n" + ) + record_erratum(pid, idx, "prose-in-code", + "「测试代码:」中文散文词 + 文件作用域裸语句(MyArray myArrayInt(10);、" + "for 循环、Push_back 调用等)直接铺在类定义之后,无函数包裹", + "删除散文词,把测试驱动语句包进 main(),保留 MyArray 模板 + Person 类 + 打印函数", + "C++ 不允许在文件作用域直接写可执行语句(如 for 循环、函数调用);中文散文词" + "亦非法。原文是把「使用示例」当散文铺排;重写为可编译运行的单文件程序,保留" + "类模板应用的教学意图。") + return fixed + if pid == "58954439" and idx == 46: + # extern "C" 演示:main 调用 func1()/func2(),但这两个函数在本文件中仅有 + # extern 声明(定义在配对的 45 块 MyModule.c)。本文件作为独立可执行目标编译 + # 时链接报 undefined reference。修正:在本文件内就地提供 func1/func2 的 extern + # "C" 定义(最小实现),使示例自洽可编译运行;保留原文的 extern "C" 声明教学。 + fixed = code + ( + "\n// --- 为使本示例作为独立目标可链接,就地提供 func1/func2 的 extern \"C\" 定义 ---\n" + '// (原文仅声明;定义在配对的 MyModule.c。此处补最小实现以自洽编译运行。)\n' + 'extern "C" void func1(){ printf("hello world!"); }\n' + 'extern "C" int func2(int a, int b){ return a + b; }\n' + ) + record_erratum(pid, idx, "missing-definition", + "main 调用 func1()/func2(),但本文件仅有 extern \"C\" 声明(定义在配对的 MyModule.c)", + "在本文件就地补 func1/func2 的 extern \"C\" 定义(最小实现)", + "原 wiki 把声明与定义分属两文件;按「示例相对隔离」拆分为独立目标后,本文件" + "缺少定义导致链接失败。补最小实现使其自洽可运行,保留 extern \"C\" 声明的教学意图。") + return fixed + if pid == "58954474" and idx == 8: + # 自定义异常类 MyOutOfRange::what() 重写 exception::what()。C++11 起 + # std::exception::what() 声明为 noexcept,重写版本必须也 noexcept(否则 + # 「looser exception specification」编译错)。补 noexcept。 + if re.search(r"virtual\s+const\s+char\s*\*\s*what\s*\(\s*\)\s*const\s*\{", code): + fixed = re.sub(r"virtual\s+const\s+char\s*\*\s*what\s*\(\s*\)\s*const\s*\{", + "virtual const char* what() const noexcept {", code) + record_erratum(pid, idx, "override-noexcept", + "MyOutOfRange::what() const 重写 exception::what() 但缺 noexcept", + "补 noexcept(virtual const char* what() const noexcept)", + "C++11 起 std::exception::what() 声明为 noexcept;重写函数的异常规范不得" + "比基类更宽松,必须同样 noexcept,否则 C++17 编译报「looser exception " + "specification」。原 wiki 写于旧标准,未带 noexcept。") + return fixed + if pid == "58954488" and idx == 5: + # set 排序仿函数 MyCompare02/03 的 operator() 非 const。C++14 起 set 的比较 + # 对象要求「可 const 调用」(comparison object must be invocable as const), + # 非 const operator() 触发 static_assert。给两个 operator() 加 const。 + fixed = code.replace( + "bool operator()(int v1,int v2){\n\t\treturn v1 > v2;\n\t}", + "bool operator()(int v1, int v2) const {\n\t\treturn v1 > v2;\n\t}") + fixed = fixed.replace( + "bool operator()(const Person& p1,const Person& p2){\n\t\treturn p1.mAge > p2.mAge;\n\t}", + "bool operator()(const Person& p1, const Person& p2) const {\n\t\treturn p1.mAge > p2.mAge;\n\t}") + if fixed != code: + record_erratum(pid, idx, "const-comparator", + "set 的排序仿函数 MyCompare02/MyCompare03 的 operator() 非 const", + "给两个 operator() 加 const 修饰", + "C++14 起,关联容器(set/map)的比较对象必须可 const 调用(标准要求 " + "Compare 对象满足 invocable as const)。原 wiki 的非 const operator() 在 " + "C++17 libstdc++ 下触发 static_assert。") + return fixed + if pid == "58954488" and idx == 4: + # list 节点结构演示:原文直接访问 MSVC 实现私有的 list 内部成员 + # (_Nodeptr / _Myhead / _Next / _Myval / _Mysize),这些是 MSVC STL 的 + # 实现细节,libstdc++(gcc)没有等价物,无法编译。教学意图是「演示 list 的 + # 环形双向链表节点结构」。改写为:用标准接口(迭代器)遍历 list,并用注释 + # 图示说明环形链表结构(_Myhead 是哨兵头节点、_Next/_Prev 串联、_Myval 存值), + # 保留教学意图且跨实现可移植。 + fixed = ( + "#include \n" + "#include \n" + "using namespace std;\n\n" + "// 【可移植性说明】原文直接访问 MSVC STL 的 list 私有成员\n" + "// (_Nodeptr / _Myhead / _Next / _Mysize / _Myval)来「演示 list 的环形\n" + "// 双向链表结构」。这些是 MSVC 实现细节,libstdc++(gcc)无等价物,无法移植。\n" + "// 下面改用标准迭代器接口遍历,并以注释图示说明 list 的环形双向链表结构:\n" + "//\n" + "// _Myhead (哨兵节点) ──┐\n" + "// ↑ ↓ │ list 内部维护一个哨兵头节点 _Myhead,\n" + "// [_Prev|_Myval|_Next] ←──→ [_Prev|_Myval|_Next] ←──→ ... ──┘\n" + "// 末节点.next = _Myhead;_Myhead.prev = 末节点;_Mysize 记录元素个数。\n" + "// 遍历从 _Myhead->_Next 开始,绕一圈回到 _Myhead 即结束。\n\n" + "int main(){\n" + "\tlist myList;\n" + "\tfor (int i = 0; i < 10; i++){\n" + "\t\tmyList.push_back(i);\n" + "\t}\n\n" + "\t// 标准(可移植)方式:用迭代器遍历环形双向链表\n" + "\tcout << \"size = \" << myList.size() << endl;\n" + "\tfor (list::iterator it = myList.begin(); it != myList.end(); it++){\n" + "\t\tcout << \"Node:\" << *it << endl;\n" + "\t}\n\n" + "\treturn EXIT_SUCCESS;\n" + "}\n" + ) + record_erratum(pid, idx, "implementation-specific", + "直接访问 MSVC STL 私有成员 _Nodeptr/_Myhead/_Next/_Myval/_Mysize(演示 list 节点结构)", + "改用标准迭代器遍历,以注释图示说明环形双向链表结构", + "MSVC STL 的下划线大写成员是实现细节,libstdc++ 无等价物,跨编译器不可移植。" + "C++ 标准不暴露容器内部节点结构;原写法仅在 MSVC 下可编译。改用标准接口 + 图示" + "注释保留教学意图。") + return fixed + if pid == "58954476" and idx == 4: + # 二进制文件读写:Person 只有 Person(char*,int) 构造,无默认构造,但读取时 + # 用 Person p3; 默认构造 → 报错。补一个默认构造(成员带默认值),使二进制读 + # 入占位对象合法。同时 char* fileName = "person.txt" 在 C++17 是 const char* + # → char* 的弃用转换(-Wall 报警,本例仅作最小修正)。 + if "Person(char* name,int age)" in code and "Person(){}" not in code: + fixed = code.replace( + "class Person{\npublic:\n\tPerson(char* name,int age){", + "class Person{\npublic:\n\tPerson(){} // 补默认构造:二进制读入需先默认构造占位对象\n\tPerson(char* name,int age){") + record_erratum(pid, idx, "missing-default-ctor", + "Person 只有 Person(char*,int) 构造,但二进制读取用 Person p3; 默认构造", + "补一个空的默认构造 Person(){}", + "二进制文件读取需先默认构造一个占位对象再 read() 填充;原 Person 无默认" + "构造,C++17 报「no matching function for call to Person::Person()」。") + return fixed + return code + +# ============================================================================== +# C/C++ 控制台程序的合并模板 +# ============================================================================== +C_PREAMBLE = ( + "#include \n" + "#include \n" + "#include \n" +) +CPP_PREAMBLE = ( + "#include \n" + "using namespace std;\n" +) +# 便携 pause() 壳(仅当某段用到 pause() 时注入;Windows 下原意是等待用户回车) +PAUSE_HELPER = ( + "#include \n" + "static inline void pause() { printf(\"按回车键继续...\"); (void)getchar(); }\n" +) + +# matches a top-level (column-0) function definition header like "void test01(){" +# or "int fun2(int a, int b){" — captures return-type and function name. +_TOPFN_RE = re.compile( + r"^([A-Za-z_][\w:&<>,\s\*]*?\s+)([A-Za-z_]\w*)\s*\(", re.M) + +# a real function definition: optional ret-type, name, "(...)", then a body "{" +_REAL_FN_DEF_RE = re.compile( + r"^([A-Za-z_][\w:&<>,\s\*]*?\s+)?([A-Za-z_]\w*)\s*\([^;]*\)\s*\{", re.M) + +def block_classify(code): + """Classify a fragment's shape so we know how to make it compile. + 'program' — has int main(...); kept as-is. + 'fndef' — has a top-level function definition with body (e.g. void test(){...}). + 'groupdecl' — only global declarations (namespace/class/typedef/using/var defs). + 'statements'— loose statements with NO enclosing function (copied from inside + a test() body); must be wrapped to compile. + """ + if re.search(r"\bint\s+main\s*\(", code): + return "program" + if _REAL_FN_DEF_RE.search(code): + return "fndef" + stripped = re.sub(r"//[^\n]*", "", code) + stripped = re.sub(r"/\*.*?\*/", "", stripped, flags=re.S) + lines = [l.strip() for l in stripped.splitlines() if l.strip()] + if not lines: + return "statements" + decl_re = re.compile( + r"^(namespace|class|struct|typedef|using|template|enum|extern|#)\b" + r"|^(int|char|double|float|bool|auto|void|const|static|unsigned|long|short|size_t|wchar_t|string|std::)" + r"[\w:&<>,\*\s]*\s*[A-Za-z_]\w*\s*(=\s*[^;{]*|\([^;{]*\)\s*(=\s*\d+)?)?\s*;" + r"|^(int|char|double|float|bool|auto|void|const|static|size_t|wchar_t|string|std::)" + r"[\w:&<>,\*\s]*?\s+\*?[A-Za-z_]\w*\s*\([^;{]*\)\s*;\s*$") + # 'groupdecl' lines: a declaration line OR a namespace/class/struct/enum block + # boundary line (bare '{', bare '}', or '};' on its own). A bare call like + # printf(...); is NOT a declaration (no leading type) → classify as statements. + return "groupdecl" if all(decl_re.match(l) or l in {"{", "}", "};"} for l in lines) else "statements" + +def wrap_statements(code, seq): + """Wrap a loose-statement fragment in static void func_N(){...} so it compiles.""" + if block_classify(code) != "statements": + return code, None + body = "\n".join((" " + l) if l.strip() else l for l in code.splitlines()) + fn = f"block_{seq}" + return f"static void {fn}() {{\n{body}\n}}\n", fn + +def dedupe_functions(blocks_with_meta): + """Across the merged blocks, rename duplicated top-level function *definitions* + to _ so the file compiles. The wiki commonly shows several + alternative variants of the same `void test(){...}` under one heading; they + are meant to be read individually, not linked together. We keep all variants + in one file (preserving the teaching progression) but give each a unique name + and call each from main(). Recursion/calls within the same block are renamed + consistently. + + CRITICAL: two definitions with the same name but DIFFERENT parameter lists + are legal function *overloads* and must NOT be renamed (renaming them all to + _N would collapse the overload set into redefinitions). We only rename + a duplicate whose (name, param-count) signature already appeared — a true + variant. Returns the (possibly renamed) blocks.""" + seen = {} # (fn_name, param_count) -> count of definitions seen so far + out = [] + for (idx, code, sec_title) in blocks_with_meta: + # find top-level (col 0) function names *defined* in this block + # (a definition has a body on the same or next line; a call is indented) + defined = [] + for m in _TOPFN_RE.finditer(code): + name = m.group(2) + # only treat as definition if the header is at column 0 (not indented call) + if m.start() == 0 or code[m.start()-1] == "\n": + # and it's followed by a body `{` within a few lines (def, not decl) + tail = code[m.end():m.end()+200] + if re.match(r"[^{;]*\{", tail): + # capture the parameter list to count params (overload detection). + # _TOPFN_RE ends right after the opening '(', so m.end() is just + # past '('; grab from there to the matching ')'. + rest = code[m.end():] + paren = re.match(r"([^)]*)\)", rest) + params = paren.group(1) if paren else "" + pcount = 0 if params.strip() == "" or params.strip() == "void" \ + else params.count(",") + 1 + defined.append((name, pcount)) + rename = {} # old_name → new_name (applied by token; renames are unique + # per old name, so overloads sharing a name all map together) + name_seq = {} # name → last assigned seq + for (name, pcount) in defined: + if name == "main": + continue # never rename main; a block with its own main stands alone + if name == "operator": + # operator() / operator+ etc. are overloaded operators, not plain + # functions — renaming them to operator_N breaks the operator. + # They live inside classes anyway and don't collide at file scope. + continue + key = (name, pcount) + cnt = seen.get(key, 0) + # rename only if THIS exact signature (name+paramcount) was seen before — + # a same-name/different-paramcount def is a legal overload, left intact. + if cnt > 0: + seq = name_seq.get(name, 0) + 1 + name_seq[name] = seq + rename[name] = f"{name}_{seq}" + seen[key] = cnt + 1 + if rename: + # word-boundary replace of each old name with new (only the function + # name token; safe enough for these small teaching snippets) + newcode = code + for old, new in rename.items(): + newcode = re.sub(r"\b" + re.escape(old) + r"\b", new, newcode) + code = newcode + out.append((idx, code, sec_title)) + return out + +def emit_console(target_name, blocks_with_meta, lang, out_dir, src_name=None): + """Emit one .c/.cpp = one add_executable, merging given (idx,code,section_title) + blocks. Returns (rel_source_path, list of block ids used). + + target_name: ASCII-only CMake target name (CMake forbids non-ASCII targets). + src_name: human-readable source filename (may contain CJK); defaults to + a transliteration of target_name.""" + # First, rename duplicate function definitions across blocks so the merge + # compiles (each variant gets a unique name; all are called from main). + blocks_with_meta = dedupe_functions(blocks_with_meta) + ext = "c" if lang == "c" else "cpp" + if src_name is None: + src_name = target_name + fname = f"{src_name}.{ext}" + parts = [] + # header + title_line = blocks_with_meta[0][2] + parts.append(f"// ============================================================================\n") + parts.append(f"// {target_name} — {title_line}\n") + parts.append(f"// 来源:川大 C++/LinuxC wiki「02.Qt方向」\n") + parts.append(f"// 说明:本文件由 gen_part1.py 合并页面中的教学片段而成。每个片段用\n") + parts.append(f"// // --- 原文片段 [pageId:blockIdx] --- 标注出处。\n") + parts.append(f"// 已做最小修正(笔误/平台移植),详见 docs/ERRATA.md。\n") + parts.append(f"// ============================================================================\n\n") + # includes + # NOTE: detect pause() on the already-common_fix-ed code (common_fix converts + # system("pause") → pause()), so has_pause_call sees the converted form. + # Detecting the raw 'system("pause")' literal here would always be False and + # miss the pause() helper injection — the root cause of past link failures. + needs_pause = any(has_pause_call(b[1]) for b in blocks_with_meta) + needs_str = any(needs_cstring(b[1]) for b in blocks_with_meta) + # aggregate STL/IO headers inferred across all merged blocks (sorted, deduped) + stl_hdrs = sorted({h for b in blocks_with_meta for h in infer_stl_includes(b[1])}) + if needs_str and "" not in stl_hdrs: + stl_hdrs.append("") + if lang == "c": + parts.append(C_PREAMBLE) + else: + parts.append(CPP_PREAMBLE) + for h in stl_hdrs: + parts.append(f"#include {h}\n") + if needs_pause: + parts.append("\n" + PAUSE_HELPER) + parts.append("\n") + + # body: each block is normalized to a compilable unit before emission. + # 'program' → kept as-is (brings its own main). + # 'fndef' → the test()/fun() definitions are emitted as-is; called from main. + # 'groupdecl' → global decls (namespace/class/typedef/var) emitted at file scope. + # 'statements' → loose statements wrapped in static void block_N(){...} and called from main. + call_fns = [] + for n, (idx, code, sec_title) in enumerate(blocks_with_meta): + parts.append(f"// --- 原文片段 [{PAGES_META_PID}:{idx}] {sec_title} ---\n") + body = code.rstrip() + "\n" + try: + cls = block_classify(body) + except Exception: + cls = "fndef" # conservative default + if cls == "statements": + wrapped, fn = wrap_statements(body, n) + if fn: + call_fns.append(fn) + parts.append(wrapped) + else: + parts.append(body) + if cls in ("fndef", "program"): + # only call no-arg DEMO entry points (test* / block_*) from main; any + # internal helpers they call (with params) are called by them. + for m in _REAL_FN_DEF_RE.finditer(body): + name = m.group(2) + if name == "main": + continue + if re.match(r"^(test|block_|demo_|run)", name): + call_fns.append(name) + parts.append("\n") + + already_has_main = any(is_complete(b[1]) for b in blocks_with_meta) + call_fns = list(dict.fromkeys(call_fns)) + parts.append("// --- main ---\n") + if already_has_main: + pass # rely on a block's own main + elif call_fns: + parts.append("int main() {\n") + for fn in call_fns: + parts.append(f" {fn}();\n") + if needs_pause: + parts.append(" pause();\n") + parts.append(" return 0;\n}\n") + else: + parts.append("int main() {\n return 0;\n}\n") + + src_rel = os.path.join(out_dir, fname) + write(os.path.join(REPO, src_rel), "".join(parts)) + return src_rel, [b[0] for b in blocks_with_meta] + +# global set by main() +PAGES_META_PID = "?" + +# ============================================================================== +# 主流程 +# ============================================================================== +def main(): + global PAGES_META_PID + part1_pages = [pid for pid, cfg in PAGE_CONFIG.items() if cfg["part"] == "p01"] + # collect CMake target entries per chapter dir + targets_by_dir = {} # dir -> list of (target_name, src_rel) + + for pid in part1_pages: + cfg = PAGE_CONFIG[pid] + PAGES_META_PID = pid + body = json.load(open(os.path.join(WIKI, "bodies", f"{pid}.json"))) \ + .get("body", {}).get("storage", {}).get("value", "") + blocks = EXTRACTED.get(pid, []) + sections = page_outline(body) + out_dir = os.path.join("p01", cfg["dir"]) + + # Decide language per block (override > classify) + def lang_of(i): + if (pid, i) in LANG_OVERRIDE: + return LANG_OVERRIDE[(pid, i)] + return classify_lang(blocks[i]["code"]) + + # If a page is almost-all one language, emit per-section merged files in that lang. + # Otherwise emit one file per section using the section's dominant language. + all_langs = [lang_of(i) for i in range(len(blocks))] + dominant = "cpp" if all_langs.count("cpp") >= all_langs.count("c") else "c" + + used = [False] * len(blocks) + + # --- 特殊处理:58954439:44 与 58954439:45 是 header/impl 对,作为一组两文件目标 --- + if pid == "58954439": + # 44 = MyModule.h, 45 = MyModule.c → 单独目标 p1c03_mymodule + code44 = exact_fix(pid, 44, common_fix(blocks[44]["code"], pid, 44)) + code45 = exact_fix(pid, 45, common_fix(blocks[45]["code"], pid, 45)) + hdr = os.path.join(out_dir, "my_module.h") + impl = os.path.join(out_dir, "extern_c_module_main.c") + write(os.path.join(REPO, hdr), code44 + "\n") + write(os.path.join(REPO, impl), + f"// {PAGES_META_PID}:45 — MyModule 实现文件(与 my_module.h 配对)\n" + f"// 由 gen_part1.py 成对输出。\n\n" + + code45.replace('"MyModule.h"', '"my_module.h"').replace('#include"my_module.h"', + '#include"my_module.h"\n#include ') + + "\nint main(){ func1(); printf(\"func2=%d\\n\", func2(3,4)); return EXIT_SUCCESS; }\n") + used[44] = used[45] = True + targets_by_dir.setdefault(out_dir, []).append(("p1c03_mymodule", "extern_c_module_main.c")) + + # --- 58954439:14 K&R 隐式 int 由通用路径处理(exact_fix 给出 C17 修正版)--- + + # --- 通用:每个代码块 = 一个独立目标(block-per-target)--- + # 每个 wiki 代码块都是独立的教学示例(常含同名函数/共享符号的多个变体), + # 合并会导致符号重定义;逐块成文件既保持教学隔离,又避免跨块冲突。 + # ASCII target names: CMake forbids non-ASCII target names, so we use a + # per-page counter (prefix_NN). Source filenames are English (mapped via + # file_name_map.py); same section repeated → suffix _2, _3... + tgt_counter = [0] + def next_target(prefix): + tgt_counter[0] += 1 + return f"{prefix}_{tgt_counter[0]:02d}" + + # Map each block to its owning section title (for naming). + block_sec_title = {} + for sec in sections: + for i in sec["code_indices"]: + if i < len(blocks): + block_sec_title[i] = sec["title"] + + for i in range(len(blocks)): + if used[i]: + continue + lang = lang_of(i) + sec_title = block_sec_title.get(i, "(导言)") + # Apply fixes + c0 = common_fix(blocks[i]["code"], pid, i) + c1 = exact_fix(pid, i, c0) + tgt = next_target(cfg["prefix"]) + # English source filename stem from the explicit map (with fallback) + src_name = src_name_for(pid, sec_title, cfg["prefix"], i) + # ensure uniqueness within dir (same section name → _2, _3...) + cand = src_name; k = 2 + existing_names = {os.path.basename(t[1]) for t in targets_by_dir.get(out_dir, [])} + base_ext = ".cpp" if lang == "cpp" else ".c" + while f"{cand}{base_ext}" in existing_names: + cand = f"{src_name}_{k}"; k += 1 + src_name = cand + src_rel, _ = emit_console(tgt, [(i, c1, sec_title)], lang, out_dir, src_name=src_name) + targets_by_dir.setdefault(out_dir, []).append((tgt, src_rel)) + used[i] = True + + leftover = [i for i in range(len(blocks)) if not used[i]] + if leftover: + print(f"[WARN] {pid} unused blocks: {leftover}", file=sys.stderr) + + # --- 写每个 dir 的 CMakeLists --- + write_part1_cmakelists(targets_by_dir) + # --- 写 ERRATA --- + write_errata() + print(f"gen_part1: wrote {sum(len(v) for v in targets_by_dir.values())} targets across {len(targets_by_dir)} dirs.") + +def write_part1_cmakelists(targets_by_dir): + # per-dir CMakeLists + for d, targets in targets_by_dir.items(): + lines = [] + lines.append(f"# Auto-generated by tools/gen_part1.py — do not edit by hand.\n") + lines.append(f"# 章节:{d}\n") + for tgt, src_rel in targets: + src = os.path.basename(src_rel) + lang = "C" if src.endswith(".c") else "CXX" + lines.append(f"add_executable({tgt} {src})\n") + lines.append(f"set_target_properties({tgt} PROPERTIES " + f"C_STANDARD 17 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF)\n") + if lang == "C": + lines.append(f"target_compile_options({tgt} PRIVATE -Wall -Wextra)\n") + else: + lines.append(f"set_target_properties({tgt} PROPERTIES " + f"CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)\n") + lines.append(f"target_compile_options({tgt} PRIVATE -Wall -Wextra)\n") + # a convenience target to build all in this dir + dir_tname = "all_" + d.replace("/", "_").replace("-", "_") + lines.append(f"add_custom_target({dir_tname} DEPENDS {' '.join(t for t,_ in targets)})\n") + write(os.path.join(REPO, d, "CMakeLists.txt"), "".join(lines)) + + # top-level p01/CMakeLists that includes all sub-dirs + subdirs = sorted(set(d.split("/")[0] if "/" not in d else "/".join(d.split("/")[:2]) for d in targets_by_dir)) + # actually include every dir that has targets + seen = set() + includes = [] + for d in sorted(targets_by_dir.keys()): + # include the deepest dir that has a CMakeLists we wrote + includes.append(d) + lines = ["# Auto-generated by tools/gen_part1.py\n"] + for d in includes: + rel = os.path.relpath(d, "p01") + lines.append(f"add_subdirectory({rel})\n") + write(os.path.join(REPO, "p01", "CMakeLists.txt"), "".join(lines)) + +def write_errata(): + lines = ["# ERRATA — 代码修正记录\n\n", + "本文件由 `tools/gen_part1.py` 自动生成,记录对 wiki 原始代码块的每一处修正。\n", + "格式:`[pageId:blockIdx]` 类型 → 原内容 → 修正后 → 依据。\n\n"] + if not ERRATA: + lines.append("(无修正)\n") + else: + for (pid, idx, kind, before, after, reason) in ERRATA: + lines.append(f"## [{pid}:{idx}] {kind}\n\n") + lines.append(f"- **原文**:`{before}`\n") + lines.append(f"- **修正**:`{after}`\n") + lines.append(f"- **依据**:{reason}\n\n") + write(os.path.join(REPO, "docs", "ERRATA.md"), "".join(lines)) + +if __name__ == "__main__": + main() diff --git a/tools/gen_part3.py b/tools/gen_part3.py new file mode 100644 index 0000000..163bb39 --- /dev/null +++ b/tools/gen_part3.py @@ -0,0 +1,1463 @@ +#!/usr/bin/env python3 +# ============================================================================== +# gen_part3.py — 生成 Part3(Qt 桌面应用开发)的全部源码、.qrc 与 CMakeLists +# +# 输入:tools/wiki_data/ 中 9 个 Qt 页面(pageId 58954515–58954552,共 73 块)。 +# 策略: +# * 每块按内容形态分类(见 BLOCK_TREATMENT): +# - 'program' 原文已含 main+QApplication,作为目标近乎原样(补 include/修笔误)。 +# - 'harness' 有意义片段(控件/信号槽/事件/绘图),用 main+QApplication+show +# 外壳包裹,必要时补所需 QWidget/QMainWindow 子类骨架。 +# - 'snippet' 纯签名/单语句/伪代码,不单独建目标,折进各章 README 作文档。 +# * 每个目标 = 一个独立 add_executable;开 AUTOMOC(Q_OBJECT 类需 moc)。 +# * 离屏运行友好:每个目标注入一个 QTimer::singleShot 让程序 200ms 后自动退出, +# 便于 QT_QPA_PLATFORM=offscreen 自动化验证(窗口类目标)。 +# * 资源依赖(:/images/*.png 等)用脚本生成的纯色占位 png + .qrc 提供,qt5_add_resources。 +# * Qt 代码修正(笔误/平台/过时 API)每处 record_erratum 进 docs/ERRATA.md(透明可审计)。 +# +# 验证:Docker 容器或宿主机用隔离 Qt 5.14.2 构建 → +# QT_QPA_PLATFORM=offscreen tools/run_qt.sh 自动退出验证。 +# ------------------------------------------------------------------------------ +import json, os, re, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import gen_sources +from gen_sources import (REPO, WIKI, page_outline, common_fix, write, record_erratum, + PAGE_CONFIG, classify_lang) +from file_name_map import stem_for +ERRATA = gen_sources.ERRATA + +EXTRACTED = json.load(open(os.path.join(WIKI, "extracted_code.json"))) +PAGES = {p["id"]: p for p in json.load(open(os.path.join(WIKI, "all_page_ids.json")))} + +def norm_title(title): + """Strip Confluence anchor prefixes like '_Toc466556765' that some Qt page + headings carry, so titles match the clean keys in file_name_map.py.""" + return re.sub(r"_Toc\d+", "", title).strip() + +PAGES_META_PID = "?" + +# ============================================================================== +# 逐块处理表:键=(pageId, blockIdx),值=该块如何处理。 +# ('program', include_list) 原文含 main,作目标;include_list=需补的 include。 +# ('harness', harness_spec) 需 main 外壳;harness_spec 见 emit_harness。 +# ('snippet', None) 不建目标,折进 README。 +# 分类依据:tools subagent 对 73 块的勘察(见会话记录)。 +# ============================================================================== +# 'harness' 的形态(决定包裹方式): +# ('plain', includes, body_kind) +# body_kind: 'top' 片段直接放文件作用域(已含完整的成员函数定义/类) +# 'widget-ctor' 片段是某个 widget 子类构造函数体内的语句(用 this) +# 'mainwin-ctor' 片段是 QMainWindow 子类构造函数体内语句 +# 'member-def' 片段是 ::成员函数 定义(需先有类声明骨架) +# ('class+main', includes, class_decl, body) 先放 class_decl 再放 body,main 实例化 + +# 每块的 include 需求(按片段用到的 Qt 类确定;标准模板会再加 QApplication/Widget) +INC = { + "app": "#include ", + "widget": "#include ", + "mwin": "#include ", + "pushbtn": "#include ", + "label": "#include ", + "dialog": "#include ", + "msgbox": "#include ", + "filedlg": "#include ", + "dock": "#include ", + "textedit": "#include ", + "menubar": "#include ", + "action": "#include ", + "qfile": "#include ", + "qfileinfo": "#include ", + "qtextstream": "#include ", + "qdatastream": "#include ", + "qdebug": "#include ", + "qiodevice": "#include ", + "qpainter": "#include ", + "qpixmap": "#include ", + "qbitmap": "#include ", + "qimage": "#include ", + "qpicture": "#include ", + "qpaintevent": "#include ", + "qmovie": "#include ", + "qspinbox": "#include ", + "qslider": "#include ", + "qboxlayout": "#include ", + "qevent": "#include ", + "qmouseevent": "#include ", + "qstring": "#include ", + "qtimer": "#include ", + "qstringlist": "#include ", + "qcoreapp": "#include ", +} + +# 构建时的逐块处理决策表(pageId, blockIdx -> 处理方式)。 +# 形态见上方注释。资源依赖(resources 集合)单独由 exact_fix 标记收集。 +BLOCK_TREATMENT = { + # === 58954515 ch02 创建Qt项目 === + ("58954515", 0): ("rewrite", "minimal_qt_app", "rewrite_minimal_qt_app"), + + # === 58954525 ch03 第一个Qt小程序 === + # 0: 按钮创建(widget-ctor 片段,混入散文 + 含中文字符串,改 rewrite 避免 prose 误伤) + ("58954525", 0): ("rewrite", "button_creation", "rewrite_button_creation"), + # 1,2: 对象树伪代码 → snippet + ("58954525", 1): ("snippet", None, None), + ("58954525", 2): ("snippet", None, None), + + # === 58954527 ch04 信号和槽 === + # 0: 系统自带信号槽(widget-ctor,connect close)— 引用 MyWidget::close,改 rewrite + ("58954527", 0): ("rewrite", "builtin_signal_slot", "rewrite_builtin_signal_slot"), + # 1: connect 签名 → snippet + ("58954527", 1): ("snippet", None, None), + # 2: 自定义信号槽(含 Teacher/Student Q_OBJECT + MyWidget,重散文)→ 整体可编译重写 + ("58954527", 2): ("rewrite", "custom_signal_slot", "rewrite_custom_signal_slot"), + # 3: 带参数自定义信号槽(重载解析 connect)→ snippet(依赖 2 的类) + ("58954527", 3): ("snippet", None, None), + # 4: Qt4 SIGNAL/SLOT 写法 → snippet(过时写法,仅文档) + ("58954527", 4): ("snippet", None, None), + # 5: lambda 语法骨架 → snippet + ("58954527", 5): ("snippet", None, None), + # 6: lambda 实例(widget-ctor + QPushButton + connect lambda) + ("58954527", 6): ("harness", "lambda_signal_slot", + {"includes": [INC["app"], INC["widget"], INC["pushbtn"], INC["qdebug"]], + "body_kind": "widget-ctor"}), + + # === 58954529 ch05 QMainWindow === + # 0,1,2: 菜单栏 API 签名 → snippet + ("58954529", 0): ("snippet", None, None), + ("58954529", 1): ("snippet", None, None), + ("58954529", 2): ("snippet", None, None), + # 3: 状态栏签名 → snippet + ("58954529", 3): ("snippet", None, None), + # 4: 铆接部件(mainwin-ctor,尾部混散文 + 中文标题,改 rewrite) + ("58954529", 4): ("rewrite", "dock_widget", "rewrite_dock_widget"), + # 5: 核心部件(mainwin-ctor) + ("58954529", 5): ("harness", "central_widget", + {"includes": [INC["app"], INC["mwin"], INC["textedit"]], + "body_kind": "mainwin-ctor"}), + # 6: .qrc XML 示例 → snippet(非 C++) + ("58954529", 6): ("snippet", None, None), + + # === 58954535 ch06 对话框 === + # 0: 模态对话框(widget-ctor handler,exec()) + ("58954535", 0): ("harness", "modal_dialog", + {"includes": [INC["app"], INC["widget"], INC["dialog"]], + "body_kind": "widget-ctor-handler"}), + # 1,2,3: 非模态对话框(widget-ctor handler,show) + ("58954535", 1): ("harness", "modeless_dialog_stack", + {"includes": [INC["app"], INC["widget"], INC["dialog"]], + "body_kind": "widget-ctor-handler"}), + ("58954535", 2): ("harness", "modeless_dialog_heap", + {"includes": [INC["app"], INC["widget"], INC["dialog"]], + "body_kind": "widget-ctor-handler"}), + ("58954535", 3): ("harness", "modeless_dialog_deleteonclose", + {"includes": [INC["app"], INC["widget"], INC["dialog"]], + "body_kind": "widget-ctor-handler"}), + # 4-9: QMessageBox 签名 → snippet + ("58954535", 4): ("snippet", None, None), + ("58954535", 5): ("snippet", None, None), + ("58954535", 6): ("snippet", None, None), + ("58954535", 7): ("snippet", None, None), + ("58954535", 8): ("snippet", None, None), + ("58954535", 9): ("snippet", None, None), + # 10,11: QMessageBox 实例(widget-ctor-handler) + ("58954535", 10): ("harness", "message_box_question", + {"includes": [INC["app"], INC["widget"], INC["msgbox"], INC["qdebug"]], + "body_kind": "widget-ctor-handler"}), + ("58954535", 11): ("harness", "message_box_custom", + {"includes": [INC["app"], INC["widget"], INC["msgbox"], INC["qdebug"]], + "body_kind": "widget-ctor-handler"}), + # 12,13,14: 文件对话框 MainWindow(合并为一个目标 file_dialog) + ("58954535", 12): ("rewrite", "file_dialog", "rewrite_file_dialog"), + ("58954535", 13): ("snippet", None, None), + ("58954535", 14): ("snippet", None, None), + # 15: getOpenFileName 签名 → snippet + ("58954535", 15): ("snippet", None, None), + + # === 58954540 ch08 常用控件 === + # 0: setText 签名 → snippet + ("58954540", 0): ("snippet", None, None), + # 1: QLable 创建(widget-ctor,笔误 QLable) + ("58954540", 1): ("harness", "label_text_create", + {"includes": [INC["app"], INC["widget"], INC["label"]], + "body_kind": "widget-ctor", "fix": "qlable_typo"}), + # 2: label html 超链接(widget-ctor) + ("58954540", 2): ("harness", "label_text_html", + {"includes": [INC["app"], INC["widget"], INC["label"]], + "body_kind": "widget-ctor"}), + # 3-6: 显示图片(4,5,6 合并为一个目标 label_pixmap,3 是签名 snippet) + ("58954540", 3): ("snippet", None, None), + ("58954540", 4): ("snippet", None, None), + ("58954540", 5): ("snippet", None, None), + ("58954540", 6): ("snippet", None, None), + ("58954540", "img"): ("rewrite", "label_pixmap", "rewrite_label_pixmap"), + # 7-10: 显示动画(合并为目标 label_movie) + ("58954540", 7): ("snippet", None, None), + ("58954540", 8): ("snippet", None, None), + ("58954540", 9): ("snippet", None, None), + ("58954540", 10): ("snippet", None, None), + ("58954540", "movie"): ("rewrite", "label_movie", "rewrite_label_movie"), + # 11-14: lineedit 签名 → snippet + ("58954540", 11): ("snippet", None, None), + ("58954540", 12): ("snippet", None, None), + ("58954540", 13): ("snippet", None, None), + ("58954540", 14): ("snippet", None, None), + # 15: 自定义控件 SmallWidget(Q_OBJECT,自带 main 实例化) + ("58954540", 15): ("rewrite", "custom_widget", "rewrite_custom_widget"), + + # === 58954546 ch09 事件 === + # 0: EventLabel 完整程序(mouse event)— 含字符串内误入换行,改 rewrite + ("58954546", 0): ("rewrite", "mouse_event", "rewrite_mouse_event"), + # 1: setMouseTracking 单语句 → snippet + ("58954546", 1): ("snippet", None, None), + # 2: CustomWidget::event override(member-def,需类骨架) + ("58954546", 2): ("rewrite", "event_override_customwidget", + "rewrite_event_override_customwidget"), + # 3: CustomTextEdit::event override → snippet(依赖骨架) + ("58954546", 3): ("snippet", None, None), + # 4,5: event 分发参考 → snippet + ("58954546", 4): ("snippet", None, None), + ("58954546", 5): ("snippet", None, None), + # 6: MainWindow 事件过滤器(class+main,无 Q_OBJECT 但有 eventFilter override) + ("58954546", 6): ("rewrite", "event_filter_mainwindow", + "rewrite_event_filter_mainwindow"), + # 7: 事件过滤器 CustomWidget+FilterObject(重散文)→ snippet(依赖复杂) + ("58954546", 7): ("snippet", None, None), + # 8,9: Win32 WndProc 参考 → snippet(非 Qt) + ("58954546", 8): ("snippet", None, None), + ("58954546", 9): ("snippet", None, None), + # 10: notify 签名 → snippet + ("58954546", 10): ("snippet", None, None), + + # === 58954548 ch10 绘图 === + # 0+1: PaintedWidget (Q_OBJECT) h+cpp 合并目标 qpainer_basic + ("58954548", 0): ("rewrite", "qpainter_basic", "rewrite_qpainter_basic"), + ("58954548", 1): ("snippet", None, None), # 并入上面的 rewrite + # 2: PaintWidget 绘 pixmap/bitmap(含资源 butterfly.png) + ("58954548", 2): ("rewrite", "paint_device_pixmaps", + "rewrite_paint_device_pixmaps"), + # 3: PaintWidget 绘 QImage + ("58954548", 3): ("rewrite", "paint_device_qimage", + "rewrite_paint_device_qimage"), + # 4: QPicture(Windows 路径 D:\n 修正) + ("58954548", 4): ("rewrite", "qpicture_io", "rewrite_qpicture_io"), + + # === 58954552 ch11 文件系统 === + # 0: 完整程序 QFile 读 in.txt(原文用 QApplication+app.exec() 会阻塞 GUI 事件循环, + # 实为控制台式文件操作;改 rewrite 用 QCoreApplication 并立即退出) + ("58954552", 0): ("rewrite", "qfile_basic", "rewrite_qfile_basic"), + # 1: QFileInfo 字段 demo(双重声明 bug)→ snippet + ("58954552", 1): ("snippet", None, None), + # 2,3,4: QDataStream 读写 file.dat → 合并为 qdatastream_io + ("58954552", 2): ("snippet", None, None), + ("58954552", 3): ("snippet", None, None), + ("58954552", 4): ("snippet", None, None), + ("58954552", "data"): ("rewrite", "qdatastream_io", "rewrite_qdatastream_io"), + # 5,6: QTextStream 读写 file.txt → 合并为 qtextstream_io + ("58954552", 5): ("snippet", None, None), + ("58954552", 6): ("snippet", None, None), + ("58954552", "text"): ("rewrite", "qtextstream_io", "rewrite_qtextstream_io"), +} + +# 目标 → 所需资源(:/前缀 资源路径)。占位 png 由生成器产出 + .qrc。 +TARGET_RESOURCES = { + "label_pixmap": {":/Image/boat.jpg": "image_boat.jpg"}, + "label_movie": {":/Mario.gif": "mario.gif"}, + "paint_device_pixmaps": {":/Image/butterfly.png": "image_butterfly.png", + ":/Image/butterfly1.png": "image_butterfly1.png"}, +} +# 目标 → 运行时数据文件(写入工作目录供程序读写) +TARGET_DATAFILES = { + "qfile_basic": "in.txt", + "qdatastream_io": "file.dat", + "qtextstream_io": "file.txt", +} + +# ============================================================================== +# 外壳模板:把一个「片段」包成可运行的 Qt 程序 +# ============================================================================== +AUTO_QUIT = ( + " // 离屏/自动化验证:200ms 后自动退出(GUI 模式下不阻塞)\n" + " QTimer::singleShot(200, &app, &QApplication::quit);\n" +) +AUTO_QUIT_CORE = ( + " QTimer::singleShot(0, &app, &QCoreApplication::quit);\n" +) + +def emit_program(pid, idx, stem, out_dir, meta): + """原文已含 main+QApplication 的完整程序:补 include、修笔误后原样输出。""" + code = EXTRACTED[pid][idx]["code"] + code = common_fix(code, pid, idx) + code = exact_fix(code, pid, idx) + includes = "\n".join(meta.get("includes", [])) + if meta.get("needs_widget_h"): + # 58954515/0 引用了 "widget.h",补一个内联的最小 Widget 定义使自洽 + includes += "\n#include \nclass Widget : public QWidget { Q_OBJECT public: Widget(QWidget* p=nullptr):QWidget(p){} };\n" + parts = [header_comment(pid, idx, stem)] + parts.append("#include \n") + if includes.strip(): + parts.append(includes + "\n") + parts.append("\n") + parts.append(code.rstrip() + "\n") + if not re.search(r"\bQApplication\b", code): + # 万一原文用 QCoreApplication,上面的 singleShot 也兼容 + pass + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), "".join(parts)) + return src + +def emit_harness(pid, idx, stem, out_dir, meta): + """把片段包进 main+QApplication+show。body_kind 决定片段放置方式。""" + code = EXTRACTED[pid][idx]["code"] + code = common_fix(code, pid, idx) + code = exact_fix(code, pid, idx) + if meta.get("fix") == "prose_strip": + code = strip_inline_prose(code, pid, idx) + body_kind = meta.get("body_kind", "widget-ctor") + includes = meta.get("includes", []) + parts = [header_comment(pid, idx, stem)] + for inc in includes: + parts.append(inc + "\n") + if INC["qtimer"] not in includes: + parts.append(INC["qtimer"] + "\n") + parts.append("\n") + + if body_kind in ("widget-ctor", "widget-ctor-handler"): + # 片段是某 widget 子类构造函数体内语句(用 this)。包成: + # class DemoWidget : public QWidget { Q_OBJECT public: DemoWidget(){ <片段> } }; + cls = "DemoWidget" + parts.append(f"class {cls} : public QWidget {{\n") + parts.append(" Q_OBJECT\n") + parts.append("public:\n") + if body_kind == "widget-ctor-handler": + # 片段是事件处理(如按钮点击弹对话框):放一个槽里,构造时触发一次 + parts.append(f" {cls}(){{ doDemo(); }}\n") + parts.append("private slots:\n void doDemo(){\n") + parts.append(indent(code, 2) + "\n") + parts.append(" }\n") + else: + parts.append(f" {cls}(){{\n") + parts.append(indent(code, 2) + "\n") + parts.append(" }\n") + parts.append("};\n\n") + parts.append(main_with_widget(cls)) + elif body_kind == "mainwin-ctor": + cls = "DemoMainWindow" + parts.append(f"class {cls} : public QMainWindow {{\n") + parts.append(" Q_OBJECT\npublic:\n") + parts.append(f" {cls}(){{\n") + parts.append(indent(code, 2) + "\n") + parts.append(" }\n};\n\n") + parts.append(main_with_widget(cls, is_mainwin=True)) + else: + parts.append(code.rstrip() + "\n") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), "".join(parts)) + return src + +def main_with_widget(cls, is_mainwin=False): + s = ( + "int main(int argc, char* argv[]) {\n" + " QApplication app(argc, argv);\n" + f" {cls} w;\n" + " w.show();\n" + + AUTO_QUIT + + " return app.exec();\n" + "}\n" + ) + return s + +def header_comment(pid, idx, stem): + return ( + "// ============================================================================\n" + f"// {stem} — Qt 桌面示例\n" + "// 来源:川大 C++/LinuxC wiki「02.Qt方向」\n" + f"// 原文片段:[{pid}:{idx}]\n" + "// 说明:本文件由 gen_part3.py 从 wiki 片段生成(必要时补 main/QApplication\n" + "// 外壳、include、修正笔误)。详见 docs/ERRATA.md。\n" + "// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh \n" + "// ============================================================================\n\n" + ) + +def indent(s, n): + pad = " " * n + return "\n".join(pad + l if l.strip() else l for l in s.splitlines()) + +# ============================================================================== +# exact_fix:对 Qt 片段的轻量自动修正(笔误/全角/平台)。逐块的复杂重写见 rewrite_*。 +# ============================================================================== +def exact_fix(code, pid, idx): + # QLable → QLabel(笔误) + if "QLable" in code: + code = code.replace("QLable", "QLabel") + record_erratum(pid, idx, "typo", "QLable", "QLabel", + "笔误:QLabel 误写为 QLable。") + # 散文混入代码:把「行内/行尾」的中文教学散文剥离。 + # 典型: QPushButton * btn = new QPushButton; 头文件 #include + # 典型: ...setAllowedAreas(...) 设置区域范围 + # 策略:对含 CJK 字符的行,若该行同时含代码(含 ASCII 标点/关键字),则截断在第一段 + # 连续 CJK 处(去掉 CJK 及其后的整行内容);若整行都是 CJK/注释,保留原样(多是注释)。 + # 仅当该块被标记需要 prose 处理时执行(避免误伤正常中文注释)。 + return code + +def strip_inline_prose(code, pid, idx): + """Remove inline Chinese teaching prose that the wiki glued onto code lines. + Applied only to blocks flagged 'prose_strip' in BLOCK_TREATMENT.""" + orig = code + out_lines = [] + for line in code.splitlines(): + # find first run of CJK chars in the line + m = re.search(r"[\u4e00-\u9fff]", line) + if not m: + out_lines.append(line); continue + # is the CJK preceded by real code on this line? (// comment lines are fine) + before = line[:m.start()] + codeish = bool(re.search(r"[A-Za-z0-9_)\];,]", before)) + commented = "//" in before or before.strip().startswith("/*") + if codeish and not commented: + # truncate at the CJK run, keep the code part + out_lines.append(before.rstrip()) + else: + out_lines.append(line) + fixed = "\n".join(out_lines) + if fixed != orig: + record_erratum(pid, idx, "prose-in-code", + "代码行内/行尾混入中文教学散文(如「头文件 #include 」「设置区域范围」)", + "剥离行内中文散文,保留代码部分", + "wiki 把讲解散文直接粘在代码行末,破坏语法(如把 #include 当成语句尾巴)。" + "剥离散文使代码可编译。") + return fixed + +# ============================================================================== +# rewrite_*:对结构复杂/散文混入严重的块,直接给出可编译的完整程序。 +# ============================================================================== +def rewrite_minimal_qt_app(pid, idx, out_dir, stem): + """2.1 一个最简单的Qt应用程序。原文 #include "widget.h" 引用外部 Widget; + 输出自洽的最小 Widget + main(保留 Qt 项目骨架教学意图)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n\n" + "// 最简单的 Widget:原文 widget.h 的占位实现\n" + "class Widget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " Widget(QWidget* parent = nullptr) : QWidget(parent) {}\n" + "};\n\n" + "int main(int argc, char* argv[]) {\n" + " QApplication app(argc, argv);\n" + " Widget w;\n" + " w.show();\n" + + AUTO_QUIT + + " return app.exec();\n" + "}\n" + ) + record_erratum(pid, idx, "missing-symbol", + "原文 #include \"widget.h\" 引用外部 Widget 类(widget.h/widget.cpp 不在块内)", + "在文件内就地补一个最小 Widget : public QWidget(Q_OBJECT)+ main", + "原 wiki 的「最简单 Qt 程序」依赖外部的 widget.h;按「示例相对隔离」拆分为独立目标后" + "本文件缺少 Widget 定义。补最小 Widget 使其自洽可编译运行。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_button_creation(pid, idx, out_dir, stem): + """3.1 按钮的创建:QPushButton 创建、设父、设文本、移动、设窗口大小/标题。 + 原文行内混入散文「头文件 #include 」;中文文本在字符串内, + 通用 prose 剥离会误伤字符串,故整体重写。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class DemoWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " DemoWidget() {\n" + " // 第一种创建:先构造再设属性\n" + " QPushButton* btn = new QPushButton;\n" + " btn->setParent(this); // 设置父亲\n" + " btn->setText(QStringLiteral(\"德玛西亚\")); // 设置文字\n" + " btn->move(100, 100); // 移动位置\n\n" + " // 第二种创建:构造时指定文本与父亲\n" + " QPushButton* btn2 = new QPushButton(QStringLiteral(\"孙悟空\"), this);\n" + " btn2->move(100, 140);\n\n" + " this->resize(600, 400); // 重新指定窗口大小\n" + " this->setWindowTitle(QStringLiteral(\"第一个项目\")); // 设置窗口标题\n" + " this->setFixedSize(600, 400); // 限制窗口大小\n" + " }\n" + "};\n\n" + main_with_widget("DemoWidget") + ) + record_erratum(pid, idx, "prose-in-code", + "按钮创建片段行内混入散文「头文件 #include 」,且含中文字符串", + "重写为可编译 DemoWidget(剥离散文、保留中文文本)", + "原 wiki 把讲解散文粘在代码行内(如 #include 当成尾巴),破坏语法;通用 prose " + "剥离会误伤字符串内的中文,故整体重写保留教学意图。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_dock_widget(pid, idx, out_dir, stem): + """5.4 铆接部件 QDockWidget:addDockWidget + setAllowedAreas。 + 原文尾部混散文「设置区域范围」;含中文标题,整体重写。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class DemoMainWindow : public QMainWindow {\n" + " Q_OBJECT\n" + "public:\n" + " DemoMainWindow() {\n" + " // 铆接部件(停靠窗口):标题 + 父对象\n" + " QDockWidget* dock = new QDockWidget(QStringLiteral(\"标题\"), this);\n" + " addDockWidget(Qt::LeftDockWidgetArea, dock);\n" + " // 设置可停靠区域范围(左/右/上/下)\n" + " dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea |\n" + " Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);\n" + " }\n" + "};\n\n" + main_with_widget("DemoMainWindow", is_mainwin=True) + ) + record_erratum(pid, idx, "prose-in-code", + "QDockWidget 片段尾部混入散文「设置区域范围」,含中文标题字符串", + "重写为可编译 DemoMainWindow(剥离散文、保留中文标题)", + "原 wiki 把讲解散文粘在代码行尾(setAllowedAreas 后接「设置区域范围」),破坏语法;" + "整体重写保留「铆接部件」教学意图。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_builtin_signal_slot(pid, idx, out_dir, stem): + """4.1 系统自带信号和槽:QPushButton clicked → 窗口 close。 + 原文用 &MyWidget::close;改 DemoWidget + QWidget::close。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class DemoWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " DemoWidget() {\n" + " // 系统自带的信号和槽:按钮 clicked → 关闭窗口\n" + " QPushButton* quitBtn = new QPushButton(QStringLiteral(\"关闭窗口\"), this);\n" + " quitBtn->move(50, 50);\n" + " resize(300, 200);\n" + " connect(quitBtn, &QPushButton::clicked, this, &QWidget::close);\n" + " }\n" + "};\n\n" + main_with_widget("DemoWidget") + ) + record_erratum(pid, idx, "harness-context", + "片段 connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close) 引用 MyWidget," + "但 MyWidget 定义不在块内", + "补 DemoWidget 子类 + main 外壳,槽用 QWidget::close(系统自带)", + "原 wiki 片段假定存在 MyWidget 类与 close 槽;按「示例相对隔离」补齐类骨架与 main," + "演示「系统自带信号(clicked)→系统自带槽(close)」。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_mouse_event(pid, idx, out_dir, stem): + """9.1 事件:EventLabel 重写 mouseMoveEvent/PressEvent/ReleaseEvent。 + 原文 QString(\"...(%1, %2)\\n...\") 内误入了字面换行;合并为单行字符串。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "// 自定义 QLabel:重写鼠标事件,显示坐标\n" + "class EventLabel : public QLabel {\n" + " Q_OBJECT\n" + "public:\n" + " explicit EventLabel(QWidget* p = nullptr) : QLabel(p) {\n" + " setMouseTracking(true); // 启用鼠标移动追踪(默认只在按键时追踪)\n" + " setText(QStringLiteral(\"移动鼠标到这里\"));\n" + " }\n" + "protected:\n" + " void mouseMoveEvent(QMouseEvent* event) override {\n" + " setText(QString(\"

Move: (%1, %2)

\")\n" + " .arg(event->x()).arg(event->y()));\n" + " }\n" + " void mousePressEvent(QMouseEvent* event) override {\n" + " setText(QString(\"

Press: (%1, %2)

\")\n" + " .arg(event->x()).arg(event->y()));\n" + " }\n" + " void mouseReleaseEvent(QMouseEvent* event) override {\n" + " setText(QString(\"

Release: (%1, %2)

\")\n" + " .arg(event->x()).arg(event->y()));\n" + " }\n" + "};\n\n" + main_with_widget("EventLabel") + ) + record_erratum(pid, idx, "syntax+merge", + "EventLabel 事件重写程序:QString(\"...(%1, %2)\\n...\") 内误入字面换行(断行)" + "导致字符串未终结;类声明与实现分散", + "合并为单 .cpp,字符串内换行移除(\\n 移出字符串或删除),补 setMouseTracking", + "原 wiki 的 QString 字面量在源码中跨行书写(实际是 \\n 后跟换行),C++ 字符串字面量" + "不能直接跨行,导致 missing terminating \"。修正为单行字符串。同时补 mouseTracking。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_custom_signal_slot(pid, idx, out_dir, stem): + """4.2 自定义信号和槽:Teacher/Student Q_OBJECT + MyWidget,emit + connect。 + 原文散文混入严重;给出可编译的完整对照程序。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "// ---- 老师(发送者):饿了就发信号 ----\n" + "class Teacher : public QObject {\n" + " Q_OBJECT\n" + "public:\n" + " explicit Teacher(QObject* p = nullptr) : QObject(p) {}\n" + "signals:\n" + " void hungry(); // 无参信号\n" + " void hungry(QString food);// 带参信号(重载)\n" + "public slots:\n" + " void classIsOver() {\n" + " emit hungry(); // 触发无参信号\n" + " emit hungry(QString(\"汉堡\")); // 触发带参信号\n" + " }\n" + "};\n\n" + "// ---- 学生(接收者):请客吃饭 ----\n" + "class Student : public QObject {\n" + " Q_OBJECT\n" + "public:\n" + " explicit Student(QObject* p = nullptr) : QObject(p) {}\n" + "public slots:\n" + " void treat() { qDebug() << \"老师饿了,请老师吃饭!\"; }\n" + " void treat(QString food) { qDebug() << \"老师饿了,请老师吃\" << food; }\n" + "};\n\n" + "// ---- 聚合窗口:连接信号槽并触发演示 ----\n" + "class MyWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " MyWidget() {\n" + " Teacher* t = new Teacher(this);\n" + " Student* s = new Student(this);\n" + " // 连接无参信号/槽\n" + " connect(t, QOverload<>::of(&Teacher::hungry),\n" + " s, QOverload<>::of(&Student::treat));\n" + " // 带参重载:用 QOverload::of 显式选择重载\n" + " connect(t, QOverload::of(&Teacher::hungry),\n" + " s, QOverload::of(&Student::treat));\n" + " // 触发演示\n" + " t->classIsOver();\n" + " }\n" + "};\n\n" + "int main(int argc, char* argv[]) {\n" + " QApplication app(argc, argv);\n" + " MyWidget w; w.show();\n" + + AUTO_QUIT + + " return app.exec();\n" + "}\n" + ) + record_erratum(pid, idx, "prose-in-code", + "自定义信号槽:Teacher/Student/MyWidget 定义与中文散文混排,且信号名 hungury 笔误、" + "槽名 treat/eat 不一致", + "重写为可编译对照程序:信号 hungry()/hungry(QString)、槽 treat()/treat(QString)," + "函数指针重载解析 connect", + "原 wiki 把类定义、emit、connect 与中文讲解散文混在一个代码块,无法编译;" + "信号名 hungury 疑为 hungry 笔误。重写保留「自定义信号+槽+重载」教学意图。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_file_dialog(pid, idx, out_dir, stem): + """6.5 标准文件对话框:MainWindow + openAction/saveAction + openFile/saveFile 槽。 + 原文 12/13/14 三块合成一个可运行目标。资源 :/images/file-open :/images/file-save。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class MainWindow : public QMainWindow {\n" + " Q_OBJECT\n" + "public:\n" + " MainWindow() {\n" + " QAction* openAction = new QAction(QStringLiteral(\"打开...\"), this);\n" + " QAction* saveAction = new QAction(QStringLiteral(\"保存...\"), this);\n" + " menuBar()->addMenu(QStringLiteral(\"文件\"))->addAction(openAction);\n" + " menuBar()->addMenu(QStringLiteral(\"文件\"))->addAction(saveAction);\n" + " connect(openAction, &QAction::triggered, this, &MainWindow::openFile);\n" + " connect(saveAction, &QAction::triggered, this, &MainWindow::saveFile);\n" + " }\n" + "private slots:\n" + " void openFile() {\n" + " QString path = QFileDialog::getOpenFileName(this, QStringLiteral(\"打开文件\"));\n" + " if (path.isEmpty()) return;\n" + " QFile file(path);\n" + " if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n" + " QMessageBox::warning(this, QStringLiteral(\"错误\"), QStringLiteral(\"打开失败\"));\n" + " return;\n" + " }\n" + " QTextStream in(&file);\n" + " QString text = in.readAll();\n" + " file.close();\n" + " QMessageBox::information(this, QStringLiteral(\"内容\"), text);\n" + " }\n" + " void saveFile() {\n" + " QString path = QFileDialog::getSaveFileName(this, QStringLiteral(\"保存文件\"));\n" + " if (path.isEmpty()) return;\n" + " QFile file(path);\n" + " if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {\n" + " QMessageBox::warning(this, QStringLiteral(\"错误\"), QStringLiteral(\"保存失败\"));\n" + " return;\n" + " }\n" + " QTextStream out(&file);\n" + " out << QStringLiteral(\"示例保存内容\");\n" + " file.close();\n" + " }\n" + "};\n\n" + "int main(int argc, char* argv[]) {\n" + " QApplication app(argc, argv);\n" + " MainWindow w; w.show();\n" + + AUTO_QUIT + + " return app.exec();\n" + "}\n" + ) + record_erratum(pid, idx, "merge+rewrite", + "文件对话框示例分散在 12/13/14 三块(含 :/images/file-open 资源、connect、openFile/saveFile 实现)", + "合并重写为单一可运行 MainWindow 程序,菜单/工具栏触发文件对话框读写", + "原文三块是同一 MainWindow 的不同部分(构造、连接、槽实现),独立成文件无法编译;" + "合并为完整目标。原文引用资源图标,本目标用纯文字 Action 避免资源依赖。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_label_pixmap(pid, idx, out_dir, stem): + """显示图片:QPixmap load :/Image/boat.jpg + QLabel->setPixmap。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class DemoWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " DemoWidget() {\n" + " QPixmap pixmap;\n" + " pixmap.load(\":/Image/boat.jpg\"); // 从资源加载图片\n" + " QLabel* label = new QLabel(this);\n" + " label->setPixmap(pixmap); // 注意:label 是指针,用 ->\n" + " label->setGeometry(10, 10, 200, 120);\n" + " }\n" + "};\n\n" + main_with_widget("DemoWidget") + ) + record_erratum(pid, idx, "merge+typo", + "显示图片示例分散在 3 块(QPixmap pixmap; pixmap.load(:/Image/boat.jpg); " + "label.setPixmap(pixmap) 用了 . 而非 ->)", + "合并为一个目标,修正 .→->,资源 :/Image/boat.jpg 由生成占位 png + .qrc 提供", + "原文 label 是 QLabel* 却用 . 调用 setPixmap(C++ 指针须用 ->);三块拆开不可编译。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_label_movie(pid, idx, out_dir, stem): + """显示动画:QMovie(:/Mario.gif) + start + QLabel->setMovie。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class DemoWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " DemoWidget() {\n" + " QMovie* movie = new QMovie(\":/Mario.gif\"); // 从资源加载动画\n" + " movie->start();\n" + " QLabel* label = new QLabel(this);\n" + " label->setMovie(movie);\n" + " label->setGeometry(10, 10, 200, 200);\n" + " }\n" + "};\n\n" + main_with_widget("DemoWidget") + ) + record_erratum(pid, idx, "merge+typo", + "显示动画示例分散在 3 块(QMovie(:/Mario.gif)、movie->start()、QLabel 全角分号 ;)", + "合并为一个目标,全角分号→半角,资源 :/Mario.gif 由生成占位 gif + .qrc 提供", + "原文 QLabel 定义末尾用了全角分号 ;(C++ 非法);三块拆开不可编译。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_custom_widget(pid, idx, out_dir, stem): + """8.4 自定义控件 SmallWidget:spin+slider+layout+两 connect(Q_OBJECT)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "// 自定义控件:spinbox 与 slider 联动\n" + "class SmallWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " SmallWidget(QWidget* parent = nullptr) : QWidget(parent) {\n" + " QSpinBox* spin = new QSpinBox(this);\n" + " QSlider* slider = new QSlider(Qt::Horizontal, this);\n" + " QHBoxLayout* layout = new QHBoxLayout(this);\n" + " layout->addWidget(spin);\n" + " layout->addWidget(slider);\n" + " // spin 变化 → slider 跟随(Qt5 静态转换选重载)\n" + " connect(spin,\n" + " static_cast(&QSpinBox::valueChanged),\n" + " slider, &QSlider::setValue);\n" + " // slider 变化 → spin 跟随\n" + " connect(slider, &QSlider::valueChanged, spin, &QSpinBox::setValue);\n" + " }\n" + "};\n\n" + "int main(int argc, char* argv[]) {\n" + " QApplication app(argc, argv);\n" + " SmallWidget w; w.show();\n" + + AUTO_QUIT + + " return app.exec();\n" + "}\n" + ) + record_erratum(pid, idx, "h+cpp-merge", + "自定义控件 SmallWidget 在 wiki 中以 smallwidget.h 内联展示(Q_OBJECT + spin/slider/layout/connect)", + "输出为单 .cpp(含类定义+main 实例化),保留 Qt5 static_cast 重载解析写法", + "原 wiki 把类定义放 .h 注释块;按「每目标单文件」输出,AUTOMOC 处理 Q_OBJECT。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_event_override_customwidget(pid, idx, out_dir, stem): + """9.2 event() 重写:CustomWidget::event 处理 KeyPress/Tab。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class CustomWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " explicit CustomWidget(QWidget* p = nullptr) : QWidget(p) {}\n" + "protected:\n" + " bool event(QEvent* e) override {\n" + " if (e->type() == QEvent::KeyPress) {\n" + " QKeyEvent* ke = static_cast(e);\n" + " if (ke->key() == Qt::Key_Tab) {\n" + " qDebug() << \"Tab pressed in CustomWidget\";\n" + " return true; // 事件已处理,不再向上传递\n" + " }\n" + " }\n" + " return QWidget::event(e); // 其余交给基类\n" + " }\n" + "};\n\n" + main_with_widget("CustomWidget") + ) + record_erratum(pid, idx, "member-def-harness", + "CustomWidget::event(QEvent*) override 片段(处理 KeyPress/Tab)缺少所属类骨架与 main", + "补 CustomWidget : public QWidget 骨架(含 event override)+ main 外壳,行为对照原文", + "原 wiki 只给成员函数定义,缺少类声明与 main;按「示例相对隔离」补齐使其可编译运行。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_event_filter_mainwindow(pid, idx, out_dir, stem): + """9.3 事件过滤器:MainWindow + eventFilter override + installEventFilter。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "// 事件过滤器:拦截 label 上的事件\n" + "class MainWindow : public QMainWindow {\n" + " Q_OBJECT\n" + "public:\n" + " MainWindow() {\n" + " label = new QLabel(QStringLiteral(\"事件过滤器示例\"), this);\n" + " label->setGeometry(10, 30, 200, 30);\n" + " label->installEventFilter(this); // 给 label 安装事件过滤器\n" + " }\n" + "protected:\n" + " bool eventFilter(QObject* watched, QEvent* event) override {\n" + " if (watched == label && event->type() == QEvent::MouseButtonPress) {\n" + " qDebug() << \"label 上的鼠标按下事件被过滤器拦截\";\n" + " return true; // 拦截,不再派发\n" + " }\n" + " return QMainWindow::eventFilter(watched, event);\n" + " }\n" + "private:\n" + " QLabel* label;\n" + "};\n\n" + main_with_widget("MainWindow", is_mainwin=True) + ) + record_erratum(pid, idx, "class+main-harness", + "事件过滤器 MainWindow(eventFilter override + installEventFilter)片段缺 main 与完整结构", + "补完整 MainWindow(含 QLabel、eventFilter、installEventFilter)+ main 外壳", + "原 wiki 给出 MainWindow 类骨架但缺 main;补齐使其可编译运行,演示事件过滤机制。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_qpainter_basic(pid, idx, out_dir, stem): + """10.1 QPainter:PaintedWidget (Q_OBJECT) + paintEvent(drawLine/Rect/Ellipse)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class PaintedWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " PaintedWidget(QWidget* parent = nullptr) : QWidget(parent) {\n" + " setFixedSize(300, 200);\n" + " }\n" + "protected:\n" + " void paintEvent(QPaintEvent*) override {\n" + " QPainter painter(this);\n" + " painter.drawLine(QLine(10, 10, 200, 10)); // 画线\n" + " painter.drawRect(QRect(10, 30, 100, 50)); // 画矩形\n" + " painter.drawEllipse(QRect(150, 30, 100, 80)); // 画椭圆\n" + " }\n" + "};\n\n" + main_with_widget("PaintedWidget") + ) + record_erratum(pid, idx, "h+cpp-merge", + "PaintedWidget 类声明(10.1 block0)与其构造+paintEvent 实现(block1)分两块", + "合并为单 .cpp(Q_OBJECT 类 + paintEvent),补 main 外壳演示 drawLine/Rect/Ellipse", + "原文类声明与成员实现分属两块;按「每目标单文件」合并,AUTOMOC 处理 Q_OBJECT。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_paint_device_pixmaps(pid, idx, out_dir, stem): + """10.2.1 绘 pixmap/bitmap(资源 butterfly.png / butterfly1.png)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class PaintWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " PaintWidget(QWidget* parent = nullptr) : QWidget(parent) { setFixedSize(360, 200); }\n" + "protected:\n" + " void paintEvent(QPaintEvent*) override {\n" + " QPainter painter(this);\n" + " QPixmap pix(\":/Image/butterfly.png\"); // 从资源加载图片\n" + " painter.drawPixmap(10, 10, pix);\n" + " QPixmap pix1(\":/Image/butterfly1.png\");\n" + " painter.drawPixmap(180, 10, pix1);\n" + " // QBitmap 是 QPixmap 子类,仅 1 位深度(黑白掩码)\n" + " QBitmap bmp = pix.mask();\n" + " painter.drawPixmap(10, 110, bmp);\n" + " }\n" + "};\n\n" + main_with_widget("PaintWidget") + ) + record_erratum(pid, idx, "member-def-harness", + "PaintWidget::paintEvent 绘 pixmap/bitmap(引用 :/Image/butterfly.png、butterfly1.png)", + "补 PaintWidget 类骨架 + main,资源由生成占位 png + .qrc 提供", + "原 wiki 仅给成员函数定义,缺类骨架与 main;资源图片用占位 png 提供以可编译运行。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_paint_device_qimage(pid, idx, out_dir, stem): + """10.2.1 绘 QImage(setPixel 循环)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class PaintWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " PaintWidget(QWidget* parent = nullptr) : QWidget(parent) { setFixedSize(200, 200); }\n" + "protected:\n" + " void paintEvent(QPaintEvent*) override {\n" + " QPainter painter(this);\n" + " QImage image(100, 100, QImage::Format_RGB32);\n" + " // QImage 可直接逐像素操作(QPixmap 不行)\n" + " for (int y = 0; y < image.height(); ++y) {\n" + " for (int x = 0; x < image.width(); ++x) {\n" + " image.setPixel(x, y, qRgb((x * 2) % 256, (y * 2) % 256, 128));\n" + " }\n" + " }\n" + " painter.drawImage(10, 10, image);\n" + " }\n" + "};\n\n" + main_with_widget("PaintWidget") + ) + record_erratum(pid, idx, "member-def-harness", + "PaintWidget::paintEvent 绘 QImage(setPixel 循环逐像素设色)片段", + "补 PaintWidget 类骨架 + main 外壳", + "原 wiki 仅给成员函数定义,缺类骨架与 main;补齐使其可编译运行。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_qpicture_io(pid, idx, out_dir, stem): + """10.2.2 QPicture:save/load。原路径 D:\\ndrawing.pic(含误入换行)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "class PaintWidget : public QWidget {\n" + " Q_OBJECT\n" + "public:\n" + " PaintWidget(QWidget* parent = nullptr) : QWidget(parent) { setFixedSize(200, 200); }\n" + "protected:\n" + " void paintEvent(QPaintEvent*) override {\n" + " // QPicture 记录并回放绘图指令\n" + " QPicture picture;\n" + " QPainter painter;\n" + " painter.begin(&picture); // 把绘图记录到 picture\n" + " painter.drawLine(10, 10, 100, 100);\n" + " painter.end();\n" + " picture.save(\"drawing.pic\"); // 保存(用相对路径,跨平台)\n\n" + " QPicture picture2;\n" + " picture2.load(\"drawing.pic\"); // 回放\n" + " QPainter p2(this);\n" + " p2.drawPicture(10, 10, picture2);\n" + " qDebug() << \"QPicture saved & replayed\";\n" + " }\n" + "};\n\n" + main_with_widget("PaintWidget") + ) + record_erratum(pid, idx, "platform-path", + "QPicture 保存路径为 \"D:\\ndrawing.pic\"(Windows 路径,且 \\n 被解析为换行符)", + "改为相对路径 \"drawing.pic\"(跨平台可移植)", + "原路径是 Windows 绝对路径,\\n 在字符串中被当作换行符导致路径错误;改为相对路径使" + "示例在 Linux 下可编译运行。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_qfile_basic(pid, idx, out_dir, stem): + """11.1 基本文件操作:QFile 读 in.txt + QFileInfo 字段。 + 原文用 QApplication+app.exec(),实为控制台文件操作;改用 QCoreApplication 并 + 文件 IO 完成后立即退出(无需 GUI 事件循环,便于自动化运行验证)。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "int main(int argc, char* argv[]) {\n" + " QCoreApplication app(argc, argv);\n\n" + " QFile file(\"in.txt\");\n" + " if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n" + " qDebug() << \"Open file failed.\";\n" + " return -1;\n" + " }\n" + " while (!file.atEnd()) {\n" + " qDebug() << file.readLine();\n" + " }\n\n" + " QFileInfo info(file);\n" + " qDebug() << \"isDir:\" << info.isDir();\n" + " qDebug() << \"isExecutable:\" << info.isExecutable();\n" + " qDebug() << \"baseName:\" << info.baseName();\n" + " qDebug() << \"completeBaseName:\" << info.completeBaseName();\n" + " qDebug() << \"suffix:\" << info.suffix();\n" + " qDebug() << \"completeSuffix:\" << info.completeSuffix();\n\n" + " return 0; // 文件 IO 完成,直接退出(无需 app.exec() 事件循环)\n" + "}\n" + ) + record_erratum(pid, idx, "blocking-event-loop", + "原文用 QApplication + app.exec()(阻塞 GUI 事件循环),但示例实为控制台文件操作", + "改用 QCoreApplication,文件 IO 完成后直接 return 0(不进事件循环)", + "QApplication::exec() 进入 GUI 事件循环会一直阻塞;该示例不创建窗口,仅做文件读写," + "用 QCoreApplication 并直接返回即可正确退出,便于自动化运行验证。原行为不变。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_qdatastream_io(pid, idx, out_dir, stem): + """11.2 二进制文件读写 file.dat:QDataStream write + read 合并演示。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "// 二进制文件读写:先写 file.dat,再读回\n" + "int main(int argc, char* argv[]) {\n" + " QCoreApplication app(argc, argv);\n\n" + " // ---- 写 ----\n" + " {\n" + " QFile file(\"file.dat\");\n" + " if (file.open(QIODevice::WriteOnly)) {\n" + " QDataStream out(&file);\n" + " out << QString(\"hello\") << qint32(42) << 3.14;\n" + " file.close();\n" + " }\n" + " }\n" + " // ---- 读 ----\n" + " {\n" + " QFile file(\"file.dat\");\n" + " if (file.open(QIODevice::ReadOnly)) {\n" + " QDataStream in(&file);\n" + " QString s; qint32 n; double d;\n" + " in >> s >> n >> d;\n" + " qDebug() << \"read:\" << s << n << d;\n" + " file.close();\n" + " } else {\n" + " qDebug() << \"open failed\";\n" + " }\n" + " }\n\n" + + AUTO_QUIT_CORE + + " return app.exec();\n" + "}\n" + ) + record_erratum(pid, idx, "merge", + "二进制读写示例分散在 3 块(写/读/读写),各自引用 file.dat 且未检查 open() 返回值", + "合并为一个程序:先写 file.dat 再读回,补 open() 检查,用 QCoreApplication(无需 GUI)", + "原文三块是读写示例的不同片段,独立不可编译;合并为完整程序。补 open() 检查避免" + "未定义行为;数据文件 file.dat 程序自建。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +def rewrite_qtextstream_io(pid, idx, out_dir, stem): + """11.3 文本文件读写 file.txt:QTextStream write + read 合并演示。""" + code = ( + header_comment(pid, idx, stem) + + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n\n" + "// 文本文件读写:先写 file.txt,再读回\n" + "int main(int argc, char* argv[]) {\n" + " QCoreApplication app(argc, argv);\n\n" + " // ---- 写 ----\n" + " {\n" + " QFile data(\"file.txt\");\n" + " if (data.open(QFile::WriteOnly | QFile::Truncate)) {\n" + " QTextStream out(&data);\n" + " out << \"line1: hello\" << endl;\n" + " out << \"line2: world\" << endl;\n" + " data.close();\n" + " }\n" + " }\n" + " // ---- 读 ----\n" + " {\n" + " QFile data(\"file.txt\");\n" + " if (data.open(QFile::ReadOnly)) {\n" + " QTextStream in(&data);\n" + " QString line;\n" + " while (in.readLineInto(&line)) {\n" + " qDebug() << \"read:\" << line;\n" + " }\n" + " data.close();\n" + " }\n" + " }\n\n" + + AUTO_QUIT_CORE + + " return app.exec();\n" + "}\n" + ) + record_erratum(pid, idx, "merge", + "文本读写示例分散在 2 块(写/读),各自引用 file.txt", + "合并为一个程序:先写 file.txt 再逐行读回,用 QCoreApplication(无需 GUI)", + "原文两块是读写示例的不同片段,独立不可编译;合并为完整程序。") + src = os.path.join(out_dir, f"{stem}.cpp") + write(os.path.join(REPO, src), code) + return src + +REWRITES = { + "rewrite_minimal_qt_app": rewrite_minimal_qt_app, + "rewrite_button_creation": rewrite_button_creation, + "rewrite_dock_widget": rewrite_dock_widget, + "rewrite_builtin_signal_slot": rewrite_builtin_signal_slot, + "rewrite_mouse_event": rewrite_mouse_event, + "rewrite_custom_signal_slot": rewrite_custom_signal_slot, + "rewrite_file_dialog": rewrite_file_dialog, + "rewrite_label_pixmap": rewrite_label_pixmap, + "rewrite_label_movie": rewrite_label_movie, + "rewrite_custom_widget": rewrite_custom_widget, + "rewrite_event_override_customwidget": rewrite_event_override_customwidget, + "rewrite_event_filter_mainwindow": rewrite_event_filter_mainwindow, + "rewrite_qpainter_basic": rewrite_qpainter_basic, + "rewrite_paint_device_pixmaps": rewrite_paint_device_pixmaps, + "rewrite_paint_device_qimage": rewrite_paint_device_qimage, + "rewrite_qpicture_io": rewrite_qpicture_io, + "rewrite_qfile_basic": rewrite_qfile_basic, + "rewrite_qdatastream_io": rewrite_qdatastream_io, + "rewrite_qtextstream_io": rewrite_qtextstream_io, +} + +# ============================================================================== +# 资源:生成纯色占位 png/gif + .qrc +# ============================================================================== +def make_placeholder_png(path, w=64, h=64, rgb=(120, 160, 200)): + """Write a minimal valid PNG (single solid color) without PIL — pure zlib.""" + import zlib, struct + def chunk(typ, data): + return (struct.pack(">I", len(data)) + typ + data + + struct.pack(">I", zlib.crc32(typ + data) & 0xFFFFFFFF)) + raw = b"" + r, g, b = rgb + for _ in range(h): + raw += b"\x00" + bytes([r, g, b]) * w # filter byte 0 + one RGBA-less RGB row + sig = b"\x89PNG\r\n\x1a\n" + ihdr = struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0) # 8-bit, color type 2 (RGB) + idat = zlib.compress(raw) + with open(path, "wb") as f: + f.write(sig + chunk(b"IHDR", ihdr) + chunk(b"IDAT", idat) + chunk(b"IEND", b"")) + +def make_placeholder_gif(path, w=4, h=4): + """Write a tiny valid GIF87a (single color). Qt QMovie accepts it as a 1-frame anim.""" + r, g, b = 180, 120, 60 + # GIF header + header = b"GIF87a" + logical = bytes([w & 255, (w >> 8) & 255, h & 255, (h >> 8) & 255, + 0b10000000, 0, 0]) # global color table flag | size=1 (2 colors) + # global color table: 2 entries (BG + one color) + gct = bytes([0, 0, 0]) + bytes([r, g, b]) + # image descriptor + imgdesc = b"\x2c" + bytes([0, 0, 0, 0, w & 255, (w >> 8) & 255, h & 255, (h >> 8) & 255, 0]) + # image data: LZW min code size=2, then a sub-block encoding the whole image + # simplest: code size 2, one clear + pixels via codes. Use minimal stream that Qt accepts. + lzw_min = 2 + # Build a trivial LZW stream: clear(4), then emit pixel index 1 codes, end(5). + # For a 4x4=16 pixel image with 2-color table, encode index 1 sixteen times. + # bit packing (LSB first), code width starts at lzw_min+1=3. + codes = [4] # clear + codes += [1] * (w * h) + codes += [5] # end + width = 3 + bits = 0; nbits = 0; out = bytearray() + for c in codes: + bits |= (c << nbits); nbits += width + while nbits >= 8: + out.append(bits & 0xFF); bits >>= 8; nbits -= 8 + if nbits > 0: + out.append(bits & 0xFF) + imgdata = bytes([lzw_min]) + bytes([len(out)]) + bytes(out) + b"\x00" + trailer = b"\x3b" + with open(path, "wb") as f: + f.write(header + logical + gct + imgdesc + imgdata + trailer) + +def emit_resources(out_dir, target): + """For a target that needs resources, write placeholder asset files + a .qrc. + Returns the .qrc basename (or None if no resources).""" + res = TARGET_RESOURCES.get(target) + if not res: + return None + asset_dir = os.path.join(out_dir, "assets") + os.makedirs(os.path.join(REPO, asset_dir), exist_ok=True) + # Standard Qt .qrc: root ..., one (prefix defaults to "/"). + # The "alias" makes the file reachable at "://" — here prefix is "/" + # so ":/Image/boat.jpg" needs the alias to include the sub-path, i.e. alias="Image/boat.jpg". + qrc_lines = ["", " "] + for res_path, asset_name in res.items(): + # res_path like ":/Image/boat.jpg" → alias "Image/boat.jpg", file "assets/boat.jpg" + rel = res_path[1:] # strip leading ":" + prefix, fname = os.path.split(rel) # prefix="Image", fname="boat.jpg" + asset_path = os.path.join(asset_dir, asset_name) + if asset_name.endswith(".gif"): + make_placeholder_gif(os.path.join(REPO, asset_path)) + else: + make_placeholder_png(os.path.join(REPO, asset_path)) + alias = f"{prefix}/{fname}" if prefix else fname + qrc_lines.append(f' {os.path.join("assets", asset_name)}') + qrc_lines.append(" ") + qrc_lines.append("") + qrc_name = f"{target}.qrc" + write(os.path.join(REPO, out_dir, qrc_name), "\n".join(qrc_lines) + "\n") + return qrc_name + +def emit_datafile(out_dir, target): + """For a target needing a runtime data file, drop a sample so first-run works.""" + fname = TARGET_DATAFILES.get(target) + if not fname: + return + content = {"in.txt": "Hello from in.txt\n", "file.txt": "sample\n", "file.dat": ""} + data_dir = os.path.join(out_dir, "rundata") + os.makedirs(os.path.join(REPO, data_dir), exist_ok=True) + write(os.path.join(REPO, data_dir, fname), content.get(fname, "")) + +def add_moc_include_if_needed(abs_src): + """For a .cpp that defines a Q_OBJECT class inline (not in a header), CMake's + AUTOMOC requires `#include ".moc"` at the bottom of the .cpp so the moc + output for that class gets compiled in. Without it, AUTOMOC errors out: + 'contains a Q_OBJECT macro, but does not include ".moc"!'.""" + with open(abs_src, "r", encoding="utf-8") as f: + content = f.read() + if "Q_OBJECT" not in content: + return + stem = os.path.splitext(os.path.basename(abs_src))[0] + moc_inc = f'#include "{stem}.moc"' + if moc_inc in content: + return + # insert just before the final closing brace region is fragile; append at EOF + # is the standard idiom (moc include is processed regardless of position). + content = content.rstrip() + "\n\n" + moc_inc + "\n" + with open(abs_src, "w", encoding="utf-8") as f: + f.write(content) + +# ============================================================================== +# 主流程 +# ============================================================================== +def main(): + global PAGES_META_PID + qt_pages = [pid for pid, cfg in PAGE_CONFIG.items() if cfg["part"] == "p03"] + targets_by_dir = {} # dir -> list of (target_name, src_rel, needs_qrc, qrc_name) + snippet_by_dir = {} # dir -> list of (idx, sec_title, code) for README + + for pid in qt_pages: + cfg = PAGE_CONFIG[pid] + PAGES_META_PID = pid + body = json.load(open(os.path.join(WIKI, "bodies", f"{pid}.json"))) \ + .get("body", {}).get("storage", {}).get("value", "") + blocks = EXTRACTED.get(pid, []) + sections = page_outline(body) + out_dir = os.path.join("p03", cfg["dir"]) + + # block idx -> normalized section title + block_sec = {} + for sec in sections: + for i in sec["code_indices"]: + if i < len(blocks): + block_sec[i] = norm_title(sec["title"]) + + used_keys = set() + for (tpid, tidx), treat in BLOCK_TREATMENT.items(): + if tpid != pid: + continue + kind = treat[0] + if kind == "snippet": + if isinstance(tidx, int): + code = blocks[tidx]["code"] + snippet_by_dir.setdefault(out_dir, []).append( + (tidx, block_sec.get(tidx, "(导言)"), code)) + continue + stem = treat[1] + target_name = stem # ASCII; file_name_map 已保证英文蛇形 + if kind == "program": + src = emit_program(pid, tidx, stem, out_dir, treat[2]) + elif kind == "harness": + src = emit_harness(pid, tidx, stem, out_dir, treat[2]) + elif kind == "rewrite": + src = REWRITES[treat[2]](pid, tidx, out_dir, stem) + else: + continue + qrc_name = emit_resources(out_dir, target_name) + emit_datafile(out_dir, target_name) + # Q_OBJECT classes defined inline in the .cpp need a trailing moc include. + add_moc_include_if_needed(os.path.join(REPO, src)) + targets_by_dir.setdefault(out_dir, []).append( + (target_name, src, bool(qrc_name), qrc_name)) + + write_part3_cmakelists(targets_by_dir) + write_part3_readmes(targets_by_dir, snippet_by_dir) + write_errata() + n = sum(len(v) for v in targets_by_dir.values()) + print(f"gen_part3: wrote {n} targets across {len(targets_by_dir)} dirs.") + +def write_part3_cmakelists(targets_by_dir): + # per-dir CMakeLists + for d, targets in targets_by_dir.items(): + lines = ["# Auto-generated by tools/gen_part3.py — do not edit by hand.\n", + f"# 章节:{d}\n", + "set(CMAKE_AUTOMOC ON)\n", + "set(CMAKE_AUTORCC ON)\n"] + for tgt, src_rel, has_qrc, qrc_name in targets: + src = os.path.basename(src_rel) + if has_qrc: + lines.append(f"qt5_add_resources(RES {qrc_name})\n") + lines.append(f"add_executable({tgt} {src} ${{RES}})\n") + else: + lines.append(f"add_executable({tgt} {src})\n") + lines.append(f"set_target_properties({tgt} PROPERTIES " + f"CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF " + f"AUTOMOC ON)\n") + # Link Qt modules so their include dirs / libs are available to every + # example. (Filesystem examples only need Core, but QApplication/QWidget + # based ones need Widgets+Gui; linking all three keeps targets uniform and + # avoids per-target include-path gaps.) + lines.append(f"target_link_libraries({tgt} PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets)\n") + lines.append(f"target_compile_options({tgt} PRIVATE -Wall -Wextra)\n") + dir_tname = "all_" + d.replace("/", "_").replace("-", "_") + lines.append(f"add_custom_target({dir_tname} DEPENDS {' '.join(t[0] for t in targets)})\n") + write(os.path.join(REPO, d, "CMakeLists.txt"), "".join(lines)) + + # top-level p03/CMakeLists including all sub-dirs + lines = ["# Auto-generated by tools/gen_part3.py\n"] + for d in sorted(targets_by_dir.keys()): + rel = os.path.relpath(d, "p03") + lines.append(f"add_subdirectory({rel})\n") + write(os.path.join(REPO, "p03", "CMakeLists.txt"), "".join(lines)) + +def write_part3_readmes(targets_by_dir, snippet_by_dir): + """Each chapter dir gets a README listing its runnable targets and the + documented snippets (signatures/pseudo-code folded in as reference).""" + all_dirs = sorted(set(list(targets_by_dir.keys()) + list(snippet_by_dir.keys()))) + for d in all_dirs: + lines = [f"# {d} — Qt 示例\n\n"] + tgts = targets_by_dir.get(d, []) + if tgts: + lines.append("## 可运行示例\n\n") + lines.append("| 目标 | 源文件 | 说明 |\n") + lines.append("|---|---|---|\n") + for tgt, src_rel, has_qrc, qrc in tgts: + lines.append(f"| `{tgt}` | `{os.path.basename(src_rel)}` | " + f"构建运行见顶层 README;离屏验证 `QT_QPA_PLATFORM=offscreen tools/run_qt.sh {tgt}` |\n") + lines.append("\n") + snips = snippet_by_dir.get(d, []) + if snips: + lines.append("## 参考片段(签名/伪代码,未单独建目标)\n\n") + for (idx, title, code) in snips: + lines.append(f"### [{PAGES_META_PID if False else ''}block {idx}] {title}\n\n") + lines.append("```cpp\n") + lines.append(code.rstrip() + "\n") + lines.append("```\n\n") + write(os.path.join(REPO, d, "README.md"), "".join(lines)) + +def write_errata(): + lines = ["# ERRATA — 代码修正记录(Part3 Qt)\n\n", + "本文件由 `tools/gen_part3.py` 自动生成,记录对 wiki 原始 Qt 代码块的每一处修正。\n", + "格式:`[pageId:blockIdx]` 类型 → 原内容 → 修正后 → 依据。\n\n"] + if not ERRATA: + lines.append("(无修正)\n") + else: + for (pid, idx, kind, before, after, reason) in ERRATA: + lines.append(f"## [{pid}:{idx}] {kind}\n\n") + lines.append(f"- **原文**:`{before}`\n") + lines.append(f"- **修正**:`{after}`\n") + lines.append(f"- **依据**:{reason}\n\n") + write(os.path.join(REPO, "docs", "ERRATA_part3.md"), "".join(lines)) + +if __name__ == "__main__": + main() diff --git a/tools/gen_sources.py b/tools/gen_sources.py new file mode 100644 index 0000000..2c9e9c3 --- /dev/null +++ b/tools/gen_sources.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 +# ============================================================================== +# gen_sources.py — 把抓取的 wiki 数据生成为课程源码 + CMakeLists(可复现) +# +# 输入 : tools/wiki_data/{extracted_code.json, all_page_ids.json, +# qt_full_tree.json, bodies/*.json} +# 输出 : part1_cpp/**, part3_qt_desktop/** 的 .c/.cpp/.h/.qrc 与 CMakeLists.txt +# +# 设计要点(与计划一致): +# * 按页面标题层级(h1..h6)解析子话题;同一子话题内的代码块(多为 test() +# 片段)合并为「一个完整小程序」,把各片段按教学顺序嵌入,并标注出处。 +# * C/C++ 按内容上下文判定:用 gcc -std=c17 或 g++ -std=c++17 构建。 +# * 每个完整小程序 = 一个独立 add_executable 目标,可单独构建运行。 +# +# 本文件是「骨架/分类/合并」的通用引擎;针对每个页面需要的人工修正 +# (笔误、system("pause")、缺 include、伪代码等)通过 OVERRIDES 字典注入, +# 并自动记录到 docs/ERRATA.md,使生成过程透明可审计。 +# ------------------------------------------------------------------------------ +import json, os, re, glob, sys +from html.parser import HTMLParser + +REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +WIKI = os.path.join(REPO, "tools", "wiki_data") + +# ------------------------------------------------------------------ 标题解析 ---- +class OutlineParser(HTMLParser): + """顺序产出 ('h', level, text) 与 ('code', idx) 事件,定位每个代码块所属标题。""" + def __init__(self): + super().__init__() + self.events = [] + self.code_depth = 0 + self.code_idx = 0 + self.in_code_body = False + self.capture_h = False + self.cur_lvl = None + self.cur_buf = [] + def handle_starttag(self, tag, attrs): + ad = dict(attrs) + if tag in ("h1","h2","h3","h4","h5","h6"): + self.capture_h = True; self.cur_lvl = int(tag[1]); self.cur_buf = [] + elif tag in ("ac:structured-macro","structured-macro") and ( + ad.get("ac:name") == "code" or ad.get("name") == "code"): + self.code_depth += 1 + def handle_endtag(self, tag): + if tag in ("h1","h2","h3","h4","h5","h6") and self.capture_h: + txt = re.sub(r"\s+", " ", "".join(self.cur_buf)).strip() + self.events.append(("h", self.cur_lvl, txt)) + self.capture_h = False + elif tag in ("ac:structured-macro","structured-macro") and self.code_depth >= 1: + if self.code_depth == 1: + self.events.append(("code", self.code_idx)) + self.code_idx += 1 + self.code_depth -= 1 + def unknown_decl(self, decl): + pass # CDATA handled by the code extractor separately + def handle_data(self, data): + if self.capture_h: + self.cur_buf.append(data) + def handle_entityref(self, name): + if self.capture_h: + self.cur_buf.append({"nbsp": " "}.get(name, f"&{name};")) + +def page_outline(body): + """Return list of {level, title, code_indices} for the deepest headings that + own at least one code block, in document order. Each code block is attached + to the heading under which it physically appears (last heading seen above it + at any level). Headings with no code blocks are dropped from this list but + their text is folded into the preceding owning heading's context.""" + if not body: + return [] + p = OutlineParser() + p.feed(body) + owning = [] # stack of (level, title) currently open + sections = [] # list of dict + cur_section = None + for ev in p.events: + if ev[0] == "h": + _, lvl, txt = ev + # pop closed headings + while owning and owning[-1][0] >= lvl: + owning.pop() + owning.append((lvl, txt)) + cur_section = None # a new heading starts a new (potential) section + else: # code + if cur_section is None: + # build a context title from the heading path + path_titles = [t for (_, t) in owning] + title = path_titles[-1] if path_titles else "(导言)" + cur_section = {"title": title, "path": path_titles, "code_indices": []} + sections.append(cur_section) + cur_section["code_indices"].append(ev[1]) + return sections + +# ------------------------------------------------------------------ 语言判定 ---- +CPP_TOKENS = ["iostream", "std::", "#include ", "#include", + "class ", "template", "namespace", "using namespace", "new ", + "delete ", "std::string", "cout", "cin", "cerr", "vector<", + "string ", "->", "const_cast", "static_cast", "dynamic_cast", + "reinterpret_cast", "operator ", "public:", "private:", "protected:", + "signals:", "slots:", "virtual ", "throw ", "catch ", "try {", + "Q_OBJECT", "QApplication", "QWidget", "emit ", "qDebug", "QFile", + "#include ", "#include", "#include ", + "#include ", "#include ", "strcpy", "strcat", + "strcmp", "sprintf", "EXIT_SUCCESS"] + +def classify_lang(code): + """Return 'c' | 'cpp' | 'qt' based on content.""" + is_qt = any(t in code for t in + ["Q_OBJECT","QApplication","QWidget","QMainWindow","QDialog","QPushButton", + "QLabel","#include / 提供)。wiki 的 C++ 片段常直接用 strcpy/ +# strlen 等,却未 include 对应头文件(原意依赖教学环境的传递包含)。检测到即补头。 +CSTRING_RE = re.compile( + r"\b(strcpy|strncpy|strcat|strncat|strlen|strcmp|strncmp|strchr|strrchr|" + r"strstr|strtok|strerror|strcoll|memcpy|memmove|memset|memcmp|memchr|" + r"sprintf|snprintf)\s*\(") + +def needs_cstring(code): + """True if the fragment calls any function and so needs the header.""" + return bool(CSTRING_RE.search(code)) + +def has_pause_call(code): + """True if the fragment calls pause() (after common_fix converted system("pause")).""" + return bool(re.search(r"\bpause\s*\(", code)) + +# Map of (header) → list of tokens that, when used unqualified (relying on +# `using namespace std;`), require that header. wiki STL/IO snippets routinely +# omit these includes (they rely on the teaching toolchain's transitive pulls). +# Detection is on bare symbol usage; includes are injected by the emit template. +_STL_HEADER_TOKENS = { + "": [r"\bvector\b"], + "": [r"\blist\b"], + "": [r"\bdeque\b"], + "": [r"\bset\s*<", r"\bmultiset\s*<"], + "": [r"\bmap\s*<", r"\bmultimap\s*<"], + "": [r"\bunordered_map\s*<"], + "": [r"\bunordered_set\s*<"], + "": [r"\bstack\s*<"], + "": [r"\bqueue\s*<", r"\bpriority_queue\s*<"], + "":[r"\bfor_each\b", r"\btransform\b", r"\bfind\b", r"\bfind_if\b", + r"\bsort\b", r"\bcopy\b", r"\bremove\b", r"\bcount\b", + r"\bcount_if\b", r"\baccumulate\b", r"\bfill\b", r"\breplace\b", + r"\bswap\b", r"\breverse\b", r"\bmerge\b", r"\bunique\b", + r"\bbind\b"], + "":[r"\bfunction\s*<", r"\bplus\b", r"\bminus\b", r"\bmultiplies\b", + r"\bnegate\b", r"\bequal_to\b", r"\bless\b", r"\bgreater\b", + r"\blogical_and\b", r"\bplaceholders::_"], + "": [r"\baccumulate\b"], + "": [r"\bifstream\b", r"\bofstream\b", r"\bfstream\b"], + "": [r"\bsetw\b", r"\bsetfill\b", r"\bsetiosflags\b", r"\bresetiosflags\b", + r"\bsetbase\b", r"\bsetprecision\b"], + "": [r"\bstd::string\b", r"\bgetline\b"], + "": [r"\bsqrt\b", r"\bpow\b", r"\bsin\b", r"\bcos\b", r"\babs\b", + r"\bfabs\b", r"\bfloor\b", r"\bceil\b", r"\blog\b", r"\bexp\b"], +} + +def infer_stl_includes(code): + """Return the sorted list of C++ standard-library headers the fragment needs + but the wiki omitted (detected by symbol usage). Returns [] for C code.""" + needed = [] + for hdr, pats in _STL_HEADER_TOKENS.items(): + if any(re.search(p, code) for p in pats): + needed.append(hdr) + # de-dup & sort for stable output + return sorted(set(needed)) + +# ------------------------------------------------------------------ 目标命名 ---- +def slug(s): + """Make a filesystem-friendly slug from a heading (keeps CJK for readability + of source filenames).""" + s = re.sub(r"^\d+(\.\d+)*\s*", "", s) # drop leading "3.10.2 " + s = re.sub(r"[((].*?[))]", "", s) # drop parenthetical + s = re.sub(r"[^\w\u4e00-\u9fff]+", "_", s).strip("_") + s = re.sub(r"_+", "_", s) + return s[:40] or "topic" + +def ascii_slug(s): + """ASCII-only slug for CMake target names (CMake forbids non-ASCII targets). + CJK chars are dropped; the result is guaranteed [A-Za-z0-9_].""" + s = re.sub(r"^\d+(\.\d+)*\s*", "", s) + s = re.sub(r"[((].*?[))]", "", s) + # drop anything that's not ASCII word char + s = re.sub(r"[^A-Za-z0-9_]+", "_", s).strip("_") + s = re.sub(r"_+", "_", s) + return s.lower()[:30] or "topic" + +# ------------------------------------------------------------------ 输出助手 ---- +def write(path, content): + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8") as f: + f.write(content) + +ERRATA = [] # list of (pageId, blockIdx, kind, before, after, reason) + +def record_erratum(pid, idx, kind, before, after, reason): + ERRATA.append((pid, idx, kind, before, after, reason)) + +# ============================================================================== +# 公共修正:对「所有」part1 代码块都适用的轻量修补(保留原意、可移植) +# ============================================================================== +def common_fix(code, pid, idx): + orig = code + # 0) Normalize non-breaking spaces (U+00A0 NBSP) that the wiki editor injected + # into indented code — these are 'stray \302' compile errors otherwise. + if "\u00a0" in code: + code = code.replace("\u00a0", " ") + # 1) system("pause") → 便携 pause() 壳(仅当出现时;壳函数由模板注入) + if has_windows_pause(code): + code = re.sub(r'system\(\s*"pause"\s*\)', 'pause()', code) + code = re.sub(r'system\(\s*" pause"\s*\)', 'pause()', code) + record_erratum(pid, idx, "platform", + 'system("pause")', 'pause()', + "system(\"pause\") 仅 Windows 可用;改用仓库提供的便携 pause() 壳函数 " + "(读一行 stdin 等待回车),行为一致,Linux 可编译运行。") + # 2) 全角分号 → 半角 + if ";" in code: + code = code.replace(";", ";") + record_erratum(pid, idx, "typo", ";(全角分号)", ";(半角)", + "全角分号在 C/C++ 中非法,统一替换为半角。") + # 3) 全角引号 → 半角(教学代码常见误输) + for fq, hq in [("“", '"'), ("”", '"'), ("‘", "'"), ("’", "'")]: + if fq in code: + code = code.replace(fq, hq) + record_erratum(pid, idx, "typo", f"{fq}(全角引号)", f"{hq}(半角)", + "字符串/字符字面量须用半角引号。") + # 4) C 字符串/内存函数缺头:wiki 的 C++ 片段常直接调用 strcpy/strlen/strcmp 等 + # 却未 include(依赖教学环境的传递包含)。检测到即补 (C++)。 + # include 由 emit 模板注入;此处只记录 erratum,避免与模板重复插入。 + if needs_cstring(code): + record_erratum(pid, idx, "missing-include", + "调用 strcpy/strlen/strcmp 等 C 字符串函数但未 #include ", + "由生成模板统一补 #include ", + "C 字符串/内存函数声明在 (C++)/ (C);" + "原 wiki 代码依赖教学环境传递包含,隔离编译时须显式 include。") + # 5) STL 容器/算法/IO 头缺失:wiki 片段依赖 `using namespace std;` 且假定教学 + # 工具链传递包含了 /// 等。隔离编译时这些 + # 符号未声明。检测到的头由 emit 模板注入;此处只记录 erratum。 + missing = infer_stl_includes(code) + if missing: + record_erratum(pid, idx, "missing-include", + f"使用 STL/IO 符号(如 vector/for_each/ifstream/setw 等)但未 #include {', '.join(missing)}", + f"由生成模板统一补 #include {' '.join(missing)}", + "原 wiki 代码假定教学环境已传递包含相应标准库头;按「示例相对隔离」独立编译" + "时须显式 include。检测依据为符号使用(见 gen_sources.infer_stl_includes)。") + return code + +# ---------------------------------------------------------------- 页面配置表 ---- +# 每个页面的目录映射、C/C++ 判定覆盖、特殊修正。键=pageId。 +# 目录采用「深缩写」:part 缩写 pNN(顶层不编号)/ 章缩写 chNN / 节缩写 sNN。 +# 例:part1_cpp/ch04_class_object/03_ctor_dtor → p01/ch04/s03 +# 例:part3_qt_desktop/ch04_signal_slot → p03/ch04 +# 单章多节的页:dir 含 sNN 子目录;单章无子节的页:dir 仅到 chNN。 +PAGE_CONFIG = { + # ---- Part 1: C++ 强化 ---- (p01) + "58954438": {"part": "p01", "dir": "ch02", "prefix": "p1c02"}, + "58954439": {"part": "p01", "dir": "ch03", "prefix": "p1c03"}, + "58954448": {"part": "p01", "dir": "ch04/s01", "prefix": "p1c04_01"}, + "58954449": {"part": "p01", "dir": "ch04/s02", "prefix": "p1c04_02"}, + "58954451": {"part": "p01", "dir": "ch04/s03", "prefix": "p1c04_03"}, + "58954456": {"part": "p01", "dir": "ch04/s04", "prefix": "p1c04_04"}, + "58954457": {"part": "p01", "dir": "ch04/s05", "prefix": "p1c04_05"}, + "58954458": {"part": "p01", "dir": "ch04/s06", "prefix": "p1c04_06"}, + "58954461": {"part": "p01", "dir": "ch04/s07", "prefix": "p1c04_07"}, + "58954469": {"part": "p01", "dir": "ch04/s08", "prefix": "p1c04_08"}, + "58954472": {"part": "p01", "dir": "ch05", "prefix": "p1c05"}, + "58954473": {"part": "p01", "dir": "ch06", "prefix": "p1c06"}, + "58954474": {"part": "p01", "dir": "ch07", "prefix": "p1c07"}, + "58954476": {"part": "p01", "dir": "ch08", "prefix": "p1c08"}, + "58954487": {"part": "p01", "dir": "ch09/s02", "prefix": "p1c09_02"}, + "58954488": {"part": "p01", "dir": "ch09/s03", "prefix": "p1c09_03"}, + "58954498": {"part": "p01", "dir": "ch09/s04", "prefix": "p1c09_04"}, + # ---- Part 3: Qt 桌面 ---- (p03) + "58954515": {"part": "p03", "dir": "ch02", "prefix": "p3c02"}, + "58954525": {"part": "p03", "dir": "ch03", "prefix": "p3c03"}, + "58954527": {"part": "p03", "dir": "ch04", "prefix": "p3c04"}, + "58954529": {"part": "p03", "dir": "ch05", "prefix": "p3c05"}, + "58954535": {"part": "p03", "dir": "ch06", "prefix": "p3c06"}, + "58954540": {"part": "p03", "dir": "ch08", "prefix": "p3c08"}, + "58954546": {"part": "p03", "dir": "ch09", "prefix": "p3c09"}, + "58954548": {"part": "p03", "dir": "ch10", "prefix": "p3c10"}, + "58954552": {"part": "p03", "dir": "ch11", "prefix": "p3c11"}, +} + +# Per-block language overrides (after manual review of ambiguous cases). +LANG_OVERRIDE = { + ("58954439", 13): "c", # tentative def + printf + EXIT_SUCCESS + ("58954439", 14): "c", # K&R implicit-int (demo of removed C89 feature) + ("58954439", 15): "c", # enum=int + malloc w/o cast (C-only) + ("58954439", 18): "c", # ternary rvalue (C side) + ("58954439", 21): "c", # const modified via cast pointer + ("58954439", 44): "c", # extern "C" guarded header (valid C) + ("58954439", 45): "c", # C impl file + ("58954448", 0): "c", # C structs + strcpy + Person* to Animal* +} + +if __name__ == "__main__": + print("gen_sources: this is the engine module; run gen_part1.py / gen_part3.py.") diff --git a/tools/install_qt_host.sh b/tools/install_qt_host.sh new file mode 100755 index 0000000..9e06de1 --- /dev/null +++ b/tools/install_qt_host.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Install the ISOLATED Qt 5.14.2 toolchain into .qt514/ DIRECTLY ON THE HOST +# (no Docker), using a local venv for aqtinstall. The host already has the right +# toolchain (gcc 13.3, cmake 3.28); this just fetches Qt files into the repo. +# +# Usage: tools/install_qt_host.sh +# Prefer this over docker_install_qt.sh when you don't need a container. +# Verified: a binary built against .qt514/ links ONLY to .qt514/ (ldd shows 0 +# system-Qt refs) and runs on the host with tools/run_qt.sh. +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VENV="$REPO_ROOT/.venv-aqt" +QT_VERSION="5.14.2" + +if [ ! -x "$VENV/bin/aqt" ]; then + echo ">> Creating local venv with aqtinstall at $VENV ..." + python3 -m venv "$VENV" --system-site-packages + "$VENV/bin/pip" install --quiet aqtinstall +fi + +echo ">> Installing Qt ${QT_VERSION} gcc_64 into $REPO_ROOT/.qt514/ ..." +"$VENV/bin/aqt" install-qt linux desktop "${QT_VERSION}" gcc_64 -O "$REPO_ROOT/.qt514" + +QT_ROOT="$REPO_ROOT/.qt514/${QT_VERSION}/gcc_64" +echo ">> Verify:" +"$QT_ROOT/bin/qmake" -query QT_VERSION +echo ">> Qt installed at: $QT_ROOT" +echo ">> Build on host: cmake -S . -B build -G Ninja -DBUILD_QT_PART=ON -DCMAKE_PREFIX_PATH='$QT_ROOT' && cmake --build build" +echo ">> Run Qt targets: tools/run_qt.sh " diff --git a/tools/run.sh b/tools/run.sh new file mode 100755 index 0000000..af88177 --- /dev/null +++ b/tools/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Run a compiled part1 (C/C++) target on the HOST. +# Usage: tools/run.sh # run ./build/path/ +# tools/run.sh [args...] # pass args +# Looks up the target binary under build/ automatically. +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +target="${1:?usage: tools/run.sh [args...]}" +shift || true +bin=$(find "$REPO_ROOT/build" -type f -name "$target" -executable 2>/dev/null | head -1) || true +if [ -z "$bin" ]; then + echo "target '$target' not found under $REPO_ROOT/build. Build first." >&2 + exit 1 +fi +exec "$bin" "$@" diff --git a/tools/run_qt.sh b/tools/run_qt.sh new file mode 100755 index 0000000..687c369 --- /dev/null +++ b/tools/run_qt.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Run a compiled Qt target on the HOST against the ISOLATED Qt 5.14.2 in .qt514/. +# Usage: tools/run_qt.sh [args...] +# Headless/scriptable: QT_QPA_PLATFORM=offscreen tools/run_qt.sh +# Isolation is by LD_LIBRARY_PATH + Qt plugin path, NOT a container. Verified by +# ldd: linked Qt libs resolve only to .qt514/, zero refs to system /usr/lib Qt5. +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +target="${1:?usage: tools/run_qt.sh [args...]}" +shift || true +QT_ROOT="$REPO_ROOT/.qt514/5.14.2/gcc_64" +bin=$(find "$REPO_ROOT/build" -type f -name "$target" -executable 2>/dev/null | head -1) || true +if [ -z "$bin" ]; then + echo "Qt target '$target' not found under $REPO_ROOT/build. Build first." >&2 + exit 1 +fi +export LD_LIBRARY_PATH="$QT_ROOT/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +export QT_PLUGIN_PATH="$QT_ROOT/plugins" +export QT_QPA_PLATFORM_PLUGIN_PATH="$QT_ROOT/plugins/platforms" +exec "$bin" "$@" diff --git a/tools/std_probe.py b/tools/std_probe.py new file mode 100644 index 0000000..f47d0af --- /dev/null +++ b/tools/std_probe.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# ============================================================================== +# std_probe.py — 快速演示「同一段代码在不同语言标准下的支持情况」 +# +# 用途(对应任务 1):把一段 C 或 C++ 代码片段,在多个 -std= 下逐个编译,报告 +# pass / fail / warnings,从而直观看到「某概念在哪个版本成立」。 +# +# 用法: +# tools/std_probe.py --lang c --code 'int main(){ __auto_type x=1; return x; }' +# tools/std_probe.py --lang c++ --file snippet.cpp +# tools/std_probe.py --lang c --file snippet.c --standards c89,c99,c11,c17,c23 +# +# 说明: +# * 编译器用宿主机 gcc/g++(本仓库 gcc 13 原生支持全部目标标准)。 +# * 仅做语法编译(-fsyntax-only),不链接、不运行,避免副作用。 +# * 退出码:全 pass 为 0;任一 fail 为 1(便于脚本/CI 判定)。 +# ------------------------------------------------------------------------------ +import argparse, os, subprocess, sys, tempfile + +# 默认要探测的标准列表(C 与 C++ 各一组,覆盖课程相关版本) +DEFAULTS = { + "c": ["c89", "c99", "c11", "c17", "c23"], + "c++": ["c++98", "c++03", "c++11", "c++14", "c++17", "c++20", "c++23"], +} + +def probe(code: str, lang: str, standards, extra_flags=None): + """Compile `code` under each standard in `standards` (syntax-only). Returns + list of dicts: {std, ok, warnings, errors}.""" + compiler = "gcc" if lang == "c" else "g++" + results = [] + extra_flags = extra_flags or [] + with tempfile.NamedTemporaryFile("w", suffix=(".c" if lang == "c" else ".cpp"), + delete=False) as f: + f.write(code) + src = f.name + try: + for std in standards: + cmd = [compiler, f"-std={std}", "-fsyntax-only", "-Wall", "-Wextra"] + extra_flags + [src] + p = subprocess.run(cmd, capture_output=True, text=True) + combined = (p.stderr or "") + (p.stdout or "") + # categorize lines: gcc/clang warnings vs errors + warns = [l for l in combined.splitlines() if "warning:" in l] + errs = [l for l in combined.splitlines() if "error:" in l] + results.append({ + "std": std, + "ok": p.returncode == 0, + "warnings": warns, + "errors": errs, + }) + finally: + os.unlink(src) + return results + +def main(): + ap = argparse.ArgumentParser(description="Probe a C/C++ snippet across -std= versions.") + g = ap.add_mutually_exclusive_group(required=True) + g.add_argument("--code", help="inline code snippet") + g.add_argument("--file", help="path to a source file") + ap.add_argument("--lang", choices=["c", "c++"], required=True, + help="language (selects compiler & default standards)") + ap.add_argument("--standards", default=None, + help="comma-separated -std values (default: course-relevant set)") + ap.add_argument("--show-warnings", action="store_true", + help="print warning lines (default: only summarize)") + ap.add_argument("--pedantic-errors", action="store_true", + help="add -pedantic-errors so removed/deprecated features FAIL hard " + "(gcc otherwise lets many removed features compile as warnings)") + args = ap.parse_args() + + code = args.code if args.code else open(args.file).read() + standards = args.standards.split(",") if args.standards else DEFAULTS[args.lang] + extra_flags = ["-pedantic-errors"] if args.pedantic_errors else [] + + print(f"Compiler : {('gcc' if args.lang=='c' else 'g++')} ({subprocess.run(['gcc','--version'],capture_output=True,text=True).stdout.splitlines()[0]})") + print(f"Language : {args.lang}") + print(f"Standards: {', '.join(standards)}" + + (" [pedantic-errors ON]" if args.pedantic_errors else "")) + print("-" * 64) + + results = probe(code, args.lang, standards, extra_flags=extra_flags) + any_fail = False + for r in results: + flag = "PASS" if r["ok"] else "FAIL" + w = f" {len(r['warnings'])} warning(s)" if r["warnings"] else "" + e = f" {len(r['errors'])} error(s)" if r["errors"] else "" + print(f" -std={r['std']:<8} : {flag}{w}{e}") + if not r["ok"]: + any_fail = True + for line in r["errors"][:3]: + print(f" {line.strip()}") + if args.show_warnings: + for line in r["warnings"][:3]: + print(f" (warn) {line.strip()}") + print("-" * 64) + print("Result : " + ("ALL PASS" if not any_fail else "SOME FAIL")) + return 1 if any_fail else 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/wiki_data/all_page_ids.json b/tools/wiki_data/all_page_ids.json new file mode 100644 index 0000000..af62b57 --- /dev/null +++ b/tools/wiki_data/all_page_ids.json @@ -0,0 +1,291 @@ +[ + { + "id": "58954436", + "title": "01.C++技术强化及提升", + "path": [ + "01.C++技术强化及提升" + ] + }, + { + "id": "58954437", + "title": "01.C++概述", + "path": [ + "01.C++技术强化及提升", + "01.C++概述" + ] + }, + { + "id": "58954438", + "title": "02.C++初识", + "path": [ + "01.C++技术强化及提升", + "02.C++初识" + ] + }, + { + "id": "58954439", + "title": "03.C++对C的扩展", + "path": [ + "01.C++技术强化及提升", + "03.C++对C的扩展" + ] + }, + { + "id": "58954440", + "title": "04.类和对象", + "path": [ + "01.C++技术强化及提升", + "04.类和对象" + ] + }, + { + "id": "58954448", + "title": "01.类和对象的基本概念", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "01.类和对象的基本概念" + ] + }, + { + "id": "58954449", + "title": "02.面向对象程序设计案例", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "02.面向对象程序设计案例" + ] + }, + { + "id": "58954451", + "title": "03.对象的构造和析构", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "03.对象的构造和析构" + ] + }, + { + "id": "58954456", + "title": "04.C++面向对象模型初探", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "04.C++面向对象模型初探" + ] + }, + { + "id": "58954457", + "title": "05.友元", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "05.友元" + ] + }, + { + "id": "58954458", + "title": "06.运算符重载", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "06.运算符重载" + ] + }, + { + "id": "58954461", + "title": "07.继承和派生", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "07.继承和派生" + ] + }, + { + "id": "58954469", + "title": "08.多态", + "path": [ + "01.C++技术强化及提升", + "04.类和对象", + "08.多态" + ] + }, + { + "id": "58954472", + "title": "05.C++模板", + "path": [ + "01.C++技术强化及提升", + "05.C++模板" + ] + }, + { + "id": "58954473", + "title": "06.C++类型转换", + "path": [ + "01.C++技术强化及提升", + "06.C++类型转换" + ] + }, + { + "id": "58954474", + "title": "07.C++异常", + "path": [ + "01.C++技术强化及提升", + "07.C++异常" + ] + }, + { + "id": "58954476", + "title": "08.C++输入输出流", + "path": [ + "01.C++技术强化及提升", + "08.C++输入输出流" + ] + }, + { + "id": "58954485", + "title": "09.STL", + "path": [ + "01.C++技术强化及提升", + "09.STL" + ] + }, + { + "id": "58954486", + "title": "01.STL概论", + "path": [ + "01.C++技术强化及提升", + "09.STL", + "01.STL概论" + ] + }, + { + "id": "58954487", + "title": "02.STL三大组件", + "path": [ + "01.C++技术强化及提升", + "09.STL", + "02.STL三大组件" + ] + }, + { + "id": "58954488", + "title": "03.常用容器", + "path": [ + "01.C++技术强化及提升", + "09.STL", + "03.常用容器" + ] + }, + { + "id": "58954498", + "title": "04.常用算法", + "path": [ + "01.C++技术强化及提升", + "09.STL", + "04.常用算法" + ] + }, + { + "id": "58954500", + "title": "02.QT安装教程(免注册和登陆)", + "path": [ + "02.QT安装教程(免注册和登陆)" + ] + }, + { + "id": "58954512", + "title": "03.Qt桌面应用开发", + "path": [ + "03.Qt桌面应用开发" + ] + }, + { + "id": "58954513", + "title": "1 Qt概述", + "path": [ + "03.Qt桌面应用开发", + "1 Qt概述" + ] + }, + { + "id": "58954515", + "title": "2 创建Qt项目", + "path": [ + "03.Qt桌面应用开发", + "2 创建Qt项目" + ] + }, + { + "id": "58954525", + "title": "3 第一个Qt小程序", + "path": [ + "03.Qt桌面应用开发", + "3 第一个Qt小程序" + ] + }, + { + "id": "58954527", + "title": "4 信号和槽机制", + "path": [ + "03.Qt桌面应用开发", + "4 信号和槽机制" + ] + }, + { + "id": "58954529", + "title": "5 QMainWindow", + "path": [ + "03.Qt桌面应用开发", + "5 QMainWindow" + ] + }, + { + "id": "58954535", + "title": "6 对话框QDialog", + "path": [ + "03.Qt桌面应用开发", + "6 对话框QDialog" + ] + }, + { + "id": "58954536", + "title": "7 布局管理器", + "path": [ + "03.Qt桌面应用开发", + "7 布局管理器" + ] + }, + { + "id": "58954540", + "title": "8 常用控件", + "path": [ + "03.Qt桌面应用开发", + "8 常用控件" + ] + }, + { + "id": "58954546", + "title": "9 Qt消息机制和事件", + "path": [ + "03.Qt桌面应用开发", + "9 Qt消息机制和事件" + ] + }, + { + "id": "58954548", + "title": "10 绘图和绘图设备", + "path": [ + "03.Qt桌面应用开发", + "10 绘图和绘图设备" + ] + }, + { + "id": "58954552", + "title": "11 文件系统", + "path": [ + "03.Qt桌面应用开发", + "11 文件系统" + ] + } +] \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954436.json b/tools/wiki_data/bodies/58954436.json new file mode 100644 index 0000000..dce6865 --- /dev/null +++ b/tools/wiki_data/bodies/58954436.json @@ -0,0 +1 @@ +{"id":"58954436","type":"page","status":"current","title":"01.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/58954436/version/1"},"_expandable":{"content":"/rest/api/content/58954436"}},"body":{"storage":{"value":"","representation":"storage","_expandable":{"content":"/rest/api/content/58954436"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":0},"_links":{"webui":"/pages/viewpage.action?pageId=58954436","edit":"/pages/resumedraft.action?draftId=58954436","tinyui":"/x/xJKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954436"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954436/child","restrictions":"/rest/api/content/58954436/restriction/byOperation","history":"/rest/api/content/58954436/history","ancestors":"","descendants":"/rest/api/content/58954436/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954437.json b/tools/wiki_data/bodies/58954437.json new file mode 100644 index 0000000..002b673 --- /dev/null +++ b/tools/wiki_data/bodies/58954437.json @@ -0,0 +1 @@ +{"id":"58954437","type":"page","status":"current","title":"01.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/58954437/version/1"},"_expandable":{"content":"/rest/api/content/58954437"}},"body":{"storage":{"value":"

1.1 c++简介

c++”中的++来自于c语言中的递增运算符++,该运算符将变量加1c++起初也叫”c with clsss”.通过名称表明,c++是对C的扩展,因此c++c语言的超集,这意味着任何有效的c程序都是有效的c++程序。c++程序可以使用已有的c程序库。

库是编程模块的集合,可以在程序中调用它们。库对很多常见的编程问题提供了可靠的解决方法,因此可以节省程序员大量的时间和工作量。

c++语言在c语言的基础上添加了面向对象编程泛型编程的支持。c++继承了c语言高效,简洁,快速和可移植的传统。

c++融合了3种不同的编程方式:

  • c语言代表的过程性语言.
  • c++在c语言基础上添加的类代表的面向对象语言.
  • c++模板支持的泛型编程

c语言和c++语言的关系:

c++语言是在C语言的基础上,添加了面向对象、模板等现代程序设计语言的特性而发展起来的。两者无论是从语法规则上,还是从运算符的数量和使用上,都非常相似,所以我们常常将这两门语言统称为“C/C++”。

C语言和C++并不是对立的竞争关系:

  1. C++是C语言的加强,是一种更好的C语言。
  2. C++是以C语言为基础的,并且完全兼容C语言的特性。

c语言和C++语言的学习是可以相互促进。学好C语言,可以为我们将来进一步地学习C++语言打好基础,而C++语言的学习,也会促进我们对于C语言的理解,从而更好地运用C语言。

1.2 可移植性和标准

       假设为运行windows 2000的老式奔腾pc编写了一个很好用的c++程序,而管理员决定使用不同操作系统(比如说Mac OS Linux)和处理器的计算机替换它。该程序是否可在新平台运行呢?当然,但是必须使用为新平台设计的c++编译器重新编译。但是是否需要修改写好的代码?如果不需要修改代码的情况下,重新编译程序后,程序依然运行良好,该程序是可移植的。

       程序是否可移植性有两个问题需要解决。第一是硬件,针对特定硬件编程的程序是不可移植的。第二,语言的实现,windows xp c++ Redhat Linux Mac OS Xc++的实现不一定相同。虽然我们希望c++版本与其他版本兼容,但是如果没有一个公开的标准,很难做到。因此,美国国家标准局(American National Standards Institute,ANSI)1990年设立一个委员会专门负责制定c++标准(ANSI制定了c语言的标准)。国际标准化组织(International Organization for StandardizationISO)很快通过自己的委员会加入到这个行列,创建了联合组织ANSI/ISO,制定c++标准。

      经过多年的努力,制定出了一个国际标准ISO/IEC 14882:1998 ,并于1998年获得了ISOIEC(International  Electrotechnical Committee,国际电工技术委员会)ANSI的批准。这个标准就是我们经常所说的c++98。它不仅描述了已有的c++特性,还对语言进行了扩展,添加了异常、运行阶段类型识别(RTTI)、模板和标准模板库(STL).

      2003年,发布了c++标准第二版(IOS/IEC 14882:2003),这一版本对第一版修订了一些错误,但并没有改变语言特性,因此c++98表示c++98/c++2003.

      c++不断发展。IOS标准委员会于20118月批准了新标准ISO/IEC 14882:2011,该标准被称为c++11,c++98一样c++11也新增了许多特性。

      ISO c++标准还吸收了ANSI c语言标准,c++尽量做到是c的超集。意味着在理想情况下,任何有效的c程序都应该是有效的c++程序。

      ANSI不仅定义了c语言,还 定义了一个ANSI c必须实现的标准c库。c++也在使用这个库,另外ANSI/ISO c++标准还提供了一个c++标准类库。

1.3 为什么C++会成功

        c++最初的目的是将c语言转变为OOP(面向对象程序设计)语言,但是c++后来并没有这么做,而是立足于程序的实际。因为在c语言方面大量投入的程序员使其完全丢掉c语言那种编程的思考方式,转而去接受一种新的语言,新的思维,那么将会导致这些程序员中大部分人在短时间内可能毫无成果,使其生产率降低。但是如果让这些c程序员在已有知识的基础上,再去学习c++语言,理解运用OOP,那么也只是在其已有思维的基础上进行扩展而已,这样可以保持其更好的生产率。

        简而言之,强迫程序员放弃c语言和c语言的思考方式,而去转到OOP上是需要代价的,但是从c语言转到c++所花费的代价就会小很多。所以也可以理解为c++的出现并不是去替代c,而是对c的扩展,所以在c++中既可以使用c++新特性,并且可以使用c的过程式思维来编写程序。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954437"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954437","edit":"/pages/resumedraft.action?draftId=58954437","tinyui":"/x/xZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954437"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954437/child","restrictions":"/rest/api/content/58954437/restriction/byOperation","history":"/rest/api/content/58954437/history","ancestors":"","descendants":"/rest/api/content/58954437/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954438.json b/tools/wiki_data/bodies/58954438.json new file mode 100644 index 0000000..24d69f3 --- /dev/null +++ b/tools/wiki_data/bodies/58954438.json @@ -0,0 +1 @@ +{"id":"58954438","type":"page","status":"current","title":"02.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/58954438/version/1"},"_expandable":{"content":"/rest/api/content/58954438"}},"body":{"storage":{"value":"

2.1 简单的c++程序

2.1.1 c++ hello world

cpp\nusing namespace std;\n\nint main(){\t\n\tcout << \"hello world\" << endl;\n\treturn EXIT_SUCCESS;\n}]]>
  • #include<iostream>;     预编译指令,引入头文件
  • using namespace std; 使用标准命名空间\\
  • cout << “hello world”<< endl; 和printf功能一样,输出字符串”hello wrold”

2.1.2 面向过程

面向过程是一种以过程为中心的编程思想。

通过分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

面向过程编程思想的核心:功能分解,自顶向下,逐层细化(程序=数据结构+算法)。

面向过程编程语言存在的主要缺点是不符合人的思维习惯,而是要用计算机的思维方式去处理问题,而且面向过程编程语言重用性低,维护困难。

2.1.3 面向对象

面向对象编程(Object-Oriented Programming)简称 OOP 技术,是开发计算机应用程序的一种新方法、新思想。过去的面向过程编程常常会导致所有的代码都包含在几个模块中,使程序难以阅读和维护。在做一些修改时常常牵一动百,使以后的开发和维护难以为继。而使用 OOP 技术,常常要使用许多代码模块,每个模块都只提供特定的功能,它们是彼此独立的,这样就增大了代码重用的几率,更加有利于软件的开发、维护和升级。

在面向对象中,算法与数据结构被看做是一个整体,称作对象,现实世界中任何类的对象都具有一定的属性和操作,也总能用数据结构与算法两者合一地来描述,所以可以用下面的等式来定义对象和程序:

对象 = 算法 + 数据结构

程序 = 对象 + 对象 + ……

从上面的等式可以看出,程序就是许多对象在计算机中相继表现自己,而对象则是一个个程序实体。

面向对象编程思想的核心:应对变化,提高复用。


2.1.4 面向对象三大特性

  • 封装

把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

类将成员变量和成员函数封装在类的内部,根据需要设置访问权限,通过成员函数管理内部状态。

  • 继承

继承所表达的是类之间相关的关系,这种关系使得对象可以继承另外一类对象的特征和能力。

继承的作用:避免公用代码的重复开发,减少代码和数据冗余。

  • 多态

多态性可以简单地概括为“一个接口,多种方法”,字面意思为多种形态。程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954438"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954438","edit":"/pages/resumedraft.action?draftId=58954438","tinyui":"/x/xpKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954438"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954438/child","restrictions":"/rest/api/content/58954438/restriction/byOperation","history":"/rest/api/content/58954438/history","ancestors":"","descendants":"/rest/api/content/58954438/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954439.json b/tools/wiki_data/bodies/58954439.json new file mode 100644 index 0000000..346f5fd --- /dev/null +++ b/tools/wiki_data/bodies/58954439.json @@ -0,0 +1 @@ +{"id":"58954439","type":"page","status":"current","title":"03.C++对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/58954439/version/1"},"_expandable":{"content":"/rest/api/content/58954439"}},"body":{"storage":{"value":"

3.1 ::作用域运算符

通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。

cpp

程序的输出结果是a:20。在test函数的输出语句中,使用的变量atest函数内定义的局部变量,因此输出的结果为局部变量a的值。

作用域运算符可以用来解决局部变量与全局变量的重名问题

cpp

这个例子可以看出,作用域运算符可以用来解决局部变量与全局变量的重名问题,即在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。

3.2 名字控制

创建名字是程序设计过程中一项最基本的活动,当一个项目很大时,它会不可避免地包含大量名字。c++允许我们对名字的产生和名字的可见性进行控制。

我们之前在学习c语言可以通过static关键字来使得名字只得在本编译单元内可见,在c++中我们将通过一种通过命名空间来控制对名字的访问。

3.2.1 C++命名空间(namespace)

c++中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称互相冲突性的可能性越大。另外使用多个厂商的类库时,也可能导致名称冲突。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入关键字namespace(命名空间/名字空间/名称空间),可以更好地控制标识符的作用域。

3.2.2命名空间使用语法

  • 创建一个命名空间:
cpp
  • 命名空间只能全局范围内定义(以下错误写法
cpp
  • 命名空间可嵌套命名空间
cpp
  • 命名空间是开放的,即可以随时把新的成员加入已有的命名空间中
cpp
  • 声明和实现可分离
cppcpp
  • 无名命名空间,意味着命名空间中的标识符只能在本文件内访问,相当于给这个标识符加上了static,使得其可以作为内部连接
cpp


  • 命名空间别名
cpp

3.2.3 using声明

using声明可使得指定的标识符可用。

cpp

using声明碰到函数重载

cpp

3.2.4 using编译指令

using编译指令使整个命名空间标识符可用.


cpp

注意:使用using声明或using编译指令会增加命名冲突的可能性。也就是说,如果有名称空间,并在代码中使用作用域解析运算符,则不会出现二义性。

3.2.5 命名空间使用

我们刚讲的一些东西一开始会觉得难一些,这些东西以后还是挺常用,只要理解了它们的工作机理,使用它们非常简单。

需要记住的关键问题是当引入一个全局的using编译指令时,就为该文件打开了该命名空间,它不会影响任何其他的文件,所以可以在每一个实现文件中调整对命名空间的控制。比如,如果发现某一个实现文件中有太多的using指令而产生的命名冲突,就要对该文件做个简单的改变,通过明确的限定或者using声明来消除名字冲突,这样不需要修改其他的实现文件。

3.3 全局变量检测增强

c语言代码:

cpp

此代码在c++下编译失败,c下编译通过.

3.4 C++中所有的变量和函数都必须有类型

c语言代码:

cpp

以上c代码c编译器编译可通过,c++编译器无法编译通过。

  • C语言中,int fun() 表示返回值为int,接受任意参数的函数,int fun(void) 表示返回值为int的无参函数。
  • C++ 中,int fun() int fun(void) 具有相同的意义,都表示返回值为int的无参函数。

3.5 更严格的类型转换

C++,不同类型的变量一般是不能直接赋值的,需要相应的强转。

c语言代码:

cpp

以上c代码c编译器编译可通过,c++编译器无法编译通过。

3.6 struct类型加强

  • c中定义结构体变量需要加上struct关键字,c++不需要。
  • c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。
cpp

3.7 “新增”bool类型关键字

标准c++bool类型有两种内建的常量true(转换为整数1)false(转换为整数0)表示状态。这三个名字都是关键字。

  • bool类型只有两个值,true(1)false(0)
  • bool类型占1个字节大小
  • bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)


cpp

[c语言中的bool类型]

c语言中也有bool类型,在c99标准之前是没有bool关键字,c99标准已经有bool类型,包含头文件stdbool.h,就可以使用和c++一样的bool类型。


3.8 三目运算符功能增强

  • c语言三目运算表达式返回值为数据值,为右值,不能赋值。
cpp b ? a : b);\n\t//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?\n\t\n\t//(a > b ? a : b) = 100;\n\t//返回的是右值]]>
  • c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值。
cpp b ? a : b);\n\t//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?\n\n\tcout << \"b:\" << b << endl;\n\t//返回的是左值,变量的引用\n\t(a > b ? a : b) = 100;//返回的是左值,变量的引用\n\tcout << \"b:\" << b << endl;]]>

3.9 C/C++中的const

3.9.1 const概述

const单词字面意思为常数,不变的。它是c/c++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量。

const int a = 10;

a= 100; //编译错误,const是一个常量,不可修改

3.9.2 C/C++const的区别

3.9.2.1 C中的const

常量的引进是在c++早期版本中,当时标准C规范正在制定。那时,尽管C委员会决定在C中引入const,但是,他们c中的const理解为”一个不能改变的普通变量”,也就是认为const应该是一个只读变量,既然是变量那么就会给const分配内存,并且在cconst是一个全局只读变量,c语言中const修饰的只读变量是外部连接的。

如果这么写:

const int arrSize = 10;

int arr[arrSize];

看似是一件合理的编码,但是这将得出一个错误。 因为arrSize占用某块内存,所以C编译器不知道它在编译时的值是多少?

3.8.2.1 C++中的const

c++中,一个const不必创建内存空间,而在c中,一个const总是需要一块内存空间。c++中,是否为const常量分配内存空间依赖于如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define一样),那么该存储局空间就不必创建。

如果存储空间没有分配内存的话,在进行完数据类型检查后,为了代码更加有效,值也许会折叠到代码中。

不过,取一个const地址, 或者把它定义为extern,则会为该const创建内存空间。

c++中,出现在所有函数之外的const作用于整个文件(也就是说它在该文件外不可见),默认为内部连接,c++中其他的标识符一般默认为外部连接。

3.8.2.3 C/C++const异同总结

  • c语言全局const会被存储到只读数据段。c++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。两个都受到了只读数据段的保护,不可修改。
cpp


c语言中局部
const存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值。以上代码在c/c++中编译通过,在运行期,修改constA的值时,发生写入错误。原因是修改只读数据段的数据。

cpp

运行结果:constA: 300 

                   *p:300

c语言中,通过指针间接赋值修改了constA的值。

c++中对于局部的const变量要区别对待:

1.对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存。

cpp


运行结果:constA: 10
                  *p:300

constA在符号表中,当我们对constA取地址,这个时候为constA分配了新的空间,*p操作的是分配的空间,而constA是从符号表获得的值。

2.对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存。

cpp运行结果:constA: 300

                  *p:300

constA 分配了内存,所以我们可以修改constA内存中的值。

3.对于自定数据类型,比如类对象,那么也会分配内存。

cppage = 100;\n\tcout << \"pPerson->age:\" << pPerson->age << endl;\n\tpPerson->age = 200;\n\tcout << \"pPerson->age:\" << pPerson->age << endl;]]>

运行结果:pPerson→age:100

                  pPerson->age:200

person分配了内存,所以我们可以通过指针的间接赋值修改person对象。

  • c中const默认为外部连接,c++const默认为内部连接.c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: extern const int a = 10;

const由c++采用,并加进标准c中,尽管他们很不一样。在c中,编译器对待const如同对待变量一样,只不过带有一个特殊的标记,意思是你不能改变我。在c++中定义const时,编译器为它创建空间,所以如果在两个不同文件定义多个同名的const,链接器将发生链接错误。简而言之,constc++中用的更好。

3.9.3 尽量以const替换#define

在旧版本C中,如果想建立一个常量,必须使用预处理器”

#define MAX 1024;

我们定义的宏MAX从未被编译器看到过,因为在预处理阶段,所有的MAX已经被替换为了1024,于是MAX并没有将其加入到符号表中。但我们使用这个常量获得一个编译错误信息时,可能会带来一些困惑,因为这个信息可能会提到1024,但是并没有提到MAX.如果MAX被定义在一个不是你写的头文件中,你可能并不知道1024代表什么,也许解决这个问题要花费很长时间。

解决办法就是用一个常量替换上面的宏。

const int max= 1024;

 

const和#define区别总结:

1.const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查.

2.const有作用域,而#define不重视作用域,默认定义处到文件结尾.如果定义在指定作用域下有效的常量,那么#define就不能用。

 

  1. 宏常量没有类型,所以调用了int类型重载的函数。const有类型,所以调用希望的short类型函数?
cppcpp


 
2.宏常量不重视作用域.

问题: 宏常量可以有命名空间吗?

cpp

3.10 引用(reference)

3.10.1 引用基本用法

引用是c++c的重要扩充。c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference),它也存在于其他一些编程语言中,并不是c++的发明。

 

  • 变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)
  • 程序中通过变量来申请并命名内存空间
  • 通过变量的名字可以使用存储空间

 

对一段连续的内存空间只能取一个别名吗?

c++中新增了引用的概念,引用可以作为一个已定义变量的别名。

基本语法:

Type& ref = val;

注意事项:

  • &在此不是求地址运算,而是起标识作用。
  • 类型标识符是指目标变量的类型
  • 必须在声明引用变量时进行初始化。
  • 引用初始化之后不能改变。
  • 不能有NULL引用。必须确保引用是和一块合法的存储单元关联。
  • 可以建立对数组的引用。


cppcpp

3.10.2 函数中的引用

最常见看见引用的地方是在函数参数和返回值中。当引用被用作函数参数的时,在函数内对任何引用的修改,将对还函数外的参数产生改变。当然,可以通过传递一个指针来做相同的事情,但引用具有更清晰的语法。

如果从函数中返回一个引用,必须像从函数中返回一个指针一样对待。当函数返回值时,引用关联的内存一定要存在。

cpp

通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单:   

  • 函数调用时传递的实参不必加“&”符
  • 在被调函数中不必在参数前加“*”符

引用作为其它变量的别名而存在,因此在一些场合可以代替指针。C++主张用引用传递取代地址传递的方式,因为引用语法容易且不易出错。

cpp
  • 不能返回局部变量的引用。
  • 函数当左值,必须返回引用。

3.10.3 引用的本质

引用的本质在c++内部实现是一个指针常量.

Type& ref = val; // Type* const ref = &val;

 

c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。

cpp

3.10.4 指针引用

c语言中如果想改变一个指针的指向而不是它所指向的内容,函数声明可能这样:


void fun(int**);

给指针变量取一个别名。

Type* pointer = NULL; 

Type*& = pointer;

Type* pointer = NULL;  Type*& = pointer;

cppmAge = 200; \n}\n//引用修改teacher年龄\nvoid AllocateAndInitByReference(Teacher*& teacher){\n\tteacher->mAge = 300;\n}\nvoid test(){\n\t//创建Teacher\n\tTeacher* teacher = NULL;\n\t//指针间接赋值\n\tAllocateAndInitByPointer(&teacher);\n\tcout << \"AllocateAndInitByPointer:\" << teacher->mAge << endl;\n\t//引用赋值,将teacher本身传到ChangeAgeByReference函数中\n\tAllocateAndInitByReference(teacher);\n\tcout << \"AllocateAndInitByReference:\" << teacher->mAge << endl;\n\tfree(teacher);\n}]]>

对于c++中的定义那个,语法清晰多了。函数参数变成指针的引用,用不着取得指针的地址。

3.10.5 常量引用

常量引用的定义格式:

const Type& ref = val;

 

常量引用注意:

  • 字面量不能赋给引用,但是可以赋给const引用
  • const修饰的引用,不能修改。
cpp

    [const引用使用场景]

    常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。

将函数的形参定义为常量引用的好处:

  •  引用不产生新的变量,减少形参与实参传递时的开销。
  • 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。

    如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。

cpp

3.11 内联函数(inline function)

3.11.1 内联函数的引出

c++从c中继承的一个重要特征就是效率。假如c++的效率明显低于c的效率,那么就会有很大的一批程序员不去使用c++了。

c中我们经常把一些短并且执行频繁的计算写成宏,而不是函数,这样做的理由是为了执行效率,宏可以避免函数调用的开销,这些都由预处理来完成。

但是在c++出现之后,使用预处理宏会出现两个问题:

  • 第一个在c中也会出现,宏看起来像一个函数调用,但是会有隐藏一些难以发现的错误。
  • 第二个问题是c++特有的,预处理器不允许访问类的成员,也就是说预处理器宏不能用作类类的成员函数。

 为了保持预处理宏的效率又增加安全性,而且还能像一般成员函数那样可以在类里访问自如,c++引入了内联函数(inline function).

内联函数为了继承宏函数的效率,没有函数调用时开销,然后又可以像普通函数那样,可以进行参数,返回值类型的安全检查,又可以作为成员函数。

3.11.2 预处理宏的缺陷

预处理器宏存在问题的关键是我们可能认为预处理器的行为和编译器的行为是一样的。当然也是由于宏函数调用和函数调用在外表看起来是一样的,因为也容易被混淆。但是其中也会有一些微妙的问题出现:

问题一:

cpp

问题二:

cpp

问题三:

预定义宏函数没有作用域概念,无法作为一个类的成员函数,也就是说预定义宏没有办法表示类的范围。

3.11.3 内联函数

3.11.3.1 内联函数基本概念

c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。

  • 在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。

inline void func(int a);

以上写法没有任何效果,仅仅是声明函数,应该如下方式来做:

inline int func(int a){return ++;}

注意: 编译器将会检查函数参数列表使用是否正确,并返回值(进行必要的转换)。这些事预处理器无法完成的。

内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压栈,跳转,返回的开销。我们可以理解为内联函数是以空间换时间

3.11.3.2 类内部的内联函数

为了定义内联函数,通常必须在函数定义前面放一个inline关键字。但是在类内部定义内联函数时并不是必须的。任何在类内部定义的函数自动成为内联函数。

cpp

构造函数Person,成员函数PrintPerson在类的内部定义,自动成为内联函数。

3.11.3.3 内联函数和编译器

内联函数并不是何时何地都有效,为了理解内联函数何时有效,应该要知道编译器碰到内联函数会怎么处理?

对于任何类型的函数,编译器会将函数类型(包括函数名字,参数类型,返回值类型)放入到符号表中。同样,当编译器看到内联函数,并且对内联函数体进行分析没有发现错误时,也会将内联函数放入符号表。

当调用一个内联函数的时候,编译器首先确保传入参数类型是正确匹配的,或者如果类型不正完全匹配,但是可以将其转换为正确类型,并且返回值在目标表达式里匹配正确类型,或者可以转换为目标类型,内联函数就会直接替换函数调用,这就消除了函数调用的开销。假如内联函数是成员函数,对象this指针也会被放入合适位置。

类型检查和类型转换、包括在合适位置放入对象this指针这些都是预处理器不能完成的。

 

但是c++内联编译会有一些限制,以下情况编译器可能考虑不会将函数进行内联编译:

  • 不能存在任何形式的循环语句
  • 不能存在过多的条件判断语句
  • 函数体不能过于庞大
  • 不能对函数进行取址操作


内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议,如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的函数。

3.12 函数的默认参数

c++在声明函数原型的时可为一个或者多个参数指定默认(缺省)的参数值,当函数调用的时候如果没有指定这个值,编译器会自动用默认值代替。

cpp

注意点:

  • 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数。
  • 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认参数。

3.13 函数的占位参数

c++在声明函数时,可以设置占位参数。占位参数只有参数类型声明,而没有参数名声明。一般情况下,在函数体内部无法使用占位参数。

cpp

3.14 函数重载(overload)

3.14.1 函数重载概述

    能使名字方便使用,是任何程序设计语言的一个重要特征!

 

我们现实生活中经常会碰到一些字在不同的场景下具有不同的意思,比如汉语中的多音字“重”。

当我们说: “他好重啊,我都背不动!”我们根据上下文意思,知道“重”在此时此地表示重量的意思。

如果我们说“你怎么写了那么多重复的代码? 维护性太差了!”这个地方我们知道,“重”表示重复的意思。

同样一个字在不同的场景下具有不同的含义。那么在c++中也有一种类似的现象出现,同一个函数名在不同场景下可以具有不同的含义。

在传统c语言中,函数名必须是唯一的,程序中不允许出现同名的函数。在c++中是允许出现同名的函数,这种现象称为函数重载。

函数重载的目的就是为了方便的使用函数名。

函数重载并不复杂,等大家学完就会明白什么时候需要用到他们,以及是如何编译,链接的。

3.14.2 函数重载

3.14.2.1 函数重载基本语法

实现函数重载的条件:

  • 同一个作用域
  • 参数个数不同
  • 参数类型不同
  • 参数顺序不同
cpp

注意: 函数重载和默认参数一起使用,需要额外注意二义性问题的产生。

cpp

3.14.2.2 函数重载实现原理

编译器为了实现函数重载,也是默认为我们做了一些幕后的工作,编译器用不同的参数类型来修饰不同的函数名,比如void func(); 编译器可能会将函数名修饰成_func,当编译器碰到void func(int x),编译器可能将函数名修饰为_func_int,当编译器碰到void func(int x,char c),编译器可能会将函数名修饰为_func_int_char我这里使用”可能”这个字眼是因为编译器如何修饰重载的函数名称并没有一个统一的标准,所以不同的编译器可能会产生不同的内部名。

cpp

以上三个函数在linux下生成的编译之后的函数名为:

_Z4funcv //v 代表void,无参数

_Z4funci //i 代表参数为int类型

_Z4funcic //i 代表第一个参数为int类型,第二个参数为char类型

3.14.3 extern “C”浅析

以下在Linux下测试:

c函数: void MyFunc(){} ,被编译成函数: MyFunc

c++函数: void MyFunc(){},被编译成函数: _Z6Myfuncv

 

通过这个测试,由于c++中需要支持函数重载,所以cc++中对同一个函数经过编译后生成的函数名是不相同的,这就导致了一个问题,如果在c++中调用一个使用c语言编写模块中的某个函数,那么c++是根据c++的名称修饰方式来查找并链接这个函数,那么就会发生链接错误,以上例,c++中调用MyFunc函数,在链接阶段会去找Z6Myfuncv,结果是没有找到的,因为这个MyFunc函数是c语言编写的,生成的符号是MyFunc

那么如果我想在c++调用c的函数怎么办?

extern "C"的主要作用就是为了实现c++代码能够调用其他c语言代码。加上extern "C"后,这部分代码编译器按c语言的方式进行编译链接,而不是按c++的方式。

MyModule.h


cpp\n\n#if __cplusplus\nextern \"C\"{\n#endif\n\n\tvoid func1();\n\tint func2(int a,int b);\n\n#if __cplusplus\n}\n#endif\n\n#endif]]>

MyModule.c

cpp

TestExternC.cpp

cpp\nusing namespace std;\n\n#if 0\n\n\t#ifdef __cplusplus\n\textern \"C\" {\n\t\t#if 0\n\t\t\tvoid func1();\n\t\t\tint func2(int a, int b);\n\t\t#else\n\t\t\t#include\"MyModule.h\"\n\t\t#endif\n\t}\n\n\t#endif\n\n#else\n\n\textern \"C\" void func1();\n\textern \"C\" int func2(int a, int b);\n\n#endif\n\nint main(){\n\tfunc1();\n\tcout << func2(10, 20) << endl;\n\treturn EXIT_SUCCESS;\n}]]>


","representation":"storage","_expandable":{"content":"/rest/api/content/58954439"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954439","edit":"/pages/resumedraft.action?draftId=58954439","tinyui":"/x/x5KDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954439"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954439/child","restrictions":"/rest/api/content/58954439/restriction/byOperation","history":"/rest/api/content/58954439/history","ancestors":"","descendants":"/rest/api/content/58954439/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954440.json b/tools/wiki_data/bodies/58954440.json new file mode 100644 index 0000000..503aef7 --- /dev/null +++ b/tools/wiki_data/bodies/58954440.json @@ -0,0 +1 @@ +{"id":"58954440","type":"page","status":"current","title":"04.类和对象","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/58954440/version/1"},"_expandable":{"content":"/rest/api/content/58954440"}},"body":{"storage":{"value":"


","representation":"storage","_expandable":{"content":"/rest/api/content/58954440"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954440","edit":"/pages/resumedraft.action?draftId=58954440","tinyui":"/x/yJKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954440"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954440/child","restrictions":"/rest/api/content/58954440/restriction/byOperation","history":"/rest/api/content/58954440/history","ancestors":"","descendants":"/rest/api/content/58954440/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954448.json b/tools/wiki_data/bodies/58954448.json new file mode 100644 index 0000000..0a0a6ed --- /dev/null +++ b/tools/wiki_data/bodies/58954448.json @@ -0,0 +1 @@ +{"id":"58954448","type":"page","status":"current","title":"01.类和对象的基本概念","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/58954448/version/1"},"_expandable":{"content":"/rest/api/content/58954448"}},"body":{"storage":{"value":"

4.1.1 CC++struct区别

  • c语言struct只有变量
  • c++语言struct 既有变量,也有函数

4.1.2 类的封装

我们编写程序的目的是为了解决现实中的问题,而这些问题的构成都是由各种事物组成,我们在计算机中要解决这种问题,首先要做就是要将这个问题的参与者:事和物抽象到计算机程序中,也就是用程序语言表示现实的事物。

那么现在问题是如何用程序语言来表示现实事物?现实世界的事物所具有的共性就是每个事物都具有自身的属性,一些自身具有的行为,所以如果我们能把事物的属性和行为表示出来,那么就可以抽象出来这个事物。

比如我们要表示人这个对象,在c语言中,我们可以这么表示:

cppname);\n}\nvoid AnimalEat(Ainmal* animal){\n\tprintf(\"%s在吃动物吃的饭!\\n\", animal->name);\n}\n\nint main(){\n\n\tPerson person;\n\tstrcpy(person.name, \"小明\");\n\tperson.age = 30;\n\tAnimalEat(&person);\n\n\treturn EXIT_SUCCESS;\n}]]>

定义一个结构体用来表示一个对象所包含的属性,函数用来表示一个对象所具有的行为,这样我们就表示出来一个事物,在c语言中,行为和属性是分开的,也就是说吃饭这个属性不属于某类对象,而属于所有的共同的数据,所以不单单是PeopleEat可以调用Person数据,AnimalEat也可以调用Person数据,那么万一调用错误,将会导致问题发生。

从这个案例我们应该可以体会到,属性和行为应该放在一起,一起表示一个具有属性和行为的对象。

假如某对象的某项属性不想被外界获知,比如说漂亮女孩的年龄不想被其他人知道,那么年龄这条属性应该作为女孩自己知道的属性;或者女孩的某些行为不想让外界知道,只需要自己知道就可以。那么这种情况下,封装应该再提供一种机制能够给属性和行为的访问权限控制住。

所以说封装特性包含两个方面,一个是属性和变量合成一个整体,一个是给属性和函数增加访问权限。

  • 封装
  1. 把变量(属性)和函数(操作)合成一个整体,封装在一个类中
  2. 对变量和函数进行访问控制
  • 访问权限
  1. 在类的内部(作用域范围内),没有访问权限之分,所有成员可以相互访问
  2. 在类的外部(作用域范围外),访问权限才有意义:publicprivateprotected
  3. 在类的外部,只有public修饰的成员才能被访问,在没有涉及继承与派生时, privateprotected是同等级的,外部不允许访问

 

访问属性属性对象内部对象外部
public公有可访问可访问
protected保护可访问不可访问
private私有可访问不可访问
cpp

 

  [structclass的区别?]

  class默认访问权限为private,struct默认访问权限为public.

cpp

4.1.3 将成员变量设置为private

  1. 可赋予客户端访问数据的一致性。

如果成员变量不是public,客户端唯一能够访问对象的方法就是通过成员函数。如果类中所有public权限的成员都是函数,客户在访问类成员时只会默认访问函数,不需要考虑访问的成员需不需要添加(),这就省下了许多搔首弄耳的时间。

  1. 可细微划分访问控制。

使用成员函数可使得我们对变量的控制处理更加精细。如果我们让所有的成员变量为public,每个人都可以读写它。如果我们设置为private,我们可以实现“不准访问”、“只读访问”、“读写访问”,甚至你可以写出“只写访问”。

cpp

4.1.4 课堂练习

请设计一个Person类,Person类具有nameage属性,提供初始化函数(Init),并提供对nameage的读写函数(setget),但必须确保age的赋值在有效范围内(0-100),超出有效范围,则拒绝赋值,并提供方法输出姓名和年龄.(10分钟)


","representation":"storage","_expandable":{"content":"/rest/api/content/58954448"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954448","edit":"/pages/resumedraft.action?draftId=58954448","tinyui":"/x/0JKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954448"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954448/child","restrictions":"/rest/api/content/58954448/restriction/byOperation","history":"/rest/api/content/58954448/history","ancestors":"","descendants":"/rest/api/content/58954448/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954449.json b/tools/wiki_data/bodies/58954449.json new file mode 100644 index 0000000..58030d1 --- /dev/null +++ b/tools/wiki_data/bodies/58954449.json @@ -0,0 +1 @@ +{"id":"58954449","type":"page","status":"current","title":"02.面向对象程序设计案例","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/58954449/version/1"},"_expandable":{"content":"/rest/api/content/58954449"}},"body":{"storage":{"value":"

4.2 面向对象程序设计案例

4.2.1 设计立方体类

设计立方体类(Cube),求出立方体的面积( 2*a*b + 2*a*c + 2*b*c )和体积( a * b * c),分别用全局函数和成员函数判断两个立方体是否相等。

cpp

4.2.2 点和圆的关系

设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。

假如圆心坐标为x0, y0, 半径为r,点的坐标为x1, y1

  • 点在圆上:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) == r*r
  • 点在圆内:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) < r*r
  • 点在圆外:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) > r*r




cpp radius){\n\t\t\tcout << \"Point(\" << point.getX() << \",\" << point.getY() << \")在圆外!\" << endl;\n\t\t}\n\t\telse{\n\t\t\tcout << \"Point(\" << point.getX() << \",\" << point.getY() << \")在圆上!\" << endl;\n\t\t}\n\t}\nprivate:\n\tPoint mP; //圆心\n\tint mR; //半径\n};\n\nvoid test(){\n\t//实例化圆对象\n\tCircle circle;\n\tcircle.setP(20, 20);\n\tcircle.setR(5);\n\t//实例化点对象\n\tPoint point;\n\tpoint.setX(25);\n\tpoint.setY(20);\n\n\tcircle.IsPointInCircle(point);\n}]]>


","representation":"storage","_expandable":{"content":"/rest/api/content/58954449"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954449","edit":"/pages/resumedraft.action?draftId=58954449","tinyui":"/x/0ZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954449"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954449/child","restrictions":"/rest/api/content/58954449/restriction/byOperation","history":"/rest/api/content/58954449/history","ancestors":"","descendants":"/rest/api/content/58954449/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954451.json b/tools/wiki_data/bodies/58954451.json new file mode 100644 index 0000000..bbacbeb --- /dev/null +++ b/tools/wiki_data/bodies/58954451.json @@ -0,0 +1 @@ +{"id":"58954451","type":"page","status":"current","title":"03.对象的构造和析构","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/58954451/version/1"},"_expandable":{"content":"/rest/api/content/58954451"}},"body":{"storage":{"value":"

4.3 对象的构造和析构

4.3.1 初始化和清理

我们大家在购买一台电脑或者手机,或者其他的产品,这些产品都有一个初始设置,也就是这些产品对被创建的时候会有一个基础属性值。那么随着我们使用手机和电脑的时间越来越久,那么电脑和手机会慢慢被我们手动创建很多文件数据,某一天我们不用手机或电脑了,那么我们应该将电脑或手机中我们增加的数据删除掉,保护自己的信息数据。

从这样的过程中,我们体会一下,所有的事物在起初的时候都应该有个初始状态,当这个事物完成其使命时,应该及时清除外界作用于上面的一些信息数据。

那么我们c++OO思想也是来源于现实,是对现实事物的抽象模拟,具体来说,当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据。

对象的初始化和清理也是两个非常重要的安全问题,一个对象或者变量没有初始时,对其使用后果是未知,同样的使用完一个变量,没有及时清理,也会造成一定的安全问题。c++为了给我们提供这种问题的解决方案,构造函数析构函数,这两个函数将会被编译器自动调用,完成对象初始化和对象清理工作。

无论你是否喜欢,对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数。

为什么初始化操作是自动调用而不是手动调用?既然是必须操作,那么自动调用会更好,如果靠程序员自觉,那么就会存在遗漏初始化的情况出现。

4.3.2 构造函数和析构函数

构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数主要用于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法:

  •  构造函数函数名和类名相同,没有返回值,不能有void,但可以有参数。
  • ClassName(){}

 

析构函数语法:

  • 析构函数函数名是在类名前面加”~”组成,没有返回值,不能有void,不能有参数,不能重载。
  • ~ClassName(){}
cpp

4.3.3 构造函数的分类及调用

  • 按参数类型:分为无参构造函数和有参构造函数
  • 按类型分类:普通构造函数和拷贝构造函数(复制构造函数)




cpp

4.3.4 拷贝构造函数的调用时机

  • 对象以值传递的方式传给函数参数
  • 函数局部对象以值传递的方式从函数返回(vs debug模式下调用一次拷贝构造,qt不调用任何构造)
  • 用一个对象初始化另一个对象
cpp

 

   [Test03结果说明:]

   编译器存在一种对返回值的优化技术,RVO(Return Value Optimization).vs debug模式下并没有进行这种优化,所以函数MyBusiness中创建p对象,调用了一次构造函数,当编译器发现你要返回这个局部的对象时,编译器通过调用拷贝构造生成一个临时Person对象返回,然后调用p的析构函数。

   我们从常理来分析的话,这个匿名对象和这个局部的p对象是相同的两个对象,那么如果能直接返回p对象,就会省去一个拷贝构造和一个析构函数的开销,在程序中一个对象的拷贝也是非常耗时的,如果减少这种拷贝和析构的次数,那么从另一个角度来说,也是编译器对程序执行效率上进行了优化。

   所以在这里,编译器偷偷帮我们做了一层优化:

   当我们这样去调用: Person p = MyBusiness();

   编译器偷偷将我们的代码更改为:

 

4.3.5 构造函数调用规则

  • 默认情况下,c++编译器至少为我们写的类增加3个函数
    • 默认构造函数(无参,函数体为空)
    • 默认析构函数(无参,函数体为空)
    • 默认拷贝构造函数,对类中非静态成员属性简单值拷贝
  • 如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数
  • 如果用户定义了普通构造(非拷贝)c++不在提供默认无参构造,但是会提供默认拷贝构造

4.3.6 深拷贝和浅拷贝

4.3.6.1 浅拷贝

同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝.

一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。

4.3.6.2 深拷贝

当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。

cpp

4.3.7 多个对象构造和析构

4.3.7.1 初始化列表

构造函数和其他函数不同,除了有名字,参数列表,函数体之外还有初始化列表。

初始化列表简单使用:




cpp

注意:初始化成员列表(参数列表)只能在构造函数使用。

4.3.7.2 类对象作为成员

在类中定义的数据成员一般都是基本的数据类型。但是类中的成员也可以是对象,叫做对象成员

C++中对对象的初始化是非常重要的操作,当创建一个对象的时候,c++编译器必须确保调用了所有子对象的构造函数。如果所有的子对象有默认构造函数,编译器可以自动调用他们。但是如果子对象没有默认的构造函数,或者想指定调用某个构造函数怎么办?

那么是否可以在类的构造函数直接调用子类的属性完成初始化呢?但是如果子类的成员属性是私有的,我们是没有办法访问并完成初始化的。

解决办法非常简单:对于子类调用构造函数,c++为此提供了专门的语法,即构造函数初始化列表。

当调用构造函数时,首先按各对象成员在类定义中的顺序(和参数列表的顺序无关)依次调用它们的构造函数,对这些对象初始化,最后再调用本身的函数体。也就是说,先调用对象成员的构造函数,再调用本身的构造函数。

析构函数和构造函数调用顺序相反,先构造,后析构。

cpp

4.3.8 explicit关键字

c++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explicit构造函数不能在隐式转换中使用。

 

[explicit注意]

n  explicit用于修饰构造函数,防止隐式转化。

n  是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)而言。




cpp

4.3.9 动态对象创建

当我们创建数组的时候,总是需要提前预定数组的长度,然后编译器分配预定长度的数组空间,在使用数组的时,会有这样的问题,数组也许空间太大了,浪费空间,也许空间不足,所以对于数组来讲,如果能根据需要来分配空间大小再好不过。

所以动态的意思意味着不确定性。

为了解决这个普遍的编程问题,在运行中可以创建和销毁对象是最基本的要求。当然c早就提供了动态内存分配(dynamic memory allocation,函数mallocfree可以在运行时从堆中分配存储单元。

然而这些函数在c++中不能很好的运行,因为它不能帮我们完成对象的初始化工作。

4.3.9.1 对象创建

当创建一个c++对象时会发生两件事:

  1. 为对象分配内存
  2. 调用构造函数来初始化那块内存

第一步我们能保证实现,需要我们确保第二步一定能发生。c++强迫我们这么做是因为使用未初始化的对象是程序出错的一个重要原因。

4.3.9.2 C动态分配内存方法

为了在运行时动态分配内存,c在他的标准库中提供了一些函数,malloc以及它的变种callocrealloc,释放内存的free,这些函数是有效的、但是原始的,需要程序员理解和小心使用。为了使用c的动态内存分配函数在堆上创建一个类的实例,我们必须这样做:




cppInit();\n\t//清理对象\n\tperson->Clean();\n\t//释放person对象\n\tfree(person);\n\n\treturn EXIT_SUCCESS;\n}\n\n]]>

问题:

1)     程序员必须确定对象的长度。

2)     malloc返回一个void*指针,c++不允许将void*赋值给其他任何指针,必须强转。

3)     malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功。

4)     用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化(构造函数是由编译器调用),用户有可能忘记调用初始化函数。


c的动态内存分配函数太复杂,容易令人混淆,是不可接受的,c++中我们推荐使用运算符new delete.

4.3.9.3 new operator

C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为new的运算符里。当用new创建一个对象时,它就在堆里为对象分配内存并调用构造函数完成初始化。

cppInit(); 构造函数\n\n]]>

New操作符能确定在调用构造函数初始化之前内存分配是成功的,所有不用显式确定调用是否成功。

现在我们发现在堆里创建对象的过程变得简单了,只需要一个简单的表达式,它带有内置的长度计算、类型转换和安全检查。这样在堆创建一个对象和在栈里创建对象一样简单。

 

4.3.9.4 delete operator

new表达式的反面是delete表达式。delete表达式先调用析构函数,然后释放内存。正如new表达式返回一个指向对象的指针一样,delete需要一个对象的地址。

delete只适用于由new创建的对象。

如果使用一个由malloc或者calloc或者realloc创建的对象使用delete,这个行为是未定义的。因为大多数newdelete的实现机制都使用了mallocfree,所以很可能没有调用析构函数就释放了内存。

如果正在删除的对象的指针是NULL,将不发生任何事,因此建议在删除指针后,立即把指针赋值为NULL,以免对它删除两次,对一些对象删除两次可能会产生某些问题。

cppShowPerson();\n\tperson2->ShowPerson();\n\n\tdelete person1;\n\tdelete person2;\n}\n\n]]>

4.3.9.5 用于数组的newdelete

使用newdelete在堆上创建数组非常容易。

cpp

当创建一个对象数组的时候,必须对数组中的每一个对象调用构造函数,除了在栈上可以聚合初始化,必须提供一个默认的构造函数。

cpp

4.3.9.6 delete void*可能会出错

 如果对一个void*指针执行delete操作,这将可能成为一个程序错误,除非指针指向的内容是非常简单的,因为它将不执行析构函数.以下代码未调用析构函数,导致可用内存减少。

cpp

 

问题:malloc、freenewdelete可以混搭使用吗?也就是说malloc分配的内存,可以调用delete吗?通过new创建的对象,可以调用free来释放吗?

 

4.3.9.7 使用newdelete采用相同形式

    Person* person = new Person[10];

    delete person;

以上代码有什么问题吗?(vs下直接中断、qt下析构函数调用一次)

使用了new也搭配使用了delete,问题在于Person10个对象,那么其他9个对象可能没有调用析构函数,也就是说其他9个对象可能删除不完全,因为它们的析构函数没有被调用。

我们现在清楚使用new的时候发生了两件事: 一、分配内存;二、调用构造函数,那么调用delete的时候也有两件事:一、析构函数;二、释放内存。

那么刚才我们那段代码最大的问题在于:person指针指向的内存中到底有多少个对象,因为这个决定应该有多少个析构函数应该被调用。换句话说,person指针指向的是一个单一的对象还是一个数组对象,由于单一对象和数组对象的内存布局是不同的。更明确的说,数组所用的内存通常还包括“数组大小记录”,使得delete的时候知道应该调用几次析构函数。单一对象的话就没有这个记录。单一对象和数组对象的内存布局可理解为下图:

本图只是为了说明,编译器不一定如此实现,但是很多编译器是这样做的。

当我们使用一个delete的时候,我们必须让delete知道指针指向的内存空间中是否存在一个“数组大小记录”的办法就是我们告诉它。当我们使用delete[],那么delete就知道是一个对象数组,从而清楚应该调用几次析构函数。

结论:

    如果在new表达式中使用[],必须在相应的delete表达式中也使用[].如果在new表达式中不使用[], 一定不要在相应的delete表达式中使用[].

4.3.10 静态成员

在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static声明为静态的,称为静态成员。

不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。

 

4.3.10.1 静态成员变量

在一个类中,若将一个成员变量声明为static,这种成员称为静态成员变量。与一般的数据成员不同,无论建立了多少个对象,都只有一个静态数据的拷贝。静态成员变量,属于某个类,所有对象共享。

静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。

 

  • 静态成员变量必须在类中声明,在类外定义
  • 静态数据成员不属于某个对象,在为对象分配空间中不包括静态成员所占空间。
  • 静态数据成员可以通过类名或者对象名来引用。
cpp

4.3.10.2 静态成员函数

在类定义中,前面有static说明的成员函数称为静态成员函数。静态成员函数使用方式和静态变量一样,同样在对象没有创建前,即可通过类名调用。静态成员函数主要为了访问静态变量,但是,不能访问普通成员变量。

静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。

 

  • 静态成员函数只能访问静态变量,不能访问普通成员变量
  • 静态成员函数的使用和静态成员变量一样
  • 静态成员函数也有访问权限
  • 普通成员函数可访问静态成员变量、也可以访问非经常成员变量




cpp

4.3.10.3 const静态成员属性

如果一个类的成员,既要实现共享,又要实现不可改变,那就用 static const 修饰。定义静态const数据成员时,最好在类内部初始化

cpp

4.3.10.4 静态成员实现单例模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其默认构造函数和拷贝构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。


用单例模式,模拟公司员工使用打印机场景,打印机可以打印员工要输出的内容,并且可以累积打印机使用次数。

cppPrintText(\"离职报告!\");\n\tprinter->PrintText(\"入职合同!\");\n\tprinter->PrintText(\"提交代码!\");\n}]]>


","representation":"storage","_expandable":{"content":"/rest/api/content/58954451"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954451","edit":"/pages/resumedraft.action?draftId=58954451","tinyui":"/x/05KDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954451"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954451/child","restrictions":"/rest/api/content/58954451/restriction/byOperation","history":"/rest/api/content/58954451/history","ancestors":"","descendants":"/rest/api/content/58954451/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954456.json b/tools/wiki_data/bodies/58954456.json new file mode 100644 index 0000000..b54272e --- /dev/null +++ b/tools/wiki_data/bodies/58954456.json @@ -0,0 +1 @@ +{"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":"

4.4 C++面向对象模型初探

4.4.1 成员变量和函数的存储

c语言中,“分开来声明的,也就是说,语言本身并没有支持“数据”和“函数”之间的关联性我们把这种程序方法称为“程序性的”,由一组“分布在各个以功能为导航的函数中”的算法驱动,它们处理的是共同的外部数据。

 

c++实现了“封装”,那么数据(成员属性)和操作(成员函数)是什么样的呢?

“数据”和“处理数据的操作(函数)”是分开存储的。

  • c++中的非静态数据成员直接内含在类对象中,就像c struct一样。
  • 成员函数(member function)虽然内含在class声明之内,却不出现在对象中。
  • 每一个非内联成员函数(non-inline member function)只会诞生一份函数实例.


cpp

4.4.2 this指针

4.4.2.1 this指针工作原理

通过上例我们知道,c++的数据和操作也是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的呢?

c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象。

       c++规定,this指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this,用以保存这个对象的地址,也就是说虽然我们没有写上this指针,编译器在编译的时候也是会加上的。因此this也称为“指向本对象的指针”,this指针并不是对象的一部分,不会影响sizeof(对象)的结果。

   this指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每一个对象都拥有自己的函数成员。一般情况下,并不写this,而是让系统进行默认设置。

this指针永远指向当前对象

成员函数通过this指针即可知道操作的是那个对象的数据。This指针是一种隐含指针,它隐含于每个类的非静态成员函数中。This指针无需定义,直接使用即可。

注意:静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量。

4.4.2.2 this指针的使用

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this.


cppname = 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}]]>

4.4.2.3 const修饰成员函数

  • const修饰的成员函数时,const修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量
  • 当成员变量类型符前用mutable修饰时例外。
cppmAge = 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}]]>

4.4.2.4 const修饰对象(常对象)

  • 常对象只能调用const的成员函数
  • 常对象可访问 const 或非 const 数据成员,不能修改,除非成员用mutable修饰
cppmAge = 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}]]>


","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"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954457.json b/tools/wiki_data/bodies/58954457.json new file mode 100644 index 0000000..34326cb --- /dev/null +++ b/tools/wiki_data/bodies/58954457.json @@ -0,0 +1 @@ +{"id":"58954457","type":"page","status":"current","title":"05.友元","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/58954457/version/1"},"_expandable":{"content":"/rest/api/content/58954457"}},"body":{"storage":{"value":"

4.5 友元

类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办?

解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从现实生活中也可以很好的理解:

比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。

程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。

4.5.1 友元语法

  • friend关键字只出现在声明处
  • 其他类、类成员函数、全局函数都可声明为友元
  • 友元函数不是类的成员,不带this指针
  • 友元函数可访问对象任意成员属性,包括私有属性
cppmSittingRoom = \"客厅\";\n\tthis->mBedroom = \"卧室\";\n}\n\nint main(){\n\n\tBuilding building;\n\tMyFriend myfriend;\n\n\tCleanBedRoom(building);\n\tmyfriend.LookAtBedRoom(building);\n\tmyfriend.PlayInBedRoom(building);\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

 

    [友元类注意]

1.友元关系不能被继承。

2.友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。

3.友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友。

思考: c++是纯面向对象的吗?

如果一个类被声明为friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义中,因此他是一个特权函数。c++不是完全的面向对象语言,而只是一个混合产品。增加friend关键字只是用来解决一些实际问题,这也说明这种语言是不纯的。毕竟c++设计的目的是为了实用性,而不是追求理想的抽象。

                                                       --- Thinking in C++

4.5.2 课堂练习

请编写电视机类,电视机有开机和关机状态,有音量,有频道,提供音量操作的方法,频道操作的方法。由于电视机只能逐一调整频道,不能指定频道,增加遥控类,遥控类除了拥有电视机已有的功能,再增加根据输入调台功能。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954457"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954457","edit":"/pages/resumedraft.action?draftId=58954457","tinyui":"/x/2ZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954457"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954457/child","restrictions":"/rest/api/content/58954457/restriction/byOperation","history":"/rest/api/content/58954457/history","ancestors":"","descendants":"/rest/api/content/58954457/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954458.json b/tools/wiki_data/bodies/58954458.json new file mode 100644 index 0000000..f10a390 --- /dev/null +++ b/tools/wiki_data/bodies/58954458.json @@ -0,0 +1 @@ +{"id":"58954458","type":"page","status":"current","title":"06.运算符重载","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/58954458/version/1"},"_expandable":{"content":"/rest/api/content/58954458"}},"body":{"storage":{"value":"

4.6 运算符重载

4.6.1 运算符重载基本概念

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

 

    运算符重载(operator overloading)只是一种语法上的方便”,也就是它只是另一种函数调用的方式。

c++中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义,只是函数的名字由关键字operator及其紧跟的运算符组成。差别仅此而已。它像任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数。

语法:

   定义重载的运算符就像定义函数,只是该函数的名字是operator@,这里的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素。

n  运算符是一元(一个参数)的还是二元(两个参数)

n  运算符被定义为全局函数(对于一元是一个参数,对于二元是两个参数)还是成员函数(对于一元没有参数,对于二元是一个参数-此时该类的对象用作左耳参数)


   [两个极端]

   有些人很容易滥用运算符重载。它确实是一个有趣的工具。但是应该注意,它仅仅是一种语法上的方便而已,是另外一种函数调用的方式。从这个角度来看,只有在能使涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比我们写代码多多了)才有理由重载运算符。如果不是这样,就改用其他更易用,更易读的方式。

   对于运算符重载,另外一个常见的反应是恐慌:突然之间,C运算符的含义变得不同寻常了,一切都变了,所有C代码的功能都要改变!并非如此,对于内置的数据类型的表达式的的运算符是不可能改变的。(例如想重载int类型数据的+号)


4.6.2 运算符重载碰上友元函数

友元函数是一个全局函数,和我们上例写的全局函数类似,只是友元函数可以访问某个类私有数据。

案例: 重载左移操作符(<<),使得cout可以输出对象。

cpp

4.6.3 可重载的运算符

几乎C中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用C中当前没有意义的运算符(例如用**求幂)不能改变运算符优先级,不能改变运算符的参数个数。这样的限制有意义,否则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。

4.6.4 自增自减(++/--)运算符重载

重载的++--运算符有点让人不知所措,因为我们总是希望能根据它们出现在所作用对象的前面还是后面来调用不同的函数。解决办法很简单,例如当编译器看到++a(前置++),它就调用operator++(a),当编译器看到a++(后置++),它就会去调用operator++(a,int).

cppmA;\n\t\ttemp.mB = this->mB;\n\t\tmA++;\n\t\tmB++;\n\t\treturn temp;\n\t}\n\t//前置--\n\tComplex& operator--(){\n\t\tmA--;\n\t\tmB--;\n\t\treturn *this;\n\t}\n\t//后置--\n\tComplex operator--(int){\n\t\tComplex temp;\n\t\ttemp.mA = mA;\n\t\ttemp.mB = mB;\n\t\tmA--;\n\t\tmB--;\n\t\treturn temp;\n\t}\n\tvoid ShowComplex(){\n\t\tcout << \"A:\" << mA << \" B:\" << mB << endl;\n\t}\nprivate:\n\tint mA;\n\tint mB;\n};\n\nvoid test(){\n\tComplex complex;\n\tcomplex++;\n\tcout << complex;\n\t++complex;\n\tcout << complex;\n\n\tComplex ret = complex++;\n\tcout << ret;\n\tcout << complex;\n\n\tcout << \"------\" << endl;\n\tret--;\n\t--ret;\n\tcout << \"ret:\" << ret;\n\tcomplex--;\n\t--complex;\n\tcout << \"complex:\" << complex;\n}]]>

 

优先使用++--的标准形式,优先调用前置++

如果定义了++c,也要定义c++,递增操作符比较麻烦,因为他们都有前缀和后缀形式,而两种语义略有不同。重载operator++operator--时应该模仿他们对应的内置操作符。

对于++--而言,后置形式是先返回,然后对象++或者--,返回的是对象的原值。前置形式,对象先++--,返回当前对象,返回的是新对象。其标准形式为:

调用代码时候,要优先使用前缀形式,除非确实需要后缀形式返回的原值,前缀和后缀形式语义上是等价的,输入工作量也相当,只是效率经常会略高一些,由于前缀形式少创建了一个临时对象。

4.6.5 指针运算符(*->)重载

cppmParam = param;\n\t}\n\tvoid PrintPerson(){\n\t\tcout << \"Param:\" << mParam << endl;\n\t}\nprivate:\n\tint mParam;\n};\n\nclass SmartPointer{\npublic:\n\tSmartPointer(Person* person){\n\t\tthis->pPerson = person;\n\t}\n\t//重载指针的->、*操作符\n\tPerson* operator->(){\n\t\treturn pPerson;\n\t}\n\tPerson& operator*(){\n\t\treturn *pPerson;\n\t}\n\t~SmartPointer(){\n\t\tif (pPerson != NULL){\n\t\t\tdelete pPerson;\n\t\t}\n\t}\npublic:\n\tPerson* pPerson;\n};\n\nvoid test01(){\n\t\n\t//Person* person = new Person(100);\n\t//如果忘记释放,那么就会造成内存泄漏\n\n\tSmartPointer pointer(new Person(100));\n\tpointer->PrintPerson();\n}]]>

4.6.6 赋值(=)运算符重载

赋值符常常初学者的混淆。这是毫无疑问的,因为’=’在编程中是最基本的运算符,可以进行赋值操作,也能引起拷贝构造函数的调用。

cppmID = id;\n\t\tthis->mAge = age;\n\t}\n\t//重载赋值运算符\n\tPerson& operator=(const Person& person){\n\t\tthis->mID = person.mID;\n\t\tthis->mAge = person.mAge;\n\t\treturn *this;\n\t}\nprivate:\n\tint mID;\n\tint mAge;\n};\n\n//1. =号混淆的地方\nvoid test01(){\n\tPerson person1(10, 20);\n\tPerson person2 = person1; //调用拷贝构造\n\t//如果一个对象还没有被创建,则必须初始化,也就是调用构造函数\n\t//上述例子由于person2还没有初始化,所以会调用构造函数\n\t//由于person2是从已有的person1来创建的,所以只有一个选择\n\t//就是调用拷贝构造函数\n\tperson2 = person1; //调用operator=函数\n\t//由于person2已经创建,不需要再调用构造函数,这时候调用的是重载的赋值运算符\n}\n//2. 赋值重载案例\nvoid test02(){\n\tPerson person1(20, 20);\n\tPerson person2(30, 30);\n\tcout << \"person1:\" << person1;\n\tcout << \"person2:\" << person2;\n\tperson2 = person1;\n\tcout << \"person2:\" << person2;\n}\n//常见错误,当准备给两个相同对象赋值时,应该首先检查一下这个对象是否对自身赋值了\n//对于本例来讲,无论如何执行这些赋值运算都是无害的,但如果对类的实现进行修改,那么将会出现差异;\n//3. 类中指针\nclass Person2{\n\tfriend ostream& operator<<(ostream& os, const Person2& person){\n\t\tos << \"Name:\" << person.pName << \" ID:\" << person.mID << \" Age:\" << person.mAge << endl;\n\t\treturn os;\n\t}\npublic:\n\tPerson2(char* name,int id, int age){\n\t\tthis->pName = new char[strlen(name) + 1];\n\t\tstrcpy(this->pName, name);\n\t\tthis->mID = id;\n\t\tthis->mAge = age;\n\t}\n#if 1\n\t//重载赋值运算符\n\tPerson2& operator=(const Person2& person){\n\n\t\t//注意:由于当前对象已经创建完毕,那么就有可能pName指向堆内存\n\t\t//这个时候如果直接赋值,会导致内存没有及时释放\n\t\tif (this->pName != NULL){\n\t\t\tdelete[] this->pName;\n\t\t}\n\n\t\tthis->pName = new char[strlen(person.pName) + 1];\n\t\tstrcpy(this->pName,person.pName);\n\t\tthis->mID = person.mID;\n\t\tthis->mAge = person.mAge;\n\t\treturn *this;\n\t}\n#endif\n\t//析构函数\n\t~Person2(){\n\t\tif (this->pName != NULL){\n\t\t\tdelete[] this->pName;\n\t\t}\n\t}\nprivate:\n\tchar* pName;\n\tint mID;\n\tint mAge;\n};\n\nvoid test03(){\n\tPerson2 person1(\"John\",20, 20);\n\tPerson2 person2(\"Edward\",30, 30);\n\tcout << \"person1:\" << person1;\n\tcout << \"person2:\" << person2;\n\tperson2 = person1;\n\tcout << \"person2:\" << person2;\n}]]>

 

    如果没有重载赋值运算符,编译器会自动创建默认的赋值运算符重载函数。行为类似默认拷贝构造,进行简单值拷贝。

4.6.7 等于和不等于(==!=)运算符重载

cpppName = new char[strlen(name) + 1];\n\t\tstrcpy(this->pName, name);\n\t\tthis->mID = id;\n\t\tthis->mAge = age;\n\t}\n\t//重载==号操作符\n\tbool operator==(const Complex& complex){\n\t\tif (strcmp(this->pName,complex.pName) == 0 && \n\t\t this->mID == complex.mID && \n\t\t\tthis->mAge == complex.mAge){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t//重载!=操作符\n\tbool operator!=(const Complex& complex){\n\t\tif (strcmp(this->pName, complex.pName) != 0 || \n\t\t this->mID != complex.mID || \n\t\t\tthis->mAge != complex.mAge){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t~Complex(){\n\t\tif (this->pName != NULL){\n\t\t\tdelete[] this->pName;\n\t\t}\n\t}\nprivate:\n\tchar* pName;\n\tint mID;\n\tint mAge;\n};\nvoid test(){\n\tComplex complex1(\"aaa\", 10, 20);\n\tComplex complex2(\"bbb\", 10, 20);\n\tif (complex1 == complex2){ cout << \"相等!\" << endl; }\n\tif (complex1 != complex2){ cout << \"不相等!\" << endl; }\n}]]>

4.6.8 函数调用符号()重载

cpp

4.6.9 不要重载&&||

不能重载operator&& operator|| 的原因是,无法在这两种情况下实现内置操作符的完整语义。说得更具体一些,内置版本版本特殊之处在于:内置版本的&&||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了--而且能够保证不需要。我们都已经习惯这种方便的特性了。

我们说操作符重载其实是另一种形式的函数调用而已,对于函数调用总是在函数执行之前对所有参数进行求值。

4.6.10 符号重载总结

  • =, [], ()-> 操作符只能通过成员函数进行重载
  • <<>>只能通过全局函数配合友元函数进行重载
  • 不要重载 &&|| 操作符,因为无法实现短路规


","representation":"storage","_expandable":{"content":"/rest/api/content/58954458"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954458","edit":"/pages/resumedraft.action?draftId=58954458","tinyui":"/x/2pKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954458"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954458/child","restrictions":"/rest/api/content/58954458/restriction/byOperation","history":"/rest/api/content/58954458/history","ancestors":"","descendants":"/rest/api/content/58954458/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954461.json b/tools/wiki_data/bodies/58954461.json new file mode 100644 index 0000000..1d4b48a --- /dev/null +++ b/tools/wiki_data/bodies/58954461.json @@ -0,0 +1 @@ +{"id":"58954461","type":"page","status":"current","title":"07.继承和派生","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/58954461/version/1"},"_expandable":{"content":"/rest/api/content/58954461"}},"body":{"storage":{"value":"

4.7 继承和派生

4.7.1 继承概述

4.7.1.1 为什么需要继承

cppHeader();\n\tnewspage->MainBody();\n\tnewspage->LeftNavigation();\n\tnewspage->Footer();\n}\n#else\n//使用继承,可以复用已有的代码,新闻业除了主体部分不一样,其他都是一样的\nclass NewsPage : public IndexPage{\npublic:\n\t//网页主体部分\n\tvoid MainBody(){\n\t\tcout << \"新闻网页主主体内容!\" << endl;\n\t}\n};\nvoid test(){\n\tNewsPage* newspage = new NewsPage;\n\tnewspage->Header();\n\tnewspage->MainBody();\n\tnewspage->LeftNavigation();\n\tnewspage->Footer();\n}\n#endif\nint main(){\n\n\ttest();\n\n\treturn EXIT_SUCCESS;\n}]]>

4.7.1.2 继承基本概念

c++最重要的特征是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员。

一个B类继承于A类,或称从类A派生类B。这样的话,类A成为基类(父类), 类B成为派生类(子类)。

派生类中的成员,包含两大部分:

  • 一类是从基类继承过来的,一类是自己增加的成员。
  • 从基类继承过过来的表现其共性,而新增的成员体现了其个性。

4.7.1.3 派生类定义

派生类定义格式:

   Class 派生类名继承方式 基类名{

         //派生类新增的数据成员和成员函数

   }

 

三种继承方式:

  • public : 公有继承
  • private : 私有继承
  • protected : 保护继承

从继承源上分:

  • 单继承:指每个派生类只直接继承了一个基类的特征
  • 多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特征

4.7.2 派生类访问控制

派生类继承基类,派生类拥有基类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在派生类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限。

派生类的访问权限规则如下:

cpp

4.7.3 继承中的构造和析构

4.7.3.1 继承中的对象模型

C++编译器的内部可以理解为结构体,子类是由父类成员叠加子类新成员而成:

cpp

4.7.3.2 对象构造和析构的调用原则

  • 继承中的构造和析构
  • 子类对象在创建时会首先调用父类的构造函数
  • 父类构造函数执行完毕后,才会调用子类的构造函数
  • 当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数
  • 析构函数调用顺序和构造函数相反

cpp
  • 继承与组合混搭的构造和析构


cpp

4.7.3 继承中同名成员的处理方法

  • 当子类成员和父类成员同名时,子类依然从父类继承同名成员
  • 如果子类有成员和父类同名,子类访问其成员默认访问子类的成员(本作用域,就近原则)
  • 在子类通过作用域::进行同名成员区分(在派生类中使用基类的同名成员,显示使用类名限定符)
cppcpp
  • Derive1 重定义了Base类的myfunc函数,derive1可访问func1及其重载版本的函数。
  • Derive2通过改变函数参数列表的方式重新定义了基类的func1函数,则从基类中继承来的其他重载版本被隐藏,不可访问
  • Derive3通过改变函数返回类型的方式重新定义了基类的func1函数,则从基类继承来的没有重新定义的重载版本的函数将被隐藏。

任何时候重新定义基类中的一个重载函数,在新类中所有的其他版本将被自动隐藏.

4.7.4 非自动继承的函数

不是所有的函数都能自动从基类继承到派生类中。构造函数和析构函数用来处理对象的创建和析构操作,构造和析构函数只知道对它们的特定层次的对象做什么,也就是说构造函数和析构函数不能被继承,必须为每一个特定的派生类分别创建。

另外operator=也不能被继承,因为它完成类似构造函数的行为。也就是说尽管我们知道如何由=右边的对象如何初始化=左边的对象的所有成员,但是这个并不意味着对其派生类依然有效。

在继承的过程中,如果没有创建这些函数,编译器会自动生成它们。

4.7.5 继承中的静态成员特性

静态成员函数和非静态成员函数的共同点:

  1. 他们都可以被继承到派生类中。
  2. 如果重新定义一个静态成员函数,所有在基类中的其他重载函数会被隐藏。
  3. 如果我们改变基类中一个函数的特征,所有使用该函数名的基类版本都会被隐藏。
cpp

4.7.6 多继承

4.7.6.1 多继承概念

我们可以从一个类继承,我们也可以能同时从多个类继承,这就是多继承。但是由于多继承是非常受争议的,从多个类继承可能会导致函数、变量等同名导致较多的歧义。

cpp

多继承会带来一些二义性的问题, 如果两个基类中有同名的函数或者变量,那么通过派生类对象去访问这个函数或变量时就不能明确到底调用从基类1继承的版本还是从基类2继承的版本?

解决方法就是显示指定调用那个基类的版本。

4.7.6.2 菱形继承和虚继承

两个派生类继承同一个基类而又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石型继承。

 

这种继承所带来的问题:

  1. 羊继承了动物的数据和函数,鸵同样继承了动物的数据和函数,当草泥马调用函数或者数据时,就会产生二义性。
  2. 草泥马继承自动物的函数和数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
cpp

上述问题如何解决?对于调用二义性,那么可通过指定调用那个基类的方式来解决,那么重复继承怎么解决?

对于这种菱形继承所带来的两个问题,c++为我们提供了一种方式,采用虚基类。那么我们采用虚基类方式将代码修改如下:

cpp

以上程序Base1 ,Base2采用虚继承方式继承BigBase,那么BigBase被称为虚基类。

通过虚继承解决了菱形继承所带来的二义性问题。

但是虚基类是如何解决二义性的呢?并且derived大小为12字节,这是怎么回事?

4.7.6.3 虚继承实现原理

cpp


通过内存图,我们发现普通继承和虚继承的对象内存图是不一样的。我们也可以猜测到编译器肯定对我们编写的程序做了一些手脚。


  • BigBase 菱形最顶层的类,内存布局图没有发生改变。
  • Base1和Base2通过虚继承的方式派生自BigBase,这两个对象的布局图中可以看出编译器为我们的对象中增加了一个vbptr (virtual base pointer),vbptr指向了一张表,这张表保存了当前的虚指针相对于虚基类的首地址的偏移量。
  • Derived派生于Base1Base2,继承了两个基类的vbptr指针,并调整了vbptr与虚基类的首地址的偏移量。

 

由此可知编译器帮我们做了一些幕后工作,使得这种菱形问题在继承时候能只继承一份数据,并且也解决了二义性的问题。现在模型就变成了Base1 Base2 Derived三个类对象共享了一份BigBase数据。


当使用虚继承时,虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存模型中均只会出现一个虚基类的子对象(这和多继承是完全不同的)。即使共享虚基类,但是必须要有一个类来完成基类的初始化(因为所有的对象都必须被初始化,哪怕是默认的),同时还不能够重复进行初始化,那到底谁应该负责完成初始化呢?C++标准中选择在每一次继承子类中都必须书写初始化语句(因为每一次继承子类可能都会用来定义对象),但是虚基类的初始化是由最后的子类完成,其他的初始化语句都不会调用。

cpp

注意:

    虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承的.

 工程开发中真正意义上的多继承是几乎不被使用,因为多重继承带来的代码复杂性远多于其带来的便利,多重继承对代码维护性上的影响是灾难性的,在设计方法上,任何多继承都可以用单继承代替。



","representation":"storage","_expandable":{"content":"/rest/api/content/58954461"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954461","edit":"/pages/resumedraft.action?draftId=58954461","tinyui":"/x/3ZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954461"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954461/child","restrictions":"/rest/api/content/58954461/restriction/byOperation","history":"/rest/api/content/58954461/history","ancestors":"","descendants":"/rest/api/content/58954461/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954469.json b/tools/wiki_data/bodies/58954469.json new file mode 100644 index 0000000..df7cac0 --- /dev/null +++ b/tools/wiki_data/bodies/58954469.json @@ -0,0 +1 @@ +{"id":"58954469","type":"page","status":"current","title":"08.多态","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/58954469/version/1"},"_expandable":{"content":"/rest/api/content/58954469"}},"body":{"storage":{"value":"

4.8 多态

4.8.1 多态基本概念

多态是面向对象程序设计语言中数据抽象和继承之外的第三个基本特征。

多态性(polymorphism)提供接口与具体实现之间的另一层隔离,从而将”what”和”how”分离开来。多态性改善了代码的可读性和组织性,同时也使创建的程序具有可扩展性,项目不仅在最初创建时期可以扩展,而且当项目在需要有新的功能时也能扩展。

c++支持编译时多态(静态多态)和运行时多态(动态多态),运算符重载和函数重载就是编译时多态,而派生类和虚函数实现运行时多态。

静态多态和动态多态的区别就是函数地址是早绑定(静态联编)还是晚绑定(动态联编)。如果函数的调用,在编译阶段就可以确定函数的调用地址,并产生代码,就是静态多态(编译时多态),就是说地址是早绑定的。而如果函数的调用地址不能编译不能在编译期间确定,而需要在运行时才能决定,这这就属于晚绑定(动态多态,运行时多态)

cppmA = a;\n\t}\n\tvoid setB(int b){\n\t\tthis->mB = b;\n\t}\n\tvoid setOperator(string oper){\n\t\tthis->mOperator = oper;\n\t}\n\tint getResult(){\n\t\t\n\t\tif (this->mOperator == \"+\"){\n\t\t\treturn mA + mB;\n\t\t}\n\t\telse if (this->mOperator == \"-\"){\n\t\t\treturn mA - mB;\n\t\t}\n\t\telse if (this->mOperator == \"*\"){\n\t\t\treturn mA * mB;\n\t\t}\n\t\telse if (this->mOperator == \"/\"){\n\t\t\treturn mA / mB;\n\t\t}\n\t}\nprivate:\n\tint mA;\n\tint mB;\n\tstring mOperator;\n};\n\n//这种程序不利于扩展,维护困难,如果修改功能或者扩展功能需要在源代码基础上修改\n//面向对象程序设计一个基本原则:开闭原则(对修改关闭,对扩展开放)\n\n//抽象基类\nclass AbstractCaculator{\npublic:\n\tvoid setA(int a){\n\t\tthis->mA = a;\n\t}\n\tvirtual void setB(int b){\n\t\tthis->mB = b;\n\t}\n\tvirtual int getResult() = 0;\nprotected:\n\tint mA;\n\tint mB;\n\tstring mOperator;\n};\n\n//加法计算器\nclass PlusCaculator : public AbstractCaculator{\npublic:\n\tvirtual int getResult(){\n\t\treturn mA + mB;\n\t}\n};\n\n//减法计算器\nclass MinusCaculator : public AbstractCaculator{\npublic:\n\tvirtual int getResult(){\n\t\treturn mA - mB;\n\t}\n};\n\n//乘法计算器\nclass MultipliesCaculator : public AbstractCaculator{\npublic:\n\tvirtual int getResult(){\n\t\treturn mA * mB;\n\t}\n};\n\nvoid DoBussiness(AbstractCaculator* caculator){\n\tint a = 10;\n\tint b = 20;\n\tcaculator->setA(a);\n\tcaculator->setB(b);\n\tcout << \"计算结果:\" << caculator->getResult() << endl;\n\tdelete caculator;\n}]]>

4.8.2 向上类型转换及问题

4.8.2.1 问题抛出

对象可以作为自己的类或者作为它的基类的对象来使用。还能通过基类的地址来操作它。取一个对象的地址(指针或引用),并将其作为基类的地址来处理,这种称为向上类型转换。

也就是说:父类引用或指针可以指向子类对象,通过父类指针或引用来操作子类对象。

cpp

 

运行结果: 动物在唱歌

问题抛出: 我们给DoBussiness传入的对象是dog,而不是animal对象,输出的结果应该是Dog::speak

 

4.8.2.1 问题解决思路

解决这个问题,我们需要了解下绑定(捆绑,binding)概念。

把函数体与函数调用相联系称为绑定(捆绑,binding)

 

当绑定在程序运行之前(由编译器和连接器)完成时,称为早绑定(early binding).C语言中只有一种函数调用方式,就是早绑定。

上面的问题就是由于早绑定引起的,因为编译器在只有Animal地址时并不知道要调用的正确函数。编译是根据指向对象的指针或引用的类型来选择函数调用。这个时候由于DoBussiness的参数类型是Animal&,编译器确定了应该调用的speakAnimal::speak的,而不是真正传入的对象Dog::speak

解决方法就是迟绑定(迟捆绑,动态绑定,运行时绑定,late binding),意味着绑定要根据对象的实际类型,发生在运行。

C++语言要实现这种动态绑定,必须有某种机制来确定运行时对象的类型并调用合适的成员函数。对于一种编译语言,编译器并不知道实际的对象类型(编译器并不知道Animal类型的指针或引用指向的实际的对象类型)。

4.8.2.1 问题解决方案(虚函数,vitual function)

C++动态多态性是通过虚函数来实现的,虚函数允许子类(派生类)重新定义父类(基类)成员函数,而子类(派生类)重新定义父类(基类)虚函数的做法称为覆盖(override),或者称为重写。

对于特定的函数进行动态绑定,c++要求在基类中声明这个函数的时候使用virtual关键字,动态绑定也就对virtual函数起作用.

  • 为创建一个需要动态绑定的虚成员函数,可以简单在这个函数声明前面加上virtual关键字,定义时候不需要.
  • 如果一个函数在基类中被声明为virtual,那么在所有派生类中它都是virtual.
  • 在派生类中virtual函数的重定义称为重写(override).
  • Virtual关键字只能修饰成员函数.
  • 构造函数不能为虚函数

 

注意: 仅需要在基类中声明一个函数为virtual.调用所有匹配基类声明行为的派生类函数都将使用虚机制。虽然可以在派生类声明前使用关键字virtual(这也是无害的),但这个样会使得程序显得冗余和杂乱。(我建议写上)

cpp

4.8.3 C++如何实现动态绑定

动态绑定什么时候发生?所有的工作都是由编译器在幕后完成。当我们告诉通过创建一个virtual函数来告诉编译器要进行动态绑定,那么编译器就会根据动态绑定机制来实现我们的要求, 不会再执行早绑定。


问题:C++的动态捆绑机制是怎么样的?

首先,我们看看编译器如何处理虚函数。当编译器发现我们的类中有虚函数的时候,编译器会创建一张虚函数表,把虚函数的函数入口地址放到虚函数表中,并且在类中秘密增加一个指针,这个指针就是vpointer(缩写vptr),这个指针是指向对象的虚函数表。在多态调用的时候,根据vptr指针,找到虚函数表来实现动态绑定。

验证对象中的虚指针:

cpp

在编译阶段,编译器秘密增加了一个vptr指针,但是此时vptr指针并没有初始化指向虚函数表(vtable),什么时候vptr才会指向虚函数表?在对象构建的时候,也就是在对象初始化调用构造函数的时候。编译器首先默认会在我们所编写的每一个构造函数中,增加一些vptr指针初始化的代码。如果没有提供构造函数,编译器会提供默认的构造函数,那么就会在默认构造函数里做此项工作,初始化vptr指针,使之指向本对象的虚函数表。

起初,子类继承基类,子类继承了基类的vptr指针,这个vptr指针是指向基类虚函数表,当子类调用构造函数,使得子类的vptr指针指向了子类的虚函数表。

  • 396px当子类无重写基类虚函数时:

 

过程分析:

     Animal* animal = new Dog;

     animal->fun1();

     当程序执行到这里,会去animal指向的空间中寻找vptr指针,通过vptr指针找到func1函数,此时由于子类并没有重写也就是覆盖基类的func1函数,所以调用func1时,仍然调用的是基类的func1.

执行结果: 我是基类的func1

测试结论: 无重写基类的虚函数,无意义

  • 当子类重写基类虚函数时:

 

过程分析:

     Animal* animal = new Dog;

     animal->fun1();

     当程序执行到这里,会去animal指向的空间中寻找vptr指针,通过vptr指针找到func1函数,由于子类重写基类的func1函数,所以调用func1时,调用的是子类的func1.

执行结果: 我是子类的func1

测试结论: 无重写基类的虚函数,无意义

 

多态的成立条件:

  1. 有继承
  2. 子类重写父类虚函数函数
    1. 返回值,函数名字,函数参数,必须和父类完全一致(析构函数除外)
    2. 子类中virtual关键字可写可不写,建议写
  3. 类型兼容,父类指针,父类引用 指向 子类对象

4.8.4 抽象基类和纯虚函数(pure virtual function)

在设计时,常常希望基类仅仅作为其派生类的一个接口。这就是说,仅想对基类进行向上类型转换,使用它的接口,而不希望用户实际的创建一个基类的对象。同时创建一个纯虚函数允许接口中放置成员原函数,而不一定要提供一段可能对这个函数毫无意义的代码。

 

做到这点,可以在基类中加入至少一个纯虚函数(pure virtual function),使得基类称为抽象类(abstract class).

  • 纯虚函数使用关键字virtual,并在其后面加上=0。如果试图去实例化一个抽象类,编译器则会阻止这种操作。
    • 当继承一个抽象类的时候,必须实现所有的纯虚函数,否则由抽象类派生的类也是一个抽象类。
    • Virtual void fun() = 0;告诉编译器在vtable中为函数保留一个位置,但在这个特定位置不放地址。

 

   建立公共接口目的是为了将子类公共的操作抽象出来,可以通过一个公共接口来操纵一组类,且这个公共接口不需要事先(或者不需要完全实现)。可以创建一个公共类.

4.8.5 纯虚函数和多继承

多继承带来了一些争议,但是接口继承可以说一种毫无争议的运用了。

绝大数面向对象语言都不支持多继承,但是绝大数面向对象对象语言都支持接口的概念,c++中没有接口的概念,但是可以通过纯虚函数实现接口。

 

    接口类中只有函数原型定义,没有任何数据定义。

 

多重继承接口不会带来二义性和复杂性问题。接口类只是一个功能声明,并不是功能实现,子类需要根据功能说明定义功能实现。

注意:除了析构函数外,其他声明都是纯虚函数。

        

4.8.6 虚析构函数

4.8.6.1 虚析构函数作用

虚析构函数是为了解决基类指针指向派生类对象,并用基类的指针删除派生类对象。

cpp~People();\n}]]>

4.8.6.2 纯虚析构函数

纯虚析构函数在c++中是合法的,但是在使用的时候有一个额外的限制:必须为纯虚析构函数提供一个函数体。

那么问题是:如果给虚析构函数提供函数体了,那怎么还能称作纯虚析构函数呢?

纯虚析构函数和非纯析构函数之间唯一的不同之处在于纯虚析构函数使得基类是抽象类,不能创建基类的对象。

cpp

如果类的目的不是为了实现多态,作为基类来使用,就不要声明虚析构函数,反之,则应该为类声明虚析构函数。

4.8.7 重写 重载 重定义

  • 重载,同一作用域的同名函数
  1. 同一个作用域
  2. 参数个数,参数顺序,参数类型不同
  3. 和函数返回值,没有关系
  4. const也可以作为重载条件 //do(const Teacher& t){}  do(Teacher& t)
  • 重定义(隐藏)
  1. 有继承
  2. 子类(派生类)重新定义父类(基类)的同名成员(非virtual函数)
  • 重写(覆盖)
  1. 有继承
  2. 子类(派生类)重写父类(基类)的virtual函数
  3. 函数返回值,函数名字,函数参数,必须和基类中的虚函数一致


cpp


","representation":"storage","_expandable":{"content":"/rest/api/content/58954469"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954469","edit":"/pages/resumedraft.action?draftId=58954469","tinyui":"/x/5ZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954469"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954469/child","restrictions":"/rest/api/content/58954469/restriction/byOperation","history":"/rest/api/content/58954469/history","ancestors":"","descendants":"/rest/api/content/58954469/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954472.json b/tools/wiki_data/bodies/58954472.json new file mode 100644 index 0000000..aae8450 --- /dev/null +++ b/tools/wiki_data/bodies/58954472.json @@ -0,0 +1 @@ +{"id":"58954472","type":"page","status":"current","title":"05.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/58954472/version/1"},"_expandable":{"content":"/rest/api/content/58954472"}},"body":{"storage":{"value":"

5.1 模板概论

c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。

  • c++提供两种模板机制:函数模板类模板
  • 类属 - 类型参数化,又称参数模板

总结:

  • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属。
  • 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

5.2 函数模板

5.2.1 什么是函数模板?

cpp\nvoid MySwap(T& a,T& b){\n\tT temp = a;\n\ta = b;\n\tb = temp;\n}\n\nvoid test01(){\n\t\n\tint a = 10;\n\tint b = 20;\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n\t//1. 这里有个需要注意点,函数模板可以自动推导参数的类型\n\tMySwap(a,b);\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n\n\tchar c1 = 'a';\n\tchar c2 = 'b';\n\tcout << \"c1:\" << c1 << \" c2:\" << c2 << endl;\n\t//2. 函数模板可以自动类型推导,那么也可以显式指定类型\n\tMySwap(c1, c2);\n\tcout << \"c1:\" << c1 << \" c2:\" << c2 << endl;\n}]]>

5.2.2 课堂练习

使用函数模板实现对charint类型数组进行排序?

5.3 函数模板和普通函数区别

  • 函数模板不允许自动类型转化
  • 普通函数能够自动进行类型转化
cpp\nT MyPlus(T a, T b){\n\tT ret = a + b;\n\treturn ret;\n}\n\n//普通函数\nint MyPlus(int a,char b){\n\tint ret = a + b;\n\treturn ret;\n}\n\nvoid test02(){\n\n\tint a = 10;\n\tchar b = 'a';\n\n\t//调用函数模板,严格匹配类型\n\tMyPlus(a, a);\n\tMyPlus(b, b);\n\t//调用普通函数\n\tMyPlus(a, b);\n\t//调用普通函数 普通函数可以隐式类型转换\n\tMyPlus(b, a);\n\n\t//结论:\n\t//函数模板不允许自动类型转换,必须严格匹配类型\n\t//普通函数可以进行自动类型转换\n}]]>

5.4 函数模板和普通函数在一起调用规则

  • c++编译器优先考虑普通函数
  • 可以通过空模板实参列表的语法限定编译器只能通过模板匹配
  • 函数模板可以像普通函数那样可以被重载
  • 如果函数模板可以产生一个更好的匹配,那么选择模板
cpp\nT MyPlus(T a, T b){\n\tT ret = a + b;\n\treturn ret;\n}\n\n//普通函数\nint MyPlus(int a, int b){\n\tint ret = a + b;\n\treturn ret;\n}\n\nvoid test03(){\n\tint a = 10;\n\tint b = 20;\n\tchar c = 'a';\n\tchar d = 'b';\n\t//如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数\n\tcout << MyPlus(a, b) << endl;\n\t//如果我必须要调用函数模板,那么怎么办?\n\tcout << MyPlus<>(a, b) << endl;\n\t//此时普通函数也可以匹配,因为普通函数可以自动类型转换\n\t//但是此时函数模板能够有更好的匹配\n\t//如果函数模板可以产生一个更好的匹配,那么选择模板\n\tcout << MyPlus(c,d);\n}\n\n//函数模板重载\ntemplate\nT MyPlus(T a, T b, T c){\n\tT ret = a + b + c;\n\treturn ret;\n}\n\nvoid test04(){\n\n\tint a = 10;\n\tint b = 20;\n\tint c = 30;\n\tcout << MyPlus(a, b, c) << endl;\n\t//如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数\n}]]>

5.5 模板机制剖析

思考:为什么函数模板可以和普通函数放在一起?c++编译器是如何实现函数模板机制的?

5.5.1编译过程

hello.cpp程序是高级c语言程序,这种程序易于被人读懂。为了在系统上运行hello.c程序,每一条c语句都必须转化为低级的机器指令。然后将这些机器指令打包成可执行目标文件格式,并以二进制形式存储于磁盘中。

预处理(Pre-processing) -> 编译(Compiling) ->汇编(Assembling) -> 链接(Linking)

5.5.2 模板实现机制

函数模板机制结论:

  • 编译器并不是把函数模板处理成能够处理任何类型的函数
  • 函数模板通过具体类型产生不同的函数
  • 编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

5.6模板的局限性

假设有如下模板函数:

       template<class T>

       void f(T a, T b)

       { … }

如果代码实现时定义了赋值操作 a = b,但是T为数组,这种假设就不成立了

同样,如果里面的语句为判断语句 if(a>b),T如果是结构体,该假设也不成立,另外如果是传入的数组,数组名为地址,因此它比较的是地址,而这也不是我们所希望的操作。

       总之,编写的模板函数很可能无法处理某些类型,另一方面,有时候通用化是有意义的,但C++语法不允许这样做。为了解决这种问题,可以提供模板的重载,为这些特定的类型提供具体化的模板。


cppmName = name;\n\t\tthis->mAge = age;\n\t}\n\tstring mName;\n\tint mAge;\n};\n\n//普通交换函数\ntemplate \nvoid mySwap(T &a,T &b)\n{\n\tT temp = a;\n\ta = b; \n\tb = temp;\n}\n//第三代具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型\n//具体化优先于常规模板\ntemplate<>void mySwap(Person &p1, Person &p2)\n{\n\tstring nameTemp;\n\tint ageTemp;\n\n\tnameTemp = p1.mName;\n\tp1.mName = p2.mName;\n\tp2.mName = nameTemp;\n\n\tageTemp = p1.mAge;\n\tp1.mAge = p2.mAge;\n\tp2.mAge = ageTemp;\n\n}\n\nvoid test()\n{\n\tPerson P1(\"Tom\", 10);\n\tPerson P2(\"Jerry\", 20);\n\n\tcout << \"P1 Name = \" << P1.mName << \" P1 Age = \" << P1.mAge << endl;\n\tcout << \"P2 Name = \" << P2.mName << \" P2 Age = \" << P2.mAge << endl;\n\tmySwap(P1, P2);\n\tcout << \"P1 Name = \" << P1.mName << \" P1 Age = \" << P1.mAge << endl;\n\tcout << \"P2 Name = \" << P2.mName << \" P2 Age = \" << P2.mAge << endl;\n}\n\n]]>


5.7 类模板

5.7.1 类模板基本概念

类模板和函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同。

  • 类模板用于实现类所需数据的类型参数化
cpp\nclass Person\n{\npublic:\n\tPerson(NameType name, AgeType age)\n\t{\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid showPerson()\n\t{\n\t\tcout << \"name: \" << this->mName << \" age: \" << this->mAge << endl;\n\t}\npublic:\n\tNameType mName;\n\tAgeType mAge;\n};\n\nvoid test01()\n{\n\t//Person P1(\"德玛西亚\",18); // 类模板不能进行类型自动推导 \n\tPersonP1(\"德玛西亚\", 18);\n\tP1.showPerson();\n}]]>

5.7.2 类模板做函数参数

cpp\nclass Person{\npublic:\n\tPerson(NameType name, AgeType age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid PrintPerson(){\n\t\tcout << \"Name:\" << this->mName << \" Age:\" << this->mAge << endl;\n\t}\npublic:\n\tNameType mName;\n\tAgeType mAge;\n};\n\n//类模板做函数参数\nvoid DoBussiness(Person& p){\n\tp.mAge += 20;\n\tp.mName += \"_vip\";\n\tp.PrintPerson();\n}\n\nint main(){\n\n\tPerson p(\"John\", 30);\n\tDoBussiness(p);\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

5.7.3 类模板派生普通类

cpp\nclass MyClass{\npublic:\n\tMyClass(T property){\n\t\tthis->mProperty = property;\n\t}\npublic:\n\tT mProperty;\n};\n\n//子类实例化的时候需要具体化的父类,子类需要知道父类的具体类型是什么样的\n//这样c++编译器才能知道给子类分配多少内存\n\n//普通派生类\nclass SubClass : public MyClass{\npublic:\n\tSubClass(int b) : MyClass(20){\n\t\tthis->mB = b;\n\t}\npublic:\n\tint mB;\n};]]>

5.7.4 类模板派生类模板

cpp\nclass Base\n{\n\tT m;\n};\ntemplate\nclass Child2 : public Base //继承类模板的时候,必须要确定基类的大小\n{\npublic:\n\tT mParam;\n};\n\nvoid test02()\n{\n\tChild2 d2;\n}]]>

5.7.5 类模板类内实现

cpp\nclass Person\n{\npublic:\n\tPerson(NameType name, AgeType age)\n\t{\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid showPerson()\n\t{\n\t\tcout << \"name: \" << this->mName << \" age: \" << this->mAge << endl;\n\t}\npublic:\n\tNameType mName;\n\tAgeType mAge;\n};\n\nvoid test01()\n{\n\t//Person P1(\"德玛西亚\",18); // 类模板不能进行类型自动推导 \n\tPersonP1(\"德玛西亚\", 18);\n\tP1.showPerson();\n}]]>

5.7.6 类模板类外实现

cpp\n#include\nusing namespace std;\n\ntemplate\nclass Person{\npublic:\n\tPerson(T1 name, T2 age);\n\tvoid showPerson();\n\npublic:\n\tT1 mName;\n\tT2 mAge;\n};\n\n\n//类外实现\ntemplate\nPerson::Person(T1 name, T2 age){\n\tthis->mName = name;\n\tthis->mAge = age;\n}\n\n\ntemplate\nvoid Person::showPerson(){\n\tcout << \"Name:\" << this->mName << \" Age:\" << this->mAge << endl;\n}\n\nvoid test()\n{\n\tPerson p(\"Obama\", 20);\n\tp.showPerson();\n}\n\nint main(){\n\n\ttest();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

5.7.7    模板类碰到友元函数

cpp\nusing namespace std;\n#include \n\ntemplate class Person;\n//告诉编译器这个函数模板是存在\ntemplate void PrintPerson2(Person& p);\n\n//友元函数在类内实现\ntemplate\nclass Person{\n\t//1. 友元函数在类内实现\n\tfriend void PrintPerson(Person& p){\n\t\tcout << \"Name:\" << p.mName << \" Age:\" << p.mAge << endl;\n\t}\n\n\t//2.友元函数类外实现\n\t//告诉编译器这个函数模板是存在\n\tfriend void PrintPerson2<>(Person& p);\n\n\t//3. 类模板碰到友元函数模板\n\ttemplate\n\tfriend void PrintPerson(Person& p);\n\npublic:\n\tPerson(T1 name, T2 age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid showPerson(){\n\t\tcout << \"Name:\" << this->mName << \" Age:\" << this->mAge << endl;\n\t}\nprivate:\n\tT1 mName;\n\tT2 mAge;\n};\n\nvoid test01()\n{\n\tPerson p(\"Jerry\", 20);\n\tPrintPerson(p);\n}\n\n\n// 类模板碰到友元函数\n//友元函数类外实现 加上<>空参数列表,告诉编译去匹配函数模板\ntemplate\nvoid PrintPerson2(Person& p)\n{\n\tcout << \"Name2:\" << p.mName << \" Age2:\" << p.mAge << endl;\n}\n\nvoid test02()\n{\n\tPerson p(\"Jerry\", 20);\n\tPrintPerson2(p); //不写可以编译通过,写了之后,会找PrintPerson2的普通函数调用,因为写了普通函数PrintPerson2的声明\t\n}\n\nint main(){\n\n\t//test01();\n\ttest02();\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

5.8         类模板的应用

设计一个数组模板类(MyArray),完成对不同类型元素的管理

cpp\nclass MyArray\n{\npublic:\n\texplicit MyArray(int capacity)\n\t{\n\t\tthis->m_Capacity = capacity;\n\t\tthis->m_Size = 0;\n\t\t// 如果T是对象,那么这个对象必须提供默认的构造函数\n\t\tpAddress = new T[this->m_Capacity];\n\t}\n\n\t//拷贝构造\n\tMyArray(const MyArray & arr)\n\t{\n\t\tthis->m_Capacity = arr.m_Capacity;\n\t\tthis->m_Size = arr.m_Size;\n\t\tthis->pAddress = new T[this->m_Capacity];\n\t\tfor (int i = 0; i < this->m_Size;i++)\n\t\t{\n\t\t\tthis->pAddress[i] = arr.pAddress[i];\n\t\t}\n\t}\n\n\t//重载[] 操作符 arr[0]\n\tT& operator [](int index)\n\t{\n\t\treturn this->pAddress[index]; \n\t}\n\t//尾插法\n\tvoid Push_back(const T & val)\n\t{\n\t\tif (this->m_Capacity == this->m_Size)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tthis->pAddress[this->m_Size] = val;\n\t\tthis->m_Size++;\n\t}\n\tvoid Pop_back()\n\t{\n\t\tif (this->m_Size == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tthis->m_Size--;\n\t}\n\tint\tgetSize()\n\t{\n\t\treturn this->m_Size;\n\t}\n\t//析构\n\t~MyArray()\n\t{\n\t\tif (this->pAddress != NULL)\n\t\t{\n\t\t\tdelete[] this->pAddress;\n\t\t\tthis->pAddress = NULL;\n\t\t\tthis->m_Capacity = 0; \n\t\t\tthis->m_Size = 0;\n\t\t}\n\t}\n\nprivate:\n\tT * pAddress; //指向一个堆空间,这个空间存储真正的数据\n\tint m_Capacity; //容量\n\tint m_Size; // 大小\n};\n\n测试代码:\n\n\nclass Person{\npublic:\n\tPerson(){}\n\tPerson(string name, int age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\npublic:\n\tstring mName;\n\tint mAge;\n};\n\n\nvoid PrintMyArrayInt(MyArray& arr){\n\tfor (int i = 0; i < arr.getSize(); i++){\n\t\tcout << arr[i] << \" \";\n\t}\n\tcout << endl;\n}\n\nvoid PrintMyPerson(MyArray& personArr)\n{\n\tfor (int i = 0; i < personArr.getSize(); i++){\n\t\tcout << \"姓名:\" << personArr[i].mName << \" 年龄: \" << personArr[i].mAge << endl;\n\t}\n\t\n}\n\t\n\tMyArray myArrayInt(10);\n\tfor (int i = 0; i < 9; i++)\n\t{\n\t\tmyArrayInt.Push_back(i);\n\t}\n\tmyArrayInt.Push_back(100);\n\tPrintMyArrayInt(myArrayInt);\n\nMyArray myArrayPerson(10);\n\tPerson p1(\"德玛西亚\", 30);\n\tPerson p2(\"提莫\", 20);\n\tPerson p3(\"孙悟空\",18);\n\tPerson p4(\"赵信\", 15);\n\tPerson p5(\"赵云\", 24);\n\tmyArrayPerson.Push_back(p1);\n\tmyArrayPerson.Push_back(p2);\n\tmyArrayPerson.Push_back(p3);\n\tmyArrayPerson.Push_back(p4);\n\tmyArrayPerson.Push_back(p5);]]>




","representation":"storage","_expandable":{"content":"/rest/api/content/58954472"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954472","edit":"/pages/resumedraft.action?draftId=58954472","tinyui":"/x/6JKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954472"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954472/child","restrictions":"/rest/api/content/58954472/restriction/byOperation","history":"/rest/api/content/58954472/history","ancestors":"","descendants":"/rest/api/content/58954472/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954473.json b/tools/wiki_data/bodies/58954473.json new file mode 100644 index 0000000..a9390c6 --- /dev/null +++ b/tools/wiki_data/bodies/58954473.json @@ -0,0 +1 @@ +{"id":"58954473","type":"page","status":"current","title":"06.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/58954473/version/1"},"_expandable":{"content":"/rest/api/content/58954473"}},"body":{"storage":{"value":"

类型转换(cast)是将一种数据类型转换成另一种数据类型。例如,如果将一个整型值赋给一个浮点类型的变量,编译器会暗地里将其转换成浮点类型。

转换是非常有用的,但是它也会带来一些问题,比如在转换指针时,我们很可能将其转换成一个比它更大的类型,但这可能会破坏其他的数据。

应该小心类型转换,因为转换也就相当于对编译器说:忘记类型检查,把它看做其他的类型。

一般情况下,尽量少的去使用类型转换,除非用来解决非常特殊的问题。

 

    无论什么原因,任何一个程序如果使用很多类型转换都值得怀疑.

 

标准c++提供了一个显示的转换的语法,来替代旧的C风格的类型转换。

使用C风格的强制转换可以把想要的任何东西转换成我们需要的类型。那为什么还需要一个新的C++类型的强制转换呢?

新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。

 

6.1 静态转换(static_cast)

  • 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
  • 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
  • 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
  • 用于基本数据类型之间的转换,如把int转换成char,把char转换成int。这种转换的安全性也要开发人员来保证。
cpp(a);\n}\n\n//继承关系指针互相转换\nvoid test02(){\n\t//继承关系指针转换\n\tAnimal* animal01 = NULL;\n\tDog* dog01 = NULL;\n\t//子类指针转成父类指针,安全\n\tAnimal* animal02 = static_cast(dog01);\n\t//父类指针转成子类指针,不安全\n\tDog* dog02 = static_cast(animal01);\n}\n\n//继承关系引用相互转换\nvoid test03(){\n\n\tAnimal ani_ref;\n\tDog dog_ref;\n\t//继承关系指针转换\n\tAnimal& animal01 = ani_ref;\n\tDog& dog01 = dog_ref;\n\t//子类指针转成父类指针,安全\n\tAnimal& animal02 = static_cast(dog01);\n\t//父类指针转成子类指针,不安全\n\tDog& dog02 = static_cast(animal01);\n}\n\n//无继承关系指针转换\nvoid test04(){\n\t\n\tAnimal* animal01 = NULL;\n\tOther* other01 = NULL;\n\n\t//转换失败\n\t//Animal* animal02 = static_cast(other01);\n}]]>

2.2 动态转换(dynamic_cast)

  • dynamic_cast主要用于类层次间的上行转换和下行转换;
  • 在类层次间进行上行转换时,dynamic_caststatic_cast的效果是一样的;
  • 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全;
cpp(a);\n}\n\n//继承关系指针\nvoid test02(){\n\n\tAnimal* animal01 = NULL;\n\tDog* dog01 = new Dog;\n\n\t//子类指针转换成父类指针 可以\n\tAnimal* animal02 = dynamic_cast(dog01);\n\tanimal02->ShowName();\n\t//父类指针转换成子类指针 不可以\n\t//Dog* dog02 = dynamic_cast(animal01);\n}\n\n//继承关系引用\nvoid test03(){\n\n\tDog dog_ref;\n\tDog& dog01 = dog_ref;\n\n\t//子类引用转换成父类引用 可以\n\tAnimal& animal02 = dynamic_cast(dog01);\n\tanimal02.ShowName();\n}\n\n//无继承关系指针转换\nvoid test04(){\n\t\n\tAnimal* animal01 = NULL;\n\tOther* other = NULL;\n\n\t//不可以\n\t//Animal* animal02 = dynamic_cast(other);\n}]]>

2.3 常量转换(const_cast)

该运算符用来修改类型的const属性。。

  • 常量指针被转化成非常量指针,并且仍然指向原来的对象;
  • 常量引用被转换成非常量引用,并且仍然指向原来的对象;

 

注意:不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const.

cpp(p);\n\n\tint* pp = NULL;\n\tconst int* npp = const_cast(pp);\n\n\tconst int a = 10; //不能对非指针或非引用进行转换\n\t//int b = const_cast(a); }\n\n//常量引用转换成非常量引用\nvoid test02(){\n\nint num = 10;\n\tint & refNum = num;\n\n\tconst int& refNum2 = const_cast(refNum);\n\t\n}]]>

2.3 重新解释转换(reinterpret_cast)。。。。。

这是最不安全的一种转换机制,最有可能出问题。

主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针.


","representation":"storage","_expandable":{"content":"/rest/api/content/58954473"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954473","edit":"/pages/resumedraft.action?draftId=58954473","tinyui":"/x/6ZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954473"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954473/child","restrictions":"/rest/api/content/58954473/restriction/byOperation","history":"/rest/api/content/58954473/history","ancestors":"","descendants":"/rest/api/content/58954473/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954474.json b/tools/wiki_data/bodies/58954474.json new file mode 100644 index 0000000..e2ceee7 --- /dev/null +++ b/tools/wiki_data/bodies/58954474.json @@ -0,0 +1 @@ +{"id":"58954474","type":"page","status":"current","title":"07.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/58954474/version/1"},"_expandable":{"content":"/rest/api/content/58954474"}},"body":{"storage":{"value":"

7.1 异常基本概念

Bjarne Stroustrup说:提供异常的基本目的就是为了处理上面的问题。基本思想是:让一个函数在发现了自己无法处理的错误时抛出(throw)一个异常,然后它的(直接或者间接)调用者能够处理这个问题。也就是《C++ primer》中说的:将问题检测和问题处理相分离。

一种思想:在所有支持异常处理的编程语言中(例如java),要认识到的一个思想:在异常处理过程中,由问题检测代码可以抛出一个对象给问题处理代码,通过这个对象的类型和内容,实际上完成了两个部分的通信,通信的内容是“出现了什么错误”。当然,各种语言对异常的具体实现有着或多或少的区别,但是这个通信的思想是不变的。


一句话:异常处理就是处理程序中的错误。所谓错误是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等)。


回顾一下:我们以前编写程序是如何处理异常?

C语言的世界中,对错误的处理总是围绕着两种方法:一是使用整型的返回值标识错误;二是使用errno宏(可以简单的理解为一个全局整型变量)去记录错误。当然C++中仍然是可以用这两种方法的。

        这两种方法最大的缺陷就是会出现不一致问题。例如有些函数返回1表示成功,返回0表示出错;而有些函数返回0表示成功,返回非0表示出错。 

        还有一个缺点就是函数的返回值只有一个,你通过函数的返回值表示错误代码,那么函数就不能返回其他的值。当然,你也可以通过指针或者C++的引用来返回另外的值,但是这样可能会令你的程序略微晦涩难懂。


c++异常机制相比C语言异常处理的优势?

  • 函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点。而如果使用C语言的error宏或者函数返回值,调用者都有可能忘记检查,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果。
  • 整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。
  • 整型返回值缺乏相关的上下文信息。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。
  • 异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理。
cpp

7.2 异常语法

7.2.1 异常基本语法

cpp

总结:

  • 若有异常则通过throw操作创建一个异常对象并抛出。
  • 将可能抛出异常的程序段放到try块之中。
  • 如果在try段执行期间没有引起异常,那么跟在try后面的catch字句就不会执行。
  • catch子句会根据出现的先后顺序被检查,匹配的catch语句捕获并处理异常(或继续抛出异常)
  • 如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序。
  • 处理不了的异常,可以在catch的最后一个分支,使用throw,向上抛。

 

c++异常处理使得异常的引发和异常的处理不必在一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理。

7.2.2 异常严格类型匹配

异常机制和函数机制互不干涉,但是捕捉方式是通过严格类型匹配

cpp

7.2.3 栈解旋(unwinding)

异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋(unwinding).

cpp

7.2.4 异常接口声明

  • 为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:void func() throw(A,B,C);这个函数func能够且只能抛出类型A,B,C及其子类型的异常。
  • 如果在函数声明中没有包含异常接口声明,则此函数可以抛任何类型的异常,例如:void func()
  • 一个不抛任何类型异常的函数可声明为:void func() throw()
  • 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序。
cpp

请分别在qt vs linux下做测试! Qt and Linux 正确!

7.2.5 异常变量生命周期

  • throw的异常是有类型的,可以是数字、字符串、类对象。
  • throw的异常是有类型的,catch需严格匹配异常类型。
cpp

7.2.6 异常的多态使用

cpp

7.3 C++标准异常库

7.3.1 标准库介绍

标准库中也提供了很多的异常类,它们是通过类继承组织起来的。异常类继承层级结构图如下:

每个类所在的头文件在图下方标识出来。

标准异常类的成员:

① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。

logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述

③ 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。


标准异常类的具体描述:

异常名称

描述

exception

所有标准异常类的父类

bad_alloc

operator new and operator new[],请求分配内存失败时

bad_exception

这是个特殊的异常,如果函数的异常抛出列表里声明了bad_exception异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为bad_exception类型

bad_typeid

使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常

bad_cast

使用dynamic_cast转换引用失败的时候

ios_base::failure

io操作过程出现错误

logic_error

逻辑错误,可以在运行前检测的错误

runtime_error    

运行时错误,仅在运行时才可以检测的错误

logic_error的子类:

异常名称

描述

length_error

试图生成一个超出该类型最大长度的对象时,例如vectorresize操作

domain_error

参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数

out_of_range

超出有效范围


invalid_argument

参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常

runtime_error的子类:

异常名称

描述

range_error

计算结果超出了有意义的值域范围

overflow_error

算术计算上溢

underflow_error

算术计算下溢

invalid_argument

参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常

cpp\nclass Person{\npublic:\n\tPerson(int age){\n\t\tif (age < 0 || age > 150){\n\t\t\tthrow out_of_range(\"年龄应该在0-150岁之间!\");\n\t\t}\n\t}\npublic:\n\tint mAge;\n};\n\nint main(){\n\n\ttry{\n\t\tPerson p(151);\n\t}\n\tcatch (out_of_range& ex){\n\t\tcout << ex.what() << endl;\n\t}\n\t\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

7.3.2 编写自己的异常类

① 标准库中的异常是有限的;

② 在自己的异常类中,可以添加自己的信息。(标准库中的异常类值允许设置一个用来描述异常的字符串)。

  1. 如何编写自己的异常类?

① 建议自己的异常类要继承标准异常类。因为C++中可以抛出任何类型的异常,所以我们的异常类可以不继承自标准异常,但是这样可能会导致程序混乱,尤其是当我们多人协同开发时。

② 当继承标准异常类时,应该重载父类的what函数和虚析构函数。

③ 因为栈展开的过程中,要复制异常类型,那么要根据你在类中添加的成员考虑是否提供自己的复制构造函数。

cppm_Error = errorInfo;\n\t}\n\n\tMyOutOfRange(const char * errorInfo)\n\t{\n\t\tthis->m_Error = string( errorInfo);\n\t}\n\n\tvirtual ~MyOutOfRange()\n\t{\n\t\n\t}\n\tvirtual const char * what() const\n\t{\n\t\treturn this->m_Error.c_str() ;\n\t}\n\n\tstring m_Error;\n\n};\n\nclass Person\n{\npublic:\n\tPerson(int age)\n\t{\n\t\tif (age <= 0 || age > 150)\n\t\t{\n\t\t\t//抛出异常 越界\n\t\t\t//cout << \"越界\" << endl;\n\t\t\t//throw out_of_range(\"年龄必须在0~150之间\");\n\n\t\t\t//throw length_error(\"长度异常\");\n\t\t\tthrow MyOutOfRange((\"我的异常 年龄必须在0~150之间\"));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis->m_Age = age;\n\t\t}\n\t\t\n\t}\n\n\tint m_Age;\n};\n\n\nvoid test01()\n{\n\ttry\n\t{\n\t\tPerson p(151);\n\t}\n\tcatch ( out_of_range & e )\n\t{\n\t\tcout << e.what() << endl;\n\t}\n\tcatch (length_error & e)\n\t{\n\t\tcout << e.what() << endl;\n\t}\n\tcatch (MyOutOfRange e)\n\t{\n\t\tcout << e.what() << endl;\n\t}\n}]]>


","representation":"storage","_expandable":{"content":"/rest/api/content/58954474"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954474","edit":"/pages/resumedraft.action?draftId=58954474","tinyui":"/x/6pKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954474"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954474/child","restrictions":"/rest/api/content/58954474/restriction/byOperation","history":"/rest/api/content/58954474/history","ancestors":"","descendants":"/rest/api/content/58954474/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954476.json b/tools/wiki_data/bodies/58954476.json new file mode 100644 index 0000000..161463f --- /dev/null +++ b/tools/wiki_data/bodies/58954476.json @@ -0,0 +1 @@ +{"id":"58954476","type":"page","status":"current","title":"08.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/58954476/version/1"},"_expandable":{"content":"/rest/api/content/58954476"}},"body":{"storage":{"value":"

8.1 流的概念和流类库的结构

程序的输入指的是从输入文件将数据传送给程序,程序的输出指的是从程序将数据传送给输出文件。

C++输入输出包含以下三个方面的内容:

    对系统指定的标准设备的输入和输出。即从键盘输入数据,输出到显示器屏幕。这种输入输出称为标准的输入输出,简称标准I/O

    以外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件。以外存文件为对象的输入输出称为文件的输入输出,简称文件I/O

    对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(实际上可以利用该空间存储任何信息)。这种输入和输出称为字符串输入输出,简称串I/O

       C++编译系统提供了用于输入输出的iostream类库。iostream这个单词是由3个部 分组成的,即i-o-stream,意为输入输出流。在iostream类库中包含许多用于输入输出的 类。常用的见表

ios是抽象基类,由它派生出istream类和ostream类,两个类名中第1个字母io分别代表输入(input)和输出(output) istream类支持输入操作,ostream类支持输出操作, iostream类支持输入输出操作。iostream类是从istream类和ostream类通过多重继承而派生的类。其继承层次见上图表示。

C++对文件的输入输出需要用ifstrcamofstream类,两个类名中第1个字母io分别代表输入和输出,第2个字母f代表文件 (file)ifstream支持对文件的输入操作, ofstream支持对文件的输出操作。类ifstream继承了类istream,类ofstream继承了类ostream,类fstream继承了 类iostream。见图

I/O类库中还有其他一些类,但是对于一般用户来说,以上这些已能满足需要了。

iostream类库有关的头文件

iostream类库中不同的类的声明被放在不同的头文件中,用户在自己的程序中用#include命令包含了有关的头文件就相当于在本程序中声明了所需 要用到的类。可以换 —种说法:头文件是程序与类库的接口,iostream类库的接口分别由不同的头文件来实现。常用的有

  • iostream  包含了对输入输出流进行操作所需的基本信息。
  • fstream  用于用户管理的文件的I/O操作。
  • strstream  用于字符串流I/O
  • stdiostream  用于混合使用CC + +I/O机制时,例如想将C程序转变为C++程序。
  • iomanip  在使用格式化I/O时应包含此头文件。

iostream头文件中定义的流对象

iostream 头文件中定义的类有 iosistreamostreamiostreamistream 等。

iostream头文件中不仅定义了有关的类,还定义了4种流对象,

对象

含义

对应设备

对应的类

c语言中相应的标准文件

cin

标准输入流

键盘

istream_withassign

stdin

cout

标准输出流

屏幕

ostream_withassign

stdout

cerr

标准错误流

屏幕

ostream_withassign

stderr

clog

标准错误流

屏幕

ostream_withassign

stderr

 

iostream头文件中定义以上4个流对象用以下的形式(以cout为例):
    ostream cout ( stdout);
      
在定义coutostream流类对象时,把标准输出设备stdout作为参数,这样它就与标准输出设备(显示器)联系起来,如果有
    cout <<3;
就会在显示器的屏幕上输出3

iostream头文件中重载运算符

<<”和“>>”本来在C++中是被定义为左位移运算符和右位移运算符的,由于在iostream头文件中对它们进行了重载, 使它们能用作标准类型数据的输入和输出运算符。所以,在用它们的程序中必须用#include命令把iostream包含到程序中。

    #include <iostream>

  • >>a表示将数据放入a对象中。
  • <<a表示将a对象中存储的数据拿出。

 

 

8.2 标准I/O

标准I/O对象:cincoutcerrclog

cout流对象

cout是console output的缩写,意为在控制台(终端显示器)的输出。强调几点。

1)   cout不是C++预定义的关键字,它是ostream流类的对象,在iostream中定义。 顾       名思义,流是流动的数据,cout流是流向显示器的数据。cout流中的数据是用流插入     运算符“<<”顺序加入的。如果有:
    cout<<"I "<<"study C++ "<<"very hard. <<
hello world !";

按顺序将字符串"I ", "study C++ ", "very hard."插人到cout流中,cout就将它们送   到显示器,在显示器上输出字符串"I study C++ very hard."cout流是容纳数据的载    体,它并不是一个运算符。人们关心的是cout流中的内容,也就是向显示器输出什么。


2)    用“cout<<”输出基本类型的数据时,可以不必考虑数据是什么类型,系统会判断数  据的类型,并根据其类型选择调用与之匹配的运算符重 载函数。这个过程都是自动的,  用户不必干预。如果在C语言中用prinf函数输出不同类型的数据,必须分别指定相应    的输出格式符,十分麻烦,而且容易出错。C++I/O机制对用户来说,显然是方便       而安全的。


  • cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当向cout流插人一个endl时,不论缓冲区是否已满,都立即输出流中所有数据,然后插入一个换行符, 并刷新流(清空缓冲区)。注意如果插人一个换行符”\\n“(如cout<<a<<"\\n"),则只输出和换行,而不刷新cout (但并不是所有编译系统都体现出这一区别)。


  • iostream中只对"<<"">>"运算符用于标准类型数据的输入输出进行了重载,但未对用户声明的类型数据的输入输出进行重载。如果用户声明了新的类型,并希望用"<<"">>"运算符对其进行输入输出,按照重运算符重载来做。

cerr流对象

cerr流对象是标准错误流,cerr流已被指定为与显示器关联。cerr的 作用是向标准错误设备(standard error device)输出有关出错信息。cerr与标准输出流cout的作用和用法差不多。但有一点不同:cout流通常是传送到显示器输出,但也可以被重定向输出到磁盘文件,而cerr流中的信息只能在显示器输出。当调试程序时,往往不希望程序运行时的出错信息被送到其他文件,而要求在显示器上及时输出,这时 应该用cerrcerr流中的信息是用户根据需要指定的。

clog流对象

clog流对象也是标准错误流,它是console log的缩写。它的作用和cerr相同,都是在终端显示器上显示出错信息。区别:cerr是不经过缓冲区,直接向显示器上输出有关信息,而clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出。


缓冲区的概念:

8.3 标准输入流

标准输入流对象cin,重点掌握的函数

cin.get() //一次只能读取一个字符

cin.get(一个参数) //读一个字符

cin.get(两个参数) //可以读字符串

cin.getline()

cin.ignore()

cin.peek()

cin.putback()

cpp= '0' && ch <= '9'){\n\t\tint number;\n\t\tcin >> number;\n\t\tcout << \"数字:\" << number << endl;\n\t}\n\telse{\n\t\tchar buf[64] = { 0 };\n\t\tcin.getline(buf, 64);\n\t\tcout << \"字符串:\" << buf << endl;\n\t}\n}]]>

8.4 标准输出流

8.4.1 字符输出

cout.flush() //刷新缓冲区 Linux下有效

cout.put() //向缓冲区写字符

cout.write() //从buffer中写num个字节到当前输出流中。

cpp 0; i --){\n\t\tcout.write(str, i);\n\t\tcout << endl;\n\t}\n\n}]]>

8.4.2 格式化输出

在输出数据时,为简便起见,往往不指定输出的格式,由系统根据数据的类型采取默认的格式,但有时希望数据按指定的格式输出,如要求以十六进制或八进制形式输出一个整数,对输出的小数只保留两位小数等。有两种方法可以达到此目的。

1)使用控制符的方法;

2)使用流对象的有关成员函数。

4.4.2.1 使用流对象的有关成员函数

通过调用流对象cout中用于控制输出格式的成员函数来控制输出格式。用于控制输出格式的常用的成员函数如下:

流成员函数setf和控制符setiosflags括号中的参数表示格式状态,它是通过格式标志来指定的。格式标志在类ios中被定义为枚举值。因此在引用这些格式标志时要在前面加上类名ios和域运算符“::”。格式标志见表13.5

8.4.2.2 控制符格式化输出

C++提供了在输入输出流中使用的控制符(有的书中称为操纵符)

cpp

8.4.2.3 对程序的几点说明

1) 成员函数width(n)和控制符setw(n)只对其后的第一个输出项有效。如:

cout. width(6);
    cout <<20 <<3.14<<endl;
       输出结果为 203.14

在输出第一个输出项20时,域宽为6,因此在20前面有4个空格,在输出3.14时,width (6)已不起作用,此时按系统默认的域宽输出(按数据实际长度输出)。如果要求在输出数据时都按指定的同一域宽n输出,不能只调用一次width(n), 而必须在输出每一项前都调用一次width(n>,上面的程序中就是这样做的。


2) 在表13.5中的输出格式状态分为5组,每一组中同时只能选用一种(例如dechexoct中只能选一,它们是互相排斥的)。在用成员函数setf和 控制符setiosflags设置输出格式状态后,如果想改设置为同组的另一状态,应当调用成员函数unsetf(对应于成员函数self)或 resetiosflags(对应于控制符setiosflags),先终止原来设置的状态。然后再设置其他状态,大家可以从本程序中看到这点。程序在开 始虽然没有用成员函数self和控制符setiosflags设置用dec输出格式状态,但系统默认指定为dec,因此要改变为hexoct,也应当先 用unsetf 函数终止原来设置。如果删去程序中的第7行和第10行,虽然在第8行和第11行中用成员函数setf设置了hexoct格式,由于未终止dec格式,因 此hexoct的设置均不起作用,系统依然以十进制形式输出。

同理,程序倒数第8行的unsetf 函数的调用也是不可缺少的。


3) 用setf 函数设置格式状态时,可以包含两个或多个格式标志,由于这些格式标志在ios类中被定义为枚举值,每一个格式标志以一个二进位代表,因此可以用位或运算符“|”组合多个格式标志。如倒数第5、第6行可以用下面一行代替:

    cout.setf(ios::internal I ios::showpos);  //包含两个状态标志,用"|"组合


  • 可以看到:对输出格式的控制,既可以用控制符(如例2),也可以用cout流的有关成员函数(如例13.3),二者的作用是相同的。控制符是在头文件iomanip中定义的,因此用控制符时,必须包含iomanip头文件。cout流的成员函数是在头文件iostream 中定义的,因此只需包含头文件iostream,不必包含iomanip。许多程序人员感到使用控制符方便简单,可以在一个cout输出语句中连续使用多种控制符。

8.4 文件读写

8.4.1文件流类和文件流对象

输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的。在实际应用中,常以磁盘文件作为对象。即从磁盘文件读取数据,将数据输出到磁盘文件。

和文件有关系的输入输出类主要在fstream.h这个头文件中被定义,在这个头文件中主要被定义了三个类,由这三个类控制对文件的各种输入输出操作,他们分别是ifstreamofstreamfstream,其中fstream类是由iostream类派生而来,他们之间的继承关系见下图所示:

由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream头文件中是没有像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象。ifstream类,它是从istream类派生的,用来支持从磁盘文件的输入。ofstream类,它是从ostream类派生的,用来支持向磁盘文件的输出。


fstream类,它是从iostream类派生的,用来支持对磁盘文件的输入输出。

8.4.2 C++打开文件

所谓打开(open)文件是一种形象的说法,如同打开房门就可以进入房间活动一样。 打开文件是指在文件读写之前做必要的准备工作,包括:

1)为文件流对象和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件。

2)指定文件的工作方式,如:该文件是作为输入文件还是输出文件,是ASCII文件还是二进制文件等。

以上工作可以通过两种不同的方法实现:

1)   调用文件流的成员函数open。如

           ofstream outfile;  //定义ofstream(输出文件流类)对象outfile

           outfile.open("f1.dat",ios::out);  //使文件流与f1.dat文件建立关联

2行是调用输出文件流的成员函数open打开磁盘文件f1.dat,并指定它为输出文件,   文件流对象outfile将向磁盘文件f1.dat输出数据。ios::outI/O模式的一种,表示    以输出方式打开一个文件。或者简单地说,此时f1.dat是一个输出文件,接收从内存 输出的数据。

磁盘文件名可以包括路径,如"c:\\\\new\\\\f1.dat",如缺省路径,则默认为当前目录下的文件。


2)   在定义文件流对象时指定参数

在声明文件流类时定义了带参数的构造函数,其中包含了打开磁盘文件的功能。因此,       可以在定义文件流对象时指定参数,调用文件流类的构造函数来实现打开文件的功能。

几点说明:
1)
新版本的I/O类库中不提供ios::nocreateios::noreplace

2)
每一个打开的文件都有一个文件指针,该指针的初始位置由I/O方式指定,每次读写都从文件指针的当前位置开始。每读入一个字节,指针就后移一个字节。当文件指针移到最后,就会遇到文件结束EOF(文件结束符也占一个字节,其值为-1),此时流对象的成员函数eof的值为非0(一般设为1),表示文件结束 了。

3)
可以用“位或”运算符“|”对输入输出方式进行组合,如表13.6中最后3行所示那样。还可以举出下面一些例子:
    ios::in | ios:: noreplace  //
打开一个输入文件,若文件不存在则返回打开失败的信息
    ios::app | ios::nocreate  //
打开一个输出文件,在文件尾接着写数据,若文件不存在,则返回打开失败的信息
    ios::out l ios::noreplace  //
打开一个新文件作为输出文件,如果文件已存在则返回打开失败的信息
    ios::in l ios::out I ios::binary  //
打开一个二进制文件,可读可写
但不能组合互相排斥的方式,如 ios::nocreate l ios::noreplace

4)
如果打开操作失败,open函数的返回值为0(),如果是用调用构造函数的方式打开文件的,则流对象的值为0。可以据此测试打开是否成功。如
    if(outfile.open("f1.bat", ios::app) ==0)
        cout <<"open error";

    if( !outfile.open("f1.bat", ios::app) )
        cout <<"open error";


8.4.3 C++关闭文件

在对已打开的磁盘文件的读写操作完成后,应关闭该文件。关闭文件用成员函数close。如:outfile.close( );  //将输出文件流所关联的磁盘文件关闭
所谓关闭,实际上是解除该磁盘文件与文件流的关联,原来设置的工作方式也失效,这样,就不能再通过文件流对该文件进行输入或输出。此时可以将文件流与其他磁盘文件建立关联,通过文件流对新的文件进行输入或输出。如:
    outfile.open("f2.dat",ios::app|ios::nocreate)

   此时文件流outfilef2.dat建立关联,并指定了f2.dat的工作方式。

 

 

8.4.4 C++ASCII文件的读写操作

如果文件的每一个字节中均以ASCII代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件(或称字符文件)。程序可以从ASCII文件中读入若干个字符,也可以向它输出一些字符。

  • 用流插入运算符“<<”和流提取运算符“>>”输入输出标准类型的数据。“<<”和“ >>”都巳在iostream中被重载为能用于ostreamistream类对象的标准类型的输入输出。由于ifstream ofstream分别是ostreamistream类的派生类;因此它们从ostreamistream类继承了公用的重载函数,所以在对磁盘文件的操作中,可以通过文件流对象和流插入运算符“<<”及 流提取运算符“>>”实现对磁盘 文件的读写,如同用cincout<<>>对标准设备进行读写一样。
  • 用文件流的putgetgeiline等成员函数进行字符的输入输出,:用C++流成员函数put输出单个字符、C++ get()函数读入一个字符和C++ getline()函数读入一行字符。
cpp

8.4.5 C++对二进制文件的读写操作

二进制文件不是以ASCII代码存放数据的,它将内存中数据存储形式不加转换地传送到磁盘文件,因此它又称为内存数据的映像文件。因为文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件。

对二进制文件的操作也需要先打开文件,用完后要关闭文件。在打开时要用ios::binary指定为以二进制形式传送和存储。二进制文件除了可以作为输入文件或输出文件外,还可以是既能输入又能输出的文件。这是和ASCII文件不同的地方。


用成员函数readwrite读写二进制文件

对二进制文件的读写主要用istream类的成员函数readwrite来实现。这两个成员函数的原型为
    istream& read(char *buffer,int len);
    ostream& write(const char * buffer,int len);
字符指针buffer指向内存中一段存储空间。len是读写的字节数。调用的方式为:
    a. write(p1,50);
    b. read(p2,30);
      
上面第一行中的a是输出文件流对象,write函数将字符指针p1所给出的地址开始的50个字节的内容不加转换地写到磁盘文件中。在第二行中,b是输入文件流对象,read 函数从b所关联的磁盘文件中,读入30个字节(或遇EOF结束),存放在字符指针p2所指的一段空间内。

cppmName, name);\n\t\tthis->mAge = age;\n\t}\npublic:\n\tchar mName[64];\n\tint mAge;\n};\n\nint main(){\n\n\tchar* fileName = \"person.txt\";\n\t//二进制模式读写文件\n\t//创建文件对象输出流\n\tofstream osm(fileName, ios::out | ios::binary);\n\n\tPerson p1(\"John\",33);\n\tPerson p2(\"Edward\", 34);\n\n\t//Person对象写入文件\n\tosm.write((const char*)&p1,sizeof(Person));\n\tosm.write((const char*)&p2, sizeof(Person));\n\n\t//关闭文件输出流\n\tosm.close();\n\n\t//从文件中读取对象数组\n\tifstream ism(fileName, ios::in | ios::binary);\n\tif (!ism){\n\t\tcout << \"打开失败!\" << endl;\n\t}\n\t\n\tPerson p3;\n\tPerson p4;\n\n\tism.read((char*)&p3, sizeof(Person));\n\tism.read((char*)&p4, sizeof(Person));\n\n\tcout << \"Name:\" << p3.mName << \" Age:\" << p3.mAge << endl;\n\tcout << \"Age:\" << p4.mName << \" Age:\" << p4.mAge << endl;\n\n\t//关闭文件输入流\n\tism.close();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>


","representation":"storage","_expandable":{"content":"/rest/api/content/58954476"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954476","edit":"/pages/resumedraft.action?draftId=58954476","tinyui":"/x/7JKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954476"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954476/child","restrictions":"/rest/api/content/58954476/restriction/byOperation","history":"/rest/api/content/58954476/history","ancestors":"","descendants":"/rest/api/content/58954476/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954485.json b/tools/wiki_data/bodies/58954485.json new file mode 100644 index 0000000..93304f4 --- /dev/null +++ b/tools/wiki_data/bodies/58954485.json @@ -0,0 +1 @@ +{"id":"58954485","type":"page","status":"current","title":"09.STL","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/58954485/version/1"},"_expandable":{"content":"/rest/api/content/58954485"}},"body":{"storage":{"value":"","representation":"storage","_expandable":{"content":"/rest/api/content/58954485"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/display/2CT/09.STL","edit":"/pages/resumedraft.action?draftId=58954485","tinyui":"/x/9ZKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954485"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954485/child","restrictions":"/rest/api/content/58954485/restriction/byOperation","history":"/rest/api/content/58954485/history","ancestors":"","descendants":"/rest/api/content/58954485/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954486.json b/tools/wiki_data/bodies/58954486.json new file mode 100644 index 0000000..d1ca72a --- /dev/null +++ b/tools/wiki_data/bodies/58954486.json @@ -0,0 +1 @@ +{"id":"58954486","type":"page","status":"current","title":"01.STL概论","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/58954486/version/1"},"_expandable":{"content":"/rest/api/content/58954486"}},"body":{"storage":{"value":"

长久以来,软件界一直希望建立一种可重复利用的东西,以及一种得以制造出”可重复运用的东西”的方法,让程序员的心血不止于随时间的迁移,人事异动而烟消云散,从函数(functions),类别(classes),函数库(function libraries),类别库(class libraries)、各种组件,从模块化设计,到面向对象(object oriented ),为的就是复用性的提升。

复用性必须建立在某种标准之上。但是在许多环境下,就连软件开发最基本的数据结构(data structures) 和算法(algorithm)都未能有一套标准。大量程序员被迫从事大量重复的工作,竟然是为了完成前人已经完成而自己手上并未拥有的程序代码,这不仅是人力资源的浪费,也是挫折与痛苦的来源。

为了建立数据结构和算法的一套标准,并且降低他们之间的耦合关系,以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability),诞生了STL


1.1 STL基本概念

STL(Standard Template Library,标准模板库),是惠普实验室开发的一系列软件的统
称。现在主要出现在 c++中,但是在引入 c++之前该技术已经存在很长时间了。
       STL
从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator),容器和算法之间通过迭代器进行无缝连接。STL 几乎所有的代码都采用了模板类或者模板函数,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。STL(Standard Template Library)标准模板库,在我们 c++标准程序库中隶属于 STL 的占到了 80%以上。

1.2 STL六大组件简介

STL提供了六大组件,彼此之间可以组合套用,这六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。

容器:各种数据结构,如vectorlistdequesetmap,用来存放数据,从实现角度来看,STL容器是一种class template

算法:各种常用的算法,如sortfindcopyfor_each。从实现的角度来看,STL算法是一种function tempalte.

迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将operator* , operator-> , operator++,operator--等指针相关操作予以重载的class template. 所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。原生指针(native pointer)也是一种迭代器。

仿函数:行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()class 或者class template

适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。

空间配置器:负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte.


STL六大组件的交互关系,容器通过空间配置器取得数据存储空间,算法通过迭代器存储容器中的内容,仿函数可以协助算法完成不同的策略的变化,适配器可以修饰仿函数。


1.3 STL优点

 

  • STL 是 C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
  • STL 的一个重要特性是将数据和操作分离。数据由容器类别加以管理,操作则由可定制的算法定义。迭代器在两者之间充当“粘合剂”,以使算法可以和容器交互运作
  • 程序员可以不用思考 STL 具体的实现过程,只要能够熟练使用 STL OK 了。这样他们就可以把精力放在程序开发的别的方面。

STL 具有高可重用性,高性能,高移植性,跨平台的优点。
     高可重用性:STL 中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。关于模板的知
识,已经给大家介绍了。
     高性能:如 map 可以高效地从十万条记录里面查找出指定的记录,因为 map 是采用红黑树的变体实现的。
     高移植性:如在项目 A 上用 STL 编写的模块,可以直接移植到项目 B 上。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954486"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954486","edit":"/pages/resumedraft.action?draftId=58954486","tinyui":"/x/9pKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954486"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954486/child","restrictions":"/rest/api/content/58954486/restriction/byOperation","history":"/rest/api/content/58954486/history","ancestors":"","descendants":"/rest/api/content/58954486/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954487.json b/tools/wiki_data/bodies/58954487.json new file mode 100644 index 0000000..ba2b0a9 --- /dev/null +++ b/tools/wiki_data/bodies/58954487.json @@ -0,0 +1 @@ +{"id":"58954487","type":"page","status":"current","title":"02.STL三大组件","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/58954487/version/1"},"_expandable":{"content":"/rest/api/content/58954487"}},"body":{"storage":{"value":"

2.1 容器

容器,置物之所也。

研究数据的特定排列方式,以利于搜索或排序或其他特殊目的,这一门学科我们称为数据结构。大学信息类相关专业里面,与编程最有直接关系的学科,首推数据结构与算法。几乎可以说,任何特定的数据结构都是为了实现某种特定的算法。STL容器就是将运用最广泛的一些数据结构实现出来。

常用的数据结构:数组(array),链表(list),tree(),栈(stack),队列(queue),集合(set),映射表(map),根据数据在容器中的排列特性,这些数据分为序列式容器关联式容器两种。

  • 序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。Vector容器、Deque容器、List容器等。
  • 关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。关联式容器另一个显著特点是:在值中选择一个值作为关键字key,这个关键字对值起到索引的作用,方便查找。Set/multiset容器 Map/multimap容器

2.2 算法

算法,问题之解法也。

以有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms).

广义而言,我们所编写的每个程序都是一个算法,其中的每个函数也都是一个算法,毕竟它们都是用来解决或大或小的逻辑问题或数学问题。STL收录的算法经过了数学上的效能分析与证明,是极具复用价值的,包括常用的排序,查找等等。特定的算法往往搭配特定的数据结构,算法与数据结构相辅相成。

算法分为:质变算法非质变算法

质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等

非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等


再好的编程技巧,也无法让一个笨拙的算法起死回生。


2.3 迭代器

迭代器(iterator)是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物。在<<Design Patterns>>一书中提供了23中设计模式的完整描述,其中iterator模式定义如下:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

迭代器的设计思维-STL的关键所在,STL的中心思想在于将容器(container)和算法(algorithms)分开,彼此独立设计,最后再一贴胶着剂将他们撮合在一起。从技术角度来看,容器和算法的泛型化并不困难,c++class templatefunction template可分别达到目标,如果设计出两这个之间的良好的胶着剂,才是大难题。


迭代器的种类:

输入迭代器

提供对数据的只读访问

只读,支持++==、!=

输出迭代器

提供对数据的只写访问

只写,支持++

前向迭代器

提供读写操作,并能向前推进迭代器

读写,支持++==、!=

双向迭代器

提供读写操作,并能向前和向后操作

读写,支持++--

随机访问迭代器

提供读写操作,并能以跳跃的方式访问容器的任意数据,是功能最强的迭代器

读写,支持++--[n]-n<<=>>=

2.4 案例

cpp\n#include\n#include\nusing namespace std;\n\n//STL 中的容器 算法 迭代器\nvoid test01(){\n\tvector v; //STL 中的标准容器之一 :动态数组\n\tv.push_back(1); //vector 容器提供的插入数据的方法\n\tv.push_back(5);\n\tv.push_back(3);\n\tv.push_back(7);\n\t//迭代器\n\tvector::iterator pStart = v.begin(); //vector 容器提供了 begin()方法 返回指向第一个元素的迭代器\n\tvector::iterator pEnd = v.end(); //vector 容器提供了 end()方法 返回指向最后一个元素下一个位置的迭代器\n\t//通过迭代器遍历\n\twhile (pStart != pEnd){\n\t\tcout << *pStart << \" \";\n\t\tpStart++;\n\t}\n\tcout << endl;\n}\n//STL 容器不单单可以存储基础数据类型,也可以存储类对象\nclass Teacher\n{\npublic:\n\tTeacher(int age) :age(age){};\n\t~Teacher(){};\npublic:\n\tint age;\n};\nvoid test02(){\n\tvector v; //存储 Teacher 类型数据的容器\n\tTeacher t1(10), t2(20), t3(30);\n\tv.push_back(t1);\n\tv.push_back(t2);\n\tv.push_back(t3);\n\tvector::iterator pStart = v.begin();\n\tvector::iterator pEnd = v.end();\n\t//通过迭代器遍历\n\twhile (pStart != pEnd){\n\t\tcout << pStart->age << \" \";\n\t\tpStart++;\n\t}\n\tcout << endl;\n}\n//存储 Teacher 类型指针\nvoid test03(){\n\tvector v; //存储 Teacher 类型指针\n\tTeacher* t1 = new Teacher(10);\n\tTeacher* t2 = new Teacher(20);\n\tTeacher* t3 = new Teacher(30);\n\tv.push_back(t1);\n\tv.push_back(t2);\n\tv.push_back(t3);\n\t//拿到容器迭代器\n\tvector::iterator pStart = v.begin();\n\tvector::iterator pEnd = v.end();\n\t//通过迭代器遍历\n\twhile (pStart != pEnd){\n\t\tcout << (*pStart)->age << \" \";\n\t\tpStart++;\n\t}\n\tcout << endl;\n}\n//容器嵌套容器 难点(不理解,可以跳过)\nvoid test04()\n{\n\tvector< vector > v;\n\tvectorv1;\n\tvectorv2;\n\tvectorv3;\n\n\tfor (int i = 0; i < 5;i++)\n\t{\n\t\tv1.push_back(i);\n\t\tv2.push_back(i * 10);\n\t\tv3.push_back(i * 100);\n\t}\n\tv.push_back(v1);\n\tv.push_back(v2);\n\tv.push_back(v3);\n\n\tfor (vector< vector >::iterator it = v.begin(); it != v.end();it++)\n\t{\n\t\tfor (vector::iterator subIt = (*it).begin(); subIt != (*it).end(); subIt ++)\n\t\t{\n\t\t\tcout << *subIt << \" \";\n\t\t}\n\t\tcout << endl;\n\t}\n} \nint main(){\n\t//test01();\n\t//test02();\n\t//test03();\n\ttest04();\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>


","representation":"storage","_expandable":{"content":"/rest/api/content/58954487"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954487","edit":"/pages/resumedraft.action?draftId=58954487","tinyui":"/x/95KDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954487"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954487/child","restrictions":"/rest/api/content/58954487/restriction/byOperation","history":"/rest/api/content/58954487/history","ancestors":"","descendants":"/rest/api/content/58954487/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954488.json b/tools/wiki_data/bodies/58954488.json new file mode 100644 index 0000000..9ff212b --- /dev/null +++ b/tools/wiki_data/bodies/58954488.json @@ -0,0 +1 @@ +{"id":"58954488","type":"page","status":"current","title":"03.常用容器","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/58954488/version/1"},"_expandable":{"content":"/rest/api/content/58954488"}},"body":{"storage":{"value":"

3.1.1 string容器基本概念

C风格字符串(以空字符结尾的字符数组)太过复杂难于掌握,不适合大程序的开发,所以C++标准库定义了一种string类,定义在头文件<string>

String和c风格字符串对比:

  • Char*是一个指针,String是一个类

string封装了char*,管理这个字符串,是一个char*型的容器。

  • String封装了很多实用的成员方法

查找find,拷贝copy,删除delete 替换replace,插入insert

  • 不用考虑内存释放和越界

 string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。

3.1.2 string容器常用操作

3.1.2.1 string 构造函数

string();//创建一个空的字符串 例如: string str;     

string(const string& str);//使用一个string对象初始化另一个string对象

string(const char* s);//使用字符串s初始化

string(int n, char c);//使用n个字符c初始化

 

3.1.2.2 string基本赋值操作

string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串

string& operator=(const string &s);//把字符串s赋给当前的字符串

string& operator=(char c);//字符赋值给当前的字符串

string& assign(const char *s);//把字符串s赋给当前的字符串

string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串

string& assign(const string &s);//把字符串s赋给当前字符串

string& assign(int n, char c);//用n个字符c赋给当前字符串

string& assign(const string &s, int start, int n);//将sstart开始n个字符赋值给字符串

 

3.1.2.3 string存取字符操作

char& operator[](int n);//通过[]方式取字符

char& at(int n);//通过at方法获取字符

 

3.1.2.4 string拼接操作

string& operator+=(const string& str);//重载+=操作符

string& operator+=(const char* str);//重载+=操作符

string& operator+=(const char c);//重载+=操作符

string& append(const char *s);//把字符串s连接到当前字符串结尾

string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾

string& append(const string &s);//同operator+=()

string& append(const string &s, int pos, int n);//把字符串s中从pos开始的n个字符连接到当前字符串结尾

string& append(int n, char c);//在当前字符串结尾添加n个字符c

 

3.1.2.5 string查找和替换

int find(const string& str, int pos = 0) const; //查找str第一次出现位置,pos开始查找

int find(const char* s, int pos = 0) const;  //查找s第一次出现位置,pos开始查找

int find(const char* s, int pos, int n) const;  //从pos位置查找s的前n个字符第一次位置

int find(const char c, int pos = 0) const;  //查找字符c第一次出现位置

int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,pos开始查找

int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,pos开始查找

int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置

int rfind(const char c, int pos = 0) const; //查找字符c最后一次出现位置

string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str

string& replace(int pos, int n, const char* s); //替换从pos开始的n个字符为字符串s

 

3.1.2.6 string比较操作

/*

compare函数在>时返回 1<时返回 -1==时返回 0

比较区分大小写,比较时参考字典顺序,排越前面的越小。

大写的A比小写的a小。

*/

int compare(const string &s) const;//与字符串s比较

int compare(const char *s) const;//与字符串s比较

3.1.2.7 string子串

string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串

 

3.1.2.8 string插入和删除操作

string& insert(int pos, const char* s); //插入字符串

string& insert(int pos, const string& str); //插入字符串

string& insert(int pos, int n, char c);//在指定位置插入n个字符c

string& erase(int pos, int n = npos);//删除从Pos开始的n个字符

 

 

3.1.2.9 stringc-style字符串转换

//string 转 char*

string str = "itcast";

const char* cstr = str.c_str();

//char* 转 string

char* s = "itcast";

string str(s);

 

提示:

  在c++中存在一个从const char*string的隐式类型转换,却不存在从一个string对象到C_string的自动类型转换。对于string类型的字符串,可以通过c_str()函数返回string对象对应的C_string.

  通常,程序员在整个程序中应坚持使用string类对象,直到必须将内容转化为char*时才将其转换为C_string.

 

提示:

   为了修改string字符串的内容,下标操作符[]at都会返回字符的引用。但当字符串的内存被重新分配之后,可能发生错误.

cpp

3.1.3 小练习

写一个函数,函数内部将string字符串中的所有小写字母都变为大写字母。

3.2 vector容器

3.2.1 vector容器基本概念

vector的数据安排以及操作方式,与array非常相似,两者的唯一差别在于空间的运用的灵活性。Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就要求一个大块头的array了。

Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高,应该加入某种未雨绸缪的考虑,稍后我们便可以看到vector的空间配置策略。

3.2.2 vector迭代器

Vector维护一个线性空间,所以不论元素的型别如何,普通指针都可以作为vector的迭代器,因为vector迭代器所需要的操作行为,如operaroe*, operator->, operator++, operator--, operator+, operator-, operator+=, operator-=, 普通指针天生具备。Vector支持随机存取,而普通指针正有着这样的能力。所以vector提供的是随机访问迭代器(Random Access Iterators).

根据上述描述,如果我们写如下的代码:

Vector<int>::iterator it1;

Vector<Teacher>::iterator it2;

it1的型别其实就是Int*,it2的型别其实就是Teacher*.


cpp\n#include\nusing namespace std;\n\nint main(){\n\n\tvector v;\n\tfor (int i = 0; i < 10;i ++){\n\t\tv.push_back(i);\n\t\tcout << v.capacity() << endl; // v.capacity()容器的容量\n\t}\n\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

3.2.3 vector的数据结构

Vector所采用的数据结构非常简单,线性连续空间,它以两个迭代器_Myfirst和_Mylast分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器_Myend指向整块连续内存空间的尾端。

为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求大一些,以备将来可能的扩充,这边是容量的概念。换句话说,一个vector的容量永远大于或等于其大小,一旦容量等于大小,便是满载,下次再有新增元素,整个vector容器就得另觅居所。


注意:

   所谓动态增加大小,并不是在原空间之后续接新空间(因为无法保证原空间之后尚有可配置的空间),而是一块更大的内存空间,然后将原数据拷贝新空间,并释放原空间。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。这是程序员容易犯的一个错误,务必小心。


3.2.4 vector常用API操作

3.2.4.1 vector构造函数

vector<T> v; //采用模板实现类实现,默认构造函数

vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。

vector(n, elem);//构造函数将nelem拷贝给本身。

vector(const vector &vec);//拷贝构造函数。


//例子 使用第二个构造函数 我们可以...

int arr[] = {2,3,4,1,9};

vector<int> v1(arr, arr + sizeof(arr) / sizeof(int));

 

3.2.4.2 vector常用赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将nelem拷贝赋值给本身。

vector& operator=(const vector  &vec);//重载等号操作符

swap(vec);// 将vec与本身的元素互换。

 

3.2.4.3 vector大小操作

size();//返回容器中元素的个数

empty();//判断容器是否为空

resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长>度的元素被删除。

capacity();//容器的容量

reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。

 

3.2.4.4 vector数据存取操作

at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。

operator[];//返回索引idx所指的数据,越界时,运行直接报错

front();//返回容器中第一个数据元素

back();//返回容器中最后一个数据元素

 

3.2.4.5 vector插入和删除操作

insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele.

push_back(ele); //尾部插入元素ele

pop_back();//删除最后一个元素

erase(const_iterator start, const_iterator end);//删除迭代器从startend之间的元素

erase(const_iterator pos);//删除迭代器指向的元素

clear();//删除容器中所有元素

 

 

3.2.5 vector小案例

3.2.5.1巧用swap,收缩内存空间

cpp\n#include\nusing namespace std;\n\nint main(){\n\n\tvector v;\n\tfor (int i = 0; i < 100000;i ++){\n\t\tv.push_back(i);\n\t}\n\n\tcout << \"capacity:\" << v.capacity() << endl;\n\tcout << \"size:\" << v.size() << endl;\n\n\t//此时 通过resize改变容器大小\n\tv.resize(10);\n\n\tcout << \"capacity:\" << v.capacity() << endl;\n\tcout << \"size:\" << v.size() << endl;\n\n\t//容量没有改变\n\tvector(v).swap(v);\n\n\tcout << \"capacity:\" << v.capacity() << endl;\n\tcout << \"size:\" << v.size() << endl;\n\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

3.2.5.2 reserve预留空间

cpp\n#include\nusing namespace std;\n\nint main(){\n\n\tvector v;\n\n\t//预先开辟空间\n\tv.reserve(100000);\n\n\tint* pStart = NULL;\n\tint count = 0;\n\tfor (int i = 0; i < 100000;i ++){\n\t\tv.push_back(i);\n\t\tif (pStart != &v[0]){\n\t\t\tpStart = &v[0];\n\t\t\tcount++;\n\t\t}\n\t}\n\n\tcout << \"count:\" << count << endl;\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

3.3 deque容器

3.3.1 deque容器基本概念

Vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。

Deque容器和vector容器最大的差异,一在于deque允许使用常数项时间对头端进行元素的插入和删除操作。二在于deque没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,换句话说,像vector那样,”旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在deque身上是不会发生的。也因此,deque没有必须要提供所谓的空间保留(reserve)功能.

虽然deque容器也提供了Random Access Iterator,但是它的迭代器并不是普通的指针,其复杂度和vector不是一个量级,这当然影响各个运算的层面。因此,除非有必要,我们应该尽可能的使用vector,而不是deque。对deque进行的排序操作,为了最高效率,可将deque先完整的复制到一个vector中,对vector容器进行排序,再复制回deque.

3.3.2 deque容器实现原理

Deque容器是连续的空间,至少逻辑上看来如此,连续现行空间总是令我们联想到arrayvector,array无法成长,vector虽可成长,却只能向尾端成长,而且其成长其实是一个假象,事实上(1) 申请更大空间 (2)原数据复制新空间 (3)释放原空间 三步骤,如果不是vector每次配置新的空间时都留有余裕,其成长假象所带来的代价是非常昂贵的。

Deque是由一段一段的定量的连续空间构成。一旦有必要在deque前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在deque的头端或者尾端。Deque最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口,避开了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构。

既然deque是分段连续内存空间,那么就必须有中央控制,维持整体连续的假象,数据结构的设计及迭代器的前进后退操作颇为繁琐。Deque代码的实现远比vectorlist都多得多。

Deque采取一块所谓的map(注意,不是STLmap容器)作为主控,这里所谓的map是一小块连续的内存空间,其中每一个元素(此处成为一个结点)都是一个指针,指向另一段连续性内存空间,称作缓冲区。缓冲区才是deque的存储空间的主体。

3.3.3 deque常用API

3.3.3.1 deque构造函数

deque<T> deqT;//默认构造形式

deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。

deque(n, elem);//构造函数将nelem拷贝给本身。

deque(const deque &deq);//拷贝构造函数。

 

3.3.3.2 deque赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将nelem拷贝赋值给本身。

deque& operator=(const deque &deq); //重载等号操作符

swap(deq);// 将deq与本身的元素互换

 

3.3.3.3 deque大小操作

deque.size();//返回容器中元素的个数

deque.empty();//判断容器是否为空

deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。

 

3.3.3.4 deque双端插入和删除操作

push_back(elem);//在容器尾部添加一个数据

push_front(elem);//在容器头部插入一个数据

pop_back();//删除容器最后一个数据

pop_front();//删除容器第一个数据

 

3.3.3.5 deque数据存取

at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range

operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。

front();//返回第一个数据。

back();//返回最后一个数据

3.3.3.6 deque插入操作

insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置。

insert(pos,n,elem);//在pos位置插入nelem数据,无返回值。

insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

3.3.3.7 deque删除操作

clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。

erase(pos);//删除pos位置的数据,返回下一个数据的位置。

 

3.3.3.8 案例(作业)

 

/*

    有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分。

    //1. 创建五名选手,放到vector中

    //2. 遍历vector容器,取出来每一个选手,执行for循环,可以把10个评分打分存到deque容器中

    //3. sort算法对deque容器中分数排序,pop_back pop_front去除最高和最低分

    //4. deque容器遍历一遍,累加分数,累加分数/d.size()

    //5. person.score = 平均分

*/


 

3.4 stack容器

3.4.1 stack容器基本概念

stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口,形式如图所示。stack容器允许新增元素,移除元素,取得栈顶元素,但是除了最顶端外,没有任何其他方法可以存取stack的其他元素。换言之,stack不允许有遍历行为。

有元素推入栈的操作称为:push,将元素推出stack的操作称为pop.

3.4.2 stack没有迭代器

Stack所有元素的进出都必须符合”先进后出”的条件,只有stack顶端的元素,才有机会被外界取用。Stack不提供遍历功能,也不提供迭代器。

3.4.3 stack常用API

3.4.3.1 stack构造函数

stack<T> stkT;//stack采用模板类实现, stack对象的默认构造形式:

stack(const stack &stk);//拷贝构造函数

3.4.3.2 stack赋值操作

stack& operator=(const stack &stk);//重载等号操作符

3.4.3.3 stack数据存取操作

push(elem);//向栈顶添加元素

pop();//从栈顶移除第一个元素

top();//返回栈顶元素

3.4.3.4 stack大小操作

empty();//判断堆栈是否为空

size();//返回堆栈的大小

 

3.5 queue容器

3.5.1 queue容器基本概念

Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口,queue容器允许从一端新增元素,从另一端移除元素。

3.5.2 queue没有迭代器

Queue所有元素的进出都必须符合”先进先出”的条件,只有queue的顶端元素,才有机会被外界取用。Queue不提供遍历功能,也不提供迭代器。


3.5.3 queue常用API

3.5.3.1 queue构造函数

queue<T> queT;//queue采用模板类实现,queue对象的默认构造形式:

queue(const queue &que);//拷贝构造函数

3.5.3.2 queue存取、插入和删除操作

push(elem);//往队尾添加元素

pop();//从队头移除第一个元素

back();//返回最后一个元素

front();//返回第一个元素

 

3.5.3.3 queue赋值操作

queue& operator=(const queue &que);//重载等号操作符

3.5.3.4 queue大小操作

empty();//判断队列是否为空

size();//返回队列的大小

 

 

3.6 list容器

3.6.1 list容器基本概念

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

相较于vector的连续线性空间,list就显得负责许多,它的好处是每次插入或者删除一个元素,就是配置或者释放一个元素的空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素的移除,list永远是常数时间。

List和vector是两个最常被使用的容器。

List容器是一个双向链表。

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
  • 链表灵活,但是空间和时间额外耗费较大

3.6.2 list容器的迭代器

List容器不能像vector一样以普通指针作为迭代器,因为其节点不能保证在同一块连续的内存空间上。List迭代器必须有能力指向list的节点,并有能力进行正确的递增、递减、取值、成员存取操作。所谓”list正确的递增,递减、取值、成员取用”是指,递增时指向下一个节点,递减时指向上一个节点,取值时取的是节点的数据值,成员取用时取的是节点的成员。

由于list是一个双向链表,迭代器必须能够具备前移、后移的能力,所以list容器提供的是Bidirectional Iterators.

List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效。这在vector是不成立的,因为vector的插入操作可能造成记忆体重新配置,导致原有的迭代器全部失效,甚至List元素的删除,也只有被删除的那个元素的迭代器失效,其他迭代器不受任何影响。

3.6.3 list容器的数据结构

list容器不仅是一个双向链表,而且还是一个循环的双向链表。

cpp\n#include\nusing namespace std;\n\nint main(){\n\n\tlist myList;\n\tfor (int i = 0; i < 10; i ++){\n\t\tmyList.push_back(i);\n\t}\n\n\tlist::_Nodeptr node = myList._Myhead->_Next;\n\n\tfor (int i = 0; i < myList._Mysize * 2;i++){\n\t\tcout << \"Node:\" << node->_Myval << endl;\n\t\tnode = node->_Next;\n\t\tif (node == myList._Myhead){\n\t\t\tnode = node->_Next;\n\t\t}\n\t}\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

3.6.4 list常用API

3.6.4.1 list构造函数

list<T> lstT;//list采用采用模板类实现,对象的默认构造形式:

list(beg,end);//构造函数将[beg, end)区间中的元素拷贝给本身。

list(n,elem);//构造函数将nelem拷贝给本身。

list(const list &lst);//拷贝构造函数。

3.6.4.2 list数据元素插入和删除操作

push_back(elem);//在容器尾部加入一个元素

pop_back();//删除容器中最后一个元素

push_front(elem);//在容器开头插入一个元素

pop_front();//从容器开头移除第一个元素

insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。

insert(pos,n,elem);//在pos位置插入nelem数据,无返回值。

insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。

erase(pos);//删除pos位置的数据,返回下一个数据的位置。

remove(elem);//删除容器中所有与elem值匹配的元素。

 

 

3.6.4.3 list大小操作

size();//返回容器中元素的个数

empty();//判断容器是否为空

resize(num);//重新指定容器的长度为num

若容器变长,则以默认值填充新位置。

如果容器变短,则末尾超出容器长度的元素被删除。

resize(num, elem);//重新指定容器的长度为num

若容器变长,则以elem值填充新位置。

如果容器变短,则末尾超出容器长度的元素被删除。

 

3.6.4.4 list赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将nelem拷贝赋值给本身。

list& operator=(const list &lst);//重载等号操作符

swap(lst);//将lst与本身的元素互换。

3.6.4.5 list数据的存取

front();//返回第一个元素。

back();//返回最后一个元素。

3.6.4.6 list反转排序

reverse();//反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素。

sort(); //list排序

 

3.7 set/multiset容器

3.7.1 set/multiset容器基本概念

3.7.1.1 set容器基本概念

Set的特性是。所有元素都会根据元素的键值自动被排序。Set的元素不像map那样可以同时拥有实值和键值,set的元素即是键值又是实值。Set不允许两个元素有相同的键值。

我们可以通过set的迭代器改变set元素的值吗?不行,因为set元素值就是其键值,关系到set元素的排序规则。如果任意改变set元素值,会严重破坏set组织。换句话说,setiterator是一种const_iterator.

set拥有和list某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前所有的迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外。

3.7.1.2 multiset容器基本概念

multiset特性及用法和set完全相同,唯一的差别在于它允许键值重复。setmultiset的底层实现是红黑树,红黑树为平衡二叉树的一种。

树的简单知识:

二叉树就是任何节点最多只允许有两个字节点。分别是左子结点和右子节点。

二叉搜索树,是指二叉树中的节点按照一定的规则进行排序,使得对二叉树中元素访问更加高效。二叉搜索树的放置规则是:任何节点的元素值一定大于其左子树中的每一个节点的元素值,并且小于其右子树的值。因此从根节点一直向左走,一直到无路可走,即得到最小值,一直向右走,直至无路可走,可得到最大值。那么在儿茶搜索树中找到最大元素和最小元素是非常简单的事情。下图为二叉搜索树:

上面我们介绍了二叉搜索树,那么当一个二叉搜索树的左子树和右子树不平衡的时候,那么搜索依据上图表示,搜索9所花费的时间要比搜索17所花费的时间要多,由于我们的输入或者经过我们插入或者删除操作,二叉树失去平衡,造成搜索效率降低。

所以我们有了一个平衡二叉树的概念,所谓的平衡不是指的完全平衡。

RB-tree(红黑树)为二叉树的一种。


3.7.2 set常用API

3.7.2.1 set构造函数

set<T> st;//set默认构造函数:

mulitset<T> mst; //multiset默认构造函数:

set(const set &st);//拷贝构造函数

3.7.2.2 set赋值操作

set& operator=(const set &st);//重载等号操作符

swap(st);//交换两个集合容器

3.7.2.3 set大小操作

size();//返回容器中元素的数目

empty();//判断容器是否为空

 

3.7.2.4 set插入和删除操作

insert(elem);//在容器中插入元素。

clear();//清除所有元素

erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。

erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

erase(elem);//删除容器中值为elem的元素。

 

3.7.2.5 set查找操作

find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();

count(key);//查找键key的元素个数

lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器。

upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。

equal_range(keyElem);//返回容器中keykeyElem相等的上下限的两个迭代器。

set的返回值    指定set排序规则:



cpp s;\n\tpair::iterator,bool> ret = s.insert(10);\n\tif (ret.second){\n\t\tcout << \"插入成功:\" << *ret.first << endl;\n\t}\n\telse{\n\t\tcout << \"插入失败:\" << *ret.first << endl;\n\t}\n\t\n\tret = s.insert(10);\n\tif(ret.second){\n\t\tcout << \"插入成功:\" << *ret.first << endl;\n\t}\n\telse{\n\t\tcout << \"插入失败:\" << *ret.first << endl;\n\t}\n\n}\n\nstruct MyCompare02{\n\tbool operator()(int v1,int v2){\n\t\treturn v1 > v2;\n\t}\n};\n\n//set从大到小\nvoid test02(){\n\n\tsrand((unsigned int)time(NULL));\n\t//我们发现set容器的第二个模板参数可以设置排序规则,默认规则是less<_Kty>\n\tset s;\n\tfor (int i = 0; i < 10;i++){\n\t\ts.insert(rand() % 100);\n\t}\n\t\n\tfor (set::iterator it = s.begin(); it != s.end(); it ++){\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n}\n\n//set容器中存放对象\nclass Person{\npublic:\n\tPerson(string name,int age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\npublic:\n\tstring mName;\n\tint mAge;\n};\n\n\nstruct MyCompare03{\n\tbool operator()(const Person& p1,const Person& p2){\n\t\treturn p1.mAge > p2.mAge;\n\t}\n};\n\nvoid test03(){\n\n\tset s;\n\n\tPerson p1(\"aaa\", 20);\n\tPerson p2(\"bbb\", 30);\n\tPerson p3(\"ccc\", 40);\n\tPerson p4(\"ddd\", 50);\n\n\ts.insert(p1);\n\ts.insert(p2);\n\ts.insert(p3);\n\ts.insert(p4);\n\n\tfor (set::iterator it = s.begin(); it != s.end(); it++){\n\t\tcout << \"Name:\" << it->mName << \" Age:\" << it->mAge << endl;\n\t}\n\n}]]>

3.7.3 对组(pair)

对组(pair)将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有属性firstsecond访问。

类模板:template <class T1, class T2> struct pair.

如何创建对组?

//第一种方法创建一个对组

pair<string, int> pair1(string("name"), 20);

cout << pair1.first << endl; //访问pair第一个值

cout << pair1.second << endl;//访问pair第二个值

//第二种

pair<string, int> pair2 = make_pair("name", 30);

cout << pair2.first << endl;

cout << pair2.second << endl;

//pair=赋值

pair<string, int> pair3 = pair2;

cout << pair3.first << endl;

cout << pair3.second << endl;


3.8 map/multimap容器

3.8.1 map/multimap基本概念

Map的特性是,所有元素都会根据元素的键值自动排序。Map所有的元素都是pair,同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值。

我们可以通过map的迭代器改变map的键值吗?答案是不行,因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的。

Map和list拥有相同的某些性质,当对它的容器元素进行新增操作或者删除操作时,操作之前的所有迭代器,在操作完成之后依然有效,当然被删除的那个元素的迭代器必然是个例外。

Multimap和map的操作类似,唯一区别multimap键值可重复。

Map和multimap都是以红黑树为底层实现机制。

3.8.2 map/multimap常用API

3.8.2.1 map构造函数

map<T1, T2> mapTT;//map默认构造函数:

map(const map &mp);//拷贝构造函数

 

 

3.8.2.2 map赋值操作

map& operator=(const map &mp);//重载等号操作符

swap(mp);//交换两个集合容器

 

3.8.2.3 map大小操作

size();//返回容器中元素的数目

empty();//判断容器是否为空

3.8.2.4 map插入数据元素操作

map.insert(...); //往容器插入元素,返回pair<iterator,bool>

map<int, string> mapStu;

// 第一种 通过pair的方式插入对象

mapStu.insert(pair<int, string>(3, "小张"));

// 第二种 通过pair的方式插入对象

mapStu.inset(make_pair(-1, "校长"));

// 第三种 通过value_type的方式插入对象

mapStu.insert(map<int, string>::value_type(1, "小李"));

// 第四种 通过数组的方式插入值

mapStu[3] = "小刘";

mapStu[5] = "小王";

3.8.2.5 map删除操作

clear();//删除所有元素

erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。

erase(beg,end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

erase(keyElem);//删除容器中keykeyElem的对组。

3.8.2.6 map查找操作

find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;/若不存在,返回map.end();

count(keyElem);//返回容器中keykeyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1

lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器。

upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。

equal_range(keyElem);//返回容器中keykeyElem相等的上下限的两个迭代器。

3.8.3 multimap案例

//公司今天招聘了5个员工,5名员工进入公司之后,需要指派员工在那个部门工作

//人员信息有: 姓名 年龄 电话 工资等组成

//通过Multimap进行信息的插入 保存 显示

//分部门显示员工信息 显示全部员工信息

cpp\n#include\n#include\n#include\nusing namespace std;\n\n//multimap 案例\n//公司今天招聘了 5 个员工,5 名员工进入公司之后,需要指派员工在那个部门工作\n//人员信息有: 姓名 年龄 电话 工资等组成\n//通过 Multimap 进行信息的插入 保存 显示\n//分部门显示员工信息 显示全部员工信息\n\n\n#define SALE_DEPATMENT 1 //销售部门\n#define DEVELOP_DEPATMENT 2 //研发部门\n#define FINACIAL_DEPATMENT 3 //财务部门\n#define ALL_DEPATMENT 4 //所有部门\n\n//员工类\nclass person{\npublic:\n\tstring name; //员工姓名\n\tint age; //员工年龄\n\tdouble salary; //员工工资\n\tstring tele; //员工电话\n};\n\n//创建5个员工\nvoid CreatePerson(vector& vlist){\n\n\tstring seed = \"ABCDE\";\n\tfor (int i = 0; i < 5; i++){\n\t\tperson p;\n\t\tp.name = \"员工\";\n\t\tp.name += seed[i];\n\t\tp.age = rand() % 30 + 20;\n\t\tp.salary = rand() % 20000 + 10000;\n\t\tp.tele = \"010-8888888\";\n\t\tvlist.push_back(p);\n\t}\n\n}\n\n//5名员工分配到不同的部门\nvoid PersonByGroup(vector& vlist, multimap& plist){\n\n\n\tint operate = -1; //用户的操作\n\n\tfor (vector::iterator it = vlist.begin(); it != vlist.end(); it++){\n\n\t\tcout << \"当前员工信息:\" << endl;\n\t\tcout << \"姓名:\" << it->name << \" 年龄:\" << it->age << \" 工资:\" << it->salary << \" 电话:\" << it->tele << endl;\n\t\tcout << \"请对该员工进行部门分配(1 销售部门, 2 研发部门, 3 财务部门):\" << endl;\n\t\tscanf(\"%d\", &operate);\n\n\t\twhile (true){\n\n\t\t\tif (operate == SALE_DEPATMENT){ //将该员工加入到销售部门\n\t\t\t\tplist.insert(make_pair(SALE_DEPATMENT, *it));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (operate == DEVELOP_DEPATMENT){\n\t\t\t\tplist.insert(make_pair(DEVELOP_DEPATMENT, *it));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (operate == FINACIAL_DEPATMENT){\n\t\t\t\tplist.insert(make_pair(FINACIAL_DEPATMENT, *it));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse{\n\t\t\t\tcout << \"您的输入有误,请重新输入(1 销售部门, 2 研发部门, 3 财务部门):\" << endl;\n\t\t\t\tscanf(\"%d\", &operate);\n\t\t\t}\n\n\t\t}\n\n\t}\n\tcout << \"员工部门分配完毕!\" << endl;\n\tcout << \"***********************************************************\" << endl;\n\n}\n\n//打印员工信息\nvoid printList(multimap& plist, int myoperate){\n\n\tif (myoperate == ALL_DEPATMENT){\n\t\tfor (multimap::iterator it = plist.begin(); it != plist.end(); it++){\n\t\t\tcout << \"姓名:\" << it->second.name << \" 年龄:\" << it->second.age << \" 工资:\" << it->second.salary << \" 电话:\" << it->second.tele << endl;\n\t\t}\n\t\treturn;\n\t}\n\n\tmultimap::iterator it = plist.find(myoperate);\n\tint depatCount = plist.count(myoperate);\n\tint num = 0;\n\tif (it != plist.end()){\n\t\twhile (it != plist.end() && num < depatCount){\n\t\t\tcout << \"姓名:\" << it->second.name << \" 年龄:\" << it->second.age << \" 工资:\" << it->second.salary << \" 电话:\" << it->second.tele << endl;\n\t\t\tit++;\n\t\t\tnum++;\n\t\t}\n\t}\n}\n\n//根据用户操作显示不同部门的人员列表\nvoid ShowPersonList(multimap& plist, int myoperate){\n\n\tswitch (myoperate)\n\t{\n\tcase SALE_DEPATMENT:\n\t\tprintList(plist, SALE_DEPATMENT);\n\t\tbreak;\n\tcase DEVELOP_DEPATMENT:\n\t\tprintList(plist, DEVELOP_DEPATMENT);\n\t\tbreak;\n\tcase FINACIAL_DEPATMENT:\n\t\tprintList(plist, FINACIAL_DEPATMENT);\n\t\tbreak;\n\tcase ALL_DEPATMENT:\n\t\tprintList(plist, ALL_DEPATMENT);\n\t\tbreak;\n\t}\n}\n\n//用户操作菜单\nvoid PersonMenue(multimap& plist){\n\n\tint flag = -1;\n\tint isexit = 0;\n\twhile (true){\n\t\tcout << \"请输入您的操作((1 销售部门, 2 研发部门, 3 财务部门, 4 所有部门, 0退出):\" << endl;\n\t\tscanf(\"%d\", &flag);\n\n\t\tswitch (flag)\n\t\t{\n\t\tcase SALE_DEPATMENT:\n\t\t\tShowPersonList(plist, SALE_DEPATMENT);\n\t\t\tbreak;\n\t\tcase DEVELOP_DEPATMENT:\n\t\t\tShowPersonList(plist, DEVELOP_DEPATMENT);\n\t\t\tbreak;\n\t\tcase FINACIAL_DEPATMENT:\n\t\t\tShowPersonList(plist, FINACIAL_DEPATMENT);\n\t\t\tbreak;\n\t\tcase ALL_DEPATMENT:\n\t\t\tShowPersonList(plist, ALL_DEPATMENT);\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tisexit = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcout << \"您的输入有误,请重新输入!\" << endl;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (isexit == 1){\n\t\t\tbreak;\n\t\t}\n\t}\n\n}\n\nint main(){\n\n\tvector vlist; //创建的5个员工 未分组\n\tmultimap plist; //保存分组后员工信息\n\n\t//创建5个员工\n\tCreatePerson(vlist);\n\t//5名员工分配到不同的部门\n\tPersonByGroup(vlist, plist);\n\t//根据用户输入显示不同部门员工信息列表 或者 显示全部员工的信息列表\n\tPersonMenue(plist);\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]>

3.9 STL容器使用时机

 

vector

deque

list

set

multiset

map

multimap

典型内存结构

单端数组

双端数组

双向链表

二叉树

二叉树

二叉树

二叉树

可随机存取

key而言:不是

元素搜寻速度

非常慢

key而言:快

key而言:快

元素安插移除

尾端

头尾两端

任何位置

-

-

-

-

 


  • vector的使用场景:比如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述。
  • deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。

     vector与deque的比较:

      一:vector.at()deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置 却是不固定的。

    二:如果有大量释放操作的话,vector花的时间更少,这跟二者的内部实现有关。

    三:deque支持头部的快速插入与快速移除,这是deque的优点。

  • list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
  • set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
  • map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。




","representation":"storage","_expandable":{"content":"/rest/api/content/58954488"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954488","edit":"/pages/resumedraft.action?draftId=58954488","tinyui":"/x/_JKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954488"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954488/child","restrictions":"/rest/api/content/58954488/restriction/byOperation","history":"/rest/api/content/58954488/history","ancestors":"","descendants":"/rest/api/content/58954488/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954498.json b/tools/wiki_data/bodies/58954498.json new file mode 100644 index 0000000..d62a85e --- /dev/null +++ b/tools/wiki_data/bodies/58954498.json @@ -0,0 +1 @@ +{"id":"58954498","type":"page","status":"current","title":"04.常用算法","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/58954498/version/1"},"_expandable":{"content":"/rest/api/content/58954498"}},"body":{"storage":{"value":"

4.1 函数对象

重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载“()”操作符,使得类对象可以像函数那样调用。

注意:

1.函数对象(仿函数)是一个类,不是一个函数。

2.函数对象(仿函数)重载了”() ”操作符使得它可以像函数一样调用。

分类:假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为“一元仿函数unary functor);相反,如果重载的operator()要求获取两个参数,就将这个类称为“二元仿函数binary functor)。

函数对象的作用主要是什么?STL提供的算法往往都有两个版本,其中一个版本表现出最常用的某种运算,另一版本则允许用户通过template参数的形式来指定所要采取的策略。

cpp

总结:

1、函数对象通常不定义构造函数和析构函数,所以在构造和析构时不会发生任何问题,避免了函数调用的运行时问题。

2、函数对象超出普通函数的概念,函数对象可以有自己的状态

3、函数对象可内联编译,性能好。用函数指针几乎不可能

4、模版函数对象使函数对象具有通用性,这也是它的优势之一 


4.2 谓词

谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接受一个参数,那么叫做一元谓词,如果接受两个参数,那么叫做二元谓词,谓词可作为一个判断式

cpp 5;\n\t}\n\n};\n//一元谓词\nvoid test01()\n{\n\tvector v;\n\tfor (int i = 0; i < 10;i ++)\n\t{\n\t\tv.push_back(i);\n\t}\n\t\n\t vector::iterator it = find_if(v.begin(), v.end(), GreaterThenFive());\n\t if (it == v.end())\n\t {\n\t\t cout << \"没有找到\" << endl;\n\t }\n\t else\n\t {\n\t\t cout << \"找到了: \" << *it << endl;\n\t }\n}\n\n//二元谓词\nclass MyCompare\n{\npublic:\n\tbool operator()(int num1, int num2)\n\t{\n\t\treturn num1 > num2;\n\t}\n};\n\nvoid test02()\n{\n\tvector v;\n\tv.push_back(10);\n\tv.push_back(40);\n\tv.push_back(20);\n\tv.push_back(90);\n\tv.push_back(60);\n\n\t//默认从小到大\n\tsort(v.begin(), v.end());\n\tfor (vector::iterator it = v.begin(); it != v.end();it++)\n\t{\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n\tcout << \"----------------------------\" << endl;\n\t//使用函数对象改变算法策略,排序从大到小\n\tsort(v.begin(), v.end(),MyCompare());\n\tfor (vector::iterator it = v.begin(); it != v.end(); it++)\n\t{\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n}]]>

4.3 内建函数对象

STL内建了一些函数对象。分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数。这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能。使用内建函数对象,需要引入头文件 #include<functional>。

 

  • 6个算数类函数对象,除了negate是一元运算,其他都是二元运算。

template<class T> T plus<T>//加法仿函数

template<class T> T minus<T>//减法仿函数

template<class T> T multiplies<T>//乘法仿函数

template<class T> T divides<T>//除法仿函数

template<class T> T modulus<T>//取模仿函数

template<class T> T negate<T>//取反仿函数

 

  • 6个关系运算类函数对象,每一种都是二元运算。

template<class T> bool equal_to<T>//等于

template<class T> bool not_equal_to<T>//不等于

template<class T> bool greater<T>//大于

template<class T> bool greater_equal<T>//大于等于

template<class T> bool less<T>//小于

template<class T> bool less_equal<T>//小于等于

 

  • 逻辑运算类运算函数,not为一元运算,其余为二元运算。

template<class T> bool logical_and<T>//逻辑与

template<class T> bool logical_or<T>//逻辑或

template<class T> bool logical_not<T>//逻辑非


内建函数对象举例:


cpp n;\n\tcout << n(50) << endl;\n}\n\n//加法仿函数\nvoid test02()\n{\n\tplus p;\n\tcout << p(10, 20) << endl;\n}\n\n//大于仿函数\nvoid test03()\n{\n\tvector v;\n\tsrand((unsigned int)time(NULL));\n\tfor (int i = 0; i < 10; i++){\n\t\tv.push_back(rand() % 100);\n\t}\n\n\tfor (vector::iterator it = v.begin(); it != v.end(); it++){\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n\tsort(v.begin(), v.end(), greater());\n\n\tfor (vector::iterator it = v.begin(); it != v.end(); it++){\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n\n}\n\n]]>

3.1.4 函数对象适配器

cpp\n{\npublic:\n\tvoid operator()(int v1,int v2) const\n\t{\n\t\tcout << \"v1 = : \" << v1 << \" v2 = :\" <v;\n\tfor (int i = 0; i < 10; i++)\n\t{\n\t\tv.push_back(i);\n\t}\n\tcout << \"请输入起始值:\" << endl;\n\tint x;\n\tcin >> x;\n\n\tfor_each(v.begin(), v.end(), bind1st(MyPrint(), x));\n\t//for_each(v.begin(), v.end(), bind2nd( MyPrint(),x ));\n}\n//总结: bind1st和bind2nd区别?\n//bind1st : 将参数绑定为函数对象的第一个参数\n//bind2nd : 将参数绑定为函数对象的第二个参数\n//bind1st bind2nd将二元函数对象转为一元函数对象\n\n\nclass GreaterThenFive:public unary_function\n{\npublic:\n\tbool operator ()(int v) const\n\t{\n\t\treturn v > 5;\n\t}\n};\n\n//2、取反适配器\nvoid test02()\n{\n\tvector v;\n\tfor (int i = 0; i < 10;i++)\n\t{\n\t\tv.push_back(i);\n\t}\n\t\n// \tvector::iterator it = find_if(v.begin(), v.end(), GreaterThenFive()); //返回第一个大于5的迭代器\n//\tvector::iterator it = find_if(v.begin(), v.end(), not1(GreaterThenFive())); //返回第一个小于5迭代器\n\t//自定义输入\n\tvector::iterator it = find_if(v.begin(), v.end(), not1 ( bind2nd(greater(),5)));\n\tif (it == v.end())\n\t{\n\t\tcout << \"没找到\" << endl;\n\t}\n\telse\n\t{\n\t\tcout << \"找到\" << *it << endl;\n\t}\n\n\t//排序 二元函数对象\n\tsort(v.begin(), v.end(), not2(less()));\n\tfor_each(v.begin(), v.end(), [](int val){cout << val << \" \"; });\n\n}\n//not1 对一元函数对象取反\n//not2 对二元函数对象取反\n\nvoid MyPrint03(int v,int v2)\n{\n\tcout << v + v2<< \" \";\n}\n\n//3、函数指针适配器 ptr_fun\nvoid test03()\n{\n\tvector v;\n\tfor (int i = 0; i < 10; i++)\n\t{\n\t\tv.push_back(i);\n\t}\n\t// ptr_fun( )把一个普通的函数指针适配成函数对象\n\tfor_each(v.begin(), v.end(), bind2nd( ptr_fun( MyPrint03 ), 100));\n}\n\n\n//4、成员函数适配器\nclass Person\n{\npublic:\n\tPerson(string name, int age)\n\t{\n\t\tm_Name = name;\n\t\tm_Age = age;\n\t}\n\n\t//打印函数\n\tvoid ShowPerson(){\n\t\tcout << \"成员函数:\" << \"Name:\" << m_Name << \" Age:\" << m_Age << endl;\n\t}\n\tvoid Plus100()\n\t{\n\t\tm_Age += 100;\n\t}\npublic:\n\tstring m_Name;\n\tint m_Age;\n};\n\nvoid MyPrint04(Person &p)\n{\n\tcout << \"姓名:\" << p.m_Name << \" 年龄:\" << p.m_Age << endl;\n\n};\n\nvoid test04()\n{\n\tvector v;\n\tPerson p1(\"aaa\", 10);\n\tPerson p2(\"bbb\", 20);\n\tPerson p3(\"ccc\", 30);\n\tPerson p4(\"ddd\", 40);\n\tv.push_back(p1);\n\tv.push_back(p2);\n\tv.push_back(p3);\n\tv.push_back(p4);\n\n\t//for_each(v.begin(), v.end(), MyPrint04);\n\t//利用 mem_fun_ref 将Person内部成员函数适配\n\tfor_each(v.begin(), v.end(), mem_fun_ref(&Person::ShowPerson));\n// \tfor_each(v.begin(), v.end(), mem_fun_ref(&Person::Plus100));\n// \tfor_each(v.begin(), v.end(), mem_fun_ref(&Person::ShowPerson));\n}\n\nvoid test05(){\n\n\tvector v1;\n\t//创建数据\n\tPerson p1(\"aaa\", 10);\n\tPerson p2(\"bbb\", 20);\n\tPerson p3(\"ccc\", 30);\n\tPerson p4(\"ddd\", 40);\n\n\tv1.push_back(&p1);\n\tv1.push_back(&p2);\n\tv1.push_back(&p3);\n\tv1.push_back(&p4);\n\n\tfor_each(v1.begin(), v1.end(), mem_fun(&Person::ShowPerson));\n}\n\n//如果容器存放的是对象指针, 那么用mem_fun\n//如果容器中存放的是对象实体,那么用mem_fun_ref]]>

4.2 算法概述

算法主要是由头文件<algorithm> <functional> <numeric>组成。

<algorithm>是所有STL头文件中最大的一个,其中常用的功能涉及到比较,交换,查找,遍历,复制,修改,反转,排序,合并等...

<numeric>体积很小,只包括在几个序列容器上进行的简单运算的模板函数.

<functional> 定义了一些模板类,用以声明函数对象。

4.3 常用遍历算法

/*

    遍历算法 遍历容器元素

    @param beg 开始迭代器

    @param end 结束迭代器

    @param _callback  函数回调或者函数对象

    @return 函数对象

*/

for_each(iterator beg, iterator end, _callback);

/*

    transform算法 将指定容器区间元素搬运到另一容器中

    注意 : transform 不会给目标容器分配内存,所以需要我们提前分配好内存

    @param beg1 源容器开始迭代器

    @param end1 源容器结束迭代器

    @param beg2 目标容器开始迭代器

    @param _cakkback 回调函数或者函数对象

    @return 返回目标容器迭代器

*/

transform(iterator beg1, iterator end1, iterator beg2, _callbakc)

for_each:

cpp inline\nvoid for_each(_InIt _First, _InIt _Last, _Fn1 _Func)\n{\n\tfor (; _First != _Last; ++_First)\n\t\t_Func(*_First);\n}\n\n*/\n\n//普通函数\nvoid print01(int val){\n\tcout << val << \" \";\n}\n//函数对象\nstruct print001{\n\tvoid operator()(int val){\n\t\tcout << val << \" \";\n\t}\n};\n\n//for_each算法基本用法\nvoid test01(){\n\t\n\tvector v;\n\tfor (int i = 0; i < 10;i++){\n\t\tv.push_back(i);\n\t}\n\n\t//遍历算法\n\tfor_each(v.begin(), v.end(), print01);\n\tcout << endl;\n\n\tfor_each(v.begin(), v.end(), print001());\n\tcout << endl;\n\n}\n\nstruct print02{\n\tprint02(){\n\t\tmCount = 0;\n\t}\n\tvoid operator()(int val){\n\t\tcout << val << \" \";\n\t\tmCount++;\n\t}\n\tint mCount;\n};\n\n//for_each返回值\nvoid test02(){\n\n\tvector v;\n\tfor (int i = 0; i < 10; i++){\n\t\tv.push_back(i);\n\t}\n\n\tprint02 p = for_each(v.begin(), v.end(), print02());\n\tcout << endl;\n\tcout << p.mCount << endl;\n}\n\nstruct print03 : public binary_function{\n\tvoid operator()(int val,int bindParam) const{\n\t\tcout << val + bindParam << \" \";\n\t}\n};\n\n//for_each绑定参数输出\nvoid test03(){\n\t\n\tvector v;\n\tfor (int i = 0; i < 10; i++){\n\t\tv.push_back(i);\n\t}\n\n\tfor_each(v.begin(), v.end(), bind2nd(print03(),100));\n}]]>

transform:

cpp inline \n\t_OutIt _Transform(_InIt _First, _InIt _Last,_OutIt _Dest, _Fn1 _Func)\n\t{\t\n\n\t\tfor (; _First != _Last; ++_First, ++_Dest)\n\t\t\t*_Dest = _Func(*_First);\n\t\treturn (_Dest);\n\t}\n\n\ttemplate inline\n\t_OutIt _Transform(_InIt1 _First1, _InIt1 _Last1,_InIt2 _First2, _OutIt _Dest, _Fn2 _Func)\n\t{\t\n\t\tfor (; _First1 != _Last1; ++_First1, ++_First2, ++_Dest)\n\t\t\t*_Dest = _Func(*_First1, *_First2);\n\t\treturn (_Dest);\n\t}\n*/\n\nstruct transformTest01{\n\tint operator()(int val){\n\t\treturn val + 100;\n\t}\n};\nstruct print01{\n\tvoid operator()(int val){\n\t\tcout << val << \" \";\n\t}\n};\nvoid test01(){\n\n\tvector vSource;\n\tfor (int i = 0; i < 10;i ++){\n\t\tvSource.push_back(i + 1);\n\t}\n\n\t//目标容器\n\tvector vTarget;\n\t//给vTarget开辟空间\n\tvTarget.resize(vSource.size());\n\t//将vSource中的元素搬运到vTarget\n\tvector::iterator it = transform(vSource.begin(), vSource.end(), vTarget.begin(), transformTest01());\n\t//打印\n\tfor_each(vTarget.begin(), vTarget.end(), print01()); cout << endl;\n\t\n}\n\n//将容器1和容器2中的元素相加放入到第三个容器中\nstruct transformTest02{\n\tint operator()(int v1,int v2){\n\t\treturn v1 + v2;\n\t}\n};\nvoid test02(){\n\n\tvector vSource1;\n\tvector vSource2;\n\tfor (int i = 0; i < 10; i++){\n\t\tvSource1.push_back(i + 1);\t\n\t}\n\n\t//目标容器\n\tvector vTarget;\n\t//给vTarget开辟空间\n\tvTarget.resize(vSource1.size());\n\ttransform(vSource1.begin(), vSource1.end(), vSource2.begin(),vTarget.begin(), transformTest02());\n\t//打印\n\tfor_each(vTarget.begin(), vTarget.end(), print01()); cout << endl;\n}]]>

4.4 常用查找算法

 

/*

    find算法 查找元素

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param value 查找的元素

    @return 返回查找元素的位置

*/

find(iterator beg, iterator end, value)

/*

    find_if算法 条件查找

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param  callback 回调函数或者谓词(返回bool类型的函数对象)

    @return bool 查找返回true 否则false

*/

find_if(iterator beg, iterator end, _callback);


/*

    adjacent_find算法 查找相邻重复元素

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param  _callback 回调函数或者谓词(返回bool类型的函数对象)

    @return 返回相邻元素的第一个位置的迭代器

*/

adjacent_find(iterator beg, iterator end, _callback);

/*

    binary_search算法 二分查找法

    注意: 在无序序列中不可用

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param value 查找的元素

    @return bool 查找返回true 否则false

*/

bool binary_search(iterator beg, iterator end, value);

/*

    count算法 统计元素出现次数

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param  value回调函数或者谓词(返回bool类型的函数对象)

    @return int返回元素个数

*/

count(iterator beg, iterator end, value);

/*

count_if算法 统计元素出现次数

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param  callback 回调函数或者谓词(返回bool类型的函数对象)

    @return int返回元素个数

*/

count_if(iterator beg, iterator end, _callback);

 

4.5 常用排序算法

/*

    merge算法 容器元素合并,并存储到另一容器中

    注意:两个容器必须是有序的

    @param beg1 容器1开始迭代器

    @param end1 容器1结束迭代器

    @param beg2 容器2开始迭代器

    @param end2 容器2结束迭代器

    @param dest  目标容器开始迭代器

*/

merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)

/*

    sort算法 容器元素排序

    @param beg 容器1开始迭代器

    @param end 容器1结束迭代器

    @param _callback 回调函数或者谓词(返回bool类型的函数对象)

*/

sort(iterator beg, iterator end, _callback)

/*

    random_shuffle算法 对指定范围内的元素随机调整次序

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

*/

random_shuffle(iterator beg, iterator end)

/*

    reverse算法 反转指定范围的元素

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

*/

reverse(iterator beg, iterator end)

 

 

4.6 常用拷贝和替换算法

/*

    copy算法 将容器内指定范围的元素拷贝到另一容器中

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param dest 目标起始迭代器

*/

copy(iterator beg, iterator end, iterator dest)

/*

    replace算法 将容器内指定范围的旧元素修改为新元素

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param oldvalue 旧元素

    @param oldvalue 新元素

*/

replace(iterator beg, iterator end, oldvalue, newvalue)

/*

    replace_if算法 将容器内指定范围满足条件的元素替换为新元素

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param callback函数回调或者谓词(返回Bool类型的函数对象)

    @param oldvalue 新元素

*/

replace_if(iterator beg, iterator end, _callback, newvalue)

/*

    swap算法 互换两个容器的元素

    @param c1容器1

    @param c2容器2

*/

swap(container c1, container c2)

 

4.7 常用算数生成算法

/*

    accumulate算法 计算容器元素累计总和

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param value累加值

*/

accumulate(iterator beg, iterator end, value)

/*

    fill算法 向容器中添加元素

    @param beg 容器开始迭代器

    @param end 容器结束迭代器

    @param value t填充元素

*/

fill(iterator beg, iterator end, value)


 

4.8 常用集合算法

/*

    set_intersection算法 求两个set集合的交集

    注意:两个集合必须是有序序列

    @param beg1 容器1开始迭代器

    @param end1 容器1结束迭代器

    @param beg2 容器2开始迭代器

    @param end2 容器2结束迭代器

    @param dest  目标容器开始迭代器

    @return 目标容器的最后一个元素的迭代器地址

*/

set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)

/*

    set_union算法 求两个set集合的并集

    注意:两个集合必须是有序序列

    @param beg1 容器1开始迭代器

    @param end1 容器1结束迭代器

    @param beg2 容器2开始迭代器

    @param end2 容器2结束迭代器

    @param dest  目标容器开始迭代器

    @return 目标容器的最后一个元素的迭代器地址

*/

set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)

/*

    set_difference算法 求两个set集合的差集

    注意:两个集合必须是有序序列

    @param beg1 容器1开始迭代器

    @param end1 容器1结束迭代器

    @param beg2 容器2开始迭代器

    @param end2 容器2结束迭代器

    @param dest  目标容器开始迭代器

    @return 目标容器的最后一个元素的迭代器地址

*/

set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)


","representation":"storage","_expandable":{"content":"/rest/api/content/58954498"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954498","edit":"/pages/resumedraft.action?draftId=58954498","tinyui":"/x/ApODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954498"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954498/child","restrictions":"/rest/api/content/58954498/restriction/byOperation","history":"/rest/api/content/58954498/history","ancestors":"","descendants":"/rest/api/content/58954498/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954500.json b/tools/wiki_data/bodies/58954500.json new file mode 100644 index 0000000..72aa637 --- /dev/null +++ b/tools/wiki_data/bodies/58954500.json @@ -0,0 +1 @@ +{"id":"58954500","type":"page","status":"current","title":"02.QT安装教程(免注册和登陆)","version":{"by":{"type":"known","username":"suncaper","userKey":"ff80818177bd0eb50179c1060d480003","profilePicture":{"path":"/images/icons/profilepics/default.svg","width":48,"height":48,"isDefault":true},"displayName":"suncaper","_links":{"self":"https://wiki.suncaper.net/rest/api/user?key=ff80818177bd0eb50179c1060d480003"},"_expandable":{"status":""}},"when":"2025-07-03T04:13:50.738Z","message":"","number":6,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954500/version/6"},"_expandable":{"content":"/rest/api/content/58954500"}},"body":{"storage":{"value":"

准备工作: 第一步: 环境安装

下载安装包,可以到Qt官网,但是下载速度非常慢。一般推荐到几个国内著名的 Qt 镜像网站下载:

下载windows系统安装文件“
qt-opensource-windows-x86-*.exe ”。该文件比较大,约2.3GB,其中集成了32位和64位的安装包。对于Linux和Mac系统,可以分别下载相应的文件进行安装。

以第一个中科大的镜像为例,进入网页后依次点击下图红框内的链接。

第二步: 下载安装包
选择QT—QT5.15.0版本开始,官方不再提供离线版安装包,除非你充钱买商业版。,下载qt-opensource-windows-x86-5.14.2.exe即可。
注意:不要选择5.15及以上的版本,否则安装会很麻烦

建议安装之前先断网,要不然会弹出注册页面!!!
双击启动安装包之后,将会打开一个欢迎界面。在这里,直接点击"下一步"即可。

接着,会提示你登陆 Qt 账户。这个 Qt 账户对普通人来说没什么必要,所以直接点击下面的"Skip"跳过即可。

随后,便会弹出真正的开始安装界面,点击"下一步"。

在安装文件夹的界面,建议大家不要将 Qt 安装在系统盘 C 盘(比如我在此作了更改,将其安装到 D 盘。当然,你安装在 C 盘也是可以的),其他保持默认即可。继续点击"下一步"。

下面,就到了重点了。在选择组件界面,请务必要选对安装 Qt 所需要的组件,否则你安装以后是无法正常运行的。
首先,点击各项前面的 > 箭头展开子项的内容。

在 Qt5.9 一项中,如果你是使用 MinGW 进行编译,请务必选中 MinGW 模块。而如果你希望调用 VS 的编译器进行编译,则需要勾选对应的 VS 模块。当然,如果希望进行安卓开发,则安卓模块是少不了了。
至于其余的一些模块,则可以根据自身需要勾选。这里着重介绍下 Qt WebEngine 和三个后面带TP的模块项。Qt WebEngine是谷歌开源的 Web 浏览器引擎模块,而带 TP 的模块则表示此模块目前是技术预览版。
我的建议是,如果你是初学,这些则可以不选。千万不要以为柿子西瓜一箩筐什么都选上是最好的,这样一来是增大存储空间,二来多多少少会影响 Qt 运行时的速度,增加不必要的内存消耗。

① MinGW 5.3.0 32 bit 编译器模块。MinGW 是 Minimalist GNU for Windows 的缩写,MinGW是 Windows 平台上使用的 GNU 工具集导入库的集合。

② 用于 UWP 编译的模块。UWP 是 Windows 10 中 Universal Windows Platform 的简称,有不同编译器类型的 UWP。

③ 用于 windows 平台上的 MSVC 编译器模块,如 msvc2015 32-bit 和 msvc2015 64-bit 等。要安装 MSVC 编译器的模块,需要计算机上已经安装相应版本的 Visual Studio。

④ 用于 Android 平台的模块,如 Android x86 和 Android ARMv7。

⑤ Sources 模块是 Qt 的源程序。

⑥ Qt Charts 是二维图表模块,用于绘制柱状图、饼图、曲线图等常用二维图表。

⑦ Qt Data Visualization 是三维数据图表模块,用于数据的三维显示,如散点的三维空间分布、三维曲面等。

⑧ Qt Purchasing、Qt WebEngine、Qt Network Auth(TP)等其他模块,括号里的 TP 表示技术预览(Technology Preview)。

⑨ Qt Scritp(Deprecated)是脚本模块,括号里的“Deprecated”表示这是已经过时的模块。

“Tools”节点下面是一些工具软件,包括内容如下:

① Qt Creator 4.3.1 是用于 Qt 程序开发的 IDE。

② MinGW 5.3.0 是 MinGW 编译工具链。

③ Strawberry Perl 是一个 Perl 语言工具。




下面,接着看工具项(Tools)的选择。
第一项是CDB的调试器,如果你仅使用 MinGW进行编译,则此项可以不选。
第二项虽然名字带 MinGW,但只是用于交叉编译的(交叉编译即在某一平台上编译用于其他平台的程序),如果用不到,也可以不选。
至于第三项,那是用于 Perl 的。如果你电脑中本身没有安装 Perl,那是装不上的。
选择完成后,继续点击"下一步"。

接着,在打开的页面中勾选"我已经阅读并同意条款"(原文是英文)。然后,继续一路点击"下一步",直到安装完成就可以了。

","representation":"storage","_expandable":{"content":"/rest/api/content/58954500"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":1},"_links":{"webui":"/pages/viewpage.action?pageId=58954500","edit":"/pages/resumedraft.action?draftId=58954500","tinyui":"/x/BJODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954500"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954500/child","restrictions":"/rest/api/content/58954500/restriction/byOperation","history":"/rest/api/content/58954500/history","ancestors":"","descendants":"/rest/api/content/58954500/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954512.json b/tools/wiki_data/bodies/58954512.json new file mode 100644 index 0000000..b14177d --- /dev/null +++ b/tools/wiki_data/bodies/58954512.json @@ -0,0 +1 @@ +{"id":"58954512","type":"page","status":"current","title":"03.Qt桌面应用开发","version":{"by":{"type":"known","username":"suncaper","userKey":"ff80818177bd0eb50179c1060d480003","profilePicture":{"path":"/images/icons/profilepics/default.svg","width":48,"height":48,"isDefault":true},"displayName":"suncaper","_links":{"self":"https://wiki.suncaper.net/rest/api/user?key=ff80818177bd0eb50179c1060d480003"},"_expandable":{"status":""}},"when":"2025-07-03T04:14:09.174Z","message":"","number":2,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954512/version/2"},"_expandable":{"content":"/rest/api/content/58954512"}},"body":{"storage":{"value":"

_Toc466556748目录

































































","representation":"storage","_expandable":{"content":"/rest/api/content/58954512"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":2},"_links":{"webui":"/pages/viewpage.action?pageId=58954512","edit":"/pages/resumedraft.action?draftId=58954512","tinyui":"/x/EJODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954512"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954512/child","restrictions":"/rest/api/content/58954512/restriction/byOperation","history":"/rest/api/content/58954512/history","ancestors":"","descendants":"/rest/api/content/58954512/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954513.json b/tools/wiki_data/bodies/58954513.json new file mode 100644 index 0000000..a569146 --- /dev/null +++ b/tools/wiki_data/bodies/58954513.json @@ -0,0 +1 @@ +{"id":"58954513","type":"page","status":"current","title":"1 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/58954513/version/1"},"_expandable":{"content":"/rest/api/content/58954513"}},"body":{"storage":{"value":"

_Toc466556749_Toc4718419771.1 什么是Qt

Qt是一个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。

_Toc466556750_Toc4718419781.2 Qt的发展史

1991年 Qt最早由奇趣科技开发
1996年 进入商业领域,它也是目前流行的Linux桌面环境KDE的基础
2008年 奇趣科技被诺基亚公司收购,Qt称为诺基亚旗下的编程语言
2012年 Qt又被Digia公司收购
2014年4月 跨平台的集成开发环境Qt Creator3.1.0发布,同年5月20日配发了Qt5.3正式版,至此Qt实现了对iOS、Android、WP等各平台的全面支持。
当前Qt最新版本为 5.9.0

_Toc466556751_Toc4718419791.3 支持的平台

  • Windows – XP、Vista、Win7、Win8、Win2008、Win10
  • Uinux/X11 – Linux、Sun Solaris、HP-UX、Compaq Tru64 UNIX、IBM AIX、SGI IRIX、FreeBSD、BSD/OS、和其他很多X11平台
  • Macintosh – Mac OS X
  • Embedded – 有帧缓冲支持的嵌入式Linux平台,Windows CE

_Toc466556752_Toc4718419801.4 Qt版本

Qt按照不同的版本发行,分为商业版和开源版

  • 商业版

为商业软件提供开发,他们提供传统商业软件发行版,并且提供在商业有效期内的免费升级和技术支持服务。

  • 开源的LGPL版本:

为了开发自有而设计的开放源码软件,它提供了和商业版本同样的功能,在GNU通用公共许可下,它是免费的。

_Toc466556759_Toc4718419871.6 Qt的优点

  • 跨平台,几乎支持所有的平台
  • 接口简单,容易上手,学习QT框架对学习其他框架有参考意义。
  • 一定程度上简化了内存回收机制
  • 开发效率高,能够快速的构建应用程序。
  • 有很好的社区氛围,市场份额在缓慢上升。
  • 可以进行嵌入式开发。

_Toc466556760_Toc4718419881.7 成功案例

  • Linux桌面环境KDE
  • WPS Office 办公软件
  • Skype 网络电话
  • Google Earth 谷歌地图
  • VLC多媒体播放器
  • VirtualBox虚拟机软件


","representation":"storage","_expandable":{"content":"/rest/api/content/58954513"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":1},"_links":{"webui":"/pages/viewpage.action?pageId=58954513","edit":"/pages/resumedraft.action?draftId=58954513","tinyui":"/x/EZODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954513"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954513/child","restrictions":"/rest/api/content/58954513/restriction/byOperation","history":"/rest/api/content/58954513/history","ancestors":"","descendants":"/rest/api/content/58954513/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954515.json b/tools/wiki_data/bodies/58954515.json new file mode 100644 index 0000000..411f153 --- /dev/null +++ b/tools/wiki_data/bodies/58954515.json @@ -0,0 +1 @@ +{"id":"58954515","type":"page","status":"current","title":"2 创建Qt项目","version":{"by":{"type":"known","username":"xpei","userKey":"2c90931b693d413801693d73eda10000","profilePicture":{"path":"/images/icons/profilepics/default.svg","width":48,"height":48,"isDefault":true},"displayName":"卢鑫培","_links":{"self":"https://wiki.suncaper.net/rest/api/user?key=2c90931b693d413801693d73eda10000"},"_expandable":{"status":""}},"when":"2023-07-09T02:15:16.000Z","message":"","number":2,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954515/version/2"},"_expandable":{"content":"/rest/api/content/58954515"}},"body":{"storage":{"value":"

_Toc466556762_Toc4718419902.1 使用向导创建

打开Qt Creator 界面选择 New Project或者选择菜单栏 【文件】-【新建文件或项目】菜单项

弹出New Project对话框,选择Qt Widgets Application(窗口小部件)

选择【Choose】按钮,弹出如下对话框

设置项目名称和路径,按照向导进行下一步,

选择编译套件

向导会默认添加一个继承自CMainWindow的类,可以在此修改类的名字和基类。默认的基类有QMainWindow、QWidget以及QDialog三个,我们可以选择QWidget(类似于空窗口),这里我们可以先创建一个不带UI的界面,继续下一步

系统会默认给我们添加main.cpp、mywidget.cpp、 mywidget.h和一个.pro项目文件,点击完成,即可创建出一个Qt桌面程序。

_Toc466556763_Toc471841991手动创建

添加一个空项目

选择【choose】进行下一步。设置项目名称和路径 —> 选择编译套件 --> 修改类信息 --> 完成(步骤同上),生成一个空项目。在空项目中添加文件:在项目名称上单击鼠标右键弹出右键菜单,选择【添加新文件】

弹出新建文件对话框

在此对话框中选择要添加的类或者文件,根据向导完成文件的添加。

_Toc466556764_Toc471841992.pro文件

在使用Qt向导生成的应用程序.pro文件格式如下:
QT += core gui //包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于Qt4版本 才包含widget模块
TARGET = QtFirst //应用程序名 生成的.exe程序名称
TEMPLATE = app //模板类型 应用程序模板
SOURCES += main.cpp\\ //源文件
mywidget.cpp
HEADERS += mywidget.h //头文件
.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件。.pro文件的写法如下:

  • 注释

从"#"开始,到这一行结束。

  • 模板变量告诉qmake为这个应用程序生成哪种makefile。下面是可供使用的选择:TEMPLATE = app
    • app -建立一个应用程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使用。
    • lib - 建立一个库的makefile。
    • vcapp - 建立一个应用程序的VisualStudio项目文件。
    • vclib - 建立一个库的VisualStudio项目文件。
    • subdirs -这是一个特殊的模板,它可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的makefile。
  • #指定生成的应用程序名:

TARGET = QtDemo

  • #工程中包含的头文件

HEADERS += include/painter.h

  • #工程中包含的.ui设计文件

FORMS += forms/painter.ui

  • #工程中包含的源文件

SOURCES += sources/main.cpp sources

  • #工程中包含的资源文件

RESOURCES += qrc/painter.qrc

  • greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

这条语句的含义是,如果QT_MAJOR_VERSION大于4(也就是当前使用的Qt5及更高版本)需要增加widgets模块。如果项目仅需支持Qt5,也可以直接添加"QT += widgets"一句。不过为了保持代码兼容,最好还是按照QtCreator生成的语句编写。

  • #配置信息

CONFIG用来告诉qmake关于应用程序的配置信息。
CONFIG = c11//使用c+11的特性
在这里使用"+=",是因为我们添加我们的配置选项到任何一个已经存在中。这样做比使用"="那样替换已经指定的所有选项更安全。

_Toc466556765_Toc471841993一个最简单的Qt应用程序

main入口函数中


cpp\n\nint main(int argc, char *argv[])\n{\n QApplication a(argc, argv);\n Widget w;\n w.show();\n\n return a.exec();\n}\n\n]]>


  • Qt系统提供的标准类名声明头文件没有.h后缀
  • Qt一个类对应一个头文件,类名就是头文件名
  • QApplication应用程序类
    • 管理图形用户界面应用程序的控制流和主要设置。
    • 是Qt的整个后台管理的命脉它包含主事件循环,在其中来自窗口系统和其它资源的所有事件处理和调度。它也处理应用程序的初始化和结束,并且提供对话管理
    • 对于任何一个使用Qt的图形用户界面应用程序,都正好存在一个QApplication 对象,而不论这个应用程序在同一时间内是不是有0、1、2或更多个窗口。
  • a.exec()

程序进入消息循环,等待对用户输入进行响应。这里main()把控制权转交给Qt,Qt完成事件处理工作,当应用程序退出的时候exec()的值就会返回。在exec()中,Qt接受并处理用户和系统的事件并且把它们传递给适当的窗口部件。

","representation":"storage","_expandable":{"content":"/rest/api/content/58954515"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":14},"_links":{"webui":"/pages/viewpage.action?pageId=58954515","edit":"/pages/resumedraft.action?draftId=58954515","tinyui":"/x/E5ODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954515"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954515/child","restrictions":"/rest/api/content/58954515/restriction/byOperation","history":"/rest/api/content/58954515/history","ancestors":"","descendants":"/rest/api/content/58954515/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954525.json b/tools/wiki_data/bodies/58954525.json new file mode 100644 index 0000000..eff6419 --- /dev/null +++ b/tools/wiki_data/bodies/58954525.json @@ -0,0 +1 @@ +{"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":"

_Toc4718419953.1 按钮的创建

在Qt程序中,最常用的控件之一就是按钮了,首先我们来看下如何创建一个按钮

cpp\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]]>


上面代码中,一个按钮其实就是一个QPushButton类下的对象,如果只是创建出对象,是无法显示到窗口中的,所以我们需要依赖一个父窗口,也就是指定一个父亲利用setParent函数即可,如果想设置按钮上显示的文字利用setText,移动按钮位置用move
对于窗口而言,我们可以修改左上角窗口的标题setWindowTitle,重新指定窗口大小:resize,或者设置固定的窗口大小setFixedSize;

_Toc4718419963.2_Toc466556776 对象模型(对象树)

在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。

  • QObject是以对象树的形式组织起来的。
    • 当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。

这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。

    • 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)

这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。

  • QWidget是能够在屏幕上显示的一切组件的父类。
    • QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
    • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。

Qt 引入对象树的概念,在一定程度上解决了内存问题。

  • 当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
  • 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。

如果QObject在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:

cpp


作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。
但是,如果我们使用下面的代码:

cpp


情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。
由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

_Toc4718419973.3_Toc466556773 Qt窗口坐标体系

_Toc466556774坐标体系:
以左上角为原点(0,0),X向右增加,Y向下增加。

对于嵌套窗口,其坐标是相对于父窗口来说的。

","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"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954527.json b/tools/wiki_data/bodies/58954527.json new file mode 100644 index 0000000..52cb6dd --- /dev/null +++ b/tools/wiki_data/bodies/58954527.json @@ -0,0 +1 @@ +{"id":"58954527","type":"page","status":"current","title":"4 信号和槽机制","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/58954527/version/1"},"_expandable":{"content":"/rest/api/content/58954527"}},"body":{"storage":{"value":"

信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

_Toc4718419994.1 系统自带的信号和槽

下面我们完成一个小功能,上面我们已经学习了按钮的创建,但是还没有体现出按钮的功能,按钮最大的功能也就是点击后触发一些事情,比如我们点击按钮,就把当前的窗口给关闭掉,那么在Qt中,这样的功能如何实现呢?
其实无法两行代码就可以搞定了,我们看下面的代码

cpp


第一行是创建一个关闭按钮,这个之前已经学过,第二行就是核心了,也就是信号槽的使用方式
connect()函数最常用的一般形式:

cpp


参数解释:

    • sender:发出信号的对象
    • signal:发送对象发出的信号
    • receiver:接收信号的对象
    • slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

那么系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档了,在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中输入QPushButton,首先我们可以在Contents中寻找关键字 signals,信号的意思,但是我们发现并没有找到,这时候我们应该想到也许这个信号的被父类继承下来的,因此我们去他的父类sidebar-contentQAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个

这里的clicked就是我们要找到,槽函数的寻找方式和信号一样,只不过他的关键字是slot。

_Toc4718420004.2 自定义信号和槽

使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。
下面我们看看使用 Qt 的信号槽:

cpphungury();\n}\n\t学生响应了槽函数,并且打印信息\n//自定义槽函数 实现\nvoid Student::eat()\n{\n qDebug() << \"该吃饭了!\";\n}\n在窗口中连接信号槽\n teacher = new Teacher(this);\n student = new Student(this);\n\n connect(teacher,&Teacher::hungury,student,&Student::treat);\n并且调用下课函数,测试打印出 “该吃饭了”\n\n]]>cpp



_Toc466556769自定义信号槽需要注意的事项:

  • 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
  • 信号和槽函数返回值是 void
  • 信号只需要声明,不需要实现
  • 槽函数需要声明也需要实现
  • 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
  • 使用 emit 在恰当的位置发送信号;
  • 使用connect()函数连接信号和槽。
  • 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
  • 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
  • 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。

_Toc466556770_Toc4718420014.3信号槽的拓展

  • 一个信号可以和多个槽相连

如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。

  • 多个信号可以连接到一个槽

只要任意一个信号发出,这个槽就会被调用

  • 一个信号可以连接到另外的一个信号

当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

  • 槽可以被取消链接

这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽

  • 信号槽可以断开

利用disconnect关键字是可以断开信号槽的

  • 使用Lambda 表达式

在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我们会详细介绍什么是Lambda表达式

_Toc4718420024.4 Qt4版本的信号槽写法

cpp


这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到connect()函数的 signal 和 slot 都是接受字符串,一旦出现连接不成功的情况,Qt4是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。
Qt5在语法上完全兼容Qt4,而反之是不可以的。

_Toc4718420034.5 Lambda表达式

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。首先看一下Lambda表达式的基本构成:




cppreturn-type\n{\nstatement\n}\n\n]]>




[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

函数对象参数;[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

  • 空。没有使用任何函数对象参数。
  • =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
  • &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
  • this。函数体内可以使用Lambda所在类中的成员变量。
  • a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
  • &a。将a按引用进行传递。
  • a, &b。将a按值进行传递,b按引用进行传递。
  • =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
  • &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

② 操作符重载函数参数;

标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

③ 可修改标示符;

mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。


cppmove(100,100);\n int m = 10;\n\n connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 20; qDebug() << m; });\n\n connect(myBtn2,&QPushButton::clicked,this,[=] () { qDebug() << m; });\n\n qDebug() << m;\n\n]]>


④ 函数返回值;

->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

⑤ 是函数体;

      {},标识函数的实现,这部分不能省略,但函数体可以为空。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954527"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":23},"_links":{"webui":"/pages/viewpage.action?pageId=58954527","edit":"/pages/resumedraft.action?draftId=58954527","tinyui":"/x/H5ODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954527"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954527/child","restrictions":"/rest/api/content/58954527/restriction/byOperation","history":"/rest/api/content/58954527/history","ancestors":"","descendants":"/rest/api/content/58954527/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954529.json b/tools/wiki_data/bodies/58954529.json new file mode 100644 index 0000000..20b5dbc --- /dev/null +++ b/tools/wiki_data/bodies/58954529.json @@ -0,0 +1 @@ +{"id":"58954529","type":"page","status":"current","title":"5 QMainWindow","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/58954529/version/1"},"_expandable":{"content":"/rest/api/content/58954529"}},"body":{"storage":{"value":"

QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等。

_Toc466556778_Toc4718420055.1 菜单栏

一个主窗口最多只有一个菜单栏。位于主窗口顶部、主窗口标题栏下面。

  • 创建菜单栏,通过QMainWindow类的menubar()函数获取主窗口菜单栏指针
cpp


  • 创建菜单,调用QMenu的成员函数addMenu来添加菜单
cpp


  • 创建菜单项,调用QMenu的成员函数addAction来添加菜单项
cpp


Qt 并没有专门的菜单项类,只是使用一个QAction类,抽象出公共的动作。当我们把QAction对象添加到菜单,就显示成一个菜单项,添加到工具栏,就显示成一个工具按钮。用户可以通过点击菜单项、点击工具栏按钮、点击快捷键来激活这个动作。

_Toc466556779_Toc4718420065.2 工具栏

主窗口的工具栏上可以有多个工具条,通常采用一个菜单对应一个工具条的的方式,也可根据需要进行工具条的划分。

    • 直接调用QMainWindow类的addToolBar()函数获取主窗口的工具条对象,每增加一个工具条都需要调用一次该函数。
    • 插入属于工具条的动作,即在工具条上添加操作。

通过QToolBar类的addAction函数添加。

    • 工具条是一个可移动的窗口,它的停靠区域由QToolBar的allowAreas决定,包括:
      • Qt::LeftToolBarArea 停靠在左侧
      • Qt::RightToolBarArea 停靠在右侧
      • Qt::TopToolBarArea 停靠在顶部
      • Qt::BottomToolBarArea 停靠在底部
      • Qt::AllToolBarAreas 以上四个位置都可停靠

使用setAllowedAreas()函数指定停靠区域:
setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea)
使用setMoveable()函数设定工具栏的可移动性:
setMoveable(false)//工具条不可移动, 只能停靠在初始化的位置上

_Toc466556780_Toc4718420075.3 状态栏

    • 派生自QWidget类,使用方法与QWidget类似,QStatusBar类常用成员函数:
    • 状态栏也只能最多有一个
cpp


_Toc4718420085.4 铆接部件

铆接部件 QDockWidget,也称浮动窗口,可以有多个。

cppsetAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea); 设置区域范围\n\n]]>


_Toc4718420095.5 核心部件(中心部件)

除了以上几个部件,中心显示的部件都可以作为核心部件,例如一个记事本文件,可以利用QTextEdit做核心部件

cpp


_Toc466556781_Toc4718420105.6 资源文件

Qt 资源系统是一个跨平台的资源机制,用于将程序运行时所需要的资源以二进制的形式存储于可执行文件内部。如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。也就是说,如果你将资源以资源文件形式存储,它是会编译到可执行文件内部。
使用 Qt Creator 可以很方便地创建资源文件。我们可以在工程上点右键,选择"添加新文件…",可以在 Qt 分类下找到"Qt 资源文件":

点击"选择…"按钮,打开"新建 Qt 资源文件"对话框。在这里我们输入资源文件的名字和路径:

点击下一步,选择所需要的版本控制系统,然后直接选择完成。我们可以在 Qt Creator 的左侧文件列表中看到"资源文件"一项,也就是我们新创建的资源文件:

右侧的编辑区有个"添加",我们首先需要添加前缀,比如我们将前缀取名为 images。然后选中这个前缀,继续点击添加文件,可以找到我们所需添加的文件。这里,我们选择 document-open.png 文件。当我们完成操作之后,Qt Creator 应该是这样子的:

接下来,我们还可以添加另外的前缀或者另外的文件。这取决于你的需要。当我们添加完成之后,我们可以像前面一章讲解的那样,通过使用 : 开头的路径来找到这个文件。比如,我们的前缀是 /images,文件是 document-open.png,那么就可以使用:/images/document-open.png找到这个文件。
这么做带来的一个问题是,如果以后我们要更改文件名,比如将 docuemnt-open.png 改成 docopen.png,那么,所有使用了这个名字的路径都需要修改。所以,更好的办法是,我们给这个文件去一个"别名",以后就以这个别名来引用这个文件。具体做法是,选中这个文件,添加别名信息:

这样,我们可以直接使用:/images/doc-open引用到这个资源,无需关心图片的真实文件名。
如果我们使用文本编辑器打开 res.qrc 文件,就会看到一下内容:

cpp\n\ndocument-open.png\n\n\ndocument-open-fr.png\n\n]]>


我们可以对比一下,看看 Qt Creator 帮我们生成的是怎样的 qrc 文件。当我们编译工程之后,我们可以在构建目录中找到 qrc_res.cpp 文件,这就是 Qt 将我们的资源编译成了 C++ 代码。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954529"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":29},"_links":{"webui":"/display/2CT/5+QMainWindow","edit":"/pages/resumedraft.action?draftId=58954529","tinyui":"/x/IZODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954529"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954529/child","restrictions":"/rest/api/content/58954529/restriction/byOperation","history":"/rest/api/content/58954529/history","ancestors":"","descendants":"/rest/api/content/58954529/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954535.json b/tools/wiki_data/bodies/58954535.json new file mode 100644 index 0000000..214deaf --- /dev/null +++ b/tools/wiki_data/bodies/58954535.json @@ -0,0 +1 @@ +{"id":"58954535","type":"page","status":"current","title":"6 对话框QDialog","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/58954535/version/1"},"_expandable":{"content":"/rest/api/content/58954535"}},"body":{"storage":{"value":"

_Toc466556783_Toc4718420126.1 基本概念

对话框是 GUI 程序中不可或缺的组成部分。很多不能或者不适合放入主窗口的功能组件都必须放在对话框中设置。对话框通常会是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。
Qt 中使用QDialog类实现对话框。就像主窗口一样,我们通常会设计一个类继承QDialog。QDialog(及其子类,以及所有Qt::Dialog类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框(此时,其默认出现的位置是 parent 的中心)。顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。
对话框分为模态对话框和非模态对话框。

  • 模态对话框,就是会阻塞同一应用程序中其它窗口的输入。

模态对话框很常见,比如"打开文件"功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,我们是不能对除此对话框之外的窗口部分进行操作的。

  • 与此相反的是非模态对话框,例如查找对话框,我们可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。

_Toc466556784_Toc4718420136.2 标准对话框

所谓标准对话框,是 Qt 内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。
Qt 的内置对话框大致分为以下几类:

  • QColorDialog: 选择颜色;
  • QFileDialog: 选择文件或者目录;
  • QFontDialog: 选择字体;
  • QInputDialog: 允许用户输入一个值,并将其值返回;
  • QMessageBox: 模态对话框,用于显示信息、询问问题等;
  • QPageSetupDialog: 为打印机提供纸张相关的选项;
  • QPrintDialog: 打印机配置;
  • QPrintPreviewDialog:打印预览;
  • QProgressDialog: 显示操作过程。

_Toc466556785_Toc4718420146.3 自定义消息框

Qt 支持模态对话框和非模态对话框。
模态与非模态的实现:

  • 使用QDialog::exec()实现应用程序级别的模态对话框
  • 使用QDialog::open()实现窗口级别的模态对话框
  • 使用QDialog::show()实现非模态对话框。

模态对话框

  • Qt 有两种级别的模态对话框:
    • 应用程序级别的模态

当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。

    • 窗口级别的模态

该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。窗口级别的模态尤其适用于多窗口模式。
一般默认是应用程序级别的模态。
在下面的示例中,我们调用了exec()将对话框显示出来,因此这就是一个模态对话框。当对话框出现时,我们不能与主窗口进行任何交互,直到我们关闭了该对话框。

cpp


非模态对话框

下面我们试着将exec()修改为show(),看看非模态对话框:

cpp


是不是事与愿违?对话框竟然一闪而过!这是因为,show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。注意,dialog 是建立在栈上的,show()函数返回,MainWindow::open()函数结束,dialog 超出作用域被析构,因此对话框消失了。知道了原因就好改了,我们将 dialog 改成堆上建立,当然就没有这个问题了:

cppsetWindowTitle(tr(\"Hello, dialog!\"));\ndialog->show();]]>


如果你足够细心,应该发现上面的代码是有问题的:dialog 存在内存泄露!dialog 使用 new 在堆上分配空间,却一直没有 delete。解决方案也很简单:将 MainWindow 的指针赋给 dialog 即可。还记得我们前面说过的 Qt 的对象系统吗?
不过,这样做有一个问题:如果我们的对话框不是在一个界面类中出现呢?由于QWidget的 parent 必须是QWidget指针,那就限制了我们不能将一个普通的 C++ 类指针传给 Qt 对话框。另外,如果对内存占用有严格限制的话,当我们将主窗口作为 parent 时,主窗口不关闭,对话框就不会被销毁,所以会一直占用内存。在这种情景下,我们可以设置 dialog 的WindowAttribute:

cppsetAttribute(Qt::WA_DeleteOnClose);\ndialog->setWindowTitle(tr(\"Hello, dialog!\"));\ndialog->show();]]>


setAttribute()函数设置对话框关闭时,自动销毁对话框。

_Toc466556786_Toc4718420156.4 消息对话框

QMessageBox用于显示消息提示。我们一般会使用其提供的几个 static 函数:

  • 显示关于对话框。
cpp


这是一个最简单的对话框,其标题是 title,内容是 text,父窗口是 parent。对话框只有一个 OK 按钮。

  • 显示关于 Qt 对话框。该对话框用于显示有关 Qt 的信息。
cpp


  • 显示严重错误对话框。
cpp


这个对话框将显示一个红色的错误符号。我们可以通过 buttons 参数指明其显示的按钮。默认情况下只有一个 Ok 按钮,我们可以使用StandardButtons类型指定多种按钮。

  • 与QMessageBox::critical()类似,不同之处在于这个对话框提供一个普通信息图标。
cpp


  • 与QMessageBox::critical ()类似,不同之处在于这个对话框提供一个问号图标,并且其显示的按钮是"是"和"否"。
cpp


  • 与QMessageBox::critical()类似,不同之处在于这个对话框提供一个黄色叹号图标。
cpp


我们可以通过下面的代码来演示下如何使用QMessageBox。

cpp


我们使用QMessageBox::question()来询问一个问题。

  • 这个对话框的父窗口是 this。

QMessageBox是QDialog的子类,这意味着它的初始显示位置将会是在 parent 窗口的中央。

  • 第二个参数是对话框的标题。
  • 第三个参数是我们想要显示的内容。
  • 第四个参数是关联的按键类型,我们可以使用或运算符(|)指定对话框应该出现的按钮。比如我们希望是一个 Yes 和一个 No。
  • 最后一个参数指定默认选择的按钮。

这个函数有一个返回值,用于确定用户点击的是哪一个按钮。按照我们的写法,应该很容易的看出,这是一个模态对话框,因此我们可以直接获取其返回值。
QMessageBox类的 static 函数优点是方便使用,缺点也很明显:非常不灵活。我们只能使用简单的几种形式。为了能够定制QMessageBox细节,我们必须使用QMessageBox的属性设置 API。如果我们希望制作一个询问是否保存的对话框,我们可以使用如下的代码:

cpp

msgBox 是一个建立在栈上的QMessageBox实例。我们设置其主要文本信息为“The document has been modified.”,informativeText 则是会在对话框中显示的简单说明文字。下面我们使用了一个detailedText,也就是详细信息,当我们点击了详细信息按钮时,对话框可以自动显示更多信息。我们自己定义的对话框的按钮有三个:保存、丢弃和取消。然后我们使用了exec()是其成为一个模态对话框,根据其返回值进行相应的操作。

6.5 标准文件对话框

QFileDialog,也就是文件对话框。在本节中,我们将尝试编写一个简单的文本文件编辑器,我们将使用QFileDialog来打开一个文本文件,并将修改过的文件保存到硬盘。

首先,我们需要创建一个带有文本编辑功能的窗口。借用我们前面的程序代码,应该可以很方便地完成:

cppsetStatusTip(tr(\"Open an existing file\"));\n\nsaveAction = new QAction(QIcon(\":/images/file-save\"), tr(\"&Save...\"), this);\nsaveAction->setStatusTip(tr(\"Save a new file\"));\n\nQMenu *file = menuBar()->addMenu(tr(\"&File\"));\nfile->addAction(openAction);\nfile->addAction(saveAction);\n\nQToolBar *toolBar = addToolBar(tr(\"&File\"));\ntoolBar->addAction(openAction);\ntoolBar->addAction(saveAction);\n\ntextEdit = new QTextEdit(this);\nsetCentralWidget(textEdit);\n\n]]>

我们在菜单和工具栏添加了两个动作:打开和保存。接下来是一个QTextEdit类,这个类用于显示富文本文件。也就是说,它不仅仅用于显示文本,还可以显示图片、表格等等。不过,我们现在只用它显示纯文本文件。QMainWindow有一个setCentralWidget()函数,可以将一个组件作为窗口的中心组件,放在窗口中央显示区。显然,在一个文本编辑器中,文本编辑区就是这个中心组件,因此我们将QTextEdit作为这种组件。

我们使用connect()函数,为这两个QAction对象添加响应的动作:


cpp

下面是最主要的openFile()saveFile()这两个函数的代码:


cppsetText(in.readAll());\n file.close();\n } \nelse \n{\n QMessageBox::warning(this, tr(\"Path\"),\n tr(\"You did not select any file.\"));\n }\n}\n\n//保存文件\nvoid MainWindow::saveFile()\n{\n QString path = QFileDialog::getSaveFileName(this,\n tr(\"Open File\"), \".\", tr(\"Text Files(*.txt)\"));\n if(!path.isEmpty()) \n{\n QFile file(path);\n if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) \n{\n QMessageBox::warning(this, tr(\"Write File\"),\n tr(\"Cannot open file:\\n%1\").arg(path));\n return;\n }\n QTextStream out(&file);\n out << textEdit->toPlainText();\n file.close();\n } \nelse \n{\n QMessageBox::warning(this, tr(\"Path\"),\n tr(\"You did not select any file.\"));\n }\n}\n\n]]>

在openFile()函数中,我们使用QFileDialog::getOpenFileName()来获取需要打开的文件的路径。这个函数原型如下:


cpp

不过注意,它的所有参数都是可选的,因此在一定程度上说,这个函数也是简单的。这六个参数分别是:

  • parent:父窗口。

Qt 的标准对话框提供静态函数,用于返回一个模态对话框;

  • caption:对话框标题;
  • dir:对话框打开时的默认目录
    • “.” 代表程序运行目录
    • “/” 代表当前盘符的根目录(特指 Windows 平台;Linux 平台当然就是根目录),这个参数也可以是平台相关的,比如“C:\\\\”等;
  • filter:过滤器。

我们使用文件对话框可以浏览很多类型的文件,但是,很多时候我们仅希望打开特定类型的文件。比如,文本编辑器希望打开文本文件,图片浏览器希望打开图片文件。过滤器就是用于过滤特定的后缀名。如果我们使用“Image Files(*.jpg *.png)”,则只能显示后缀名是 jpg 或者 png 的文件。如果需要多个过滤器,使用“;;”分割,比如“JPEG Files(*.jpg);;PNG Files(*.png)”;

  • selectedFilter:默认选择的过滤器;
  • options:对话框的一些参数设定

比如只显示文件夹等等,它的取值是enum QFileDialog::Option,每个选项可以使用 | 运算组合起来。

QFileDialog::getOpenFileName()返回值是选择的文件路径。我们将其赋值给 path。通过判断 path 是否为空,可以确定用户是否选择了某一文件。只有当用户选择了一个文件时,我们才执行下面的操作。

在saveFile()中使用的QFileDialog::getSaveFileName()也是类似的。使用这种静态函数,在 WindowsMac OS 上面都是直接调用本地对话框,但是 Linux 上则是QFileDialog自己的模拟。这暗示了,如果你不使用这些静态函数,而是直接使用QFileDialog进行设置,那么得到的对话框很可能与系统对话框的外观不一致。这一点是需要注意的。

","representation":"storage","_expandable":{"content":"/rest/api/content/58954535"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":36},"_links":{"webui":"/pages/viewpage.action?pageId=58954535","edit":"/pages/resumedraft.action?draftId=58954535","tinyui":"/x/J5ODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954535"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954535/child","restrictions":"/rest/api/content/58954535/restriction/byOperation","history":"/rest/api/content/58954535/history","ancestors":"","descendants":"/rest/api/content/58954535/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954536.json b/tools/wiki_data/bodies/58954536.json new file mode 100644 index 0000000..96921a9 --- /dev/null +++ b/tools/wiki_data/bodies/58954536.json @@ -0,0 +1 @@ +{"id":"58954536","type":"page","status":"current","title":"7 布局管理器","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/58954536/version/1"},"_expandable":{"content":"/rest/api/content/58954536"}},"body":{"storage":{"value":"

所谓 GUI 界面,归根结底,就是一堆组件的叠加。我们创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。在放置时,组件的位置尤其重要。我们必须要指定组件放在哪里,以便窗口能够按照我们需要的方式进行渲染。这就涉及到组件定位的机制。
\nQt 提供了两种组件定位机制:绝对定位和布局定位。

\n
    \n\t
  • 绝对定位就是一种最原始的定位方法:给出这个组件的坐标和长宽值。
  • \n
\n\n\n

这样,Qt 就知道该把组件放在哪里以及如何设置组件的大小。但是这样做带来的一个问题是,如果用户改变了窗口大小,比如点击最大化按钮或者使用鼠标拖动窗口边缘,采用绝对定位的组件是不会有任何响应的。这也很自然,因为你并没有告诉 Qt,在窗口变化时,组件是否要更新自己以及如何更新。或者,还有更简单的方法:禁止用户改变窗口大小。但这总不是长远之计。

\n
    \n\t
  • 布局定位:你只要把组件放入某一种布局,布局由专门的布局管理器进行管理。当需要调整大小或者位置的时候,Qt 使用对应的布局管理器进行调整。
  • \n
\n\n\n

布局定位完美的解决了使用绝对定位的缺陷。
\nQt 提供的布局中以下三种是我们最常用的:

\n
    \n\t
  • QHBoxLayout:按照水平方向从左到右布局;
  • \n\t
  • QVBoxLayout:按照竖直方向从上到下布局;
  • \n\t
  • QGridLayout:在一个网格中进行布局,类似于 HTML 的 table;
  • \n
\n\n\n

_Toc466556793_Toc4718420187.1 系统提供的布局控件

\n


\n这4个为系统给我们提供的布局的控件,但是使用起来不是非常的灵活,这里就不详细介绍了。

\n

_Toc4718420197.2 利用widget做布局

\n

第二种布局方式是利用控件里的widget来做布局,在Containers中
\n
\n在widget中的控件可以进行水平、垂直、栅格布局等操作,比较灵活。
\n再布局的同时我们需要灵活运用弹簧的特性让我们的布局更加的美观,下面是一个登陆窗口,利用widget可以搭建出如下登陆界面:
\n

","representation":"storage","_expandable":{"content":"/rest/api/content/58954536"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":44},"_links":{"webui":"/pages/viewpage.action?pageId=58954536","edit":"/pages/resumedraft.action?draftId=58954536","tinyui":"/x/KJODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954536"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954536/child","restrictions":"/rest/api/content/58954536/restriction/byOperation","history":"/rest/api/content/58954536/history","ancestors":"","descendants":"/rest/api/content/58954536/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954540.json b/tools/wiki_data/bodies/58954540.json new file mode 100644 index 0000000..51fb6f5 --- /dev/null +++ b/tools/wiki_data/bodies/58954540.json @@ -0,0 +1 @@ +{"id":"58954540","type":"page","status":"current","title":"8 常用控件","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/58954540/version/1"},"_expandable":{"content":"/rest/api/content/58954540"}},"body":{"storage":{"value":"

Qt为我们应用程序界面开发提供的一系列的控件,下面我们介绍两种最常用一些控件,所有控件的使用方法我们都可以通过帮助文档获取。

_Toc466556789_Toc4718420218.1 QLabel控件使用

QLabel是我们最常用的控件之一,其功能很强大,我们可以用来显示文本,图片和动画等。

_Toc471842022显示文字 (普通文本、html)

通过QLabel类的setText函数设置显示的内容:

cpp


  • 可以显示普通文本字符串
cppsetText(\"Hello, World!\");]]>


  • 可以显示HTML格式的字符串

比如显示一个链接:

cppsetText(\"Hello, World\");\nlabel ->setText(\"

百度一下

\");\nlabel ->setOpenExternalLinks(true);]]>


其中setOpenExternalLinks()函数是用来设置用户点击链接之后是否自动打开链接,如果参数指定为true则会自动打开。

_Toc471842023显示图片

可以使用QLabel的成员函数setPixmap设置图片

cpp


首先定义QPixmap对象

cpp


然后加载图片

cpp


最后将图片设置到QLabel中

cpp


_Toc471842024显示动画

可以使用QLabel 的成员函数setMovie加载动画,可以播放gif格式的文件

cpp


首先定义QMovied对象,并初始化:

cpp


播放加载的动画:

cppstart();]]>


将动画设置到QLabel中:

cppsetMovie(movie);]]>


_Toc466556790_Toc4718420258.2 QLineEdit

Qt提供的单行文本编辑框。

设置/获取内容

  • 获取编辑框内容使用text(),函数声明如下:
cpp


  • 设置编辑框内容
cpp


设置显示模式

使用QLineEdit类的setEchoMode () 函数设置文本的显示模式,函数声明:

cpp


EchoMode是一个枚举类型,一共定义了四种显示模式:

  • QLineEdit::Normal 模式显示方式,按照输入的内容显示。
  • QLineEdit::NoEcho 不显示任何内容,此模式下无法看到用户的输入。
  • QLineEdit::Password 密码模式,输入的字符会根据平台转换为特殊字符。
  • QLineEdit::PasswordEchoOnEdit 编辑时显示字符否则显示字符作为密码。

另外,我们再使用QLineEdit显示文本的时候,希望在左侧留出一段空白的区域,那么,就可以使用QLineEdit给我们提供的setTextMargins函数:

cpp


用此函数可以指定显示的文本与输入框上下左右边界的间隔的像素数。

_Toc466556791_Toc4718420268.3 其他控件

Qt中控件的使用方法可参考Qt提供的帮助文档。

_Toc466556794_Toc4718420278.4 自定义控件

在搭建Qt窗口界面的时候,在一个项目中很多窗口,或者是窗口中的某个模块会被经常性的重复使用。一般遇到这种情况我们都会将这个窗口或者模块拿出来做成一个独立的窗口类,以备以后重复使用。
在使用Qt的ui文件搭建界面的时候,工具栏栏中只为我们提供了标准的窗口控件,如果我们想使用自定义控件怎么办?
例如:我们从QWidget派生出一个类SmallWidget,实现了一个自定窗口,

cppaddWidget(spin);\nlayout->addWidget(slider);\n// 将布局设置到窗口中\nsetLayout(layout);\n// 添加消息响应\nconnect(spin,\nstatic_cast(&QSpinBox::valueChanged),\nslider, &QSlider::setValue);\nconnect(slider, &QSlider::valueChanged,\nspin, &QSpinBox::setValue);\n}]]>



那么这个SmallWidget可以作为独立的窗口显示,也可以作为一个控件来使用:
打开Qt的.ui文件,因为SmallWidget是派生自Qwidget类,所以需要在ui文件中先放入一个QWidget控件, 然后再上边鼠标右键

弹出提升窗口部件对话框

添加要提升的类的名字,然后选择 添加

添加之后,类名会显示到上边的列表框中,然后单击提升按钮,完成操作.
我们可以看到, 这个窗口对应的类从原来的QWidget变成了SmallWidget

再次运行程序,这个widget_3中就能显示出我们自定义的窗口了.

","representation":"storage","_expandable":{"content":"/rest/api/content/58954540"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":47},"_links":{"webui":"/pages/viewpage.action?pageId=58954540","edit":"/pages/resumedraft.action?draftId=58954540","tinyui":"/x/LJODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954540"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954540/child","restrictions":"/rest/api/content/58954540/restriction/byOperation","history":"/rest/api/content/58954540/history","ancestors":"","descendants":"/rest/api/content/58954540/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954546.json b/tools/wiki_data/bodies/58954546.json new file mode 100644 index 0000000..26b7ba1 --- /dev/null +++ b/tools/wiki_data/bodies/58954546.json @@ -0,0 +1 @@ +{"id":"58954546","type":"page","status":"current","title":"9 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:26.000Z","message":"Migration of unmigrated content due to installation of a new plugin","number":2,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954546/version/2"},"_expandable":{"content":"/rest/api/content/58954546"}},"body":{"storage":{"value":"

_Toc466556796_Toc4718420299.1 事件

事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),关于这一点,会在后边详细说明。
在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如

    • keyPressEvent()
    • keyReleaseEvent()
    • mouseDoubleClickEvent()
    • mouseMoveEvent()
    • mousePressEvent()
    • mouseReleaseEvent() 等。

这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。下面来看一个例子:


cppsetText(QString(\"

Move: (%1, %2)\n

\").arg(QString::number(event->x()),\n QString::number(event->y())));\n}\n \nvoid EventLabel::mousePressEvent(QMouseEvent *event)\n{\n this->setText(QString(\"

Press:(%1, %2)\n

\").arg(QString::number(event->x()),\n QString::number(event->y())));\n}\n \nvoid EventLabel::mouseReleaseEvent(QMouseEvent *event)\n{\n QString msg;\n msg.sprintf(\"

Release: (%d, %d)

\",\n event->x(), event->y());\n this->setText(msg);\n}\n \nint main(int argc, char *argv[])\n{\n QApplication a(argc, argv);\n \n EventLabel *label = new EventLabel;\n label->setWindowTitle(\"MouseEvent Demo\");\n label->resize(300, 200);\n label->show();\n \n return a.exec();\n}\n\n]]>


  • EventLabel继承了QLabel,覆盖了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三个函数。我们并没有添加什么功能,只是在鼠标按下(press)、鼠标移动(move)和鼠标释放(release)的时候,把当前鼠标的坐标值显示在这个Label上面。由于QLabel是支持 HTML 代码的,因此我们直接使用了 HTML 代码来格式化文字。
  • QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种。

  • 在mouseReleaseEvent()函数中,我们使用了另外一种QString的构造方法。我们使用类似 C 风格的格式化函数sprintf()来构造QString。

运行上面的代码,当我们点击了一下鼠标之后,label 上将显示鼠标当前坐标值。

为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?
这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。
知道了这一点,我们就可以在main()函数中添加如下代码:

cppsetMouseTracking(true);]]>


在运行程序就没有这个问题了。

_Toc466556797_Toc4718420309.2 event()

事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。
如上所述,event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。例如,我们希望在一个QWidget组件中监听 tab 键的按下,那么就可以继承QWidget,并重写它的event()函数,来达到这个目的:

cpptype() == QEvent::KeyPress) {\nQKeyEvent *keyEvent = static_cast(e);\nif (keyEvent->key() == Qt::Key_Tab) {\nqDebug() << \"You press tab.\";\nreturn true;\n}\n}\nreturn QWidget::event(e);\n}]]>


CustomWidget是一个普通的QWidget子类。我们重写了它的event()函数,这个函数有一个QEvent对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。

  • 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
  • 在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播

我们可以通过使用QEvent::type()函数可以检查事件的实际类型,其返回值是QEvent::Type类型的枚举。我们处理过自己感兴趣的事件之后,可以直接返回 true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的event()函数继续转发,否则这个组件就只能处理我们定义的事件了。为了测试这一种情况,我们可以尝试下面的代码:

cpptype() == QEvent::KeyPress)\n{\nQKeyEvent *keyEvent = static_cast(e);\nif (keyEvent->key() == Qt::Key_Tab)\n{\nqDebug() << \"You press tab.\";\nreturn true;\n}\n}\nreturn false;\n}]]>


CustomTextEdit是QTextEdit的一个子类。我们重写了其event()函数,却没有调用父类的同名函数。这样,我们的组件就只能处理 Tab 键,再也无法输入任何文本,也不能响应其它事件,比如鼠标点击之后也不会有光标出现。这是因为我们只处理的KeyPress类型的事件,并且如果不是KeyPress事件,则直接返回 false,鼠标事件根本不会被转发,也就没有了鼠标事件。
通过查看QObject::event()的实现,我们可以理解,event()函数同前面的章节中我们所说的事件处理器有什么联系:

这是 Qt 5 中QObject::event()函数的源代码(Qt 4 的版本也是类似的)。我们可以看到,同前面我们所说的一样,Qt 也是使用QEvent::type()判断事件类型,然后调用了特定的事件处理器。比如,如果event->type()返回值是QEvent::Timer,则调用timerEvent()函数。可以想象,QWidget::event()中一定会有如下的代码:

cpptype()) {\ncase QEvent::MouseMove:\nmouseMoveEvent((QMouseEvent*)event);\nbreak;\n// ...\n}]]>


事实也的确如此。timerEvent()和mouseMoveEvent()这样的函数,就是我们前面章节所说的事件处理器 event handler。也就是说,event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理"委托"给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。
由此可以见,event()是一个集中处理不同类型的事件的地方。如果你不想重写一大堆事件处理器,就可以重写这个event()函数,通过QEvent::type()判断不同的事件。鉴于重写event()函数需要十分小心注意父类的同名函数的调用,一不留神就可能出现问题,所以一般还是建议只重写事件处理器(当然,也必须记得是不是应该调用父类的同名处理器)。这其实暗示了event()函数的另外一个作用:屏蔽掉某些不需要的事件处

cpptype()) {\ncase QEvent::Timer:\ntimerEvent((QTimerEvent*)e);\nbreak;\ncase QEvent::ChildAdded:\ncase QEvent::ChildPolished:\ncase QEvent::ChildRemoved:\nchildEvent((QChildEvent*)e);\nbreak;\n// ...\ndefault:\nif (e->type() >= QEvent::User) {\ncustomEvent(e);\nbreak;\n}\nreturn false;\n}\nreturn true;\n}]]>

理器。正如我们前面的CustomTextEdit例子看到的那样,我们创建了一个只能响应 tab 键的组件。这种作用是重写事件处理器所不能实现的。

_Toc466556798_Toc4718420319.3 事件过滤器

有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。
通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器。
QObject有一个eventFilter()函数,用于建立事件过滤器。函数原型如下:
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
这个函数正如其名字显示的那样,是一个"事件过滤器"。所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。
我们来看一段简单的代码:

cppinstallEventFilter(this);\n}\nbool MainWindow::eventFilter(QObject *obj, QEvent *event)\n{\nif (obj == textEdit) {\nif (event->type() == QEvent::KeyPress) {\nQKeyEvent *keyEvent = static_cast(event);\nqDebug() << \"Ate key press\" << keyEvent->key();\nreturn true;\n} else {\nreturn false;\n}\n} else {\n// pass the event on to the parent class\nreturn QMainWindow::eventFilter(obj, event);\n}\n}]]>


  • MainWindow是我们定义的一个类。我们重写了它的eventFilter()函数。为了过滤特定组件上的事件,首先需要判断这个对象是不是我们感兴趣的组件,然后判断这个事件的类型。在上面的代码中,我们不想让textEdit组件处理键盘按下的事件。所以,首先我们找到这个组件,如果这个事件是键盘事件,则直接返回 true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回 false。对于其它的组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。
  • eventFilter()函数相当于创建了过滤器,然后我们需要安装这个过滤器。安装过滤器需要调用QObject::installEventFilter()函数。函数的原型如下:

void QObject::installEventFilter ( QObject * filterObj )
这个函数接受一个QObject *类型的参数。记得刚刚我们说的,eventFilter()函数是QObject的一个成员函数,因此,任意QObject都可以作为事件过滤器(问题在于,如果你没有重写eventFilter()函数,这个事件过滤器是没有任何作用的,因为默认什么都不会过滤)。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。

  • 我们可以向一个对象上面安装多个事件处理器,只要调用多次installEventFilter()函数。如果一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是后进先执行的顺序。


还记得我们前面的那个例子吗?我们使用event()函数处理了 Tab 键:

cpptype() == QEvent::KeyPress) {\nQKeyEvent *keyEvent = static_cast(e);\nif (keyEvent->key() == Qt::Key_Tab) {\nqDebug() << \"You press tab.\";\nreturn true;\n}\n}\nreturn QWidget::event(e);\n}\n现在,我们可以给出一个使用事件过滤器的版本:\nbool FilterObject::eventFilter(QObject *object, QEvent *event)\n{\nif (object == target && event->type() == QEvent::KeyPress)\n{\nQKeyEvent *keyEvent = static_cast(event);\nif (keyEvent->key() == Qt::Key_Tab) {\nqDebug() << \"You press tab.\";\nreturn true;\n} else {\nreturn false;\n}\n}\nreturn false;\n}]]>


事件过滤器的强大之处在于,我们可以为整个应用程序添加一个事件过滤器。记得,installEventFilter()函数是QObject的函数,QApplication或者QCoreApplication对象都是QObject的子类,因此,我们可以向QApplication或者QCoreApplication添加事件过滤器。这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管很强大,但这种行为会严重降低整个应用程序的事件分发效率。因此,除非是不得不使用的情况,否则的话我们不应该这么做。
注意,
事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

_Toc466556799_Toc4718420329.4 总结

Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂。说它复杂,更多是因为它涉及到的函数众多,而处理方法也很多,有时候让人难以选择。现在我们简单总结一下 Qt 中的事件机制。
Qt 中有很多种事件:鼠标事件、键盘事件、大小改变的事件、位置移动的事件等等。如何处理这些事件,实际有两种选择:

  • 所有事件对应一个事件处理函数,在这个事件处理函数中用一个很大的分支语句进行选择,其代表作就是 win32 API 的WndProc()函数:
cpp


在这个函数中,我们需要使用switch语句,选择message参数的类型进行处理,典型代码是:

cpp


  • 每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制:
    • mouseEvent()
    • keyPressEvent()

Qt 具有这么多种事件处理函数,肯定有一个地方对其进行分发,否则,Qt 怎么知道哪一种事件调用哪一个事件处理函数呢?这个分发的函数,就是event()。显然,当QMouseEvent产生之后,event()函数将其分发给mouseEvent()事件处理器进行处理。
event()函数会有两个问题:

  • event()函数是一个 protected 的函数,这意味着我们要想重写event(),必须继承一个已有的类。试想,我的程序根本不想要鼠标事件,程序中所有组件都不允许处理鼠标事件,是不是我得继承所有组件,一一重写其event()函数?protected 函数带来的另外一个问题是,如果我基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的。我怎么去继承这种库中的组件呢?
  • event()函数的确有一定的控制,不过有时候我的需求更严格一些:我希望那些组件根本看不到这种事件。event()函数虽然可以拦截,但其实也是接收到了QMouseEvent对象。我连让它收都收不到。这样做的好处是,模拟一种系统根本没有那个事件的效果,所以其它组件根本不会收到这个事件,也就无需修改自己的事件处理函数。这种需求怎么办呢?

这两个问题是event()函数无法处理的。于是,Qt 提供了另外一种解决方案:事件过滤器。事件过滤器给我们一种能力,让我们能够完全移除某种事件。事件过滤器可以安装到任意QObject类型上面,并且可以安装多个。如果要实现全局的事件过滤器,则可以安装到QApplication或者QCoreApplication上面。这里需要注意的是,如果使用installEventFilter()函数给一个对象安装事件过滤器,那么该事件过滤器只对该对象有效,只有这个对象的事件需要先传递给事件过滤器的eventFilter()函数进行过滤,其它对象不受影响。如果给QApplication对象安装事件过滤器,那么该过滤器对程序中的每一个对象都有效,任何对象的事件都是先传给eventFilter()函数。
事件过滤器可以解决刚刚我们提出的event()函数的两点不足:

  • 首先,事件过滤器不是 protected 的,因此我们可以向任何QObject子类安装事件过滤器;
  • 其次,事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件。

事实上,还有一种方法,我们没有介绍。Qt 事件的调用最终都会追溯到QCoreApplication::notify()函数,因此,最大的控制权实际上是重写QCoreApplication::notify()。这个函数的声明是:

cpp


该函数会将event发送给receiver,也就是调用receiver->event(event),其返回值就是来自receiver的事件处理器。注意,这个函数为任意线程的任意对象的任意事件调用,因此,它不存在事件过滤器的线程的问题。不过我们并不推荐这么做,因为notify()函数只有一个,而事件过滤器要灵活得多。
现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:

  • 重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
  • 重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。
  • 在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
  • 在QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
  • 重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。

","representation":"storage","_expandable":{"content":"/rest/api/content/58954546"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":57},"_links":{"webui":"/pages/viewpage.action?pageId=58954546","edit":"/pages/resumedraft.action?draftId=58954546","tinyui":"/x/MpODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954546"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954546/child","restrictions":"/rest/api/content/58954546/restriction/byOperation","history":"/rest/api/content/58954546/history","ancestors":"","descendants":"/rest/api/content/58954546/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954548.json b/tools/wiki_data/bodies/58954548.json new file mode 100644 index 0000000..97492ec --- /dev/null +++ b/tools/wiki_data/bodies/58954548.json @@ -0,0 +1 @@ +{"id":"58954548","type":"page","status":"current","title":"10 绘图和绘图设备","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/58954548/version/1"},"_expandable":{"content":"/rest/api/content/58954548"}},"body":{"storage":{"value":"

_Toc466556801_Toc47184203410.1 QPainter

Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类。
QPainter用来执行绘制的操作;QPaintDevice是一个二维空间的抽象,这个二维空间允许QPainter在其上面进行绘制,也就是QPainter工作的空间;QPaintEngine提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口。QPaintEngine类应用于QPainter和QPaintDevice之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心QPaintEngine这个类的。我们可以把QPainter理解成画笔;把QPaintDevice理解成使用画笔的地方,比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了QPaintEngine类,这个类让不同的纸张、屏幕都能使用一种画笔。
下图给出了这三个类之间的层次结构:

上面的示意图告诉我们,Qt 的绘图系统实际上是,使用QPainter在QPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。
下面我们通过一个实例来介绍QPainter的使用:

cpp


注意我们重写了QWidget的paintEvent()函数。接下来就是PaintedWidget的源代码:

cpp


在构造函数中,我们仅仅设置了窗口的大小和标题。而paintEvent()函数则是绘制的代码。首先,我们在栈上创建了一个QPainter对象,也就是说,每次运行paintEvent()函数的时候,都会重建这个QPainter对象。注意,这一点可能会引发某些细节问题:由于我们每次重建QPainter,因此第一次运行时所设置的画笔颜色、状态等,第二次再进入这个函数时就会全部丢失。有时候我们希望保存画笔状态,就必须自己保存数据,否则的话则需要将QPainter作为类的成员变量。
QPainter接收一个QPaintDevice指针作为参数。QPaintDevice有很多子类,比如QImage,以及QWidget。注意回忆一下,QPaintDevice可以理解成要在哪里去绘制,而现在我们希望画在这个组件,因此传入的是 this 指针。
QPainter有很多以 draw 开头的函数,用于各种图形的绘制,比如这里的drawLine(),drawRect()以及drawEllipse()等。当绘制轮廓线时,使用QPainter的pen()属性。比如,我们调用了painter.setPen(Qt::red)将 pen 设置为红色,则下面绘制的矩形具有红色的轮廓线。接下来,我们将 pen 修改为绿色,5 像素宽(painter.setPen(QPen(Qt::green, 5))),又设置了画刷为蓝色。这时候再调用 draw 函数,则是具有绿色 5 像素宽轮廓线、蓝色填充的椭圆。

_Toc466556802_Toc47184203510.2 绘图设备

绘图设备是指继承QPainterDevice的子类。Qt提供了很多这样的类,例如QPixmap、QBitmap、QImage和 QPicture。其中,

  • QPixmap专门为图像在屏幕上的显示做了优化
  • QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
  • QImage专门为图像的像素级访问做了优化。
  • QPicture则可以记录和重现QPainter的各条命令。

_Toc466556803_Toc47184203610.2.1 QPixmap、QBitmap、QImage

QPixmap继承了QPaintDevice,因此,你可以使用QPainter直接在上面绘制图形。QPixmap也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开png、jpeg之类的文件,就可以使用 QPixmap。使用QPainter的drawPixmap()函数可以把这个文件绘制到一个QLabel、QPushButton或者其他的设备上面。QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap的显示可能会有所差别。
QBitmap继承自QPixmap,因此具有QPixmap的所有特性,提供单色图像。QBitmap的色深始终为1. 色深这个概念来自计算机图形学,是指用于表现颜色的二进制的位数。我们知道,计算机里面的数据都是使用二进制表示的。为了表示一种颜色,我们也会使用二进制。比如我们要表示8种颜色,需要用3个二进制位,这时我们就说色深是3. 因此,所谓色深为1,也就是使用1个二进制位表示颜色。1个位只有两种状态:0和1,因此它所表示的颜色就有两种,黑和白。所以说,QBitmap实际上是只有黑白两色的图像数据。
由于QBitmap色深小,因此只占用很少的存储空间,所以适合做光标文件和笔刷。
下面我们来看同一个图像文件在QPixmap和QBitmap下的不同表现:

cpp



这里我们给出了两张png图片。butterfly1.png是没有透明色的纯白背景,而butterfly.png是具有透明色的背景。我们分别使用QPixmap和QBitmap来加载它们。注意看它们的区别:白色的背景在QBitmap中消失了,而透明色在QBitmap中转换成了黑色;其他颜色则是使用点的疏密程度来体现的。
QPixmap使用底层平台的绘制系统进行绘制,无法提供像素级别的操作,而QImage则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。
我们声明了一个QImage对象,大小是300 x 300,颜色模式是RGB32,即使用32位数值表示一个颜色的RGB值,也就是说每种颜色使用8位。然后我们对每个像素进行颜色赋值,从而构成了这个图像。我们可以把QImage想象成一个RGB颜色的二维数组,记录了每一像素的颜色。

cpp



QImage与QPixmap的区别

    • QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的
    • QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果
    • 由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。
    • QImage可通过setPixpel()和pixel()等方法直接存取指定的像素。

QImage与QPixmap之间的转换:

  • QImage转QPixmap

使用QPixmap的静态成员函数: fromImage()
QPixmapfromImage(const QImage & image,
Qt::ImageConversionFlags flags = Qt::AutoColor)

  • QPixmap转QImage:

使用QPixmap类的成员函数: toImage()
QImage toImage() const

_Toc466556804_Toc47184203710.2.2 QPicture

最后一个需要说明的是QPicture。这是一个可以记录和重现QPainter命令的绘图设备。 QPicture将QPainter的命令序列化到一个IO设备,保存为一个平台独立的文件格式。这种格式有时候会是"元文件(meta- files)"。Qt的这种格式是二进制的,不同于某些本地的元文件,Qt的pictures文件没有内容上的限制,只要是能够被QPainter绘制的元素,不论是字体还是pixmap,或者是变换,都可以保存进一个picture中。
QPicture是平台无关的,因此它可以使用在多种设备之上,比如svg、pdf、ps、打印机或者屏幕。回忆下我们这里所说的QPaintDevice,实际上是说可以有QPainter绘制的对象。QPicture使用系统的分辨率,并且可以调整 QPainter来消除不同设备之间的显示差异。
如果我们要记录下QPainter的命令,首先要使用QPainter::begin()函数,将QPicture实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用QPainter::end()命令终止。代码示例如下:

cpp","representation":"storage","_expandable":{"content":"/rest/api/content/58954548"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":62},"_links":{"webui":"/pages/viewpage.action?pageId=58954548","edit":"/pages/resumedraft.action?draftId=58954548","tinyui":"/x/NJODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954548"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954548/child","restrictions":"/rest/api/content/58954548/restriction/byOperation","history":"/rest/api/content/58954548/history","ancestors":"","descendants":"/rest/api/content/58954548/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/bodies/58954552.json b/tools/wiki_data/bodies/58954552.json new file mode 100644 index 0000000..891ca36 --- /dev/null +++ b/tools/wiki_data/bodies/58954552.json @@ -0,0 +1 @@ +{"id":"58954552","type":"page","status":"current","title":"11 文件系统","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/58954552/version/1"},"_expandable":{"content":"/rest/api/content/58954552"}},"body":{"storage":{"value":"

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):

  • QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;
  • QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。
  • QFlie:访问本地文件或者嵌入资源;
  • QTemporaryFile:创建和访问本地文件系统的临时文件;
  • QBuffer:读写QbyteArray, 内存文件;
  • QProcess:运行外部程序,处理进程间通讯;
  • QAbstractSocket:所有套接字类的父类;
  • QTcpSocket:TCP协议网络数据传输;
  • QUdpSocket:传输 UDP 报文;
  • QSslSocket:使用 SSL/TLS 传输数据;

文件系统分类:

  • 顺序访问设备:

是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节,这其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。

  • 随机访问设备:

可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针,QFile、QOLE_LINK9OLE_LINK10TemporaryFile和QBuffer是随机访问设备,

_Toc466556806_Toc47184203911.1 基本文件操作

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。在所有的 I/O 设备中,文件 I/O 是最重要的部分之一。因为我们大多数的程序依旧需要首先访问本地文件(当然,在云计算大行其道的将来,这一观点可能改变)。QFile提供了从文件中读取和写入数据的能力。

我们通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改。QFile需要使用 / 作为文件分隔符,不过,它会自动将其转换成操作系统所需要的形式。例如 C:/windows 这样的路径在 Windows 平台下同样是可以的。

QFile主要提供了有关文件的各种操作,比如打开文件、关闭文件、刷新文件等。我们可以使用QDataStreamQTextStream类来读写文件,也可以使用QIODevice类提供的read()readLine()readAll()以及write()这样的函数。值得注意的是,有关文件本身的信息,比如文件名、文件所在目录的名字等,则是通过QFileInfo获取,而不是自己分析文件路径字符串。

下面我们使用一段代码来看看QFile的有关操作:


cpp


  • 我们首先使用QFile创建了一个文件对象。

这个文件名字是 in.txt。如果你不知道应该把它放在哪里,可以使用QDir::currentPath()来获得应用程序执行时的当前路径。只要将这个文件放在与当前路径一致的目录下即可。

  • 使用open()函数打开这个文件,打开形式是只读方式,文本格式。

这个类似于fopen()的 r 这样的参数。open()函数返回一个 bool 类型,如果打开失败,我们在控制台输出一段提示然后程序退出。否则,我们利用 while 循环,将每一行读到的内容输出。

  • 可以使用QFileInfo获取有关该文件的信息。

QFileInfo有很多类型的函数,我们只举出一些例子。比如:

    • isDir()检查该文件是否是目录;
    • isExecutable() 检查该文件是否是可执行文件等。
    • baseName() 可以直接获得文件名;
    • completeBaseName() 获取完整的文件名
    • suffix() 则直接获取文件后缀名。
    • completeSuffix() 获取完整的文件后缀

我们可以由下面的示例看到,baseName()和completeBaseName(),以及suffix()和completeSuffix()的区别:

cpp


_Toc466556807_Toc47184204011.2 二进制文件读写

QDataStream提供了基于QIODevice的二进制数据的序列化。数据流是一种二进制流,这种流完全不依赖于底层操作系统、CPU 或者字节顺序(大端或小端)。例如,在安装了 Windows 平台的 PC 上面写入的一个数据流,可以不经过任何处理,直接拿到运行了 Solaris 的 SPARC 机器上读取。由于数据流就是二进制流,因此我们也可以直接读写没有编码的二进制数据,例如图像、视频、音频等。
QDataStream既能够存取 C++ 基本类型,如 int、char、short 等,也可以存取复杂的数据类型,例如自定义的类。实际上,QDataStream对于类的存储,是将复杂的类分割为很多基本单元实现的。
结合QIODevice,QDataStream可以很方便地对文件、网络套接字等进行读写操作。我们从代码开始看起:

cpp


  • 在这段代码中,我们首先打开一个名为 file.dat 的文件(注意,我们为简单起见,并没有检查文件打开是否成功,这在正式程序中是不允许的)。然后,我们将刚刚创建的file对象的指针传递给一个QDataStream实例out。类似于std::cout标准输出流,QDataStream也重载了输出重定向<<运算符。后面的代码就很简单了:将"the answer is"和数字 42 输出到数据流。由于我们的 out 对象建立在file之上,因此相当于将问题和答案写入file。
  • 需要指出一点:最好使用 Qt 整型来进行读写,比如程序中的qint32。这保证了在任意平台和任意编译器都能够有相同的行为。

如果你直接运行这段代码,你会得到一个空白的 file.dat,并没有写入任何数据。这是因为我们的file没有正常关闭。为性能起见,数据只有在文件关闭时才会真正写入。因此,我们必须在最后添加一行代码:
file.close(); // 如果不想关闭文件,可以使用 file.flush();
接下来我们将存储到文件中的答案取出来

cpp> str >> a;]]>


唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。
那么,既然QIODevice提供了read()、readLine()之类的函数,为什么还要有QDataStream呢?QDataStream同QIODevice有什么区别?区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API 更好一些。我们通过下面一段代码看看什么是流的形式:

cpp


_Toc466556808_Toc47184204111.3 文本文件读写

上一节我们介绍了有关二进制文件的读写。二进制文件比较小巧,却不是人可读的格式。而文本文件是一种人可读的文件。为了操作这种文件,我们需要使用QTextStream类。QTextStream和QDataStream的使用类似,只不过它是操作纯文本文件的。
QTextStream会自动将 Unicode 编码同操作系统的编码进行转换,这一操作对开发人员是透明的。它也会将换行符进行转换,同样不需要自己处理。QTextStream使用 16 位的QChar作为基础的数据存储单位,同样,它也支持 C++ 标准类型,如 int 等。实际上,这是将这种标准类型与字符串进行了相互转换。
QTextStream同QDataStream的使用基本一致,例如下面的代码将把"The answer is 42"写入到 file.txt 文件中:

cpp


这里,我们在open()函数中增加了QIODevice::Truncate打开方式。我们可以从下表中看到这些打开方式的区别:
枚举值 描述

  • QIODevice::NotOpen 未打开
  • QIODevice::ReadOnly 以只读方式打开
  • QIODevice::WriteOnly 以只写方式打开
  • QIODevice::ReadWrite 以读写方式打开
  • QIODevice::Append 以追加的方式打开,

新增加的内容将被追加到文件末尾

  • QIODevice::Truncate 以重写的方式打开,在写入新的数据时会将原有

数据全部清除,游标设置在文件开头。

  • QIODevice::Text 在读取时,将行结束符转换成 \\n;在写入时,

将行结束符转换成本地格式,例如 Win32 平台
上是 \\r\\n

  • QIODevice::Unbuffered忽略缓存

我们在这里使用了QFile::WriteOnly | QIODevice::Truncate,也就是以只写并且覆盖已有内容的形式操作文件。注意,QIODevice::Truncate会直接将文件内容清空。
虽然QTextStream的写入内容与QDataStream一致,但是读取时却会有些困难:

cpp> str >> ans;\n}]]>


在使用QDataStream的时候,这样的代码很方便,但是使用了QTextStream时却有所不同:读出的时候,str 里面将是 The answer is 42,ans 是 0。这是因为当使用QDataStream写入的时候,实际上会在要写入的内容前面,额外添加一个这段内容的长度值。而以文本形式写入数据,是没有数据之间的分隔的。因此,使用文本文件时,很少会将其分割开来读取,而是使用诸如使用:

  • QTextStream::readLine() 读取一行
  • QTextStream::readAll()读取所有文本

这种函数之后再对获得的QString对象进行处理。
默认情况下,QTextStream的编码格式是 Unicode,如果我们需要使用另外的编码,可以使用:
stream.setCodec("UTF-8");
这样的函数进行设置。


","representation":"storage","_expandable":{"content":"/rest/api/content/58954552"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":67},"_links":{"webui":"/pages/viewpage.action?pageId=58954552","edit":"/pages/resumedraft.action?draftId=58954552","tinyui":"/x/OJODAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954552"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954552/child","restrictions":"/rest/api/content/58954552/restriction/byOperation","history":"/rest/api/content/58954552/history","ancestors":"","descendants":"/rest/api/content/58954552/descendant","space":"/rest/api/space/2CT"}} \ No newline at end of file diff --git a/tools/wiki_data/extracted_code.json b/tools/wiki_data/extracted_code.json new file mode 100644 index 0000000..3fc2970 --- /dev/null +++ b/tools/wiki_data/extracted_code.json @@ -0,0 +1,1589 @@ +{ + "58954436": [], + "58954437": [], + "58954438": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#include\nusing namespace std;\n\nint main(){\t\n\tcout << \"hello world\" << endl;\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954439": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//全局变量\nint a = 10;\nvoid test(){\n\t//局部变量\n\tint a = 20;\n\t//全局a被隐藏\n\tcout << \"a:\" << a << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//全局变量\nint a = 10;\n//1. 局部变量和全局变量同名\nvoid test(){\n\tint a = 20;\n\t//打印局部变量a\n\tcout << \"局部变量a:\" << a << endl;\n\t//打印全局变量a\n\tcout << \"全局变量a:\" << ::a << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace A{\n\tint a = 10;\n}\nnamespace B{\n\tint a = 20;\n}\nvoid test(){\n\tcout << \"A::a : \" << A::a << endl;\n\tcout << \"B::a : \" << B::a << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void test(){\n\tnamespace A{\n\t\tint a = 10;\n\t}\n\tnamespace B{\n\t\tint a = 20;\n\t}\n\tcout << \"A::a : \" << A::a << endl;\n\tcout << \"B::a : \" << B::a << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace A{\n\tint a = 10;\n\tnamespace B{\n\t\tint a = 20;\n\t}\n}\nvoid test(){\n\tcout << \"A::a : \" << A::a << endl;\n\tcout << \"A::B::a : \" << A::B::a << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace A{\n\tint a = 10;\n}\n\nnamespace A{\n\tvoid func(){\n\t\tcout << \"hello namespace!\" << endl;\n\t}\n}\n\nvoid test(){\n\tcout << \"A::a : \" << A::a << endl;\n\tA::func();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace MySpace{\n\tvoid func1();\n\tvoid func2(int param);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void MySpace::func1(){\n\tcout << \"MySpace::func1\" << endl;\n}\nvoid MySpace::func2(int param){\n\tcout << \"MySpace::func2 : \" << param << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace{\n\tint a = 10;\n\tvoid func(){ cout << \"hello namespace\" << endl; }\n}\nvoid test(){\n\tcout << \"a : \" << a << endl;\n\tfunc();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace veryLongName{\n\t\n\tint a = 10;\n\tvoid func(){ cout << \"hello namespace\" << endl; }\n}\n\nvoid test(){\n\tnamespace shortName = veryLongName;\n\tcout << \"veryLongName::a : \" << shortName::a << endl;\n\tveryLongName::func();\n\tshortName::func();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace A{\n\tint paramA = 20;\n\tint paramB = 30;\n\tvoid funcA(){ cout << \"hello funcA\" << endl; }\n\tvoid funcB(){ cout << \"hello funcA\" << endl; }\n}\n\nvoid test(){\n\t//1. 通过命名空间域运算符\n\tcout << A::paramA << endl;\n\tA::funcA();\n\t//2. using声明\n\tusing A::paramA;\n\tusing A::funcA;\n\tcout << paramA << endl;\n\t//cout << paramB << endl; //不可直接访问\n\tfuncA();\n\t//3. 同名冲突\n\t//int paramA = 20; //相同作用域注意同名冲突\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace A{\n\tvoid func(){}\n\tvoid func(int x){}\n\tint func(int x,int y){}\n}\nvoid test(){\n\tusing A::func;\n\tfunc();\n\tfunc(10);\n\tfunc(10, 20);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace A{\n\tint paramA = 20;\n\tint paramB = 30;\n\tvoid funcA(){ cout << \"hello funcA\" << endl; }\n\tvoid funcB(){ cout << \"hello funcB\" << endl; }\n}\n\nvoid test01(){\n\tusing namespace A;\n\tcout << paramA << endl;\n\tcout << paramB << endl;\n\tfuncA();\n\tfuncB();\n\n\t//不会产生二义性\n\tint paramA = 30;\n\tcout << paramA << endl;\n}\n\nnamespace B{\n\tint paramA = 20;\n\tint paramB = 30;\n\tvoid funcA(){ cout << \"hello funcA\" << endl; }\n\tvoid funcB(){ cout << \"hello funcB\" << endl; }\n}\n\nvoid test02(){\n\tusing namespace A;\n\tusing namespace B;\n\t//二义性产生,不知道调用A还是B的paramA\n\t//cout << paramA << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "int a = 10; //赋值,当做定义\nint a; //没有赋值,当做声明\n\nint main(){\n\tprintf(\"a:%d\\n\",a);\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//i没有写类型,可以是任意类型\nint fun1(i){\n\tprintf(\"%d\\n\", i);\n\treturn 0;\n}\n//i没有写类型,可以是任意类型\nint fun2(i){\n\tprintf(\"%s\\n\", i);\n\treturn 0;\n}\n//没有写参数,代表可以传任何类型的实参\nint fun3(){ \n\tprintf(\"fun33333333333333333\\n\");\n\treturn 0;\n}\n\n//C语言,如果函数没有参数,建议写void,代表没有参数\nint fun4(void){\n\tprintf(\"fun4444444444444\\n\");\n\treturn 0;\n}\n\ng(){\n\treturn 10;\n}\n\nint main(){\n\n\tfun1(10);\n\tfun2(\"abc\");\n\tfun3(1, 2, \"abc\");\n\tprintf(\"g = %d\\n\", g());\n\n\treturn 0;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "typedef enum COLOR{ GREEN, RED, YELLOW } color;\nint main(){\n\tcolor mycolor = GREEN;\n\tmycolor = 10;\n\tprintf(\"mycolor:%d\\n\", mycolor);\n\tchar* p = malloc(10);\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//1. 结构体中即可以定义成员变量,也可以定义成员函数\nstruct Student{\n\tstring mName;\n\tint mAge;\n\tvoid setName(string name){ mName = name; }\n\tvoid setAge(int age){ mAge = age; }\n\tvoid showStudent(){\n\t\tcout << \"Name:\" << mName << \" Age:\" << mAge << endl;\n\t}\n};\n\n//2. c++中定义结构体变量不需要加struct关键字\nvoid test01(){\n\tStudent student;\n\tstudent.setName(\"John\");\n\tstudent.setAge(20);\n\tstudent.showStudent();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void test()\n{\tcout << sizeof(false) << endl; //为1,//bool类型占一个字节大小\n\tbool flag = true; // c语言中没有这种类型\n\tflag = 100; //给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\tint a = 10;\n\tint b = 20;\n\tprintf(\"ret:%d\\n\", a > b ? a : b);\n\t//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?\n\t\n\t//(a > b ? a : b) = 100;\n\t//返回的是右值" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\tint a = 10;\n\tint b = 20;\n\tprintf(\"ret:%d\\n\", a > b ? a : b);\n\t//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?\n\n\tcout << \"b:\" << b << endl;\n\t//返回的是左值,变量的引用\n\t(a > b ? a : b) = 100;//返回的是左值,变量的引用\n\tcout << \"b:\" << b << endl;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": " const int constA = 10;\n int main(){\n int* p = (int*)&constA;\n *p = 200;\n }" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "    const int constA = 10;\n\n    int* p = (int*)&constA;\n\n    *p = 300;\n\n    printf(\"constA:%d\\n\",constA);\n\n    printf(\"*p:%d\\n\", *p);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\tconst int constA = 10;\n\tint* p = (int*)&constA;\n\t*p = 300;\n\tcout << \"constA:\" << constA << endl;\n\tcout << \"*p:\" << *p << endl;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\tint b = 10;\n\tconst int constA = b;\n\tint* p = (int*)&constA;\n\t*p = 300;\n\tcout << \"constA:\" << constA << endl;\n\tcout << \"*p:\" << *p << endl;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": " const Person person; //未初始化age\n\t//person.age = 50; //不可修改\n\tPerson* pPerson = (Person*)&person;\n\t//指针间接修改\n\tpPerson->age = 100;\n\tcout << \"pPerson->age:\" << pPerson->age << endl;\n\tpPerson->age = 200;\n\tcout << \"pPerson->age:\" << pPerson->age << endl;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define PARAM 128\n\nconst short param = 128;\n\nvoid func(short a){\n    cout << \"short!\" << endl;\n}\nvoid func(int a){\n    cout << \"int\" << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void func1(){\n    const int a = 10;\n    #define A 20\n    //#undef A  //卸载宏常量A\n}\n\nvoid func2(){\n    //cout << \"a:\" << a << endl; //不可访问,超出了const int a作用域\n    cout << \"A:\" << A << endl; //#define作用域从定义到文件结束或者到#undef,可访问\n}\n\nint main(){\n    func2();\n    return EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "namespace MySpace{\n    #define num 1024\n}\n\nvoid test(){\n    //cout << MySpace::NUM << endl; //错误\n    //int num = 100; //命名冲突\n    cout << num << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//1. 认识引用\nvoid test01(){\n\n\tint a = 10;\n\t//给变量a取一个别名b\n\tint& b = a;\n\tcout << \"a:\" << a << endl;\n\tcout << \"b:\" << b << endl;\n\tcout << \"------------\" << endl;\n\t//操作b就相当于操作a本身\n\tb = 100;\n\tcout << \"a:\" << a << endl;\n\tcout << \"b:\" << b << endl;\n\tcout << \"------------\" << endl;\n\t//一个变量可以有n个别名\n\tint& c = a;\n\tc = 200;\n\tcout << \"a:\" << a << endl;\n\tcout << \"b:\" << b << endl;\n\tcout << \"c:\" << c << endl;\n\tcout << \"------------\" << endl;\n\t//a,b,c的地址都是相同的\n\tcout << \"a:\" << &a << endl;\n\tcout << \"b:\" << &b << endl;\n\tcout << \"c:\" << &c << endl;\n}\n//2. 使用引用注意事项\nvoid test02(){\n\t//1) 引用必须初始化\n\t//int& ref; //报错:必须初始化引用\n\t//2) 引用一旦初始化,不能改变引用\n\tint a = 10;\n\tint b = 20;\n\tint& ref = a;\n\tref = b; //不能改变引用\n\t//3) 不能对数组建立引用\n\tint arr[10];\n\t//int& ref3[10] = arr;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\t//1. 建立数组引用方法一\n\ttypedef int ArrRef[10];\n\tint arr[10];\n\tArrRef& aRef = arr;\n\tfor (int i = 0; i < 10;i ++){\n\t\taRef[i] = i+1;\n\t}\n\tfor (int i = 0; i < 10;i++){\n\t\tcout << arr[i] << \" \";\n\t}\n\tcout << endl;\n\t//2. 建立数组引用方法二\n\tint(&f)[10] = arr;\n\tfor (int i = 0; i < 10; i++){\n\t\tf[i] = i+10;\n\t}\n\tfor (int i = 0; i < 10; i++){\n\t\tcout << arr[i] << \" \";\n\t}\n\tcout << endl;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//值传递\nvoid ValueSwap(int m,int n){\n\tint temp = m;\n\tm = n;\n\tn = temp;\n}\n//地址传递\nvoid PointerSwap(int* m,int* n){\n\tint temp = *m;\n\t*m = *n;\n\t*n = temp;\n}\n//引用传递\nvoid ReferenceSwap(int& m,int& n){\n\tint temp = m;\n\tm = n;\n\tn = temp;\n}\nvoid test(){\n\tint a = 10;\n\tint b = 20;\n\t//值传递\n\tValueSwap(a, b);\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n\t//地址传递\n\tPointerSwap(&a, &b);\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n\t//引用传递\n\tReferenceSwap(a, b);\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//返回局部变量引用\nint& TestFun01(){\n\tint a = 10; //局部变量\n\treturn a;\n}\n//返回静态变量引用\nint& TestFunc02(){\t\n\tstatic int a = 20;\n\tcout << \"static int a : \" << a << endl;\n\treturn a;\n}\nint main(){\n\t//不能返回局部变量的引用\n\tint& ret01 = TestFun01();\n\t//如果函数做左值,那么必须返回引用\n\tTestFunc02();\n\tTestFunc02() = 100;\n\tTestFunc02();\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//发现是引用,转换为 int* const ref = &a;\nvoid testFunc(int& ref){\n\tref = 100; // ref是引用,转换为*ref = 100\n}\nint main(){\n\tint a = 10;\n\tint& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化\n\taRef = 20; //内部发现aRef是引用,自动帮我们转换为: *aRef = 20;\n\tcout << \"a:\" << a << endl;\n\tcout << \"aRef:\" << aRef << endl;\n\ttestFunc(a);\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "struct Teacher{\n\tint mAge;\n};\n//指针间接修改teacher的年龄\nvoid AllocateAndInitByPointer(Teacher** teacher){\n\t*teacher = (Teacher*)malloc(sizeof(Teacher));\n\t(*teacher)->mAge = 200; \n}\n//引用修改teacher年龄\nvoid AllocateAndInitByReference(Teacher*& teacher){\n\tteacher->mAge = 300;\n}\nvoid test(){\n\t//创建Teacher\n\tTeacher* teacher = NULL;\n\t//指针间接赋值\n\tAllocateAndInitByPointer(&teacher);\n\tcout << \"AllocateAndInitByPointer:\" << teacher->mAge << endl;\n\t//引用赋值,将teacher本身传到ChangeAgeByReference函数中\n\tAllocateAndInitByReference(teacher);\n\tcout << \"AllocateAndInitByReference:\" << teacher->mAge << endl;\n\tfree(teacher);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void test01(){\n\tint a = 100;\n\tconst int& aRef = a; //此时aRef就是a\n\t//aRef = 200; 不能通过aRef的值\n\ta = 100; //OK\n\tcout << \"a:\" << a << endl;\n\tcout << \"aRef:\" << aRef << endl;\n}\nvoid test02(){\n\t//不能把一个字面量赋给引用\n\t//int& ref = 100;\n\t//但是可以把一个字面量赋给常引用\n\tconst int& ref = 100; //int temp = 200; const int& ret = temp;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//const int& param防止函数中意外修改数据\nvoid ShowVal(const int& param){\n\tcout << \"param:\" << param << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define ADD(x,y) x+y\ninline int Add(int x,int y){\n\treturn x + y;\n}\nvoid test(){\n\tint ret1 = ADD(10, 20) * 10; //希望的结果是300\n\tint ret2 = Add(10, 20) * 10; //希望结果也是300\n\tcout << \"ret1:\" << ret1 << endl; //210\n\tcout << \"ret2:\" << ret2 << endl; //300\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define COMPARE(x,y) ((x) < (y) ? (x) : (y))\nint Compare(int x,int y){\n\treturn x < y ? x : y;\n}\nvoid test02(){\n\tint a = 1;\n\tint b = 3;\n\t//cout << \"COMPARE(++a, b):\" << COMPARE(++a, b) << endl; // 3\n\tcout << \"Compare(int x,int y):\" << Compare(++a, b) << endl; //2\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){ cout << \"构造函数!\" << endl; }\n\tvoid PrintPerson(){ cout << \"输出Person!\" << endl; }\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void TestFunc01(int a = 10, int b = 20){\n\tcout << \"a + b = \" << a + b << endl;\n}\n//注意点:\n//1. 形参b设置默认参数值,那么后面位置的形参c也需要设置默认参数\nvoid TestFunc02(int a,int b = 10,int c = 10){}\n//2. 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数\nvoid TestFunc03(int a = 0,int b = 0);\nvoid TestFunc03(int a, int b){}\n\nint main(){\n\t//1.如果没有传参数,那么使用默认参数\n\tTestFunc01();\n\t//2. 如果传一个参数,那么第二个参数使用默认参数\n\tTestFunc01(100);\n\t//3. 如果传入两个参数,那么两个参数都使用我们传入的参数\n\tTestFunc01(100, 200);\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void TestFunc01(int a,int b,int){\n\t//函数内部无法使用占位参数\n\tcout << \"a + b = \" << a + b << endl;\n}\n//占位参数也可以设置默认值\nvoid TestFunc02(int a, int b, int = 20){\n\t//函数内部依旧无法使用占位参数\n\tcout << \"a + b = \" << a + b << endl;\n}\nint main(){\n\n\t//错误调用,占位参数也是参数,必须传参数\n\t//TestFunc01(10,20); \n\t//正确调用\n\tTestFunc01(10,20,30);\n\t//正确调用\n\tTestFunc02(10,20);\n\t//正确调用\n\tTestFunc02(10, 20, 30);\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//1. 函数重载条件\nnamespace A{\n\tvoid MyFunc(){ cout << \"无参数!\" << endl; }\n\tvoid MyFunc(int a){ cout << \"a: \" << a << endl; }\n\tvoid MyFunc(string b){ cout << \"b: \" << b << endl; }\n\tvoid MyFunc(int a, string b){ cout << \"a: \" << a << \" b:\" << b << endl;}\n void MyFunc(string b, int a){cout << \"a: \" << a << \" b:\" << b << endl;}\n}\n//2.返回值不作为函数重载依据\nnamespace B{\n\tvoid MyFunc(string b, int a){}\n\t//int MyFunc(string b, int a){} //无法重载仅按返回值区分的函数\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void MyFunc(string b){\n\tcout << \"b: \" << b << endl;\n}\n//函数重载碰上默认参数\nvoid MyFunc(string b, int a = 10){\n\tcout << \"a: \" << a << \" b:\" << b << endl;\n}\nint main(){\n\tMyFunc(\"hello\"); //这时,两个函数都能匹配调用,产生二义性\n\treturn 0;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void func(){}\nvoid func(int x){}\nvoid func(int x,char y){}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#ifndef MYMODULE_H\n#define MYMODULE_H\n\n#include\n\n#if __cplusplus\nextern \"C\"{\n#endif\n\n\tvoid func1();\n\tint func2(int a,int b);\n\n#if __cplusplus\n}\n#endif\n\n#endif" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#include\"MyModule.h\"\n\nvoid func1(){\n\tprintf(\"hello world!\");\n}\nint func2(int a, int b){\n\treturn a + b;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\nusing namespace std;\n\n#if 0\n\n\t#ifdef __cplusplus\n\textern \"C\" {\n\t\t#if 0\n\t\t\tvoid func1();\n\t\t\tint func2(int a, int b);\n\t\t#else\n\t\t\t#include\"MyModule.h\"\n\t\t#endif\n\t}\n\n\t#endif\n\n#else\n\n\textern \"C\" void func1();\n\textern \"C\" int func2(int a, int b);\n\n#endif\n\nint main(){\n\tfunc1();\n\tcout << func2(10, 20) << endl;\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954440": [], + "58954448": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "typedef struct _Person{\n\tchar name[64];\n\tint age;\n}Person;\ntypedef struct _Aninal{\n\tchar name[64];\n\tint age;\n\tint type; //动物种类\n}Ainmal;\n\nvoid PersonEat(Person* person){\n\tprintf(\"%s在吃人吃的饭!\\n\",person->name);\n}\nvoid AnimalEat(Ainmal* animal){\n\tprintf(\"%s在吃动物吃的饭!\\n\", animal->name);\n}\n\nint main(){\n\n\tPerson person;\n\tstrcpy(person.name, \"小明\");\n\tperson.age = 30;\n\tAnimalEat(&person);\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//封装两层含义\n//1. 属性和行为合成一个整体\n//2. 访问控制,现实事物本身有些属性和行为是不对外开放\nclass Person{\n//人具有的行为(函数)\npublic:\n\tvoid Dese(){ cout << \"我有钱,年轻,个子又高,就爱嘚瑟!\" << endl;}\n//人的属性(变量)\npublic:\n\tint mTall; //多高,可以让外人知道\nprotected:\n\tint mMoney; // 有多少钱,只能儿子孙子知道\nprivate:\n\tint mAge; //年龄,不想让外人知道\n};\n\nint main(){\n\n\tPerson p;\n\tp.mTall = 220;\n\t//p.mMoney 保护成员外部无法访问\n\t//p.mAge 私有成员外部无法访问\n\tp.Dese();\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class A{\n\tint mAge;\n};\nstruct B{\n\tint mAge;\n};\n\nvoid test(){\n\tA a;\n\tB b;\n\t//a.mAge; //无法访问私有成员\n\tb.mAge; //可正常外部访问\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class AccessLevels{\npublic:\n\t//对只读属性进行只读访问\n\tint getReadOnly(){ return readOnly; }\n\t//对读写属性进行读写访问\n\tvoid setReadWrite(int val){ readWrite = val; }\n\tint getReadWrite(){ return readWrite; }\n\t//对只写属性进行只写访问\n\tvoid setWriteOnly(int val){ writeOnly = val; }\nprivate:\n\tint readOnly; //对外只读访问\n\tint noAccess; //外部不可访问\n\tint readWrite; //读写访问\n\tint writeOnly; //只写访问\n};" + } + ], + "58954449": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//立方体类\nclass Cub{\npublic:\n\tvoid setL(int l){ mL = l; }\n\tvoid setW(int w){ mW = w; }\n\tvoid setH(int h){ mH = h; }\n\tint getL(){ return mL; }\n\tint getW(){ return mW; }\n\tint getH(){ return mH; }\n\t//立方体面积\n\tint caculateS(){ return (mL*mW + mL*mH + mW*mH) * 2; }\n\t//立方体体积\n\tint caculateV(){ return mL * mW * mH; }\n\t//成员方法\n\tbool CubCompare(Cub& c){\n\t\tif (getL() == c.getL() && getW() == c.getW() && getH() == c.getH()){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\nprivate:\n\tint mL; //长\n\tint mW; //宽\n\tint mH; //高\n};\n\n//比较两个立方体是否相等\nbool CubCompare(Cub& c1, Cub& c2){\n\tif (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH()){\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid test(){\n\tCub c1, c2;\n\tc1.setL(10);\n\tc1.setW(20);\n\tc1.setH(30);\n\n\tc2.setL(20);\n\tc2.setW(20);\n\tc2.setH(30);\n\n\tcout << \"c1面积:\" << c1.caculateS() << \" 体积:\" << c1.caculateV() << endl;\n\tcout << \"c2面积:\" << c2.caculateS() << \" 体积:\" << c2.caculateV() << endl;\n\n\t//比较两个立方体是否相等\n\tif (CubCompare(c1, c2)){\n\t\tcout << \"c1和c2相等!\" << endl;\n\t}\n\telse{\n\t\tcout << \"c1和c2不相等!\" << endl;\n\t}\n\n\tif (c1.CubCompare(c2)){\n\t\tcout << \"c1和c2相等!\" << endl;\n\t}\n\telse{\n\t\tcout << \"c1和c2不相等!\" << endl;\n\t}\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//点类\nclass Point{\npublic:\n\tvoid setX(int x){ mX = x; }\n\tvoid setY(int y){ mY = y; }\n\tint getX(){ return mX; }\n\tint getY(){ return mY; }\nprivate:\n\tint mX;\n\tint mY;\n};\n\n//圆类\nclass Circle{\npublic:\n\tvoid setP(int x,int y){\n\t\tmP.setX(x);\n\t\tmP.setY(y);\n\t}\n\tvoid setR(int r){ mR = r; }\n\tPoint& getP(){ return mP; }\n\tint getR(){ return mR; }\n\t//判断点和圆的关系\n\tvoid IsPointInCircle(Point& point){\n\t\tint distance = (point.getX() - mP.getX()) * (point.getX() - mP.getX()) + (point.getY() - mP.getY()) * (point.getY() - mP.getY());\n\t\tint radius = mR * mR;\n\t\tif (distance < radius){\n\t\t\tcout << \"Point(\" << point.getX() << \",\" << point.getY() << \")在圆内!\" << endl;\n\t\t}\n\t\telse if (distance > radius){\n\t\t\tcout << \"Point(\" << point.getX() << \",\" << point.getY() << \")在圆外!\" << endl;\n\t\t}\n\t\telse{\n\t\t\tcout << \"Point(\" << point.getX() << \",\" << point.getY() << \")在圆上!\" << endl;\n\t\t}\n\t}\nprivate:\n\tPoint mP; //圆心\n\tint mR; //半径\n};\n\nvoid test(){\n\t//实例化圆对象\n\tCircle circle;\n\tcircle.setP(20, 20);\n\tcircle.setR(5);\n\t//实例化点对象\n\tPoint point;\n\tpoint.setX(25);\n\tpoint.setY(20);\n\n\tcircle.IsPointInCircle(point);\n}" + } + ], + "58954451": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){\n\t\tcout << \"构造函数调用!\" << endl;\n\t\tpName = (char*)malloc(sizeof(\"John\"));\n\t\tstrcpy(pName, \"John\");\n\t\tmTall = 150;\n\t\tmMoney = 100;\n\t}\n\t~Person(){\n\t\tcout << \"析构函数调用!\" << endl;\n\t\tif (pName != NULL){\n\t\t\tfree(pName);\n\t\t\tpName = NULL;\n\t\t}\n\t}\npublic:\n\tchar* pName;\n\tint mTall;\n\tint mMoney;\n};\n\nvoid test(){\n\tPerson person;\n\tcout << person.pName << person.mTall << person.mMoney << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){\n\t\tcout << \"no param constructor!\" << endl;\n\t\tmAge = 0;\n\t}\n\t//有参构造函数\n\tPerson(int age){\n\t\tcout << \"1 param constructor!\" << endl;\n\t\tmAge = age;\n\t}\n\t//拷贝构造函数(复制构造函数) 使用另一个对象初始化本对象\n\tPerson(const Person& person){\n\t\tcout << \"copy constructor!\" << endl;\n\t\tmAge = person.mAge;\n\t}\n\t//打印年龄\n\tvoid PrintPerson(){\n\t\tcout << \"Age:\" << mAge << endl;\n\t}\nprivate:\n\tint mAge;\n};\n//1. 无参构造调用方式\nvoid test01(){\n\t\n\t//调用无参构造函数\n\tPerson person1; \n\tperson1.PrintPerson();\n\n\t//无参构造函数错误调用方式\n\t//Person person2();\n\t//person2.PrintPerson();\n}\n//2. 调用有参构造函数\nvoid test02(){\n\t\n\t//第一种 括号法,最常用\n\tPerson person01(100);\n\tperson01.PrintPerson();\n\n\t//调用拷贝构造函数\n\tPerson person02(person01);\n\tperson02.PrintPerson();\n\n\t//第二种 匿名对象(显示调用构造函数)\n\tPerson(200); //匿名对象,没有名字的对象\n\n\tPerson person03 = Person(300);\n\tperson03.PrintPerson();\n\n\t//注意: 使用匿名对象初始化判断调用哪一个构造函数,要看匿名对象的参数类型\n\tPerson person06(Person(400)); //等价于 Person person06 = Person(400);\n\tperson06.PrintPerson();\n\n\t//第三种 =号法 隐式转换\n\tPerson person04 = 100; //Person person04 = Person(100)\n\tperson04.PrintPerson();\n\n\t//调用拷贝构造\n\tPerson person05 = person04; //Person person05 = Person(person04)\n\tperson05.PrintPerson();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){\n\t\tcout << \"no param contructor!\" << endl;\n\t\tmAge = 10;\n\t}\n\tPerson(int age){\n\t\tcout << \"param constructor!\" << endl;\n\t\tmAge = age;\n\t}\n\tPerson(const Person& person){\n\t\tcout << \"copy constructor!\" << endl;\n\t\tmAge = person.mAge;\n\t}\n\t~Person(){\n\t\tcout << \"destructor!\" << endl;\n\t}\npublic:\n\tint mAge;\n};\n//1. 旧对象初始化新对象\nvoid test01(){\n\n\tPerson p(10);\n\tPerson p1(p);\n\tPerson p2 = Person(p);\n\tPerson p3 = p; // 相当于Person p2 = Person(p);\n}\n\n//2. 传递的参数是普通对象,函数参数也是普通对象,传递将会调用拷贝构造\nvoid doBussiness(Person p){}\n\nvoid test02(){\n\tPerson p(10);\n\tdoBussiness(p);\n}\n\n//3. 函数返回局部对象\nPerson MyBusiness(){\n\tPerson p(10);\n\tcout << \"局部p:\" << (int*)&p << endl;\n\treturn p;\n}\nvoid test03(){\n\t//vs release、qt下没有调用拷贝构造函数\n\t//vs debug下调用一次拷贝构造函数\n\tPerson p = MyBusiness();\n\tcout << \"局部p:\" << (int*)&p << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(char* name,int age){\n\t\tpName = (char*)malloc(strlen(name) + 1);\n\t\tstrcpy(pName,name);\n\t\tmAge = age;\n\t}\n\t//增加拷贝构造函数\n\tPerson(const Person& person){\n\t\tpName = (char*)malloc(strlen(person.pName) + 1);\n\t\tstrcpy(pName, person.pName);\n\t\tmAge = person.mAge;\n\t}\n\t~Person(){\n\t\tif (pName != NULL){\n\t\t\tfree(pName);\n\t\t}\n\t}\nprivate:\n\tchar* pName;\n\tint mAge;\n};\n\nvoid test(){\n\tPerson p1(\"Edward\",30);\n\t//用对象p1初始化对象p2,调用c++提供的默认拷贝构造函数\n\tPerson p2 = p1;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n#if 0\n\t//传统方式初始化\n\tPerson(int a,int b,int c){\n\t\tmA = a;\n\t\tmB = b;\n\t\tmC = c;\n\t}\n#endif\n\t//初始化列表方式初始化\n\tPerson(int a, int b, int c):mA(a),mB(b),mC(c){}\n\tvoid PrintPerson(){\n\t\tcout << \"mA:\" << mA << endl;\n\t\tcout << \"mB:\" << mB << endl;\n\t\tcout << \"mC:\" << mC << endl;\n\t}\nprivate:\n\tint mA;\n\tint mB;\n\tint mC;\n};" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//汽车类\nclass Car{\npublic:\n\tCar(){\n\t\tcout << \"Car 默认构造函数!\" << endl;\n\t\tmName = \"大众汽车\";\n\t}\n\tCar(string name){\n\t\tcout << \"Car 带参数构造函数!\" << endl;\n\t\tmName = name;\n\t}\n\t~Car(){\n\t\tcout << \"Car 析构函数!\" << endl;\n\t}\npublic:\n\tstring mName;\n};\n\n//拖拉机\nclass Tractor{\npublic:\n\tTractor(){\n\t\tcout << \"Tractor 默认构造函数!\" << endl;\n\t\tmName = \"爬土坡专用拖拉机\";\n\t}\n\tTractor(string name){\n\t\tcout << \"Tractor 带参数构造函数!\" << endl;\n\t\tmName = name;\n\t}\n\t~Tractor(){\n\t\tcout << \"Tractor 析构函数!\" << endl;\n\t}\npublic:\n\tstring mName;\n};\n\n//人类\nclass Person{\npublic:\n#if 1\n\t//类mCar不存在合适的构造函数\n\tPerson(string name){\n\t\tmName = name;\n\t}\n#else\n\t//初始化列表可以指定调用构造函数\n\tPerson(string carName, string tracName, string name) : mTractor(tracName), mCar(carName), mName(name){\n\t\tcout << \"Person 构造函数!\" << endl;\n\t}\n#endif\n\t\n\tvoid GoWorkByCar(){\n\t\tcout << mName << \"开着\" << mCar.mName << \"去上班!\" << endl;\n\t}\n\tvoid GoWorkByTractor(){\n\t\tcout << mName << \"开着\" << mTractor.mName << \"去上班!\" << endl;\n\t}\n\t~Person(){\n\t\tcout << \"Person 析构函数!\" << endl;\n\t}\nprivate:\n\tstring mName;\n\tCar mCar;\n\tTractor mTractor;\n};\n\nvoid test(){\n\t//Person person(\"宝马\", \"东风拖拉机\", \"赵四\");\n\tPerson person(\"刘能\");\n\tperson.GoWorkByCar();\n\tperson.GoWorkByTractor();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class MyString{\npublic:\n\texplicit MyString(int n){\n\t\tcout << \"MyString(int n)!\" << endl;\n\t}\n\tMyString(const char* str){\n\t\tcout << \"MyString(const char* str)\" << endl;\n\t}\n};\n\nint main(){\n\n\t//给字符串赋值?还是初始化?\n\t//MyString str1 = 1; \n\tMyString str2(10);\n\n\t//寓意非常明确,给字符串赋值\n\tMyString str3 = \"abcd\";\n\tMyString str4(\"abcd\");\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){\n\t\tmAge = 20;\n\t\tpName = (char*)malloc(strlen(\"john\")+1);\n\t\tstrcpy(pName, \"john\");\n\t}\n\tvoid Init(){\n\t\tmAge = 20;\n\t\tpName = (char*)malloc(strlen(\"john\")+1);\n\t\tstrcpy(pName, \"john\");\n\t}\n\tvoid Clean(){\n\t\tif (pName != NULL){\n\t\t\tfree(pName);\n\t\t}\n\t}\npublic:\n\tint mAge;\n\tchar* pName;\n};\nint main(){\n\n\t//分配内存\n\tPerson* person = (Person*)malloc(sizeof(Person));\n\tif(person == NULL){\n\t\treturn 0;\n\t}\n\t//调用初始化函数\n\tperson->Init();\n\t//清理对象\n\tperson->Clean();\n\t//释放person对象\n\tfree(person);\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "Person* person = new Person;\n相当于:\nPerson* person = (Person*)malloc(sizeof(Person));\n\tif(person == NULL){\n\t\treturn 0;\n\t}\nperson->Init(); 构造函数" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){\n\t\tcout << \"无参构造函数!\" << endl;\n\t\tpName = (char*)malloc(strlen(\"undefined\") + 1);\n\t\tstrcpy(pName, \"undefined\");\n\t\tmAge = 0;\n\t}\n\tPerson(char* name, int age){\n\t\tcout << \"有参构造函数!\" << endl;\n\t\tpName = (char*)malloc(strlen(name) + 1);\n\t\tstrcpy(pName, name);\n\t\tmAge = age;\n\t}\n\tvoid ShowPerson(){\n\t\tcout << \"Name:\" << pName << \" Age:\" << mAge << endl;\n\t}\n\t~Person(){\n\t\tcout << \"析构函数!\" << endl;\n\t\tif (pName != NULL){\n\t\t\tdelete pName;\n\t\t\tpName = NULL;\n\t\t}\n\t}\npublic:\n\tchar* pName;\n\tint mAge;\n};\n\nvoid test(){\n\tPerson* person1 = new Person;\n\tPerson* person2 = new Person(\"John\",33);\n\n\tperson1->ShowPerson();\n\tperson2->ShowPerson();\n\n\tdelete person1;\n\tdelete person2;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//创建字符数组\nchar* pStr = new char[100];\n//创建整型数组\nint* pArr1 = new int[100]; \n//创建整型数组并初始化\nint* pArr2 = new int[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n\n//释放数组内存\ndelete[] pStr;\ndelete[] pArr1;\ndelete[] pArr2;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(){\n\t\tpName = (char*)malloc(strlen(\"undefined\") + 1);\n\t\tstrcpy(pName, \"undefined\");\n\t\tmAge = 0;\n\t}\n\tPerson(char* name, int age){\n\t\tpName = (char*)malloc(sizeof(name));\n\t\tstrcpy(pName, name);\n\t\tmAge = age;\n\t}\n\t~Person(){\n\t\tif (pName != NULL){\n\t\t\tdelete pName;\n\t\t}\n\t}\npublic:\n\tchar* pName;\n\tint mAge;\n};\n\nvoid test(){\n\t//栈聚合初始化\n\tPerson person[] = { Person(\"john\", 20), Person(\"Smith\", 22) };\n\tcout << person[1].pName << endl;\n //创建堆上对象数组必须提供构造函数\n\tPerson* workers = new Person[20];\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(char* name, int age){\n\t\tpName = (char*)malloc(sizeof(name));\n\t\tstrcpy(pName,name);\n\t\tmAge = age;\n\t}\n\t~Person(){\n\t\tif (pName != NULL){\n\t\t\tdelete pName;\n\t\t}\n\t}\npublic:\n\tchar* pName;\n\tint mAge;\n};\n\nvoid test(){\n\tvoid* person = new Person(\"john\",20);\n\tdelete person;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\t//类的静态成员属性\n\tstatic int sNum;\nprivate:\n\tstatic int sOther;\n};\n\n//类外初始化,初始化时不加static\nint Person::sNum = 0;\nint Person::sOther = 0;\nint main(){\n\n\n\t//1. 通过类名直接访问\n\tPerson::sNum = 100;\n\tcout << \"Person::sNum:\" << Person::sNum << endl;\n\n\t//2. 通过对象访问\n\tPerson p1, p2;\n\tp1.sNum = 200;\n\n\tcout << \"p1.sNum:\" << p1.sNum << endl;\n\tcout << \"p2.sNum:\" << p2.sNum << endl;\n\n\t//3. 静态成员也有访问权限,类外不能访问私有成员\n\t//cout << \"Person::sOther:\" << Person::sOther << endl;\n\tPerson p3;\n\t//cout << \"p3.sOther:\" << p3.sOther << endl;\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\t//普通成员函数可以访问static和non-static成员属性\n\tvoid changeParam1(int param){\n\t\tmParam = param;\n\t\tsNum = param;\n\t}\n\t//静态成员函数只能访问static成员属性\n\tstatic void changeParam2(int param){\n\t\t//mParam = param; //无法访问\n\t\tsNum = param;\n\t}\nprivate:\n\tstatic void changeParam3(int param){\n\t\t//mParam = param; //无法访问\n\t\tsNum = param;\n\t}\npublic:\n\tint mParam;\n\tstatic int sNum;\n};\n\n//静态成员属性类外初始化\nint Person::sNum = 0;\n\nint main(){\n\n\t//1. 类名直接调用\n\tPerson::changeParam2(100);\n\n\t//2. 通过对象调用\n\tPerson p;\n\tp.changeParam2(200);\n\n\t//3. 静态成员函数也有访问权限\n\t//Person::changeParam3(100); //类外无法访问私有静态成员函数\n\t//Person p1;\n\t//p1.changeParam3(200);\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\t//static const int mShare = 10;\n\tconst static int mShare = 10; //只读区,不可修改\n};\nint main(){\n\n\tcout << Person::mShare << endl;\n\t//Person::mShare = 20;\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Printer{\npublic:\n\tstatic Printer* getInstance(){ return pPrinter;}\n\tvoid PrintText(string text){\n\t\tcout << \"打印内容:\" << text << endl;\n\t\tcout << \"已打印次数:\" << mTimes << endl;\n\t\tcout << \"--------------\" << endl;\n\t\tmTimes++;\n\t}\nprivate:\n\tPrinter(){ mTimes = 0; }\n\tPrinter(const Printer&){}\nprivate:\n\tstatic Printer* pPrinter;\n\tint mTimes;\n};\n\nPrinter* Printer::pPrinter = new Printer;\n\nvoid test(){\n\tPrinter* printer = Printer::getInstance();\n\tprinter->PrintText(\"离职报告!\");\n\tprinter->PrintText(\"入职合同!\");\n\tprinter->PrintText(\"提交代码!\");\n}" + } + ], + "58954456": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "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}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "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}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//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}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "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}" + } + ], + "58954457": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Building;\n//友元类\nclass MyFriend{\npublic:\n\t//友元成员函数\n\tvoid LookAtBedRoom(Building& building);\n\tvoid PlayInBedRoom(Building& building);\n};\nclass Building{\n\t//全局函数做友元函数\n\tfriend void CleanBedRoom(Building& building);\n#if 0\n\t//成员函数做友元函数\n\tfriend void MyFriend::LookAtBedRoom(Building& building);\n\tfriend void MyFriend::PlayInBedRoom(Building& building);\n#else\t\n\t//友元类\n\tfriend class MyFriend;\n#endif\npublic:\n\tBuilding();\npublic:\n\tstring mSittingRoom;\nprivate:\n\tstring mBedroom;\n};\n\nvoid MyFriend::LookAtBedRoom(Building& building){\n\tcout << \"我的朋友参观\" << building.mBedroom << endl;\n}\nvoid MyFriend::PlayInBedRoom(Building& building){\n\tcout << \"我的朋友玩耍在\" << building.mBedroom << endl;\n}\n\n//友元全局函数\nvoid CleanBedRoom(Building& building){\n\tcout << \"友元全局函数访问\" << building.mBedroom << endl;\n}\n\nBuilding::Building(){\n\tthis->mSittingRoom = \"客厅\";\n\tthis->mBedroom = \"卧室\";\n}\n\nint main(){\n\n\tBuilding building;\n\tMyFriend myfriend;\n\n\tCleanBedRoom(building);\n\tmyfriend.LookAtBedRoom(building);\n\tmyfriend.PlayInBedRoom(building);\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954458": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\n\tfriend ostream& operator<<(ostream& os, Person& person);\npublic:\n\tPerson(int id,int age){\n\t\tmID = id;\n\t\tmAge = age;\n\t}\nprivate:\n\tint mID;\n\tint mAge;\n};\n\nostream& operator<<(ostream& os, Person& person){\n\tos << \"ID:\" << person.mID << \" Age:\" << person.mAge;\n\treturn os;\n}\n\nint main(){\n\n\tPerson person(1001, 30);\n\t//cout << person; //cout.operator+(person)\n\tcout << person << \" | \" << endl;\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Complex{\n\tfriend ostream& operator<<(ostream& os,Complex& complex){\n\t\tos << \"A:\" << complex.mA << \" B:\" << complex.mB << endl;\n\t\treturn os;\n\t}\npublic:\n\tComplex(){\n\t\tmA = 0;\n\t\tmB = 0;\n\t}\n\t//重载前置++\n\tComplex& operator++(){\n\t\tmA++;\n\t\tmB++;\n\t\treturn *this;\n\t}\n\t//重载后置++\n\tComplex operator++(int){\t\n\t\tComplex temp;\n\t\ttemp.mA = this->mA;\n\t\ttemp.mB = this->mB;\n\t\tmA++;\n\t\tmB++;\n\t\treturn temp;\n\t}\n\t//前置--\n\tComplex& operator--(){\n\t\tmA--;\n\t\tmB--;\n\t\treturn *this;\n\t}\n\t//后置--\n\tComplex operator--(int){\n\t\tComplex temp;\n\t\ttemp.mA = mA;\n\t\ttemp.mB = mB;\n\t\tmA--;\n\t\tmB--;\n\t\treturn temp;\n\t}\n\tvoid ShowComplex(){\n\t\tcout << \"A:\" << mA << \" B:\" << mB << endl;\n\t}\nprivate:\n\tint mA;\n\tint mB;\n};\n\nvoid test(){\n\tComplex complex;\n\tcomplex++;\n\tcout << complex;\n\t++complex;\n\tcout << complex;\n\n\tComplex ret = complex++;\n\tcout << ret;\n\tcout << complex;\n\n\tcout << \"------\" << endl;\n\tret--;\n\t--ret;\n\tcout << \"ret:\" << ret;\n\tcomplex--;\n\t--complex;\n\tcout << \"complex:\" << complex;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(int param){\n\t\tthis->mParam = param;\n\t}\n\tvoid PrintPerson(){\n\t\tcout << \"Param:\" << mParam << endl;\n\t}\nprivate:\n\tint mParam;\n};\n\nclass SmartPointer{\npublic:\n\tSmartPointer(Person* person){\n\t\tthis->pPerson = person;\n\t}\n\t//重载指针的->、*操作符\n\tPerson* operator->(){\n\t\treturn pPerson;\n\t}\n\tPerson& operator*(){\n\t\treturn *pPerson;\n\t}\n\t~SmartPointer(){\n\t\tif (pPerson != NULL){\n\t\t\tdelete pPerson;\n\t\t}\n\t}\npublic:\n\tPerson* pPerson;\n};\n\nvoid test01(){\n\t\n\t//Person* person = new Person(100);\n\t//如果忘记释放,那么就会造成内存泄漏\n\n\tSmartPointer pointer(new Person(100));\n\tpointer->PrintPerson();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\n\tfriend ostream& operator<<(ostream& os,const Person& person){\n\t\tos << \"ID:\" << person.mID << \" Age:\" << person.mAge << endl;\n\t\treturn os;\n\t}\npublic:\n\tPerson(int id,int age){\n\t\tthis->mID = id;\n\t\tthis->mAge = age;\n\t}\n\t//重载赋值运算符\n\tPerson& operator=(const Person& person){\n\t\tthis->mID = person.mID;\n\t\tthis->mAge = person.mAge;\n\t\treturn *this;\n\t}\nprivate:\n\tint mID;\n\tint mAge;\n};\n\n//1. =号混淆的地方\nvoid test01(){\n\tPerson person1(10, 20);\n\tPerson person2 = person1; //调用拷贝构造\n\t//如果一个对象还没有被创建,则必须初始化,也就是调用构造函数\n\t//上述例子由于person2还没有初始化,所以会调用构造函数\n\t//由于person2是从已有的person1来创建的,所以只有一个选择\n\t//就是调用拷贝构造函数\n\tperson2 = person1; //调用operator=函数\n\t//由于person2已经创建,不需要再调用构造函数,这时候调用的是重载的赋值运算符\n}\n//2. 赋值重载案例\nvoid test02(){\n\tPerson person1(20, 20);\n\tPerson person2(30, 30);\n\tcout << \"person1:\" << person1;\n\tcout << \"person2:\" << person2;\n\tperson2 = person1;\n\tcout << \"person2:\" << person2;\n}\n//常见错误,当准备给两个相同对象赋值时,应该首先检查一下这个对象是否对自身赋值了\n//对于本例来讲,无论如何执行这些赋值运算都是无害的,但如果对类的实现进行修改,那么将会出现差异;\n//3. 类中指针\nclass Person2{\n\tfriend ostream& operator<<(ostream& os, const Person2& person){\n\t\tos << \"Name:\" << person.pName << \" ID:\" << person.mID << \" Age:\" << person.mAge << endl;\n\t\treturn os;\n\t}\npublic:\n\tPerson2(char* name,int id, int age){\n\t\tthis->pName = new char[strlen(name) + 1];\n\t\tstrcpy(this->pName, name);\n\t\tthis->mID = id;\n\t\tthis->mAge = age;\n\t}\n#if 1\n\t//重载赋值运算符\n\tPerson2& operator=(const Person2& person){\n\n\t\t//注意:由于当前对象已经创建完毕,那么就有可能pName指向堆内存\n\t\t//这个时候如果直接赋值,会导致内存没有及时释放\n\t\tif (this->pName != NULL){\n\t\t\tdelete[] this->pName;\n\t\t}\n\n\t\tthis->pName = new char[strlen(person.pName) + 1];\n\t\tstrcpy(this->pName,person.pName);\n\t\tthis->mID = person.mID;\n\t\tthis->mAge = person.mAge;\n\t\treturn *this;\n\t}\n#endif\n\t//析构函数\n\t~Person2(){\n\t\tif (this->pName != NULL){\n\t\t\tdelete[] this->pName;\n\t\t}\n\t}\nprivate:\n\tchar* pName;\n\tint mID;\n\tint mAge;\n};\n\nvoid test03(){\n\tPerson2 person1(\"John\",20, 20);\n\tPerson2 person2(\"Edward\",30, 30);\n\tcout << \"person1:\" << person1;\n\tcout << \"person2:\" << person2;\n\tperson2 = person1;\n\tcout << \"person2:\" << person2;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Complex{\npublic:\n\tComplex(char* name,int id,int age){\n\t\tthis->pName = new char[strlen(name) + 1];\n\t\tstrcpy(this->pName, name);\n\t\tthis->mID = id;\n\t\tthis->mAge = age;\n\t}\n\t//重载==号操作符\n\tbool operator==(const Complex& complex){\n\t\tif (strcmp(this->pName,complex.pName) == 0 && \n\t\t this->mID == complex.mID && \n\t\t\tthis->mAge == complex.mAge){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t//重载!=操作符\n\tbool operator!=(const Complex& complex){\n\t\tif (strcmp(this->pName, complex.pName) != 0 || \n\t\t this->mID != complex.mID || \n\t\t\tthis->mAge != complex.mAge){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t~Complex(){\n\t\tif (this->pName != NULL){\n\t\t\tdelete[] this->pName;\n\t\t}\n\t}\nprivate:\n\tchar* pName;\n\tint mID;\n\tint mAge;\n};\nvoid test(){\n\tComplex complex1(\"aaa\", 10, 20);\n\tComplex complex2(\"bbb\", 10, 20);\n\tif (complex1 == complex2){ cout << \"相等!\" << endl; }\n\tif (complex1 != complex2){ cout << \"不相等!\" << endl; }\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Complex{\npublic:\n\tint Add(int x,int y){\n\t\treturn x + y;\n\t}\n\tint operator()(int x,int y){\n\t\treturn x + y;\n\t}\n};\nvoid test01(){\n\tComplex complex;\n\tcout << complex.Add(10,20) << endl;\n\t//对象当做函数来调用\n\tcout << complex(10, 20) << endl;\n}" + } + ], + "58954461": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "网页类\nclass IndexPage{\npublic:\n\t//网页头部\n\tvoid Header(){\n\t\tcout << \"网页头部!\" << endl;\n\t}\n\t//网页左侧菜单\n\tvoid LeftNavigation(){\n\t\tcout << \"左侧导航菜单!\" << endl;\n\t}\n\t//网页主体部分\n\tvoid MainBody(){\n\t\tcout << \"首页网页主题内容!\" << endl;\n\t}\n\t//网页底部\n\tvoid Footer(){\n\t\tcout << \"网页底部!\" << endl;\n\t}\nprivate:\n\tstring mTitle; //网页标题\n};\n\n#if 0\n//如果不使用继承,那么定义新闻页类,需要重新写一遍已经有的代码\nclass NewsPage{\npublic:\n\t//网页头部\n\tvoid Header(){\n\t\tcout << \"网页头部!\" << endl;\n\t}\n\t//网页左侧菜单\n\tvoid LeftNavigation(){\n\t\tcout << \"左侧导航菜单!\" << endl;\n\t}\n\t//网页主体部分\n\tvoid MainBody(){\n\t\tcout << \"新闻网页主体内容!\" << endl;\n\t}\n\t//网页底部\n\tvoid Footer(){\n\t\tcout << \"网页底部!\" << endl;\n\t}\nprivate:\n\tstring mTitle; //网页标题\n};\n\nvoid test(){\n\tNewsPage* newspage = new NewsPage;\n\tnewspage->Header();\n\tnewspage->MainBody();\n\tnewspage->LeftNavigation();\n\tnewspage->Footer();\n}\n#else\n//使用继承,可以复用已有的代码,新闻业除了主体部分不一样,其他都是一样的\nclass NewsPage : public IndexPage{\npublic:\n\t//网页主体部分\n\tvoid MainBody(){\n\t\tcout << \"新闻网页主主体内容!\" << endl;\n\t}\n};\nvoid test(){\n\tNewsPage* newspage = new NewsPage;\n\tnewspage->Header();\n\tnewspage->MainBody();\n\tnewspage->LeftNavigation();\n\tnewspage->Footer();\n}\n#endif\nint main(){\n\n\ttest();\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//基类\nclass A{\npublic:\n\tint mA;\nprotected:\n\tint mB;\nprivate:\n\tint mC;\n};\n\n//1. 公有(public)继承\nclass B : public A{\npublic:\n\tvoid PrintB(){\n\t\tcout << mA << endl; //可访问基类public属性\n\t\tcout << mB << endl; //可访问基类protected属性\n\t\t//cout << mC << endl; //不可访问基类private属性\n\t} \n};\nclass SubB : public B{\n\tvoid PrintSubB(){\n\t\tcout << mA << endl; //可访问基类public属性\n\t\tcout << mB << endl; //可访问基类protected属性\n\t\t//cout << mC << endl; //不可访问基类private属性\n\t}\n};\nvoid test01(){\n\n\tB b;\n\tcout << b.mA << endl; //可访问基类public属性\n\t//cout << b.mB << endl; //不可访问基类protected属性\n\t//cout << b.mC << endl; //不可访问基类private属性\n}\n\n//2. 私有(private)继承\nclass C : private A{\npublic:\n\tvoid PrintC(){\n\t\tcout << mA << endl; //可访问基类public属性\n\t\tcout << mB << endl; //可访问基类protected属性\n\t\t//cout << mC << endl; //不可访问基类private属性\n\t}\n};\nclass SubC : public C{\n\tvoid PrintSubC(){\n\t\t//cout << mA << endl; //不可访问基类public属性\n\t\t//cout << mB << endl; //不可访问基类protected属性\n\t\t//cout << mC << endl; //不可访问基类private属性\n\t}\n};\nvoid test02(){\n\tC c;\n\t//cout << c.mA << endl; //不可访问基类public属性\n\t//cout << c.mB << endl; //不可访问基类protected属性\n\t//cout << c.mC << endl; //不可访问基类private属性\n}\n//3. 保护(protected)继承\nclass D : protected A{\npublic:\n\tvoid PrintD(){\n\t\tcout << mA << endl; //可访问基类public属性\n\t\tcout << mB << endl; //可访问基类protected属性\n\t\t//cout << mC << endl; //不可访问基类private属性\n\t}\n};\nclass SubD : public D{\n\tvoid PrintD(){\n\t\tcout << mA << endl; //可访问基类public属性\n\t\tcout << mB << endl; //可访问基类protected属性\n\t\t//cout << mC << endl; //不可访问基类private属性\n\t}\n};\nvoid test03(){\n\tD d;\n\t//cout << d.mA << endl; //不可访问基类public属性\n\t//cout << d.mB << endl; //不可访问基类protected属性\n\t//cout << d.mC << endl; //不可访问基类private属性\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Aclass{\npublic:\n\tint mA;\n\tint mB;\n};\nclass Bclass : public Aclass{\npublic:\n\tint mC;\n};\nclass Cclass : public Bclass{\npublic:\n\tint mD;\n};\nvoid test(){\n\tcout << \"A size:\" << sizeof(Aclass) << endl;\n\tcout << \"B size:\" << sizeof(Bclass) << endl;\n\tcout << \"C size:\" << sizeof(Cclass) << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class A{\npublic:\n\tA(){\n\t\tcout << \"A类构造函数!\" << endl;\n\t}\n\t~A(){\n\t\tcout << \"A类析构函数!\" << endl;\n\t}\n};\n\nclass B : public A{\npublic:\n\tB(){\n\t\tcout << \"B类构造函数!\" << endl;\n\t}\n\t~B(){\n\t\tcout << \"B类析构函数!\" << endl;\n\t}\n};\n\nclass C : public B{\npublic:\n\tC(){\n\t\tcout << \"C类构造函数!\" << endl;\n\t}\n\t~C(){\n\t\tcout << \"C类析构函数!\" << endl;\n\t}\n};\n\nvoid test(){\n\tC c;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class D{\npublic:\n\tD(){\n\t\tcout << \"D类构造函数!\" << endl;\n\t}\n\t~D(){\n\t\tcout << \"D类析构函数!\" << endl;\n\t}\n};\nclass A{\npublic:\n\tA(){\n\t\tcout << \"A类构造函数!\" << endl;\n\t}\n\t~A(){\n\t\tcout << \"A类析构函数!\" << endl;\n\t}\n};\nclass B : public A{\npublic:\n\tB(){\n\t\tcout << \"B类构造函数!\" << endl;\n\t}\n\t~B(){\n\t\tcout << \"B类析构函数!\" << endl;\n\t}\n};\nclass C : public B{\npublic:\n\tC(){\n\t\tcout << \"C类构造函数!\" << endl;\n\t}\n\t~C(){\n\t\tcout << \"C类析构函数!\" << endl;\n\t}\npublic:\n\tD c;\n};\nvoid test(){\n\tC c;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Base{\npublic:\n\tBase():mParam(0){}\n\tvoid Print(){ cout << mParam << endl; }\npublic:\n\tint mParam;\n};\n\nclass Derived : public Base{\npublic:\n\tDerived():mParam(10){}\n\tvoid Print(){\n\t\t//在派生类中使用和基类的同名成员,显示使用类名限定符\n\t\tcout << Base::mParam << endl;\n\t\tcout << mParam << endl;\n\t}\n\t//返回基类重名成员\n\tint& getBaseParam(){ return Base::mParam; }\npublic:\n\tint mParam;\n};\n\nint main(){\n\n\tDerived derived;\n\t//派生类和基类成员属性重名,子类访问成员默认是子类成员\n\tcout << derived.mParam << endl; //10\n\tderived.Print();\n\t//类外如何获得基类重名成员属性\n\tderived.getBaseParam() = 100;\n\tcout << \"Base:mParam:\" << derived.getBaseParam() << endl;\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Base{\npublic:\n\tvoid func1(){\n\t\tcout << \"Base::void func1()\" << endl;\n\t};\n\tvoid func1(int param){\n\t\tcout << \"Base::void func1(int param)\" << endl;\n\t}\n\tvoid myfunc(){\n\t\tcout << \"Base::void myfunc()\" << endl;\n\t}\n};\nclass Derived1 : public Base{\npublic:\n\tvoid myfunc(){\n\t\tcout << \"Derived1::void myfunc()\" << endl;\n\t}\n};\nclass Derived2 : public Base{\npublic:\n\t//改变成员函数的参数列表\n\tvoid func1(int param1, int param2){\n\t\tcout << \"Derived2::void func1(int param1,int param2)\" << endl;\n\t};\n};\nclass Derived3 : public Base{\npublic:\n\t//改变成员函数的返回值\n\tint func1(int param){\n\t\tcout << \"Derived3::int func1(int param)\" << endl;\n\t\treturn 0;\n\t}\n};\nint main(){\n\n\tDerived1 derived1;\n\tderived1.func1();\n\tderived1.func1(20);\n\tderived1.myfunc();\n\tcout << \"-------------\" << endl;\n\tDerived2 derived2;\n\t//derived2.func1(); //func1被隐藏\n\t//derived2.func1(20); //func2被隐藏\n\tderived2.func1(10,20); //重载func1之后,基类的函数被隐藏\n\tderived2.myfunc();\n\tcout << \"-------------\" << endl;\n\tDerived3 derived3;\n\t//derived3.func1(); 没有重新定义的重载版本被隐藏\n\tderived3.func1(20);\n\tderived3.myfunc();\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Base{\npublic:\n\tstatic int getNum(){ return sNum; }\n\tstatic int getNum(int param){\n\t\treturn sNum + param;\n\t}\npublic:\n\tstatic int sNum;\n};\nint Base::sNum = 10;\n\nclass Derived : public Base{\npublic:\n\tstatic int sNum; //基类静态成员属性将被隐藏\n#if 0\n\t//重定义一个函数,基类中重载的函数被隐藏\n\tstatic int getNum(int param1, int param2){\n\t\treturn sNum + param1 + param2;\n\t}\n#else\n\t//改变基类函数的某个特征,返回值或者参数个数,将会隐藏基类重载的函数\n\tstatic void getNum(int param1, int param2){\n\t\tcout << sNum + param1 + param2 << endl;\n\t}\n#endif\n};\nint Derived::sNum = 20;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Base1{\npublic:\n\tvoid func1(){ cout << \"Base1::func1\" << endl; }\n};\nclass Base2{\npublic:\n\tvoid func1(){ cout << \"Base2::func1\" << endl; }\n\tvoid func2(){ cout << \"Base2::func2\" << endl; }\n};\n//派生类继承Base1、Base2\nclass Derived : public Base1, public Base2{};\nint main(){\n\n\tDerived derived;\n\t//func1是从Base1继承来的还是从Base2继承来的?\n\t//derived.func1(); \n\tderived.func2();\n\n\t//解决歧义:显示指定调用那个基类的func1\n\tderived.Base1::func1(); \n\tderived.Base2::func1();\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class BigBase{\npublic:\n\tBigBase(){ mParam = 0; }\n\tvoid func(){ cout << \"BigBase::func\" << endl; }\npublic:\n\tint mParam;\n};\n\nclass Base1 : public BigBase{};\nclass Base2 : public BigBase{};\nclass Derived : public Base1, public Base2{};\n\nint main(){\n\n\tDerived derived;\n\t//1. 对“func”的访问不明确\n\t//derived.func();\n\t//cout << derived.mParam << endl;\n\tcout << \"derived.Base1::mParam:\" << derived.Base1::mParam << endl;\n\tcout << \"derived.Base2::mParam:\" << derived.Base2::mParam << endl;\n\n\t//2. 重复继承\n\tcout << \"Derived size:\" << sizeof(Derived) << endl; //8\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class BigBase{\npublic:\n\tBigBase(){ mParam = 0; }\n\tvoid func(){ cout << \"BigBase::func\" << endl; }\npublic:\n\tint mParam;\n};\n\nclass Base1 : virtual public BigBase{};\nclass Base2 : virtual public BigBase{};\nclass Derived : public Base1, public Base2{};\n\nint main(){\n\n\tDerived derived;\n\t//二义性问题解决\n\tderived.func();\n\tcout << derived.mParam << endl;\n\t//输出结果:12\n\tcout << \"Derived size:\" << sizeof(Derived) << endl;\n\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class BigBase{\npublic:\n\tBigBase(){ mParam = 0; }\n\tvoid func(){ cout << \"BigBase::func\" << endl; }\npublic: int mParam;\n};\n#if 0 //虚继承\nclass Base1 : virtual public BigBase{};\nclass Base2 : virtual public BigBase{};\n#else //普通继承\nclass Base1 : public BigBase{};\nclass Base2 : public BigBase{};\n#endif\nclass Derived : public Base1, public Base2{};" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class BigBase{\npublic:\n\tBigBase(int x){mParam = x;}\n\tvoid func(){cout << \"BigBase::func\" << endl;}\npublic:\n\tint mParam;\n};\nclass Base1 : virtual public BigBase{\npublic:\n\tBase1() :BigBase(10){} //不调用BigBase构造\n};\nclass Base2 : virtual public BigBase{\npublic:\n\tBase2() :BigBase(10){} //不调用BigBase构造\n};\n\nclass Derived : public Base1, public Base2{\npublic:\n\tDerived() :BigBase(10){} //调用BigBase构造\n};\n//每一次继承子类中都必须书写初始化语句\nint main(){\n\tDerived derived;\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954469": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//计算器\nclass Caculator{\npublic:\n\tvoid setA(int a){\n\t\tthis->mA = a;\n\t}\n\tvoid setB(int b){\n\t\tthis->mB = b;\n\t}\n\tvoid setOperator(string oper){\n\t\tthis->mOperator = oper;\n\t}\n\tint getResult(){\n\t\t\n\t\tif (this->mOperator == \"+\"){\n\t\t\treturn mA + mB;\n\t\t}\n\t\telse if (this->mOperator == \"-\"){\n\t\t\treturn mA - mB;\n\t\t}\n\t\telse if (this->mOperator == \"*\"){\n\t\t\treturn mA * mB;\n\t\t}\n\t\telse if (this->mOperator == \"/\"){\n\t\t\treturn mA / mB;\n\t\t}\n\t}\nprivate:\n\tint mA;\n\tint mB;\n\tstring mOperator;\n};\n\n//这种程序不利于扩展,维护困难,如果修改功能或者扩展功能需要在源代码基础上修改\n//面向对象程序设计一个基本原则:开闭原则(对修改关闭,对扩展开放)\n\n//抽象基类\nclass AbstractCaculator{\npublic:\n\tvoid setA(int a){\n\t\tthis->mA = a;\n\t}\n\tvirtual void setB(int b){\n\t\tthis->mB = b;\n\t}\n\tvirtual int getResult() = 0;\nprotected:\n\tint mA;\n\tint mB;\n\tstring mOperator;\n};\n\n//加法计算器\nclass PlusCaculator : public AbstractCaculator{\npublic:\n\tvirtual int getResult(){\n\t\treturn mA + mB;\n\t}\n};\n\n//减法计算器\nclass MinusCaculator : public AbstractCaculator{\npublic:\n\tvirtual int getResult(){\n\t\treturn mA - mB;\n\t}\n};\n\n//乘法计算器\nclass MultipliesCaculator : public AbstractCaculator{\npublic:\n\tvirtual int getResult(){\n\t\treturn mA * mB;\n\t}\n};\n\nvoid DoBussiness(AbstractCaculator* caculator){\n\tint a = 10;\n\tint b = 20;\n\tcaculator->setA(a);\n\tcaculator->setB(b);\n\tcout << \"计算结果:\" << caculator->getResult() << endl;\n\tdelete caculator;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Animal{\npublic:\n\tvoid speak(){\n\t\tcout << \"动物在唱歌...\" << endl;\n\t}\n};\n\nclass Dog : public Animal{\npublic:\n\tvoid speak(){\n\t\tcout << \"小狗在唱歌...\" << endl;\n\t}\n};\n\nvoid DoBussiness(Animal& animal){\n\tanimal.speak();\n}\n\nvoid test(){\n\tDog dog;\n\tDoBussiness(dog);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Animal{\npublic:\n\tvirtual void speak(){\n\t\tcout << \"动物在唱歌...\" << endl;\n\t}\n};\nclass Dog : public Animal{\npublic:\n\tvirtual void speak(){\n\t\tcout << \"小狗在唱歌...\" << endl;\n\t}\n};\nvoid DoBussiness(Animal& animal){\n\tanimal.speak();\n}\nvoid test(){\n\tDog dog;\n\tDoBussiness(dog);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class A{\npublic:\n\tvirtual void func1(){}\n\tvirtual void func2(){}\n};\n\n//B类为空,那么大小应该是1字节,实际情况是这样吗?\nclass B : public A{};\n\nvoid test(){\n\tcout << \"A size:\" << sizeof(A) << endl;\n\tcout << \"B size:\" << sizeof(B) << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class People{\npublic:\n\tPeople(){\n\t\tcout << \"构造函数 People!\" << endl;\n\t}\n\tvirtual void showName() = 0;\n\tvirtual ~People(){\n\t\tcout << \"析构函数 People!\" << endl;\n\t}\n};\n\nclass Worker : public People{\npublic:\n\tWorker(){\n\t\tcout << \"构造函数 Worker!\" << endl;\n\t\tpName = new char[10];\n\t}\n\tvirtual void showName(){\n\t\tcout << \"打印子类的名字!\" << endl;\n\t}\n\t~Worker(){\n\t\tcout << \"析构函数 Worker!\" << endl;\n\t\tif (pName != NULL){\n\t\t\tdelete pName;\n\t\t}\n\t}\nprivate:\n\tchar* pName;\n};\n\nvoid test(){\n\n\tPeople* people = new Worker;\n\tpeople->~People();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//非纯虚析构函数\nclass A{\npublic:\n\tvirtual ~A();\n};\n\nA::~A(){}\n\n//纯析构函数\nclass B{\npublic:\n\tvirtual ~B() = 0;\n};\n\nB::~B(){}\n\nvoid test(){\n\tA a; //A类不是抽象类,可以实例化对象\n\tB b; //B类是抽象类,不可以实例化对象\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class A{\npublic:\n\t//同一作用域下,func1函数重载\n\tvoid func1(){}\n\tvoid func1(int a){}\n\tvoid func1(int a,int b){}\n\tvoid func2(){}\n\tvirtual void func3(){}\n};\n\nclass B : public A{\npublic:\n\t//重定义基类的func2,隐藏了基类的func2方法\n\tvoid func2(){}\n\t//重写基类的func3函数,也可以覆盖基类func3\n\tvirtual void func3(){}\n};" + } + ], + "58954472": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//交换int数据\nvoid SwapInt(int& a,int& b){\n\tint temp = a;\n\ta = b;\n\tb = temp;\n}\n\n//交换char数据\nvoid SwapChar(char& a,char& b){\n\tchar temp = a;\n\ta = b;\n\tb = temp;\n}\n//问题:如果我要交换double类型数据,那么还需要些一个double类型数据交换的函数\n//繁琐,写的函数越多,当交换逻辑发生变化的时候,所有的函数都需要修改,无形当中增加了代码的维护难度\n\n//如果能把类型作为参数传递进来就好了,传递int就是Int类型交换,传递char就是char类型交换\n//我们有一种技术,可以实现类型的参数化---函数模板\n\n\n//class 和 typename都是一样的,用哪个都可以\ntemplate\nvoid MySwap(T& a,T& b){\n\tT temp = a;\n\ta = b;\n\tb = temp;\n}\n\nvoid test01(){\n\t\n\tint a = 10;\n\tint b = 20;\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n\t//1. 这里有个需要注意点,函数模板可以自动推导参数的类型\n\tMySwap(a,b);\n\tcout << \"a:\" << a << \" b:\" << b << endl;\n\n\tchar c1 = 'a';\n\tchar c2 = 'b';\n\tcout << \"c1:\" << c1 << \" c2:\" << c2 << endl;\n\t//2. 函数模板可以自动类型推导,那么也可以显式指定类型\n\tMySwap(c1, c2);\n\tcout << \"c1:\" << c1 << \" c2:\" << c2 << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//函数模板\ntemplate\nT MyPlus(T a, T b){\n\tT ret = a + b;\n\treturn ret;\n}\n\n//普通函数\nint MyPlus(int a,char b){\n\tint ret = a + b;\n\treturn ret;\n}\n\nvoid test02(){\n\n\tint a = 10;\n\tchar b = 'a';\n\n\t//调用函数模板,严格匹配类型\n\tMyPlus(a, a);\n\tMyPlus(b, b);\n\t//调用普通函数\n\tMyPlus(a, b);\n\t//调用普通函数 普通函数可以隐式类型转换\n\tMyPlus(b, a);\n\n\t//结论:\n\t//函数模板不允许自动类型转换,必须严格匹配类型\n\t//普通函数可以进行自动类型转换\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//函数模板\ntemplate\nT MyPlus(T a, T b){\n\tT ret = a + b;\n\treturn ret;\n}\n\n//普通函数\nint MyPlus(int a, int b){\n\tint ret = a + b;\n\treturn ret;\n}\n\nvoid test03(){\n\tint a = 10;\n\tint b = 20;\n\tchar c = 'a';\n\tchar d = 'b';\n\t//如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数\n\tcout << MyPlus(a, b) << endl;\n\t//如果我必须要调用函数模板,那么怎么办?\n\tcout << MyPlus<>(a, b) << endl;\n\t//此时普通函数也可以匹配,因为普通函数可以自动类型转换\n\t//但是此时函数模板能够有更好的匹配\n\t//如果函数模板可以产生一个更好的匹配,那么选择模板\n\tcout << MyPlus(c,d);\n}\n\n//函数模板重载\ntemplate\nT MyPlus(T a, T b, T c){\n\tT ret = a + b + c;\n\treturn ret;\n}\n\nvoid test04(){\n\n\tint a = 10;\n\tint b = 20;\n\tint c = 30;\n\tcout << MyPlus(a, b, c) << endl;\n\t//如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person\n{\npublic:\n\tPerson(string name, int age)\n\t{\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tstring mName;\n\tint mAge;\n};\n\n//普通交换函数\ntemplate \nvoid mySwap(T &a,T &b)\n{\n\tT temp = a;\n\ta = b; \n\tb = temp;\n}\n//第三代具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型\n//具体化优先于常规模板\ntemplate<>void mySwap(Person &p1, Person &p2)\n{\n\tstring nameTemp;\n\tint ageTemp;\n\n\tnameTemp = p1.mName;\n\tp1.mName = p2.mName;\n\tp2.mName = nameTemp;\n\n\tageTemp = p1.mAge;\n\tp1.mAge = p2.mAge;\n\tp2.mAge = ageTemp;\n\n}\n\nvoid test()\n{\n\tPerson P1(\"Tom\", 10);\n\tPerson P2(\"Jerry\", 20);\n\n\tcout << \"P1 Name = \" << P1.mName << \" P1 Age = \" << P1.mAge << endl;\n\tcout << \"P2 Name = \" << P2.mName << \" P2 Age = \" << P2.mAge << endl;\n\tmySwap(P1, P2);\n\tcout << \"P1 Name = \" << P1.mName << \" P1 Age = \" << P1.mAge << endl;\n\tcout << \"P2 Name = \" << P2.mName << \" P2 Age = \" << P2.mAge << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "template\nclass Person\n{\npublic:\n\tPerson(NameType name, AgeType age)\n\t{\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid showPerson()\n\t{\n\t\tcout << \"name: \" << this->mName << \" age: \" << this->mAge << endl;\n\t}\npublic:\n\tNameType mName;\n\tAgeType mAge;\n};\n\nvoid test01()\n{\n\t//Person P1(\"德玛西亚\",18); // 类模板不能进行类型自动推导 \n\tPersonP1(\"德玛西亚\", 18);\n\tP1.showPerson();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//类模板\ntemplate\nclass Person{\npublic:\n\tPerson(NameType name, AgeType age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid PrintPerson(){\n\t\tcout << \"Name:\" << this->mName << \" Age:\" << this->mAge << endl;\n\t}\npublic:\n\tNameType mName;\n\tAgeType mAge;\n};\n\n//类模板做函数参数\nvoid DoBussiness(Person& p){\n\tp.mAge += 20;\n\tp.mName += \"_vip\";\n\tp.PrintPerson();\n}\n\nint main(){\n\n\tPerson p(\"John\", 30);\n\tDoBussiness(p);\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//类模板\ntemplate\nclass MyClass{\npublic:\n\tMyClass(T property){\n\t\tthis->mProperty = property;\n\t}\npublic:\n\tT mProperty;\n};\n\n//子类实例化的时候需要具体化的父类,子类需要知道父类的具体类型是什么样的\n//这样c++编译器才能知道给子类分配多少内存\n\n//普通派生类\nclass SubClass : public MyClass{\npublic:\n\tSubClass(int b) : MyClass(20){\n\t\tthis->mB = b;\n\t}\npublic:\n\tint mB;\n};" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//父类类模板\ntemplate\nclass Base\n{\n\tT m;\n};\ntemplate\nclass Child2 : public Base //继承类模板的时候,必须要确定基类的大小\n{\npublic:\n\tT mParam;\n};\n\nvoid test02()\n{\n\tChild2 d2;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "template\nclass Person\n{\npublic:\n\tPerson(NameType name, AgeType age)\n\t{\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid showPerson()\n\t{\n\t\tcout << \"name: \" << this->mName << \" age: \" << this->mAge << endl;\n\t}\npublic:\n\tNameType mName;\n\tAgeType mAge;\n};\n\nvoid test01()\n{\n\t//Person P1(\"德玛西亚\",18); // 类模板不能进行类型自动推导 \n\tPersonP1(\"德玛西亚\", 18);\n\tP1.showPerson();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\n#include\nusing namespace std;\n\ntemplate\nclass Person{\npublic:\n\tPerson(T1 name, T2 age);\n\tvoid showPerson();\n\npublic:\n\tT1 mName;\n\tT2 mAge;\n};\n\n\n//类外实现\ntemplate\nPerson::Person(T1 name, T2 age){\n\tthis->mName = name;\n\tthis->mAge = age;\n}\n\n\ntemplate\nvoid Person::showPerson(){\n\tcout << \"Name:\" << this->mName << \" Age:\" << this->mAge << endl;\n}\n\nvoid test()\n{\n\tPerson p(\"Obama\", 20);\n\tp.showPerson();\n}\n\nint main(){\n\n\ttest();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\nusing namespace std;\n#include \n\ntemplate class Person;\n//告诉编译器这个函数模板是存在\ntemplate void PrintPerson2(Person& p);\n\n//友元函数在类内实现\ntemplate\nclass Person{\n\t//1. 友元函数在类内实现\n\tfriend void PrintPerson(Person& p){\n\t\tcout << \"Name:\" << p.mName << \" Age:\" << p.mAge << endl;\n\t}\n\n\t//2.友元函数类外实现\n\t//告诉编译器这个函数模板是存在\n\tfriend void PrintPerson2<>(Person& p);\n\n\t//3. 类模板碰到友元函数模板\n\ttemplate\n\tfriend void PrintPerson(Person& p);\n\npublic:\n\tPerson(T1 name, T2 age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\n\tvoid showPerson(){\n\t\tcout << \"Name:\" << this->mName << \" Age:\" << this->mAge << endl;\n\t}\nprivate:\n\tT1 mName;\n\tT2 mAge;\n};\n\nvoid test01()\n{\n\tPerson p(\"Jerry\", 20);\n\tPrintPerson(p);\n}\n\n\n// 类模板碰到友元函数\n//友元函数类外实现 加上<>空参数列表,告诉编译去匹配函数模板\ntemplate\nvoid PrintPerson2(Person& p)\n{\n\tcout << \"Name2:\" << p.mName << \" Age2:\" << p.mAge << endl;\n}\n\nvoid test02()\n{\n\tPerson p(\"Jerry\", 20);\n\tPrintPerson2(p); //不写可以编译通过,写了之后,会找PrintPerson2的普通函数调用,因为写了普通函数PrintPerson2的声明\t\n}\n\nint main(){\n\n\t//test01();\n\ttest02();\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#pragma once\ntemplate\nclass MyArray\n{\npublic:\n\texplicit MyArray(int capacity)\n\t{\n\t\tthis->m_Capacity = capacity;\n\t\tthis->m_Size = 0;\n\t\t// 如果T是对象,那么这个对象必须提供默认的构造函数\n\t\tpAddress = new T[this->m_Capacity];\n\t}\n\n\t//拷贝构造\n\tMyArray(const MyArray & arr)\n\t{\n\t\tthis->m_Capacity = arr.m_Capacity;\n\t\tthis->m_Size = arr.m_Size;\n\t\tthis->pAddress = new T[this->m_Capacity];\n\t\tfor (int i = 0; i < this->m_Size;i++)\n\t\t{\n\t\t\tthis->pAddress[i] = arr.pAddress[i];\n\t\t}\n\t}\n\n\t//重载[] 操作符 arr[0]\n\tT& operator [](int index)\n\t{\n\t\treturn this->pAddress[index]; \n\t}\n\t//尾插法\n\tvoid Push_back(const T & val)\n\t{\n\t\tif (this->m_Capacity == this->m_Size)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tthis->pAddress[this->m_Size] = val;\n\t\tthis->m_Size++;\n\t}\n\tvoid Pop_back()\n\t{\n\t\tif (this->m_Size == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tthis->m_Size--;\n\t}\n\tint\tgetSize()\n\t{\n\t\treturn this->m_Size;\n\t}\n\t//析构\n\t~MyArray()\n\t{\n\t\tif (this->pAddress != NULL)\n\t\t{\n\t\t\tdelete[] this->pAddress;\n\t\t\tthis->pAddress = NULL;\n\t\t\tthis->m_Capacity = 0; \n\t\t\tthis->m_Size = 0;\n\t\t}\n\t}\n\nprivate:\n\tT * pAddress; //指向一个堆空间,这个空间存储真正的数据\n\tint m_Capacity; //容量\n\tint m_Size; // 大小\n};\n\n测试代码:\n\n\nclass Person{\npublic:\n\tPerson(){}\n\tPerson(string name, int age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\npublic:\n\tstring mName;\n\tint mAge;\n};\n\n\nvoid PrintMyArrayInt(MyArray& arr){\n\tfor (int i = 0; i < arr.getSize(); i++){\n\t\tcout << arr[i] << \" \";\n\t}\n\tcout << endl;\n}\n\nvoid PrintMyPerson(MyArray& personArr)\n{\n\tfor (int i = 0; i < personArr.getSize(); i++){\n\t\tcout << \"姓名:\" << personArr[i].mName << \" 年龄: \" << personArr[i].mAge << endl;\n\t}\n\t\n}\n\t\n\tMyArray myArrayInt(10);\n\tfor (int i = 0; i < 9; i++)\n\t{\n\t\tmyArrayInt.Push_back(i);\n\t}\n\tmyArrayInt.Push_back(100);\n\tPrintMyArrayInt(myArrayInt);\n\nMyArray myArrayPerson(10);\n\tPerson p1(\"德玛西亚\", 30);\n\tPerson p2(\"提莫\", 20);\n\tPerson p3(\"孙悟空\",18);\n\tPerson p4(\"赵信\", 15);\n\tPerson p5(\"赵云\", 24);\n\tmyArrayPerson.Push_back(p1);\n\tmyArrayPerson.Push_back(p2);\n\tmyArrayPerson.Push_back(p3);\n\tmyArrayPerson.Push_back(p4);\n\tmyArrayPerson.Push_back(p5);" + } + ], + "58954473": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Animal{};\nclass Dog : public Animal{};\nclass Other{};\n\n//基础数据类型转换\nvoid test01(){\n\tchar a = 'a';\n\tdouble b = static_cast(a);\n}\n\n//继承关系指针互相转换\nvoid test02(){\n\t//继承关系指针转换\n\tAnimal* animal01 = NULL;\n\tDog* dog01 = NULL;\n\t//子类指针转成父类指针,安全\n\tAnimal* animal02 = static_cast(dog01);\n\t//父类指针转成子类指针,不安全\n\tDog* dog02 = static_cast(animal01);\n}\n\n//继承关系引用相互转换\nvoid test03(){\n\n\tAnimal ani_ref;\n\tDog dog_ref;\n\t//继承关系指针转换\n\tAnimal& animal01 = ani_ref;\n\tDog& dog01 = dog_ref;\n\t//子类指针转成父类指针,安全\n\tAnimal& animal02 = static_cast(dog01);\n\t//父类指针转成子类指针,不安全\n\tDog& dog02 = static_cast(animal01);\n}\n\n//无继承关系指针转换\nvoid test04(){\n\t\n\tAnimal* animal01 = NULL;\n\tOther* other01 = NULL;\n\n\t//转换失败\n\t//Animal* animal02 = static_cast(other01);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Animal {\npublic:\n\tvirtual void ShowName() = 0;\n};\nclass Dog : public Animal{\n\tvirtual void ShowName(){\n\t\tcout << \"I am a dog!\" << endl;\n\t}\n};\nclass Other {\npublic:\n\tvoid PrintSomething(){\n\t\tcout << \"我是其他类!\" << endl;\n\t}\n};\n\n//普通类型转换\nvoid test01(){\n\n\t//不支持基础数据类型\n\tint a = 10;\n\t//double a = dynamic_cast(a);\n}\n\n//继承关系指针\nvoid test02(){\n\n\tAnimal* animal01 = NULL;\n\tDog* dog01 = new Dog;\n\n\t//子类指针转换成父类指针 可以\n\tAnimal* animal02 = dynamic_cast(dog01);\n\tanimal02->ShowName();\n\t//父类指针转换成子类指针 不可以\n\t//Dog* dog02 = dynamic_cast(animal01);\n}\n\n//继承关系引用\nvoid test03(){\n\n\tDog dog_ref;\n\tDog& dog01 = dog_ref;\n\n\t//子类引用转换成父类引用 可以\n\tAnimal& animal02 = dynamic_cast(dog01);\n\tanimal02.ShowName();\n}\n\n//无继承关系指针转换\nvoid test04(){\n\t\n\tAnimal* animal01 = NULL;\n\tOther* other = NULL;\n\n\t//不可以\n\t//Animal* animal02 = dynamic_cast(other);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//常量指针转换成非常量指针\nvoid test01(){\n\t\n\tconst int* p = NULL;\n\tint* np = const_cast(p);\n\n\tint* pp = NULL;\n\tconst int* npp = const_cast(pp);\n\n\tconst int a = 10; //不能对非指针或非引用进行转换\n\t//int b = const_cast(a); }\n\n//常量引用转换成非常量引用\nvoid test02(){\n\nint num = 10;\n\tint & refNum = num;\n\n\tconst int& refNum2 = const_cast(refNum);\n\t\n}" + } + ], + "58954474": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//如果判断返回值,那么返回值是错误码还是结果?\n//如果不判断返回值,那么b==0时候,程序结果已经不正确\n//A写的代码\nint A_MyDivide(int a,int b){\n\tif (b == 0){\n\t\treturn -1;\n\t}\n\n\treturn a / b;\n}\n\n//B写的代码\nint B_MyDivide(int a,int b){\n\n\tint ba = a + 100;\n\tint bb = b;\n\n\tint ret = A_MyDivide(ba, bb); //由于B没有处理异常,导致B结果运算错误\n\n\treturn ret;\n}\n\n//C写的代码\nint C_MyDivide(){\n\n\tint a = 10;\n\tint b = 0;\n\n\tint ret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常\n\tif (ret == -1){\n\t\treturn -1;\n\t}\n\telse{\n\t\treturn ret;\n\t}\n}\n\n//所以,我们希望:\n//1.异常应该捕获,如果你捕获,可以,那么异常必须继续抛给上层函数,你不处理,不代表你的上层不处理\n//2.这个例子,异常没有捕获的结果就是运行结果错的一塌糊涂,结果未知,未知的结果程序没有必要执行下去" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "int A_MyDivide(int a, int b){\n\tif (b == 0){\n\t\tthrow 0;\n\t}\n\n\treturn a / b;\n}\n\n//B写的代码 B写代码比较粗心,忘记处理异常\nint B_MyDivide(int a, int b){\n\n\tint ba = a;\n\tint bb = b;\n\n\tint ret = A_MyDivide(ba, bb) + 100; //由于B没有处理异常,导致B结果运算错误\n\n\treturn ret;\n}\n\n//C写的代码\nint C_MyDivide(){\n\n\tint a = 10;\n\tint b = 0;\n\n\tint ret = 0;\n\n//没有处理异常,程序直接中断执行\n#if 1 \n\tret = B_MyDivide(a, b);\n\n//处理异常\n#else \n\ttry{\n\t\tret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常\n\t}\n\tcatch (int e){\n\t\tcout << \"C_MyDivide Call B_MyDivide 除数为:\" << e << endl;\n\t}\n#endif\n\t\n\treturn ret;\n}\n\nint main(){\n\n\tC_MyDivide();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void TestFunction(){\n\t\n\tcout << \"开始抛出异常...\" << endl;\n\t//throw 10; //抛出int类型异常\n\t//throw 'a'; //抛出char类型异常\n\t//throw \"abcd\"; //抛出char*类型异常\n\tstring ex = \"string exception!\";\n\tthrow ex;\n\n}\n\nint main(){\n\n\ttry{\n\t\tTestFunction();\n\t}\n\tcatch (int){\n\t\tcout << \"抛出Int类型异常!\" << endl;\n\t}\n\tcatch (char){\n\t\tcout << \"抛出Char类型异常!\" << endl;\n\t}\n\tcatch (char*){\n\t\tcout << \"抛出Char*类型异常!\" << endl;\n\t}\n\tcatch (string){\n\t\tcout << \"抛出string类型异常!\" << endl;\n\t}\n\t//捕获所有异常\n\tcatch (...){\n\t\tcout << \"抛出其他类型异常!\" << endl;\n\t}\n\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(string name){\n\t\tmName = name;\n\t\tcout << mName << \"对象被创建!\" << endl;\n\t}\n\t~Person(){\n\t\tcout << mName << \"对象被析构!\" << endl;\n\t}\npublic:\n\tstring mName;\n};\n\nvoid TestFunction(){\n\t\n\tPerson p1(\"aaa\");\n\tPerson p2(\"bbb\");\n\tPerson p3(\"ccc\");\n\n\t//抛出异常\n\tthrow 10;\n}\n\nint main(){\n\n\ttry{\n\t\tTestFunction();\n\t}\n\tcatch (...){\n\t\tcout << \"异常被捕获!\" << endl;\n\t}\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//可抛出所有类型异常\nvoid TestFunction01(){\n\tthrow 10;\n}\n\n//只能抛出int char char*类型异常\nvoid TestFunction02() throw(int,char,char*){\n\tstring exception = \"error!\";\n\tthrow exception;\n}\n\n//不能抛出任何类型异常\nvoid TestFunction03() throw(){\n\tthrow 10;\n}\n\nint main(){\n\n\ttry{\n\t\t//TestFunction01();\n\t\t//TestFunction02();\n\t\t//TestFunction03();\n\t}\n\tcatch (...){\n\t\tcout << \"捕获异常!\" << endl;\n\t}\n\t\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class MyException\n{\npublic:\n\tMyException(){\n\t\tcout << \"异常变量构造\" << endl;\n\t};\n\tMyException(const MyException & e)\n\t{\n\t\tcout << \"拷贝构造\" << endl;\n\t}\n\t~MyException()\n\t{\n\t\tcout << \"异常变量析构\" << endl;\n\t}\n};\nvoid DoWork()\n{\n\tthrow new MyException(); //test1 2都用 throw MyExecption();\n}\n\nvoid test01()\n{\n\ttry\n\t{\n\t\tDoWork();\n\t}\n\tcatch (MyException e)\n\t{\n\t\tcout << \"捕获 异常\" << endl;\n\t}\n}\nvoid test02()\n{\n\ttry\n\t{\n\t\tDoWork();\n\t}\n\tcatch (MyException &e)\n\t{\n\t\tcout << \"捕获 异常\" << endl;\n\t}\n}\n\nvoid test03()\n{\n\ttry\n\t{\n\t\tDoWork();\n\t}\n\tcatch (MyException *e)\n\t{\n\t\tcout << \"捕获 异常\" << endl;\n\t\tdelete e;\n\t}\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//异常基类\nclass BaseException{\npublic:\n\tvirtual void printError(){};\n};\n\n//空指针异常\nclass NullPointerException : public BaseException{\npublic:\n\tvirtual void printError(){\n\t\tcout << \"空指针异常!\" << endl;\n\t}\n};\n//越界异常\nclass OutOfRangeException : public BaseException{\npublic:\n\tvirtual void printError(){\n\t\tcout << \"越界异常!\" << endl;\n\t}\n};\n\nvoid doWork(){\n\n\tthrow NullPointerException();\n}\n\nvoid test()\n{\n\ttry{\n\t\tdoWork();\n\t}\n\tcatch (BaseException& ex){\n\t\tex.printError();\n\t}\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#include\nclass Person{\npublic:\n\tPerson(int age){\n\t\tif (age < 0 || age > 150){\n\t\t\tthrow out_of_range(\"年龄应该在0-150岁之间!\");\n\t\t}\n\t}\npublic:\n\tint mAge;\n};\n\nint main(){\n\n\ttry{\n\t\tPerson p(151);\n\t}\n\tcatch (out_of_range& ex){\n\t\tcout << ex.what() << endl;\n\t}\n\t\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//自定义异常类\nclass MyOutOfRange:public exception\n{\npublic:\n\tMyOutOfRange(const string errorInfo)\n\t{\n\t\tthis->m_Error = errorInfo;\n\t}\n\n\tMyOutOfRange(const char * errorInfo)\n\t{\n\t\tthis->m_Error = string( errorInfo);\n\t}\n\n\tvirtual ~MyOutOfRange()\n\t{\n\t\n\t}\n\tvirtual const char * what() const\n\t{\n\t\treturn this->m_Error.c_str() ;\n\t}\n\n\tstring m_Error;\n\n};\n\nclass Person\n{\npublic:\n\tPerson(int age)\n\t{\n\t\tif (age <= 0 || age > 150)\n\t\t{\n\t\t\t//抛出异常 越界\n\t\t\t//cout << \"越界\" << endl;\n\t\t\t//throw out_of_range(\"年龄必须在0~150之间\");\n\n\t\t\t//throw length_error(\"长度异常\");\n\t\t\tthrow MyOutOfRange((\"我的异常 年龄必须在0~150之间\"));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis->m_Age = age;\n\t\t}\n\t\t\n\t}\n\n\tint m_Age;\n};\n\n\nvoid test01()\n{\n\ttry\n\t{\n\t\tPerson p(151);\n\t}\n\tcatch ( out_of_range & e )\n\t{\n\t\tcout << e.what() << endl;\n\t}\n\tcatch (length_error & e)\n\t{\n\t\tcout << e.what() << endl;\n\t}\n\tcatch (MyOutOfRange e)\n\t{\n\t\tcout << e.what() << endl;\n\t}\n}" + } + ], + "58954476": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//cin.get\nvoid test01(){\n#if 0\n\tchar ch = cin.get();\n\tcout << ch << endl;\n\n\tcin.get(ch);\n\tcout << ch << endl;\n\n\n\t//链式编程\n\tchar char1, char2, char3, char4;\n\tcin.get(char1).get(char2).get(char3).get(char4);\n\n\tcout << char1 << \" \" << char2 << \"\" << char3 << \" \" << char4 << \" \";\n#endif\n\n\tchar buf[1024] = { 0 };\n\t//cin.get(buf.1024);\n\tcin.getline(buf,1024);\n\tcout << buf;\n}\n\n//cin.ignore\nvoid test02(){\n\n\tchar buf[1024] = { 0 };\n\tcin.ignore(2); //忽略缓冲区当前字符\n\tcin.get(buf,1024);\n\tcout << buf << endl;\n}\n\n//cin.putback 将数据放回缓冲区\nvoid test03(){\n\n\t//从缓冲区取走一个字符\n\tchar ch = cin.get();\n\tcout << \"从缓冲区取走的字符:\" << ch << endl;\n\t//将数据再放回缓冲区\n\tcin.putback(ch);\n\tchar buf[1024] = { 0 };\n\tcin.get(buf,1024);\n\tcout << buf << endl;\n\n}\n\n//cin.peek 偷窥\nvoid test04(){\n\t\n\t//偷窥下缓冲区的数据\n\tchar ch = cin.peek();\n\tcout << \"偷窥缓冲区数据:\" << ch << endl;\n\tchar buf[1024] = { 0 };\n\tcin.get(buf, 1024);\n\tcout << buf << endl;\n}\n\n//练习 作业 使用cin.get和putback完成类似功能\nvoid test05(){\n\t\n\tcout << \"请输入一个数字或者字符串:\" << endl;\n\tchar ch = cin.peek();\n\tif(ch >= '0' && ch <= '9'){\n\t\tint number;\n\t\tcin >> number;\n\t\tcout << \"数字:\" << number << endl;\n\t}\n\telse{\n\t\tchar buf[64] = { 0 };\n\t\tcin.getline(buf, 64);\n\t\tcout << \"字符串:\" << buf << endl;\n\t}\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//cout.flush 刷新缓冲区,linux下有效\nvoid test01(){\t\n\tcout << \"hello world\";\n\t//刷新缓冲区\n\tcout.flush(); \n}\n\n//cout.put 输出一个字符\nvoid test02(){\n\t\n\tcout.put('a');\n\t//链式编程\n\tcout.put('h').put('e').put('l');\n}\n\n//cout.write 输出字符串 buf,输出多少个\nvoid test03(){\n\t\n\t//char* str = \"hello world!\";\n\t//cout.write(str, strlen(str));\n\tchar* str = \"*************\";\n\tfor (int i = 1; i <= strlen(str); i ++){\n\t\tcout.write(str, i);\n\t\tcout << endl;\n\t}\n\n\tfor (int i = strlen(str); i > 0; i --){\n\t\tcout.write(str, i);\n\t\tcout << endl;\n\t}\n\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//通过流成员函数\nvoid test01(){\n\t\n\tint number = 99;\n\tcout.width(20);\n\tcout.fill('*');\n\tcout.setf(ios::left);\n\tcout.unsetf(ios::dec); //卸载十进制\n\tcout.setf(ios::hex);\n\tcout.setf(ios::showbase);\n\tcout.unsetf(ios::hex);\n\tcout.setf(ios::oct);\n\tcout << number << endl;\n\n}\n\n//使用控制符\nvoid test02(){\n\n\tint number = 99;\n\tcout << setw(20)\n\t\t<< setfill('~')\n\t\t<< setiosflags(ios::showbase)\n\t\t<< setiosflags(ios::left)\n\t\t<< hex\n\t\t<< number\n\t\t<< endl;\n\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "int main(){\n\n\tchar* sourceFileName = \"./source.txt\";\n\tchar* targetFileName = \"./target.txt\";\n\t//创建文件输入流对象\n\tifstream ism(sourceFileName, ios::in);\n\t//创建文件输出流对象\n\tofstream osm(targetFileName,ios::out);\n\n\tif (!ism){\n\t\tcout << \"文件打开失败!\" << endl;\n\t}\n\n\twhile (!ism.eof()){\n\t\tchar buf[1024] = { 0 };\n\t\tism.getline(buf,1024);\n\t\tcout << buf << endl;\n\t\tosm << buf << endl;\n\t}\n\n\t//关闭文件流对象\n\tism.close();\n\tosm.close();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class Person{\npublic:\n\tPerson(char* name,int age){\n\t\tstrcpy(this->mName, name);\n\t\tthis->mAge = age;\n\t}\npublic:\n\tchar mName[64];\n\tint mAge;\n};\n\nint main(){\n\n\tchar* fileName = \"person.txt\";\n\t//二进制模式读写文件\n\t//创建文件对象输出流\n\tofstream osm(fileName, ios::out | ios::binary);\n\n\tPerson p1(\"John\",33);\n\tPerson p2(\"Edward\", 34);\n\n\t//Person对象写入文件\n\tosm.write((const char*)&p1,sizeof(Person));\n\tosm.write((const char*)&p2, sizeof(Person));\n\n\t//关闭文件输出流\n\tosm.close();\n\n\t//从文件中读取对象数组\n\tifstream ism(fileName, ios::in | ios::binary);\n\tif (!ism){\n\t\tcout << \"打开失败!\" << endl;\n\t}\n\t\n\tPerson p3;\n\tPerson p4;\n\n\tism.read((char*)&p3, sizeof(Person));\n\tism.read((char*)&p4, sizeof(Person));\n\n\tcout << \"Name:\" << p3.mName << \" Age:\" << p3.mAge << endl;\n\tcout << \"Age:\" << p4.mName << \" Age:\" << p4.mAge << endl;\n\n\t//关闭文件输入流\n\tism.close();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954485": [], + "58954486": [], + "58954487": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\n#include\n#include\nusing namespace std;\n\n//STL 中的容器 算法 迭代器\nvoid test01(){\n\tvector v; //STL 中的标准容器之一 :动态数组\n\tv.push_back(1); //vector 容器提供的插入数据的方法\n\tv.push_back(5);\n\tv.push_back(3);\n\tv.push_back(7);\n\t//迭代器\n\tvector::iterator pStart = v.begin(); //vector 容器提供了 begin()方法 返回指向第一个元素的迭代器\n\tvector::iterator pEnd = v.end(); //vector 容器提供了 end()方法 返回指向最后一个元素下一个位置的迭代器\n\t//通过迭代器遍历\n\twhile (pStart != pEnd){\n\t\tcout << *pStart << \" \";\n\t\tpStart++;\n\t}\n\tcout << endl;\n}\n//STL 容器不单单可以存储基础数据类型,也可以存储类对象\nclass Teacher\n{\npublic:\n\tTeacher(int age) :age(age){};\n\t~Teacher(){};\npublic:\n\tint age;\n};\nvoid test02(){\n\tvector v; //存储 Teacher 类型数据的容器\n\tTeacher t1(10), t2(20), t3(30);\n\tv.push_back(t1);\n\tv.push_back(t2);\n\tv.push_back(t3);\n\tvector::iterator pStart = v.begin();\n\tvector::iterator pEnd = v.end();\n\t//通过迭代器遍历\n\twhile (pStart != pEnd){\n\t\tcout << pStart->age << \" \";\n\t\tpStart++;\n\t}\n\tcout << endl;\n}\n//存储 Teacher 类型指针\nvoid test03(){\n\tvector v; //存储 Teacher 类型指针\n\tTeacher* t1 = new Teacher(10);\n\tTeacher* t2 = new Teacher(20);\n\tTeacher* t3 = new Teacher(30);\n\tv.push_back(t1);\n\tv.push_back(t2);\n\tv.push_back(t3);\n\t//拿到容器迭代器\n\tvector::iterator pStart = v.begin();\n\tvector::iterator pEnd = v.end();\n\t//通过迭代器遍历\n\twhile (pStart != pEnd){\n\t\tcout << (*pStart)->age << \" \";\n\t\tpStart++;\n\t}\n\tcout << endl;\n}\n//容器嵌套容器 难点(不理解,可以跳过)\nvoid test04()\n{\n\tvector< vector > v;\n\tvectorv1;\n\tvectorv2;\n\tvectorv3;\n\n\tfor (int i = 0; i < 5;i++)\n\t{\n\t\tv1.push_back(i);\n\t\tv2.push_back(i * 10);\n\t\tv3.push_back(i * 100);\n\t}\n\tv.push_back(v1);\n\tv.push_back(v2);\n\tv.push_back(v3);\n\n\tfor (vector< vector >::iterator it = v.begin(); it != v.end();it++)\n\t{\n\t\tfor (vector::iterator subIt = (*it).begin(); subIt != (*it).end(); subIt ++)\n\t\t{\n\t\t\tcout << *subIt << \" \";\n\t\t}\n\t\tcout << endl;\n\t}\n} \nint main(){\n\t//test01();\n\t//test02();\n\t//test03();\n\ttest04();\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954488": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\tstring s = \"abcdefg\";\n\tchar& a = s[2];\n\tchar& b = s[3];\n\n\ta = '1';\n\tb = '2';\n\n\tcout << s << endl;\n\tcout << (int*)s.c_str() << endl;\n\n\ts = \"pppppppppppppppppppppppp\";\n\n\t//a = '1';\n\t//b = '2';\n\n\tcout << s << endl;\n\tcout << (int*)s.c_str() << endl;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\n#include\nusing namespace std;\n\nint main(){\n\n\tvector v;\n\tfor (int i = 0; i < 10;i ++){\n\t\tv.push_back(i);\n\t\tcout << v.capacity() << endl; // v.capacity()容器的容量\n\t}\n\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\n#include\nusing namespace std;\n\nint main(){\n\n\tvector v;\n\tfor (int i = 0; i < 100000;i ++){\n\t\tv.push_back(i);\n\t}\n\n\tcout << \"capacity:\" << v.capacity() << endl;\n\tcout << \"size:\" << v.size() << endl;\n\n\t//此时 通过resize改变容器大小\n\tv.resize(10);\n\n\tcout << \"capacity:\" << v.capacity() << endl;\n\tcout << \"size:\" << v.size() << endl;\n\n\t//容量没有改变\n\tvector(v).swap(v);\n\n\tcout << \"capacity:\" << v.capacity() << endl;\n\tcout << \"size:\" << v.size() << endl;\n\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\n#include\nusing namespace std;\n\nint main(){\n\n\tvector v;\n\n\t//预先开辟空间\n\tv.reserve(100000);\n\n\tint* pStart = NULL;\n\tint count = 0;\n\tfor (int i = 0; i < 100000;i ++){\n\t\tv.push_back(i);\n\t\tif (pStart != &v[0]){\n\t\t\tpStart = &v[0];\n\t\t\tcount++;\n\t\t}\n\t}\n\n\tcout << \"count:\" << count << endl;\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n#include\n#include\nusing namespace std;\n\nint main(){\n\n\tlist myList;\n\tfor (int i = 0; i < 10; i ++){\n\t\tmyList.push_back(i);\n\t}\n\n\tlist::_Nodeptr node = myList._Myhead->_Next;\n\n\tfor (int i = 0; i < myList._Mysize * 2;i++){\n\t\tcout << \"Node:\" << node->_Myval << endl;\n\t\tnode = node->_Next;\n\t\tif (node == myList._Myhead){\n\t\t\tnode = node->_Next;\n\t\t}\n\t}\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//插入操作返回值\nvoid test01(){\n\n\tset s;\n\tpair::iterator,bool> ret = s.insert(10);\n\tif (ret.second){\n\t\tcout << \"插入成功:\" << *ret.first << endl;\n\t}\n\telse{\n\t\tcout << \"插入失败:\" << *ret.first << endl;\n\t}\n\t\n\tret = s.insert(10);\n\tif(ret.second){\n\t\tcout << \"插入成功:\" << *ret.first << endl;\n\t}\n\telse{\n\t\tcout << \"插入失败:\" << *ret.first << endl;\n\t}\n\n}\n\nstruct MyCompare02{\n\tbool operator()(int v1,int v2){\n\t\treturn v1 > v2;\n\t}\n};\n\n//set从大到小\nvoid test02(){\n\n\tsrand((unsigned int)time(NULL));\n\t//我们发现set容器的第二个模板参数可以设置排序规则,默认规则是less<_Kty>\n\tset s;\n\tfor (int i = 0; i < 10;i++){\n\t\ts.insert(rand() % 100);\n\t}\n\t\n\tfor (set::iterator it = s.begin(); it != s.end(); it ++){\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n}\n\n//set容器中存放对象\nclass Person{\npublic:\n\tPerson(string name,int age){\n\t\tthis->mName = name;\n\t\tthis->mAge = age;\n\t}\npublic:\n\tstring mName;\n\tint mAge;\n};\n\n\nstruct MyCompare03{\n\tbool operator()(const Person& p1,const Person& p2){\n\t\treturn p1.mAge > p2.mAge;\n\t}\n};\n\nvoid test03(){\n\n\tset s;\n\n\tPerson p1(\"aaa\", 20);\n\tPerson p2(\"bbb\", 30);\n\tPerson p3(\"ccc\", 40);\n\tPerson p4(\"ddd\", 50);\n\n\ts.insert(p1);\n\ts.insert(p2);\n\ts.insert(p3);\n\ts.insert(p4);\n\n\tfor (set::iterator it = s.begin(); it != s.end(); it++){\n\t\tcout << \"Name:\" << it->mName << \" Age:\" << it->mAge << endl;\n\t}\n\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#define _CRT_SECURE_NO_WARNINGS\n\n#include\n#include\n#include\n#include\nusing namespace std;\n\n//multimap 案例\n//公司今天招聘了 5 个员工,5 名员工进入公司之后,需要指派员工在那个部门工作\n//人员信息有: 姓名 年龄 电话 工资等组成\n//通过 Multimap 进行信息的插入 保存 显示\n//分部门显示员工信息 显示全部员工信息\n\n\n#define SALE_DEPATMENT 1 //销售部门\n#define DEVELOP_DEPATMENT 2 //研发部门\n#define FINACIAL_DEPATMENT 3 //财务部门\n#define ALL_DEPATMENT 4 //所有部门\n\n//员工类\nclass person{\npublic:\n\tstring name; //员工姓名\n\tint age; //员工年龄\n\tdouble salary; //员工工资\n\tstring tele; //员工电话\n};\n\n//创建5个员工\nvoid CreatePerson(vector& vlist){\n\n\tstring seed = \"ABCDE\";\n\tfor (int i = 0; i < 5; i++){\n\t\tperson p;\n\t\tp.name = \"员工\";\n\t\tp.name += seed[i];\n\t\tp.age = rand() % 30 + 20;\n\t\tp.salary = rand() % 20000 + 10000;\n\t\tp.tele = \"010-8888888\";\n\t\tvlist.push_back(p);\n\t}\n\n}\n\n//5名员工分配到不同的部门\nvoid PersonByGroup(vector& vlist, multimap& plist){\n\n\n\tint operate = -1; //用户的操作\n\n\tfor (vector::iterator it = vlist.begin(); it != vlist.end(); it++){\n\n\t\tcout << \"当前员工信息:\" << endl;\n\t\tcout << \"姓名:\" << it->name << \" 年龄:\" << it->age << \" 工资:\" << it->salary << \" 电话:\" << it->tele << endl;\n\t\tcout << \"请对该员工进行部门分配(1 销售部门, 2 研发部门, 3 财务部门):\" << endl;\n\t\tscanf(\"%d\", &operate);\n\n\t\twhile (true){\n\n\t\t\tif (operate == SALE_DEPATMENT){ //将该员工加入到销售部门\n\t\t\t\tplist.insert(make_pair(SALE_DEPATMENT, *it));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (operate == DEVELOP_DEPATMENT){\n\t\t\t\tplist.insert(make_pair(DEVELOP_DEPATMENT, *it));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (operate == FINACIAL_DEPATMENT){\n\t\t\t\tplist.insert(make_pair(FINACIAL_DEPATMENT, *it));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse{\n\t\t\t\tcout << \"您的输入有误,请重新输入(1 销售部门, 2 研发部门, 3 财务部门):\" << endl;\n\t\t\t\tscanf(\"%d\", &operate);\n\t\t\t}\n\n\t\t}\n\n\t}\n\tcout << \"员工部门分配完毕!\" << endl;\n\tcout << \"***********************************************************\" << endl;\n\n}\n\n//打印员工信息\nvoid printList(multimap& plist, int myoperate){\n\n\tif (myoperate == ALL_DEPATMENT){\n\t\tfor (multimap::iterator it = plist.begin(); it != plist.end(); it++){\n\t\t\tcout << \"姓名:\" << it->second.name << \" 年龄:\" << it->second.age << \" 工资:\" << it->second.salary << \" 电话:\" << it->second.tele << endl;\n\t\t}\n\t\treturn;\n\t}\n\n\tmultimap::iterator it = plist.find(myoperate);\n\tint depatCount = plist.count(myoperate);\n\tint num = 0;\n\tif (it != plist.end()){\n\t\twhile (it != plist.end() && num < depatCount){\n\t\t\tcout << \"姓名:\" << it->second.name << \" 年龄:\" << it->second.age << \" 工资:\" << it->second.salary << \" 电话:\" << it->second.tele << endl;\n\t\t\tit++;\n\t\t\tnum++;\n\t\t}\n\t}\n}\n\n//根据用户操作显示不同部门的人员列表\nvoid ShowPersonList(multimap& plist, int myoperate){\n\n\tswitch (myoperate)\n\t{\n\tcase SALE_DEPATMENT:\n\t\tprintList(plist, SALE_DEPATMENT);\n\t\tbreak;\n\tcase DEVELOP_DEPATMENT:\n\t\tprintList(plist, DEVELOP_DEPATMENT);\n\t\tbreak;\n\tcase FINACIAL_DEPATMENT:\n\t\tprintList(plist, FINACIAL_DEPATMENT);\n\t\tbreak;\n\tcase ALL_DEPATMENT:\n\t\tprintList(plist, ALL_DEPATMENT);\n\t\tbreak;\n\t}\n}\n\n//用户操作菜单\nvoid PersonMenue(multimap& plist){\n\n\tint flag = -1;\n\tint isexit = 0;\n\twhile (true){\n\t\tcout << \"请输入您的操作((1 销售部门, 2 研发部门, 3 财务部门, 4 所有部门, 0退出):\" << endl;\n\t\tscanf(\"%d\", &flag);\n\n\t\tswitch (flag)\n\t\t{\n\t\tcase SALE_DEPATMENT:\n\t\t\tShowPersonList(plist, SALE_DEPATMENT);\n\t\t\tbreak;\n\t\tcase DEVELOP_DEPATMENT:\n\t\t\tShowPersonList(plist, DEVELOP_DEPATMENT);\n\t\t\tbreak;\n\t\tcase FINACIAL_DEPATMENT:\n\t\t\tShowPersonList(plist, FINACIAL_DEPATMENT);\n\t\t\tbreak;\n\t\tcase ALL_DEPATMENT:\n\t\t\tShowPersonList(plist, ALL_DEPATMENT);\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tisexit = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcout << \"您的输入有误,请重新输入!\" << endl;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (isexit == 1){\n\t\t\tbreak;\n\t\t}\n\t}\n\n}\n\nint main(){\n\n\tvector vlist; //创建的5个员工 未分组\n\tmultimap plist; //保存分组后员工信息\n\n\t//创建5个员工\n\tCreatePerson(vlist);\n\t//5名员工分配到不同的部门\n\tPersonByGroup(vlist, plist);\n\t//根据用户输入显示不同部门员工信息列表 或者 显示全部员工的信息列表\n\tPersonMenue(plist);\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}" + } + ], + "58954498": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//函数对象是重载了函数调用符号的类\nclass MyPrint\n{\npublic:\n\tMyPrint()\n\t{\n\t\tm_Num = 0;\n\t}\n\tint m_Num;\n\npublic:\n\tvoid operator() (int num)\n\t{\n\t\tcout << num << endl;\n\t\tm_Num++;\n\t}\n};\n\n//函数对象\n//重载了()操作符的类实例化的对象,可以像普通函数那样调用,可以有参数 ,可以有返回值\nvoid test01()\n{\n\tMyPrint myPrint;\n\tmyPrint(20);\n\n}\n// 函数对象超出了普通函数的概念,可以保存函数的调用状态\nvoid test02()\n{\n\tMyPrint myPrint;\n\tmyPrint(20);\n\tmyPrint(20);\n\tmyPrint(20);\n\tcout << myPrint.m_Num << endl;\n}\n\nvoid doBusiness(MyPrint print,int num)\n{\n\tprint(num);\n}\n\n//函数对象作为参数\nvoid test03()\n{\n\t//参数1:匿名函数对象\n\tdoBusiness(MyPrint(),30);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class GreaterThenFive\n{\npublic:\n\tbool operator()(int num)\n\t{\n\t\treturn num > 5;\n\t}\n\n};\n//一元谓词\nvoid test01()\n{\n\tvector v;\n\tfor (int i = 0; i < 10;i ++)\n\t{\n\t\tv.push_back(i);\n\t}\n\t\n\t vector::iterator it = find_if(v.begin(), v.end(), GreaterThenFive());\n\t if (it == v.end())\n\t {\n\t\t cout << \"没有找到\" << endl;\n\t }\n\t else\n\t {\n\t\t cout << \"找到了: \" << *it << endl;\n\t }\n}\n\n//二元谓词\nclass MyCompare\n{\npublic:\n\tbool operator()(int num1, int num2)\n\t{\n\t\treturn num1 > num2;\n\t}\n};\n\nvoid test02()\n{\n\tvector v;\n\tv.push_back(10);\n\tv.push_back(40);\n\tv.push_back(20);\n\tv.push_back(90);\n\tv.push_back(60);\n\n\t//默认从小到大\n\tsort(v.begin(), v.end());\n\tfor (vector::iterator it = v.begin(); it != v.end();it++)\n\t{\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n\tcout << \"----------------------------\" << endl;\n\t//使用函数对象改变算法策略,排序从大到小\n\tsort(v.begin(), v.end(),MyCompare());\n\tfor (vector::iterator it = v.begin(); it != v.end(); it++)\n\t{\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//取反仿函数\nvoid test01()\n{\n\tnegate n;\n\tcout << n(50) << endl;\n}\n\n//加法仿函数\nvoid test02()\n{\n\tplus p;\n\tcout << p(10, 20) << endl;\n}\n\n//大于仿函数\nvoid test03()\n{\n\tvector v;\n\tsrand((unsigned int)time(NULL));\n\tfor (int i = 0; i < 10; i++){\n\t\tv.push_back(rand() % 100);\n\t}\n\n\tfor (vector::iterator it = v.begin(); it != v.end(); it++){\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n\tsort(v.begin(), v.end(), greater());\n\n\tfor (vector::iterator it = v.begin(); it != v.end(); it++){\n\t\tcout << *it << \" \";\n\t}\n\tcout << endl;\n\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//函数适配器bind1st bind2nd\n//现在我有这个需求 在遍历容器的时候,我希望将容器中的值全部加上100之后显示出来,怎么做?\n//我们直接给函数对象绑定参数 编译阶段就会报错\n//for_each(v.begin(), v.end(), bind2nd(myprint(),100));\n//如果我们想使用绑定适配器,需要我们自己的函数对象继承binary_function 或者 unary_function\n//根据我们函数对象是一元函数对象 还是二元函数对象\nclass MyPrint :public binary_function\n{\npublic:\n\tvoid operator()(int v1,int v2) const\n\t{\n\t\tcout << \"v1 = : \" << v1 << \" v2 = :\" <v;\n\tfor (int i = 0; i < 10; i++)\n\t{\n\t\tv.push_back(i);\n\t}\n\tcout << \"请输入起始值:\" << endl;\n\tint x;\n\tcin >> x;\n\n\tfor_each(v.begin(), v.end(), bind1st(MyPrint(), x));\n\t//for_each(v.begin(), v.end(), bind2nd( MyPrint(),x ));\n}\n//总结: bind1st和bind2nd区别?\n//bind1st : 将参数绑定为函数对象的第一个参数\n//bind2nd : 将参数绑定为函数对象的第二个参数\n//bind1st bind2nd将二元函数对象转为一元函数对象\n\n\nclass GreaterThenFive:public unary_function\n{\npublic:\n\tbool operator ()(int v) const\n\t{\n\t\treturn v > 5;\n\t}\n};\n\n//2、取反适配器\nvoid test02()\n{\n\tvector v;\n\tfor (int i = 0; i < 10;i++)\n\t{\n\t\tv.push_back(i);\n\t}\n\t\n// \tvector::iterator it = find_if(v.begin(), v.end(), GreaterThenFive()); //返回第一个大于5的迭代器\n//\tvector::iterator it = find_if(v.begin(), v.end(), not1(GreaterThenFive())); //返回第一个小于5迭代器\n\t//自定义输入\n\tvector::iterator it = find_if(v.begin(), v.end(), not1 ( bind2nd(greater(),5)));\n\tif (it == v.end())\n\t{\n\t\tcout << \"没找到\" << endl;\n\t}\n\telse\n\t{\n\t\tcout << \"找到\" << *it << endl;\n\t}\n\n\t//排序 二元函数对象\n\tsort(v.begin(), v.end(), not2(less()));\n\tfor_each(v.begin(), v.end(), [](int val){cout << val << \" \"; });\n\n}\n//not1 对一元函数对象取反\n//not2 对二元函数对象取反\n\nvoid MyPrint03(int v,int v2)\n{\n\tcout << v + v2<< \" \";\n}\n\n//3、函数指针适配器 ptr_fun\nvoid test03()\n{\n\tvector v;\n\tfor (int i = 0; i < 10; i++)\n\t{\n\t\tv.push_back(i);\n\t}\n\t// ptr_fun( )把一个普通的函数指针适配成函数对象\n\tfor_each(v.begin(), v.end(), bind2nd( ptr_fun( MyPrint03 ), 100));\n}\n\n\n//4、成员函数适配器\nclass Person\n{\npublic:\n\tPerson(string name, int age)\n\t{\n\t\tm_Name = name;\n\t\tm_Age = age;\n\t}\n\n\t//打印函数\n\tvoid ShowPerson(){\n\t\tcout << \"成员函数:\" << \"Name:\" << m_Name << \" Age:\" << m_Age << endl;\n\t}\n\tvoid Plus100()\n\t{\n\t\tm_Age += 100;\n\t}\npublic:\n\tstring m_Name;\n\tint m_Age;\n};\n\nvoid MyPrint04(Person &p)\n{\n\tcout << \"姓名:\" << p.m_Name << \" 年龄:\" << p.m_Age << endl;\n\n};\n\nvoid test04()\n{\n\tvector v;\n\tPerson p1(\"aaa\", 10);\n\tPerson p2(\"bbb\", 20);\n\tPerson p3(\"ccc\", 30);\n\tPerson p4(\"ddd\", 40);\n\tv.push_back(p1);\n\tv.push_back(p2);\n\tv.push_back(p3);\n\tv.push_back(p4);\n\n\t//for_each(v.begin(), v.end(), MyPrint04);\n\t//利用 mem_fun_ref 将Person内部成员函数适配\n\tfor_each(v.begin(), v.end(), mem_fun_ref(&Person::ShowPerson));\n// \tfor_each(v.begin(), v.end(), mem_fun_ref(&Person::Plus100));\n// \tfor_each(v.begin(), v.end(), mem_fun_ref(&Person::ShowPerson));\n}\n\nvoid test05(){\n\n\tvector v1;\n\t//创建数据\n\tPerson p1(\"aaa\", 10);\n\tPerson p2(\"bbb\", 20);\n\tPerson p3(\"ccc\", 30);\n\tPerson p4(\"ddd\", 40);\n\n\tv1.push_back(&p1);\n\tv1.push_back(&p2);\n\tv1.push_back(&p3);\n\tv1.push_back(&p4);\n\n\tfor_each(v1.begin(), v1.end(), mem_fun(&Person::ShowPerson));\n}\n\n//如果容器存放的是对象指针, 那么用mem_fun\n//如果容器中存放的是对象实体,那么用mem_fun_ref" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "/*\n\ntemplate inline\nvoid for_each(_InIt _First, _InIt _Last, _Fn1 _Func)\n{\n\tfor (; _First != _Last; ++_First)\n\t\t_Func(*_First);\n}\n\n*/\n\n//普通函数\nvoid print01(int val){\n\tcout << val << \" \";\n}\n//函数对象\nstruct print001{\n\tvoid operator()(int val){\n\t\tcout << val << \" \";\n\t}\n};\n\n//for_each算法基本用法\nvoid test01(){\n\t\n\tvector v;\n\tfor (int i = 0; i < 10;i++){\n\t\tv.push_back(i);\n\t}\n\n\t//遍历算法\n\tfor_each(v.begin(), v.end(), print01);\n\tcout << endl;\n\n\tfor_each(v.begin(), v.end(), print001());\n\tcout << endl;\n\n}\n\nstruct print02{\n\tprint02(){\n\t\tmCount = 0;\n\t}\n\tvoid operator()(int val){\n\t\tcout << val << \" \";\n\t\tmCount++;\n\t}\n\tint mCount;\n};\n\n//for_each返回值\nvoid test02(){\n\n\tvector v;\n\tfor (int i = 0; i < 10; i++){\n\t\tv.push_back(i);\n\t}\n\n\tprint02 p = for_each(v.begin(), v.end(), print02());\n\tcout << endl;\n\tcout << p.mCount << endl;\n}\n\nstruct print03 : public binary_function{\n\tvoid operator()(int val,int bindParam) const{\n\t\tcout << val + bindParam << \" \";\n\t}\n};\n\n//for_each绑定参数输出\nvoid test03(){\n\t\n\tvector v;\n\tfor (int i = 0; i < 10; i++){\n\t\tv.push_back(i);\n\t}\n\n\tfor_each(v.begin(), v.end(), bind2nd(print03(),100));\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//transform 将一个容器中的值搬运到另一个容器中\n/*\n\ttemplate inline \n\t_OutIt _Transform(_InIt _First, _InIt _Last,_OutIt _Dest, _Fn1 _Func)\n\t{\t\n\n\t\tfor (; _First != _Last; ++_First, ++_Dest)\n\t\t\t*_Dest = _Func(*_First);\n\t\treturn (_Dest);\n\t}\n\n\ttemplate inline\n\t_OutIt _Transform(_InIt1 _First1, _InIt1 _Last1,_InIt2 _First2, _OutIt _Dest, _Fn2 _Func)\n\t{\t\n\t\tfor (; _First1 != _Last1; ++_First1, ++_First2, ++_Dest)\n\t\t\t*_Dest = _Func(*_First1, *_First2);\n\t\treturn (_Dest);\n\t}\n*/\n\nstruct transformTest01{\n\tint operator()(int val){\n\t\treturn val + 100;\n\t}\n};\nstruct print01{\n\tvoid operator()(int val){\n\t\tcout << val << \" \";\n\t}\n};\nvoid test01(){\n\n\tvector vSource;\n\tfor (int i = 0; i < 10;i ++){\n\t\tvSource.push_back(i + 1);\n\t}\n\n\t//目标容器\n\tvector vTarget;\n\t//给vTarget开辟空间\n\tvTarget.resize(vSource.size());\n\t//将vSource中的元素搬运到vTarget\n\tvector::iterator it = transform(vSource.begin(), vSource.end(), vTarget.begin(), transformTest01());\n\t//打印\n\tfor_each(vTarget.begin(), vTarget.end(), print01()); cout << endl;\n\t\n}\n\n//将容器1和容器2中的元素相加放入到第三个容器中\nstruct transformTest02{\n\tint operator()(int v1,int v2){\n\t\treturn v1 + v2;\n\t}\n};\nvoid test02(){\n\n\tvector vSource1;\n\tvector vSource2;\n\tfor (int i = 0; i < 10; i++){\n\t\tvSource1.push_back(i + 1);\t\n\t}\n\n\t//目标容器\n\tvector vTarget;\n\t//给vTarget开辟空间\n\tvTarget.resize(vSource1.size());\n\ttransform(vSource1.begin(), vSource1.end(), vSource2.begin(),vTarget.begin(), transformTest02());\n\t//打印\n\tfor_each(vTarget.begin(), vTarget.end(), print01()); cout << endl;\n}" + } + ], + "58954500": [], + "58954512": [], + "58954513": [], + "58954515": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "#include \"widget.h\"\n#include \n\nint main(int argc, char *argv[])\n{\n QApplication a(argc, argv);\n Widget w;\n w.show();\n\n return a.exec();\n}" + } + ], + "58954525": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QPushButton * btn = new QPushButton; 头文件 #include \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);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "{\n QWidget window;\n QPushButton quit(\"Quit\", &window);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "{\n QPushButton quit(\"Quit\");\n QWidget window;\n quit.setParent(&window);\n}" + } + ], + "58954527": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": " QPushButton * quitBtn = new QPushButton(\"关闭窗口\",this);\n connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "connect(sender, signal, receiver, slot);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "首先定义一个学生类和老师类:\n\t老师类中声明信号 饿了 hungry\nsignals:\n void hungury();\n\t学生类中声明槽 请客 treat\n\tpublic slots:\n void treat();\n\t在窗口中声明一个公共方法下课,这个方法的调用会触发老师饿了这个信号,而响应槽函数学生请客\n\tvoid MyWidget::ClassIsOver()\n{\n //发送信号\n emit teacher->hungury();\n}\n\t学生响应了槽函数,并且打印信息\n//自定义槽函数 实现\nvoid Student::eat()\n{\n qDebug() << \"该吃饭了!\";\n}\n在窗口中连接信号槽\n teacher = new Teacher(this);\n student = new Student(this);\n\n connect(teacher,&Teacher::hungury,student,&Student::treat);\n并且调用下课函数,测试打印出 “该吃饭了”" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "自定义的信号 hungry带参数,需要提供重载的自定义信号和 自定义槽\nvoid hungury(QString name); 自定义信号\nvoid treat(QString name ); 自定义槽\n但是由于有两个重名的自定义信号和自定义的槽,直接连接会报错,所以需要利用函数指针来指向函数地址, 然后在做连接\nvoid (Teacher:: * teacherSingal)(QString) = &Teacher::hungury;\nvoid (Student:: * studentSlot)(QString) = &Student::treat;\nconnect(teacher,teacherSingal,student,studentSlot);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "connect(zt,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "[capture](parameters) mutable ->return-type\n{\nstatement\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": " QPushButton * myBtn = new QPushButton (this);\n QPushButton * myBtn2 = new QPushButton (this);\n myBtn2->move(100,100);\n int m = 10;\n\n connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 20; qDebug() << m; });\n\n connect(myBtn2,&QPushButton::clicked,this,[=] () { qDebug() << m; });\n\n qDebug() << m;" + } + ], + "58954529": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QMenuBar *menuBar() const" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QAction* addMenu(QMenu * menu)\nQMenu* addMenu(const QString & title)\nQMenu* addMenu(const QIcon & icon, const QString & title)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QAction* activeAction() const\nQAction* addAction(const QString & text)\nQAction* addAction(const QIcon & icon, const QString & text)\nQAction* addAction(const QString & text, const QObject * receiver,\nconst char * member, const QKeySequence & shortcut = 0)\nQAction* addAction(const QIcon & icon, const QString & text,\nconst QObject * receiver, const char * member,\nconst QKeySequence & shortcut = 0)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//添加小部件\nvoid addWidget(QWidget * widget, int stretch = 0)\n//插入小部件\nintinsertWidget(int index, QWidget * widget, int stretch = 0)\n//删除小部件\nvoid removeWidget(QWidget * widget)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QDockWidget * dock = new QDockWidget(\"标题\",this);\n addDockWidget(Qt::LeftDockWidgetArea,dock);\ndock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea); 设置区域范围" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QTextEdit * edit = new QTextEdit(this);\nsetCentralWidget(edit);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "\n\ndocument-open.png\n\n\ndocument-open-fr.png\n\n" + } + ], + "58954535": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QDialog dialog;\ndialog.setWindowTitle(tr(\"Hello, dialog!\"));\ndialog.exec();" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QDialog dialog(this);\ndialog.setWindowTitle(tr(\"Hello, dialog!\"));\ndialog.show();" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QDialog *dialog = new QDialog;\ndialog->setWindowTitle(tr(\"Hello, dialog!\"));\ndialog->show();" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QDialog *dialog = new QDialog;\ndialog->setAttribute(Qt::WA_DeleteOnClose);\ndialog->setWindowTitle(tr(\"Hello, dialog!\"));\ndialog->show();" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void about(QWidget * parent, const QString & title, const QString & text)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void aboutQt(QWidget * parent, const QString & title = QString()):" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "StandardButton critical(QWidget * parent,\nconst QString & title,\nconst QString & text,\nStandardButtons buttons = Ok,\nStandardButton defaultButton = NoButton):" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "StandardButton information(QWidget * parent,\nconst QString & title,\nconst QString & text,\nStandardButtons buttons = Ok,\nStandardButton defaultButton = NoButton)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "StandardButton question(QWidget * parent,\nconst QString & title,\nconst QString & text,\nStandardButtons buttons = StandardButtons( Yes | No ),\nStandardButton defaultButton = NoButton)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "StandardButton warning(QWidget * parent,\nconst QString & title,\nconst QString & text,\nStandardButtons buttons = Ok,\nStandardButton defaultButton = NoButton)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "if (QMessageBox::Yes == QMessageBox::question(this,\ntr(\"Question\"), tr(\"Are you OK?\"),\nQMessageBox::Yes | QMessageBox::No,\nQMessageBox::Yes))\n{\nQMessageBox::information(this, tr(\"Hmmm...\"),\ntr(\"I'm glad to hear that!\"));\n}\nelse\n{\nQMessageBox::information(this, tr(\"Hmmm...\"),\ntr(\"I'm sorry!\"));\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QMessageBox msgBox;\nmsgBox.setText(tr(\"The document has been modified.\"));\nmsgBox.setInformativeText(tr(\"Do you want to save your changes?\"));\nmsgBox.setDetailedText(tr(\"Differences here...\"));\nmsgBox.setStandardButtons(QMessageBox::Save\n | QMessageBox::Discard\n | QMessageBox::Cancel);\nmsgBox.setDefaultButton(QMessageBox::Save);\nint ret = msgBox.exec();\nswitch (ret) \n{\ncase QMessageBox::Save:\n qDebug() << \"Save document!\";\n break;\ncase QMessageBox::Discard:\n qDebug() << \"Discard changes!\";\n break;\ncase QMessageBox::Cancel:\n qDebug() << \"Close document!\";\n break;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "openAction = new QAction(QIcon(\":/images/file-open\"),tr(\"&Open...\"), this);\nopenAction->setStatusTip(tr(\"Open an existing file\"));\n\nsaveAction = new QAction(QIcon(\":/images/file-save\"), tr(\"&Save...\"), this);\nsaveAction->setStatusTip(tr(\"Save a new file\"));\n\nQMenu *file = menuBar()->addMenu(tr(\"&File\"));\nfile->addAction(openAction);\nfile->addAction(saveAction);\n\nQToolBar *toolBar = addToolBar(tr(\"&File\"));\ntoolBar->addAction(openAction);\ntoolBar->addAction(saveAction);\n\ntextEdit = new QTextEdit(this);\nsetCentralWidget(textEdit);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "connect(openAction, &QAction::triggered, \nthis, &MainWindow::openFile);\nconnect(saveAction, &QAction::triggered, \nthis, &MainWindow::saveFile);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//打开文件\nvoid MainWindow::openFile()\n{\n QString path = QFileDialog::getOpenFileName(this,\n tr(\"Open File\"), \".\", tr(\"Text Files(*.txt)\"));\n if(!path.isEmpty()) \n{\n QFile file(path);\n if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) \n{\n QMessageBox::warning(this, tr(\"Read File\"),\n tr(\"Cannot open file:\\n%1\").arg(path));\n return;\n }\n QTextStream in(&file);\n textEdit->setText(in.readAll());\n file.close();\n } \nelse \n{\n QMessageBox::warning(this, tr(\"Path\"),\n tr(\"You did not select any file.\"));\n }\n}\n\n//保存文件\nvoid MainWindow::saveFile()\n{\n QString path = QFileDialog::getSaveFileName(this,\n tr(\"Open File\"), \".\", tr(\"Text Files(*.txt)\"));\n if(!path.isEmpty()) \n{\n QFile file(path);\n if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) \n{\n QMessageBox::warning(this, tr(\"Write File\"),\n tr(\"Cannot open file:\\n%1\").arg(path));\n return;\n }\n QTextStream out(&file);\n out << textEdit->toPlainText();\n file.close();\n } \nelse \n{\n QMessageBox::warning(this, tr(\"Path\"),\n tr(\"You did not select any file.\"));\n }\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QString getOpenFileName(QWidget * parent = 0,\n const QString & caption = QString(),\n const QString & dir = QString(),\n const QString & filter = QString(),\n QString * selectedFilter = 0,\n Options options = 0)" + } + ], + "58954536": [], + "58954540": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "voidsetText(const QString &)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QLable *label = new QLable;\nlabel->setText(\"Hello, World!\");" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QLabel * label = new QLabel(this);\nlabel ->setText(\"Hello, World\");\nlabel ->setText(\"

百度一下

\");\nlabel ->setOpenExternalLinks(true);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "voidsetPixmap(const QPixmap &)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QPixmap pixmap;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "pixmap.load(\":/Image/boat.jpg\");" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QLabel *label = new QLabel;\nlabel.setPixmap(pixmap);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "voidsetMovie(QMovie * movie)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QMovie *movie = new QMovie(\":/Mario.gif\");" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "movie->start();" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QLabel *label = new QLabel;\nlabel->setMovie(movie);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QStringtext() const" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "voidsetText(const QString &)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "voidsetEchoMode(EchoMode mode)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void setTextMargins(int left, int top, int right, int bottom)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "// smallwidget.h\nclass SmallWidget : public QWidget\n{\nQ_OBJECT\npublic:\nexplicit SmallWidget(QWidget *parent = 0);\nsignals:\npublic slots:\nprivate:\nQSpinBox* spin;\nQSlider* slider;\n};\n// smallwidget.cpp\nSmallWidget::SmallWidget(QWidget *parent) : QWidget(parent)\n{\nspin = new QSpinBox(this);\nslider = new QSlider(Qt::Horizontal, this);\n// 创建布局对象\nQHBoxLayout* layout = new QHBoxLayout;\n// 将控件添加到布局中\nlayout->addWidget(spin);\nlayout->addWidget(slider);\n// 将布局设置到窗口中\nsetLayout(layout);\n// 添加消息响应\nconnect(spin,\nstatic_cast(&QSpinBox::valueChanged),\nslider, &QSlider::setValue);\nconnect(slider, &QSlider::valueChanged,\nspin, &QSpinBox::setValue);\n}" + } + ], + "58954546": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class EventLabel : public QLabel\n{\nprotected:\n void mouseMoveEvent(QMouseEvent *event);\n void mousePressEvent(QMouseEvent *event);\n void mouseReleaseEvent(QMouseEvent *event);\n};\n \nvoid EventLabel::mouseMoveEvent(QMouseEvent *event)\n{\nthis->setText(QString(\"

Move: (%1, %2)\n

\").arg(QString::number(event->x()),\n QString::number(event->y())));\n}\n \nvoid EventLabel::mousePressEvent(QMouseEvent *event)\n{\n this->setText(QString(\"

Press:(%1, %2)\n

\").arg(QString::number(event->x()),\n QString::number(event->y())));\n}\n \nvoid EventLabel::mouseReleaseEvent(QMouseEvent *event)\n{\n QString msg;\n msg.sprintf(\"

Release: (%d, %d)

\",\n event->x(), event->y());\n this->setText(msg);\n}\n \nint main(int argc, char *argv[])\n{\n QApplication a(argc, argv);\n \n EventLabel *label = new EventLabel;\n label->setWindowTitle(\"MouseEvent Demo\");\n label->resize(300, 200);\n label->show();\n \n return a.exec();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "label->setMouseTracking(true);" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "bool CustomWidget::event(QEvent *e)\n{\nif (e->type() == QEvent::KeyPress) {\nQKeyEvent *keyEvent = static_cast(e);\nif (keyEvent->key() == Qt::Key_Tab) {\nqDebug() << \"You press tab.\";\nreturn true;\n}\n}\nreturn QWidget::event(e);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "bool CustomTextEdit::event(QEvent *e)\n{\nif (e->type() == QEvent::KeyPress)\n{\nQKeyEvent *keyEvent = static_cast(e);\nif (keyEvent->key() == Qt::Key_Tab)\n{\nqDebug() << \"You press tab.\";\nreturn true;\n}\n}\nreturn false;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "switch (event->type()) {\ncase QEvent::MouseMove:\nmouseMoveEvent((QMouseEvent*)event);\nbreak;\n// ...\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "//!!! Qt5\nbool QObject::event(QEvent *e)\n{\nswitch (e->type()) {\ncase QEvent::Timer:\ntimerEvent((QTimerEvent*)e);\nbreak;\ncase QEvent::ChildAdded:\ncase QEvent::ChildPolished:\ncase QEvent::ChildRemoved:\nchildEvent((QChildEvent*)e);\nbreak;\n// ...\ndefault:\nif (e->type() >= QEvent::User) {\ncustomEvent(e);\nbreak;\n}\nreturn false;\n}\nreturn true;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class MainWindow : public QMainWindow\n{\npublic:\nMainWindow();\nprotected:\nbool eventFilter(QObject *obj, QEvent *event);\nprivate:\nQTextEdit *textEdit;\n};\nMainWindow::MainWindow()\n{\ntextEdit = new QTextEdit;\nsetCentralWidget(textEdit);\ntextEdit->installEventFilter(this);\n}\nbool MainWindow::eventFilter(QObject *obj, QEvent *event)\n{\nif (obj == textEdit) {\nif (event->type() == QEvent::KeyPress) {\nQKeyEvent *keyEvent = static_cast(event);\nqDebug() << \"Ate key press\" << keyEvent->key();\nreturn true;\n} else {\nreturn false;\n}\n} else {\n// pass the event on to the parent class\nreturn QMainWindow::eventFilter(obj, event);\n}\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "bool CustomWidget::event(QEvent *e)\n{\nif (e->type() == QEvent::KeyPress) {\nQKeyEvent *keyEvent = static_cast(e);\nif (keyEvent->key() == Qt::Key_Tab) {\nqDebug() << \"You press tab.\";\nreturn true;\n}\n}\nreturn QWidget::event(e);\n}\n现在,我们可以给出一个使用事件过滤器的版本:\nbool FilterObject::eventFilter(QObject *object, QEvent *event)\n{\nif (object == target && event->type() == QEvent::KeyPress)\n{\nQKeyEvent *keyEvent = static_cast(event);\nif (keyEvent->key() == Qt::Key_Tab) {\nqDebug() << \"You press tab.\";\nreturn true;\n} else {\nreturn false;\n}\n}\nreturn false;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "LRESULT CALLBACK WndProc(HWND hWnd,\nUINT message,\nWPARAM wParam,\nLPARAM lParam)" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "switch(message)\n{\ncase WM_PAINT:\n// ...\nbreak;\ncase WM_DESTROY:\n// ...\nbreak;\n...\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "virtual bool QCoreApplication::notify ( QObject * receiver,\nQEvent * event );" + } + ], + "58954548": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "class PaintedWidget : public QWidget\n{\nQ_OBJECT\npublic:\nPaintedWidget(QWidget *parent = 0);\nprotected:\nvoid paintEvent(QPaintEvent *);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "PaintedWidget::PaintedWidget(QWidget *parent) :\nQWidget(parent)\n{\nresize(800, 600);\nsetWindowTitle(tr(\"Paint Demo\"));\n}\nvoid PaintedWidget::paintEvent(QPaintEvent *)\n{\nQPainter painter(this);\npainter.drawLine(80, 100, 650, 500);\npainter.setPen(Qt::red);\npainter.drawRect(10, 10, 100, 400);\npainter.setPen(QPen(Qt::green, 5));\npainter.setBrush(Qt::blue);\npainter.drawEllipse(50, 150, 400, 200);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void PaintWidget::paintEvent(QPaintEvent *)\n{\nQPixmap pixmap(\":/Image/butterfly.png\");\nQPixmap pixmap1(\":/Image/butterfly1.png\");\nQBitmap bitmap(\":/Image/butterfly.png\");\nQBitmap bitmap1(\":/Image/butterfly1.png\");\nQPainter painter(this);\npainter.drawPixmap(0, 0, pixmap);\npainter.drawPixmap(200, 0, pixmap1);\npainter.drawPixmap(0, 130, bitmap);\npainter.drawPixmap(200, 130, bitmap1);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void PaintWidget::paintEvent(QPaintEvent *)\n{\nQPainter painter(this);\nQImage image(300, 300, QImage::Format_RGB32);\nQRgb value;\n//将图片背景填充为白色\nimage.fill(Qt::white);\n//改变指定区域的像素点的值\nfor(int i=50; i<100; ++i)\n{\nfor(int j=50; j<100; ++j)\n{\nvalue = qRgb(255, 0, 0); // 红色\nimage.setPixel(i, j, value);\n}\n}\n//将图片绘制到窗口中\npainter.drawImage(QPoint(0, 0), image);\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "void PaintWidget::paintEvent(QPaintEvent *)\n{\nQPicture pic;\nQPainter painter;\n//将图像绘制到QPicture中,并保存到文件\npainter.begin(&pic);\npainter.drawEllipse(20, 20, 100, 50);\npainter.fillRect(20, 100, 100, 100, Qt::red);\npainter.end();\npic.save(\"D:\ndrawing.pic\");\n//将保存的绘图动作重新绘制到设备上\npic.load(\"D:\ndrawing.pic\");\npainter.begin(this);\npainter.drawPicture(200, 200, pic);\npainter.end();\n}" + } + ], + "58954552": [ + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "int main(int argc, char *argv[])\n{\n QApplication app(argc, argv);\n \n QFile file(\"in.txt\");\n if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n qDebug() << \"Open file failed.\";\n return -1;\n } else {\n while (!file.atEnd()) {\n qDebug() << file.readLine();\n }\n }\n \n QFileInfo info(file);\n qDebug() << info.isDir();\n qDebug() << info.isExecutable();\n qDebug() << info.baseName();\n qDebug() << info.completeBaseName();\n qDebug() << info.suffix();\n qDebug() << info.completeSuffix();\n \n return app.exec();\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QFileInfo fi(\"/tmp/archive.tar.gz\");\nQString base = fi.baseName(); // base = \"archive\"\nQString base = fi.completeBaseName(); // base = \"archive.tar\"\nQString ext = fi.suffix(); // ext = \"gz\"\nQString ext = fi.completeSuffix(); // ext = \"tar.gz\"" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QFile file(\"file.dat\");\nfile.open(QIODevice::WriteOnly);\nQDataStream out(&file);\nout << QString(\"the answer is\");\nout << (qint32)42;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QFile file(\"file.dat\");\nfile.open(QIODevice::ReadOnly);\nQDataStream in(&file);\nQString str;\nqint32 a;\nin >> str >> a;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QFile file(\"file.dat\");\nfile.open(QIODevice::ReadWrite);\nQDataStream stream(&file);\nQString str = \"the answer is 42\";\nstream << str;" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QFile data(\"file.txt\");\nif (data.open(QFile::WriteOnly | QIODevice::Truncate))\n{\nQTextStream out(&data);\nout << \"The answer is \" << 42;\n}" + }, + { + "lang": "cpp", + "params": { + "language": "cpp" + }, + "code": "QFile data(\"file.txt\");\nif (data.open(QFile::ReadOnly))\n{\nQTextStream in(&data);\nQString str;\nint ans = 0;\nin >> str >> ans;\n}" + } + ] +} \ No newline at end of file diff --git a/tools/wiki_data/qt_full_tree.json b/tools/wiki_data/qt_full_tree.json new file mode 100644 index 0000000..473ab05 --- /dev/null +++ b/tools/wiki_data/qt_full_tree.json @@ -0,0 +1,185 @@ +{ + "id": "58954499", + "title": "02.Qt方向", + "children": [ + { + "id": "58954436", + "title": "01.C++技术强化及提升", + "children": [ + { + "id": "58954437", + "title": "01.C++概述", + "children": [] + }, + { + "id": "58954438", + "title": "02.C++初识", + "children": [] + }, + { + "id": "58954439", + "title": "03.C++对C的扩展", + "children": [] + }, + { + "id": "58954440", + "title": "04.类和对象", + "children": [ + { + "id": "58954448", + "title": "01.类和对象的基本概念", + "children": [] + }, + { + "id": "58954449", + "title": "02.面向对象程序设计案例", + "children": [] + }, + { + "id": "58954451", + "title": "03.对象的构造和析构", + "children": [] + }, + { + "id": "58954456", + "title": "04.C++面向对象模型初探", + "children": [] + }, + { + "id": "58954457", + "title": "05.友元", + "children": [] + }, + { + "id": "58954458", + "title": "06.运算符重载", + "children": [] + }, + { + "id": "58954461", + "title": "07.继承和派生", + "children": [] + }, + { + "id": "58954469", + "title": "08.多态", + "children": [] + } + ] + }, + { + "id": "58954472", + "title": "05.C++模板", + "children": [] + }, + { + "id": "58954473", + "title": "06.C++类型转换", + "children": [] + }, + { + "id": "58954474", + "title": "07.C++异常", + "children": [] + }, + { + "id": "58954476", + "title": "08.C++输入输出流", + "children": [] + }, + { + "id": "58954485", + "title": "09.STL", + "children": [ + { + "id": "58954486", + "title": "01.STL概论", + "children": [] + }, + { + "id": "58954487", + "title": "02.STL三大组件", + "children": [] + }, + { + "id": "58954488", + "title": "03.常用容器", + "children": [] + }, + { + "id": "58954498", + "title": "04.常用算法", + "children": [] + } + ] + } + ] + }, + { + "id": "58954500", + "title": "02.QT安装教程(免注册和登陆)", + "children": [] + }, + { + "id": "58954512", + "title": "03.Qt桌面应用开发", + "children": [ + { + "id": "58954513", + "title": "1 Qt概述", + "children": [] + }, + { + "id": "58954515", + "title": "2 创建Qt项目", + "children": [] + }, + { + "id": "58954525", + "title": "3 第一个Qt小程序", + "children": [] + }, + { + "id": "58954527", + "title": "4 信号和槽机制", + "children": [] + }, + { + "id": "58954529", + "title": "5 QMainWindow", + "children": [] + }, + { + "id": "58954535", + "title": "6 对话框QDialog", + "children": [] + }, + { + "id": "58954536", + "title": "7 布局管理器", + "children": [] + }, + { + "id": "58954540", + "title": "8 常用控件", + "children": [] + }, + { + "id": "58954546", + "title": "9 Qt消息机制和事件", + "children": [] + }, + { + "id": "58954548", + "title": "10 绘图和绘图设备", + "children": [] + }, + { + "id": "58954552", + "title": "11 文件系统", + "children": [] + } + ] + } + ] +} \ No newline at end of file