├── .github ├── ISSUE_TEMPLATE │ ├── bug-report---问题反馈.md │ └── feature-request---功能请求.md └── workflows │ └── mdbook.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── LICENSE-BOOK ├── LICENSE-CODE ├── README.md ├── book ├── .gitignore ├── book.toml └── src │ ├── 0_array.md │ ├── 1_linkedlist.md │ ├── Instroduction.md │ ├── Resources.md │ ├── SUMMARY.md │ ├── chapter_01_array.md │ ├── chapter_02_vector.md │ ├── chapter_04_embedded_slist.md │ ├── chapter_05_slist.md │ ├── chapter_06_embedded_dlist.md │ ├── dslings.md │ └── other │ ├── 0_ds_base.md │ ├── 1_cpp_base.md │ ├── 1_cpp_base.template.md │ ├── 2_cpp_base.rangefor.md │ └── 3_cpp_base.bigfive.md ├── config.xlings └── dslings ├── common ├── common.hpp ├── dslings_config.hpp └── honly_logger.hpp ├── exercises ├── array │ ├── Array.hpp │ └── Vector.hpp ├── dslings.hpp ├── linked-list │ ├── EmbeddedList.hpp │ └── SLinkedList.hpp └── other │ └── cpp-base │ ├── RangeFor.hpp │ └── Template.hpp ├── tests ├── array │ ├── array.0.cpp │ ├── array.1.cpp │ ├── array.2.cpp │ ├── array.3.cpp │ ├── array.4.cpp │ ├── array.5.cpp │ └── array.6.cpp ├── dslings.0.cpp ├── dslings.1.cpp ├── dslings.2.cpp ├── embedded-list │ ├── embedded-dlist.0.cpp │ ├── embedded-dlist.1.cpp │ ├── embedded-dlist.2.cpp │ ├── embedded-dlist.3.cpp │ ├── embedded-dlist.4.cpp │ ├── embedded-slist.0.cpp │ ├── embedded-slist.1.cpp │ ├── embedded-slist.2.cpp │ ├── embedded-slist.3.cpp │ └── embedded-slist.4.cpp ├── other │ └── cpp-base │ │ ├── range_for.0.cpp │ │ ├── range_for.1.cpp │ │ ├── range_for.2.cpp │ │ ├── range_for.3.cpp │ │ ├── template.0.cpp │ │ ├── template.1.cpp │ │ └── template.2.cpp ├── slinked-list │ ├── slist.0.cpp │ ├── slist.1.cpp │ ├── slist.2.cpp │ ├── slist.3.cpp │ ├── slist.4.cpp │ ├── slist.5.cpp │ ├── slist.6.cpp │ ├── slist.7.cpp │ ├── slist.8.cpp │ ├── slist.it.0.cpp │ ├── slist.it.1.cpp │ ├── slist.it.2.cpp │ ├── slist.it.3.cpp │ ├── slist.it.4.cpp │ └── slist.it.5.cpp └── vector │ ├── vector.0.0.cpp │ ├── vector.0.1.cpp │ ├── vector.0.cpp │ ├── vector.1.0.cpp │ ├── vector.1.1.cpp │ ├── vector.1.2.cpp │ ├── vector.1.cpp │ ├── vector.2.cpp │ ├── vector.3.0.cpp │ ├── vector.3.cpp │ ├── vector.4.cpp │ └── vector.5.cpp └── xmake.lua /.github/ISSUE_TEMPLATE/bug-report---问题反馈.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report | 问题反馈 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: waiting | 待分配 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug | 问题描述 11 | 12 | ### Desktop | 环境 13 | 14 | ### To Reproduce | 复现步骤 15 | 16 | ### Expection | 预期结果 17 | 18 | ### Additional context | 额外补充 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request---功能请求.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request | 功能请求 3 | about: Suggest an idea for this project 4 | title: "[Feature]" 5 | labels: enhancement | 新功能 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Feature Describe | 功能描述 11 | 12 | ## Expectation Effect | 预期效果 13 | 14 | ## Possible‘s Solutions | 可能的实现方案 15 | 16 | ## Research Direction | 研究方向 17 | 18 | ## Related Links | 相关链接 19 | -------------------------------------------------------------------------------- /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a mdBook site to GitHub Pages 2 | # 3 | # To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html 4 | # 5 | name: Deploy mdBook site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["main"] 11 | paths: 12 | - 'book/src/**' 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 18 | permissions: 19 | contents: read 20 | pages: write 21 | id-token: write 22 | 23 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 24 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 25 | concurrency: 26 | group: "pages" 27 | cancel-in-progress: false 28 | 29 | jobs: 30 | # Build job 31 | build: 32 | runs-on: ubuntu-latest 33 | 34 | defaults: 35 | run: 36 | working-directory: book 37 | 38 | env: 39 | MDBOOK_VERSION: 0.4.36 40 | steps: 41 | - uses: actions/checkout@v4 42 | - name: Install mdBook 43 | run: | 44 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh 45 | rustup update 46 | cargo install --version ${MDBOOK_VERSION} mdbook 47 | - name: Setup Pages 48 | id: pages 49 | uses: actions/configure-pages@v4 50 | - name: Build with mdBook 51 | run: mdbook build 52 | - name: Upload artifact 53 | uses: actions/upload-pages-artifact@v3 54 | with: 55 | path: ./book/book 56 | 57 | # Deployment job 58 | deploy: 59 | environment: 60 | name: github-pages 61 | url: ${{ steps.deployment.outputs.page_url }} 62 | runs-on: ubuntu-latest 63 | needs: build 64 | steps: 65 | - name: Deploy to GitHub Pages 66 | id: deployment 67 | uses: actions/deploy-pages@v4 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .xmake 2 | build 3 | .vscode 4 | .xlings -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dstruct"] 2 | path = dslings/dstruct 3 | url = https://github.com/Sunrisepeak/DStruct 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "C_Cpp.default.includePath": [ 4 | // local include path 5 | "${default}", 6 | "${workspaceFolder}", 7 | "${workspaceFolder}/dstruct", 8 | "${workspaceFolder}/dslings", 9 | // Linux - gcc -v -E -x c++ - 10 | // - ubuntu22.04 - gcc11 - system include path 11 | "/usr/include/c++/11", 12 | "/usr/include/x86_64-linux-gnu/c++/11", 13 | "/usr/include/c++/11/backward", 14 | "/usr/lib/gcc/x86_64-linux-gnu/11/include", 15 | "/usr/local/include", 16 | "/usr/include/x86_64-linux-gnu", 17 | "/usr/include", 18 | ], 19 | 20 | "C_Cpp.files.exclude": { 21 | "**/.vscode": true, 22 | "**/build": true, 23 | "/.xmake": true 24 | }, 25 | 26 | "files.encoding": "utf8", 27 | "terminal.integrated.env.windows": { 28 | "LANG": "en_US.UTF-8" 29 | }, 30 | "terminal.integrated.env.linux": { 31 | "LANG": "en_US.UTF-8" 32 | }, 33 | "terminal.integrated.env.osx": { 34 | "LANG": "en_US.UTF-8" 35 | }, 36 | 37 | "files.associations": { 38 | "array": "cpp", 39 | "string": "cpp", 40 | "string_view": "cpp", 41 | "deque": "cpp", 42 | "list": "cpp", 43 | "*.tcc": "cpp", 44 | "hash_map": "cpp", 45 | "unordered_map": "cpp", 46 | "vector": "cpp", 47 | "functional": "cpp", 48 | "optional": "cpp", 49 | "ratio": "cpp", 50 | "system_error": "cpp", 51 | "tuple": "cpp", 52 | "type_traits": "cpp", 53 | "utility": "cpp", 54 | "istream": "cpp", 55 | "ostream": "cpp", 56 | "chrono": "cpp", 57 | "compare": "cpp", 58 | "cstddef": "cpp", 59 | "initializer_list": "cpp", 60 | "atomic": "cpp", 61 | "bit": "cpp", 62 | "cctype": "cpp", 63 | "clocale": "cpp", 64 | "cmath": "cpp", 65 | "concepts": "cpp", 66 | "cstdarg": "cpp", 67 | "cstdint": "cpp", 68 | "cstdio": "cpp", 69 | "cstdlib": "cpp", 70 | "cstring": "cpp", 71 | "ctime": "cpp", 72 | "cwchar": "cpp", 73 | "cwctype": "cpp", 74 | "map": "cpp", 75 | "exception": "cpp", 76 | "algorithm": "cpp", 77 | "iterator": "cpp", 78 | "memory": "cpp", 79 | "memory_resource": "cpp", 80 | "numeric": "cpp", 81 | "random": "cpp", 82 | "fstream": "cpp", 83 | "iosfwd": "cpp", 84 | "iostream": "cpp", 85 | "limits": "cpp", 86 | "new": "cpp", 87 | "numbers": "cpp", 88 | "semaphore": "cpp", 89 | "sstream": "cpp", 90 | "stdexcept": "cpp", 91 | "stop_token": "cpp", 92 | "streambuf": "cpp", 93 | "thread": "cpp", 94 | "cinttypes": "cpp", 95 | "typeinfo": "cpp", 96 | "cassert": "cpp" 97 | } 98 | } -------------------------------------------------------------------------------- /LICENSE-BOOK: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-present sunrisepeak, speakshen@163.com 2 | 3 | --- 4 | 5 | Attribution-NonCommercial-ShareAlike 4.0 International 6 | 7 | ======================================================================= 8 | 9 | Creative Commons Corporation ("Creative Commons") is not a law firm and 10 | does not provide legal services or legal advice. Distribution of 11 | Creative Commons public licenses does not create a lawyer-client or 12 | other relationship. Creative Commons makes its licenses and related 13 | information available on an "as-is" basis. Creative Commons gives no 14 | warranties regarding its licenses, any material licensed under their 15 | terms and conditions, or any related information. Creative Commons 16 | disclaims all liability for damages resulting from their use to the 17 | fullest extent possible. 18 | 19 | Using Creative Commons Public Licenses 20 | 21 | Creative Commons public licenses provide a standard set of terms and 22 | conditions that creators and other rights holders may use to share 23 | original works of authorship and other material subject to copyright 24 | and certain other rights specified in the public license below. The 25 | following considerations are for informational purposes only, are not 26 | exhaustive, and do not form part of our licenses. 27 | 28 | Considerations for licensors: Our public licenses are 29 | intended for use by those authorized to give the public 30 | permission to use material in ways otherwise restricted by 31 | copyright and certain other rights. Our licenses are 32 | irrevocable. Licensors should read and understand the terms 33 | and conditions of the license they choose before applying it. 34 | Licensors should also secure all rights necessary before 35 | applying our licenses so that the public can reuse the 36 | material as expected. Licensors should clearly mark any 37 | material not subject to the license. This includes other CC- 38 | licensed material, or material used under an exception or 39 | limitation to copyright. More considerations for licensors: 40 | wiki.creativecommons.org/Considerations_for_licensors 41 | 42 | Considerations for the public: By using one of our public 43 | licenses, a licensor grants the public permission to use the 44 | licensed material under specified terms and conditions. If 45 | the licensor's permission is not necessary for any reason--for 46 | example, because of any applicable exception or limitation to 47 | copyright--then that use is not regulated by the license. Our 48 | licenses grant only permissions under copyright and certain 49 | other rights that a licensor has authority to grant. Use of 50 | the licensed material may still be restricted for other 51 | reasons, including because others have copyright or other 52 | rights in the material. A licensor may make special requests, 53 | such as asking that all changes be marked or described. 54 | Although not required by our licenses, you are encouraged to 55 | respect those requests where reasonable. More considerations 56 | for the public: 57 | wiki.creativecommons.org/Considerations_for_licensees 58 | 59 | ======================================================================= 60 | 61 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 62 | Public License 63 | 64 | By exercising the Licensed Rights (defined below), You accept and agree 65 | to be bound by the terms and conditions of this Creative Commons 66 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 67 | ("Public License"). To the extent this Public License may be 68 | interpreted as a contract, You are granted the Licensed Rights in 69 | consideration of Your acceptance of these terms and conditions, and the 70 | Licensor grants You such rights in consideration of benefits the 71 | Licensor receives from making the Licensed Material available under 72 | these terms and conditions. 73 | 74 | 75 | Section 1 -- Definitions. 76 | 77 | a. Adapted Material means material subject to Copyright and Similar 78 | Rights that is derived from or based upon the Licensed Material 79 | and in which the Licensed Material is translated, altered, 80 | arranged, transformed, or otherwise modified in a manner requiring 81 | permission under the Copyright and Similar Rights held by the 82 | Licensor. For purposes of this Public License, where the Licensed 83 | Material is a musical work, performance, or sound recording, 84 | Adapted Material is always produced where the Licensed Material is 85 | synched in timed relation with a moving image. 86 | 87 | b. Adapter's License means the license You apply to Your Copyright 88 | and Similar Rights in Your contributions to Adapted Material in 89 | accordance with the terms and conditions of this Public License. 90 | 91 | c. BY-NC-SA Compatible License means a license listed at 92 | creativecommons.org/compatiblelicenses, approved by Creative 93 | Commons as essentially the equivalent of this Public License. 94 | 95 | d. Copyright and Similar Rights means copyright and/or similar rights 96 | closely related to copyright including, without limitation, 97 | performance, broadcast, sound recording, and Sui Generis Database 98 | Rights, without regard to how the rights are labeled or 99 | categorized. For purposes of this Public License, the rights 100 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 101 | Rights. 102 | 103 | e. Effective Technological Measures means those measures that, in the 104 | absence of proper authority, may not be circumvented under laws 105 | fulfilling obligations under Article 11 of the WIPO Copyright 106 | Treaty adopted on December 20, 1996, and/or similar international 107 | agreements. 108 | 109 | f. Exceptions and Limitations means fair use, fair dealing, and/or 110 | any other exception or limitation to Copyright and Similar Rights 111 | that applies to Your use of the Licensed Material. 112 | 113 | g. License Elements means the license attributes listed in the name 114 | of a Creative Commons Public License. The License Elements of this 115 | Public License are Attribution, NonCommercial, and ShareAlike. 116 | 117 | h. Licensed Material means the artistic or literary work, database, 118 | or other material to which the Licensor applied this Public 119 | License. 120 | 121 | i. Licensed Rights means the rights granted to You subject to the 122 | terms and conditions of this Public License, which are limited to 123 | all Copyright and Similar Rights that apply to Your use of the 124 | Licensed Material and that the Licensor has authority to license. 125 | 126 | j. Licensor means the individual(s) or entity(ies) granting rights 127 | under this Public License. 128 | 129 | k. NonCommercial means not primarily intended for or directed towards 130 | commercial advantage or monetary compensation. For purposes of 131 | this Public License, the exchange of the Licensed Material for 132 | other material subject to Copyright and Similar Rights by digital 133 | file-sharing or similar means is NonCommercial provided there is 134 | no payment of monetary compensation in connection with the 135 | exchange. 136 | 137 | l. Share means to provide material to the public by any means or 138 | process that requires permission under the Licensed Rights, such 139 | as reproduction, public display, public performance, distribution, 140 | dissemination, communication, or importation, and to make material 141 | available to the public including in ways that members of the 142 | public may access the material from a place and at a time 143 | individually chosen by them. 144 | 145 | m. Sui Generis Database Rights means rights other than copyright 146 | resulting from Directive 96/9/EC of the European Parliament and of 147 | the Council of 11 March 1996 on the legal protection of databases, 148 | as amended and/or succeeded, as well as other essentially 149 | equivalent rights anywhere in the world. 150 | 151 | n. You means the individual or entity exercising the Licensed Rights 152 | under this Public License. Your has a corresponding meaning. 153 | 154 | 155 | Section 2 -- Scope. 156 | 157 | a. License grant. 158 | 159 | 1. Subject to the terms and conditions of this Public License, 160 | the Licensor hereby grants You a worldwide, royalty-free, 161 | non-sublicensable, non-exclusive, irrevocable license to 162 | exercise the Licensed Rights in the Licensed Material to: 163 | 164 | a. reproduce and Share the Licensed Material, in whole or 165 | in part, for NonCommercial purposes only; and 166 | 167 | b. produce, reproduce, and Share Adapted Material for 168 | NonCommercial purposes only. 169 | 170 | 2. Exceptions and Limitations. For the avoidance of doubt, where 171 | Exceptions and Limitations apply to Your use, this Public 172 | License does not apply, and You do not need to comply with 173 | its terms and conditions. 174 | 175 | 3. Term. The term of this Public License is specified in Section 176 | 6(a). 177 | 178 | 4. Media and formats; technical modifications allowed. The 179 | Licensor authorizes You to exercise the Licensed Rights in 180 | all media and formats whether now known or hereafter created, 181 | and to make technical modifications necessary to do so. The 182 | Licensor waives and/or agrees not to assert any right or 183 | authority to forbid You from making technical modifications 184 | necessary to exercise the Licensed Rights, including 185 | technical modifications necessary to circumvent Effective 186 | Technological Measures. For purposes of this Public License, 187 | simply making modifications authorized by this Section 2(a) 188 | (4) never produces Adapted Material. 189 | 190 | 5. Downstream recipients. 191 | 192 | a. Offer from the Licensor -- Licensed Material. Every 193 | recipient of the Licensed Material automatically 194 | receives an offer from the Licensor to exercise the 195 | Licensed Rights under the terms and conditions of this 196 | Public License. 197 | 198 | b. Additional offer from the Licensor -- Adapted Material. 199 | Every recipient of Adapted Material from You 200 | automatically receives an offer from the Licensor to 201 | exercise the Licensed Rights in the Adapted Material 202 | under the conditions of the Adapter's License You apply. 203 | 204 | c. No downstream restrictions. You may not offer or impose 205 | any additional or different terms or conditions on, or 206 | apply any Effective Technological Measures to, the 207 | Licensed Material if doing so restricts exercise of the 208 | Licensed Rights by any recipient of the Licensed 209 | Material. 210 | 211 | 6. No endorsement. Nothing in this Public License constitutes or 212 | may be construed as permission to assert or imply that You 213 | are, or that Your use of the Licensed Material is, connected 214 | with, or sponsored, endorsed, or granted official status by, 215 | the Licensor or others designated to receive attribution as 216 | provided in Section 3(a)(1)(A)(i). 217 | 218 | b. Other rights. 219 | 220 | 1. Moral rights, such as the right of integrity, are not 221 | licensed under this Public License, nor are publicity, 222 | privacy, and/or other similar personality rights; however, to 223 | the extent possible, the Licensor waives and/or agrees not to 224 | assert any such rights held by the Licensor to the limited 225 | extent necessary to allow You to exercise the Licensed 226 | Rights, but not otherwise. 227 | 228 | 2. Patent and trademark rights are not licensed under this 229 | Public License. 230 | 231 | 3. To the extent possible, the Licensor waives any right to 232 | collect royalties from You for the exercise of the Licensed 233 | Rights, whether directly or through a collecting society 234 | under any voluntary or waivable statutory or compulsory 235 | licensing scheme. In all other cases the Licensor expressly 236 | reserves any right to collect such royalties, including when 237 | the Licensed Material is used other than for NonCommercial 238 | purposes. 239 | 240 | 241 | Section 3 -- License Conditions. 242 | 243 | Your exercise of the Licensed Rights is expressly made subject to the 244 | following conditions. 245 | 246 | a. Attribution. 247 | 248 | 1. If You Share the Licensed Material (including in modified 249 | form), You must: 250 | 251 | a. retain the following if it is supplied by the Licensor 252 | with the Licensed Material: 253 | 254 | i. identification of the creator(s) of the Licensed 255 | Material and any others designated to receive 256 | attribution, in any reasonable manner requested by 257 | the Licensor (including by pseudonym if 258 | designated); 259 | 260 | ii. a copyright notice; 261 | 262 | iii. a notice that refers to this Public License; 263 | 264 | iv. a notice that refers to the disclaimer of 265 | warranties; 266 | 267 | v. a URI or hyperlink to the Licensed Material to the 268 | extent reasonably practicable; 269 | 270 | b. indicate if You modified the Licensed Material and 271 | retain an indication of any previous modifications; and 272 | 273 | c. indicate the Licensed Material is licensed under this 274 | Public License, and include the text of, or the URI or 275 | hyperlink to, this Public License. 276 | 277 | 2. You may satisfy the conditions in Section 3(a)(1) in any 278 | reasonable manner based on the medium, means, and context in 279 | which You Share the Licensed Material. For example, it may be 280 | reasonable to satisfy the conditions by providing a URI or 281 | hyperlink to a resource that includes the required 282 | information. 283 | 3. If requested by the Licensor, You must remove any of the 284 | information required by Section 3(a)(1)(A) to the extent 285 | reasonably practicable. 286 | 287 | b. ShareAlike. 288 | 289 | In addition to the conditions in Section 3(a), if You Share 290 | Adapted Material You produce, the following conditions also apply. 291 | 292 | 1. The Adapter's License You apply must be a Creative Commons 293 | license with the same License Elements, this version or 294 | later, or a BY-NC-SA Compatible License. 295 | 296 | 2. You must include the text of, or the URI or hyperlink to, the 297 | Adapter's License You apply. You may satisfy this condition 298 | in any reasonable manner based on the medium, means, and 299 | context in which You Share Adapted Material. 300 | 301 | 3. You may not offer or impose any additional or different terms 302 | or conditions on, or apply any Effective Technological 303 | Measures to, Adapted Material that restrict exercise of the 304 | rights granted under the Adapter's License You apply. 305 | 306 | 307 | Section 4 -- Sui Generis Database Rights. 308 | 309 | Where the Licensed Rights include Sui Generis Database Rights that 310 | apply to Your use of the Licensed Material: 311 | 312 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 313 | to extract, reuse, reproduce, and Share all or a substantial 314 | portion of the contents of the database for NonCommercial purposes 315 | only; 316 | 317 | b. if You include all or a substantial portion of the database 318 | contents in a database in which You have Sui Generis Database 319 | Rights, then the database in which You have Sui Generis Database 320 | Rights (but not its individual contents) is Adapted Material, 321 | including for purposes of Section 3(b); and 322 | 323 | c. You must comply with the conditions in Section 3(a) if You Share 324 | all or a substantial portion of the contents of the database. 325 | 326 | For the avoidance of doubt, this Section 4 supplements and does not 327 | replace Your obligations under this Public License where the Licensed 328 | Rights include other Copyright and Similar Rights. 329 | 330 | 331 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 332 | 333 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 334 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 335 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 336 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 337 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 338 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 339 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 340 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 341 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 342 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 343 | 344 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 345 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 346 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 347 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 348 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 349 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 350 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 351 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 352 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 353 | 354 | c. The disclaimer of warranties and limitation of liability provided 355 | above shall be interpreted in a manner that, to the extent 356 | possible, most closely approximates an absolute disclaimer and 357 | waiver of all liability. 358 | 359 | 360 | Section 6 -- Term and Termination. 361 | 362 | a. This Public License applies for the term of the Copyright and 363 | Similar Rights licensed here. However, if You fail to comply with 364 | this Public License, then Your rights under this Public License 365 | terminate automatically. 366 | 367 | b. Where Your right to use the Licensed Material has terminated under 368 | Section 6(a), it reinstates: 369 | 370 | 1. automatically as of the date the violation is cured, provided 371 | it is cured within 30 days of Your discovery of the 372 | violation; or 373 | 374 | 2. upon express reinstatement by the Licensor. 375 | 376 | For the avoidance of doubt, this Section 6(b) does not affect any 377 | right the Licensor may have to seek remedies for Your violations 378 | of this Public License. 379 | 380 | c. For the avoidance of doubt, the Licensor may also offer the 381 | Licensed Material under separate terms or conditions or stop 382 | distributing the Licensed Material at any time; however, doing so 383 | will not terminate this Public License. 384 | 385 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 386 | License. 387 | 388 | 389 | Section 7 -- Other Terms and Conditions. 390 | 391 | a. The Licensor shall not be bound by any additional or different 392 | terms or conditions communicated by You unless expressly agreed. 393 | 394 | b. Any arrangements, understandings, or agreements regarding the 395 | Licensed Material not stated herein are separate from and 396 | independent of the terms and conditions of this Public License. 397 | 398 | 399 | Section 8 -- Interpretation. 400 | 401 | a. For the avoidance of doubt, this Public License does not, and 402 | shall not be interpreted to, reduce, limit, restrict, or impose 403 | conditions on any use of the Licensed Material that could lawfully 404 | be made without permission under this Public License. 405 | 406 | b. To the extent possible, if any provision of this Public License is 407 | deemed unenforceable, it shall be automatically reformed to the 408 | minimum extent necessary to make it enforceable. If the provision 409 | cannot be reformed, it shall be severed from this Public License 410 | without affecting the enforceability of the remaining terms and 411 | conditions. 412 | 413 | c. No term or condition of this Public License will be waived and no 414 | failure to comply consented to unless expressly agreed to by the 415 | Licensor. 416 | 417 | d. Nothing in this Public License constitutes or may be interpreted 418 | as a limitation upon, or waiver of, any privileges and immunities 419 | that apply to the Licensor or You, including from the legal 420 | processes of any jurisdiction or authority. 421 | 422 | ======================================================================= 423 | 424 | Creative Commons is not a party to its public 425 | licenses. Notwithstanding, Creative Commons may elect to apply one of 426 | its public licenses to material it publishes and in those instances 427 | will be considered the “Licensor.” The text of the Creative Commons 428 | public licenses is dedicated to the public domain under the CC0 Public 429 | Domain Dedication. Except for the limited purpose of indicating that 430 | material is shared under a Creative Commons public license or as 431 | otherwise permitted by the Creative Commons policies published at 432 | creativecommons.org/policies, Creative Commons does not authorize the 433 | use of the trademark "Creative Commons" or any other trademark or logo 434 | of Creative Commons without its prior written consent including, 435 | without limitation, in connection with any unauthorized modifications 436 | to any of its public licenses or any other arrangements, 437 | understandings, or agreements concerning use of licensed material. For 438 | the avoidance of doubt, this paragraph does not form part of the 439 | public licenses. 440 | 441 | Creative Commons may be contacted at creativecommons.org. -------------------------------------------------------------------------------- /LICENSE-CODE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-present sunrisepeak, speakshen@163.com 2 | 3 | --- 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright [yyyy] [name of copyright owner] 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目已转移到[D2Learn/d2ds](https://github.com/d2learn/d2ds) 2 | 3 | # 动手写数据结构 | d2ds 4 | 5 | Dive into Data Structures - 强调**动手实践**的**数据结构**学习项目,其中包含**在线书籍、公开课、练习代码**等子项目 6 | 7 | | [项目主页](https://sunrisepeak.github.io/d2ds-courses) - [开发看板](https://github.com/users/Sunrisepeak/projects/10) | 8 | | ------------------------------------------------------------ | 9 | | [d2ds-book](https://sunrisepeak.github.io/d2ds) - [d2ds-courses](https://sunrisepeak.github.io/d2ds-courses) - [d2ds-dslings](dslings) - [dstruct](https://github.com/Sunrisepeak/dstruct) | 10 | 11 | **项目已经使用xlings重构 - 文档待更新** 12 | 13 | --- 14 | 15 | ## 目标 16 | 17 | - **1.** 任何人都可以在网上**免费获取** 18 | - **2.** 不仅强调原理, 更**强调动手实践** 19 | - **3.** **自动化的代码练习系统**, 让使用者轻松**从零实现一个数据结构** 20 | - **4.** **强调共建共享**, 让更多的人加入d2ds社区, 快速迭代和提升内容 21 | - **5.** 提供社区成员用于**代码分享、技术交流、的论坛和群组** 22 | 23 | 24 | ## book & courses | 阅读 25 | 26 | | d2ds | 材料 | 视频 | 参考 | 备注 | 27 | | --- | --- | --- | --- | --- | 28 | | Array | [chapter1](https://sunrisepeak.github.io/d2ds/chapter_01_array.html) | [video1](https://www.bilibili.com/video/BV1hD421T7sU) - [video2](https://www.bilibili.com/video/BV16t421w7c2) | [dstruct::Array](https://github.com/Sunrisepeak/dstruct/blob/main/core/ds/array/Array.hpp) | [备用地址](https://zhuanlan.zhihu.com/p/693936490) | 29 | | Vector | [chapter2](https://sunrisepeak.github.io/d2ds/chapter_01_array.html) | [video1](https://www.bilibili.com/video/BV1K1421z7kt) - [video2](https://www.bilibili.com/video/BV1yb421B7ZG) | [dstruct::Vector](https://github.com/Sunrisepeak/dstruct/blob/main/core/ds/array/Vector.hpp) | [备用地址](https://zhuanlan.zhihu.com/p/696455403) | 30 | | SinglyLink | [chapter3](https://sunrisepeak.github.io/d2ds/chapter_04_embeddedlist.html) | [video1](https://www.bilibili.com/video/BV1ND421V7Wn) - [video2](https://www.bilibili.com/video/BV1ir421w71C) | [dstruct::SinglyLink](https://github.com/Sunrisepeak/dstruct/blob/main/core/ds/linked-list/EmbeddedList.hpp#L15) | [备用地址](https://zhuanlan.zhihu.com/p/699299313) | 31 | | SLinkedList | [chapter5](https://sunrisepeak.github.io/d2ds) | [video1](https://www.bilibili.com/video/BV1uf421Q7jG) - [video2](https://www.bilibili.com/video/BV1H1421r7QD) | [dstruct::SList](https://github.com/Sunrisepeak/dstruct/blob/main/core/ds/linked-list/SinglyLinkedList.hpp) | [备用地址](https://zhuanlan.zhihu.com/p/706196372) | 32 | | | | [video3](https://www.bilibili.com/video/BV1zW421R75C) - [video4](https://www.bilibili.com/video/BV1WM4m1m7wj) | | | 33 | | X | [chapterX]() | [video1]() - [video2]() | [dstruct::X]() | [备用地址]() | 34 | 35 | ## d2ds-dslings | 练习 36 | 37 | 通过使用[xlings](https://github.com/d2learn/xlings)下载项目并运行自动化检测的**编译器驱动开发模式**来进行代码练习 38 | 39 | > 注: [xlings安装文档](https://github.com/d2learn/xlings) 40 | 41 | ### 获取练习代码 42 | 43 | ```bash 44 | xlings drepo d2ds 45 | ``` 46 | 47 | ### 执行dslings进入练习 48 | 49 | > 进入d2ds目录, 执行dslings开始练习 50 | 51 | ```bash 52 | cd d2ds 53 | xlings dslings 54 | ``` 55 | 56 | ### 打开电子书 57 | 58 | ```bash 59 | xlings book 60 | ``` 61 | 62 | ## 社区 | 交流 63 | 64 | ### 知识库 65 | 66 | > [知识库汇总页](https://github.com/Sunrisepeak/d2ds/issues/32) 67 | 68 | - 一个d2ds相关技术点解析的知识库 69 | - 统一的汇总页, 便于查找 70 | - 降低d2ds的上手难度, 和帮助使用者理解 71 | - 技术/环境搭建及项目常见问题汇总 72 | - ShowYourCode-2024 73 | 74 | ### 交流渠道 75 | 76 | - **即时交流(QQ群):** 167535744 77 | - **论坛:** [github-discussions](https://github.com/Sunrisepeak/d2ds-courses/discussions) 78 | - **问题交流&反馈:** [github-issues](https://github.com/Sunrisepeak/d2ds-courses/issues) 79 | 80 | ## 参与项目 | 社区贡献 81 | 82 | - **参与社区交流:** 反馈问题、参与社区问题讨论 83 | - **参与项目开发:** 通过[开发看板](https://github.com/users/Sunrisepeak/projects/10), 参与社区中问题处理、修复Bug、开发&优化新功能/模块 84 | - **参与[知识库](https://github.com/Sunrisepeak/d2ds/issues/32)的建设:** 对d2ds中的一些内容做技术解读, 丰富知识库, 以至于帮助到更多同学 85 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Sunrisepeak"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "动手写数据结构(d2ds)" 7 | 8 | [output.html] 9 | git-repository-url = "https://github.com/Sunrisepeak/d2ds" -------------------------------------------------------------------------------- /book/src/0_array.md: -------------------------------------------------------------------------------- 1 | # 数组 2 | 3 | 数组在编程当中是最常用到的数据结构, 本章将会探讨关于定长数组Array和动态数组Vector核心部分的实现细节 4 | 5 | - [定长数组Array](chapter_01_array.md) 6 | - [动态数组Vector]() -------------------------------------------------------------------------------- /book/src/1_linkedlist.md: -------------------------------------------------------------------------------- 1 | # 链表 2 | 3 | - [嵌入式单链表](chapter_04_embedded_slist.md) 4 | - [单链表](chapter_05_slist.md) 5 | - [嵌入式双链表](chapter_06_embedded_dlist.md) -------------------------------------------------------------------------------- /book/src/Instroduction.md: -------------------------------------------------------------------------------- 1 | # 导读 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 章节结构 9 | - 数据结构接口使用 | 代码演示 10 | - dslings - 测试代码 11 | - dslings - 检测结果 12 | - 代码接口介绍 13 | - 数据结构接口实现 | 类型定义 14 | - 数据结构接口实现 | 接口实现 15 | - 数据结构接口实现 | 完整代码 16 | - 代码练习dslings 17 | - 代码下载 18 | - 安装xmake 19 | - dslings自动检测 20 | - 错误提示 21 | - 代码通过提示 22 | - XLINGS_WAIT - dslings等待标志 23 | - 总结 24 | 25 | --- 26 | 27 | 动手写数据结构(d2ds)是一本强调动手实践的开源电子书, 每一章节都会介绍一个数据结构的基本用法和对应的具体实现。本书使用C++作为数据结构的开发语言, 并使用"编译器驱动开发模式"面向接口编程的形式, 来介绍常见数据结构的主体功能和实现。同时, 在[d2ds仓库](https://github.com/Sunrisepeak/d2ds)中也为每章节配有对应的练习代码和dslings检测程序, 真正让读者感受到"动手写"的感觉。下面我们就来详细的介绍一下 章节结构 和 dslings的使用。 28 | 29 | > 注: 练习代码采用了类似rustlings的代码检测风格 30 | 31 | 32 | ## 章节结构 33 | 34 | 核心分两大部分, **数据结构接口使用 + 数据结构接口实现**。如下: 35 | 36 | ### 数据结构接口使用 37 | 38 | **dslings - MaxValue代码示例** 39 | 40 | ```cpp 41 | // dslings.2.cpp - readonly 42 | // 43 | // 描述: 44 | // 通过实现一个MaxVal类型(保存最大值), 来介绍dslings的"编译器驱动开发" 45 | // 即根据编译器的错误提示来完成这个训练流程的演示Demo, 并且通常为了降低难度会把一个'数据结构'的实现分成多个检测模块. 46 | // 如: dslings.0.cpp dslings.1.cpp dslings.2.cpp 47 | // 48 | // 目标/要求: 49 | // - 不修改该代码检测文件 50 | // - 在exercises/dslings.hpp中完成你的代码设计 51 | // - 通过所有编译器检测 和 断言 52 | // 53 | 54 | #include 55 | 56 | #include "common/common.hpp" 57 | #include "exercises/dslings.hpp" 58 | 59 | int main() { 60 | 61 | d2ds::MaxValue mVal(2); 62 | 63 | d2ds_assert_eq(mVal.get(), 2); 64 | 65 | mVal.set(-1); 66 | d2ds_assert_eq(mVal.get(), 2); 67 | 68 | mVal.set(100); 69 | d2ds_assert_eq(mVal.get(), 100); 70 | 71 | // random test 72 | dstruct::Array data; 73 | d2ds::random_data_generator(data, 0, 200); 74 | d2ds::ds_print(data); 75 | 76 | int maxVal = 0; 77 | for (int i = 0; i < data.size(); i++) { 78 | mVal.set(data[i]); 79 | if (data[i] > maxVal) { 80 | maxVal = data[i]; 81 | } 82 | } 83 | 84 | d2ds_assert_eq(mVal.get(), maxVal); 85 | 86 | XLINGS_WAIT 87 | 88 | return 0; 89 | } 90 | ``` 91 | 92 | **dslings - 检测结果** 93 | 94 | ```bash 95 | 🌏Progress: [==>----------] 2/12 96 | 97 | [Target: 0.dslings-2] 98 | 99 | ✅ Successfully ran tests/dslings.2.cpp! 100 | 101 | 🎉 The code is compiling! 🎉 102 | 103 | Output: 104 | ==================== 105 | [D2DS LOGI]: - ✅ | mVal.get() == 2 (2 == 2) 106 | [D2DS LOGI]: - ✅ | mVal.get() == 2 (2 == 2) 107 | [D2DS LOGI]: - ✅ | mVal.get() == 100 (100 == 100) 108 | [D2DS LOGI]: - ✅ | mVal.get() == maxVal (191 == 191) 109 | [D2DS LOGW]: main: tests/dslings.2.cpp:46 - Delete the XLINGS_WAIT to continue... 110 | 111 | ==================== 112 | 113 | Book: https://sunrisepeak.github.io/d2ds 114 | ``` 115 | 116 | **代码接口介绍** 117 | 118 | MaxValue一个数据最大值检查器 119 | 120 | MaxValue构造函数设置默认值 121 | 122 | ```cpp 123 | d2ds::MaxValue mVal(2); 124 | ``` 125 | 126 | get函数获取当前最大值 127 | 128 | ```cpp 129 | d2ds_assert_eq(mVal.get(), 2); 130 | ``` 131 | 132 | set函数设置一个值 133 | 134 | > 如果当前最大值小于这个值则需要进行替换 135 | 136 | ```cpp 137 | mVal.set(-1); 138 | d2ds_assert_eq(mVal.get(), 2); 139 | 140 | mVal.set(100); 141 | d2ds_assert_eq(mVal.get(), 100); 142 | ``` 143 | 144 | MaxVal的应用测试 - 获取最大数组中最大值 145 | 146 | ```cpp 147 | // random test 148 | dstruct::Array data; 149 | d2ds::random_data_generator(data, 0, 200); 150 | d2ds::ds_print(data); 151 | 152 | int maxVal = 0; 153 | for (int i = 0; i < data.size(); i++) { 154 | mVal.set(data[i]); 155 | if (data[i] > maxVal) { 156 | maxVal = data[i]; 157 | } 158 | } 159 | 160 | d2ds_assert_eq(mVal.get(), maxVal); 161 | 162 | ``` 163 | 164 | ### 数据结构接口实现 165 | 166 | 根据代码示例和接口描述来实现这个数据结构 167 | 168 | #### 类型定义 169 | 170 | ```cpp 171 | class MaxValue { 172 | public: 173 | MaxValue(int val) : mMaxVal_e { val } { } 174 | 175 | private: 176 | int mMaxVal_e; 177 | }; 178 | ``` 179 | 180 | #### get接口实现 181 | 182 | ```cpp 183 | class MaxValue { 184 | public: 185 | //... 186 | int get() { 187 | return mMaxVal_e; 188 | } 189 | 190 | private: 191 | int mMaxVal_e; 192 | }; 193 | ``` 194 | 195 | #### set接口实现 196 | 197 | ```cpp 198 | class MaxValue { 199 | public: 200 | //... 201 | void set(int val) { 202 | if (val > mMaxVal_e) { 203 | mMaxVal_e = val; 204 | } 205 | } 206 | 207 | private: 208 | int mMaxVal_e; 209 | }; 210 | ``` 211 | 212 | ## 代码练习dslings 213 | 214 | 用dslings的**编译器驱动开发模式**来进行代码练习 215 | 216 | ### 代码下载 217 | 218 | ```bash 219 | git clone --recursive git@github.com:Sunrisepeak/d2ds.git 220 | ``` 221 | 222 | ### 安装xmake 223 | 224 | ```bash 225 | sudo add-apt-repository ppa:xmake-io/xmake 226 | sudo apt-get update 227 | sudo apt-get install g++ gdb xmake make -y 228 | ``` 229 | 230 | ### dslings自动检测 231 | 232 | 在本地[d2ds仓库](https://github.com/Sunrisepeak/d2ds)的根目录执行如下命令 233 | 234 | ```bash 235 | xmake dslings 236 | ``` 237 | 238 | 程序就开始自动的测试/校验, 直到一个没有完成(或错误的)练习代码, 并给出对应的练习位置以及相关的错误信息提示 239 | 240 | > **注** 241 | > 242 | > - 执行命令前, 请确保电脑已经配置了C++环境, 并安装了[xmake](https://github.com/xmake-io/xmake)构建工具 243 | > 244 | > - 强烈建议使用vscode作为代码练习的编辑器, 这样dslings在控制台给出的练习代码路径, 只需要用**ctrl+鼠标左键**点击就可以自动转跳到目标位置 245 | > 246 | 247 | **错误提示** 248 | 249 | ```text 250 | 🌏Progress: [>-----] 0/5 251 | 252 | [Target: 0.dslings-0] 253 | 254 | ❌ Error: Compilation/Running failed for tests/dslings.0.cpp: 255 | 256 | The code exist some error! 257 | 258 | Output: 259 | ==================== 260 | [ 25%]: cache compiling.release tests/dslings.0.cpp 261 | error: tests/dslings.0.cpp:20:11: error: ‘MaxValue’ is not a member of ‘d2ds’ 262 | 20 | d2ds::MaxValue mVal(2); 263 | | ^~~~~~~~ 264 | In file included from /usr/include/c++/11/cassert:44, 265 | from ./tests/common.hpp:6, 266 | from tests/dslings.0.cpp:14: 267 | tests/dslings.0.cpp:22:12: error: ‘mVal’ was not declared in this scope 268 | 22 | d2ds_assert_eq(mVal.get(), 2); 269 | | ^~~~ 270 | > in tests/dslings.0.cpp 271 | 272 | 273 | ==================== 274 | 275 | Book: https://sunrisepeak.github.io/d2ds 276 | ``` 277 | 278 | 执行命令后dslings程序会停在最近的未完成的练习, 并会"实时"检测和这个练习相关的数据结构代码的实现。 279 | 我们可以根据dslings在控制台的输出找到对应的练习代码: 280 | 281 | ```cpp 282 | // dslings.0.cpp - readonly 283 | // 284 | // 描述: 285 | // 通过实现一个MaxVal类型(保存最大值), 来介绍dslings的"编译器驱动开发" 286 | // 即根据编译器的错误提示来完成这个训练流程的演示Demo, 并且通常为了降低难度会把一个'数据结构'的实现分成多个检测模块. 287 | // 如: dslings.0.cpp dslings.1.cpp dslings.2.cpp 288 | // 289 | // 目标/要求: 290 | // - 不修改该代码检测文件 291 | // - 在exercises/dslings.hpp中完成你的代码设计 292 | // - 通过所有编译器检测 和 断言 293 | // 294 | 295 | #include "common/common.hpp" 296 | 297 | #include "exercises/dslings.hpp" 298 | 299 | int main() { 300 | 301 | d2ds::MaxValue mVal(2); 302 | 303 | d2ds_assert_eq(mVal.get(), 2); 304 | 305 | return 0; 306 | } 307 | ``` 308 | 309 | 根据对应的练习代码中给的描述和要求完成该练习, 过程中可以结合dslings在控制台的提示来进行相关数据结构练习的代码设计。当正确完成代码后, dslings就会更新控制的输出给出对应的提示。 310 | 311 | **代码通过提示** 312 | 313 | ```text 314 | 🌏Progress: [=>----] 1/5 315 | 316 | [Target: 0.dslings-0] 317 | 318 | ✅ Successfully ran tests/dslings.0.cpp! 319 | 320 | 🎉 The code is compiling! 🎉 321 | 322 | Output: 323 | ==================== 324 | 325 | ==================== 326 | 327 | Book: https://sunrisepeak.github.io/d2ds 328 | 329 | ``` 330 | 331 | **XLINGS_WAIT - dslings等待标志** 332 | 333 | 当完整完成一个小节的练习的时候, dslings检测程序会进入等待状态, 并且打印出类似如下的提示消息 334 | 335 | ```bash 336 | [D2DS LOGW]: main: tests/dslings.2.cpp:46 - Delete the XLINGS_WAIT to continue... 337 | ``` 338 | 339 | 按照消息中给出的文件地址, 选择注释掉(或删除)程序中的`XLINGS_WAIT`标志, dslings就会进入下一个练习并开启自动检测 340 | 341 | ## 总结 342 | 343 | 好的, 到这里你应该已经了解了本书的叙述逻辑和结构 - **[数据结构使用 + 数据结构实现 + 对应代码练习]**。但该项目现任处于持续构建中(WIP), 依然存在相当多的问题。如果你在这个过程中你发现了一些项目的问题或自己遇到了一些问题, 欢迎到[d2ds讨论区](https://github.com/Sunrisepeak/d2ds-courses/discussions)反馈和交流。那么下面可以开始你的**动手写数据结构**之旅了... -------------------------------------------------------------------------------- /book/src/Resources.md: -------------------------------------------------------------------------------- 1 | # 资源 2 | 3 | 开源电子书 + 代码练习 + 公开课 + 论坛讨论 4 | 5 | #### 开源电子书 6 | 7 | --- 8 | 9 | [在线阅读](https://sunrisepeak.github.io/d2ds/) 10 | 11 | [书籍原始文件](https://github.com/Sunrisepeak/d2ds/tree/main/src) 12 | 13 | 14 | 15 | #### 代码练习 16 | 17 | --- 18 | 19 | [d2ds数据结构代码练习目录](https://github.com/Sunrisepeak/d2ds/tree/main/exercises) 20 | 21 | [d2ds数据结构代码检测项目录](https://github.com/Sunrisepeak/d2ds/tree/main/tests) 22 | 23 | 24 | 25 | #### 公开课 26 | 27 | --- 28 | 29 | [课程主页](https://sunrisepeak.github.io/d2ds-courses/) 30 | 31 | [B站 - 视频列表](https://space.bilibili.com/65858958/channel/seriesdetail?sid=4040405) 32 | 33 | [YouTube - Playlist](https://www.youtube.com/playlist?list=PL7uow6t1QjF1MtrsJdhkJXsCKwwnVZApH) 34 | 35 | 36 | 37 | #### 论坛 38 | 39 | --- 40 | 41 | [d2ds书籍 | dslings](https://github.com/Sunrisepeak/d2ds/discussions) 42 | 43 | [d2ds课程内容讨论](https://github.com/Sunrisepeak/d2ds-courses/discussions) 44 | 45 | 46 | 47 | #### 参与贡献 48 | 49 | --- 50 | 51 | [d2ds - issues | task](https://github.com/Sunrisepeak/d2ds/issues) 52 | 53 | [d2ds-courses - issues | task](https://github.com/Sunrisepeak/d2ds-courses/issues) 54 | 55 | #### 其他 56 | 57 | --- 58 | 59 | [show-your-code-2024](https://github.com/Sunrisepeak/d2ds-courses/tree/show-your-code-2024) 60 | 61 | [DStruct 数据结构模板库](https://github.com/Sunrisepeak/DStruct) -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | # 阅读准备 4 | - [导读](./Instroduction.md) 5 | - [资源](./Resources.md) 6 | - [环境配置](./dslings.md) 7 | 8 | # 常用数据结构 9 | - [数组](0_array.md) 10 | - [定长数组Array](chapter_01_array.md) 11 | - [动态数组Vector](chapter_02_vector.md) 12 | - [Array和Vector对比]() 13 | 14 | - [链表](1_linkedlist.md) 15 | - [嵌入式单链表](chapter_04_embedded_slist.md) 16 | - [单链表](chapter_05_slist.md) 17 | - [嵌入式双链表](chapter_06_embedded_dlist.md) 18 | - [双链表]() 19 | - [静态链表]() 20 | - [几种链表对比]() 21 | 22 | - [栈]() 23 | - [栈适配器]() 24 | 25 | - [队列]() 26 | - [队列适配器]() 27 | - [双端队列]() 28 | - [循环队列]() 29 | 30 | - [数组VS链表]() 31 | 32 | # 非线性数据结构 33 | 34 | - [树]() 35 | - [二叉树]() 36 | - [二叉搜索树]() 37 | 38 | - [图]() 39 | 40 | # 相关主题 41 | - [数据结构基本概念](other/0_ds_base.md) 42 | - [C++基础](other/1_cpp_base.md) 43 | - [范型编程](other/1_cpp_base.template.md) 44 | - [语法糖 | 范围for循环](other/2_cpp_base.rangefor.md) 45 | - [行为控制](other/3_cpp_base.bigfive.md) 46 | - [深入理解new/delete]() 47 | - [内存管理]() 48 | - [设计模式]() 49 | - [迭代器设计模式]() 50 | - [适配器设计模式]() -------------------------------------------------------------------------------- /book/src/chapter_01_array.md: -------------------------------------------------------------------------------- 1 | # 定长数组Array 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 原生数组 9 | - Array数组 10 | - Array核心实现 11 | - 类型定义 - 固定类型模板参数 12 | - 数据初始化 - 列表初始化器的使用 13 | - BigFive - 行为控制 14 | - 数据访问 - 下标访问运算符重载 15 | - 常用函数实现 - size/back 16 | - 迭代器支持 - 范围for 17 | - 功能扩展 - 负下标访问支持 18 | - 总结 19 | 20 | --- 21 | 22 | Array是一个对原生数组轻量级封装的容器, 在性能于原生数组几乎相等的情况下, 携带了大小信息提供了类型安全可以避免许多由于数组边界问题导致的错误。同时Array也相对提供了更丰富的接口, 并且提供了接口标准化可能性, 方便使用数据结构的通用算法。 23 | 24 | **原生数组** 25 | 26 | ```cpp 27 | void arr_init(int *arr, int size) { 28 | for (int i = 0; i < size; i++) { 29 | arr[i] = -1; 30 | } 31 | } 32 | 33 | int main() { 34 | int arr[10]; 35 | arr_init(arr, 10); 36 | return 0; 37 | } 38 | ``` 39 | 40 | **Array数组** 41 | 42 | ```cpp 43 | void arr_init(const Array &arr) { 44 | for (int i = 0; i < arr.size(); i++) { 45 | arr[i] = -1; 46 | } 47 | } 48 | 49 | template 50 | void process(const IntArrayType &arr) { 51 | for (int &val : arr) { 52 | val *= 2; 53 | } 54 | } 55 | 56 | int main() { 57 | Array arr; 58 | arr_init(arr, 10); 59 | return 0; 60 | } 61 | ``` 62 | 63 | 从上面两个简单的例子, 在多数情况下**原生数组**的数据长度信息是需要开发人员额外记忆的, 这可能会引发一些潜在风险, 而对于**Array容器**的对象本身就会携带长度信息, 并且这个携带信息的代价不需要而外的存储空间。此外, 也有利用像`process`这种"通用型算法"的设计。换句话说, Array也能使用更多的通用数据结构算法。 64 | 65 | > 注: Array使用栈区内存, Vector使用的是"动态分配"内存, 对于固定大小的数组使用Array要比Vector在性能上更有优势 66 | 67 | ## Array核心实现 68 | 69 | ### 类型定义 - 固定类型模板参数 70 | 71 | 在Array的模板参数中的第二个参数上使用N来标识数组长度信息, 需要注意的是这里的N和类型参数T时有差异的, 它是一个`unsigned int`类型的**非类型模板参数**, 可以简单视为固定类型(指定类型)的信息标识, 在模板参数中就指定类型。 72 | 73 | ```cpp 74 | template 75 | class Array { 76 | 77 | }; 78 | ``` 79 | 80 | ### 数据初始化 - 列表初始化器的使用 81 | 82 | 数组的列表初始化, 在编程中非常常用和方便对数据做初始化的方式, 如下: 83 | 84 | ```cpp 85 | d2ds::Array intArr { 5, 4, 3, 2 /*, 1*/ }; 86 | ``` 87 | 88 | 对于自定义类型的Array模板, 要想支持这个特性可以使用`initializer_list`来获取列表中的数据, 并且使用迭代器进行数据的访问, 所以在Array中实现一个支持`initializer_list`的构造器即可 89 | 90 | ```cpp 91 | template 92 | class Array { 93 | public: 94 | Array(std::initializer_list list) { 95 | int i = 0; 96 | for (auto it = list.begin(); it != list.end() && i < N; it++) { 97 | mData_e[i] = *it; 98 | i++; 99 | } 100 | } 101 | 102 | private: 103 | T mData_e[N == 0 ? 1 : N]; 104 | }; 105 | ``` 106 | 107 | > 注: 这里N存在为0的情况, 所以使用三目运算`N == 0 ? 1 : N`来保证数组长度至少为1 108 | 109 | ### BigFive - 行为控制 110 | 111 | BigFive核心指的是一个类型对象的**拷贝语义**和**移动语义**, 也常被称为**三五原则**(见[cppreference](https://en.cppreference.com/w/cpp/language/rule_of_three))。 多数情况下编译器是能自动生成这些代码的, 但作为一个库的话(特别是个数据结构容器库), 往往需要明确每个数据结构的行为, 这对数据结构中的数据复制和移动中的性能优化也是非常有帮助的。我们可以通过下面的类成员来实现其行为控制: 112 | 113 | | 类成员 | 简述 | 114 | |---|---| 115 | | `~ClassName()` | 析构 | 116 | | `ClassName(const ClassName &)` | 拷贝构造 | 117 | | `ClassName & operator=(const ClassName &)` | 拷贝赋值 | 118 | | `ClassName(ClassName &&)` | 移动构造 | 119 | | `ClassName & operator=(ClassName &&)` | 移动赋值 | 120 | 121 | **析构行为** 122 | 123 | 由于Array中也是使用原生数组来进行存储数据的, 这里使用使用默认的构造函数和析构和原生数组行为保持一致 124 | 125 | ```cpp 126 | template 127 | class Array { 128 | public: // bigFive 129 | Array() = default; 130 | ~Array() = default; 131 | //... 132 | }; 133 | ``` 134 | 135 | **拷贝语义** 136 | 137 | 主要是方便数据结构中数据复制的方便 138 | 139 | 拷贝构造 140 | 141 | ```cpp 142 | d2ds::Array intArr1; 143 | d2ds::Array intArr2(intArr1); 144 | ``` 145 | 146 | 拷贝构造函数常用于"**一个同类型已存在的对象来初始化一个新对象**"的场景, 对于Array来说主要实现把已存在对象中的数据进行复制到新对象中即可 147 | 148 | ```cpp 149 | template 150 | class Array { 151 | public: // bigFive 152 | //... 153 | Array(const Array &dsObj) { 154 | for (int i = 0; i < N; i++) { 155 | mData_e[i] = dsObj.mData_e[i]; 156 | } 157 | } 158 | //... 159 | }; 160 | ``` 161 | 162 | > Note: 这里也可使用 `placement new` 来构造数据结构中的对象, 他的主要功能是把内存分配和对象构造进行分离--即在已有的内存上进行构造对象。更多关于**new/delete运算符的分析**将放到[C++基础]()章节 163 | 164 | 拷贝赋值 165 | 166 | ```cpp 167 | d2ds::Array intArr1, intArr2; 168 | // ... 169 | intArr1 = intArr2; 170 | ``` 171 | 172 | 通过赋值`=`运算符, 把一个Array中的数据复制到另一个数组中 173 | 174 | ```cpp 175 | template 176 | class Array { 177 | public: // bigFive 178 | //... 179 | Array & operator=(const Array &dsObj) { 180 | D2DS_SELF_ASSIGNMENT_CHECKER 181 | for (int i = 0; i < N; i++) { 182 | mData_e[i] = dsObj.mData_e[i]; 183 | } 184 | return *this; 185 | } 186 | //... 187 | }; 188 | ``` 189 | 190 | **移动语义** 191 | 192 | 有些场景为了性能会使用**移动语义**, 只去改变数据的**所有权**来避免数据资源的重复制、频繁分配/释放带来的开销 193 | 194 | 移动构造 195 | 196 | ```cpp 197 | d2ds::Array intArr1; 198 | //... 199 | d2ds::Array intArr2 { std::move(intArr1) }; 200 | ``` 201 | 202 | 这里假设intArr1后续不在使用, 如果使用**拷贝构造**, 可能就会造成`BigFiveTest::Obj`中动态分配的资源没有必要的分配和复制。例如, 对象中有一个指针并指向一块资源, 使用移动构造去触发对象只复制资源的地址, 而不需要重新分配并做数据复制。**移动构造**中不仅要把数据结构的资源进行移动, 一些情况也要把**移动语义**传给直接管理的数据 203 | 204 | ```cpp 205 | template 206 | class Array { 207 | public: // bigFive 208 | //... 209 | Array(Array &&dsObj) { 210 | for (int i = 0; i < N; i++) { 211 | mData_e[i] = std::move(dsObj.mData_e[i]); 212 | } 213 | } 214 | //... 215 | }; 216 | ``` 217 | 218 | 移动赋值 219 | 220 | ```cpp 221 | d2ds::Array intArr1, intArr2; 222 | // ... 223 | intArr1 = std::move(intArr2); 224 | ``` 225 | 226 | 移动赋值和移动构造一样 227 | 228 | ```cpp 229 | template 230 | class Array { 231 | public: // bigFive 232 | //... 233 | Array & operator=(Array &&dsObj) { 234 | D2DS_SELF_ASSIGNMENT_CHECKER 235 | for (int i = 0; i < N; i++) { 236 | mData_e[i] = std::move(dsObj.mData_e[i]); 237 | } 238 | return *this; 239 | } 240 | //... 241 | }; 242 | ``` 243 | 244 | > Note1: D2DS_SELF_ASSIGNMENT_CHECKER 宏是防止对象自我赋值情况的一个检测。例如:对象myObj赋值给自己(`myObj = myObj;`)。 该宏的实现原理(`if (this == &dsObj) return *this;`)是通过地址检查来规避这种情况 245 | 246 | > Note2: 对于Array的移动语义是不够直观的, 在Vector实现中有更直观的使用, 且更多关于BigFive-**拷贝语义**和**移动语义**的介绍将放到**C++基础章节** 247 | 248 | ### 数据访问 - 下标运算符重载 249 | 250 | ```cpp 251 | intArr[1] = 6; 252 | intArr[4] = intArr[0]; 253 | ``` 254 | 255 | 实现类数组的下标访问的核心就是, 在对应类型中实现对下标运算符`[]`的重载 256 | 257 | ```cpp 258 | template 259 | class Array { 260 | public: 261 | //... 262 | T & operator[](int index) { 263 | return mData_e[index]; 264 | } 265 | //... 266 | }; 267 | ``` 268 | 269 | ### 常用函数实现 270 | 271 | ```cpp 272 | d2ds::Array intArr { 0, 1, 2, 3, 4 }; 273 | for (int i = 0; i < intArr.size(); i++) { 274 | d2ds_assert_eq(i, intArr[i]); 275 | } 276 | d2ds_assert_eq(4, intArr.back()); 277 | ``` 278 | 279 | 获取数据结构中的元素数量, 和获取最后一个元素都是常用的功能 280 | 281 | **size** 282 | 283 | ```cpp 284 | template 285 | class Array { 286 | public: 287 | unsigned int size() const { 288 | return N; 289 | } 290 | //... 291 | }; 292 | ``` 293 | 294 | **back** 295 | 296 | ```cpp 297 | template 298 | class Array { 299 | public: 300 | T back() const { 301 | return mData_e[N != 0 ? N - 1 : 0]; 302 | } 303 | //... 304 | }; 305 | ``` 306 | 307 | ### 迭代器支持 308 | 309 | 对于Array的迭代器, 由于内部是使用数组来存储数据, 数据是在连续内存空间上的, 可以直接使用类型的指针作为迭代器类型来做迭代器支持和范围for的使用 310 | 311 | ```cpp 312 | template 313 | class Array { 314 | public: 315 | T * begin() { 316 | return mData_e; 317 | } 318 | 319 | T * end() { 320 | return mData_e + N; 321 | } 322 | //... 323 | }; 324 | ``` 325 | 326 | > Note: 关于范围for和迭代器相关的内容见**相关主体**部分 327 | 328 | ### 扩展功能 - 负下标访问 329 | 330 | 在里在给Array添加一个**扩展功能**, 像其他一些语言来支持负号下标访问 331 | 332 | ```cpp 333 | template 334 | class Array { 335 | public: 336 | //... 337 | T & operator[](int index) { 338 | // add start 339 | if (index < 0) 340 | index = N + index; 341 | // add end 342 | d2ds_assert(index >= 0 && index < N); 343 | return mData_e[index]; 344 | } 345 | //... 346 | }; 347 | ``` 348 | 349 | ## 总结 350 | 351 | 本章节介绍了固定长度的数据结构Array实现, 它既具备了原生数组的性能又具备了现代数据结构容器的安全性和扩展性。同时也介绍了很多来支持Array模板实现的技术和编程技巧: 列表初始化器、类行为控制、自定义下标访问等, 下一章我们将开始介绍不定长数组(动态数组)Vector的核心功能实现 -------------------------------------------------------------------------------- /book/src/chapter_02_vector.md: -------------------------------------------------------------------------------- 1 | # 动态数组Vector 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 定长数据 9 | - 变长数组 10 | - Vector核心实现 11 | - 类型定义和数据初始化 - 自定义分配器支持 12 | - BigFive - 行为控制 13 | - 常用函数和数据访问 14 | - 数据增删和扩容机制 - resize 15 | - 迭代器支持 - 范围for 16 | - 功能扩展 - 向量加减法 17 | - 总结 18 | 19 | --- 20 | 21 | Vector是一个动态大小的数组, 元素存储在一个动态分配的连续空间。在使用中, 可以向Vector添加元素或删除数据结构中已有的元素, 内部会自动的根据数据量的大小进行扩大或缩小容量 22 | 23 | **手动管理** 24 | 25 | ```cpp 26 | int main() { 27 | int *intArr = (int *)malloc(sizeof(int) * 2); 28 | intArr[0] = 1; intArr[1] = 2; // init 29 | 30 | // do something 31 | 32 | int *oldIntArr = intArr; 33 | intArr = (int *)malloc(sizeof(int) * 4); 34 | 35 | intArr[0] = oldIntArr[0]; intArr[1] = oldIntArr[1]; // copy 36 | free(oldIntArr); 37 | 38 | intArr[2] = 3; 39 | intArr[3] = 4; 40 | 41 | for (int i = 0; i < 4; i++) { 42 | std::cout << intArr[i] << " "; 43 | } 44 | std::cout << std::endl; 45 | for (int i = 0; i < 2; i++) { 46 | std::cout << intArr[i] << " "; 47 | } 48 | 49 | free(intArr); 50 | 51 | return 0; 52 | }; 53 | 54 | ``` 55 | 56 | **自动管理** 57 | 58 | ```cpp 59 | int main() { 60 | d2ds::Vector intArr = { 1, 2 }; 61 | 62 | intArr.push_back(3); 63 | intArr.push_back(4); 64 | 65 | for (int i = 0; i < 4; i++) { 66 | std::cout << intArr[i] << " "; 67 | } 68 | std::cout << std::endl; 69 | 70 | intArr.pop_back(); 71 | intArr.pop_back(); 72 | 73 | for (int i = 0; i < intArr.size() /* 2 */; i++) { 74 | std::cout << intArr[i] << " "; 75 | } 76 | 77 | return 0; 78 | }; 79 | ``` 80 | 81 | **输出结果** 82 | 83 | ```cpp 84 | 1 2 3 4 85 | 1 2 86 | ``` 87 | 88 | 上面使用`Vector`创建了一个`intArr`数组, 并在使用中通过`push_back`和`pop_back`改变了数组的长度, 而关于存储数据的内存的扩大和缩小全由`Vector`内部完成, 对使用者是"透明"的, 从而降低了开发者手动去管理内存分配的负担 89 | 90 | ## Vector核心实现 91 | 92 | ### 类型定义和数据初始化 93 | 94 | **统一分配器接口** 95 | 96 | 使用一个分配器类型作为作用域标识, 类型中包含两个静态成员函数用于内存的分配和释放 97 | 98 | ```cpp 99 | struct Allocator { 100 | static void * allocate(int bytes); 101 | static void deallocate(void *addr, int bytes); 102 | }; 103 | ``` 104 | 105 | 其中`allocate`用于分配内存, 它的参数为请求的内存字节数; `deallocate`用于内存的释放, addr为内存块地址, bytes为内存块的大小 106 | 107 | **类型定义** 108 | 109 | 第一个模板参数用于接收数据类型, 第二个参数用于接收一个满足上面标准的分配器类型。为了方便使用, 使用`DefaultAllocator`作为分配器模板参数的默认类型, 这样开发者在不明确指定分配器的时候就会使用默认的分配器进行内存分配 110 | 111 | ```cpp 112 | template 113 | class Vector { 114 | 115 | }; 116 | ``` 117 | 118 | > 注: `DefaultAllocator`是一个已经定义在`d2ds`命名空间的分配器。文件: common/common.hpp 119 | 120 | **数据初始化** 121 | 122 | ```cpp 123 | d2ds::Vector vec1; 124 | d2ds::Vector vec2(10); 125 | d2ds::Vector vec3 = { 1, 2, 3 }; 126 | ``` 127 | 128 | 定义数据成员, 并实现常见的默认初始化、指定长度的初始化、列表初始化器初始化 129 | 130 | ```cpp 131 | template 132 | class Vector { 133 | public: 134 | 135 | Vector() : mSize_e { 0 }, mDataPtr_e { nullptr } { } 136 | 137 | Vector(int size) : mSize_e { size } { 138 | mDataPtr_e = static_cast(Alloc::allocate(sizeof(T) * mSize_e)); 139 | for (int i = 0; i < mSize_e; i++) { 140 | new (mDataPtr_e + i) T(); 141 | } 142 | } 143 | 144 | Vector(std::initializer_list list) { 145 | mSize_e = list.end() - list.begin(); 146 | mDataPtr_e = static_cast(Alloc::allocate(sizeof(T) * mSize_e)); 147 | auto it = list.begin(); 148 | T *dataPtr = mDataPtr_e; 149 | while (it != list.end()) { 150 | new (dataPtr) T(*it); 151 | it++; dataPtr++; 152 | } 153 | } 154 | 155 | private: 156 | int mSize_e; 157 | T * mDataPtr_e; 158 | }; 159 | ``` 160 | 161 | 定义一个`mSize_e`来标识元素数量, 使用`Alloc`进行内存分配来存储数据, 并由`mDataPtr_e`来管理。 162 | 同时配合使用**定位new**(placenment new)来完成数据的构造。这里把元素对象的创建划分成了两步: 第一步, 分配对应的内存; 第二步, 基于获得的内存进行构造对象 163 | 164 | > 注: C++中使用`new Obj()`创建对象, 可以看作是`ptr = malloc(sizeof(Obj)); new (ptr) Obj();`这两步的组合。详情见[深入理解new/delete]()章节 165 | 166 | 167 | ### BigFive - 行为控制 168 | 169 | **析构行为** 170 | 171 | 由于使用了内存分配和对象构造分离的模式, 所以在析构函数中需要对数据结构中的元素要先析构, 最后再释放内存。即需要满足如下构造/析构链, 让对象的创建和释放步骤对称: 172 | 173 | - 分配对象内存A 174 | - 基于内存A构造对象B 175 | - 析构对象B 176 | - 释放B对应的内存A 177 | 178 | ```cpp 179 | template 180 | class Vector { 181 | public: 182 | ~Vector() { 183 | if (mSize_e) { 184 | for (int i = 0; i < mSize_e; i++) { 185 | (mDataPtr_e + i)->~T(); 186 | } 187 | Alloc::deallocate(mDataPtr_e, mSize_e * sizeof(T)); 188 | } 189 | } 190 | } 191 | ``` 192 | 193 | **拷贝语义** 194 | 195 | 在拷贝构造函数中, 使用`new (addr) T(const T &)`把**拷贝构造语义**传递给数据结构中存储的元素 196 | 197 | ```cpp 198 | template 199 | class Vector { 200 | public: 201 | Vector(const Vector &dsObj) : mSize_e { dsObj.mSize_e } { 202 | mDataPtr_e = (T *) Alloc::allocate(sizeof(T) * mSize_e); 203 | for (int i = 0; i < mSize_e; i++) { 204 | new (mDataPtr_e + i) T(dsObj.mDataPtr_e[i]); 205 | } 206 | } 207 | } 208 | ``` 209 | 210 | 在拷贝赋值函数中, 先调用析构函数进行数据清理, 同时也使用`operator=`进行语义传递 211 | 212 | ```cpp 213 | template 214 | class Vector { 215 | public: 216 | Vector & operator=(const Vector &dsObj) { 217 | D2DS_SELF_ASSIGNMENT_CHECKER 218 | this->~Vector(); 219 | mSize_e = dsObj.mSize_e; 220 | mDataPtr_e = static_cast(Alloc::allocate(sizeof(T) * mSize_e)); 221 | for (int i = 0; i < mSize_e; i++) { 222 | mDataPtr_e[i] = dsObj.mDataPtr_e[i]; 223 | } 224 | return *this; 225 | } 226 | } 227 | ``` 228 | 229 | **移动语义** 230 | 231 | 在移动构造函数中, 只需要把要目标对象的资源移动到该对象, 然后对被移动的对象做重置操作即可。对于Vector来说, 只需进行**浅拷贝**数据成员, 并对被移动的对象置空 232 | 233 | ```cpp 234 | template 235 | class Vector { 236 | public: 237 | Vector(Vector &&dsObj) : mSize_e { dsObj.mSize_e } { 238 | mDataPtr_e = dsObj.mDataPtr_e; 239 | // reset 240 | dsObj.mSize_e = 0; 241 | dsObj.mDataPtr_e = nullptr; 242 | } 243 | } 244 | ``` 245 | 246 | 在移动赋值函数中, 比移动构造多了对对象本身资源的释放操作 247 | 248 | ```cpp 249 | template 250 | class Vector { 251 | public: 252 | Vector & operator=(Vector &&dsObj) { 253 | D2DS_SELF_ASSIGNMENT_CHECKER 254 | this->~Vector(); 255 | mSize_e = dsObj.mSize_e; 256 | mDataPtr_e = dsObj.mDataPtr_e; 257 | // reset 258 | dsObj.mSize_e = 0; 259 | dsObj.mDataPtr_e = nullptr; 260 | return *this; 261 | } 262 | } 263 | ``` 264 | 265 | ### 常用函数和数据访问 266 | 267 | **常用函数 - size / empty** 268 | 269 | ```cpp 270 | template 271 | class Vector { 272 | public: 273 | int size() const { 274 | return mSize_e; 275 | } 276 | 277 | bool empty() const { 278 | return mSize_e == 0; 279 | } 280 | } 281 | ``` 282 | 283 | **数据访问** 284 | 285 | ```cpp 286 | d2ds::Vector intArr3 = { -1, -2, -3 }; 287 | const d2ds::Vector constIntArr3 = { 1, 2, 3 }; 288 | ``` 289 | 290 | Vector存在被`const`修饰的情况, 所以`operator=`也要对应实现一个`const`版本, 返回值为`const T &` 291 | 292 | ```cpp 293 | template 294 | class Vector { 295 | public: 296 | T & operator[](int index) { 297 | return mDataPtr_e[index]; 298 | } 299 | 300 | const T & operator[](int index) const { 301 | return mDataPtr_e[index]; 302 | } 303 | } 304 | ``` 305 | 306 | ### 数据增删 - 扩容和缓存机制 307 | 308 | 当动态数组Vector执行push操作进行添加元素时, 如果每次都需要重新分配内存这会极大的影响效率 309 | 310 | ```cpp 311 | void push(const int &obj) { 312 | newDataPtr = malloc(sizeof(int) * (size + 1)); // 分配内存 313 | copy(newDataPtr, oldDataPtr); // 复制数据 314 | free(oldDataPtr); // 释放内存 315 | newDataPtr[size + 1] = obj; // 添加新元素 316 | size++; // 数量加1 317 | } 318 | ``` 319 | 320 | 通过引入内存容量的缓存或者说预分配机制, 来避免过多的内存分配释放, 可以有效的降低它的影响。所以就需要引入另外一个标识`mCapacity_e`来标识当前内存最大容量, 而`mSize_e`用来标识当前数据结构中的实际元素数量, 所以`mCapacity_e`是大于等于`mSize_e`的 321 | 322 | ```cpp 323 | template 324 | class Vector { 325 | private: 326 | int mSize_e, mCapacity_e; 327 | T * mDataPtr_e; 328 | } 329 | ``` 330 | 331 | 这里需要先说明一下, 扩容(缩容)机制通常是包含两个概念或步骤: 332 | 333 | - 第一个是, 扩容(缩容)的条件, 也是执行实际操作的时机。通常扩容发生再数据增加操作, 缩容发生数据删除操作中 334 | - 第二个是, 具体的扩容(缩容)规则。最简单的就是二倍扩容(缩容) 335 | 336 | > 注: 成员变量的变动, 意味着对应的**BigFive**也需要修改 337 | 338 | **push_back 和 扩容** 339 | 340 | 在每次扩容的时候, 可以选择基于当前容量的二倍进行扩容。例如: 当`mCapacity_e`等于4时, 做扩容时应该分配可以容纳8个元素的内存 341 | 342 | ```cpp 343 | d2ds::Vector intArr = {0, 1, 2, 3}; 344 | intArr.push_back(4); 345 | /* 346 | old: mCapacity_e == 4, mSize_e == 4 347 | +---------------+ 348 | mDataPtr_e -> | 0 | 1 | 2 | 3 | 349 | +---------------+ 350 | new: mCapacity_e == 8, mSize_e == 5 351 | +-------------------------------+ 352 | mDataPtr_e -> | 0 | 1 | 2 | 3 | 4 | | | | 353 | +-------------------------------+ 354 | */ 355 | ``` 356 | 357 | 什么时候扩容? 最直观的是增加元素, 但容量又不够的时候。执行push_back时, 当`mSize_e + 1 > mCapacity_e`时就需要扩容来获取更大的空间用于新数据/元素的存放, 既是否扩容需要在存储新元素操作之前 358 | 359 | ```cpp 360 | template 361 | class Vector { 362 | public: 363 | void push_back(const T &element) { 364 | if (mSize_e + 1 > mCapacity_e) { 365 | resize(mCapacity_e == 0 ? 2 : 2 * mCapacity_e); 366 | } 367 | new (mDataPtr_e + mSize_e) T(element); 368 | mSize_e++; 369 | } 370 | } 371 | ``` 372 | 373 | **pop_back 和 缩容** 374 | 375 | 当数据量减少时, 同样需要释放过多的内存容量来避免内存浪费。这时就引入一个问题, 如果使用二倍原则, 是当数据结构中的真实数据量等于最大容量的1/2时进行重新分配吗? 考虑一下这样的场景: 376 | 377 | ```cpp 378 | d2ds::Vector intArr = { 1, 2, 3, 4 }; 379 | for (int i = 0; i < 10; i++) { 380 | intArr.push_back(i); // 触发扩容 381 | // ... 382 | intArr.pop_back(); // 触发缩容 383 | } 384 | ``` 385 | 386 | 当频繁小数据量的增加和减少, 就会造成Vector内部不停的扩容和缩容操作, 这种现象也称为——**抖动**。 387 | 388 | 为了近可能的避免这种情况, 在执行缩容之后也应该保留/缓存一部分未使用的内存空间, 用于后续可能的数据增加操作。即扩容或者缩容都要保证一定的空闲内存, 用于后续可能的操作。如: 下面就是1/3触发条件, 2倍(1/2)扩容机制的内存变化情况 389 | 390 | ```cpp 391 | mCapacity_e == 8, mSize_e == 5 392 | +-------------------------------+ 393 | mDataPtr_e -> | 0 | 1 | 2 | 3 | 4 | | | | 394 | +-------------------------------+ 395 | 396 | intArr.pop_back(); 397 | 398 | mCapacity_e == 8, mSize_e == 4 399 | +-------------------------------+ 400 | mDataPtr_e -> | 0 | 1 | 2 | 3 | | | | | 401 | +-------------------------------+ 402 | 403 | intArr.pop_back(); 404 | 405 | mCapacity_e == 8, mSize_e == 3 406 | +-------------------------------+ 407 | mDataPtr_e -> | 0 | 1 | 2 | | | | | | 408 | +-------------------------------+ 409 | 410 | intArr.pop_back(); 411 | 412 | mCapacity_e == 4, mSize_e == 2 413 | +---------------+ 414 | mDataPtr_e -> | 0 | 1 | | | 415 | +---------------+ 416 | ``` 417 | 418 | 当`mSize_e <= mCapacity_e / 3`时就触发一次二倍扩容机制的执行, 把容量从8缩小一半到4, 此时实际存储的数据量`mSize_e == 2`。这里需要注意的是, 虽然`pop_back`不一定会释放Vector管理的内存, 但依然需要去调用被删除元素的析构函数去释放它额外管理的资源(如果存在) 419 | 420 | ```cpp 421 | template 422 | class Vector { 423 | public: 424 | void pop_back() { 425 | mSize_e--; 426 | (mDataPtr_e + mSize_e)->~T(); 427 | if (mSize_e <= mCapacity_e / 3) { 428 | resize(mCapacity_e / 2); 429 | } 430 | } 431 | } 432 | ``` 433 | 434 | **resize实现** 435 | 436 | 对于resize的实现, 需要关注的核心点: 437 | 438 | - 新老内存的分配和释放 439 | - 老数据的迁移 440 | 441 | 首先进行分配一块能存n个元素的内存块, 然后在对数据进行迁移, 最后释放老的内存块。在进行数据迁移的过程中, 如果使用拷贝语义则需要通过**显式调用**析构进行释放老的内存, 如果使用移动语语义则可以避免**在所管理元素对象内部的资源的频繁分配释放**。为了能呈现主要骨架但有不过于复杂, 下面只实现了`mSize_e <= n`的情况的简化版本 442 | 443 | ```cpp 444 | template 445 | class Vector { 446 | void resize(int n) { // only mSize_e <= n 447 | auto newDataPtr = n == 0 ? nullptr : static_cast(Alloc::allocate(n * sizeof(T))); 448 | 449 | for (int i = 0; i < mSize_e; i++) { 450 | new (newDataPtr + i) T(mDataPtr_e[i]); 451 | (mDataPtr_e + i)->~T(); 452 | } 453 | 454 | if (mDataPtr_e) { 455 | // Note: 456 | // memory-size is mCapacity_e * sizeof(T) rather than mSize_e * sizeof(T) 457 | Alloc::deallocate(mDataPtr_e, mCapacity_e * sizeof(T)); 458 | } 459 | 460 | mCapacity_e = n; 461 | mDataPtr_e = newDataPtr; 462 | } 463 | } 464 | ``` 465 | 466 | ### 迭代器支持 467 | 468 | 由于Vector用于存储数据元素的内存是连续的, 所以可以使用原生指针作为数据访问的迭代器 469 | 470 | ```cpp 471 | const d2ds::Vector constIntArr = intArr; 472 | int sum = 0; 473 | for (auto &val : constIntArr) { 474 | sum += val; 475 | } 476 | ``` 477 | 478 | 为了让被`const`修饰的Vector, 可以正常使用迭代器访问数据, 所以可以再实现一套const版本的begin和end 479 | 480 | ```cpp 481 | template 482 | class Vector { 483 | public: 484 | T * begin() { 485 | return mDataPtr_e; 486 | } 487 | 488 | T * end() { 489 | return mDataPtr_e + mSize_e; 490 | } 491 | 492 | const T * begin() const { 493 | return mDataPtr_e; 494 | } 495 | 496 | const T * end() const { 497 | return mDataPtr_e + mSize_e; 498 | } 499 | }; 500 | ``` 501 | 502 | ### 功能扩展 - 向量加减法 503 | 504 | 假设有如下**OQ**、**OP**、**QP**三个向量 505 | 506 | ```cpp 507 | ^ 508 | | * P(2, 4) 509 | | 510 | | *Q(4, 1) 511 | *--------------> 512 | O(0, 0) 513 | ``` 514 | 515 | ```cpp 516 | d2ds::Vector OQ = { 4, 1 }; 517 | d2ds::Vector OP = { 2, 4 }; 518 | d2ds::Vector QP = { -2, 3 }; 519 | d2ds_assert(OQ + QP == OP); 520 | d2ds_assert(OP - OQ == QP); 521 | ``` 522 | 523 | 下面通过重载`operator+`和`operator-`来扩展下Vector再向量中的应用。这里为了直观我们直接假设向量是2维的, 在运算符重载函数中分别再实现向量的加减算法即可。怎么支持N维向量? 想必你心中已有答案 524 | 525 | 526 | ```cpp 527 | namespace d2ds { 528 | 529 | template 530 | bool operator==(const Vector &v1, const Vector &v2) { 531 | bool equal = v1.size() == v2.size(); 532 | if (equal) { 533 | for (int i = 0; i < v1.size(); i++) { 534 | if (v1[i] != v2[i]) { 535 | equal = false; 536 | break; 537 | } 538 | } 539 | } 540 | return equal; 541 | } 542 | 543 | template 544 | Vector operator+(const Vector &v1, const Vector &v2) { 545 | Vector v(2); 546 | v[0] = v1[0] + v2[0]; 547 | v[1] = v1[1] + v2[1]; 548 | return std::move(v); 549 | } 550 | 551 | template 552 | Vector operator-(const Vector &v1, const Vector &v2) { 553 | Vector v(2); 554 | v[0] = v1[0] - v2[0]; 555 | v[1] = v1[1] - v2[1]; 556 | return std::move(v); 557 | } 558 | 559 | } 560 | ``` 561 | 562 | ## 总结 563 | 564 | 本章节先是对比了一下, 对变长数组有需求的场景下。使用Vector自动管理内存和手动管理内存的差异和优势。然后,介绍了需要动态分配内存的数据结构如何去支持用户自定义分配的方法; 以及在内部自动管理内存的扩容机制的核心原理和对应**二倍扩容机制**的简单实现; 最后, 介绍了一个对Vector进行在向量领域的扩展应用。当然, 为了能够在呈现出动态数组Vector的核心原理下, 但又不过于复杂和拘迂细节, 本章中并没有去实现同样很常用的一些功能如: erase、back、data等。**但我相信在你学习完本章内容后的此时此刻, 你已基本具备自己去实现他们的能力** -------------------------------------------------------------------------------- /book/src/chapter_04_embedded_slist.md: -------------------------------------------------------------------------------- 1 | # 嵌入式单链表 - SinglyLink 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 核心原理 8 | - 结构 - 链域和数据域 9 | - 操作 - 链表的逻辑抽象 10 | - 使用 - 数据存储和访问 11 | - 设计/使用技巧 12 | - 通信库 - 组合式 13 | - Linux内核 - 嵌入式 14 | - V8引擎 - 继承式(C++) 15 | - 总结 16 | 17 | --- 18 | 19 | 嵌入式链表是一种高性能且范型支持的链表。同时也是一种"底层"的数据结构。它在内存效率和性能上的优秀的表现, 使得在[Linux内核](https://github.com/torvalds/linux/blob/master/include/linux/list.h)、[浏览器的v8引擎](https://source.chromium.org/chromium/chromium/src/+/main:v8/src/heap/list.h?q=ListNode&ss=chromium%2Fchromium%2Fsrc)、[Redis数据库](https://github.com/redis/redis/blob/unstable/src/adlist.h)等许多大型的开源项目中都有使用。对于大多数的数据结构实现, 关注的核心点可以归为**内存管理**、**类型控制**、**操作**这三个方面。通常这是库作者的工作, 而使用者只需要关心数据。而在嵌入式链表的使用中**内存管理**、**类型控制**是常需要使用者来显示控制的, 这使得它的使用难度远大于普通数据结构。这也是为什么它常应用到一些追求性能的系统模块, 而应用软件中却很少见到它的身影, 下面我们将从它的最小代码实现开始一步一步介绍其设计理念和使用技巧 20 | 21 | ### 结构 - 链域和数据域 22 | 23 | ```cpp 24 | struct ListNode { 25 | struct ListNode *next; // link区域 26 | int data; // data区域 27 | }; 28 | ``` 29 | 30 | 一个链表节点可以分成**link区域**和**数据区域**。其中,数据区用于储存数据,link区域用于存储指向下一个节点的指针,把分散的节点串连成一个链表。 31 | 32 | ### 操作 - 链表的逻辑抽象 33 | 34 | 如果我们像上面一样, 把数据类型和链表进行耦合。就会发现, 每定义一个链表就**只能给特定的数据类型使用**, 很难实现通用数据结构。当然, 在C++中有很多方法来实现这种通用性。例如: 编译器代码生成技术 - 模板 35 | 36 | ```cpp 37 | template 38 | struct ListNode { 39 | struct ListNode *next; // link区域 40 | T data; // data区域 41 | }; 42 | ``` 43 | 44 | 但模板的本质就是需要手写两遍的代码量, 转为编译器来帮你手写了。从底层角度看**数据类型**和**链表**依然是耦合的, 并且C语言中是不支持模板的。对于链表的很多操作一定要和存储的数据类型进行绑定吗?显然,链表的操作从逻辑上是和数据类型无关的。例如下面把**数据区域**丢弃的链表代码: 45 | 46 | ```cpp 47 | struct SinglyLink { 48 | struct SinglyLink *next; 49 | }; 50 | 51 | static void insert(SinglyLink *prev, SinglyLink *target) { 52 | target->next = prev->next; 53 | prev->next = target; 54 | } 55 | 56 | static void remove(SinglyLink *prev, SinglyLink *target) { 57 | prev->next = target->next; 58 | target->next = target; 59 | } 60 | ``` 61 | 62 | 这是不是链表? 是。但不存数据的链表有什么意义呢? 如果这时我说——这就是**嵌入式链表**的最小原型。你会不会产生如下疑问: 63 | 64 | - 它的使用方法? 65 | - 它存储和管理数据的原理? 66 | 67 | 下面我们将逐一回答 68 | 69 | ### 使用 - 数据存储和访问 70 | 71 | 开头的简介里也说了, 嵌入式链表只管理数据, 内存的分配和释放是由使用者完成的。这样只要节点中包含一个统一的**SinglyLink链接区域**, 所有节点就可以被组织起来 72 | 73 | ```cpp 74 | struct ListNodeInt { 75 | SinglyLink link; // link区域 76 | int data; // data区域 77 | }; 78 | 79 | struct ListNodeDouble { 80 | SinglyLink link; // link区域 81 | double data; // data区域 82 | }; 83 | 84 | int main() { 85 | ListNodeInt node1; 86 | auto node2Ptr = new ListNodeInt(); 87 | 88 | insert(&(node1.link), (SinglyLink *)node2Ptr); 89 | 90 | auto linkPtr = (SinglyLink *)(&node1); 91 | while (linkPtr != nullptr) { 92 | auto nodePtr = (ListNodeInt *)linkPtr; 93 | std::cout << nodePtr->data << std::endl; 94 | linkPtr = linkPtr->next; 95 | } 96 | delete node2Ptr; 97 | } 98 | ``` 99 | 100 | 嵌入式链表, 可以**忽视**一个节点中除去link以外的数据。通过操作每个节点中link对链表做增加、删除和遍历的操作。在循环遍历链表时, link下面的数据类型的处理是交给使用这显示控制的, 即通过类型转换把link类型转为它本身的节点或数据类型, 进而区访问这个节点真实携带的数据信息。而这些数据的结构、大小等细节链表操作是不关心的, 它只关注对link区域的处理 101 | 102 | ### 优点 103 | 104 | **C语法实现通用链表** 105 | 106 | 不需要使用复杂的代码生成技术和范型编程支持, 就可以实现高效的通用数据结构 107 | 108 | **性能更好** 109 | 110 | 对于可变数据, 不使用二次分配内存的方式。link区域和data区域是位于同一块连续内存上, Cache更友好(相对两次分配)。同时相对于std::list在链表数据迁移的时候不需要额外释放和分配内存。 111 | 112 | **节点可位于多个链表** 113 | 114 | 一个节点可以同时位于多个链表中。如: 一个Task节点可以同时位于eventList和runList中 115 | 116 | ```cpp 117 | struct Task { 118 | SinglyLink eventList; 119 | // ... 120 | SinglyLink runList; 121 | }; 122 | ``` 123 | 124 | ## 设计/使用技巧 125 | 126 | 虽然上面介绍了嵌入式链表的总体设计思想——**只关心统一的链表操作**。而**数据类型**和**内存分配**的处理上会有些许不同, 下面就介绍三种经典的处理方法: 127 | 128 | ### 通信库 - 组合式 129 | 130 | 在很多消息通信的场景, 每个消息携带的数据量可能是不一样的。管理消息的链表, 可以通过组合的形式把Link域和变长数据域的内存做**物理拼接** 131 | 132 | ```cpp 133 | template 134 | struct Msg { 135 | int size; 136 | char data[N]; 137 | 138 | static void init(void *msg) { 139 | reinterpret_cast(msg)->size = N; 140 | // fill data 141 | } 142 | }; 143 | 144 | int main() { 145 | auto node1 = (SinglyLink *) malloc(sizeof(SinglyLink) + sizeof(Msg<1024>)); 146 | auto node2 = (SinglyLink *) malloc(sizeof(SinglyLink) + sizeof(Msg<1024 * 3>)); 147 | auto node3 = (SinglyLink *) malloc(sizeof(SinglyLink) + sizeof(Msg<1024 * 2>)); 148 | 149 | Msg<1024>::init(node1 + 1); 150 | Msg<1024 * 3>::init(node2 + 1); 151 | Msg<1024 * 2>::init(node3 + 1); 152 | 153 | SinglyLink msg_list; 154 | 155 | insert(&msg_list, node1); 156 | insert(&msg_list, node2); 157 | insert(&msg_list, node3); 158 | 159 | //... 160 | 161 | free(node1); 162 | free(node2); 163 | free(node3); 164 | return 0; 165 | } 166 | ``` 167 | 在使用malloc分配内存时, 申请的大小是`sizeof(link) + sizeof(data)`, 这样在物理上数据和只有link的嵌入式链表的节点在一块连续的内存上。在链表视角相当于 在每个节点下面挂载了一个**隐藏**的消息数据 168 | 169 | ```bash 170 | +-------+ +-------+ +-------+ 171 | List: | next | -> | next | -> ... -> | next | 172 | +-------+ +-------+ +-------+ 173 | +-------+ +-------+ +-------+ 174 | Data: |payload| | | | | 175 | +-------+ |payload| |payload| 176 | | | +-------+ 177 | +-------+ 178 | ``` 179 | 180 | 这样可以使得每个链表节点的消息负载(长度)是可变的。数据的使用者解析时只需要对节点地址做1单位的偏移, 就能去解析数据的结构/格式。并由于link和data是通过一次申请来分配的, 所以在解析失败或节点释放的时候可以直接通过`free(nodePtr)`去释放内存块, 而不需要分两次释放 181 | 182 | ### Linux内核 - 嵌入式 183 | 184 | 在最开始介绍设计思想的时候使用的就是者中把`SinglyLink`"嵌入"到一个数据结构中的形式 185 | 186 | ```cpp 187 | struct MemBlock { 188 | SinglyLink link; 189 | int size; 190 | }; 191 | ``` 192 | 193 | 由于link是被嵌入数据结构的第一个数据成员(无继承), 所以从link到MemBlock和MemBlock到link的转换都比较方便, 可以直接通过强制类型转换来实现 194 | 195 | ```cpp 196 | auto mbPtr = new MemBlock; 197 | auto linkPtr = static_cast(mbPtr); 198 | //... 199 | auto mbPtr_tmp = static_cast(linkPtr); 200 | ``` 201 | 202 | 但是在内核源码中常可以见到如下的**嵌入方式** 203 | 204 | ```cpp 205 | struct Demo { 206 | char member1; 207 | int member2 208 | SinglyLink link; 209 | double member3; 210 | }; 211 | ``` 212 | link成员并不是结构的第一个成员, 这个时候怎么做地址和类型的相互转换呢? 213 | 214 | **成员偏移量计算** 215 | 216 | ```cpp 217 | #define offset_of(Type, member) ((size_t) & ((Type *)0)->member) 218 | 219 | int main() { 220 | auto offset = offset_of(Demo, link); 221 | /* 222 | demoPtr = (Demo *)0; 223 | linkPtr = &(demoPtr->link); 224 | offset = (size_t) linkPtr - 0; 225 | */ 226 | return 0; 227 | } 228 | ``` 229 | 230 | 这里通过把0转为一个Demo指针类型, 在取这个对象中link的地址。由于对象的基地址是0, 那么Demo中link成员的地址就是该成员相对Demo对象首地址的偏移量。由于中间设计到对空指针的操作, 虽然没有访问数据。但有些编译器上可能不生效或产生未知行为, 我们可以按照这个思路把0换成一个有效地址即可 231 | 232 | ```cpp 233 | #define offset_of(Type, member) \ 234 | []() { Type t; return ((size_t) & (&t)->member - (size_t)&t); } () 235 | 236 | int main() { 237 | auto offset = offset_of(Demo, link); 238 | /* 239 | auto offset_of_func = []() { 240 | Demo t; 241 | Demo ptr = &t; 242 | baseAddr = (size_t)ptr; 243 | memberAddr = (size_t) & (ptr->link); 244 | return memberAddr - baseAddr; 245 | }; 246 | offset = offset_of_func(); 247 | */ 248 | return 0; 249 | } 250 | ``` 251 | 252 | 以上只是为了解释**成员偏移量计算**的原理和可行性, 实际上编译器内部已经帮我们实现了这样的"函数", 我们直接使用就可以了 253 | 254 | ```cpp 255 | #include 256 | size_t offset = offsetof(Demo, link); 257 | ``` 258 | 259 | **通用类型转换操作** 260 | 261 | ```cpp 262 | #define to_link(nodePtr) (&((nodePtr)->link)) 263 | #define to_node(linkPtr, NodeType, member) \ 264 | (NodeType *)( (size_t)linkPtr - offset_of(NodeType, member) ) 265 | ``` 266 | 267 | 通过node指针转向link指针可以直接通过取成员地址实现, 而通过link指针**还原为原来的node指针**则需要上面介绍的**成员偏移量计算**工具的辅助。即: link指针 - link成员偏移量 -> 原数据结构地址 268 | 269 | ```cpp 270 | int main() { 271 | Demo nodeList; 272 | Demo node1, node2, node3 273 | 274 | insert(to_link(&nodeList), to_link(&node1)); 275 | insert(to_link(&nodeList), to_link(&node2)); 276 | insert(to_link(&nodeList), to_link(&node3)); 277 | 278 | // ... 279 | 280 | SinglyLink *p = nodeList.link.next; 281 | while (p != to_link(&nodeList)) { 282 | Demo *demo = to_node(p, Demo, link); 283 | std::cout << demo->member1 << std::endl; 284 | p = p->next; 285 | } 286 | return 0; 287 | } 288 | ``` 289 | 290 | ### V8引擎 - 继承式(C++) 291 | 292 | 继承的好处是, 子类指针可以向上转型, 自动完成从实际数据结构到link的转换。同时在link中通过模板来携带类型信息(并未有额外定义)。通过直接继承SinglyLink(编译期多态)来使用链表功能 293 | 294 | ```cpp 295 | template 296 | struct SinglyLink { 297 | struct T *next; 298 | }; 299 | 300 | struct Demo : public SinglyLink { 301 | int a; 302 | double b; 303 | }; 304 | ``` 305 | 306 | 这里的SinglyLink里只使用了Demo类型的指针, 并不会引起循环定义问题。 307 | 308 | ```cpp 309 | int main() { 310 | 311 | SinglyLink demoList; 312 | Demo d1, d2, d3 313 | 314 | insert(&demoList, &d1); 315 | insert(&demoList, &d2); 316 | insert(&demoList, &d3); 317 | 318 | for (auto it = demoList.next; it != nullptr; it = it->next) { 319 | std::cout << it->a << std::endl; 320 | } 321 | 322 | return 0; 323 | } 324 | ``` 325 | 326 | 这样实现的链表, 在节点遍历和数据访问上实现了一定的统一, 避免了使用to_link和to_node等类型转换操作 327 | 328 | 329 | ## 总结 330 | 331 | 本章节介绍了嵌入式(侵入式)链表的核心原理, 及常见的几种实现/使用方式。虽然嵌入式链表在性能和控制力上有一些优势, 但其使用上的复杂度对开发人员确是一种"负担"。所以, 它常出现在追求性能的系统编程场景, 而对于应用软件的开发, 往往封装度完整的链表(如:std::list)会更加适合。总的来说, 数据结构的选择是一个`trade-off`权衡的结果 -------------------------------------------------------------------------------- /book/src/chapter_05_slist.md: -------------------------------------------------------------------------------- 1 | # 单链表 - SLinkedList 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 链表 VS 数组 9 | - SLinkedList核心实现 10 | - 类型及成员定义 11 | - 数据初始化 12 | - BigFive - 行为控制 13 | - 常用函数和数据访问 14 | - 数据增删 15 | - SLinkedList迭代器实现 16 | - 迭代器定义 17 | - 数据访问与类指针行为 18 | - 迭代器的判等 19 | - 向前迭代/++操作 20 | - 支持范围for 21 | - 迭代器版 - 插入删除操作 22 | - 总结 23 | 24 | --- 25 | 26 | 单链表是一个内存离散型的数据结构。由于存储的每一个元素都是一个独立的内存块, 所以在插入和删除元素时的复杂度是O(1)。因此, 链表常用于需要频繁进行插入/删除的场景, 来避免其他元素移动/拷贝的开销。 27 | 28 | **数组 VS 链表** 29 | 30 | | \ | 数组 | 链表 | 31 | | --- | --- | --- | 32 | | **内存** | 连续 | 离散 | 33 | | **插入** | O(n) | O(1) | 34 | | **删除** | O(n) | O(1) | 35 | 36 | 链表相对于数组的优势不仅仅**插入/删除**的操作非常快, 而且由于链表的内存是离散的, 所以在增加元素扩容的时候不需要像数组一样需要一个连续的存下所有的元素的大内存块, 只要能满足每个元素的内存大小即可。这样就一定程度的避免了无法使用小内存块的问题 37 | 38 | ## SLinkedList核心实现 39 | 40 | ### 类型及数据成员定义 41 | 42 | 使用两个模板参数一个作为存储的数据类型, 一个作为分配节点的内存分配器。并且定义一个SLinkedListNode模板作为存储数据的链表节点: 它的第一个成员next表示指向下一个节点, 第二个成员data为模板的第一个参数类型, 用来存储实际的数据。同时在数据结构中使用using给节点起一个Node别名方便后续使用 43 | 44 | ```cpp 45 | template 46 | struct SLinkedListNode { 47 | SLinkedListNode *next; 48 | T data; 49 | }; 50 | 51 | template 52 | class SLinkedList { 53 | using Node = SLinkedListNode; 54 | private: 55 | int mSize_e; 56 | Node mHead_e; 57 | Node *mTailPtr_e; 58 | }; 59 | ``` 60 | 61 | 链表的数据成员中, 用Node类型定义一个链表的头节点, 方便对链表的管理。同时使用一个int类型的mSize_e来记录链表的长度, 避免每次都遍历链表来求解长度。最后, 为了单链表的尾插法的实现再加一个指向最后一个元素的节点指针mTailPtr_e。 62 | 63 | ### 数据成员的初始化 64 | 65 | **默认初始化** 66 | 67 | ```cpp 68 | d2ds::SLinkedList intList; 69 | ``` 70 | 71 | 默认初始化对应的构造函数, 需要完成数据成员的初始化。这里构造函数后, 初始化列表的形式对成员进行初始化。其中头节点mHead_e中的next指针和mTailPtr_e初始化为自己/头节点 -- 循环单链表 72 | 73 | ```cpp 74 | template 75 | class SLinkedList { 76 | public: 77 | SLinkedList() : mSize_e { 0 }, mHead_e { &mHead_e, T()}, mTailPtr_e { &mHead_e } { 78 | 79 | } 80 | }; 81 | ``` 82 | 83 | **列表初始化** 84 | 85 | ```cpp 86 | d2ds::SLinkedList intList = { 1, 2, 3 }; 87 | ``` 88 | 89 | 实现链表的列表初始化形式, 需要实现对应的插入操作 - push_back 90 | 91 | ```cpp 92 | template 93 | class SLinkedList { 94 | public: 95 | void push_back(const T &data) { 96 | 97 | auto nodePtr = static_cast(Alloc::allocate(sizeof(Node))); 98 | 99 | new (&(nodePtr->data)) T(data); 100 | nodePtr->next = &mHead_e; 101 | 102 | mTailPtr_e->next = nodePtr; 103 | mTailPtr_e = nodePtr; 104 | 105 | mSize_e++; 106 | } 107 | }; 108 | ``` 109 | 110 | push_back的实现主要分4个步骤 111 | 112 | - 1.使用Alloc, 分配一块节点大小的内存 113 | - 2.使用传进来的data对节点进行初始化, 并把节点的next指针指向头节点 114 | - 3.修改尾节点的next指向新节点, 并更新mTailPtr_e指向新的尾部节点 115 | - 4.size增加1 116 | 117 | ```cpp 118 | template 119 | class SLinkedList { 120 | public: 121 | SLinkedList(std::initializer_list list) : SLinkedList() { 122 | for (auto it = list.begin(); it != list.end(); it++) { 123 | push_back(*it); 124 | } 125 | } 126 | }; 127 | ``` 128 | 129 | 有了push_back实现后, 只需要遍历list中的元素并使用push_back到最后即可 130 | 131 | 132 | ### BigFive - 行为控制 133 | 134 | **析构实现** 135 | 136 | 析构函数主要是释放所有数据里的资源, 以及每个节点对应的内存块。每次都删除第一个节点, 直到链表为空`mHead_e.next == &mHead_e` 137 | 138 | ```cpp 139 | template 140 | class SLinkedList { 141 | public: 142 | ~SLinkedList() { 143 | while (mHead_e.next != &mHead_e) { 144 | Node *nodePtr = mHead_e.next; 145 | mHead_e.next = nodePtr->next; 146 | nodePtr->data.~T(); 147 | Alloc::deallocate(nodePtr, sizeof(Node)); 148 | } 149 | mSize_e = 0; 150 | mTailPtr_e = &mHead_e; 151 | } 152 | }; 153 | ``` 154 | 155 | **拷贝语义** 156 | 157 | 由于拷贝语义主要是资源的复制。拷贝构造的实现主要就是使用for遍历目标对象, 并使用push_back一个一个添加数据 158 | 159 | ```cpp 160 | template 161 | class SLinkedList { 162 | public: 163 | SLinkedList(const SLinkedList &dsObj) : SLinkedList() { 164 | for (Node *nodePtr = dsObj.mHead_e.next; nodePtr != &(dsObj.mHead_e);) { 165 | push_back(nodePtr->data); 166 | nodePtr = nodePtr->next; 167 | } 168 | } 169 | 170 | SLinkedList & operator=(const SLinkedList &dsObj) { 171 | if (this != &dsObj) { 172 | this->~SLinkedList(); 173 | for (Node *nodePtr = dsObj.mHead_e.next; nodePtr != &(dsObj.mHead_e);) { 174 | push_back(nodePtr->data); 175 | nodePtr = nodePtr->next; 176 | } 177 | } 178 | return *this; 179 | } 180 | }; 181 | ``` 182 | 183 | 拷贝赋值的主要实现思路和拷贝构造是一样的, 只需要额外做自赋值检查以及资源的提前释放即可 184 | 185 | **移动语义** 186 | 187 | 移动语义的实现的核心是转移链表的管理权, 而链表的管理的核心是: 188 | 189 | - mHead_e.next : 第一个节点的地址 190 | - mTailPtr_e : 最后一个节点的地址 191 | - mSize_e : 节点数量 192 | 193 | ```cpp 194 | template 195 | class SLinkedList { 196 | public: 197 | SLinkedList(SLinkedList &&dsObj) : SLinkedList() { 198 | mHead_e.next = dsObj.mHead_e.next; 199 | mTailPtr_e = dsObj.mTailPtr_e; 200 | mSize_e = dsObj.mSize_e; 201 | mTailPtr_e->next = &mHead_e; // Note: update tail node 202 | 203 | // reset 204 | dsObj.mHead_e.next = &(dsObj.mHead_e); 205 | dsObj.mTailPtr_e = &(dsObj.mHead_e); 206 | dsObj.mSize_e = 0; 207 | } 208 | 209 | SLinkedList & operator=(SLinkedList &&dsObj) { 210 | if (this != &dsObj) { 211 | this->~SLinkedList(); 212 | mHead_e.next = dsObj.mHead_e.next; 213 | mTailPtr_e = dsObj.mTailPtr_e; 214 | mSize_e = dsObj.mSize_e; 215 | mTailPtr_e->next = &mHead_e; 216 | 217 | // reset 218 | dsObj.mHead_e.next = &(dsObj.mHead_e); 219 | dsObj.mTailPtr_e = &(dsObj.mHead_e); 220 | dsObj.mSize_e = 0; 221 | } 222 | return *this; 223 | } 224 | }; 225 | ``` 226 | 227 | 不需要额外的内存分配和释放, 直接转移链表节点的地址。然后在把目标dsObj中的数据重置。这里有一个要注意的点是: **由于是循环单链表, 所以需要更新最后一个节点/尾节点的next指向新的头节点** 228 | 229 | ### 常用函数 230 | 231 | **size/empty** 232 | 233 | size的实现可以直接返回mSize_e的记录, 避免重新遍历链表进行计算。而empty直接判断头节点的next是不是指向自己即可 234 | 235 | ```cpp 236 | template 237 | class SLinkedList { 238 | public: 239 | int size() const { 240 | return mSize_e; 241 | } 242 | 243 | bool empty() const { 244 | return mHead_e.next == &mHead_e; 245 | } 246 | }; 247 | ``` 248 | 249 | **front/back** 250 | 251 | 访问第一个元素和最后一个元素可以直接通过头节点的next指针和尾指针来直接访问, 时间复杂度为O(1) 252 | 253 | ```cpp 254 | template 255 | class SLinkedList { 256 | public: 257 | T & front() { 258 | return mHead_e.next->data; 259 | } 260 | 261 | T & back() { 262 | return mTailPtr_e->data; 263 | } 264 | }; 265 | ``` 266 | 267 | **数据访问** 268 | 269 | ```cpp 270 | d2ds::SLinkedList intList = { 0, 1, 2, 3, 4, 5 }; 271 | for (int i = 0; i < intList.size(); i++) { 272 | d2ds_assert_eq(intList[i], i); 273 | } 274 | ``` 275 | 276 | 由于链表内存是离散的, 所以不支持随机访问。所以通过索引的方式访问元素的算法复杂度为O(n)。即每次都是通过遍历链表来找到对应位置的数据再返回(**链表数据结构一般不提供直接的索引访问方式**)。这里我们通过**模拟随机访问的方式**, 使用下标运算符来进行数据访问。 277 | 278 | ```cpp 279 | template 280 | class SLinkedList { 281 | public: 282 | T & operator[](int index) { 283 | // d2ds_assert(index < mSize_e && mSize != 0); 284 | Node *nodePtr = mHead_e.next; 285 | for (int i = 0; i < index; i++) { 286 | nodePtr = nodePtr->next; 287 | } 288 | return nodePtr->data; 289 | } 290 | }; 291 | ``` 292 | 293 | ### 数据增删 294 | 295 | **push_back/_pop_back** 296 | 297 | 通过这两个操作, 可以实现再数据结构尾部进行增删数据。其中push_back在最开始已经实现了。而对于单链表来说, 可以使用尾部指针来优化push_back的复杂度到O(1)。 而_pop_back的实现要麻烦很多。由于删除一个节点需要找到它的前一个节点, 所以需要先遍历链表找到最后一个节点的前一个节点。然后, 再删除尾节点并把内存给释放了。步骤可以总结如下: 298 | 299 | - 1.找到尾节点的前一个节点 300 | - 2.从链表中删除节点 301 | - 3.释放节点的数据和对应的内存 302 | - 4.size减1并更新尾部节点 303 | 304 | ```cpp 305 | template 306 | class SLinkedList { 307 | public: 308 | void _pop_back() { 309 | // assert(size() > 0); 310 | Node *nodePtr = &mHead_e; 311 | while (nodePtr->next != mTailPtr_e) { 312 | nodePtr = nodePtr->next; 313 | } 314 | // delete mTailPtr_e from list 315 | nodePtr->next = &mHead_e; 316 | // release 317 | mTailPtr_e->data.~T(); 318 | Alloc::deallocate(mTailPtr_e, sizeof(Node)); 319 | mSize_e--; 320 | 321 | mTailPtr_e = nodePtr; // update 322 | } 323 | }; 324 | ``` 325 | 326 | > 注: 可以重新实现mTailPtr_e, 让其指向倒数第二个节点来实现 327 | 328 | 329 | **push_front/pop_front** 330 | 331 | push_front的实现流程和push_back的实现类似, 只是插入节点的前置节点换成了头节点, 并且在push数据的时候要更新一下尾指针指向该节点 332 | 333 | ```cpp 334 | template 335 | class SLinkedList { 336 | public: 337 | void push_front(const T &data) { 338 | 339 | auto nodePtr = static_cast(Alloc::allocate(sizeof(Node))); 340 | new (&(nodePtr->data)) T(data); 341 | 342 | nodePtr->next = mHead_e.next; 343 | mHead_e.next = nodePtr; 344 | mSize_e++; 345 | 346 | if (mSize_e == 1) { 347 | mTailPtr_e = nodePtr; 348 | } 349 | } 350 | 351 | void pop_front() { 352 | Node *nodePtr = mHead_e.next; 353 | // delete from list 354 | mHead_e.next = nodePtr->next; 355 | // release 356 | nodePtr->data.~T(); 357 | Alloc::deallocate(nodePtr, sizeof(Node)); 358 | mSize_e--; 359 | 360 | if (mSize_e == 0) { 361 | mTailPtr_e = &mHead_e; // update 362 | } 363 | } 364 | }; 365 | ``` 366 | 367 | pop_front的实现相对于_pop_back, 减少了前置节点的查找过程。所以它实现的算法复杂度是O(1).通过头节点的next指针找到要删除的节点, 并把next更新到删除节点的下一个节点, 然后释放目标节点。同时, 这里也需要在删除到链表为空时, 重置mTailPtr_e尾部节点指针 368 | 369 | ## SLinkedList迭代器实现 370 | 371 | 迭代器是一种访问数据的设计模式 - 它把数据的访问抽象成统一的操作/行为。如: 372 | 373 | - `*` : 取数据 374 | - `->` : 访问成员 375 | - `++` : 移动到下一个数据 376 | - `--` : 返回上一个数据 377 | 378 | ### 迭代器类型定义 379 | 380 | 在迭代器中定义一个指向目标节点的指针, 用来访问数据以及迭代到下一个数据。所以它的构造函数的输入只需要目标节点的指针即可 381 | 382 | ```cpp 383 | template 384 | struct SLinkedListIterator { 385 | using Node = SLinkedListNode; 386 | SLinkedListIterator() : mNodePtr { nullptr } { } 387 | SLinkedListIterator(Node *nodePtr) : mNodePtr { nodePtr } { } 388 | Node *mNodePtr; 389 | }; 390 | ``` 391 | 392 | 同时在SLinkedList定义一个数据结构对应的具体类型(T)的迭代器别名, 方便后面的使用 393 | 394 | ```cpp 395 | template 396 | class SLinkedList { 397 | public: 398 | using Iterator = SLinkedListIterator; 399 | } 400 | ``` 401 | 402 | ### 数据访问与类指针行为 403 | 404 | ```cpp 405 | struct MyObj { 406 | char a; 407 | int b; 408 | float c; 409 | }; 410 | MyObj obj {'a', 1, 1.1}; 411 | 412 | d2ds::SLinkedListNode node; 413 | node.data = obj; 414 | 415 | d2ds::SLinkedList::Iterator iterator(&node); 416 | d2ds_assert_eq(iterator->a, obj.a); 417 | d2ds_assert_eq(iterator->b, obj.b); 418 | d2ds_assert_eq(iterator->c, obj.c); 419 | 420 | d2ds_assert_eq(*(iterator).c, 1.1f); 421 | ``` 422 | 423 | 迭代器的本质是一个类, 但它使用起来就像是所管理数据类型的指针。可以通过`->`运算符访问对应的成员数据。同时也可以通过`*`元算符号访问到对象 424 | 425 | ```cpp 426 | template 427 | struct SLinkedListIterator { 428 | T * operator->() { 429 | return &(mNodePtr->data); 430 | } 431 | 432 | T & operator*() { 433 | return mNodePtr->data; 434 | } 435 | }; 436 | ``` 437 | 438 | 实现这种类指针的行为, 只需要重载并实现`operator*`和`operator->` 439 | 440 | - `operator*` : 返回数据的引用 441 | - `operator->` : 返回数据的指针 442 | 443 | ### 迭代器的判等 444 | 445 | ```cpp 446 | d2ds::SLinkedListNode node; 447 | d2ds::SLinkedList::Iterator iterator1(&node); 448 | d2ds::SLinkedList::Iterator iterator2(&node); 449 | d2ds::SLinkedList::Iterator iterator3(nullptr); 450 | 451 | d2ds_assert(iterator1 == iterator2); 452 | d2ds_assert(iterator2 != iterator3); 453 | ``` 454 | 455 | 对于单链表的迭代器, 不用通过判断节点中的数据是否相等, 而是通过直接判断迭代器中管理的节点地址是否相等, 这样即可以判断数据有可以判断是否属于同一个链表 456 | 457 | ```cpp 458 | template 459 | struct SLinkedListIterator { 460 | bool operator==(const SLinkedListIterator &it) const { 461 | return mNodePtr == it.mNodePtr; 462 | } 463 | 464 | bool operator!=(const SLinkedListIterator &it) const { 465 | return mNodePtr != it.mNodePtr; 466 | } 467 | }; 468 | ``` 469 | 470 | ### 向前迭代/++操作 471 | 472 | ```cpp 473 | d2ds_assert(++iterator1 == iterator3); 474 | d2ds_assert(iterator2++ != iterator3); 475 | d2ds_assert(iterator2 == iterator3); 476 | ``` 477 | 478 | 单链表只有一个next指向下一个节点, 所以对应的迭代器也只能向前迭代。所以这里只需要实现迭代器的++操作 479 | 480 | - `Self & operator++()` : 对应的是前置++ 481 | - `Self operator++(int)` : 对应的是后置++ 482 | 483 | ```cpp 484 | template 485 | struct SLinkedListIterator { 486 | SLinkedListIterator & operator++() { 487 | mNodePtr = mNodePtr->next; 488 | return *this; 489 | } 490 | 491 | SLinkedListIterator operator++(int) { 492 | auto old = *this; 493 | mNodePtr = mNodePtr->next; 494 | return old; 495 | } 496 | }; 497 | ``` 498 | 499 | 其中前置++操作的实现, 只需要修改节点指针让其指向下一个节点, 然后返回自己。而由于后置++的特性(在下一条语句中才生效, 当前语句不变), 所以需要先保留一份旧数据, 然会更新mNodePtr指向下一个节点。最后返回旧数据, 这样就能实现 - **在下一条语句生效的性质** 500 | 501 | 502 | ### 支持范围for 503 | 504 | ```cpp 505 | d2ds::SLinkedList intList = { 5, 4, 3, 2, 1 }; 506 | 507 | auto it = intList.begin(); 508 | 509 | d2ds_assert_eq(*it, 5); 510 | it++; d2ds_assert_eq(*it, 4); 511 | it++; d2ds_assert_eq(*it, 3); 512 | it++; d2ds_assert_eq(*it, 2); 513 | it++; d2ds_assert_eq(*it, 1); 514 | 515 | d2ds_assert(++it == intList.end()); 516 | 517 | int tmp = 5; 518 | for (auto val : intList) { 519 | d2ds_assert_eq(val, tmp); 520 | tmp--; 521 | } 522 | ``` 523 | 524 | 在有了迭代器后, 就可以通过实现数据结构的begin/end来支持范围for循环 525 | 526 | ```cpp 527 | template 528 | class SLinkedList { 529 | public: 530 | Iterator begin() { 531 | return mHead_e.next; 532 | } 533 | 534 | Iterator end() { 535 | return &mHead_e; 536 | } 537 | }; 538 | ``` 539 | 540 | begin返回第一个节点对应的迭代器, 由于`Iterator`的构造函数接受Node类型的指针, 所以直接返回节点的指针就可以自动匹配返回值的迭代器类型。而end迭代器是指向最后节点的下一个节点 -- 头节点。用含头节点的地址的迭代器标识结束 541 | 542 | ### 迭代器版本 - 插入删除操作 543 | 544 | ```cpp 545 | d2ds::SLinkedList intList = { 5, 4, 3, 2, 1 }; 546 | auto it = intList.begin(); 547 | intList.erase_after(it); 548 | ++it; d2ds_assert_eq(*it, 3); 549 | for (int val : intList) { 550 | std::cout << " " << val; 551 | } 552 | /* 553 | [D2DS LOGI]: - ✅ | *it == 3 (3 == 3) 554 | 5 3 2 1 555 | */ 556 | ``` 557 | 558 | 由于单链表的节点只有next指针的限制, 只能删除当前迭代器的下一个节点。所以只能实现erase_after/insert_after。并且他们的实现步骤和push/pop操作的流程是一样的, 不一样的是前驱节点由传入的迭代器中获取的 559 | 560 | ```cpp 561 | template 562 | class SLinkedList { 563 | public: 564 | void erase_after(Iterator pos) { 565 | // assert(pos.mNodePtr->next != &mHead_e); 566 | Node *nodePtr = pos.mNodePtr->next; 567 | pos.mNodePtr->next = nodePtr->next; 568 | 569 | nodePtr->data.~T(); 570 | Alloc::deallocate(nodePtr, sizeof(Node)); 571 | mSize_e--; 572 | 573 | if (pos.mNodePtr->next == &mHead_e) { 574 | mTailPtr_e = pos.mNodePtr->next; 575 | } 576 | } 577 | 578 | void insert_after(Iterator pos, const T &data) { 579 | auto nodePtr = static_cast(Alloc::allocate(sizeof(Node))); 580 | new (&(nodePtr->data)) T(data); 581 | 582 | nodePtr->next = pos.mNodePtr->next; 583 | pos.mNodePtr->next = nodePtr; 584 | mSize_e++; 585 | 586 | if (nodePtr->next == &mHead_e) { 587 | mTailPtr_e = nodePtr; 588 | } 589 | } 590 | }; 591 | ``` 592 | 593 | ## 总结 594 | 595 | 本章先是介绍了链表和数组的区别, 然后又从单链表的节点定义定义开始开始一步一步实现数据初始化、拷贝/移动语义、常用的函数、以及单链表一般不直接提供的功能(pop_back/operator[]), 从实现的角度来解释为什么不提的原因。最后, 又介绍了链表数据结构对应的迭代器的实现。其中关键的是对各种运算符(`*`/`->`/...)进行重载来实现类指针的行为。从单链表的实现上看, 它自身具有一定的限制在给定一个节点只能向下寻找, 这限制了迭代器的--操作的实现。并且不能直接删除当前节点(需要前驱节点)。下一章我们将开始介绍链表中最常使用的双链表的实现, 它能很好解决单链表的一些问题。 -------------------------------------------------------------------------------- /book/src/chapter_06_embedded_dlist.md: -------------------------------------------------------------------------------- 1 | # 嵌入式双链表 - DoublyLink 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 单链表 VS 双链表 9 | - DoublyLink核心实现 10 | - 定义和初始化 11 | - 插入和删除操作 12 | - 逆序遍历 13 | - 使用示例 14 | - 组合式 15 | - 嵌入式 16 | - 继承式 17 | - 总结 18 | 19 | --- 20 | 21 | 前面介绍过了**嵌入式单链表**, 但其并不支持向前查找。本章节就将介绍链表中最常用的双链表对应的"无数据域"的 -- **嵌入式双链表** 22 | 23 | **内存使用对比** 24 | 25 | 在双链表中, 每一个节点都比单链表多了一个指向前一个节点的指针`prev`, 所以对应的在存储相同数据量的元素N时, 内存的使用要比单链表多N x 8字节(设:指针占8字节)。但多数时候大家都原因花费这个内存的代价来换取使用的便利 26 | 27 | ```cpp 28 | struct SinglyLink { 29 | struct SinglyLink *next; 30 | }; 31 | 32 | struct DoublyLink { 33 | struct DoublyLink *prev, *next; 34 | }; 35 | ``` 36 | 37 | **单链表的局限性** 38 | 39 | 双链表引入的这个`prev`指针就是为了解决单链表无法向前查找的问题, 这里我们可以用删除当前节点的操作来演示使用场景 40 | 41 | - target: 我们需要删除的节点 42 | - del: 链表删除操作 43 | 44 | ```cpp 45 | SinglyLink::del(prevNodePtr ?, &target); 46 | DoublyLink::del(target.prev, &target); 47 | ``` 48 | 49 | 通过代码可以直观的看出, 由于**从链表中删除节点需要知道该节点的前一个节点**, 所以从单链中删除target, 而找到target的前一个节点并不能很高效的完成。对于双链表只需要O(1)的复杂度。 50 | 51 | ## DoublyLink核心实现 52 | 53 | 采用节点 + 静态成员函数的方式进行定义DoublyLink 54 | 55 | ### 定义和初始化 56 | 57 | 只包含两个指针, 一个指向前驱节点, 一个指向后继节点。节点的初始化就是把这两个指针指向自己。可以把这样的节点视为一个没有数据的链表头结点, 或一个不在链表中的节点 58 | 59 | ```cpp 60 | struct DoublyLink { 61 | struct DoublyLink *prev, *next; 62 | 63 | static void init(struct DoublyLink *target) { 64 | target->prev = target->next = target; 65 | } 66 | }; 67 | ``` 68 | 69 | ### 插入和删除操作 70 | 71 | 双链表的插入操作, 需要处理三个节点中的4个指针。它们分别是: 72 | 73 | - target的next指针 - 指向prev节点的next 74 | - target的prev指针 - 指向prev节点 75 | - prev的下一个节点的prev指针 - 指向target 76 | - prev的next指针 - 指向target 77 | 78 | ```cpp 79 | struct DoublyLink { 80 | static void insert(DoublyLink *prev, DoublyLink *target) { 81 | target->next = prev->next; 82 | target->prev = prev; 83 | prev->next->prev = target; 84 | prev->next = target; 85 | } 86 | 87 | static void del(DoublyLink *prev, DoublyLink *target) { 88 | prev->next = target->next; 89 | target->next->prev = prev; 90 | } 91 | }; 92 | ``` 93 | 94 | 双链表的删除操作, 只需要更新删除节点的 95 | 96 | - 前一个节点的next指针 - 指向target的下一个节点 97 | - 后一个节点的prev指针 - 指向target的前一个节点 98 | 99 | > 注: 100 | > 对于双链表的del接口, 其实可以不用显示的传前一个节点的指针prev。但这里为了和单链表的节点格式进行统一保留了prev。等价形式: 101 | > - del(target) 102 | > - del(target->prev, target) 103 | 104 | 105 | ### 逆序遍历 106 | 107 | 通过prev指针, 从后向前遍历将变的异常容易。如下的逆序遍历for循环的代码框架几乎和向后遍历一致的 108 | 109 | ```cpp 110 | // Node : linkPtr = malloc(sizeof(Link) + sizeof(Data)) 111 | for (auto linkPtr = head.prev; linkPtr != &head; linkPtr = linkPtr->prev) { 112 | MyData *dataPtr = reinterpret_cast(linkPtr + 1); 113 | // do something 114 | } 115 | ``` 116 | 117 | ## 使用示例 118 | 119 | 在**嵌入式单链表**章节从设计者视角详细的介绍了常用的使用示例。本章将中从实用角度出发来介绍。通过使用**组合式、嵌入式、继承式**的方式来实现同样一个功能来进行对比 120 | 121 | ### 组合式 122 | 123 | ```cpp 124 | // embedded-dlist.2.cpp - write 125 | // 126 | // 描述: 127 | // 嵌入式双链表 - 组合式 128 | // 129 | // 目标/要求: 130 | // - 在对应的SHOW_YOUR_CODE代码块实现 逆序遍历 和 链表的释放 131 | // - 通过所有编译器检测 和 断言 132 | // 133 | 134 | #include "common/common.hpp" 135 | 136 | #include "exercises/linked-list/EmbeddedList.hpp" 137 | 138 | using d2ds::DefaultAllocator; 139 | 140 | struct MyData { 141 | int id; 142 | char data; 143 | }; 144 | 145 | int main() { 146 | 147 | d2ds::DefaultAllocator::debug() = true; 148 | 149 | d2ds::DoublyLink head; 150 | 151 | d2ds::DoublyLink::init(&head); 152 | 153 | for (int i = 0; i < 10; i++) { 154 | auto linkPtr = ( d2ds::DoublyLink* ) d2ds::DefaultAllocator::malloc(sizeof(d2ds::DoublyLink) + sizeof(MyData)); 155 | d2ds::DoublyLink::init(linkPtr); 156 | auto dataPtr = (MyData *)(linkPtr + 1); 157 | dataPtr->id = i; 158 | dataPtr->data = 'a' + i; 159 | d2ds::DoublyLink::insert(&head, linkPtr); 160 | } 161 | 162 | dstruct::Stack dataStack; 163 | for (auto linkPtr = head.next; linkPtr != &head; linkPtr = linkPtr->next) { 164 | auto dataPtr = reinterpret_cast(linkPtr + 1); 165 | dataStack.push(*dataPtr); 166 | } 167 | 168 | SHOW_YOUR_CODE({ // reverse traverse 169 | for (auto linkPtr = head.prev; linkPtr != &head; linkPtr = linkPtr->prev) { 170 | MyData *dataPtr = reinterpret_cast(linkPtr + 1); 171 | 172 | DONT_CHANGE( 173 | auto myData = dataStack.top(); 174 | d2ds_assert_eq(dataPtr->id, myData.id); 175 | d2ds_assert_eq(dataPtr->data, myData.data); 176 | dataStack.pop(); 177 | ) 178 | } 179 | }) 180 | 181 | d2ds_assert(dataStack.empty()); 182 | 183 | SHOW_YOUR_CODE({ // use DefaultAllocator::free(addr) to release 184 | d2ds::DoublyLink *target = head.next; 185 | while (target != &head) { 186 | d2ds::DoublyLink::del(&head, target); 187 | DefaultAllocator::free(target); 188 | target = head.next; 189 | } 190 | }) 191 | 192 | d2ds_assert(head.next == &head); 193 | 194 | XLINGS_WAIT 195 | 196 | return 0; 197 | } 198 | ``` 199 | 200 | ### 嵌入式 201 | 202 | ```cpp 203 | // embedded-dlist.3.cpp - write 204 | // 205 | // 描述: 206 | // 嵌入式双链表 - 嵌入式 207 | // 208 | // 目标/要求: 209 | // - 在对应的SHOW_YOUR_CODE代码块实现 逆序遍历 和 链表的释放 210 | // - 通过所有编译器检测 和 断言 211 | // 212 | 213 | #include "common/common.hpp" 214 | 215 | #include "exercises/linked-list/EmbeddedList.hpp" 216 | 217 | using d2ds::DefaultAllocator; 218 | 219 | struct MyData { 220 | d2ds::DoublyLink link; 221 | int id; 222 | char data; 223 | }; 224 | 225 | int main() { 226 | 227 | d2ds::DefaultAllocator::debug() = true; 228 | 229 | d2ds::DoublyLink head; 230 | 231 | d2ds::DoublyLink::init(&head); 232 | 233 | for (int i = 0; i < 10; i++) { 234 | auto dataPtr = (MyData *) d2ds::DefaultAllocator::malloc(sizeof(MyData)); 235 | d2ds::DoublyLink::init(&(dataPtr->link)); 236 | dataPtr->id = i; 237 | dataPtr->data = 'a' + i; 238 | d2ds::DoublyLink::insert(&head, &(dataPtr->link)); 239 | } 240 | 241 | dstruct::Stack dataStack; 242 | for (auto linkPtr = head.next; linkPtr != &head; linkPtr = linkPtr->next) { 243 | auto dataPtr = reinterpret_cast(linkPtr); 244 | dataStack.push(*dataPtr); 245 | } 246 | 247 | SHOW_YOUR_CODE({ // reverse traverse 248 | for (auto linkPtr = head.prev; linkPtr != &head; linkPtr = linkPtr->prev) { 249 | MyData *dataPtr = reinterpret_cast(linkPtr); 250 | 251 | DONT_CHANGE( 252 | auto myData = dataStack.top(); 253 | d2ds_assert_eq(dataPtr->id, myData.id); 254 | d2ds_assert_eq(dataPtr->data, myData.data); 255 | dataStack.pop(); 256 | ) 257 | } 258 | }) 259 | 260 | d2ds_assert(dataStack.empty()); 261 | 262 | SHOW_YOUR_CODE({ // use DefaultAllocator::free(addr) to release 263 | d2ds::DoublyLink *target = head.next; 264 | while (target != &head) { 265 | d2ds::DoublyLink::del(&head, target); 266 | DefaultAllocator::free(target); 267 | target = head.next; 268 | } 269 | }) 270 | 271 | d2ds_assert(head.next == &head); 272 | 273 | XLINGS_WAIT 274 | 275 | return 0; 276 | } 277 | ``` 278 | 279 | ### 继承式 280 | 281 | ```cpp 282 | // embedded-dlist.4.cpp - write 283 | // 284 | // 描述: 285 | // 嵌入式双链表 - 继承式 286 | // 287 | // 目标/要求: 288 | // - 在对应的SHOW_YOUR_CODE代码块实现 逆序遍历 和 链表的释放 289 | // - 通过所有编译器检测 和 断言 290 | // 291 | 292 | #include "common/common.hpp" 293 | 294 | #include "exercises/linked-list/EmbeddedList.hpp" 295 | 296 | using d2ds::DefaultAllocator; 297 | 298 | template 299 | struct ENode : d2ds::DoublyLink { 300 | ENode * prev() { 301 | return static_cast(d2ds::DoublyLink::prev); 302 | } 303 | 304 | ENode * next() { 305 | return static_cast(d2ds::DoublyLink::next); 306 | } 307 | 308 | T * data() { 309 | return static_cast(this); 310 | } 311 | }; 312 | 313 | struct MyData : ENode { 314 | int id; 315 | char data; 316 | }; 317 | 318 | int main() { 319 | 320 | d2ds::DefaultAllocator::debug() = true; 321 | 322 | ENode head; 323 | 324 | d2ds::DoublyLink::init(&head); 325 | 326 | for (int i = 0; i < 10; i++) { 327 | auto dataPtr = (MyData *) d2ds::DefaultAllocator::malloc(sizeo(MyData)); 328 | d2ds::DoublyLink::init(dataPtr); 329 | dataPtr->id = i; 330 | dataPtr->data = 'a' + i; 331 | d2ds::DoublyLink::insert(&head, dataPtr); 332 | } 333 | 334 | dstruct::Stack dataStack; 335 | for (auto nodePtr = head.next(); nodePtr != &head; nodePtr = nodePtr->nex()) { 336 | auto dataPtr = nodePtr->data(); 337 | dataStack.push(*dataPtr); 338 | } 339 | 340 | SHOW_YOUR_CODE({ // reverse traverse 341 | for (auto nodePtr = head.prev(); nodePtr != &head; nodePtr = nodePtr->prev()) { 342 | MyData *dataPtr = nodePtr->data(); 343 | 344 | DONT_CHANGE( 345 | auto myData = dataStack.top(); 346 | d2ds_assert_eq(dataPtr->id, myData.id); 347 | d2ds_assert_eq(dataPtr->data, myData.data); 348 | dataStack.pop(); 349 | ) 350 | } 351 | }) 352 | 353 | d2ds_assert(dataStack.empty()); 354 | 355 | SHOW_YOUR_CODE({ // use DefaultAllocator::free(addr) to release 356 | ENode *target = head.next(); 357 | while (target != &head) { 358 | d2ds::DoublyLink::del(&head, target); 359 | DefaultAllocator::free(target); 360 | target = head.next(); 361 | } 362 | }) 363 | 364 | d2ds_assert(head.next() == &head); 365 | 366 | XLINGS_WAIT 367 | 368 | return 0; 369 | } 370 | ``` 371 | 372 | ## 总结 373 | 374 | 本章节是基于**嵌入式单链表**章节继续来介绍链表中最常用的双链表对应的嵌入式双链表的实现和使用示例。显示简单介绍了双链表的引入原因(相对与单链表)。然后, 又以同一个示例展示了嵌入式双链表的不同的使用方式 -------------------------------------------------------------------------------- /book/src/dslings.md: -------------------------------------------------------------------------------- 1 | # d2ds-dslings | 代码练习 2 | 3 | 通过使用dslings自动化检测的**编译器驱动开发模式**来进行代码练习 4 | 5 | ## 代码下载 6 | 7 | ```bash 8 | git clone --recursive git@github.com:Sunrisepeak/d2ds.git 9 | ``` 10 | 11 | ## 安装xmake 12 | 13 | **linux/macos** 14 | 15 | > 使用bash执行tools目录下的安装脚本 16 | 17 | ```bash 18 | bash tools/install.unix.sh 19 | ``` 20 | 21 | **windows** 22 | 23 | > 执行tools目录下的安装脚本 或 直接双击运行 24 | 25 | ```bash 26 | tools\install.win.bat 27 | ``` 28 | 29 | ## dslings使用流程 30 | 31 | ### 第一步: 开启代码检测 32 | 33 | 在本地[d2ds仓库](https://github.com/Sunrisepeak/d2ds)的根目录执行如下命令 34 | 35 | ```bash 36 | xmake dslings 37 | ``` 38 | 39 | 程序开始自动的测试/校验, 直到一个没有完成(或检测不通过)的练习代码。dslings会在控制台输出提示信息。如: 40 | 41 | - 练习进度 42 | - 练习的代码路径信息 43 | - 编译期错误信息提示 44 | - 运行时错误提示 45 | 46 | > **注** 47 | > 48 | > - 执行命令前, 请确保电脑已经配置了C++环境, 并安装了[xmake](https://github.com/xmake-io/xmake)构建工具 49 | > 50 | > - 建议使用vscode作为代码练习的编辑器, 用**ctrl+鼠标左键**点击路径就可以自动转跳到目标位置 51 | > 52 | > - 由于vscode的C/C++插件会检测文件变化, 可以参考[issue-5](https://github.com/Sunrisepeak/d2ds/issues/5)来避免卡顿 53 | 54 | ### 第二步: 根据dslings提示, 找到对应的练习代码 55 | 56 | ```bash 57 | 🌏Progress: [>-----------------------------] 0/29 58 | 59 | [Target: 0.dslings-0] 60 | 61 | ❌ Error: Compilation/Running failed for tests/dslings.0.cpp: 62 | 63 | The code exist some error! 64 | 65 | Output: 66 | ==================== 67 | [ 50%]: cache compiling.release tests/dslings.0.cpp 68 | error: tests/dslings.0.cpp:20:11: error: ‘MaxValue’ is not a member of ‘d2ds’ 69 | 20 | d2ds::MaxValue mVal(2); 70 | | ^~~~~~~~ 71 | In file included from tests/dslings.0.cpp:14: 72 | tests/dslings.0.cpp:22:20: error: ‘mVal’ was not declared in this scope 73 | 22 | d2ds_assert_eq(mVal.get(), 2); 74 | | ^~~~ 75 | ./common/common.hpp:28:9: note: in definition of macro ‘d2ds_assert_eq’ 76 | 28 | if (a != b) {\ 77 | | ^ 78 | > in tests/dslings.0.cpp 79 | 80 | 81 | ==================== 82 | 83 | Homepage: https://github.com/Sunrisepeak/d2ds-courses 84 | ``` 85 | 86 | 执行命令后dslings程序会停在最近的未完成的练习, 并会"实时"检测和这个练习相关的数据结构代码的实现。我们可以根据dslings在控制台的输出找到对应的练习代码: 87 | 88 | ```cpp 89 | // dslings.0.cpp - readonly 90 | // 91 | // 描述: 92 | // 通过实现一个MaxVal类型(保存最大值), 来介绍dslings的"编译器驱动开发" 93 | // 即根据编译器的错误提示来完成这个训练流程的演示Demo, 并且通常为了降低难度会把一个'数据结构'的实现分成多个检测模块. 94 | // 如: dslings.0.cpp dslings.1.cpp dslings.2.cpp 95 | // 96 | // 目标/要求: 97 | // - 不修改该代码检测文件 98 | // - 在exercises/dslings.hpp中完成你的代码设计 99 | // - 通过所有编译器检测 和 断言 100 | // 101 | 102 | #include "common/common.hpp" 103 | 104 | #include "exercises/dslings.hpp" 105 | 106 | int main() { 107 | 108 | d2ds::MaxValue mVal(2); 109 | 110 | d2ds_assert_eq(mVal.get(), 2); 111 | 112 | HONLY_LOGI_P("Hello D2DS!"); 113 | 114 | XLINGS_WAIT 115 | 116 | return 0; 117 | } 118 | ``` 119 | 120 | ### 第三步: 阅读练习描述和要求并完成练习 121 | 122 | 根据对应的练习代码中给的描述和要求完成该练习, 过程中可以结合dslings在控制台的提示来进行相关数据结构练习的代码设计。当正确完成代码后, dslings就会更新控制的输出给出对应的提示 123 | 124 | ```bash 125 | 🌏Progress: [>-----------------------------] 0/29 126 | 127 | [Target: 0.dslings-0] 128 | 129 | ✅ Successfully ran tests/dslings.0.cpp! 130 | 131 | 🎉 The code is compiling! 🎉 132 | 133 | Output: 134 | ==================== 135 | [D2DS LOGI]: - ✅ | mVal.get() == 2 (2 == 2) 136 | [D2DS LOGI]: - Hello D2DS! 137 | [D2DS LOGW]: main: tests/dslings.0.cpp:26 - 🥳 Delete the XLINGS_WAIT to continue... 138 | 139 | ==================== 140 | 141 | Homepage: https://github.com/Sunrisepeak/d2ds-courses 142 | ``` 143 | 144 | ### 第四步: 注释XLINGS_WAIT, 进入下一个练习 145 | 146 | 根据dslings在控制台的提示信息, 找到`tests/dslings.0.cpp:26`, 并进行注释或者删除。 147 | dslings就会进入下一个练习并进行检测 148 | 149 | ```bash 150 | int main() { 151 | 152 | d2ds::MaxValue mVal(2); 153 | 154 | d2ds_assert_eq(mVal.get(), 2); 155 | 156 | HONLY_LOGI_P("Hello D2DS!"); 157 | 158 | XLINGS_WAIT 159 | 160 | return 0; 161 | } 162 | ``` 163 | 164 | ## 工具 | 快捷命令 165 | 166 | ### xmake dslings 167 | 168 | 从指定练习开始检测, 支持模糊匹配 169 | 170 | ```bash 171 | # xmake dslings 默认从第一开始检测 172 | xmake dslings -s [target] 173 | #xmake dslings -s vector 174 | ``` 175 | 176 | ### xmake d2ds 177 | 178 | 查看版本信息 179 | 180 | ```bash 181 | xmake d2ds info 182 | ``` 183 | 184 | 查看工具使用 185 | 186 | ```bash 187 | xmake d2ds help 188 | ``` 189 | 190 | 同步(主仓库)最新代码 191 | 192 | ```bash 193 | xmake d2ds update 194 | ``` -------------------------------------------------------------------------------- /book/src/other/0_ds_base.md: -------------------------------------------------------------------------------- 1 | # 数据结构的基本概念 2 | 3 | 数据结构是计算机科学中存储、组织数据的方式,以便可以有效地访问和修改。它们是编程中的核心概念,决定了数据的逻辑结构以及数据之间的关系。数据结构的选择对程序的性能有很大影响,包括内存使用效率和执行速度。 4 | 5 | ## 线性数据结构 6 | 7 | 线性数据结构中的元素以线性顺序排列,这意味着它们只有一个前驱和一个后继。常见的线性数据结构包括: 8 | 9 | - **数组:** 一组固定大小的元素序列,通常是相同类型的数据。 10 | - **链表:** 由节点组成,每个节点包含数据和指向下一个节点的引用。 11 | - **栈:** 遵循后进先出(LIFO)原则的集合,只能在一端(顶部)进行数据的添加和删除。 12 | - **队列:** 遵循先进先出(FIFO)原则的集合,数据从后端添加,在前端移除。 13 | - **双端队列(Deque):** 一种允许从两端添加和移除元素的队列。 14 | 15 | 16 | ## 非线性数据结构 17 | 18 | 在非线性数据结构中,数据元素不是以线性方式排列,每个元素可能有多个前驱和后继。常见的非线性数据结构包括: 19 | 20 | ### 树 21 | > 包含节点和边的集合,其中每个节点最多只有一个父节点和零个或多个子节点。 22 | 23 | - **二叉树:** 每个节点最多有两个子节点的树结构。 24 | - **堆:** 一种特殊的完全二叉树,满足某种特定顺序(最大堆或最小堆)。 25 | - **二叉搜索树(BST):** 一种二叉树,其中每个节点都满足左子树中所有元素的值小于节点的值,右子树中所有元素的值大于节点的值。 26 | 27 | ### 图 28 | > 由节点(或顶点)和连接这些节点的边组成的集合。 29 | - **有向图:** 边有方向的图。 30 | - **无向图:** 边没有方向的图。 31 | - **加权图:** 边被赋予了权重的图。 32 | 33 | 34 | ## 特殊类型或抽象数据结构(ADT) 35 | 36 | 除了上述基本分类,还存在特殊类型的数据结构,通常被视为抽象数据类型(ADT),它们更多地关注操作和行为,而不是实现的具体细节。主要包括: 37 | 38 | - **散列表(哈希表):** 通过哈希函数将键映射到数组中的位置来存储键值对。 39 | - **集合:** 一组无序的不重复元素。 40 | - **字典(映射):** 一组存储键值对的数据结构,键是唯一的。 41 | - **优先队列:** 元素出队顺序是根据它们的优先级来确定的。 -------------------------------------------------------------------------------- /book/src/other/1_cpp_base.md: -------------------------------------------------------------------------------- 1 | # C++ 基础 2 | 3 | 本章节将会介绍在实现d2ds中练习对应的数据结构时会遇到的一些C++相关的基础知识 4 | 5 | - [范型编程](1_cpp_base.template.md) 6 | - [语法糖 | 范围for循环](2_cpp_base.rangefor.md) -------------------------------------------------------------------------------- /book/src/other/1_cpp_base.template.md: -------------------------------------------------------------------------------- 1 | # template | 模板 : 范型编程初识 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 函数模板 - max 9 | - 代码演示 10 | - dslings - 测试代码 11 | - dslings - 检测结果 12 | - max函数 - 重载版本实现 13 | - max函数 - 函数模板版本实现 14 | - 类模板 - Box 15 | - 代码演示 16 | - dslings - 测试代码 17 | - dslings - 检测结果 18 | - Box类模板 - 类型定义 19 | - Box类模板 - 具体实现 20 | - 总结 21 | 22 | --- 23 | 24 | **范型编程**是一种**代码生成技术**, 它能帮助我们节省写大量重复代码的时间。例如, 在我们实现数据结构的时候, 使用**范型编程**技术可以让我们写一套代码就能应用到多种类型的效果。当然, 要想深度掌握**范型编程**技术不是一个简单的事情, 它的难度不亚于学习一门新的语言。 但是幸运的是在[**d2ds**](https://sunrisepeak.github.io/d2ds/)中我们只涉及其最基础的部分, 下面我们将来简单的介绍它们。 25 | 26 | 27 | ## 函数模板 - max 28 | 29 | 实现`d2ds::max`函数, 实现获取两个a和b变量中的最大值 30 | 31 | **dslings - 测试代码** 32 | 33 | ```cpp 34 | // template.0.cpp - readonly 35 | // 36 | // 描述: 37 | // 实现max函数模板 38 | // 39 | // 目标/要求: 40 | // - 不修改该代码检测文件 41 | // - 在exercises/other/cpp-base/Template.hpp中完成你的代码设计 42 | // - 通过所有断言检测 43 | // 44 | 45 | #include 46 | 47 | #include "common/common.hpp" 48 | 49 | #include "exercises/other/cpp-base/Template.hpp" 50 | 51 | int main() { 52 | { // int 53 | int a = -1, b = 1; 54 | d2ds_assert_eq(d2ds::max(a, b), dstruct::max(a, b)); 55 | } 56 | 57 | { // unsigned int 58 | unsigned int a = 4294967295, b = 1; 59 | d2ds_assert_eq(d2ds::max(a, b), dstruct::max(a, b)); 60 | } 61 | 62 | { // double 63 | double a = 1.3, b = 3.1; 64 | d2ds_assert_eq(d2ds::max(a, b), dstruct::max(a, b)); 65 | } 66 | 67 | return 0; 68 | } 69 | ``` 70 | 71 | **dslings - 检测结果** 72 | 73 | ```bash 74 | 🌏Progress: [===>---------] 3/12 75 | 76 | [Target: 1.template-0] 77 | 78 | ✅ Successfully ran tests/other/cpp-base/template.0.cpp! 79 | 80 | 🎉 The code is compiling! 🎉 81 | 82 | Output: 83 | ==================== 84 | [D2DS LOGI]: - ✅ | d2ds::max(a, b) == dstruct::max(a, b) (1 == 1) 85 | [D2DS LOGI]: - ✅ | d2ds::max(a, b) == dstruct::max(a, b) (4294967295 == 4294967295) 86 | [D2DS LOGI]: - ✅ | d2ds::max(a, b) == dstruct::max(a, b) (3.100000 == 3.100000) 87 | [D2DS LOGW]: main: tests/other/cpp-base/template.0.cpp:35 - Delete the XLINGS_WAIT to continue... 88 | 89 | ==================== 90 | 91 | Book: https://sunrisepeak.github.io/d2ds 92 | ``` 93 | 94 | ### max函数 - 重载版本实现 95 | 96 | 通过C++的函数**重载技术(overload)**, 我们分别对`int` | `unsigned int` | `double` 类型版本的max进行实现 97 | 98 | ```c++ 99 | int max(int a, int b) { 100 | return a > b ? a : b; 101 | } 102 | 103 | unsigned int max(unsigned int a, unsigned int b) { 104 | return a > b ? a : b; 105 | } 106 | 107 | double max(double a, double b) { 108 | return a > b ? a : b; 109 | } 110 | ``` 111 | 112 | 这里通过观察**max函数**这三个类型的实现, 可以轻易感觉到它们只有参数和返回值类型不一样, 而函数体的代码都是一样的。此时的需求开发越多, 就会让开发者产生在做**重复工作**的感觉。部分想"偷懒"的程序员, 可能会借助IDE来设置代码模板来减轻工作量。幸运由于大多数程序员对这种"偷懒"的必要性达成了共识, 这促使了多数的编程语言对**范型编程|模板**做了支持, 在C++中对应的就是`template`。 113 | 114 | ### max函数 - 函数模板版本实现 115 | 116 | 通过函数模板, 可以写一套代码实现上面(重载实现)三套代码的效果 117 | 118 | ```c++ 119 | template 120 | T max(T a, T b) { 121 | return a > b ? a : b; 122 | } 123 | ``` 124 | 125 | | 标识 | 解释 | 126 | | - | - | 127 | | `template` | 模板标识 | 128 | | `<>` | 范形参数类型列表(可以有多个参数) | 129 | | `typename T` | `typename`为类型名修饰符, 后面跟着类型名标识`T` | 130 | 131 | 在数据结构实现中使用模板技术 132 | - **第一个好处:** 只需要实现一套代码逻辑实现就可以支撑多种类型 133 | - **第二个好处:** 编译器在编译期只会对使用到的类型做模板实例化, 对于没有用到的类型不会进行代码生成 134 | 135 | 如当只使用`int` 和 `double` 类型时: 136 | 137 | ```cpp 138 | d2ds::max(1, 2); 139 | d2ds::max(1.1, 0.8); 140 | ``` 141 | 142 | 编译器通过按需进行代码生成来减少代码量, 只会实例化出如下两个版本: 143 | 144 | ```cpp 145 | int max(int a, int b) { 146 | return a > b ? a : b; 147 | } 148 | 149 | double max(double a, double b) { 150 | return a > b ? a : b; 151 | } 152 | ``` 153 | 154 | > 注: 模板的代码生成技术在特定情况下, 也可能造成代码膨胀(code bloat)的问题 155 | 156 | ## 类模板 - Box 157 | 158 | 实现`d2ds::Box`用于存储**指定类型**(原生类型和自定义类型)的值 159 | 160 | **dslings - 测试代码** 161 | 162 | ```cpp 163 | // template.2.cpp - readonly 164 | // 165 | // 描述: 166 | // 实现Box类模板, 来存储指定类型的值 167 | // 168 | // 目标/要求: 169 | // - 不修改该代码检测文件 170 | // - 在exercises/other/cpp-base/Template.hpp中完成你的代码设计 171 | // - 通过所有断言检测 172 | // 173 | 174 | #include 175 | 176 | #include "common/common.hpp" 177 | 178 | #include "exercises/other/cpp-base/Template.hpp" 179 | 180 | 181 | int main() { 182 | { 183 | d2ds::Box box; 184 | box.set_value(2); 185 | d2ds_assert_eq(box.get_value(), 2); 186 | } 187 | 188 | { 189 | d2ds::Box box; 190 | box.set_value("Hello, d2ds!"); 191 | d2ds_assert(box.get_value() == dstruct::String("Hello, d2ds!")); 192 | } 193 | 194 | XLINGS_WAIT 195 | 196 | return 0; 197 | } 198 | ``` 199 | 200 | **dslings - 检测结果** 201 | 202 | ```bash 203 | 🌏Progress: [=====>-------] 5/12 204 | 205 | [Target: 1.template-2] 206 | 207 | ✅ Successfully ran tests/other/cpp-base/template.2.cpp! 208 | 209 | 🎉 The code is compiling! 🎉 210 | 211 | Output: 212 | ==================== 213 | [D2DS LOGI]: - ✅ | box.get_value() == 2 (2 == 2) 214 | [D2DS LOGI]: - ✅ | box.get_value() == dstruct::String("Hello, d2ds!") 215 | [D2DS LOGW]: main: tests/other/cpp-base/template.2.cpp:33 - Delete the XLINGS_WAIT to continue... 216 | 217 | ==================== 218 | 219 | Book: https://sunrisepeak.github.io/d2ds 220 | ``` 221 | 222 | ### Box类模板 - 类型定义 223 | 224 | ```cpp 225 | d2ds::Box intBox; 226 | d2ds::Box stringBox; 227 | ``` 228 | 229 | 类模板的定义和函数模板的定义是类似的, 都是在类名(函数签名)前使用`template`进行标识 230 | 231 | ```cpp 232 | template 233 | class Box { 234 | 235 | }; 236 | ``` 237 | 238 | ### Box类模板 - 具体实现 239 | 240 | 在类模板的作用域中, 可以直接把类型`T`当成一个正常的类型符号使用以及用它来完成对应的代码实现 241 | 242 | ```cpp 243 | template 244 | class Box { 245 | public: 246 | Box() : mVal_e{} { } 247 | 248 | T get_value() const { 249 | return mVal_e; 250 | } 251 | 252 | void set_value(const T &val) { 253 | mVal_e = val; 254 | } 255 | 256 | private: 257 | T mVal_e; 258 | }; 259 | ``` 260 | 261 | 在Box的实现中, 使用`T mVal_e;`定义了一个存储用户指定类型值的成员变量。并且在`get_value`和`set_value`成员函数中也像使用正常的类型一样使用**类型名`T`**。它是一个未确定的类型的标识符, 在编译期编译器将会根据使用者指定的类型来去实例化出对应的版本, 就像上面函数模板一样。总之, 在编写模板代码的时候我们可以把`T`当成一个**未知类型名**, 像正常大多数类型名的用法一样来使用它。 262 | 263 | > 注: 264 | > 265 | > 关于**模板**, 不同的群体有不同的称呼偏好。如: 模板XX - (模板函数 模板类) 也有 XX模板 - (函数模板 类模板), 。总体来说, 习惯性的称谓没有固定的统一形式(或者不必定要强制统一)。 266 | > 267 | > 但是为了避免歧义和方便交流讨论, 在同一本书中保持一致性是有必要的, 本书基本遵从后者(代码特征后置)的习惯, 即 XX类 | XX模板 | XX函数 分别对应的是 类 | 模板 | 函数 268 | 269 | ## 总结 270 | 271 | 本小节介绍了C++中**范型编程**中最基础和常用的两个部分 -- **函数模板**和**类模板**的定义方法。通过使用模板的编译器**代码生成**可以让我们设计的数据结构支持多种类型, 而又不用编写多份代码。如果你已经掌握了本节内容, 那就快去到数据结构实现的部分去实际使用C++的范型编程技术吧。 -------------------------------------------------------------------------------- /book/src/other/2_cpp_base.rangefor.md: -------------------------------------------------------------------------------- 1 | # 语法糖 | 范围for循环 2 | 3 | **预览** 4 | 5 | --- 6 | 7 | - 基本介绍 8 | - 使用普通for循环 9 | - 使用范围for循环 10 | - 自定义类型如何支持这个语法糖? 11 | - 模拟Python中range - 实现PyRange 12 | - 代码演示 13 | - Python - range 14 | - dslings - 测试代码 15 | - dslings - 检测结果 16 | - PyRange - 类型定义 17 | - PyRange - begin 和 end 18 | - PyRange - 迭代器的 * 和 ++ 操作 19 | - PyRange - 完整实现 20 | - 总结 21 | 22 | --- 23 | 24 | C++从C++11开始也像很多语言一样提供了范围for循环这个"语法糖"。它是用作对范围中的各个值(如容器中的所有元素)进行操作的较传统for循环更加可读的等价版本。下面以`std::vector`为例对比并演示一下它的使用: 25 | 26 | **使用普通for循环** 27 | 28 | 通过`std::vector`的`begin`和`end`迭代器来获取数据结构(容器)中存储的数据。 其中**迭代器`it`**的行为很像指针, 可以通过*号去"解引用"获取数据, 通过`++`让迭代器指向存储的下一个数据。 29 | 30 | ```cpp 31 | #include 32 | 33 | int main() { 34 | std::vector vec { 1, 2, 3, 4 }; 35 | 36 | int val; 37 | for (auto it = vec.begin(); it != vec.end(); it++) { 38 | val = *it; 39 | //... 40 | } 41 | 42 | return 0; 43 | } 44 | ``` 45 | 46 | **使用范围for循环** 47 | 48 | 通过使用范围for循环简化了对数据结构中数据的访问, 不需要开发者直接去控制和判断迭代器就可以轻松访问到所有数据。这里需要注意的是 —— 其实这个简化了的for循环的本质也是像上面一样使用了迭代器的设计模式, 只是编译器帮我们省去了关于迭代器的相关操作, 原理上可视为等价的。 49 | 50 | ```cpp 51 | #include 52 | 53 | int main() { 54 | std::vector vec { 1, 2, 3, 4 }; 55 | 56 | for (int val : vec) { 57 | // ... 58 | } 59 | 60 | return 0; 61 | } 62 | ``` 63 | 64 | ## 自定义类型如何支持这个语法糖? 65 | 66 | 对于库开发者, 比起使用这个范围for循环, 更让其好奇和兴奋的是 —— **如何让自己写的数据结构也能支持这么好的性质, 这样大家用起来就会更爽了(这可能就是传说中的大家好才是真的好哈哈)**。下面将讨论自定义类型如何支持这个范围for语法糖。 67 | 68 | 这里引用一下[cppreference](https://en.cppreference.com)上对它的解释 69 | 70 | ```cpp 71 | // https://en.cppreference.com/w/cpp/language/range-for 72 | { // until C++17 73 | auto && __range = range-expression ; 74 | for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) 75 | { 76 | range-declaration = *__begin; 77 | loop-statement 78 | } 79 | } 80 | ``` 81 | 82 | 为了更好的观察, 我们还是以上面`std::vector`的范围for作为例子, 给出编译对这个语法套进行代码展开的**可能实现** 83 | 84 | ```cpp 85 | { // 没有展开的形式 86 | for (int val : vec) { 87 | // ... 88 | } 89 | } 90 | { // 编译器代码展开的可能实现 91 | auto && __range = vec; 92 | for (auto __begin = __range.begin(), __end = __range.end(); __begin != __end; ++__begin) { 93 | auto && val = *__begin; 94 | // ... 95 | } 96 | } 97 | { // 编译器代码展开的可能实现 -- 易读版 98 | auto __end = vec.end(); 99 | for (auto it = vec.begin(); it != __end; ++it) { 100 | int val = *it; 101 | // ... 102 | } 103 | } 104 | ``` 105 | 106 | 从简化的`易读版`上可以看出, 和前面最开始的普通版本的for循环实现是差不多的, 并且我们可以总结出如下要素: 107 | 108 | - 1.需要实现`begin()` 109 | - 2.需要实现`end()` 110 | - 3.`begin()` / `end()` 返回的类型需要具备指针的行为操作(或者至少要满足`*`和`++`操作) 111 | 112 | 下面我就以一个例子的实现来具体阐述和感受**自定类型**支持范围for的完整过程 113 | 114 | ## 模拟Python中range - 实现PyRange 115 | 116 | range 类型表示不可变的数字序列,通常用于在 for 循环中循环指定的次数。下面我们将简单介绍一下Python中range对象在for循环中的应用, 然后在使用C++实现一个**支持范围for循环的PyRange**来近似模拟它的行为。 117 | 118 | **Python - range** 119 | 120 | - `range(start, stop)` 121 | - `range(start, stop, step)` 122 | 123 | ```Python 124 | speak@speak-pc:~/workspace/github/d2ds$ python3 125 | Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux 126 | Type "help", "copyright", "credits" or "license" for more information. 127 | >>> for i in range(0, 10): 128 | ... print(i) 129 | ... 130 | 0 131 | 1 132 | 2 133 | 3 134 | 4 135 | 5 136 | 6 137 | 7 138 | 8 139 | 9 140 | >>> for i in range(0, 50, 5): 141 | ... print(i) 142 | ... 143 | 0 144 | 5 145 | 10 146 | 15 147 | 20 148 | 25 149 | 30 150 | 35 151 | 40 152 | 45 153 | >>> 154 | ``` 155 | 156 | **dslings - 测试代码** 157 | 158 | 为了简单PyRange只模拟**Python - range**中在for循环中应用的有限部分。下面是PyRange在范围for循环中生成索引数据(int)的代码示例: 159 | 160 | ```cpp 161 | // range_for.3.cpp - readonly 162 | // 163 | // 描述: 164 | // 实现PyRange在范围for循环的支持, 并保证数据生成的正确性 165 | // 166 | // 目标/要求: 167 | // - 不修改该代码检测文件 168 | // - 在exercises/other/cpp-base/RangeFor.hpp中完成你的代码设计 169 | // - 通过所有断言检测 170 | // 171 | 172 | #include 173 | 174 | #include "common/common.hpp" 175 | 176 | #include "exercises/other/cpp-base/RangeFor.hpp" 177 | 178 | int main() { 179 | { 180 | int index = 0; 181 | for (int val : d2ds::PyRange(0, 10)) { 182 | d2ds_assert_eq(val, index); 183 | index++; 184 | } 185 | } 186 | 187 | { 188 | int index = 0, step = 5; 189 | for (auto val : d2ds::PyRange(0, 50, step)) { 190 | d2ds_assert_eq(val, index); 191 | index += step; 192 | } 193 | } 194 | 195 | XLINGS_WAIT 196 | 197 | return 0; 198 | } 199 | ``` 200 | 201 | 上述代码中, 在接口的使用上为了更像Python中的range, PyRange也遵从了如下设计 202 | 203 | 204 | | 接口 | 简介 | 205 | | - | - | 206 | | `PyRange(start, stop, step = 1)` | step为值变化步长默认为1.数据生成遵从左闭右开原则 | 207 | 208 | **dslings - 检测结果** 209 | 210 | ```bash 211 | 🌏Progress: [=========>---] 9/12 212 | 213 | [Target: 2.range_for-3] 214 | 215 | ✅ Successfully ran tests/other/cpp-base/range_for.3.cpp! 216 | 217 | 🎉 The code is compiling! 🎉 218 | 219 | Output: 220 | ==================== 221 | [D2DS LOGI]: - ✅ | start < stop 222 | [D2DS LOGI]: - ✅ | step > 0 223 | [D2DS LOGI]: - ✅ | mLen_e <= 100 224 | [D2DS LOGI]: - ✅ | val == index (0 == 0) 225 | [D2DS LOGI]: - ✅ | val == index (1 == 1) 226 | [D2DS LOGI]: - ✅ | val == index (2 == 2) 227 | [D2DS LOGI]: - ✅ | val == index (3 == 3) 228 | [D2DS LOGI]: - ✅ | val == index (4 == 4) 229 | [D2DS LOGI]: - ✅ | val == index (5 == 5) 230 | [D2DS LOGI]: - ✅ | val == index (6 == 6) 231 | [D2DS LOGI]: - ✅ | val == index (7 == 7) 232 | [D2DS LOGI]: - ✅ | val == index (8 == 8) 233 | [D2DS LOGI]: - ✅ | val == index (9 == 9) 234 | [D2DS LOGI]: - ✅ | start < stop 235 | [D2DS LOGI]: - ✅ | step > 0 236 | [D2DS LOGI]: - ✅ | mLen_e <= 100 237 | [D2DS LOGI]: - ✅ | val == index (0 == 0) 238 | [D2DS LOGI]: - ✅ | val == index (5 == 5) 239 | [D2DS LOGI]: - ✅ | val == index (10 == 10) 240 | [D2DS LOGI]: - ✅ | val == index (15 == 15) 241 | [D2DS LOGI]: - ✅ | val == index (20 == 20) 242 | [D2DS LOGI]: - ✅ | val == index (25 == 25) 243 | [D2DS LOGI]: - ✅ | val == index (30 == 30) 244 | [D2DS LOGI]: - ✅ | val == index (35 == 35) 245 | [D2DS LOGI]: - ✅ | val == index (40 == 40) 246 | [D2DS LOGI]: - ✅ | val == index (45 == 45) 247 | [D2DS LOGW]: main: tests/other/cpp-base/range_for.3.cpp:35 - Delete the XLINGS_WAIT to continue... 248 | 249 | ==================== 250 | 251 | Book: https://sunrisepeak.github.io/d2ds 252 | ``` 253 | 254 | ### PyRange - 类型定义 255 | 256 | ```cpp 257 | d2ds::PyRange(0, 10); 258 | d2ds::PyRange(0, 5, 200); 259 | ``` 260 | 261 | PyRange的构造函数为了简单, 使用了三个int作为输入参数, 并且为了支持上面两种使用模式最后一个参数step使用了默认参数为1的设置 262 | 263 | ```cpp 264 | class PyRange { 265 | public: 266 | PyRange(int start, int stop, int step = 1) { 267 | 268 | } 269 | }; 270 | ``` 271 | 272 | ### PyRange - begin 和 end 273 | 274 | ```cpp 275 | d2ds::PyRange range(2, 10); 276 | auto begin = range.begin(); 277 | auto end = range.end(); 278 | ``` 279 | 280 | 给PyRange实现两个无参数的成员函数begin和end, 搭出基本结构 281 | 282 | ```cpp 283 | class PyRange { 284 | public: 285 | PyRange(int start, int stop, int step = 1) { 286 | 287 | } 288 | 289 | public: 290 | void * begin() const { 291 | return nullptr; 292 | } 293 | 294 | void * end() const { 295 | return nullptr; 296 | } 297 | }; 298 | ``` 299 | 300 | ### PyRange - 迭代器的 * 和 ++ 操作 301 | 302 | ```cpp 303 | d2ds::PyRange range(0, 10); 304 | 305 | auto begin = range.begin(); 306 | auto end = range.end(); 307 | 308 | d2ds_assert_eq(*begin, 0); 309 | ++begin; 310 | d2ds_assert_eq(*begin, 1); 311 | ``` 312 | 313 | C++的范围for循环使用的迭代器, 是一种类指针行为的类型。幸运的是原生指针就符合这种迭代器的性质, 所以这里让`begin/end`返回`const int *`类型, 这就自动实现了*操作符解引用获取int类型数据和通过++自增运算符移动到下一个数据。 314 | 315 | 通过在PyRange内部设置一个数组mArr_e用来存储数据值和一个mLen_e来标识结束位置来简化实现, 虽然它看起来很不优雅。同时在构造函数中暂时也只实现step等于1的情况 316 | 317 | ```cpp 318 | class PyRange { 319 | public: 320 | PyRange(int start, int stop, int step = 1) { 321 | mLen_e = stop - start; 322 | for (int i = 0; i < mLen_e; i++) { 323 | mArr_e[i] = i + start; 324 | } 325 | } 326 | 327 | public: 328 | const int * begin() const { 329 | return mArr_e; 330 | } 331 | 332 | const int * end() const { 333 | return mArr_e + mLen_e; 334 | } 335 | 336 | private: 337 | int mLen_e; 338 | int mArr_e[100]; 339 | }; 340 | 341 | ``` 342 | 343 | > 注: 本文为了简单实现PyRange的方式是不够优雅的, 相对优雅一些的实现见[设计模式 - 迭代器设计模式]()章节中的实现 344 | 345 | 346 | ### PyRange - 完整实现 347 | 348 | 这里完善了PyRange构造函数中对step的支持和增加了一些参数限制的检测 349 | 350 | ```cpp 351 | class PyRange { 352 | public: 353 | PyRange(int start, int stop, int step = 1) { 354 | 355 | mLen_e = (stop - start) / step; 356 | 357 | d2ds_assert(start < stop); 358 | d2ds_assert(step > 0); 359 | d2ds_assert(mLen_e <= 100); 360 | 361 | for (int i = 0; i < mLen_e; i++) { 362 | mArr_e[i] = start; 363 | start = start + step; 364 | } 365 | } 366 | 367 | public: 368 | const int * begin() const { 369 | return mArr_e; 370 | } 371 | 372 | const int * end() const { 373 | return mArr_e + mLen_e; 374 | } 375 | 376 | private: 377 | int mLen_e; 378 | int mArr_e[100]; 379 | }; 380 | ``` 381 | 382 | ## 总结 383 | 384 | 本小节先是对比了普通for循环和范围for循环的使用, 然后通过分析编译器对**范围for循环**的代码展开结构, 来探究在自定义类型中如何实现**范围for循环**的支持, 最后通过实现一个模拟Python中常用的range对象 —— PyRange, 来进一步通过写代码的方式体验实现**范围for循环**支持的完整过程。那么, 现在快去给自己实现的数据结构添加**范围for循环**的语法糖支持吧(如果你的数据结构内存布局不是连续存储, 你可能还需要阅读[设计模式 - 迭代器设计模式]()章节中的内容)... -------------------------------------------------------------------------------- /book/src/other/3_cpp_base.bigfive.md: -------------------------------------------------------------------------------- 1 | # 行为控制 2 | 3 | ## 拷贝语义 4 | 5 | ## 移动语义 6 | 7 | 移动语义允许资源(如动态内存)从一个对象转移到另一个对象,这样可以避免不必要的资源复制,从而提高应用程序的性能和效率。它主要是通过**移动构造函数**和**移动赋值运算符**实现 -------------------------------------------------------------------------------- /config.xlings: -------------------------------------------------------------------------------- 1 | xlings_name = "dslings" 2 | xlings_lang = "cpp" 3 | xlings_editor = "vscode" 4 | --xlings_llm_config = ".xlings/llm.config.xlings" -------------------------------------------------------------------------------- /dslings/common/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HPP_D2DS 2 | #define COMMON_HPP_D2DS 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #define HONLY_LOGGER_TAG "D2DS" 14 | #include "common/honly_logger.hpp" 15 | #include "common/dslings_config.hpp" 16 | 17 | 18 | #define d2ds_assert(expr) \ 19 | { \ 20 | if (!(expr)) { \ 21 | HONLY_LOGW("❌ | %s", #expr); \ 22 | } else { \ 23 | HONLY_LOGI_P("✅ | %s", #expr); \ 24 | } \ 25 | } 26 | 27 | #define d2ds_assert_eq(a, b) \ 28 | { \ 29 | if (a != b) {\ 30 | HONLY_LOGW("❌ | %s == %s (%s == %s)", \ 31 | #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ 32 | } else {\ 33 | HONLY_LOGI_P("✅ | %s == %s (%s == %s)", \ 34 | #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ 35 | } \ 36 | } 37 | 38 | #define XLINGS_WAIT HONLY_LOGW("🥳 Delete the XLINGS_WAIT to continue..."); 39 | #define D2DS_RETURN HONLY_LOGW("🥳 Delete the XLINGS_RETURN to continue..."); return 0; 40 | 41 | #define D2DS_SELF_ASSIGNMENT_CHECKER if (this == &dsObj) return *this; 42 | 43 | #define SHOW_YOUR_CODE(code_block) code_block 44 | #define DONT_CHANGE(code) code; 45 | 46 | namespace d2ds { 47 | 48 | struct DefaultAllocator { 49 | 50 | static void * malloc(int bytes) { 51 | return allocate(bytes); 52 | } 53 | 54 | static void free(void *addr) { 55 | deallocate(addr, 0); 56 | } 57 | 58 | public: 59 | using Address = unsigned long long; 60 | 61 | static void * allocate(int bytes) { 62 | allocate_counter()++; 63 | if (debug()) 64 | HONLY_LOGI("DefaultAllocator: try to allocate %d bytes", bytes); 65 | void *memPtr = ::malloc(bytes); 66 | memory_addr_flag_d()[(Address)memPtr] = true; 67 | return memPtr; 68 | } 69 | 70 | static void deallocate(void *addr, int bytes) { 71 | deallocate_counter()++; 72 | if (debug()) 73 | HONLY_LOGI("DefaultAllocator: free addr %p, bytes %d", addr, bytes); 74 | if (addr == nullptr) { 75 | HONLY_LOGE("free null pointer"); 76 | } else if (memory_addr_flag_d()[(Address)addr] == false) { 77 | HONLY_LOGE("double free - %p", addr); 78 | } else { 79 | memory_addr_flag_d()[(Address)addr] = false; 80 | ::free(addr); 81 | } 82 | } 83 | 84 | public: // config & status 85 | static bool & debug() { 86 | static bool debugFlag = false; 87 | return debugFlag; 88 | } 89 | 90 | static int & allocate_counter() { 91 | static int cnt = 0; 92 | return cnt; 93 | } 94 | 95 | static int & deallocate_counter() { 96 | static int cnt = 0; 97 | return cnt; 98 | } 99 | 100 | static void clear_status() { 101 | allocate_counter() = 0; 102 | deallocate_counter() = 0; 103 | } 104 | 105 | protected: 106 | static dstruct::Map & memory_addr_flag_d() { 107 | static dstruct::Map memAddrFlag; 108 | return memAddrFlag; 109 | } 110 | 111 | }; 112 | 113 | class BigFiveTest { 114 | public: 115 | 116 | struct Obj { 117 | Obj(int data_ = 0) : data { data_ } { get_test_data_e().mDestructor++; } 118 | 119 | Obj(const Obj &obj) { 120 | get_test_data_e().mDestructor++; 121 | get_test_data_e().mCopyConstructor = true; 122 | } 123 | Obj & operator=(const Obj &obj) { 124 | get_test_data_e().mCopyAssignment = true; 125 | if (this == &obj) get_test_data_e().mSelfAssignment = true; 126 | return *this; 127 | } 128 | 129 | Obj(Obj &&obj) { get_test_data_e().mMoveConstructor = true; } 130 | Obj & operator=(Obj &&obj) { 131 | get_test_data_e().mMoveAssignment = true; 132 | if (this == &obj) get_test_data_e().mSelfAssignment = true; 133 | return *this; 134 | } 135 | 136 | ~Obj() { get_test_data_e().mDestructor--; } 137 | 138 | int data; 139 | }; 140 | 141 | public: // checker 142 | static bool destructor(bool enableInfo = false) { 143 | if (enableInfo) 144 | HONLY_LOGD("checker -> %d == 0", get_test_data_e().mDestructor); 145 | return get_test_data_e().mDestructor == 0; 146 | } 147 | 148 | static bool copy_constructor(bool enableInfo = false) { 149 | if (enableInfo) 150 | HONLY_LOGD("checker -> %d", get_test_data_e().mCopyConstructor); 151 | return get_test_data_e().mCopyConstructor; 152 | } 153 | 154 | static bool copy_assignment(bool enableInfo = false) { 155 | if (enableInfo) 156 | HONLY_LOGD("checker -> %d", get_test_data_e().mCopyAssignment); 157 | return get_test_data_e().mCopyAssignment; 158 | } 159 | 160 | static bool move_constructor(bool enableInfo = false) { 161 | if (enableInfo) 162 | HONLY_LOGD("checker -> %d", get_test_data_e().mMoveConstructor); 163 | return get_test_data_e().mMoveConstructor; 164 | } 165 | 166 | static bool move_assignment(bool enableInfo = false) { 167 | if (enableInfo) 168 | HONLY_LOGD("checker -> %d", get_test_data_e().mMoveAssignment); 169 | return get_test_data_e().mMoveAssignment; 170 | } 171 | 172 | static bool self_assignment(bool enableInfo = false) { 173 | if (enableInfo) 174 | HONLY_LOGD("checker -> %d", get_test_data_e().mMoveAssignment); 175 | return get_test_data_e().mSelfAssignment == false; 176 | } 177 | 178 | public: 179 | static void copy_constructor_pass() { 180 | get_test_data_e().mCopyConstructor = true; 181 | } 182 | 183 | static void copy_assignment_pass() { 184 | get_test_data_e().mCopyAssignment = true; 185 | } 186 | 187 | static void move_constructor_pass() { 188 | get_test_data_e().mMoveConstructor = true; 189 | } 190 | 191 | static void move_assignment_pass() { 192 | get_test_data_e().mMoveAssignment = true; 193 | } 194 | 195 | static void self_assignment_pass() { 196 | get_test_data_e().mSelfAssignment = true; 197 | } 198 | 199 | public: 200 | 201 | static void clear_status() { 202 | get_test_data_e().mDestructor = 0; 203 | get_test_data_e().mCopyConstructor = false; 204 | get_test_data_e().mCopyAssignment = false; 205 | get_test_data_e().mMoveConstructor = false; 206 | get_test_data_e().mMoveAssignment = false; 207 | } 208 | 209 | static void print_status() { 210 | HONLY_LOGW("BigFiveTest status:"); 211 | HONLY_LOGI("mDestructor %s", get_test_data_e().mDestructor == 0 ? "pass" : "failed"); 212 | HONLY_LOGI("mCopyConstructor %s", get_test_data_e().mCopyConstructor ? "pass" : "failed"); 213 | HONLY_LOGI("mCopyAssignment %s", get_test_data_e().mCopyAssignment ? "pass" : "failed"); 214 | HONLY_LOGI("mMoveConstructor %s", get_test_data_e().mMoveConstructor ? "pass" : "failed"); 215 | HONLY_LOGI("mMoveAssignment %s", get_test_data_e().mMoveAssignment ? "pass" : "failed"); 216 | HONLY_LOGI("mSelfAssignment %s", get_test_data_e().mSelfAssignment == false ? "pass" : "failed"); 217 | } 218 | 219 | private: 220 | struct TestData { 221 | int mDestructor = 0; 222 | bool mCopyConstructor = false; 223 | bool mCopyAssignment = false; 224 | bool mMoveConstructor = false; 225 | bool mMoveAssignment = false; 226 | bool mSelfAssignment = false; 227 | }; 228 | 229 | static TestData & get_test_data_e() { 230 | static TestData data; 231 | return data; 232 | } 233 | }; 234 | 235 | template 236 | static void random_data_generator(DSVType &dsv, int rangeL, int rangeR) { 237 | // test: random data 238 | // Choose a random mean between 1 and 2 * ARR_SIZE 239 | // https://en.cppreference.com/w/cpp/numeric/random 240 | std::random_device r; 241 | std::default_random_engine e1(r()); 242 | std::uniform_int_distribution uniform_dist(rangeL, rangeR); 243 | 244 | for (auto it = dsv.begin(); it != dsv.end(); it++) { 245 | *it = uniform_dist(e1); 246 | } 247 | } 248 | 249 | template 250 | static void ds_print(DSVType &dsv) { 251 | std::cerr << "[ "; 252 | for (auto it = dsv.begin(); it != dsv.end(); it++) { 253 | std::cerr << *it << " "; 254 | } 255 | std::cerr << "]" << std::endl; 256 | } 257 | 258 | } 259 | 260 | #endif -------------------------------------------------------------------------------- /dslings/common/dslings_config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DSLINGS_CONFIG_HPP_D2DS 2 | #define DSLINGS_CONFIG_HPP_D2DS 3 | 4 | /* 5 | // checker config 6 | #define DSLINGS_0_CHECKER CHCKER_ENABLE 7 | #define DSLINGS_1_CHECKER CHCKER_ENABLE 8 | #define DSLINGS_2_CHECKER CHCKER_ENABLE 9 | #define TEMPLATE_0_CHECKER CHCKER_ENABLE 10 | #define TEMPLATE_1_CHECKER CHCKER_ENABLE 11 | #define TEMPLATE_2_CHECKER CHCKER_ENABLE 12 | #define CHCKER_ENABLE 13 | #define CHCKER_PASS return 0; 14 | */ 15 | 16 | #endif -------------------------------------------------------------------------------- /dslings/common/honly_logger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_HPP__HONLY 2 | #define LOGGER_HPP__HONLY 3 | 4 | #include 5 | 6 | #ifndef HONLY_LOGGER_TAG 7 | #define HONLY_LOGGER_TAG "HONLY" 8 | #endif 9 | 10 | #define LOG_ENABLE true 11 | #define _HONLY_LOG(fd, ...) if (LOG_ENABLE) { fprintf (fd, __VA_ARGS__); fprintf (fd, "\033[0m\n"); fflush(stdout); } 12 | #define HONLY_LOGI_P(...) { fprintf (stdout, "\033[32m[%s LOGI]: - ", HONLY_LOGGER_TAG); _HONLY_LOG(stdout, __VA_ARGS__); } 13 | #define HONLY_LOGI(...) { fprintf (stdout, "\033[32m[%s LOGI]: %s: %s:%d - ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } 14 | #define HONLY_LOGD(...) { fprintf (stdout, "\033[37m[%s LOGD]: %s: %s:%d - ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } 15 | #define HONLY_LOGW(...) { fprintf (stdout, "\033[33m[%s LOGW]: %s: %s:%d - ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } 16 | #define HONLY_LOGE(...) { fprintf (stdout, "\033[31m[%s LOGE]: %s: %s:%d - ❌ | ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } 17 | 18 | #endif -------------------------------------------------------------------------------- /dslings/exercises/array/Array.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ARRAY_HPP_D2DS 2 | #define ARRAY_HPP_D2DS 3 | 4 | #include 5 | 6 | namespace d2ds { 7 | // show your code 8 | 9 | 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /dslings/exercises/array/Vector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_HPP_D2DS 2 | #define VECTOR_HPP_D2DS 3 | 4 | #include 5 | 6 | #include "common/common.hpp" 7 | 8 | namespace d2ds { 9 | // show your code 10 | 11 | 12 | } 13 | 14 | #endif -------------------------------------------------------------------------------- /dslings/exercises/dslings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DSLINGS_HPP_D2DS 2 | #define DSLINGS_HPP_D2DS 3 | 4 | namespace d2ds { 5 | // show your code 6 | 7 | 8 | /* 9 | class MaxValue { 10 | public: 11 | MaxValue(int val) { 12 | mMaxVal_e = val; 13 | } 14 | 15 | int get() { 16 | return mMaxVal_e; 17 | } 18 | 19 | void set(int val) { 20 | if (val > mMaxVal_e) { 21 | mMaxVal_e = val; 22 | } 23 | } 24 | 25 | private: 26 | int mMaxVal_e; 27 | }; 28 | */ 29 | 30 | } 31 | 32 | #endif -------------------------------------------------------------------------------- /dslings/exercises/linked-list/EmbeddedList.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EMBEDDED_LIST_HPP_D2DS 2 | #define EMBEDDED_LIST_HPP_D2DS 3 | 4 | namespace d2ds { 5 | // show your code 6 | 7 | } 8 | 9 | #endif -------------------------------------------------------------------------------- /dslings/exercises/linked-list/SLinkedList.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SLINKED_LIST_HPP_D2DS 2 | #define SLINKED_LIST_HPP_D2DS 3 | 4 | #include 5 | 6 | namespace d2ds { 7 | // show your code 8 | 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /dslings/exercises/other/cpp-base/RangeFor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RANGE_BASE_FOR_HPP_D2DS 2 | #define RANGE_BASE_FOR_HPP_D2DS 3 | 4 | #include 5 | 6 | namespace d2ds { 7 | // show your code 8 | 9 | 10 | 11 | } 12 | 13 | #endif -------------------------------------------------------------------------------- /dslings/exercises/other/cpp-base/Template.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEMPLATE_HPP_D2DS 2 | #define TEMPLATE_HPP_D2DS 3 | 4 | namespace d2ds { 5 | // show your code 6 | 7 | int max(int a, int b) { 8 | return a > b ? a : b; 9 | } 10 | 11 | 12 | } 13 | 14 | #endif -------------------------------------------------------------------------------- /dslings/tests/array/array.0.cpp: -------------------------------------------------------------------------------- 1 | // array.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array范型支持 5 | // 6 | // 目标/要求: 7 | // - 实现类模板定义 8 | // - 在exercises/array/Array.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | d2ds::Array intArr; 18 | d2ds::Array doubleArr; 19 | 20 | XLINGS_WAIT 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /dslings/tests/array/array.1.cpp: -------------------------------------------------------------------------------- 1 | // array.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array的数据初始化 5 | // 6 | // 目标/要求: 7 | // - 实现列表初始化器的数据初始化 8 | // - 在exercises/array/Array.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | d2ds::Array intArr { 5, 4, 3, 2 /*, 1*/ }; 18 | 19 | XLINGS_WAIT 20 | 21 | return 0; 22 | } -------------------------------------------------------------------------------- /dslings/tests/array/array.2.cpp: -------------------------------------------------------------------------------- 1 | // array.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array类模板行为控制(显式定义bigfive) 5 | // (构造|析构 / 赋值构造 / 赋值分配 / 移动构造 / 移动赋值) 6 | // https://en.cppreference.com/w/cpp/language/rule_of_three 7 | // 8 | // 目标/要求: 9 | // - 实现下标访问功能 10 | // - 在exercises/array/Array.hpp中完成你的代码设计 11 | // - 通过编译器检测 & 断言检查 12 | // 13 | 14 | #include "common/common.hpp" 15 | 16 | #include 17 | 18 | using d2ds::BigFiveTest; 19 | 20 | int main() { 21 | { 22 | d2ds::Array intArr; 23 | } 24 | d2ds_assert(BigFiveTest::destructor()); 25 | 26 | { 27 | d2ds::Array intArr1; 28 | 29 | d2ds::Array intArr2(intArr1); 30 | d2ds::Array intArr3 = intArr1; 31 | // d2ds_assert(BigFiveTest::copy_constructor()); 32 | 33 | intArr3 = intArr2; 34 | d2ds_assert(BigFiveTest::copy_assignment()); 35 | 36 | intArr1 = intArr1; 37 | } 38 | 39 | { 40 | d2ds::Array intArr1; 41 | d2ds::Array intArr2 { std::move(intArr1) }; 42 | // d2ds_assert(BigFiveTest::move_constructor()); 43 | 44 | intArr1 = std::move(intArr2); 45 | d2ds_assert(BigFiveTest::move_assignment()); 46 | 47 | intArr1 = std::move(intArr1); 48 | } 49 | 50 | d2ds_assert(BigFiveTest::self_assignment()); 51 | d2ds_assert(BigFiveTest::destructor()); 52 | //BigFiveTest::print_status(); 53 | 54 | XLINGS_WAIT 55 | 56 | return 0; 57 | } -------------------------------------------------------------------------------- /dslings/tests/array/array.3.cpp: -------------------------------------------------------------------------------- 1 | // array.3.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array类模板的数据访问 5 | // 6 | // 目标/要求: 7 | // - 实现下标访问功能 8 | // - 在exercises/array/Array.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | d2ds::Array intArr { 5, 4, 3, 2, 1 }; 18 | 19 | intArr[1] = 6; 20 | 21 | d2ds_assert_eq(intArr[0], 5); 22 | d2ds_assert_eq(intArr[4], 1); 23 | 24 | intArr[4] = intArr[0]; 25 | d2ds_assert_eq(intArr[0], intArr[4]); 26 | 27 | XLINGS_WAIT 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /dslings/tests/array/array.4.cpp: -------------------------------------------------------------------------------- 1 | // array.4.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array常用函数实现 5 | // 6 | // 目标/要求: 7 | // - 实现 size, back 成员函数 8 | // - 在exercises/array/Array.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | d2ds::Array intArr { 0, 1, 2, 3, 4 }; 18 | 19 | for (int i = 0; i < intArr.size(); i++) { 20 | d2ds_assert_eq(i, intArr[i]); 21 | } 22 | 23 | d2ds_assert_eq(4, intArr.back()); 24 | 25 | XLINGS_WAIT 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /dslings/tests/array/array.5.cpp: -------------------------------------------------------------------------------- 1 | // array.5.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array的迭代器支持 - 范围for支持 5 | // 6 | // 目标/要求: 7 | // - 实现迭代器支持 & 范围for支持 8 | // - 在exercises/array/Array.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | d2ds::Array intArr { 0, 1, 2, 3, 4 }; 18 | 19 | d2ds_assert(intArr.begin() != intArr.end()); 20 | 21 | int val = 0; 22 | for (int data : intArr) { 23 | d2ds_assert_eq(data, val); 24 | val++; 25 | } 26 | 27 | XLINGS_WAIT 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /dslings/tests/array/array.6.cpp: -------------------------------------------------------------------------------- 1 | // array.6.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Array负下标访问数据支持 5 | // 6 | // 目标/要求: 7 | // - 实现负下标访问数据支持, 其中 -1 表示最后一个元素 8 | // - 在exercises/array/Array.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | 18 | d2ds::Array intArr { 'd', '2', 'd', 's'}; 19 | 20 | d2ds_assert_eq(intArr[0], intArr[-4]); 21 | d2ds_assert_eq(intArr[1], intArr[-3]); 22 | d2ds_assert_eq(intArr[2], intArr[-2]); 23 | d2ds_assert_eq(intArr[3], intArr[-1]); 24 | 25 | XLINGS_WAIT 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /dslings/tests/dslings.0.cpp: -------------------------------------------------------------------------------- 1 | // dslings.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 通过实现一个MaxVal类型(保存最大值), 来介绍dslings的"编译器驱动开发" 5 | // 即根据编译器的错误提示来完成这个训练流程的演示Demo, 并且通常为了降低难度会把一个'数据结构'的实现分成多个检测模块. 6 | // 如: dslings.0.cpp dslings.1.cpp dslings.2.cpp 7 | // 8 | // 目标/要求: 9 | // - 不修改该代码检测文件 10 | // - 在exercises/dslings.hpp中完成你的代码设计 11 | // - 通过所有编译器检测 和 断言 12 | // 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/dslings.hpp" 17 | 18 | int main() { 19 | 20 | d2ds::MaxValue mVal(2); 21 | 22 | d2ds_assert_eq(mVal.get(), 2); 23 | 24 | HONLY_LOGI_P("Hello D2DS!"); 25 | 26 | XLINGS_WAIT 27 | 28 | return 0; 29 | } -------------------------------------------------------------------------------- /dslings/tests/dslings.1.cpp: -------------------------------------------------------------------------------- 1 | // dslings.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 通过实现一个MaxVal类型(保存最大值), 来介绍dslings的"编译器驱动开发" 5 | // 即根据编译器的错误提示来完成这个训练流程的演示Demo, 并且通常为了降低难度会把一个'数据结构'的实现分成多个检测模块. 6 | // 如: dslings.0.cpp dslings.1.cpp dslings.2.cpp 7 | // 8 | // 目标/要求: 9 | // - 不修改该代码检测文件 10 | // - 在exercises/dslings.hpp中完成你的代码设计 11 | // - 通过所有编译器检测 和 断言 12 | // 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/dslings.hpp" 17 | 18 | int main() { 19 | 20 | d2ds::MaxValue mVal(2); 21 | 22 | d2ds_assert_eq(mVal.get(), 2); 23 | 24 | mVal.set(-1); 25 | d2ds_assert_eq(mVal.get(), 2); 26 | 27 | mVal.set(100); 28 | d2ds_assert_eq(mVal.get(), 100); 29 | 30 | XLINGS_WAIT 31 | 32 | return 0; 33 | } -------------------------------------------------------------------------------- /dslings/tests/dslings.2.cpp: -------------------------------------------------------------------------------- 1 | // dslings.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 通过实现一个MaxVal类型(保存最大值), 来介绍dslings的"编译器驱动开发" 5 | // 即根据编译器的错误提示来完成这个训练流程的演示Demo, 并且通常为了降低难度会把一个'数据结构'的实现分成多个检测模块. 6 | // 如: dslings.0.cpp dslings.1.cpp dslings.2.cpp 7 | // 8 | // 目标/要求: 9 | // - 不修改该代码检测文件 10 | // - 在exercises/dslings.hpp中完成你的代码设计 11 | // - 通过所有编译器检测 和 断言 12 | // 13 | 14 | #include 15 | 16 | #include "common/common.hpp" 17 | #include "exercises/dslings.hpp" 18 | 19 | int main() { 20 | 21 | d2ds::MaxValue mVal(2); 22 | 23 | d2ds_assert_eq(mVal.get(), 2); 24 | 25 | mVal.set(-1); 26 | d2ds_assert_eq(mVal.get(), 2); 27 | 28 | mVal.set(100); 29 | d2ds_assert_eq(mVal.get(), 100); 30 | 31 | // random test 32 | dstruct::Array data; 33 | d2ds::random_data_generator(data, 0, 200); 34 | d2ds::ds_print(data); 35 | 36 | int maxVal = 0; 37 | for (int i = 0; i < data.size(); i++) { 38 | mVal.set(data[i]); 39 | if (data[i] > maxVal) { 40 | maxVal = data[i]; 41 | } 42 | } 43 | 44 | d2ds_assert_eq(mVal.get(), maxVal); 45 | 46 | XLINGS_WAIT 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-dlist.0.cpp: -------------------------------------------------------------------------------- 1 | // embedded-dlist.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义嵌入式双链表和初始化操作 5 | // 6 | // 目标/要求: 7 | // - 定义对应的DoublyLink和对应的init操作 8 | // - 在exercises/linked-list/EmbeddedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::DoublyLink dLink; 19 | d2ds::DoublyLink::init(&dLink); 20 | d2ds_assert(dLink.next == dLink.prev); 21 | 22 | XLINGS_WAIT 23 | 24 | return 0; 25 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-dlist.1.cpp: -------------------------------------------------------------------------------- 1 | // embedded-dlist.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 嵌入式双链表的插入和删除操作 5 | // 6 | // 目标/要求: 7 | // - 在DoublyLink中实现insert和del操作 8 | // - 在exercises/linked-list/EmbeddedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::DoublyLink head; 19 | 20 | d2ds::DoublyLink::init(&head); 21 | 22 | for (int i = 0; i < 10; i++) { 23 | auto linkPtr = new d2ds::DoublyLink(); 24 | d2ds::DoublyLink::insert(&head, linkPtr); 25 | } 26 | 27 | int cnt = 0; 28 | while (head.next != &head) { 29 | cnt++; 30 | auto target = head.next; 31 | d2ds::DoublyLink::del(&head, target); 32 | delete target; 33 | } 34 | 35 | d2ds_assert_eq(cnt, 10); 36 | 37 | XLINGS_WAIT 38 | 39 | return 0; 40 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-dlist.2.cpp: -------------------------------------------------------------------------------- 1 | // embedded-dlist.2.cpp - write 2 | // 3 | // 描述: 4 | // 嵌入式双链表 - 组合式 5 | // 6 | // 目标/要求: 7 | // - 在对应的SHOW_YOUR_CODE代码块实现 逆序遍历 和 链表的释放 8 | // - 通过所有编译器检测 和 断言 9 | // 10 | 11 | #include "common/common.hpp" 12 | 13 | #include "exercises/linked-list/EmbeddedList.hpp" 14 | 15 | using d2ds::DefaultAllocator; 16 | 17 | struct MyData { 18 | int id; 19 | char data; 20 | }; 21 | 22 | int main() { 23 | 24 | d2ds::DefaultAllocator::debug() = true; 25 | 26 | d2ds::DoublyLink head; 27 | 28 | d2ds::DoublyLink::init(&head); 29 | 30 | for (int i = 0; i < 10; i++) { 31 | auto linkPtr = ( d2ds::DoublyLink* ) d2ds::DefaultAllocator::malloc(sizeof(d2ds::DoublyLink) + sizeof(MyData)); 32 | d2ds::DoublyLink::init(linkPtr); 33 | auto dataPtr = (MyData *)(linkPtr + 1); 34 | dataPtr->id = i; 35 | dataPtr->data = 'a' + i; 36 | d2ds::DoublyLink::insert(&head, linkPtr); 37 | } 38 | 39 | dstruct::Stack dataStack; 40 | for (auto linkPtr = head.next; linkPtr != &head; linkPtr = linkPtr->next) { 41 | auto dataPtr = reinterpret_cast(linkPtr + 1); 42 | dataStack.push(*dataPtr); 43 | } 44 | 45 | SHOW_YOUR_CODE({ // reverse traverse 46 | for (auto linkPtr = ?; linkPtr != &head; ) { 47 | MyData *dataPtr = ?; 48 | 49 | DONT_CHANGE( 50 | auto myData = dataStack.top(); 51 | d2ds_assert_eq(dataPtr->id, myData.id); 52 | d2ds_assert_eq(dataPtr->data, myData.data); 53 | dataStack.pop(); 54 | ) 55 | } 56 | }) 57 | 58 | d2ds_assert(dataStack.empty()); 59 | 60 | SHOW_YOUR_CODE({ // use DefaultAllocator::free(addr) to release 61 | d2ds::DoublyLink *target = head.next; 62 | while (target != &head) { 63 | DefaultAllocator::free(target); 64 | } 65 | }) 66 | 67 | d2ds_assert(head.next == &head); 68 | 69 | XLINGS_WAIT 70 | 71 | return 0; 72 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-dlist.3.cpp: -------------------------------------------------------------------------------- 1 | // embedded-dlist.3.cpp - write 2 | // 3 | // 描述: 4 | // 嵌入式双链表 - 嵌入式 5 | // 6 | // 目标/要求: 7 | // - 在对应的SHOW_YOUR_CODE代码块实现 逆序遍历 和 链表的释放 8 | // - 通过所有编译器检测 和 断言 9 | // 10 | 11 | #include "common/common.hpp" 12 | 13 | #include "exercises/linked-list/EmbeddedList.hpp" 14 | 15 | using d2ds::DefaultAllocator; 16 | 17 | struct MyData { 18 | d2ds::DoublyLink link; 19 | int id; 20 | char data; 21 | }; 22 | 23 | int main() { 24 | 25 | d2ds::DefaultAllocator::debug() = true; 26 | 27 | d2ds::DoublyLink head; 28 | 29 | d2ds::DoublyLink::init(&head); 30 | 31 | for (int i = 0; i < 10; i++) { 32 | auto dataPtr = (MyData *) d2ds::DefaultAllocator::malloc(sizeof(MyData)); 33 | d2ds::DoublyLink::init(&(dataPtr->link)); 34 | dataPtr->id = i; 35 | dataPtr->data = 'a' + i; 36 | d2ds::DoublyLink::insert(&head, &(dataPtr->link)); 37 | } 38 | 39 | dstruct::Stack dataStack; 40 | for (auto linkPtr = head.next; linkPtr != &head; linkPtr = linkPtr->next) { 41 | auto dataPtr = reinterpret_cast(linkPtr); 42 | dataStack.push(*dataPtr); 43 | } 44 | 45 | SHOW_YOUR_CODE({ // reverse traverse 46 | for (auto linkPtr = head.prev; linkPtr != &head; linkPtr = linkPtr->prev) { 47 | MyData *dataPtr = ?; 48 | 49 | DONT_CHANGE( 50 | auto myData = dataStack.top(); 51 | d2ds_assert_eq(dataPtr->id, myData.id); 52 | d2ds_assert_eq(dataPtr->data, myData.data); 53 | dataStack.pop(); 54 | ) 55 | } 56 | }) 57 | 58 | d2ds_assert(dataStack.empty()); 59 | 60 | SHOW_YOUR_CODE({ // use DefaultAllocator::free(addr) to release 61 | 62 | }) 63 | 64 | d2ds_assert(head.next == &head); 65 | 66 | XLINGS_WAIT 67 | 68 | return 0; 69 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-dlist.4.cpp: -------------------------------------------------------------------------------- 1 | // embedded-dlist.4.cpp - write 2 | // 3 | // 描述: 4 | // 嵌入式双链表 - 继承式 5 | // 6 | // 目标/要求: 7 | // - 在对应的SHOW_YOUR_CODE代码块实现 逆序遍历 和 链表的释放 8 | // - 通过所有编译器检测 和 断言 9 | // 10 | 11 | #include "common/common.hpp" 12 | 13 | #include "exercises/linked-list/EmbeddedList.hpp" 14 | 15 | using d2ds::DefaultAllocator; 16 | 17 | template 18 | struct ENode : d2ds::DoublyLink { 19 | ENode * prev() { 20 | return static_cast(d2ds::DoublyLink::prev); 21 | } 22 | 23 | ENode * next() { 24 | return static_cast(d2ds::DoublyLink::next); 25 | } 26 | 27 | T * data() { 28 | return static_cast(this); 29 | } 30 | }; 31 | 32 | struct MyData : ENode { 33 | int id; 34 | char data; 35 | }; 36 | 37 | int main() { 38 | 39 | d2ds::DefaultAllocator::debug() = true; 40 | 41 | ENode head; 42 | 43 | d2ds::DoublyLink::init(&head); 44 | 45 | for (int i = 0; i < 10; i++) { 46 | auto dataPtr = (MyData *) d2ds::DefaultAllocator::malloc(sizeof(MyData)); 47 | d2ds::DoublyLink::init(dataPtr); 48 | dataPtr->id = i; 49 | dataPtr->data = 'a' + i; 50 | d2ds::DoublyLink::insert(&head, dataPtr); 51 | } 52 | 53 | dstruct::Stack dataStack; 54 | for (auto nodePtr = head.next(); nodePtr != &head; nodePtr = nodePtr->next()) { 55 | auto dataPtr = nodePtr->data(); 56 | dataStack.push(*dataPtr); 57 | } 58 | 59 | SHOW_YOUR_CODE({ // reverse traverse 60 | for () { 61 | MyData *dataPtr = ?; 62 | 63 | DONT_CHANGE( 64 | auto myData = dataStack.top(); 65 | d2ds_assert_eq(dataPtr->id, myData.id); 66 | d2ds_assert_eq(dataPtr->data, myData.data); 67 | dataStack.pop(); 68 | ) 69 | } 70 | }) 71 | 72 | d2ds_assert(dataStack.empty()); 73 | 74 | SHOW_YOUR_CODE({ // use DefaultAllocator::free(addr) to release 75 | 76 | }) 77 | 78 | d2ds_assert(head.next() == &head); 79 | 80 | XLINGS_WAIT 81 | 82 | return 0; 83 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-slist.0.cpp: -------------------------------------------------------------------------------- 1 | // embedded-slist.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义嵌入式单链表节点 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/linked-list/EmbeddedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds_assert_eq(sizeof(d2ds::SinglyLink), 8); 19 | 20 | XLINGS_WAIT 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-slist.1.cpp: -------------------------------------------------------------------------------- 1 | // embedded-slist.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义嵌入式单链表的插入操作(默认为循环链表) 5 | // 6 | // 目标/要求: 7 | // - 使用SinglyLink的insert操作 8 | // - 在exercises/linked-list/EmbeddedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | using namespace d2ds; 17 | 18 | int main() { 19 | 20 | SinglyLink head; 21 | 22 | for (int i = 0; i < 10; i++) { 23 | auto linkPtr = new SinglyLink(); 24 | SinglyLink::insert(&head, linkPtr); 25 | } 26 | 27 | int cnt = 0; 28 | SinglyLink *p = head.next; 29 | while (p != &head) { 30 | cnt++; 31 | auto q = p->next; 32 | delete p; 33 | p = q; 34 | } 35 | 36 | d2ds_assert_eq(cnt, 10); 37 | 38 | XLINGS_WAIT 39 | 40 | return 0; 41 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-slist.2.cpp: -------------------------------------------------------------------------------- 1 | // embedded-slist.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义嵌入式单链表的删除操作 5 | // 6 | // 目标/要求: 7 | // - 使用SinglyLink的remove操作 8 | // - 在exercises/linked-list/EmbeddedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | using namespace d2ds; 17 | 18 | int main() { 19 | 20 | SinglyLink head; 21 | 22 | for (int i = 0; i < 10; i++) { 23 | auto linkPtr = new SinglyLink(); 24 | SinglyLink::insert(&head, linkPtr); 25 | } 26 | 27 | for (int i = 0; i < 5; i++) { 28 | auto linkPtr = head.next; 29 | SinglyLink::remove(&head, linkPtr); 30 | delete linkPtr; 31 | } 32 | 33 | int cnt = 0; 34 | SinglyLink *p = head.next; 35 | while (p != &head) { 36 | cnt++; 37 | auto q = p->next; 38 | delete p; 39 | p = q; 40 | } 41 | 42 | d2ds_assert_eq(cnt, 5); 43 | 44 | XLINGS_WAIT 45 | 46 | return 0; 47 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-slist.3.cpp: -------------------------------------------------------------------------------- 1 | // embedded-slist.3.cpp - write 2 | // 3 | // 描述: 4 | // 嵌入式表链-插入操作练习 5 | // 6 | // 目标/要求: 7 | // - 创建5个IntNode节点并初始化为0 ~ 4插入到list中 8 | // - 在本文件中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | using namespace d2ds; 17 | 18 | struct IntNode { 19 | SinglyLink link; 20 | int data; 21 | 22 | IntNode() : link(), data { 0 } {} 23 | }; 24 | 25 | int main() { 26 | 27 | SinglyLink list; 28 | 29 | // create & insert 30 | for (int i = 0; i < 5; i++) { 31 | // show your code (use `new` to create node) 32 | 33 | } 34 | 35 | // release all 36 | int sum = 0; 37 | SinglyLink *p = list.next; 38 | while (p != &list) { 39 | sum += reinterpret_cast(p)->data; 40 | auto q = p->next; 41 | delete p; 42 | p = q; 43 | } 44 | 45 | d2ds_assert_eq(sum, 0 + 1 + 2 + 3 + 4); 46 | 47 | XLINGS_WAIT 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /dslings/tests/embedded-list/embedded-slist.4.cpp: -------------------------------------------------------------------------------- 1 | // embedded-slist.4.cpp - write 2 | // 3 | // 描述: 4 | // 嵌入式表链-remove操作练习 5 | // 6 | // 目标/要求: 7 | // - 删除list中数值为偶数的节点 8 | // - 在本文件中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/EmbeddedList.hpp" 15 | 16 | using namespace d2ds; 17 | 18 | struct IntNode : SinglyLink { 19 | int data; 20 | }; 21 | 22 | int main() { 23 | 24 | SinglyLink list; 25 | 26 | // insert int node 27 | for (int i = 1; i <= 10; i++) { 28 | auto nodePtr = new IntNode(); 29 | nodePtr->data = i; 30 | SinglyLink::insert(&list, nodePtr); 31 | } 32 | 33 | for (auto it = &list; it->next != &list; it = it->next) { 34 | // show your code 35 | 36 | } 37 | 38 | // release all 39 | int sum = 0; 40 | SinglyLink *p = list.next; 41 | while (p != &list) { 42 | 43 | sum += reinterpret_cast(p)->data; 44 | 45 | auto q = p->next; 46 | delete p; 47 | p = q; 48 | } 49 | 50 | d2ds_assert_eq(sum, 1 + 3 + 5 + 7 + 9); 51 | 52 | XLINGS_WAIT 53 | 54 | return 0; 55 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/range_for.0.cpp: -------------------------------------------------------------------------------- 1 | // range_for.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现PyRange的类型定义 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/RangeFor.hpp中完成你的代码设计 9 | // 10 | 11 | #include 12 | 13 | #include "common/common.hpp" 14 | 15 | #include "exercises/other/cpp-base/RangeFor.hpp" 16 | 17 | int main() { 18 | d2ds::PyRange(0, 10); 19 | d2ds::PyRange(0, 5, 200); 20 | 21 | XLINGS_WAIT 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/range_for.1.cpp: -------------------------------------------------------------------------------- 1 | // range_for.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现PyRange的begin和end 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/RangeFor.hpp中完成你的代码设计 9 | // - 通过所有断言检测 10 | // 11 | 12 | #include 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/other/cpp-base/RangeFor.hpp" 17 | 18 | int main() { 19 | d2ds::PyRange range(2, 10); 20 | 21 | auto begin = range.begin(); 22 | auto end = range.end(); 23 | 24 | XLINGS_WAIT 25 | 26 | return 0; 27 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/range_for.2.cpp: -------------------------------------------------------------------------------- 1 | // range_for.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现PyRange对象begin和end返回值支持*解引用和++操作 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/RangeFor.hpp中完成你的代码设计 9 | // - 通过所有断言检测 10 | // 11 | 12 | #include 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/other/cpp-base/RangeFor.hpp" 17 | 18 | int main() { 19 | d2ds::PyRange range(0, 10); 20 | 21 | auto begin = range.begin(); 22 | auto end = range.end(); 23 | 24 | d2ds_assert(begin != end); 25 | 26 | d2ds_assert_eq(*begin, 0); 27 | ++begin; 28 | d2ds_assert_eq(*begin, 1); 29 | 30 | XLINGS_WAIT 31 | 32 | return 0; 33 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/range_for.3.cpp: -------------------------------------------------------------------------------- 1 | // range_for.3.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现PyRange在范围for循环的支持, 并保证数据生成的正确性 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/RangeFor.hpp中完成你的代码设计 9 | // - 通过所有断言检测 10 | // 11 | 12 | #include 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/other/cpp-base/RangeFor.hpp" 17 | 18 | int main() { 19 | 20 | { // avoid "begin == end" lead to checker's assert invalid 21 | d2ds::PyRange range(0, 10); 22 | auto begin = range.begin(); 23 | auto end = range.end(); 24 | d2ds_assert(begin != end); 25 | } 26 | 27 | { 28 | int index = 0; 29 | for (int val : d2ds::PyRange(0, 10)) { 30 | d2ds_assert_eq(val, index); 31 | index++; 32 | } 33 | } 34 | 35 | { 36 | int index = 0, step = 5; 37 | for (auto val : d2ds::PyRange(0, 50, step)) { 38 | d2ds_assert_eq(val, index); 39 | index += step; 40 | } 41 | } 42 | 43 | XLINGS_WAIT 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/template.0.cpp: -------------------------------------------------------------------------------- 1 | // template.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现max函数模板 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/Template.hpp中完成你的代码设计 9 | // - 通过所有断言检测 10 | // 11 | 12 | #include 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/other/cpp-base/Template.hpp" 17 | 18 | int main() { 19 | 20 | { // int 21 | int a = -1, b = 1; 22 | d2ds_assert_eq(d2ds::max(a, b), dstruct::max(a, b)); 23 | } 24 | 25 | { // unsigned int 26 | unsigned int a = 4294967295, b = 1; 27 | d2ds_assert_eq(d2ds::max(a, b), dstruct::max(a, b)); 28 | } 29 | 30 | { // double 31 | double a = 1.3, b = 3.1; 32 | d2ds_assert_eq(d2ds::max(a, b), dstruct::max(a, b)); 33 | } 34 | 35 | XLINGS_WAIT 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/template.1.cpp: -------------------------------------------------------------------------------- 1 | // template.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现Box类模板 - 范型支持 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/Template.hpp中完成你的代码设计 9 | // - 通过所有编译器检查 10 | // 11 | 12 | #include 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/other/cpp-base/Template.hpp" 17 | 18 | 19 | int main() { 20 | d2ds::Box intBox; 21 | d2ds::Box stringBox; 22 | 23 | XLINGS_WAIT 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /dslings/tests/other/cpp-base/template.2.cpp: -------------------------------------------------------------------------------- 1 | // template.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现Box类模板, 来存储指定类型的值 5 | // 6 | // 目标/要求: 7 | // - 不修改该代码检测文件 8 | // - 在exercises/other/cpp-base/Template.hpp中完成你的代码设计 9 | // - 通过所有断言检测 10 | // 11 | 12 | #include 13 | 14 | #include "common/common.hpp" 15 | 16 | #include "exercises/other/cpp-base/Template.hpp" 17 | 18 | 19 | int main() { 20 | 21 | { 22 | d2ds::Box box; 23 | box.set_value(2); 24 | d2ds_assert_eq(box.get_value(), 2); 25 | } 26 | 27 | { 28 | d2ds::Box box; 29 | box.set_value("Hello, d2ds!"); 30 | d2ds_assert(box.get_value() == dstruct::String("Hello, d2ds!")); 31 | } 32 | 33 | XLINGS_WAIT 34 | 35 | return 0; 36 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.0.cpp: -------------------------------------------------------------------------------- 1 | // slist.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义SLinkedList及其数据成员 5 | // 6 | // 目标/要求: 7 | // - 定义SLinkedList及链表节点Node和对应的数据成员 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedListNode node; 19 | d2ds::SLinkedList intList; 20 | d2ds::SLinkedList doubleList; 21 | 22 | d2ds_assert(sizeof(intList) > 24); 23 | 24 | XLINGS_WAIT 25 | 26 | return 0; 27 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.1.cpp: -------------------------------------------------------------------------------- 1 | // slist.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的数据成员的初始化, 以及资源的释放 5 | // 6 | // 目标/要求: 7 | // - 实现 默认初始化/列表初始化对应的构造函数, 以及析构函数 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = true; 19 | 20 | { 21 | d2ds::SLinkedList intList1; 22 | d2ds::SLinkedList intList3 = { 1, 2, 3 }; 23 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(),3); 24 | } 25 | 26 | d2ds_assert_eq( 27 | d2ds::DefaultAllocator::allocate_counter(), 28 | d2ds::DefaultAllocator::deallocate_counter() 29 | ); 30 | 31 | XLINGS_WAIT 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.2.cpp: -------------------------------------------------------------------------------- 1 | // slist.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的拷贝语义 5 | // 6 | // 目标/要求: 7 | // - 实现 拷贝构造 和 拷贝赋值, 并通过资源分配与释放的检查 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = true; 19 | 20 | { 21 | d2ds::SLinkedList intList1 = { 1, 2, 3 }; 22 | d2ds::SLinkedList intList2(intList1); 23 | 24 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 6); 25 | 26 | d2ds::SLinkedList intList3; 27 | intList3 = intList1; 28 | 29 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 9); 30 | 31 | intList3 = intList3; // self-assign checker 32 | } 33 | 34 | d2ds_assert_eq(d2ds::DefaultAllocator::deallocate_counter(), 9); 35 | 36 | XLINGS_WAIT 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.3.cpp: -------------------------------------------------------------------------------- 1 | // slist.3.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的移动语义 5 | // 6 | // 目标/要求: 7 | // - 实现 移动构造 和 移动赋值, 并通过资源分配与释放的检查 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = true; 19 | 20 | { 21 | d2ds::SLinkedList intList1 = { 1, 2, 3 }; 22 | d2ds::SLinkedList intList2(std::move(intList1)); 23 | 24 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 3); 25 | 26 | d2ds::SLinkedList intList3; 27 | intList3 = std::move(intList2); 28 | 29 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 3); 30 | 31 | intList3 = std::move(intList3); // self-assign checker 32 | 33 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 3); 34 | } 35 | 36 | d2ds_assert_eq(d2ds::DefaultAllocator::deallocate_counter(), 3); 37 | 38 | XLINGS_WAIT 39 | 40 | return 0; 41 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.4.cpp: -------------------------------------------------------------------------------- 1 | // slist.4.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的常用函数 - size/empty 5 | // 6 | // 目标/要求: 7 | // - 实现 size/empty 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList; 19 | 20 | d2ds_assert_eq(intList.size(), 0); 21 | d2ds_assert(intList.empty()); 22 | 23 | d2ds::SLinkedList intList1 = { 1, 2, 3 }; 24 | 25 | d2ds_assert_eq(intList1.size(), 3); 26 | d2ds_assert(!intList1.empty()); 27 | 28 | XLINGS_WAIT 29 | 30 | return 0; 31 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.5.cpp: -------------------------------------------------------------------------------- 1 | // slist.5.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的常用函数 - front/back 5 | // 6 | // 目标/要求: 7 | // - 实现 front/back 访问第一个元素和最后一个元素 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList = { 1, 2, 3 }; 19 | 20 | d2ds_assert_eq(intList.front(), 1); 21 | d2ds_assert_eq(intList.back(), 3); 22 | 23 | XLINGS_WAIT 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.6.cpp: -------------------------------------------------------------------------------- 1 | // slist.6.cpp - readonly 2 | // 3 | // 描述: 4 | // 链表的"随机访问/下标索引访问用法模拟" 5 | // 6 | // 目标/要求: 7 | // - 实现重载operator[], 模拟实现下标索引访问 (一般不直接提供) 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList = { 0, 1, 2, 3, 4, 5 }; 19 | 20 | for (int i = 0; i < intList.size(); i++) { 21 | d2ds_assert_eq(intList[i], i); 22 | } 23 | 24 | XLINGS_WAIT 25 | 26 | return 0; 27 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.7.cpp: -------------------------------------------------------------------------------- 1 | // slist.7.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的尾部增删 - push_back / _pop_back 5 | // 6 | // 目标/要求: 7 | // - 实现单链表的_pop_back(通常不直接提供这个操作) 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList; 19 | 20 | for (int i = 0; i < 5; i++) { 21 | intList.push_back(i); 22 | } 23 | 24 | for (int i = 0; i < intList.size(); i++) { 25 | d2ds_assert_eq(intList[i], i); 26 | } 27 | 28 | for (int i = 0; i < 5; i++) { 29 | d2ds_assert_eq(intList.size(), 5 - i); 30 | intList._pop_back(); 31 | } 32 | 33 | d2ds_assert(intList.empty()); 34 | 35 | XLINGS_WAIT 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.8.cpp: -------------------------------------------------------------------------------- 1 | // slist.8.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现链表的前置增删 - push_front / pop_front 5 | // 6 | // 目标/要求: 7 | // - 实现单链表的push_front / pop_front 函数 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList; 19 | 20 | for (int i = 0; i < 5; i++) { 21 | intList.push_front(i); 22 | } 23 | 24 | for (int i = 0; i < intList.size(); i++) { 25 | d2ds_assert_eq(intList[i], 5 - i - 1); 26 | } 27 | 28 | for (int i = 0; i < 5; i++) { 29 | d2ds_assert_eq(intList.front(), 5 - i - 1); 30 | intList.pop_front(); 31 | } 32 | 33 | d2ds_assert(intList.empty()); 34 | 35 | XLINGS_WAIT 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.it.0.cpp: -------------------------------------------------------------------------------- 1 | // slist.it.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义SLinkedList的迭代器类型, 用来管理当前节点的数据访问 5 | // 6 | // 目标/要求: 7 | // - 定义SLinkedListIterator类模板, 并在SLinkedList定义对应的类型别名 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedListNode node; 19 | 20 | node.next = nullptr; 21 | node.data = 0; 22 | 23 | d2ds::SLinkedListIterator iterator1(&node); 24 | d2ds::SLinkedList::Iterator iterator2(&node); 25 | 26 | d2ds_assert_eq(sizeof(iterator1), 8); 27 | d2ds_assert_eq(sizeof(iterator2), 8); 28 | 29 | XLINGS_WAIT 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.it.1.cpp: -------------------------------------------------------------------------------- 1 | // slist.it.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现迭代器的类指针行为 5 | // 6 | // 目标/要求: 7 | // - 重载SLinkedListIterator的 -> 和 * 运算符 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | struct MyObj { 19 | char a; 20 | int b; 21 | float c; 22 | }; 23 | MyObj obj {'a', 1, 1.1}; 24 | 25 | d2ds::SLinkedListNode node; 26 | node.data = obj; 27 | 28 | d2ds::SLinkedList::Iterator iterator(&node); 29 | d2ds_assert_eq(iterator->a, obj.a); 30 | d2ds_assert_eq(iterator->b, obj.b); 31 | d2ds_assert_eq(iterator->c, obj.c); 32 | 33 | (*iterator).c = 2.2; 34 | 35 | //d2ds_assert_eq(2.2, node.data.c); // defaul 2.2 is double 36 | d2ds_assert_eq(2.2f, node.data.c); 37 | 38 | XLINGS_WAIT 39 | 40 | return 0; 41 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.it.2.cpp: -------------------------------------------------------------------------------- 1 | // slist.it.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现迭代器的判断等及不等 5 | // 6 | // 目标/要求: 7 | // - 重载operator== 和 operator!= 运算符 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedListNode node; 19 | d2ds::SLinkedList::Iterator iterator1(&node); 20 | d2ds::SLinkedList::Iterator iterator2(&node); 21 | d2ds::SLinkedList::Iterator iterator3(nullptr); 22 | 23 | d2ds_assert(iterator1 == iterator2); 24 | d2ds_assert(iterator2 != iterator3); 25 | 26 | XLINGS_WAIT 27 | 28 | return 0; 29 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.it.3.cpp: -------------------------------------------------------------------------------- 1 | // slist.it.3.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现单链表迭代器的前进迭代操作 5 | // 6 | // 目标/要求: 7 | // - 重载operator++运算符 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedListNode node; 19 | node.next = nullptr; 20 | 21 | d2ds::SLinkedList::Iterator iterator1(&node); 22 | d2ds::SLinkedList::Iterator iterator2(&node); 23 | d2ds::SLinkedList::Iterator iterator3(nullptr); 24 | 25 | d2ds_assert(++iterator1 == iterator3); 26 | d2ds_assert(iterator2++ != iterator3); 27 | d2ds_assert(iterator2 == iterator3); 28 | 29 | XLINGS_WAIT 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.it.4.cpp: -------------------------------------------------------------------------------- 1 | // slist.it.4.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现SLinkedList的begin/end来获取迭代器 5 | // 6 | // 目标/要求: 7 | // - 实现begin/end成员函数 并通过for循环检查 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList = { 5, 4, 3, 2, 1 }; 19 | 20 | auto it = intList.begin(); 21 | 22 | d2ds_assert_eq(*it, 5); 23 | it++; d2ds_assert_eq(*it, 4); 24 | it++; d2ds_assert_eq(*it, 3); 25 | it++; d2ds_assert_eq(*it, 2); 26 | it++; d2ds_assert_eq(*it, 1); 27 | 28 | d2ds_assert(++it == intList.end()); 29 | 30 | int tmp = 5; 31 | for (auto val : intList) { 32 | d2ds_assert_eq(val, tmp); 33 | tmp--; 34 | } 35 | 36 | XLINGS_WAIT 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /dslings/tests/slinked-list/slist.it.5.cpp: -------------------------------------------------------------------------------- 1 | // slist.it.5.cpp - readonly 2 | // 3 | // 描述: 4 | // 实现SLinkedList的插入/删除操作 5 | // 6 | // 目标/要求: 7 | // - 实现erase_after/insert_after成员函数 8 | // - 在exercises/linked-list/SLinkedList.hpp中完成你的代码设计 9 | // - 通过所有编译器检测 和 断言 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include "exercises/linked-list/SLinkedList.hpp" 15 | 16 | int main() { 17 | 18 | d2ds::SLinkedList intList = { 5, 4, 3, 2, 1 }; 19 | 20 | auto it = intList.begin(); 21 | intList.erase_after(it); 22 | ++it; d2ds_assert_eq(*it, 3); 23 | 24 | for (int val : intList) { 25 | std::cout << " " << val; 26 | } 27 | 28 | std::cout << std::endl; 29 | 30 | it = intList.begin(); 31 | intList.insert_after(it, 6); 32 | ++it; d2ds_assert_eq(*it, 6); 33 | 34 | for (int val : intList) { 35 | std::cout << " " << val; 36 | } 37 | 38 | std::cout << std::endl; 39 | 40 | XLINGS_WAIT 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.0.0.cpp: -------------------------------------------------------------------------------- 1 | // vector.0.cpp - readonly 2 | // 3 | // 描述: 4 | // 定义Vector 5 | // 6 | // 目标/要求: 7 | // - 实现类模板和数据初成员定义 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | struct DataMember { 17 | int size; 18 | void *ptr; 19 | }; 20 | 21 | int main() { 22 | 23 | d2ds::Vector intVec; 24 | d2ds_assert(sizeof(intVec) >= sizeof(DataMember)); 25 | 26 | XLINGS_WAIT 27 | 28 | return 0; 29 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.0.1.cpp: -------------------------------------------------------------------------------- 1 | // vector.1.cpp - readonly 2 | // 3 | // 描述: 4 | // Vector自定义分配器支持 5 | // 6 | // struct AllocatorInterface { 7 | // static void * allocate(int bytes); 8 | // static void deallocate(void *addr, int bytes); 9 | // }; 10 | // 11 | // 目标/要求: 12 | // - 开发者能使用默认分配器, 也可以通过模板的第二个参数配置自定义的分配器 13 | // - 在exercises/array/Vector.hpp中完成你的代码设计 14 | // - 通过编译器检测 15 | // 16 | 17 | #include "common/common.hpp" 18 | 19 | #include 20 | 21 | struct StackMemAllocator { 22 | 23 | static void * allocate(int bytes) { 24 | HONLY_LOGI("StackMemAllocator: try to allocate %d bytes", bytes); 25 | mTop = mTop - bytes; 26 | assert(mTop >= mMemory); 27 | return mTop; 28 | } 29 | 30 | static void deallocate(void *addr, int bytes) { 31 | HONLY_LOGI("StackMemAllocator: free addr %p, bytes %d", addr, bytes); 32 | // nothing 33 | } 34 | 35 | static void config_and_init(char *memory, int size) { 36 | mMemory = memory; 37 | mTop = memory + size; 38 | } 39 | 40 | static char *mTop; 41 | static char *mMemory; 42 | }; 43 | 44 | char * StackMemAllocator::mTop = nullptr; 45 | char * StackMemAllocator::mMemory = nullptr; 46 | 47 | int main() { 48 | 49 | char stackMemory[1024]; 50 | StackMemAllocator::config_and_init(stackMemory, 1024); 51 | 52 | { 53 | int *intPtr = (int *) StackMemAllocator::allocate(sizeof(4)); 54 | *intPtr = 1010; 55 | std::cout << intPtr << ": " << *intPtr << std::endl; 56 | StackMemAllocator::deallocate(intPtr, sizeof(intPtr)); 57 | } 58 | 59 | d2ds::Vector intVec; 60 | d2ds::Vector charVecByStack; 61 | d2ds::Vector doubleVec; 62 | 63 | XLINGS_WAIT 64 | 65 | return 0; 66 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.0.cpp: -------------------------------------------------------------------------------- 1 | // vector.0.cpp - readonly 2 | // 3 | // 描述: 4 | // Vector的不同初始化方式 5 | // 6 | // 目标/要求: 7 | // - 实现 默认初始化、指定长度初始化、列表初始化 对应的构造函数 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | struct StackMemAllocator { 17 | 18 | static void * allocate(int bytes) { 19 | HONLY_LOGI("StackMemAllocator: try to allocate %d bytes", bytes); 20 | mTop = mTop - bytes; 21 | assert(mTop >= mMemory); 22 | return mTop; 23 | } 24 | 25 | static void deallocate(void *addr, int bytes) { 26 | HONLY_LOGI("StackMemAllocator: free addr %p, bytes %d", addr, bytes); 27 | // nothing 28 | } 29 | 30 | static void config_and_init(char *memory, int size) { 31 | mMemory = memory; 32 | mTop = memory + size; 33 | } 34 | 35 | static char *mTop; 36 | static char *mMemory; 37 | }; 38 | 39 | char * StackMemAllocator::mTop = nullptr; 40 | char * StackMemAllocator::mMemory = nullptr; 41 | 42 | 43 | int main() { 44 | 45 | d2ds::DefaultAllocator::debug() = true; 46 | 47 | char stackMemory[1024]; 48 | StackMemAllocator::config_and_init(stackMemory, 1024); 49 | 50 | d2ds::Vector intVec; 51 | d2ds::Vector charVecByStack(10); 52 | d2ds::Vector doubleVec = { 1.1, 2.2, 3.3 }; 53 | 54 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 1); 55 | 56 | XLINGS_WAIT 57 | 58 | return 0; 59 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.1.0.cpp: -------------------------------------------------------------------------------- 1 | // vector.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 显示定义数据结构Vector的bigfive - 析构 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的析构函数 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | using d2ds::BigFiveTest; 17 | 18 | int main() { 19 | 20 | d2ds::DefaultAllocator::debug() = true; 21 | 22 | { 23 | d2ds::Vector objArr(1); 24 | } 25 | 26 | d2ds_assert(BigFiveTest::destructor()); 27 | d2ds_assert_eq(1,d2ds::DefaultAllocator::deallocate_counter()); 28 | 29 | XLINGS_WAIT 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.1.1.cpp: -------------------------------------------------------------------------------- 1 | // vector.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 显示定义数据结构Vector的bigfive - 拷贝语义 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的拷贝构造和拷贝赋值 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | using d2ds::BigFiveTest; 17 | 18 | int main() { 19 | 20 | d2ds::DefaultAllocator::debug() = true; 21 | 22 | { 23 | d2ds::Vector objArr1(5); 24 | 25 | d2ds::Vector objArr2(objArr1); 26 | d2ds::Vector objArr3 = objArr1; 27 | d2ds_assert(BigFiveTest::copy_constructor()); 28 | 29 | D2DS_RETURN 30 | 31 | objArr3 = objArr2; 32 | //d2ds_assert(BigFiveTest::copy_assignment()); 33 | 34 | objArr1 = objArr1; 35 | } 36 | 37 | d2ds_assert(BigFiveTest::destructor(true)); 38 | 39 | XLINGS_WAIT 40 | 41 | return 0; 42 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.1.2.cpp: -------------------------------------------------------------------------------- 1 | // vector.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 显示定义数据结构Vector的bigfive - 移动语义 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的移动构造和移动赋值 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | using d2ds::BigFiveTest; 17 | 18 | int main() { 19 | 20 | d2ds::DefaultAllocator::debug() = true; 21 | 22 | { 23 | d2ds::Vector objArr1(5), objArr2(10); 24 | d2ds::Vector objArr3 { std::move(objArr1) }; 25 | //d2ds_assert(BigFiveTest::move_constructor()); 26 | 27 | objArr3 = std::move(objArr2); 28 | //d2ds_assert(BigFiveTest::move_assignment()); 29 | 30 | objArr3 = std::move(objArr3); 31 | 32 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 2); 33 | } 34 | 35 | d2ds_assert_eq(d2ds::DefaultAllocator::deallocate_counter(), 2); 36 | 37 | d2ds_assert(BigFiveTest::self_assignment()); 38 | d2ds_assert(BigFiveTest::destructor(true)); 39 | 40 | XLINGS_WAIT 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.1.cpp: -------------------------------------------------------------------------------- 1 | // vector.1.cpp - readonly 2 | // 3 | // 描述: 4 | // 显示定义数据结构Vector的bigfive 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的拷贝语义和移动语义 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | using d2ds::BigFiveTest; 17 | 18 | int main() { 19 | 20 | d2ds::DefaultAllocator::debug() = true; 21 | 22 | { 23 | d2ds::Vector objArr(1); 24 | } 25 | d2ds_assert(BigFiveTest::destructor()); 26 | 27 | { 28 | d2ds::Vector objArr1(5); 29 | 30 | d2ds::Vector objArr2(objArr1); 31 | d2ds::Vector objArr3 = objArr1; 32 | d2ds_assert(BigFiveTest::copy_constructor()); 33 | 34 | objArr3 = objArr2; 35 | //d2ds_assert(BigFiveTest::copy_assignment()); 36 | 37 | objArr1 = objArr1; 38 | } 39 | 40 | { 41 | d2ds::DefaultAllocator::clear_status(); 42 | 43 | d2ds::Vector objArr1(5), objArr2(10); 44 | d2ds::Vector objArr3 { std::move(objArr1) }; 45 | //d2ds_assert(BigFiveTest::move_constructor()); 46 | 47 | objArr3 = std::move(objArr2); 48 | //d2ds_assert(BigFiveTest::move_assignment()); 49 | 50 | objArr3 = std::move(objArr3); 51 | 52 | d2ds_assert_eq(d2ds::DefaultAllocator::allocate_counter(), 2); 53 | } 54 | 55 | d2ds_assert_eq( 56 | 2, 57 | d2ds::DefaultAllocator::deallocate_counter() 58 | ); 59 | 60 | d2ds_assert(BigFiveTest::self_assignment()); 61 | d2ds_assert(BigFiveTest::destructor(true)); 62 | 63 | XLINGS_WAIT 64 | 65 | return 0; 66 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.2.cpp: -------------------------------------------------------------------------------- 1 | // vector.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Vector的常用函数和数据访问 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的empty/size/operator[] 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = false; 19 | 20 | d2ds::Vector intArr1; 21 | d2ds_assert(intArr1.empty()); 22 | 23 | d2ds::Vector intArr2(10); 24 | d2ds_assert_eq(intArr2.size(), 10); 25 | 26 | d2ds::Vector intArr3 = { -1, -2, -3 }; 27 | const d2ds::Vector constIntArr3 = { 1, 2, 3 }; 28 | 29 | d2ds_assert_eq(intArr3[1], -2); 30 | 31 | intArr3[0] = constIntArr3[1] * intArr3[2]; 32 | d2ds_assert_eq(intArr3[0], 2 * -3); 33 | 34 | XLINGS_WAIT 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.3.0.cpp: -------------------------------------------------------------------------------- 1 | // vector.3.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Vector的最大内存容量capacity标记 5 | // 6 | // 目标/要求: 7 | // - Vector中 添加capacity成员和函数, 并更新所有构造函数和bigfive 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = false; 19 | 20 | { // default cntor() 21 | d2ds::Vector intArr1, intArr2; 22 | d2ds_assert_eq(intArr1.capacity(), intArr2.capacity()); 23 | d2ds_assert_eq(intArr1.size(), intArr2.capacity()); 24 | } 25 | 26 | { // cntor(int) 27 | d2ds::Vector intArr(10); 28 | d2ds_assert_eq(intArr.capacity(), 10); 29 | } 30 | 31 | { // cntor(initializer_list) 32 | d2ds::Vector intArr = {0, 1, 2, 3}; 33 | d2ds_assert_eq(intArr.capacity(), 4); 34 | } 35 | 36 | { // copy-sem 37 | d2ds::Vector intArr1 = {0, 1, 2, 3}; 38 | d2ds::Vector intArr2(intArr1), intArr3; 39 | d2ds_assert_eq(intArr1.capacity(), intArr2.capacity()); 40 | 41 | intArr3 = intArr2; 42 | d2ds_assert_eq(intArr3.capacity(), intArr2.capacity()); 43 | } 44 | 45 | { // move-sem 46 | d2ds::Vector intArr1(10); 47 | d2ds::Vector intArr2(std::move(intArr1)), intArr3; 48 | 49 | d2ds_assert_eq(intArr1.capacity(), 0); 50 | d2ds_assert_eq(intArr2.capacity(), 10); 51 | 52 | intArr3 = std::move(intArr2); 53 | 54 | d2ds_assert_eq(intArr3.capacity(), 10); 55 | d2ds_assert_eq(intArr2.capacity(), 0); 56 | } 57 | 58 | XLINGS_WAIT 59 | 60 | return 0; 61 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.3.cpp: -------------------------------------------------------------------------------- 1 | // vector.3.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Vector的扩容和缓存机制 5 | // 扩容/缩容机制: 二倍缩放 6 | // 扩容条件: mSize_e + 1 > mCapacity_e 7 | // 缩容条件: mSize_e <= mCapacity_e / 3 8 | // 9 | // 目标/要求: 10 | // - 实现Vector的 capacity/push_back/pop_back/resize 11 | // - 在exercises/array/Vector.hpp中完成你的代码设计 12 | // - 通过编译器检测 13 | // 14 | 15 | #include "common/common.hpp" 16 | 17 | #include 18 | 19 | int main() { 20 | 21 | d2ds::DefaultAllocator::debug() = false; 22 | 23 | { // push_back 24 | d2ds::Vector intArr; 25 | intArr.push_back(1); d2ds_assert_eq(intArr[0], 1); 26 | d2ds_assert_eq(intArr.capacity(), 2); 27 | intArr.push_back(2); d2ds_assert_eq(intArr[1], 2); 28 | intArr.push_back(3); d2ds_assert_eq(intArr[2], 3); 29 | d2ds_assert_eq(intArr.size(), 3); 30 | d2ds_assert_eq(intArr.capacity(), 4); 31 | d2ds_assert_eq(intArr[1], 2); 32 | } 33 | 34 | D2DS_RETURN 35 | 36 | { // pop_back 37 | d2ds::Vector intArr = { 1, 2, 3, 4 }; 38 | intArr.push_back(5); d2ds_assert_eq(intArr[0], 1); 39 | intArr.pop_back(); 40 | intArr.pop_back(); 41 | d2ds_assert_eq(intArr.capacity(), 8); 42 | intArr.pop_back(); d2ds_assert_eq(intArr[intArr.size() - 1], 2); 43 | d2ds_assert_eq(intArr.size(), 2); 44 | d2ds_assert_eq(intArr.capacity(), 4); 45 | } 46 | 47 | XLINGS_WAIT 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.4.cpp: -------------------------------------------------------------------------------- 1 | // vector.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Vector的迭代器支持 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的begin/end 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = false; 19 | 20 | d2ds::Vector intArr = { 4, 3, 2, 1 }; 21 | 22 | for (auto &val : intArr) { 23 | val *= 2; 24 | } 25 | 26 | const d2ds::Vector constIntArr = intArr; 27 | 28 | int sum = 0; 29 | for (auto &val : constIntArr) { 30 | sum += val; 31 | } 32 | 33 | d2ds_assert_eq(sum, (4 + 3 + 2 + 1) * 2); 34 | 35 | XLINGS_WAIT 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /dslings/tests/vector/vector.5.cpp: -------------------------------------------------------------------------------- 1 | // vector.2.cpp - readonly 2 | // 3 | // 描述: 4 | // 数据结构Vector的向量加减法扩展和判断等 5 | // 6 | // 目标/要求: 7 | // - 实现Vector的operator+/operator-/operator==的重载 8 | // - 在exercises/array/Vector.hpp中完成你的代码设计 9 | // - 通过编译器检测 10 | // 11 | 12 | #include "common/common.hpp" 13 | 14 | #include 15 | 16 | int main() { 17 | 18 | d2ds::DefaultAllocator::debug() = false; 19 | 20 | d2ds::Vector OQ = { 4, 1, 1 }; 21 | d2ds::Vector OP = { 2, 4, 4 }; 22 | d2ds::Vector QP = { -2, 3, 3 }; 23 | 24 | d2ds_assert(OQ + QP == OP); 25 | d2ds_assert(OP - OQ == QP); 26 | 27 | XLINGS_WAIT 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /dslings/xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.debug") 2 | 3 | set_languages("cxx11") 4 | add_includedirs(".") 5 | 6 | -- verify lib 7 | add_includedirs("dstruct") 8 | 9 | target("0.dslings-0") 10 | set_kind("binary") 11 | add_files("tests/dslings.0.cpp") 12 | 13 | target("0.dslings-1") 14 | set_kind("binary") 15 | add_files("tests/dslings.1.cpp") 16 | 17 | target("0.dslings-2") 18 | set_kind("binary") 19 | add_files("tests/dslings.2.cpp") 20 | 21 | target("1.template-0") 22 | set_kind("binary") 23 | add_files("tests/other/cpp-base/template.0.cpp") 24 | 25 | target("1.template-1") 26 | set_kind("binary") 27 | add_files("tests/other/cpp-base/template.1.cpp") 28 | 29 | target("1.template-2") 30 | set_kind("binary") 31 | add_files("tests/other/cpp-base/template.2.cpp") 32 | 33 | target("2.range_for-0") 34 | set_kind("binary") 35 | add_files("tests/other/cpp-base/range_for.0.cpp") 36 | 37 | target("2.range_for-1") 38 | set_kind("binary") 39 | add_files("tests/other/cpp-base/range_for.1.cpp") 40 | 41 | target("2.range_for-2") 42 | set_kind("binary") 43 | add_files("tests/other/cpp-base/range_for.2.cpp") 44 | 45 | target("2.range_for-3") 46 | set_kind("binary") 47 | add_files("tests/other/cpp-base/range_for.3.cpp") 48 | 49 | target("3.array-0") 50 | set_kind("binary") 51 | add_files("tests/array/array.0.cpp") 52 | 53 | target("3.array-1") 54 | set_kind("binary") 55 | add_files("tests/array/array.1.cpp") 56 | 57 | target("3.array-2") 58 | set_kind("binary") 59 | add_files("tests/array/array.2.cpp") 60 | 61 | target("3.array-3") 62 | set_kind("binary") 63 | add_files("tests/array/array.3.cpp") 64 | 65 | target("3.array-4") 66 | set_kind("binary") 67 | add_files("tests/array/array.4.cpp") 68 | 69 | target("3.array-5") 70 | set_kind("binary") 71 | add_files("tests/array/array.5.cpp") 72 | 73 | target("3.array-6") 74 | set_kind("binary") 75 | add_files("tests/array/array.6.cpp") 76 | 77 | target("4.vector-0-0") 78 | set_kind("binary") 79 | add_files("tests/vector/vector.0.0.cpp") 80 | 81 | target("4.vector-0-1") 82 | set_kind("binary") 83 | add_files("tests/vector/vector.0.1.cpp") 84 | 85 | target("4.vector-0-all") 86 | set_kind("binary") 87 | add_files("tests/vector/vector.0.cpp") 88 | 89 | target("4.vector-1-0") 90 | set_kind("binary") 91 | add_files("tests/vector/vector.1.0.cpp") 92 | 93 | target("4.vector-1-1") 94 | set_kind("binary") 95 | add_files("tests/vector/vector.1.1.cpp") 96 | 97 | target("4.vector-1-2") 98 | set_kind("binary") 99 | add_files("tests/vector/vector.1.2.cpp") 100 | 101 | target("4.vector-1-all") 102 | set_kind("binary") 103 | add_files("tests/vector/vector.1.cpp") 104 | 105 | target("4.vector-2") 106 | set_kind("binary") 107 | add_files("tests/vector/vector.2.cpp") 108 | 109 | target("4.vector-3-0") 110 | set_kind("binary") 111 | add_files("tests/vector/vector.3.0.cpp") 112 | 113 | target("4.vector-3-all") 114 | set_kind("binary") 115 | add_files("tests/vector/vector.3.cpp") 116 | 117 | target("4.vector-4") 118 | set_kind("binary") 119 | add_files("tests/vector/vector.4.cpp") 120 | 121 | target("4.vector-5") 122 | set_kind("binary") 123 | add_files("tests/vector/vector.5.cpp") 124 | 125 | target("5.embedded-slist-0") 126 | set_kind("binary") 127 | add_files("tests/embedded-list/embedded-slist.0.cpp") 128 | 129 | target("5.embedded-slist-1") 130 | set_kind("binary") 131 | add_files("tests/embedded-list/embedded-slist.1.cpp") 132 | 133 | target("5.embedded-slist-2") 134 | set_kind("binary") 135 | add_files("tests/embedded-list/embedded-slist.2.cpp") 136 | 137 | target("5.embedded-slist-3") 138 | set_kind("binary") 139 | add_files("tests/embedded-list/embedded-slist.3.cpp") 140 | 141 | target("5.embedded-slist-4") 142 | set_kind("binary") 143 | add_files("tests/embedded-list/embedded-slist.4.cpp") 144 | 145 | target("6.slinked-list-0") 146 | set_kind("binary") 147 | add_files("tests/slinked-list/slist.0.cpp") 148 | 149 | target("6.slinked-list-1") 150 | set_kind("binary") 151 | add_files("tests/slinked-list/slist.1.cpp") 152 | 153 | target("6.slinked-list-2") 154 | set_kind("binary") 155 | add_files("tests/slinked-list/slist.2.cpp") 156 | 157 | target("6.slinked-list-3") 158 | set_kind("binary") 159 | add_files("tests/slinked-list/slist.3.cpp") 160 | 161 | target("6.slinked-list-4") 162 | set_kind("binary") 163 | add_files("tests/slinked-list/slist.4.cpp") 164 | 165 | target("6.slinked-list-5") 166 | set_kind("binary") 167 | add_files("tests/slinked-list/slist.5.cpp") 168 | 169 | target("6.slinked-list-6") 170 | set_kind("binary") 171 | add_files("tests/slinked-list/slist.6.cpp") 172 | 173 | target("6.slinked-list-7") 174 | set_kind("binary") 175 | add_files("tests/slinked-list/slist.7.cpp") 176 | 177 | target("6.slinked-list-8") 178 | set_kind("binary") 179 | add_files("tests/slinked-list/slist.8.cpp") 180 | 181 | target("7.slinked-list-iterator-0") 182 | set_kind("binary") 183 | add_files("tests/slinked-list/slist.it.0.cpp") 184 | 185 | target("7.slinked-list-iterator-1") 186 | set_kind("binary") 187 | add_files("tests/slinked-list/slist.it.1.cpp") 188 | 189 | target("7.slinked-list-iterator-2") 190 | set_kind("binary") 191 | add_files("tests/slinked-list/slist.it.2.cpp") 192 | 193 | target("7.slinked-list-iterator-3") 194 | set_kind("binary") 195 | add_files("tests/slinked-list/slist.it.3.cpp") 196 | 197 | target("7.slinked-list-iterator-4") 198 | set_kind("binary") 199 | add_files("tests/slinked-list/slist.it.4.cpp") 200 | 201 | target("7.slinked-list-iterator-5") 202 | set_kind("binary") 203 | add_files("tests/slinked-list/slist.it.5.cpp") 204 | 205 | target("8.embedded-dlist-0") 206 | set_kind("binary") 207 | add_files("tests/embedded-list/embedded-dlist.0.cpp") 208 | 209 | target("8.embedded-dlist-1") 210 | set_kind("binary") 211 | add_files("tests/embedded-list/embedded-dlist.1.cpp") 212 | 213 | target("8.embedded-dlist-2") 214 | set_kind("binary") 215 | add_files("tests/embedded-list/embedded-dlist.2.cpp") 216 | 217 | target("8.embedded-dlist-3") 218 | set_kind("binary") 219 | add_files("tests/embedded-list/embedded-dlist.3.cpp") 220 | 221 | target("8.embedded-dlist-4") 222 | set_kind("binary") 223 | add_files("tests/embedded-list/embedded-dlist.4.cpp") --------------------------------------------------------------------------------