diff --git a/README.md b/README.md index 6754cfa..0808160 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # C++ 那些事 +[English version is here](./README_EN.md) + 感谢各位对《C++ 那些事》的支持,现将内容也同步至网站,可以打开网站直接阅读~欢迎大家 star、转发、PR。 @@ -10,6 +12,8 @@ https://light-city.club/sc/ ### 关于作者 +公众号已开放两大入口,分别是专辑与菜单,可以直接在微信公众号上阅读《C++那些事》内容,配上该仓库的代码,将会非常的爽,建议大家关注一波。 + 个人公众号:guangcity ![](https://light-city.club/sc/assets/images/wechat.jpg) @@ -26,9 +30,13 @@ https://light-city.club/sc/ Ubuntu 18.04 -- **工具** +- **编辑工具** - CLion gcc/g++ + CLion + +- **编译器** + +> gcc/g++ 尽量用g++,因为一开始我用的vim写的,所以纯g++,后来用编译器CLion,部分有CMakeLists.txt文件,直接导入就行,后面会更新这个文件包含整个项目,持续关注~ @@ -145,7 +153,7 @@ C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。 #### 3.1 [C++11 新特性](./c++2.0/c++11) -- [Variadic Templates](./c++2.0/variadic) +- [Variadic Templates](./c++2.0/c++11/variadic) - Spaces in Template Expressions @@ -154,17 +162,17 @@ C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。 vector> // before c++ 11 error error: ‘>>’ should be ‘> >’ within a nested template argument list,c++11后可以正常通过 ``` -- [nullptr and nullptr_t](./c++2.0/nullptr.cpp) +- [nullptr and nullptr_t](./c++2.0/c++11/nullptr.cpp) -- [Automatic Type Deduction with auto](./c++2.0/auto.cpp) +- [Automatic Type Deduction with auto](./c++2.0/c++11/auto.cpp) -- [Uniform Initialization ](./c++2.0/uniform_initialization.cpp) +- [Uniform Initialization ](./c++2.0/c++11/uniform_initialization.cpp) -- [initializer_list](./c++2.0/initializer.cpp) +- [initializer_list](./c++2.0/c++11/initializer.cpp) -- [explicit for ctors taking more than one argument](./c++2.0/explicit.cpp) +- [explicit for ctors taking more than one argument](./c++2.0/c++11/explicit.cpp) -- [range-based for statement](./c++2.0/auto.cpp) +- [range-based for statement](./c++2.0/c++11/auto.cpp) ```cpp for(decl:col) { @@ -172,33 +180,33 @@ C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。 } ``` -- [=default,=delete](./c++2.0/default_delete.cpp) +- [=default,=delete](./c++2.0/c++11/default_delete.cpp) 如果你自行定义了一个 ctor,那么编译器就不会给你一个 default ctor 如果强制加上 =default,就可以重新获得并使用 default ctor. - Alias(化名)Template(template typedef) - - [alias.cpp](./c++2.0/alias.cpp) - - [template_template.cpp](./c++2.0/template_template.cpp) + - [alias.cpp](./c++2.0/c++11/alias.cpp) + - [template_template.cpp](./c++2.0/c++11/template_template.cpp) - [template template parameter](./c++2.0/template_template.cpp) -- [type alias](./c++2.0/type_alias.cpp) +- [type alias](./c++2.0/c++11/type_alias.cpp) -- [noexcept](./c++2.0/noexcept.cpp) +- [noexcept](./c++2.0/c++11/noexcept.cpp) -- [override](./c++2.0/override.cpp) +- [override](./c++2.0/c++11/override.cpp) -- [final](./c++2.0/final.cpp) +- [final](./c++2.0/c++11/final.cpp) -- [decltype](./c++2.0/decltype.cpp) +- [decltype](./c++2.0/c++11/decltype.cpp) -- [lambda](./c++2.0/lambda.cpp) +- [lambda](./c++2.0/c++11/lambda.cpp) -- [Rvalue reference](./c++2.0/rvalue.cpp) +- [Rvalue reference](./c++2.0/c++11/rvalue.cpp) -- [move aware class](./c++2.0/move.cpp) +- [move aware class](./c++2.0/c++11/move.cpp) - 容器-结构与分类 @@ -206,9 +214,9 @@ C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。 - (2) 关联式容器包括:`set/multiset`,`map/multimap` - (3) 无序容器(C++2.0 新引入,更换原先 `hash_xxx` 为 `unordered_xxx`)包括:`unordered_map/unordered_multimap,unordered_set/unordered_multiset` -- [Hash Function](./c++2.0/hash.cpp) +- [Hash Function](./c++2.0/c++11/hash.cpp) -- [tuple](./c++2.0/tuple.cpp) +- [tuple](./c++2.0c++11//tuple.cpp) 学习资料:https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061 @@ -219,6 +227,7 @@ C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。 ### 4.设计模式 - [单例模式](./design_pattern/singleton) +- [生产消费者模式](./design_pattern/producer_consumer) ### 5. [STL 源码剖析](./src_analysis/stl) @@ -344,24 +353,15 @@ map mp{ ### 11.贡献者 - - - - - - - -
- -
- Francis -

-
- -
- ChungZH -

-
+ + +| 贡献人 | 地址 | +| ------- | ----------------------------- | +| 光城 | https://github.com/Light-City | +| ChungZH | https://github.com/ChungZH | +| xliu79 | https://github.com/xliu79 | + + ### 12.赞助我 diff --git a/README.md~ b/README.md~ deleted file mode 100644 index 44cd0bb..0000000 --- a/README.md~ +++ /dev/null @@ -1,357 +0,0 @@ -# C++ 那些事 - - -感谢各位对《C++ 那些事》的支持,现将内容也同步至网站,可以打开网站直接阅读~欢迎大家 star、转发、PR。 - -[直通点](https://light-city.club/sc/) - ------------------------ - - -### 0.项目概要 - -- 中文名:**C++ 那些事** -- 英文名:**Stories About C Plus Plus** - -这是一个适合初学者从**入门到进阶**的仓库,解决了**面试者与学习者**想要**深入 C++**及如何**入坑 C++**的问题。除此之外,本仓库拓展了更加深入的源码分析,多线程并发等的知识,是一个比较全面的 C++ 学习从入门到进阶提升的仓库。 - -### 1.基础进阶 - -- [const 那些事](./basic_content/const) -- [static 那些事](./basic_content/static) -- [this 那些事](./basic_content/this) -- [inline 那些事](./basic_content/inline) -- [sizeof 那些事](./basic_content/sizeof) -- [函数指针那些事](./basic_content/func_pointer) -- [纯虚函数和抽象类那些事](./basic_content/abstract) -- [vptr_vtable 那些事](./basic_content/vptr_vtable) -- [virtual 那些事](./basic_content/virtual) -- [volatile 那些事](./basic_content/volatile) -- [assert 那些事](./basic_content/assert) -- [位域那些事](./basic_content/bit) -- [extern 那些事](./basic_content/extern) -- [struct 那些事](./basic_content/struct) -- [struct 与 class 那些事](./basic_content/struct_class) -- [union 那些事](./basic_content/union) -- [c 实现 c++ 多态那些事](./basic_content/c_poly) -- [explicit 那些事](./basic_content/explicit) -- [friend 那些事](./basic_content/friend) -- [using 那些事](./basic_content/using) -- [:: 那些事](./basic_content/maohao) -- [enum 那些事](./basic_content/enum) -- [decltype 那些事](./basic_content/decltype) -- [引用与指针那些事](./basic_content/pointer_refer) -- [宏那些事](./basic_content/macro) - -### 2.实战系列 - -#### 2.1 10日狂练 - -- [x] day1 - -基本c++语法 - -- [x] day2 - -递归、结构体、枚举、静态变量等 - -- [x] day3 - -函数 - -- [x] day4 - -函数深入 - -- [x] day5 - -继承多态 - -- [x] day6 - -虚函数、抽象类 - -- [x] day7 - -运算符重载 - -- [x] day8 - -模板与STL - -- [x] day9 - -异常 - -- [x] day10 - -文件与流 - -#### 2.2 重点实战练习 - -├── [中括号重载.cpp](./practical_exercises/key_exercises/中括号重载.cpp) -├── [时钟++运算符重载.cpp](./practical_exercises/key_exercises/时钟++运算符重载.cpp) -├── [运算符重载之强制转换.cpp](./practical_exercises/key_exercises/运算符重载之强制转换.cpp) -└── [重载圆括号的时钟.cpp](./practical_exercises/key_exercises/重载圆括号的时钟.cpp) - -├── [函数模板.cpp](./practical_exercises/key_exercises/函数模板.cpp) - -├── [动态数组.cpp](./practical_exercises/key_exercises/动态数组.cpp) - -├── [字典插入与查找.cpp](./practical_exercises/key_exercises/字典插入与查找.cpp) - -├── [异常捕获.cpp](./practical_exercises/key_exercises/异常捕获.cpp) - -├── [类模板之栈.cpp](./practical_exercises/key_exercises/类模板之栈.cpp) - -├── [类模板特化之数组.cpp](./practical_exercises/key_exercises/类模板特化之数组.cpp) - -├── [继承与封装.cpp](./practical_exercises/key_exercises/继承与封装.cpp) - -├── [读写文件综合题.cpp](./practical_exercises/key_exercises/读写文件综合题.cpp) -├── [输入输出运算符重载.cpp](./practical_exercises/key_exercises/输入输出运算符重载.cpp) -├── [输入输出重载.cpp](./practical_exercises/key_exercises/输入输出重载.cpp) -├── [输出格式.cpp](./practical_exercises/key_exercises/输出格式.cpp) - -### 2.[C++2.0 新特性](./c++2.0) - -#### 2.0 概况 - -C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。 - -#### 2.1 [C++11 新特性](./c++2.0/c++11) - -- [Variadic Templates](./c++2.0/variadic) - -- Spaces in Template Expressions - - ```cpp - vector > //ok in each C++ version - vector> // before c++ 11 error error: ‘>>’ should be ‘> >’ within a nested template argument list,c++11后可以正常通过 - ``` - -- [nullptr and nullptr_t](./c++2.0/nullptr.cpp) - -- [Automatic Type Deduction with auto](./c++2.0/auto.cpp) - -- [Uniform Initialization ](./c++2.0/uniform_initialization.cpp) - -- [initializer_list](./c++2.0/initializer.cpp) - -- [explicit for ctors taking more than one argument](./c++2.0/explicit.cpp) - -- [range-based for statement](./c++2.0/auto.cpp) - - ```cpp - for(decl:col) { - statement - } - ``` - -- [=default,=delete](./c++2.0/default_delete.cpp) - - 如果你自行定义了一个 ctor,那么编译器就不会给你一个 default ctor - 如果强制加上 =default,就可以重新获得并使用 default ctor. - -- Alias(化名)Template(template typedef) - - - [alias.cpp](./c++2.0/alias.cpp) - - [template_template.cpp](./c++2.0/template_template.cpp) - -- [template template parameter](./c++2.0/template_template.cpp) - -- [type alias](./c++2.0/type_alias.cpp) - -- [noexcept](./c++2.0/noexcept.cpp) - -- [override](./c++2.0/override.cpp) - -- [final](./c++2.0/final.cpp) - -- [decltype](./c++2.0/decltype.cpp) - -- [lambda](./c++2.0/lambda.cpp) - -- [Rvalue reference](./c++2.0/rvalue.cpp) - -- [move aware class](./c++2.0/move.cpp) - -- 容器-结构与分类 - - - (1) 序列式容器包括:`array`(C++2.0 新引入),`vector`,`deque`,`list`,`forward_list`(C++2.0 新引入) - - (2) 关联式容器包括:`set/multiset`,`map/multimap` - - (3) 无序容器(C++2.0 新引入,更换原先 `hash_xxx` 为 `unordered_xxx`)包括:`unordered_map/unordered_multimap,unordered_set/unordered_multiset` - -- [Hash Function](./c++2.0/hash.cpp) - -- [tuple](./c++2.0/tuple.cpp) - - 学习资料:https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061 - -### 2.2 C++14/17/20 - -待更新... - -### 3.设计模式 - -- [单例模式](./design_pattern/singleton) - -### 4. [STL 源码剖析](./src_analysis/stl) - -**STL 源码剖析:gcc 4.9.1** - -- [array](./src_analysis/stl/array.md) -- [deque](./src_analysis/stl/deque.md) -- [queue and stack](./src_analysis/stl/queue_stack.md) -- [list](./src_analysis/stl/list.md) -- [vector](./src_analysis/stl/vector.md) -- [typename](./src_analysis/stl/typename.md) -- [traits](./src_analysis/stl/traits.md) -- [iterator](./src_analysis/stl/iterator.md) -- [谈谈 STL 设计之 EBO 优化](./src_analysis/stl/谈谈STL设计之EBO优化.md) -- [rb_tree](./src_analysis/stl/rb_tree.md) -- [set and multiset](set_multiset.md) -- [map and multimap](./src_analysis/stl/map_multimap.md) -- [hashtable](./src_analysis/stl/hashtable.md) -- [myhashtable](./src_analysis/stl/myhashtable.md) -- [unordered_map](./src_analysis/stl/unordered_map.md) - -### 4. [并发编程](./concurrency) - -#### 4.1 C++ Concurrency in Action - -- [第一章](./concurrency/concurrency_v1/chapter1) -- [第二章](./concurrency/concurrency_v1/chapter2) - -学习资料:https://chenxiaowei.gitbook.io/cpp_concurrency_in_action/ - -#### 4.2 多线程与多进程 - -##### 4.2.1 Threading In C++ - -- [介绍](./concurrency/Threading_In_CPlusPlus/1.thread) -- [创建线程的五种类型](./concurrency/Threading_In_CPlusPlus/2.create_type) -- [Join 与 Detachs](./concurrency/Threading_In_CPlusPlus/3.join_detach) -- [mutex in C++ Threading](./concurrency/Threading_In_CPlusPlus/4.mutex) - -> 学习自: -> -> https://www.youtube.com/watch?v=eZ8yKZo-PGw&list=PLk6CEY9XxSIAeK-EAh3hB4fgNvYkYmghp&index=4 - -### 5. [C++ 惯用法](./codingStyleIdioms) - -##### 你最喜欢的 c++ 编程风格惯用法是什么? - -- [1.类初始化列表](./codingStyleIdioms/1_classInitializers) -- [2.枚举类替换命名空间](./codingStyleIdioms/2_enumclass_namespace) -- [3.RAII(资源获取即初始化)](./codingStyleIdioms/3_RAII) -- [4.copy and swap](./codingStyleIdioms/4_copy-swap) -- [5.pImpl(指针指向具体实现)](./codingStyleIdioms/5_pImpl) - -### 6.学习课程 - -#### 6.1 [极客时间《现代 C++ 实战 30 讲》](https://time.geekbang.org/channel/home) - -- [堆、栈、RAII:C++ 里该如何管理资源?](./learn_class/modern_C++_30/RAII) - - [堆](./modern_++_30/RAII/heap.cpp) - - [栈](./learn_class/modern_C++_30/RAII/stack.cpp) - - [RAII](./learn_class/modern_C++_30/RAII/RAII.cpp) -- [自己动手,实现 C++ 的智能指针](./learn_class/modern_C++_30/smart_ptr) - - [auto_ptr、scope_ptr](./learn_class/modern_C++_30/smart_ptr/auto_scope.cpp) - - [unique_ptr](./learn_class/modern_C++_30/smart_ptr/unique_ptr.cpp) - - [shared_ptr](./learn_class/modern_C++_30/smart_ptr/shared_ptr.cpp) -- [右值和移动究竟解决了什么问题?](./learn_class/modern_C++_30/reference) - - [左值与右值](./learn_class/modern_C++_30/reference/reference.cpp) - - [延长声明周期](./learn_class/modern_C++_30/reference/lifetime.cpp) - - [引用折叠](./learn_class/modern_C++_30/reference/collapses.cpp) - - [完美转发](./learn_class/modern_C++_30/reference/forward.cpp) - - [不要返回本地变量的引用](./learn_class/modern_C++_30/reference/don'treturnReference.cpp) -- [容器 1](./learn_class/modern_C++_30/container1) -- [容器 2](./learn_class/modern_C++_30/container2) -- [异常](./learn_class/modern_C++_30/exception) -- [字面量、静态断言和成员函数说明符](./learn_class/modern_C++_30/literalAssert) -- [是不是应该返回对象?](./learn_class/modern_C++_30/returnObj) -- [编译期多态:泛型编程和模板入门](./learn_class/modern_C++_30/compilerpoly) -- [译期能做些什么?一个完整的计算世界](./learn_class/modern_C++_30/compilercompute) -- [SFINAE:不是错误的替换失败是怎么回事?](./learn_class/modern_C++_30/SFINAE) -- [constexpr:一个常态的世界](./learn_class/modern_C++_30/constexpr) -- [函数对象和 lambda:进入函数式编程](./learn_class/modern_C++_30/functionLambda) -- [内存模型和 atomic:理解并发的复杂性](./learn_class/modern_C++_30/memorymodel_atomic) - -### 7.工具篇 - -#### 7.1 [容器快捷输出工具](./tool/output) - -对吴老师的代码进行了修改,[点击此处直通代码](./tool/output/output_container.h) - -输入: - -```cpp -map mp{ - {1, 1}, - {2, 4}, - {3, 9}}; - cout << mp << endl; -``` - -输出结果显示: - -```cpp -{ 1 => 1, 2 => 4, 3 => 9 } -``` - -#### 7.2 像 Python 一样简单输出(Jupyter Notebook) - -- [像 Python 一样玩 C/C++](./tool/像Python一样玩CC++.md) - -#### 7.3 观察编译过程变化 - -- [https://cppinsights.io](https://cppinsights.io/) - -#### 7.4 C++ 的 Debug 工具 dbg-macro - -- [C++ 的 Debug 工具 dbg-macro](./tool/C++的Debug工具dbg-macro.md) - -### 8.拓展部分 - -#### 8.1 一些问题 - -- [C++ 中如何将 string 类型转换为 int 类型?](./extension/some_problem/string_int.md) - -### 9.代码运行 - -- **代码环境** - - Ubuntu 18.04 - -- **工具** - - CLion gcc/g++ - -### 10.贡献者 - - - - - - - - -
- -
- Francis -

-
- -
- ChungZH -

-
- -### 11.关于作者 - -个人公众号: - -![](./img/wechat.jpg) - diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..3651c82 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,347 @@ +# Stories About C Plus Plus + +Thanks for all the support about << Stories About C ++ >>, right now you could open this link(https://light-city.club/sc/) to read this article. +Any star, retweet or pr will be weclomed. + + +----------------------- + + +### 0. Directory + +- Chinese version:**C++ 那些事** +- English version:**Stories About C Plus Plus** + +This repository meets the need of people who wants to really know about C++, and may help you in the interview. Besides, it also provides +other details,such as in-depth source code analysis and multi-threaded concurrency. It is a comprehensive C ++ repository from entry to advanced improvement + +### 1.Foundation + +- [Stories About const](./basic_content/const) +- [Stories About static](./basic_content/static) +- [Stories about this](./basic_content/this) +- [Stories About inline](./basic_content/inline) +- [Stories About sizeof](./basic_content/sizeof) +- [Stories About pointer of function](./basic_content/func_pointer) +- [Stories About pure virtual function and abstract](./basic_content/abstract) +- [Stories About vptr_vtable](./basic_content/vptr_vtable) +- [Stories About virtual](./basic_content/virtual) +- [Stories About volatile](./basic_content/volatile) +- [Stories About assert](./basic_content/assert) +- [Stories About bit](./basic_content/bit) +- [Stories About extern](./basic_content/extern) +- [Stories About struct](./basic_content/struct) +- [Stories About struct and class](./basic_content/struct_class) +- [Stories About union](./basic_content/union) +- [Stories About polymorphism](./basic_content/c_poly) +- [Stories About explicit](./basic_content/explicit) +- [Stories About friend](./basic_content/friend) +- [Stories About using](./basic_content/using) +- [Stories About :: ](./basic_content/maohao) +- [Stories About enum](./basic_content/enum) +- [Stories About decltype](./basic_content/decltype) +- [Stories About pointer_refer](./basic_content/pointer_refer) +- [Stories About macro](./basic_content/macro) + +### 2.Code Samples + +#### 2.1 10 Days Practice + +- [x] day1 + +Basic Grammar About C ++ +- [x] day2 + +Recursive、Structor、Enumerate、Static Variable + +- [x] day3 + +Function + +- [x] day4 + +Thorough About Function + +- [x] day5 + +Inheritance and Polymorphism + +- [x] day6 + +Vitrual Function、Abstruct + +- [x] day7 + +Operator overloading + +- [x] day8 + +Template And STL + +- [x] day9 + +Exception + +- [x] day10 + +File And Stream + +#### 2.2 Practical Exercises + +├── [Square brackets overload .cpp](./practical_exercises/key_exercises/中括号重载.cpp) +├── [Clock++operator overloading.cpp](./practical_exercises/key_exercises/时钟++运算符重载.cpp) +├── [Mandatory conversion of operator overloading.cpp](./practical_exercises/key_exercises/运算符重载之强制转换.cpp) +└── [Clock with overloaded parenthesis.cpp](./practical_exercises/key_exercises/重载圆括号的时钟.cpp) + +├── [Template of Function.cpp](./practical_exercises/key_exercises/函数模板.cpp) + +├── [Dynamic array.cpp](./practical_exercises/key_exercises/动态数组.cpp) + +├── [Dictionary insertion and search.cpp](./practical_exercises/key_exercises/字典插入与查找.cpp) + +├── [Catch Exception.cpp](./practical_exercises/key_exercises/异常捕获.cpp) + +├── [Template of Stack.cpp](./practical_exercises/key_exercises/类模板之栈.cpp) + +├── [Template of Array.cpp](./practical_exercises/key_exercises/类模板特化之数组.cpp) + +├── [Inheritance And Package.cpp](./practical_exercises/key_exercises/继承与封装.cpp) + +├── [Read And Write files.cpp](./practical_exercises/key_exercises/读写文件综合题.cpp) +├── [Operator Overloading About Input and Output.cpp](./practical_exercises/key_exercises/输入输出运算符重载.cpp) +├── [Input And Output Overloading.cpp](./practical_exercises/key_exercises/输入输出重载.cpp) +├── [Input Format.cpp](./practical_exercises/key_exercises/输出格式.cpp) + +### 2.[C++2.0 New Features](./c++2.0) + +#### 2.0 Overview + +C++2.0 is an Abbreviation, meaning「modern C++」,including C++11/14/17/20. + +#### 2.1 [C++11 New Features](./c++2.0/c++11) + +- [Variadic Templates](./c++2.0/variadic) + +- Spaces in Template Expressions + + ```cpp + vector > //ok in each C++ version + vector> // before c++ 11 error error: ‘>>’ should be ‘> >’ within a nested template argument list,version beyond c++ 11 could compile successfully + ``` + +- [nullptr and nullptr_t](./c++2.0/nullptr.cpp) + +- [Automatic Type Deduction with auto](./c++2.0/auto.cpp) + +- [Uniform Initialization ](./c++2.0/uniform_initialization.cpp) + +- [initializer_list](./c++2.0/initializer.cpp) + +- [explicit for ctors taking more than one argument](./c++2.0/explicit.cpp) + +- [range-based for statement](./c++2.0/auto.cpp) + + ```cpp + for(decl:col) { + statement + } + ``` + +- [=default,=delete](./c++2.0/default_delete.cpp) + + If you define a ctor by yourself, compiler won't compile the default ctor. + If you add =default, you could recatch and use default ctor. + +- Alias(化名)Template(template typedef) + + - [alias.cpp](./c++2.0/alias.cpp) + - [template_template.cpp](./c++2.0/template_template.cpp) + +- [template template parameter](./c++2.0/template_template.cpp) + +- [type alias](./c++2.0/type_alias.cpp) + +- [noexcept](./c++2.0/noexcept.cpp) + +- [override](./c++2.0/override.cpp) + +- [final](./c++2.0/final.cpp) + +- [decltype](./c++2.0/decltype.cpp) + +- [lambda](./c++2.0/lambda.cpp) + +- [Rvalue reference](./c++2.0/rvalue.cpp) + +- [move aware class](./c++2.0/move.cpp) + +- Container-Struct And Classify + + - (1) Serial containers include:`array`(C++2.0 Newly Introduced),`vector`,`deque`,`list`,`forward_list`(C++2.0 Newly Introduced) + - (2) Associated containers include:`set/multiset`,`map/multimap` + - (3) Unordered container(C++2.0 Newly Introduced,replace `hash_xxx` to `unordered_xxx`) include:`unordered_map/unordered_multimap,unordered_set/unordered_multiset` + +- [Hash Function](./c++2.0/hash.cpp) + +- [tuple](./c++2.0/tuple.cpp) + + Learning Material:https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061 + +### 2.2 C++14/17/20 + +To Be Continued ... + +### 3.Design Pattern + +- [Singleton pattern](./design_pattern/singleton) + +### 4. [STL Source Code Analysis](./src_analysis/stl) + +**STL Source Code Analysis:gcc 4.9.1** + +- [array](./src_analysis/stl/array.md) +- [deque](./src_analysis/stl/deque.md) +- [queue and stack](./src_analysis/stl/queue_stack.md) +- [list](./src_analysis/stl/list.md) +- [vector](./src_analysis/stl/vector.md) +- [typename](./src_analysis/stl/typename.md) +- [traits](./src_analysis/stl/traits.md) +- [iterator](./src_analysis/stl/iterator.md) +- [Talking about STL design and EBO optimization](./src_analysis/stl/谈谈STL设计之EBO优化.md) +- [rb_tree](./src_analysis/stl/rb_tree.md) +- [set and multiset](set_multiset.md) +- [map and multimap](./src_analysis/stl/map_multimap.md) +- [hashtable](./src_analysis/stl/hashtable.md) +- [myhashtable](./src_analysis/stl/myPhashtable.md) +- [unordered_map](./src_analysis/stl/unordered_map.md) + +### 4. [Concurrent Programming](./concurrency) + +#### 4.1 C++ Concurrency in Action + +- [Chapter One](./concurrency/concurrency_v1/chapter1) +- [Chapter Two](./concurrency/concurrency_v1/chapter2) + +Learning materials: https://chenxiaowei.gitbook.io/cpp_concurrency_in_action/ + +#### 4.2 Multithreading And Multiprocess + +##### 4.2.1 Threading In C++ + +- [Introduction](./concurrency/Threading_In_CPlusPlus/1.thread) +- [Five Types of Thread Creation](./concurrency/Threading_In_CPlusPlus/2.create_type) +- [Join And Detachs](./concurrency/Threading_In_CPlusPlus/3.join_detach) +- [mutex in C++ Threading](./concurrency/Threading_In_CPlusPlus/4.mutex) + +> From: +> +> https://www.youtube.com/watch?v=eZ8yKZo-PGw&list=PLk6CEY9XxSIAeK-EAh3hB4fgNvYkYmghp&index=4 + +### 5. [C++ Conventional method](./codingStyleIdioms) + +##### What is your favorite custom for c ++ programming style? + +- [1.ClassInitializers](./codingStyleIdioms/1_classInitializers) +- [2.Replace Enumclass with Namespace](./codingStyleIdioms/2_enumclass_namespace) +- [3.RAII(Resource Acquisition Initialization)](./codingStyleIdioms/3_RAII) +- [4.Copy and Swap](./codingStyleIdioms/4_copy-swap) +- [5.pImpl(Pointer Implement)](./codingStyleIdioms/5_pImpl) + +### 6.Learning Courses + +#### 6.1 [Chinese Name:极客时间《现代 C++ 实战 30 讲》](https://time.geekbang.org/channel/home) + +- [heap、stack、RAII:How to manage resources for C ++ ?](./learn_class/modern_C++_30/RAII) + - [heap](./modern_++_30/RAII/heap.cpp) + - [stack](./learn_class/modern_C++_30/RAII/stack.cpp) + - [RAII](./learn_class/modern_C++_30/RAII/RAII.cpp) +- [Implementing smart pointers for C ++](./learn_class/modern_C++_30/smart_ptr) + - [auto_ptr、scope_ptr](./learn_class/modern_C++_30/smart_ptr/auto_scope.cpp) + - [unique_ptr](./learn_class/modern_C++_30/smart_ptr/unique_ptr.cpp) + - [shared_ptr](./learn_class/modern_C++_30/smart_ptr/shared_ptr.cpp) +- [What exactly does r value and move solve?](./learn_class/modern_C++_30/reference) + - [L value and R value](./learn_class/modern_C++_30/reference/reference.cpp) + - [Extend the declaration cycle](./learn_class/modern_C++_30/reference/lifetime.cpp) + - [Reference folding](./learn_class/modern_C++_30/reference/collapses.cpp) + - [Perfect forward](./learn_class/modern_C++_30/reference/forward.cpp) + - [Do not return Reference](./learn_class/modern_C++_30/reference/don'treturnReference.cpp) +- [Container 1](./learn_class/modern_C++_30/container1) +- [Container 2](./learn_class/modern_C++_30/container2) +- [Exception](./learn_class/modern_C++_30/exception) +- [Literal、Static Assertion And Member Function Specifier](./learn_class/modern_C++_30/literalAssert) +- [Return Object?](./learn_class/modern_C++_30/returnObj)c +- [Getting started with generic programming and templates](./learn_class/modern_C++_30/compilerpoly) +- [A whole Compiler Compute World](./learn_class/modern_C++_30/compilercompute) +- [SFINAE:What is it if it is not replace error?](./learn_class/modern_C++_30/SFINAE) +- [constexpr:A Normal World](./learn_class/modern_C++_30/constexpr) +- [Function object and Lambda:functionLambda](./learn_class/modern_C++_30/functionLambda) +- [Memory Model and Atomic:Understangding the complexity of concurrency](./learn_class/modern_C++_30/memorymodel_atomic) + +### 7.Tools + +#### 7.1 [Container shortcut output tool](./tool/output) + +Modified the code, [Click here for the code](./tool/output/output_container.h) + +Input: + +```cpp +map mp{ + {1, 1}, + {2, 4}, + {3, 9}}; + cout << mp << endl; +``` + +Output: + +```cpp +{ 1 => 1, 2 => 4, 3 => 9 } +``` + +#### 7.2 Output Like Python(Jupyter Notebook) + +- [How to output like python in C/C++](./tool/像Python一样玩CC++.md) + +#### 7.3 Observe the changes in the compilation process + +- [https://cppinsights.io](https://cppinsights.io/) + +#### 7.4 Debug Tools For C ++:dbg-macro + +- [Debug Tool: dbg-macro](./tool/C++的Debug工具dbg-macro.md) + +### 8.Expansion + +#### 8.1 Other Problems +- [How to convert string to in C ++?](./extension/some_problem/string_int.md) + + +### 9.Circumstance + + +- **Running Circumstance** + + Ubuntu 18.04 + +- **IDE** + + CLion gcc/g++ + +### 10.Contributor + + + +| 贡献人 | 地址 | +| ------- | ----------------------------- | +| 光城 | https://github.com/Light-City | +| ChungZH | https://github.com/ChungZH | +| xliu79 | https://github.com/xliu79 | + + + +### 11.About The Writer + +个人公众号: + +![](./img/wechat.jpg) diff --git a/basic_content/abstract/README.md b/basic_content/abstract/README.md index 5629dc1..e11aa35 100644 --- a/basic_content/abstract/README.md +++ b/basic_content/abstract/README.md @@ -8,51 +8,133 @@ ## 1.纯虚函数与抽象类 -C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它!通过声明中赋值0来声明纯虚函数! +C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它! 通过声明中赋值0来声明纯虚函数! +```cpp +// 抽象类 +Class A { +public: + virtual void show() = 0; // 纯虚函数 + /* Other members */ +}; +``` -对应的代码:[test.cpp](./test.cpp) - - * 纯虚函数:没有函数体的虚函数 + * 纯虚函数:没有函数体的虚函数 * 抽象类:包含纯虚函数的类 -对应的代码:[pure_virtual.cpp](./pure_virtual.cpp) - 抽象类只能作为基类来派生新类使用,不能创建抽象类的对象,抽象类的指针和引用->由抽象类派生出来的类的对象! +> 代码样例:[test.cpp](./test.cpp)、[pure_virtual.cpp](./pure_virtual.cpp) + ## 2.实现抽象类 抽象类中:在成员函数内可以调用纯虚函数,在构造函数/析构函数内部不能使用纯虚函数。 如果一个类从抽象类派生而来,它必须实现了基类中的所有纯虚函数,才能成为非抽象类。 +```cpp +// A为抽象类 +class A { +public: + virtual void f() = 0; // 纯虚函数 + void g(){ this->f(); } + A(){} // 构造函数 +}; -对应的代码:[abstract.cpp](./abstract.cpp) +class B : public A{ +public: + void f(){ cout<<"B:f()"< 代码样例:[abstract.cpp](./abstract.cpp) ## 3.重要点 -- 纯虚函数使一个类变成抽象类 +- [纯虚函数使一个类变成抽象类](./interesting_facts1.cpp) +```cpp +// 抽象类至少包含一个纯虚函数 +class Base{ +public: + virtual void show() = 0; // 纯虚函数 + int getX() { return x; } // 普通成员函数 -对应的代码:[interesting_facts1.cpp](./interesting_facts1.cpp) +private: + int x; +}; +``` -- 抽象类类型的指针和引用 +- [抽象类类型的指针和引用](./interesting_facts2.cpp) +```cpp +class Derived : public Base { +public: + void show() { cout << "In Derived \n"; } // 实现抽象类的纯虚函数 + Derived(){} // 构造函数 +}; -对应的代码:[interesting_facts2.cpp](./interesting_facts2.cpp) +int main(void) +{ + //Base b; // error! 不能创建抽象类的对象 + //Base *b = new Base(); error! + + Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象 + bp->show(); + return 0; +} +``` -- 如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类。 +- [如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类](./interesting_facts3.cpp) +```cpp +// Derived为抽象类 +class Derived: public Base +{ +public: +// void show() {} +}; +``` -对应的代码:[interesting_facts3.cpp](./interesting_facts3.cpp) +- [抽象类可以有构造函数](./interesting_facts4.cpp) +```cpp +// 抽象类 +class Base { + protected: + int x; + public: + virtual void fun() = 0; + Base(int i) { x = i; } // 构造函数 +}; +// 派生类 +class Derived: public Base +{ + int y; +public: + Derived(int i, int j) : Base(i) { y = j; } // 构造函数 + void fun() { cout << "x = " << x << ", y = " << y; } +}; +``` -- 抽象类可以有构造函数 +- [构造函数不能是虚函数,而析构函数可以是虚析构函数](./interesting_facts5.cpp) +```cpp +// 抽象类 +class Base { +public: + Base(){ cout << "Constructor: Base" << endl; } + virtual ~Base(){ cout << "Destructor : Base" << endl; } + + virtual void func() = 0; +}; -对应的代码:[interesting_facts4.cpp](./interesting_facts4.cpp) - -- 构造函数不能是虚函数,而析构函数可以是虚析构函数。 - -对应的代码:[interesting_facts5.cpp](./interesting_facts5.cpp) - -当基类指针指向派生类对象并删除对象时,我们可能希望调用适当的析构函数。如果析构函数不是虚拟的,则只能调用基类析构函数。 +class Derived: public Base { +public: + Derived(){ cout << "Constructor: Derived" << endl; } + ~Derived(){ cout << "Destructor : Derived" << endl;} + + void func(){cout << "In Derived.func()." << endl;} +}; +``` +>当基类指针指向派生类对象并删除对象时,我们可能希望调用适当的析构函数。 +> 如果析构函数不是虚拟的,则只能调用基类析构函数。 ## 4.完整实例 抽象类由派生类继承实现! -对应的代码:[derived_full.cpp](./derived_full.cpp) \ No newline at end of file +> 代码样例:[derived_full.cpp](./derived_full.cpp) \ No newline at end of file diff --git a/basic_content/abstract/abstract.cpp b/basic_content/abstract/abstract.cpp index 61e6665..3a26d99 100644 --- a/basic_content/abstract/abstract.cpp +++ b/basic_content/abstract/abstract.cpp @@ -7,16 +7,12 @@ * @date 2019-07-20 */ - - - #include - - using namespace std; + class A { public: - virtual void f() = 0; + virtual void f() = 0; // 纯虚函数 void g(){ this->f(); } A(){} }; diff --git a/basic_content/abstract/derived_full.cpp b/basic_content/abstract/derived_full.cpp index 90a938f..176ace8 100644 --- a/basic_content/abstract/derived_full.cpp +++ b/basic_content/abstract/derived_full.cpp @@ -21,7 +21,7 @@ class Derived: public Base { int y; public: - void fun() { cout << "fun() called"; } ///< 实现了fun()函数 + void fun() { cout << "fun() called"; } // 实现了fun()函数 }; int main(void) diff --git a/basic_content/abstract/interesting_facts1.cpp b/basic_content/abstract/interesting_facts1.cpp index c98690d..048ede7 100644 --- a/basic_content/abstract/interesting_facts1.cpp +++ b/basic_content/abstract/interesting_facts1.cpp @@ -9,7 +9,6 @@ #include using namespace std; - /** * @brief 抽象类至少包含一个纯虚函数 */ diff --git a/basic_content/abstract/interesting_facts3.cpp b/basic_content/abstract/interesting_facts3.cpp index 6ff352b..fb76824 100644 --- a/basic_content/abstract/interesting_facts3.cpp +++ b/basic_content/abstract/interesting_facts3.cpp @@ -9,7 +9,6 @@ #include using namespace std; - class Base { int x; diff --git a/basic_content/abstract/pure_virtual.cpp b/basic_content/abstract/pure_virtual.cpp index ab552d4..bfaf177 100644 --- a/basic_content/abstract/pure_virtual.cpp +++ b/basic_content/abstract/pure_virtual.cpp @@ -16,14 +16,19 @@ class A private: int a; public: - virtual void show()=0; //< 纯虚函数 + virtual void show()=0; // 纯虚函数 }; int main() { - // 抽象类只能作为基类来派生新类使用,不能创建抽象类的对象,抽象类的指针和引用->由抽象类派生出来的类的对象! -// A a; // error 抽象类,不能创建对象 + /* + * 1. 抽象类只能作为基类来派生新类使用 + * 2. 抽象类的指针和引用->由抽象类派生出来的类的对象! + */ + A a; // error 抽象类,不能创建对象 + A *a1; // ok 可以定义抽象类的指针 -// A *a2 = new A; // error,A是抽象类,不能创建对象 + + A *a2 = new A(); // error, A是抽象类,不能创建对象 } diff --git a/basic_content/assert/README.md b/basic_content/assert/README.md index 534fc49..8699eff 100644 --- a/basic_content/assert/README.md +++ b/basic_content/assert/README.md @@ -10,12 +10,17 @@ ## 1.第一个断言案例 -断言,**是宏,而非函数**。assert 宏的原型定义在 (C)、(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义 NDEBUG 来关闭 assert,但是需要在源代码的开头,include 之前。 +断言,**是宏,而非函数**。 + +assert 宏的原型定义在 (C)、(C++)中。其作用是如果它的条件返回错误,则终止程序执行。 + +可以通过定义 `NDEBUG` 来关闭 assert,**但是需要在源代码的开头,include 之前。** ```c void assert(int expression); ``` -对应代码:[assert.c](./assert.c) + +> 代码样例:[assert.c](./assert.c) ```c #include #include @@ -35,22 +40,22 @@ int main() return 0; } - ``` 输出: ```c -assert: assert.c:13: main: Assertion `x==7' failed. +assert: assert.c:13: main: Assertion 'x==7' failed. ``` 可以看到输出会把源码文件,行号错误位置,提示出来! ## 2.断言与正常错误处理 -断言主要用于检查逻辑上不可能的情况。例如,它们可用于检查代码在开始运行之前所期望的状态,或者在运行完成后检查状态。与正常的错误处理不同,断言通常在运行时被禁用。 ++ 断言主要用于检查逻辑上不可能的情况。 -忽略断言: +>例如,它们可用于检查代码在开始运行之前所期望的状态,或者在运行完成后检查状态。与正常的错误处理不同,断言通常在运行时被禁用。 -在代码开头加上: ++ 忽略断言,在代码开头加上: ```c++ #define NDEBUG // 加上这行,则 assert 不可用 ``` -对应学习的代码:[ignore_assert.c](./ignore_assert.c) + +> 样例代码:[ignore_assert.c](./ignore_assert.c) diff --git a/basic_content/assert/assert b/basic_content/assert/assert deleted file mode 100755 index 7ece973..0000000 Binary files a/basic_content/assert/assert and /dev/null differ diff --git a/basic_content/assert/iga b/basic_content/assert/iga deleted file mode 100755 index 618f153..0000000 Binary files a/basic_content/assert/iga and /dev/null differ diff --git a/basic_content/assert/ignore_assert.c b/basic_content/assert/ignore_assert.c index d3069e5..0a5a15e 100644 --- a/basic_content/assert/ignore_assert.c +++ b/basic_content/assert/ignore_assert.c @@ -6,12 +6,10 @@ * @date 2019-07-25 */ -# define NDEBUG +# define NDEBUG // 忽略断言 #include - - int main(){ int x=7; assert(x==5); diff --git a/basic_content/const/README.md b/basic_content/const/README.md index 69af42c..c23e458 100644 --- a/basic_content/const/README.md +++ b/basic_content/const/README.md @@ -10,47 +10,45 @@ ## 2.const作用 -(1)可以定义常量 ++ 可以定义常量 -```c++ +```cpp const int a=100; ``` -(2)类型检查 ++ 类型检查 -const常量与#define宏定义常量的区别:~~**const常量具有类型,编译器可以进行安全检查;#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查。**~~感谢两位大佬指出这里问题,见: + + const常量与`#define`宏定义常量的区别: + > ~~**const常量具有类型,编译器可以进行安全检查;#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查。**~~感谢两位大佬指出这里问题,见:[issue](https://github.com/Light-City/CPlusPlusThings/issues/5) -> https://github.com/Light-City/CPlusPlusThings/issues/5 + + const定义的变量只有类型为整数或枚举,且以常量表达式初始化时才能作为常量表达式。 + + 其他情况下它只是一个 `const` 限定的变量,不要将与常量混淆。 -`const` 定义的变量只有类型为整数或枚举,且以常量表达式初始化时才能作为常量表达式。其他情况下它只是一个 `const` 限定的变量,不要将与常量混淆。 ++ 防止修改,起保护作用,增加程序健壮性 -(3)防止修改,起保护作用,增加程序健壮性 - -```c++ +```cpp void f(const int i){ i++; //error! } ``` -(4)可以节省空间,避免不必要的内存分配 ++ 可以节省空间,避免不必要的内存分配 -const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。 + + const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像`#define`一样给出的是立即数。 + + const定义的常量在程序运行过程中只有一份拷贝,而`#define`定义的常量在内存中有若干个拷贝。 ## 3.const对象默认为文件局部变量

注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。

+ > 未被const修饰的变量在不同文件的访问 -```c++ +```cpp // file1.cpp int ext // file2.cpp #include -/** - * by 光城 - * compile: g++ -o file file2.cpp file1.cpp - * execute: ./file - */ + extern int ext; int main(){ std::cout<<(ext+10)< const常量在不同文件的访问 -```c++ +```cpp //extern_file1.cpp extern const int ext=12; //extern_file2.cpp #include -/** - * by 光城 - * compile: g++ -o file const_file2.cpp const_file1.cpp - * execute: ./file - */ extern const int ext; int main(){ std::cout<小结:可以发现未被const修饰的变量不需要extern显式声明!而const常量需要显式声明extern,并且需要做初始化!因为常量在定义后就不能被修改,所以定义时必须初始化。

+> 小结:
可以发现未被const修饰的变量不需要extern显式声明!而const常量需要显式声明extern,并且需要做初始化!因为常量在定义后就不能被修改,所以定义时必须初始化。

## 4.定义常量 @@ -86,7 +79,9 @@ const string s = "helloworld"; const int i,j=0 // error: uninitialized const ‘i’ ``` -上述有两个错误,第一:b为常量,不可更改!第二:i为常量,必须进行初始化!(因为常量在定义后就不能被修改,所以定义时必须初始化。) +上述有两个错误: ++ b 为常量,不可更改! ++ i 为常量,必须进行初始化!(因为常量在定义后就不能被修改,所以定义时必须初始化。) ## 5.指针与const @@ -99,13 +94,14 @@ char * const a; //指向类型对象的const指针。或者说常指针、const const char * const a; //指向const对象的const指针。 ``` -小结:如果*const*位于`*`的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于`*`的右侧,*const*就是修饰指针本身,即指针本身是常量。 +> **小结:**
+> 如果*const*位于`*`的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于`*`的右侧,*const*就是修饰指针本身,即指针本身是常量。 具体使用如下: -(1)指向常量的指针 +(1) **指向常量的指针** -```c++ +```cpp const int *ptr; *ptr = 10; //error ``` @@ -140,12 +136,13 @@ int *ptr1 = &val; cout<<*ptr<小结:对于指向常量的指针,不能通过指针来修改对象的值。
也不能使用void`*`指针保存const对象的地址,必须使用const void`*`类型的指针保存const对象的地址。
允许把非const对象的地址赋值给const对象的指针,如果要修改指针所指向的对象值,必须通过其他方式修改,不能直接通过当前指针直接修改。

-(2)常指针 +> 小结:
1.对于指向常量的指针,不能通过指针来修改对象的值。
2.不能使用void`*`指针保存const对象的地址,必须使用const void`*`类型的指针保存const对象的地址。
3.允许把非const对象的地址赋值给const对象的指针,如果要修改指针所指向的对象值,必须通过其他方式修改,不能直接通过当前指针直接修改。 + +(2) **常指针** const指针必须进行初始化,且const指针的值不能修改。 -```c++ +```cpp #include using namespace std; int main(){ @@ -162,7 +159,7 @@ int main(){ 最后,当把一个const常量的地址赋值给ptr时候,由于ptr指向的是一个变量,而不是const常量,所以会报错,出现:const int`*` -> int `*`错误! -```c++ +```cpp #include using namespace std; int main(){ @@ -174,11 +171,11 @@ int main(){ 上述若改为 const int `*`ptr或者改为const int `*`const ptr,都可以正常! -(3)指向常量的常指针 +(3)**指向常量的常指针** 理解完前两种情况,下面这个情况就比较好理解了: -```c++ +```cpp const int p = 3; const int * const ptr = &p; ``` @@ -191,17 +188,17 @@ ptr是一个const指针,然后指向了一个int 类型的const对象。 这个跟const修饰普通变量以及指针的含义基本相同: -(1)const int +(1)**const int** -```c++ +```cpp const int func1(); ``` 这个本身无意义,因为参数返回本身就是赋值给其他的变量! -(2)const int* +(2)**const int*** -```c++ +```cpp const int* func2(); ``` @@ -209,7 +206,7 @@ const int* func2(); (3)int *const -```c++ +```cpp int *const func2(); ``` @@ -219,7 +216,7 @@ int *const func2(); (1)传递过来的参数及指针本身在函数内不可变,无意义! -```c++ +```cpp void func(const int var); // 传递过来的参数不可变 void func(int *const var); // 指针本身不可变 ``` @@ -228,60 +225,49 @@ void func(int *const var); // 指针本身不可变 输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。 -(2)参数指针所指内容为常量不可变 +(2)**参数指针所指内容为常量不可变** -```c++ +```cpp void StringCopy(char *dst, const char *src); ``` 其中src 是输入参数,dst 是输出参数。给src加上const修饰后,如果函数体内的语句试图改动src的内容,编译器将指出错误。这就是加了const的作用之一。 -(3)参数为引用,为了增加效率同时防止修改。 +(3)**参数为引用,为了增加效率同时防止修改。** -```c++ +```cpp void func(const A &a) ``` -对于非内部数据类型的参数而言,象void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型 - -的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。 +对于非内部数据类型的参数而言,象void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。 为了提高效率,可以将函数声明改为void func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临 +时对象。 -时对象。但是函数void func(A &a) 存在一个缺点: - -“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为 - +> 但是函数void func(A &a) 存在一个缺点:

“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为 void func(const A &a)。 以此类推,是否应将void func(int x) 改写为void func(const int &x),以便提高效率?完全没有必要,因为内部数 据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。 -

小结:对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void func(A a) 改为void func(const A &a)。
对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void func(int x) 不应该改为void func(const int &x)。

+> 小结:
1.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void func(A a) 改为void func(const A &a)。

2.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void func(int x) 不应该改为void func(const int &x)。

+ 以上解决了两个面试问题: -(1)如果函数需要传入一个指针,是否需要为该指针加上const,把const加在指针不同的位置有什么区别; - -(2)如果写的函数需要传入的参数是一个复杂类型的实例,传入值参数或者引用参数有什么区别,什么时候需要为传入的引用参数加上const。 ++ 如果函数需要传入一个指针,是否需要为该指针加上const,把const加在指针不同的位置有什么区别; ++ 如果写的函数需要传入的参数是一个复杂类型的实例,传入值参数或者引用参数有什么区别,什么时候需要为传入的引用参数加上const。 ## 7.类中使用const -在一个类中,任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改 - -数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。使用const关 - -字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字 - -明的成员函数不能用来操作常对象。 - +在一个类中,任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改 数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。 +使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字进行说明的成员函数不能用来操作常对象。 对于类中的const成员变量必须通过初始化列表进行初始化,如下所示: -```c++ -class Apple -{ +```cpp +class Apple{ private: int people[100]; public: @@ -299,7 +285,7 @@ const对象只能访问const成员函数,而非const对象可以访问任意的 例如: -```c++ +```cpp //apple.cpp class Apple { @@ -347,8 +333,11 @@ int main(){ b.add(100); return 0; } -//编译: g++ -o main main.cpp apple.cpp -//结果 +``` +> 编译: g++ -o main main.cpp apple.cpp
+ +结果: +``` take func 1 2 take func 10 @@ -365,21 +354,21 @@ take func 100 第一:将常量定义与static结合,也就是: -```c++ +```cpp static const int apple_number ``` 第二:在外面初始化: -```c++ +```cpp const int Apple::apple_number=10; ``` 当然,如果你使用c++11进行编译,直接可以在定义出初始化,可以直接写成: -```c++ +```cpp static const int apple_number=10; -或者 +// 或者 const int apple_number=10; ``` @@ -393,13 +382,13 @@ const int apple_number=10; 在类中声明: -```c++ +```cpp static int ap; ``` 在类实现文件中使用: -```c++ +```cpp int Apple::ap=666 ``` diff --git a/basic_content/decltype/README.md b/basic_content/decltype/README.md index 4a8aa80..02d38f4 100644 --- a/basic_content/decltype/README.md +++ b/basic_content/decltype/README.md @@ -47,7 +47,7 @@ for (vectype i = vec.begin; i != vec.end(); i++) struct { int d ; - doubel b; + double b; }anon_s; ``` diff --git a/basic_content/enum/README.md b/basic_content/enum/README.md index 7ab270c..197203f 100644 --- a/basic_content/enum/README.md +++ b/basic_content/enum/README.md @@ -50,7 +50,7 @@ namespace Color 这样之后就可以用 `Color::Type c = Color::RED;` 来定义新的枚举变量了。如果 `using namespace Color` 后,前缀还可以省去,使得代码简化。不过,因为命名空间是可以随后被扩充内容的,所以它提供的作用域封闭性不高。在大项目中,还是有可能不同人给不同的东西起同样的枚举类型名。 -更“有效”的办法是用一个类或结构体来限定其作用域,例如:定义新变量的方法和上面命名空间的相同。不过这样就不用担心类在别处被修改内容。这里用结构体而非类,一是因为本身希望这些常量可以公开访问,二是因为它只包含数据没有成员函数。 +更“有效”的办法是用一个类或结构体来限定其作用域,例如:定义新变量的方法和上面命名空间的相同。不过这样就不用担心类在别处被修改内容。这里用结构体而非类,是因为本身希望这些常量可以公开访问。 ```c++ struct Color1 @@ -153,4 +153,4 @@ public: 枚举常量不会占用对象的存储空间,它们在编译时被全部求值。 -枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点。 \ No newline at end of file +枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点。 diff --git a/basic_content/extern/README.md b/basic_content/extern/README.md index 41c1ebe..dd75f50 100644 --- a/basic_content/extern/README.md +++ b/basic_content/extern/README.md @@ -99,6 +99,21 @@ gcc -c add.c g++ add.cpp add.o -o main ``` +而通常为了C代码能够通用,即既能被C调用,又能被C++调用,头文件通常会有如下写法: + +```c +#ifdef __cplusplus +extern "C"{ +#endif +int add(int x,int y); +#ifdef __cplusplus +} +#endif +``` + +即在C++调用该接口时,会以C接口的方式调用。这种方式使得C++者不需要额外的extern C,而标准库头文件通常也是类似的做法,否则你为何不需要extern C就可以直接使用stdio.h中的C函数呢? + + 上述案例源代码见: - [add.h](extern_c++/add.h) @@ -191,3 +206,5 @@ int add(){ extern int add(); ``` + +不过与C++调用C接口不同,C++确实是能够调用编译好的C函数,而这里C调用C++,不过是把C++代码当成C代码编译后调用而已。也就是说,C并不能直接调用C++库函数。 diff --git a/basic_content/inline/README.md b/basic_content/inline/README.md index 3030e27..9f757a1 100644 --- a/basic_content/inline/README.md +++ b/basic_content/inline/README.md @@ -90,7 +90,7 @@ int main() ## 2.虚函数(virtual)可以是内联函数(inline)吗? - 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。 -- 内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。 +- 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。 - `inline virtual` 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 `Base::who()`),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。 ```c++ diff --git a/basic_content/using/derived_base.cpp b/basic_content/using/derived_base.cpp index b25ec55..bc57800 100644 --- a/basic_content/using/derived_base.cpp +++ b/basic_content/using/derived_base.cpp @@ -21,7 +21,7 @@ class Base { Base():value(20) {} virtual ~Base() {} void test1() { cout << "Base test1..." << endl; } - protected: //私有 + private: //私有 int value; }; diff --git a/basic_content/virtual/README.md b/basic_content/virtual/README.md index 58d0f3d..44436bd 100644 --- a/basic_content/virtual/README.md +++ b/basic_content/virtual/README.md @@ -38,7 +38,7 @@ static成员函数不属于任何类对象或类实例,所以即使给此函 (2)**构造函数可以为虚函数吗?** -构造函数不可以声明为虚函数。同时除了inline之外,构造函数不允许使用其它任何关键字。 +构造函数不可以声明为虚函数。同时除了inline|explicit之外,构造函数不允许使用其它任何关键字。 为什么构造函数不可以为虚函数? @@ -86,7 +86,7 @@ static成员函数不属于任何类对象或类实例,所以即使给此函 - [inline_virtual.cpp](./set3/inline_virtual.cpp) - ## 5.RTTI与dynamic_cast +## 5.RTTI与dynamic_cast RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用[基类](https://baike.baidu.com/item/%E5%9F%BA%E7%B1%BB/9589663)的[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88/2878304)或引用来检查这些指针或引用所指的对象的实际[派生类](https://baike.baidu.com/item/%E6%B4%BE%E7%94%9F%E7%B1%BB)型。 @@ -99,4 +99,4 @@ RTTI(Run-Time Type Identification),通过运行时类型信息程序能够 ## 6.纯虚函数和抽象类 -见[纯虚函数和抽象类那些事](../abstract) \ No newline at end of file +见[纯虚函数和抽象类那些事](../abstract) diff --git a/basic_content/vptr_vtable/README.md b/basic_content/vptr_vtable/README.md index 47f9df5..1b82cc6 100644 --- a/basic_content/vptr_vtable/README.md +++ b/basic_content/vptr_vtable/README.md @@ -114,9 +114,9 @@ int main(void) Base &p = d; // 基类引用指向派生类实例 cout<<"基类对象直接调用"<fun1(); cout<<"基类引用指向基类实例并调用虚函数"< +#include +#include +#include +#include +#include + +using namespace std; + +const int MAX_NUM = 10000; + +class BoundedBuffer +{ +public: + BoundedBuffer(size_t n) { + array_.resize(n); + start_pos_ = 0; + end_pos_ = 0; + pos_ = 0; + } + void Produce(int n) { + { + std::unique_lock lock(mtx_); + //wait for not full + not_full_.wait(lock, [=] { return pos_ < array_.size(); }); + + usleep(1000 * 400); + array_[end_pos_] = n; + end_pos_ = (end_pos_ + 1) % array_.size(); + ++pos_; + cout << "Produce pos:" << pos_ << endl; + } //auto unlock + + not_empty_.notify_one(); + } + + int Consume() { + std::unique_lock lock(mtx_); + //wait for not empty + not_empty_.wait(lock, [=] { return pos_ > 0; }); + + usleep(1000 * 400); + int n = array_[start_pos_]; + start_pos_ = (start_pos_ + 1) % array_.size(); + --pos_; + cout << "Consume pos:" << pos_ << endl; + lock.unlock(); + + not_full_.notify_one(); + + return n; + } +private: + std::vector array_; + size_t start_pos_; + size_t end_pos_; + size_t pos_; + std::mutex mtx_; + std::condition_variable not_full_; + std::condition_variable not_empty_; +}; + +BoundedBuffer bb(10); +std::mutex g_mtx; + +void Producer() +{ + int n = 0; + while(n < 100) { + bb.Produce(n); + cout << "Producer:" << n << endl; + n++; + } + + bb.Produce(-1); +} + + +void Consumer() +{ + std::thread::id thread_id = std::this_thread::get_id(); + int n = 0; + do { + n = bb.Consume(); + cout << "Consumer thread:" << thread_id << "=====> " << n << endl; + } while(-1 != n); + + bb.Produce(-1); +} + +int main() +{ + std::vector t; + t.push_back(std::thread(&Producer)); + t.push_back(std::thread(&Consumer)); + t.push_back(std::thread(&Consumer)); + t.push_back(std::thread(&Consumer)); + + for (auto& one : t) { + one.join(); + } + + return 0; +} diff --git a/design_pattern/singleton/README.md b/design_pattern/singleton/README.md index f593319..73bbf40 100644 --- a/design_pattern/singleton/README.md +++ b/design_pattern/singleton/README.md @@ -164,7 +164,7 @@ p = new singleton; - 线程A调用instance,执行第一次p的测试,获得锁,按照1,3,执行,然后被挂起。此时p是非空的,但是p指向的内存中还没有Singleton对象被构造。 - 线程B调用instance,判定p非空, 将其返回给instance的调用者。调用者对指针解引用以获得singleton,噢,一个还没有被构造出的对象。bug就出现了。 -DCLP能够良好的工作仅当步骤一和二在步骤三之前被执行,但是并没有并没有方法在C或C++中表达这种限制。这就像是插在DCLP心脏上的一把匕首:我们需要在相对指令顺序上定义限制,但是我们的语言没有给出表达这种限制的方法。 +DCLP能够良好的工作仅当步骤一和二在步骤三之前被执行,但是并没有方法在C或C++中表达这种限制。这就像是插在DCLP心脏上的一把匕首:我们需要在相对指令顺序上定义限制,但是我们的语言没有给出表达这种限制的方法。 ## 5.memory barrier指令 @@ -324,4 +324,4 @@ public: > https://www.cnblogs.com/liyuan989/p/4264889.html -> https://segmentfault.com/a/1190000015950693 \ No newline at end of file +> https://segmentfault.com/a/1190000015950693 diff --git a/english/basic_content/abstract/README.md b/english/basic_content/abstract/README.md new file mode 100644 index 0000000..9040d49 --- /dev/null +++ b/english/basic_content/abstract/README.md @@ -0,0 +1,142 @@ +# Pure virtual functions and abstract classes + +## About Author: + + + +![](../img/wechat.jpg) + +## 1.Pure virtual function and abstract class + + +Pure virtual functions (or abstract functions) in C + + are virtual functions that we have not implemented!We just need to state it! +example: +```cpp +// abstract class +Class A { +public: + virtual void show() = 0; // pure virtual function + /* Other members */ +}; +``` + + * Pure virtual function: virtual function without function body + * Abstract classes: classes containing pure virtual functions + +Abstract classes can only be used as base classes to derive new classes. Objects, pointers and references of abstract classes cannot be created->An object of a class derived from an abstract class! + +> Code example:[test.cpp](./test.cpp)、[pure_virtual.cpp](./pure_virtual.cpp) + +## 2.Implement abstract classes + +Abstract class:Pure virtual functions can be called within member functions.Pure virtual functions cannot be used inside constructors / destructors. + +If a class derives from an abstract class, it must implement all pure virtual functions in the base class to become a non abstract class. +```cpp +// A is abstract class +class A { +public: + virtual void f() = 0; // pure virtual function + void g(){ this->f(); } + A(){} // 构造函数 +}; + +class B : public A{ +public: + void f(){ cout<<"B:f()"< Code Example:[abstract.cpp](./abstract.cpp) + +## 3.Key point + +- [Pure virtual functions make a class abstract](./interesting_facts1.cpp) +```cpp +// Abstract class contains at least one pure virtual function +class Base{ +public: + virtual void show() = 0; // 纯虚函数 + int getX() { return x; } // 普通成员函数 + +private: + int x; +}; +``` + +- [Pointers and references to abstract class types](./interesting_facts2.cpp) +```cpp +class Derived : public Base { +public: + void show() { cout << "In Derived \n"; } // 实现抽象类的纯虚函数 + Derived(){} // 构造函数 +}; + +int main(void) +{ + //Base b; // error! 不能创建抽象类的对象 + //Base *b = new Base(); error! + + Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象 + bp->show(); + return 0; +} +``` + +- [If we do not override pure virtual functions in the derived class, the derived class will also become an abstract class](./interesting_facts3.cpp) +```cpp +// Derived为抽象类 +class Derived: public Base +{ +public: +// void show() {} +}; +``` + +- [Abstract classes can have constructors](./interesting_facts4.cpp) +```cpp +// abstract class +class Base { + protected: + int x; + public: + virtual void fun() = 0; + Base(int i) { x = i; } // constructor function +}; +// 派生类 +class Derived: public Base +{ + int y; +public: + Derived(int i, int j) : Base(i) { y = j; } // constructor function + void fun() { cout << "x = " << x << ", y = " << y; } +}; +``` + +- [A constructor cannot be a virtual function, and a destructor can be a virtual destructor](./interesting_facts5.cpp) +```cpp +// 抽象类 +class Base { +public: + Base(){ cout << "Constructor: Base" << endl; } + virtual ~Base(){ cout << "Destructor : Base" << endl; } + + virtual void func() = 0; +}; + +class Derived: public Base { +public: + Derived(){ cout << "Constructor: Derived" << endl; } + ~Derived(){ cout << "Destructor : Derived" << endl;} + + void func(){cout << "In Derived.func()." << endl;} +}; +``` +>When the base class pointer points to a derived class object and removes the object, we may want to call the appropriate destructor. +>The destructor can only be called if it is not a virtual destructor. + +## 4.Complete example + +Abstract classes are inherited and implemented by derived classes! + +> Code Example:[derived_full.cpp](./derived_full.cpp) diff --git a/english/basic_content/abstract/abstract.cpp b/english/basic_content/abstract/abstract.cpp new file mode 100644 index 0000000..3a26d99 --- /dev/null +++ b/english/basic_content/abstract/abstract.cpp @@ -0,0 +1,27 @@ +/** + * @file abstract.cpp + * @brief 抽象类中:在成员函数内可以调用纯虚函数,在构造函数/析构函数内部不能使用纯虚函数 + * 如果一个类从抽象类派生而来,它必须实现了基类中的所有纯虚函数,才能成为非抽象类 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +using namespace std; + +class A { +public: + virtual void f() = 0; // 纯虚函数 + void g(){ this->f(); } + A(){} +}; +class B:public A{ +public: + void f(){ cout<<"B:f()"< +using namespace std; + +class Base +{ + int x; + public: + virtual void fun() = 0; + int getX() { return x; } +}; + +class Derived: public Base +{ + int y; + public: + void fun() { cout << "fun() called"; } // 实现了fun()函数 +}; + +int main(void) +{ + Derived d; + d.fun(); + return 0; +} diff --git a/english/basic_content/abstract/interesting_facts1.cpp b/english/basic_content/abstract/interesting_facts1.cpp new file mode 100644 index 0000000..048ede7 --- /dev/null +++ b/english/basic_content/abstract/interesting_facts1.cpp @@ -0,0 +1,28 @@ +/** + * @file interesting_facts1.cpp + * @brief 纯虚函数使一个类变成抽象类 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +using namespace std; + +/** + * @brief 抽象类至少包含一个纯虚函数 + */ +class Test +{ + int x; +public: + virtual void show() = 0; + int getX() { return x; } +}; + +int main(void) +{ + Test t; //error! 不能创建抽象类的对象 + return 0; +} + diff --git a/english/basic_content/abstract/interesting_facts2.cpp b/english/basic_content/abstract/interesting_facts2.cpp new file mode 100644 index 0000000..84d293e --- /dev/null +++ b/english/basic_content/abstract/interesting_facts2.cpp @@ -0,0 +1,38 @@ +/** + * @file interesting_facts2.cpp + * @brief 抽象类类型的指针和引用 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +using namespace std; + + +/** + * @brief 抽象类至少包含一个纯虚函数 + */ +class Base +{ + int x; +public: + virtual void show() = 0; + int getX() { return x; } + +}; +class Derived: public Base +{ +public: + void show() { cout << "In Derived \n"; } + Derived(){} +}; +int main(void) +{ + //Base b; //error! 不能创建抽象类的对象 + //Base *b = new Base(); error! + Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象 + bp->show(); + return 0; +} + diff --git a/english/basic_content/abstract/interesting_facts3.cpp b/english/basic_content/abstract/interesting_facts3.cpp new file mode 100644 index 0000000..fb76824 --- /dev/null +++ b/english/basic_content/abstract/interesting_facts3.cpp @@ -0,0 +1,29 @@ +/** + * @file interesting_facts3.cpp + * @brief 如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类。 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +using namespace std; + +class Base +{ + int x; +public: + virtual void show() = 0; + int getX() { return x; } +}; +class Derived: public Base +{ +public: +// void show() { } +}; +int main(void) +{ + Derived d; //error! 派生类没有实现纯虚函数,那么派生类也会变为抽象类,不能创建抽象类的对象 + return 0; +} + diff --git a/english/basic_content/abstract/interesting_facts4.cpp b/english/basic_content/abstract/interesting_facts4.cpp new file mode 100644 index 0000000..028554b --- /dev/null +++ b/english/basic_content/abstract/interesting_facts4.cpp @@ -0,0 +1,35 @@ +/** + * @file interesting_facts4.cpp + * @brief 抽象类可以有构造函数 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +using namespace std; + +// An abstract class with constructor +class Base +{ + protected: + int x; + public: + virtual void fun() = 0; + Base(int i) { x = i; } +}; + +class Derived: public Base +{ + int y; + public: + Derived(int i, int j):Base(i) { y = j; } + void fun() { cout << "x = " << x << ", y = " << y; } +}; + +int main(void) +{ + Derived d(4, 5); + d.fun(); + return 0; +} diff --git a/english/basic_content/abstract/interesting_facts5.cpp b/english/basic_content/abstract/interesting_facts5.cpp new file mode 100644 index 0000000..e9152b9 --- /dev/null +++ b/english/basic_content/abstract/interesting_facts5.cpp @@ -0,0 +1,30 @@ + +/** + * @file interesting_facts5.cpp + * @brief 构造函数不能是虚函数,而析构函数可以是虚析构函数。 + * 例如:当基类指针指向派生类对象并删除对象时,我们可能希望调用适当的析构函数。如果析构函数不是虚拟的,则只能调用基类析构函数。 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ +#include +using namespace std; + +class Base { + public: + Base() { cout << "Constructor: Base" << endl; } + virtual ~Base() { cout << "Destructor : Base" << endl; } +}; + +class Derived: public Base { + public: + Derived() { cout << "Constructor: Derived" << endl; } + ~Derived() { cout << "Destructor : Derived" << endl; } +}; + +int main() { + Base *Var = new Derived(); + delete Var; + return 0; +} + diff --git a/english/basic_content/abstract/pure_virtual.cpp b/english/basic_content/abstract/pure_virtual.cpp new file mode 100644 index 0000000..bfaf177 --- /dev/null +++ b/english/basic_content/abstract/pure_virtual.cpp @@ -0,0 +1,34 @@ +/** + * @file pure_virtual.cpp + * @brief 纯虚函数:没有函数体的虚函数 + * 抽象类:包含纯虚函数的类 + * + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include + +using namespace std; +class A +{ +private: + int a; +public: + virtual void show()=0; // 纯虚函数 +}; + + +int main() +{ + /* + * 1. 抽象类只能作为基类来派生新类使用 + * 2. 抽象类的指针和引用->由抽象类派生出来的类的对象! + */ + A a; // error 抽象类,不能创建对象 + + A *a1; // ok 可以定义抽象类的指针 + + A *a2 = new A(); // error, A是抽象类,不能创建对象 +} diff --git a/english/basic_content/abstract/test.cpp b/english/basic_content/abstract/test.cpp new file mode 100644 index 0000000..7797c90 --- /dev/null +++ b/english/basic_content/abstract/test.cpp @@ -0,0 +1,23 @@ +/** + * @file test.cpp + * @brief C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它!通过声明中赋值0来声明纯虚函数! + * 纯虚函数:没有函数体的虚函数 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + + + +/** + * @brief 抽象类 + */ +class Test +{ + // Data members of class +public: + // Pure Virtual Function + virtual void show() = 0; + + /* Other members */ +}; diff --git a/english/basic_content/assert/README.md b/english/basic_content/assert/README.md new file mode 100644 index 0000000..1647518 --- /dev/null +++ b/english/basic_content/assert/README.md @@ -0,0 +1,59 @@ +# Things about assert + +## About Author: + + +![](../img/wechat.jpg) + + + +## 1.First assertion case + +assert,**is macro,rather than function**。 + +assert The prototype of a macro is defined in (C)、(C++).If its condition returns an error,Program execution is terminated. + +You can close assert by defining 'ndebug', **But it needs to be at the beginning of the source code,before include .** + +```c +void assert(int expression); +``` + +> Code Example:[assert.c](./assert.c) +```c +#include +#include + +int main() +{ + int x = 7; + + /* Some big code in between and let's say x + is accidentally changed to 9 */ + x = 9; + + // Programmer assumes x to be 7 in rest of the code + assert(x==7); + + /* Rest of the code */ + + return 0; +} +``` +Output: +```c +assert: assert.c:13: main: Assertion 'x==7' failed. +``` + +## 2.Assertion and normal error handling + ++ Assertions are mainly used to check for logically impossible situations. + +>For example, they can be used to check the state that code expects before it starts to run, or after the run is complete. Unlike normal error handling, assertions are usually disabled at run time. + ++ Ignore the assertion and add at the beginning of the code: +```c++ +#define NDEBUG // Adding this line,then you do not need the assert +``` + +> Code Example:[ignore_assert.c](./ignore_assert.c) diff --git a/english/basic_content/assert/assert.c b/english/basic_content/assert/assert.c new file mode 100644 index 0000000..f66a2c0 --- /dev/null +++ b/english/basic_content/assert/assert.c @@ -0,0 +1,18 @@ +#include +#include + +int main() +{ + int x = 7; + + /* Some big code in between and let's say x + * is accidentally changed to 9 */ + x = 9; + + // Programmer assumes x to be 7 in rest of the code + assert(x==7); + + /* Rest of the code */ + + return 0; +} diff --git a/english/basic_content/assert/ignore_assert.c b/english/basic_content/assert/ignore_assert.c new file mode 100644 index 0000000..0a5a15e --- /dev/null +++ b/english/basic_content/assert/ignore_assert.c @@ -0,0 +1,17 @@ +/** + * @file ignore_assert.c + * @brief 忽略断言 + * @author 光城 + * @version v1 + * @date 2019-07-25 + */ + +# define NDEBUG // 忽略断言 + +#include + +int main(){ + int x=7; + assert(x==5); + return 0; +} diff --git a/english/basic_content/bit/README.md b/english/basic_content/bit/README.md new file mode 100644 index 0000000..498ed23 --- /dev/null +++ b/english/basic_content/bit/README.md @@ -0,0 +1,177 @@ +## About Author: + + +![](../img/wechat.jpg) + +## What is Bit field ? + +“ Bit field is a kind of data structure.Data can be stored compactly in bits, And allows the programmer to operate on the bits of this structure. One of the advantages of this data structure is that it can save storage space in data units, which is particularly important when programs need thousands of data units. The second advantage is that bit segments can easily access part of the contents of an integer value, which can simplify the program source code. The disadvantage of this data structure is that bit segment implementation depends on specific machines and systems, and different results may be obtained in different platforms, which leads to the fact that bit segments are not portable in nature + +- The layout of bit fields in memory is machine dependent +- The type of bit field must be integer or enumeration type. The behavior of bit field in signed type depends on the implementation +- The addressing operator (&) cannot be applied to a bit field, and no pointer can point to a bit field of a class + +## Bit field usage + +Bit fields usually use struct declarations, which set the name of each bit field member and determine its width: + +``` +struct bit_field_name +{ + type member_name : width; +}; +``` + + + +| Elements | Description | +| -------------- | ------------------------------------------------------------ | +| bit_field_name | Bit field structure name | +| type | must be int、signed int or unsigned int type | +| member_name | | +| width | | + +For example, declare the following bit field: + +``` +struct _PRCODE +{ + unsigned int code1: 2; + unsigned int cdde2: 2; + unsigned int code3: 8; +}; +struct _PRCODE prcode; +``` + +This definition makes' prcode 'contain two 2-bit fields and one 8-bit field. We can use the member operator of the structure to assign values to it + +``` +prcode.code1 = 0; +prcode.code2 = 3; +procde.code3 = 102; +``` + +When assigning a value, it should be noted that the size of the value should not exceed the capacity of the bit field member,For example prcode.code3 Is a bit domain member of 8 bits. Its capacity is 2^8 = 256,Assignment range should be [0,255]。 + +## Size and alignment of bit fields + +### Size of bit field + +For example, the following bit field: + +``` +struct box +{ + unsigned int a: 1; + unsigned int : 3; + unsigned int b: 4; +}; +``` +There is an unnamed bit field in the middle of the bit field structure, which occupies 3 bits and only plays a filling role and has no practical significance. The padding makes the structure use 8 bits in total. But C language uses unsigned int as the basic unit of bit field,Even if the only member of a structure is a bit field of 1 bit, the size of the structure is the same as that of an unsigned int.In some systems, the unsigned int is 16 bits, and in x86 systems it is 32 bits.For the following articles, the default value of unsigned int is 32 bits. + +### Alignment of bit fields + +A bit field member is not allowed to cross the boundary of two unsigned ints. If the total number of bits declared by a member exceeds the size of an unsigned int, the editor will automatically shift the bit field members to align them according to the boundary of the unsigned int + +For example: + +``` +struct stuff +{ + unsigned int field1: 30; + unsigned int field2: 4; + unsigned int field3: 3; +}; +``` + + + +`field1` + `field2` = 34 Bits,beyond 32 Bits, Complier`field2` move to the next unsigned int unit. stuff.field1 and stuff.field2 will leave the 2 Bits space, stuff.field3 follow closely stuff.field2.The structure is now of size 2 * 32 = 64 Bits。 + +This hole can be filled with the unnamed bit field member mentioned earlier, or we can use an unnamed bit field member with a width of 0 to align the next field member with the next integer. +For example: + +``` +struct stuff +{ + unsigned int field1: 30; + unsigned int : 2; + unsigned int field2: 4; + unsigned int : 0; + unsigned int field3: 3; +}; +``` + + + +Between stuff.field1 and stuff.field2 there is a 2 Bits space. Stuff.field3 will be stored in the next unsigned in. The size of this structure is 3 * 32 = 96 Bits。 + +Code:[learn.cpp](learn.cpp) + +## Initialization of bit field and remapping of bit + +### Initialization + + +The initialization method of bit field is the same as that of ordinary structure. Here, two methods are listed as follows: + +``` +struct stuff s1= {20,8,6}; +``` + +Or directly assign values to the bit field members: + +``` +struct stuff s1; +s1.field1 = 20; +s1.field2 = 8; +s1.field3 = 4; +``` + +### Re-mapping of bit field + +Declare a bit field with a size of 32 bits + +``` +struct box { + unsigned int ready: 2; + unsigned int error: 2; + unsigned int command: 4; + unsigned int sector_no: 24; +}b1; +``` + +#### Zeroing bit field by remapping + +``` +int* p = (int *) &b1; // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址" +*p = 0; // clear s1, set all members to zero +``` + +#### The 32 bits bit field is remapped to the unsigned int type by union + +Let's briefly introduce the alliance + +> "Union" is a special class and a data structure of construction type.Many different data types can be defined in a "union". Any kind of data defined by the "union" can be loaded into a variable described as the "union" type. These data share the same memory to save space +> +> There are some similarities between "union" and "structure". But there are essential differences between them.In a structure, each member has its own memory space. The total length of a structure variable is the sum of the length of each member (except for empty structure, and boundary adjustment is not considered).In Union, members share a certain amount of memory space, and the length of a union variable is equal to the longest length of each member.It should be noted that the so-called sharing does not mean that multiple members are loaded into a joint variable at the same time, but that the joint variable can be assigned any member value, but only one value can be assigned at a time. + +We can declare the following Union: + +``` +union u_box { + struct box st_box; + unsigned int ui_box; +}; +``` + + + +In x86 system, unsigned int and box are 32 Bits, Through this combination, St_ Box and UI_ Box shares a piece of memory.Which bit in the specific bit field corresponds to which bit of the unsigned int depends on the compiler and hardware.Use union to return the bit field to zero. The code is as follows: + +``` +union u_box u; +u.ui_box = 0; +``` + +> From: diff --git a/english/basic_content/bit/learn.cpp b/english/basic_content/bit/learn.cpp new file mode 100644 index 0000000..505256b --- /dev/null +++ b/english/basic_content/bit/learn.cpp @@ -0,0 +1,19 @@ +#include + +using namespace std; +struct stuff +{ + unsigned int field1: 30; + unsigned int : 2; + unsigned int field2: 4; + unsigned int : 0; + unsigned int field3: 3; +}; +int main(){ + struct stuff s={1,3,5}; + cout< + +using namespace std; +class A +{ + public: + virtual void f()//Implement a virtual function + { + cout << "Base A::f() " << endl; + } +}; + +class B:public A // 必须为共有继承,否则后面调用不到,class默认为私有继承! +{ + public: + virtual void f()//Virtual function implementation, virtual keyword in subclass can not be appearence + { + cout << "Derived B::f() " << endl; + } +}; + + +int main() +{ + A a;//Base class object + B b;//an object of derived type + + A* pa = &a;//The parent class pointer points to the parent object + pa->f();//Call the function of the parent class + pa = &b; //The parent class pointer points to the subclass object, which is implemented in polymorphism + + + pa->f();//Call the function with the same name of the derived class + return 0; +} diff --git a/english/basic_content/c_poly/c_examp b/english/basic_content/c_poly/c_examp new file mode 100755 index 0000000..458fb2a Binary files /dev/null and b/english/basic_content/c_poly/c_examp differ diff --git a/english/basic_content/c_poly/c_examp.c b/english/basic_content/c_poly/c_examp.c new file mode 100644 index 0000000..7798d3e --- /dev/null +++ b/english/basic_content/c_poly/c_examp.c @@ -0,0 +1,55 @@ +/** + * @file c_examp.c + * @brief C实现多态 + * @author 光城 + * @version v1 + * @date 2019-08-06 + */ + +#include + +/// Define a pointer of function +typedef void (*pf) (); + +/** + * @brief parent class + */ +typedef struct _A +{ + pf _f; +}A; + + +/** + * @brief child class + */ +typedef struct _B +{ + A _b; ///< The inheritance of the parent class can be realized by defining an object of the base class in the subclass +}B; + +void FunA() +{ + printf("%s\n","Base A::fun()"); +} + +void FunB() +{ + printf("%s\n","Derived B::fun()"); +} + + +int main() +{ + A a; + B b; + + a._f = FunA; + b._b._f = FunB; + + A *pa = &a; + pa->_f(); + pa = (A *)&b; /// The parent class pointer points to the object of the subclass. Because of the type mismatch, it needs to be forced + pa->_f(); + return 0; +} diff --git a/english/basic_content/const/README.md b/english/basic_content/const/README.md new file mode 100644 index 0000000..f324abc --- /dev/null +++ b/english/basic_content/const/README.md @@ -0,0 +1,382 @@ + + + +## 1.The Definition Of const + +Const type is that people use type specifier **const** signiture type to demonstrate,const variables or objects can not be updated。 + +## 2. Effect Of Const + +(1)define variable + +``` +const int a=100; +``` + +(2) check type + +The difference between const variable and define variable: ~~**Constants have types, which can be checked by the compiler; the define macro definition has no data type, it is just a simple string replacement, and cannot be checked for security +**~~ Thanks for point this bug out. +> https://github.com/Light-City/CPlusPlusThings/issues/5 + +Only variables of type integer or enumeration are defined by `const`. In other cases, it is only a 'const' qualified variable and should not be confused with constants. + +(3)prevent modification, protect and increase program robustness + + +``` +void f(const int i){ + i++; //error! +} +``` +(4)Save space and avoid unnecessary memory allocation + +From the compile point of view, the variables can be defined by const only give the corresponding memory address. Meanwhile the #define gives an immediate number. + +## 3.const object is the file local variable by default + + +

Attention:not const will be set as extern by default.For the const variable to be accessible in other files, it must be explicitly specified as extern in the file + +

+> Access of variables not modified by const in different files + + +``` +// file1.cpp +int ext +// file2.cpp +#include +/** + * by 光城 + * compile: g++ -o file file2.cpp file1.cpp + * execute: ./file + */ +extern int ext; +int main(){ + std::cout<<(ext+10)< +using namespace std; +int main(){ + + int num=0; + int * const ptr=# //const指针必须初始化!且const指针的值不能修改 + int * t = # + *t = 1; + cout<<*ptr< +using namespace std; +int main(){ + const int num=0; + int * const ptr=# //error! const int* -> int* + cout<<*ptr< +#include"apple.cpp" +using namespace std; + +Apple::Apple(int i):apple_number(i) +{ + +} +int Apple::add(int num){ + take(num); +} +int Apple::add(int num) const{ + take(num); +} +void Apple::take(int num) const +{ + cout<<"take func "< +#include"apple.cpp" +using namespace std; +Apple::Apple(int i) +{ + +} +int Apple::add(int num){ + take(num); +} +int Apple::add(int num) const{ + take(num); +} +void Apple::take(int num) const +{ + cout<<"take func "< +#include"apple.cpp" +using namespace std; + +Apple::Apple(int i):apple_number(i) +{ + +} +int Apple::add(int num){ + take(num); +} +int Apple::add(int num) const{ + take(num); +} +void Apple::take(int num) const +{ + cout<<"take func "< +#include"apple.cpp" +using namespace std; +const int Apple::apple_number=10; +Apple::Apple(int i) +{ + +} +int Apple::add(int num){ + take(num); +} +int Apple::add(int num) const{ + take(num); +} +void Apple::take(int num) const +{ + cout<<"take func "< +#include"apple.cpp" +using namespace std; +const int Apple::apple_number=10; +int Apple::ap=666; +Apple::Apple(int i) +{ + +} +int Apple::add(int num){ + take(num); +} +int Apple::add(int num) const{ + take(num); +} +void Apple::take(int num) const +{ + cout<<"take func "< +using namespace std; + +void f(const int i){ + i=10; // error: assignment of read-only parameter ‘i’ + cout< +using namespace std; +int main(){ + const int b = 10; + b = 0; //error + const string s = "helloworld"; + const int i,j=0; +} diff --git a/english/basic_content/const/extern_const/const_file1.cpp b/english/basic_content/const/extern_const/const_file1.cpp new file mode 100644 index 0000000..7077ac9 --- /dev/null +++ b/english/basic_content/const/extern_const/const_file1.cpp @@ -0,0 +1 @@ +extern const int ext=12; diff --git a/english/basic_content/const/extern_const/const_file2.cpp b/english/basic_content/const/extern_const/const_file2.cpp new file mode 100644 index 0000000..53729f4 --- /dev/null +++ b/english/basic_content/const/extern_const/const_file2.cpp @@ -0,0 +1,11 @@ +#include +/** + * by 光城 + * compile: g++ -o file const_file2.cpp const_file1.cpp + * execute: ./file + */ +extern const int ext; +int main(){ + + std::cout< +/** + * by 光城 + * compile: g++ -o file file2.cpp file1.cpp + * execute: ./file + */ +extern int ext; +int main(){ + + std::cout<<(ext+10)< +using namespace std; + + +int main(){ + const int *ptr; + *ptr=10; //error +} diff --git a/english/basic_content/const/funciton_const/condition1/condition2.cpp b/english/basic_content/const/funciton_const/condition1/condition2.cpp new file mode 100644 index 0000000..9db7339 --- /dev/null +++ b/english/basic_content/const/funciton_const/condition1/condition2.cpp @@ -0,0 +1,9 @@ +#include +using namespace std; + + +int main(){ + const int p = 10; + const void *vp = &p; + void *vp = &p; //error +} diff --git a/english/basic_content/const/funciton_const/condition1/condition3.cpp b/english/basic_content/const/funciton_const/condition1/condition3.cpp new file mode 100644 index 0000000..5809e5f --- /dev/null +++ b/english/basic_content/const/funciton_const/condition1/condition3.cpp @@ -0,0 +1,12 @@ +#include +using namespace std; + +int main(){ + const int *ptr; + int val = 3; + ptr = &val; //ok + int *ptr1 = &val; + *ptr1=4; + cout<<*ptr< +using namespace std; +int main(){ + + int num=0; + int * const ptr=# //const指针必须初始化!且const指针的值不能修改 + int * t = # + *t = 1; + cout<<*ptr< +using namespace std; +int main(){ + + const int num=0; + int * const ptr=# //error! const int* -> int* + cout<<*ptr< +using namespace std; +int main(){ + + const int num=10; + const int * const ptr=# //error! const int* -> int* + cout<<*ptr< +using namespace std; + +int main(){ + + const int p = 3; + const int * const ptr = &p; + cout<<*ptr<vec; +typedef decltype(vec.begin()) vectype; +for (vectype i = vec.begin; i != vec.end(); i++) +{ +//... +} +``` + +like auto, improves the readability of the code. + + +### 1.3 Reuse anonymous types + +In C + +, we sometimes encounter some anonymous types, such as: + +```c++ +struct +{ + int d ; + doubel b; +}anon_s; +``` + +With decltype, we can reuse this anonymous structure: +```c++ +decltype(anon_s) as ;//Defines an anonymous structure above +``` + +### 1.4 In generic programming, auto is used to trace the return value type of function + + +```c++ +template +auto multiply(T x, T y)->decltype(x*y) +{ + return x*y; +} +``` + +Code:[decltype.cpp](decltype.cpp) + +## 2.Discriminant rules + + + +For decltype (E), the results are affected by the following conditions: +If e is a marker expression or class member access expression without parentheses, decltype (E) of is the type of entity named by E.In addition, if e is an overloaded function, it will result in compilation errors. +Otherwise, assume that the type of E is t, and if e is a dying value, then decltype (E) is t&& +Otherwise, assume that the type of E is t, and if e is a dying value, then decltype (E) is t&& +Otherwise, assuming that the type of E is t, decltype (E) is t. + +Markers are defined by programmers except for keywords, literals and other tags that the compiler needs to use. The expression corresponding to a single marker is a marker expression. For example: + +```c++ +int arr[4] +``` +Then arr is a marker expression,ranther than arr[3]+0 + +Example: + +```c++ +int i = 4; +int arr[5] = { 0 }; +int *ptr = arr; +struct S{ double d; }s ; +void Overloaded(int); +void Overloaded(char);// reload function +int && RvalRef(); +const bool Func(int); + +//Rule 1: derivation is its type +decltype (arr) var1; //int 标记符表达式 + +decltype (ptr) var2;//int * 标记符表达式 + +decltype(s.d) var3;//doubel 成员访问表达式 + +//decltype(Overloaded) var4;//重载函数。编译错误。 + +//规则二:将亡值。推导为类型的右值引用。 + +decltype (RvalRef()) var5 = 1; + +//规则三:Lvalue, derived as a reference to the type + +decltype ((i))var6 = i; //int& + +decltype (true ? i : i) var7 = i; //int& A conditional expression returns an lvalue + +decltype (++i) var8 = i; //int& ++i返回i的左值。 + +decltype(arr[5]) var9 = i;//int&. []操作返回左值 + +decltype(*ptr)var10 = i;//int& *操作返回左值 + +decltype("hello")var11 = "hello"; //const char(&)[9] 字符串字面常量为左值,且为const左值。 + + +//Rule 4: if none of the above is true, this type is derived + +decltype(1) var12;//const int + +decltype(Func(1)) var13=true;//const bool + +decltype(i++) var14 = i;//int i++返回右值 +``` + +From:https://www.cnblogs.com/QG-whz/p/4952980.html diff --git a/english/basic_content/decltype/decltype b/english/basic_content/decltype/decltype new file mode 100755 index 0000000..fb9f578 Binary files /dev/null and b/english/basic_content/decltype/decltype differ diff --git a/english/basic_content/decltype/decltype.cpp b/english/basic_content/decltype/decltype.cpp new file mode 100644 index 0000000..4bf9876 --- /dev/null +++ b/english/basic_content/decltype/decltype.cpp @@ -0,0 +1,60 @@ +/** + * @file decltype.cpp + * @brief g++ -o decltype decltype.cpp -std=c++11 + * @author 光城 + * @version v1 + * @date 2019-08-08 + */ + +#include +#include +using namespace std; +/** + * 泛型编程中结合auto,用于追踪函数的返回值类型 + */ +template + +auto multiply(T x, T y)->decltype(x*y) +{ + return x*y; +} + +int main() +{ + int nums[] = {1,2,3,4}; + vector vec(nums,nums+4); + vector::iterator it; + + for(it=vec.begin();it!=vec.end();it++) + cout<<*it<<" "; + cout< +using namespace std; + +enum Color {RED,BLUE}; +enum Feeling {EXCITED,BLUE}; + +int main() +{ + return 0; +} +``` +- Implicit conversion to int + +- The actual type used to represent an enumerated variable cannot be explicitly specified, Therefore, forward declaration of enumeration types cannot be supported. +Implementation:[tradition_color.cpp](tradition_color.cpp) + +## Classic Method + +A simple way to solve the problem of naming conflicts caused by unlimited scope is to prefix the enumeration variables. Change the above example to COLOR_BLUE and FEELING_BLUE。 + +Generally speaking, we usually prefix all constants for uniformity.But the code for defining enumeration variables is cumbersome.This may have to be done in the C program. But C++ coder do not like this method。Alternatives is namespace: +```c++ +namespace Color +{ + enum Type + { + RED=15, + YELLOW, + BLUE + }; +}; +``` + +Then you can use `Color::Type c = Color::RED;` to define the new enumeration。If after`using namespace Color` ,the prefix can also be omitted to simplify the code.However, the scope closure provided by a namespace is not high because it can be subsequently extended.In large projects, it is still possible for different people to give different things the same enumeration type names. + +A more "effective" approach is to limit its scope with a class or struct.For example:The new variable is defined in the same way as in the namespace above. This way, you don't have to worry about the class being modified elsewhere.We use structs instead of classes because we want these constants to be publicly accessible. + +```c++ +struct Color1 +{ + enum Type + { + RED=102, + YELLOW, + BLUE + }; +}; +``` + +Implementation:[classic_practice.cpp](classic_practice.cpp) + +## C++11 Enum class + +The above approach solves the first problem, but it can not do anything for the latter two.Fortunately,C ++ 11 standard introduces enum class. It can solve the above problems. + +- The scope of the new enum is no longer global + +- Cannot be implicitly converted to another type + + +```c++ +/** + * @brief C++11 enum class + * Equals to enum class Color2:int + */ +enum class Color2 +{ + RED=2, + YELLOW, + BLUE +}; +r2 c2 = Color2::RED; +cout << static_cast(c2) << endl; //! +``` + +- You can specify a specific type to store enum + +```c++ +enum class Color3:char; // Forward statement + +// Definition +enum class Color3:char +{ + RED='r', + BLUE +}; +char c3 = static_cast(Color3::RED); +``` + +Implementation:[classic_practice.cpp](classic_practice.cpp) + +## Enum types in class + + + +Sometimes we want certain constants to work only in classes. Because the macro constant defined by a is global, it can not achieve the purpose, so we want to use const to modify data members.The const data member does exist, but its meaning is not what we expected. +Data members are constants only for the lifetime of an objec. However, it is variable for the whole class, because the class can create multiple objects, and the values of const data members of different objects can be different. + + + +Cannot be initialized in a class declaration const data memeber。The following usage is incorrect,Because the compiler does not know what the value of size is when the object of the class is not created.(c++11) + +```c++ +class A +{ + const int SIZE = 100; // Error,Attempt to initialize const data member in class declaration + int array[SIZE]; // Error,Unknown size +}; +``` + +This should be done in the initialization list of the class's constructor: + +```c++ +class A +{ + A(int size); // Constructor + const int SIZE ; +}; +A::A(int size) : SIZE(size) // The definition of Struct +{ + +} +A a(100); // The size of Object A is 100 +A b(200); // The size of Object B is 200 +``` + +How can I establish constants that are constant throughout a class? + +It should be implemented with enumeration constants in the class. Such as: + +```c++ +class Person{ +public: + typedef enum { + BOY = 0, + GIRL + }SexType; +}; +//Access via Person::BOY or Person::GIRL. +``` + +Enum constants do not take up the storage space of the object . They are all evaluated at compile time + + +Drawback of Enum:Its implied data type is an integer, the maximum is limited, and it cannot represent floating point. diff --git a/english/basic_content/enum/classic_practice b/english/basic_content/enum/classic_practice new file mode 100755 index 0000000..f9f67b7 Binary files /dev/null and b/english/basic_content/enum/classic_practice differ diff --git a/english/basic_content/enum/classic_practice.cpp b/english/basic_content/enum/classic_practice.cpp new file mode 100644 index 0000000..f831521 --- /dev/null +++ b/english/basic_content/enum/classic_practice.cpp @@ -0,0 +1,87 @@ +/** + * @file classic_practice.cpp + * @brief g++ -o classic_practice classic_practice.cpp -std=c++11 + * @author 光城 + * @version v1 + * @date 2019-08-07 + */ + +#include +using namespace std; +/** + * @brief namespace解决作用域不受限 + */ +namespace Color +{ + enum Type + { + RED=15, + YELLOW, + BLUE + }; +}; + +/** + * @brief 上述如果 using namespace Color 后,前缀还可以省去,使得代码简化。 + * 不过,因为命名空间是可以随后被扩充内容的,所以它提供的作用域封闭性不高。 + * 在大项目中,还是有可能不同人给不同的东西起同样的枚举类型名。 + * 更“有效”的办法是用一个类或结构体来限定其作用域。 + * + * 定义新变量的方法和上面命名空间的相同。 + * 不过这样就不用担心类在别处被修改内容。 + * 这里用结构体而非类,一是因为本身希望这些常量可以公开访问, + * 二是因为它只包含数据没有成员函数。 + */ +struct Color1 +{ + enum Type + { + RED=102, + YELLOW, + BLUE + }; +}; + +/** + * @brief C++11的枚举类 + * 下面等价于enum class Color2:int + */ +enum class Color2 +{ + RED=2, + YELLOW, + BLUE +}; + +enum class Color3:char; // 前向声明 + +// 定义 +enum class Color3:char +{ + RED='r', + BLUE +}; + +int main() +{ + // 定义新的枚举变量 + Color::Type c = Color::RED; + cout<(c2) << endl; + + char c3 = static_cast(Color3::RED); + cout< +using namespace std; + +enum Color {RED,BLUE}; +enum Feeling {EXCITED,BLUE}; + +int main() +{ + return 0; +} diff --git a/english/basic_content/explicit/README.md b/english/basic_content/explicit/README.md new file mode 100644 index 0000000..58c3b45 --- /dev/null +++ b/english/basic_content/explicit/README.md @@ -0,0 +1,15 @@ +# Things about explicit + +## About Author: + + +![](../img/wechat.jpg) + +- explicit When decorating a constructor, you can prevent implicit conversion and copy initialization +- explicit When modifying a conversion function, you can prevent implicit conversion, except for context conversion + + +Code :[.explicit.cpp](./explicit.cpp) + +Reference Link: +> https://stackoverflow.com/questions/4600295/what-is-the-meaning-of-operator-bool-const diff --git a/english/basic_content/explicit/explicit b/english/basic_content/explicit/explicit new file mode 100755 index 0000000..ab3a412 Binary files /dev/null and b/english/basic_content/explicit/explicit differ diff --git a/english/basic_content/explicit/explicit.cpp b/english/basic_content/explicit/explicit.cpp new file mode 100644 index 0000000..f659635 --- /dev/null +++ b/english/basic_content/explicit/explicit.cpp @@ -0,0 +1,46 @@ +#include + +using namespace std; + +struct A +{ + A(int) { } + operator bool() const { return true; } +}; + +struct B +{ + explicit B(int) {} + explicit operator bool() const { return true; } +}; + +void doA(A a) {} + +void doB(B b) {} + +int main() +{ + A a1(1); // OK:直接初始化 + A a2 = 1; // OK:复制初始化 + A a3{ 1 }; // OK:直接列表初始化 + A a4 = { 1 }; // OK:复制列表初始化 + A a5 = (A)1; // OK:允许 static_cast 的显式转换 + doA(1); // OK:允许从 int 到 A 的隐式转换 + if (a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 + bool a6(a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 + bool a7 = a1; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 + bool a8 = static_cast(a1); // OK :static_cast 进行直接初始化 + + B b1(1); // OK:直接初始化 +// B b2 = 1; // 错误:被 explicit 修饰构造函数的对象不可以复制初始化 + B b3{ 1 }; // OK:直接列表初始化 +// B b4 = { 1 }; // 错误:被 explicit 修饰构造函数的对象不可以复制列表初始化 + B b5 = (B)1; // OK:允许 static_cast 的显式转换 +// doB(1); // 错误:被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换 + if (b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 + bool b6(b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 +// bool b7 = b1; // 错误:被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换 + bool b8 = static_cast(b1); // OK:static_cast 进行直接初始化 + + return 0; +} diff --git a/english/basic_content/extern/README.md b/english/basic_content/extern/README.md new file mode 100644 index 0000000..8402ad6 --- /dev/null +++ b/english/basic_content/extern/README.md @@ -0,0 +1,195 @@ +# extern "C" + +## About Author: + + +![](../img/wechat.jpg) + +## 1. Compiler difference between C and C ++ + +In C + +, we often see extern "C" modifier function in the header file. What's the effect. Is a function defined in C language module for C + + link. + +Although C + + is compatible with C, the symbols generated by function compilation in C + + files are different from those generated by C language.Because C + + supports function overloading, the symbols generated by C + + function compilation have the information of function parameter type, while C does not. + + +Take `int add(int a, int b)` for example. The C + + compiler generates the. O file, `add` becomes `add_int_int` and so on, while C would be like this `_add`, that is:For the same function, in C and C + +, the symbols generated after compilation are different. + +This leads to a problem: if the function implemented in C + + is implemented in C language, an error will occur when compiling the link, indicating that the corresponding symbol cannot be found. At this time`extern "C"` works:Tell the linker to find its `_ C `language symbols such as `add` are not modified by C ++. + +## 2.C ++ calls C functions + +When referring to the header file of C, you need to add `extern "C"` + +```c++ +//add.h +#ifndef ADD_H +#define ADD_H +int add(int x,int y); +#endif + +//add.c +#include "add.h" + +int add(int x,int y) { + return x+y; +} + +//add.cpp +#include +#include "add.h" +using namespace std; +int main() { + add(2,3); + return 0; +} +``` + +Compile: + +``` +//Generate add.o file +gcc -c add.c +``` + +Link: + +``` +g++ add.cpp add.o -o main +``` + +Without extern "C": + +```c++ +> g++ add.cpp add.o -o main +add.o:在函数‘main’中: +add.cpp:(.text+0x0): `main'被多次定义 +/tmp/ccH65yQF.o:add.cpp:(.text+0x0):第一次在此定义 +/tmp/ccH65yQF.o:在函数‘main’中: +add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用 +add.o:在函数‘main’中: +add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用 +collect2: error: ld returned 1 exit status +``` + +With extern "C": + +`add.cpp` + +```c++ +#include +using namespace std; +extern "C" { + #include "add.h" +} +int main() { + add(2,3); + return 0; +} +``` +When compiling, you must pay attention to generating intermediate files add.o through GCC + + +``` +gcc -c add.c +``` + +Compile: + +``` +g++ add.cpp add.o -o main +``` + +Code: + +- [add.h](extern_c++/add.h) + +- [add.c](extern_c++/add.c) + +- [add.cpp](extern_c++/add.cpp) + +## 2.Calling C++ function in C + + +`extern "C"` It is a syntax error in C, which needs to be put in the C + + header file. + +```c +// add.h +#ifndef ADD_H +#define ADD_H +extern "C" { + int add(int x,int y); +} +#endif + +// add.cpp +#include "add.h" + +int add(int x,int y) { + return x+y; +} + +// add.c +extern int add(int x,int y); +int main() { + add(2,3); + return 0; +} +``` + +Compile: + +```c +g++ -c add.cpp +``` + +Link: + +``` +gcc add.c add.o -o main +``` + +Code: + +- [add.h](extern_c/add.h) + +- [add.c](extern_c/add.c) + +- [add.cpp](extern_c/add.cpp) + +In the header file of C language, the external function can only be specified as extern type. The declaration of extern "C" is not supported in C language. There will be compiler syntax error when the. C file contains extern "C". Therefore, the use of external "C" is all placed in CPP program related files or its header files. + + +The following forms are summarized: + +(1)C + + calls C functions: + +```c++ +//xx.h +extern int add(...) + +//xx.c +int add(){ + +} + +//xx.cpp +extern "C" { + #include "xx.h" +} +``` + +(2)C calls C + + functions + +```c +//xx.h +extern "C"{ + int add(); +} +//xx.cpp +int add(){ + +} +//xx.c +extern int add(); +``` + diff --git a/english/basic_content/extern/extern_c++/add.c b/english/basic_content/extern/extern_c++/add.c new file mode 100644 index 0000000..b347468 --- /dev/null +++ b/english/basic_content/extern/extern_c++/add.c @@ -0,0 +1,5 @@ +#include "add.h" + +int add(int x,int y) { + return x+y; +} diff --git a/english/basic_content/extern/extern_c++/add.cpp b/english/basic_content/extern/extern_c++/add.cpp new file mode 100644 index 0000000..8ed76fa --- /dev/null +++ b/english/basic_content/extern/extern_c++/add.cpp @@ -0,0 +1,9 @@ +#include +using namespace std; +extern "C" { + #include "add.h" +} +int main() { + add(2,3); + return 0; +} diff --git a/english/basic_content/extern/extern_c++/add.h b/english/basic_content/extern/extern_c++/add.h new file mode 100644 index 0000000..6984cdf --- /dev/null +++ b/english/basic_content/extern/extern_c++/add.h @@ -0,0 +1,4 @@ +#ifndef ADD_H +#define ADD_H +extern int add(int x,int y); +#endif diff --git a/english/basic_content/extern/extern_c++/add.o b/english/basic_content/extern/extern_c++/add.o new file mode 100644 index 0000000..4c8c016 Binary files /dev/null and b/english/basic_content/extern/extern_c++/add.o differ diff --git a/english/basic_content/extern/extern_c++/main b/english/basic_content/extern/extern_c++/main new file mode 100755 index 0000000..451dd01 Binary files /dev/null and b/english/basic_content/extern/extern_c++/main differ diff --git a/english/basic_content/extern/extern_c/add.c b/english/basic_content/extern/extern_c/add.c new file mode 100644 index 0000000..6f8a3d8 --- /dev/null +++ b/english/basic_content/extern/extern_c/add.c @@ -0,0 +1,5 @@ +extern int add(int x,int y); +int main() { + add(2,3); + return 0; +} diff --git a/english/basic_content/extern/extern_c/add.cpp b/english/basic_content/extern/extern_c/add.cpp new file mode 100644 index 0000000..b347468 --- /dev/null +++ b/english/basic_content/extern/extern_c/add.cpp @@ -0,0 +1,5 @@ +#include "add.h" + +int add(int x,int y) { + return x+y; +} diff --git a/english/basic_content/extern/extern_c/add.h b/english/basic_content/extern/extern_c/add.h new file mode 100644 index 0000000..719b7c3 --- /dev/null +++ b/english/basic_content/extern/extern_c/add.h @@ -0,0 +1,6 @@ +#ifndef ADD_H +#define ADD_H +extern "C" { + int add(int x,int y); +} +#endif diff --git a/english/basic_content/extern/extern_c/add.o b/english/basic_content/extern/extern_c/add.o new file mode 100644 index 0000000..564710e Binary files /dev/null and b/english/basic_content/extern/extern_c/add.o differ diff --git a/english/basic_content/extern/extern_c/main b/english/basic_content/extern/extern_c/main new file mode 100755 index 0000000..f026f11 Binary files /dev/null and b/english/basic_content/extern/extern_c/main differ diff --git a/english/basic_content/friend/README.md b/english/basic_content/friend/README.md new file mode 100644 index 0000000..01ebcbe --- /dev/null +++ b/english/basic_content/friend/README.md @@ -0,0 +1,111 @@ +# Friend and Friend Class + +## About Author: + + +![](../img/wechat.jpg) + +## 0.Summary + +Friends provide a mechanism for ordinary functions or class member functions to access private or protected members in another class.In other words, there are two forms of friends: + +(1)Friend Function:Ordinary functions access a private or protected member of a class. + +(2)Friend Class:Member functions in class a access private or protected members in class B + +Advantages: improve the efficiency of the program. + +Disadvantages: it destroys the encapsulation of classes and the transparency of data. +Conclusion: +- Access to private members +- Breaking encapsulation +- Friendship is not transitive +- The unidirectionality of friend relationship +- There are no restrictions on the form and number of friend declarations + +## 1.Friend function + +It is declared in any region of the class declaration, and the definition is outside the class. + +``` +friend (); +``` + +Note that the friend function is only a common function, not a class member function of this class. It can be called anywhere. In the friend function, private or protected members of the class can be accessed through the object name. + +Code:[friend_func.cpp](friend_func.cpp) + +```c++ +#include + +using namespace std; + +class A +{ +public: + A(int _a):a(_a){}; + friend int geta(A &ca); ///< Friend function +private: + int a; +}; + +int geta(A &ca) +{ + return ca.a; +} + +int main() +{ + A a(3); + cout<; +``` + +Class B is a friend of class A, so class B can directly access private members of A. +Code:[friend_class.cpp](friend_class.cpp) +```c++ +#include + +using namespace std; + +class A +{ +public: + A(int _a):a(_a){}; + friend class B; +private: + int a; +}; + +class B +{ +public: + int getb(A ca) { + return ca.a; + }; +}; + +int main() +{ + A a(3); + B b; + cout< + +using namespace std; + +class A +{ +public: + A(int _a):a(_a){}; + friend class B; +private: + int a; +}; + +class B +{ +public: + int getb(A ca) { + return ca.a; + }; +}; + +int main() +{ + A a(3); + B b; + cout< + +using namespace std; + +class A +{ +public: + A(int _a):a(_a){}; + friend int geta(A &ca); ///< 友元函数 +private: + int a; +}; + +int geta(A &ca) +{ + return ca.a; +} + +int main() +{ + A a(3); + cout< +using namespace std; + +/** + * @brief 定义了一个变量pFun,这个变量是个指针,指向返回值和参数都是空的函数的指针! + */ +void (*pFun)(int); + +/** + * @brief 代表一种新类型,不是变量!所以与上述的pFun不一样! + */ +typedef void (*func)(void); + +void myfunc(void) +{ + cout<<"asda"< +#include "inline.h" + +using namespace std; + +/** + * @brief To work, inline should be placed with function definition. Inline is a kind of "keyword for implementation, not for declaration" + * + * @param x + * @param y + * + * @return + */ +int Foo(int x,int y); // Function declaration + + +inline int Foo(int x,int y) // Function definition +{ + return x+y; +} + +// It is recommended to add the keyword "inline" to the definition! +inline void A::f1(int x){ + +} + +int main() +{ + + + cout< +using namespace std; +class Base +{ +public: + inline virtual void who() + { + cout << "I am Base\n"; + } + virtual ~Base() {} +}; +class Derived : public Base +{ +public: + inline void who() // + { + cout << "I am Derived\n"; + } +}; + +int main() +{ + // + Base b; + b.who(); + + // + Base *ptr = new Derived(); + ptr->who(); + + // + delete ptr; + ptr = nullptr; + + system("pause"); + return 0; +} +``` + + + diff --git a/english/basic_content/inline/inline b/english/basic_content/inline/inline new file mode 100755 index 0000000..c45bdb6 Binary files /dev/null and b/english/basic_content/inline/inline differ diff --git a/english/basic_content/inline/inline.cpp b/english/basic_content/inline/inline.cpp new file mode 100644 index 0000000..5b1463b --- /dev/null +++ b/english/basic_content/inline/inline.cpp @@ -0,0 +1,57 @@ +#include +#include "inline.h" + + + +using namespace std; + +/** + * @brief inline要起作用,inline要与函数定义放在一起,inline是一种“用于实现的关键字,而不是用于声明的关键字” + * + * @param x + * @param y + * + * @return + */ +int Foo(int x,int y); // 函数声明 +inline int Foo(int x,int y) // 函数定义 +{ + return x+y; +} + + + +// 定义处加inline关键字,推荐这种写法! +inline void A::f1(int x){ + + +} + + + +/** + * @brief 内联能提高函数效率,但并不是所有的函数都定义成内联函数!内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。 + * 如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收货会更少!另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。 + * 以下情况不宜用内联: + * (1) 如果函数体内的代码比较长,使得内联将导致内存消耗代价比较高。 + * (2) 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。 + * + * @return + */ +int main() +{ + + + cout< +using namespace std; +class Base +{ + public: + inline virtual void who() + { + cout << "I am Base\n"; + } + virtual ~Base() {} +}; +class Derived : public Base +{ + public: + inline void who() // 不写inline时隐式内联 + { + cout << "I am Derived\n"; + } +}; + +int main() +{ + // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 + Base b; + b.who(); + + // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。 + Base *ptr = new Derived(); + ptr->who(); + + // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。 + delete ptr; + + return 0; +} diff --git a/english/basic_content/inline/iv b/english/basic_content/inline/iv new file mode 100755 index 0000000..e0b0e95 Binary files /dev/null and b/english/basic_content/inline/iv differ diff --git a/english/basic_content/macro/README.md b/english/basic_content/macro/README.md new file mode 100644 index 0000000..18022cf --- /dev/null +++ b/english/basic_content/macro/README.md @@ -0,0 +1,236 @@ +# Story about Macro + + + +![](../img/wechat.jpg) + +## 1.The macro contains special symbols + +Several type:`#`,`##`,`\` + +### 1.1 String operator(#) + +**Using a # before macro parameter,The preprocessor converts this parameter into an array of characters**,In other words:**# is “stringlize”,The#, which appears in the macro definition, is to convert the following parameter into a string + +**。 + +**Attention:It can only be used in macro definitions that have passed in parameters, and must be placed before the parameter name in the macro definition body.** + +For example: + +```c++ +#define exp(s) printf("test s is:%s\n",s) +#define exp1(s) printf("test s is:%s\n",#s) +#define exp2(s) #s +int main() { + exp("hello"); + exp1(hello); + + string str = exp2( bac ); + cout<(b) ? (a) \ + :(b)) +int main() { + int max_val = MAX(3,6); + cout<0) + fun() +``` + +When this macro is expanded, it will be: + +``` +if(a>0) + f1(); + f2(); +``` + + +In order to solve this problem, when writing code, usually can adopt + +`{}`。 + +ex: + +```c++ +#define fun() {f1();f2();} +if(a>0) + fun(); +// 宏展开 +if(a>0) +{ + f1(); + f2(); +}; +``` + +However, you will find that there is a semicolon after the macro is expanded, so the actual syntax is not correct.(Although the compiler runs well, there is no semicolon). + +### 2.2 Avoid using goto to control flow + +In some functions, we may need to do some cleaning before the return statement, such as releasing the memory space requested by malloc at the beginning of the function. Using goto is always a simple method: + + +```c++ +int f() { + int *p = (int *)malloc(sizeof(int)); + *p = 10; + cout<<*p< + diff --git a/english/basic_content/macro/do_while b/english/basic_content/macro/do_while new file mode 100755 index 0000000..ce87035 Binary files /dev/null and b/english/basic_content/macro/do_while differ diff --git a/english/basic_content/macro/do_while.cpp b/english/basic_content/macro/do_while.cpp new file mode 100644 index 0000000..68af132 --- /dev/null +++ b/english/basic_content/macro/do_while.cpp @@ -0,0 +1,76 @@ +#include +#include + +using namespace std; +#define f1() cout<<"f1()"<0) + fun(); + + if(2>0) + fun1(); + + f(); + ff(); + fc(); + return 0; +} diff --git a/english/basic_content/macro/sig_examp b/english/basic_content/macro/sig_examp new file mode 100755 index 0000000..b342360 Binary files /dev/null and b/english/basic_content/macro/sig_examp differ diff --git a/english/basic_content/macro/sig_examp.cpp b/english/basic_content/macro/sig_examp.cpp new file mode 100644 index 0000000..85290a7 --- /dev/null +++ b/english/basic_content/macro/sig_examp.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +using namespace std; + +///=========================================== +/** + * (#)字符串操作符 + */ +///=========================================== +#define exp(s) printf("test s is:%s\n",s) +#define exp1(s) printf("test s is:%s\n",#s) + +#define exp2(s) #s + + +///=========================================== +/** + *(##)符号连接操作符 + */ +///=========================================== +#define expA(s) printf("前缀加上后的字符串为:%s\n",gc_##s) //gc_s必须存在 + +#define expB(s) printf("前缀加上后的字符串为:%s\n",gc_ ## s) //gc_s必须存在 + +#define gc_hello1 "I am gc_hello1" + + +///=========================================== +/** + * (\)续行操作符 + */ +///=========================================== +#define MAX(a,b) ((a)>(b) ? (a) \ + :(b)) + +int main() { + ///=========================================== + /** + * (#)字符串操作符 + */ + ///=========================================== + exp("hello"); + exp1(hello); + + string str = exp2( bac ); + cout< +using namespace std; + +int count=0; // 全局(::)的count + +class A { +public: + static int count; // 类A的count (A::count) + +}; +// 静态变量必须在此处定义 +int A::count; +int main() { + ::count=1; // 设置全局的count为1 + A::count=5; // 设置类A的count为2 + cout< References must be initialized, and pointers can be uninitialized + +When we define a reference, we must specify an initial value for it, but the pointer does not + +```c++ +int &r; //Illegal, no initialization reference +int *p; //It is legal, but p is a wild pointer. You should be careful when using it +``` + +> Reference cannot be null and pointer can be null + +Since the reference cannot be null, we do not need to test its validity when using a reference. When using a pointer, we need to first judge whether the pointer is a null pointer, otherwise it may cause the program to crash. +```c++ +void test_p(int* p) +{ + if(p != null_ptr) // + *p = 3; + return; +} +void test_r(int& r) +{ + r = 3; // + return; +} +``` + +> References cannot change targets + + + +The pointer can be changed at any time, but the reference can only point to the object pointed to during initialization, and cannot be changed. + + +``` +int a = 1; +int b = 2; + +int &r = a; // +int *p = &a; // + +p = &b; // +r = b; //引 +``` + +## 2.Reference + +#### lvalue reference + +General reference, which generally represents the identity of an object. + + +#### rvalue reference + + + +An R-value reference is a reference that must be bound to an R-value (a temporary object, an object to be destroyed), and generally represents the value of an object. + + +An R-value reference is a reference that must be bound to an R-value (a temporary object, an object to be destroyed), and generally represents the value of an object. + +- It eliminates unnecessary copy of objects when two objects interact, saves operation storage resources and improves efficiency +- It can define generic functions more concisely and clearly + +#### Reference collapse + +- `X& &`、`X& &&`、`X&& &` can be folded into `X&` +- `X&& &&` can be floded into `X&&` + +The reference of C ++ **At the same time, it improves the security of memory operation and the beauty of semantics**。For example, the mandatory requirement of reference must be initialized, so that we don't have to judge whether the reference is empty when using the reference, which makes the code more concise and elegant, and avoids the situation of pointer flying all over the sky. In addition to this scenario, references are used for the following two scenarios: + +> Reference type parameter + +In general, we use const reference parameter as a read-only formal parameter. In this case, we can not only avoid parameter copy, but also get the same call method as value passing parameter. + +```c++ +void test(const vector &data) +{ + //... +} +int main() +{ + vector data{1,2,3,4,5,6,7,8}; + test(data); +} +``` + +> Reference type return value + +C++ Provides the ability to overload operators.The syntax of the overloaded operator is the same as that of the original operator.An example is the operator [] operator, which generally needs to return a reference object in order to be modified correctly. + +```c++ +vector v(10); +v[5] = 10; //[]Operator returns the reference, and then the corresponding element of vector can be modified + //If[] operator do not return a reference but a pointer, the assignment statement needs to be written like this +*v[5] = 10; // This way of writing is totally inconsistent with our understanding of the call of [], which is easy to be misunderstood +``` + +## 3.Performance gap between pointer and reference + +Is there a performance gap between pointers and references?This kind of problem needs to enter the assembly level to have a look. Let's first write a test1 function, which uses pointers for parameter passing: + +```c++ +void test1(int* p) +{ + *p = 3; //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。 + return; +} +``` + +The assembly code corresponding to this code segment is as follows: + +```c++ +(gdb) disassemble +Dump of assembler code for function test1(int*): + 0x0000000000400886 <+0>: push %rbp + 0x0000000000400887 <+1>: mov %rsp,%rbp + 0x000000000040088a <+4>: mov %rdi,-0x8(%rbp) +=> 0x000000000040088e <+8>: mov -0x8(%rbp),%rax + 0x0000000000400892 <+12>: movl $0x3,(%rax) + 0x0000000000400898 <+18>: nop + 0x0000000000400899 <+19>: pop %rbp + 0x000000000040089a <+20>: retq +End of assembler dump. + +``` + +The above code lines 1 and 2 are the field operation of parameter calling and saving;The third line is parameter passing. The first parameter of the function call is usually placed in the RDI register. This line of code writes the RDI register value (the value of pointer P) to the stack;Line 4 writes the value of P in the stack to the rax register;Line 5 is to write the immediate number 3 to the**Memory pointed to by the value of the rax register**. + +Let's write out the reference C + + code segment test2 for parameter passing: + +```c++ +void test2(int& r) +{ + r = 3; // do not need to judge whether reference is null. + return; +} +``` + +This code corresponds to the following assembly code: + +```c++ +(gdb) disassemble +Dump of assembler code for function test2(int&): + 0x000000000040089b <+0>: push %rbp + 0x000000000040089c <+1>: mov %rsp,%rbp + 0x000000000040089f <+4>: mov %rdi,-0x8(%rbp) +=> 0x00000000004008a3 <+8>: mov -0x8(%rbp),%rax + 0x00000000004008a7 <+12>: movl $0x3,(%rax) + 0x00000000004008ad <+18>: nop + 0x00000000004008ae <+19>: pop %rbp + 0x00000000004008af <+20>: retq +End of assembler dump. + +``` + +We find that the assembly code corresponding to test2 is exactly the same as that of test1.This shows that the C + + compiler compiles the pointer and reference into exactly the same machine code when compiling the program. Therefore, the reference in C + + is just a "syntax sugar" of pointer operation in C ++,In the underlying implementation, the C + + compiler implements these two operations in the same way. + +## 3. Conclusion + +The introduction of reference operation in C + + ensures the security and convenience of reference use and maintains the elegance of code under the condition of adding more restrictions on the use of reference. The use of reference can avoid the situation of "pointer flying all over the sky" to a certain extent, and it has a positive significance to improve the stability of the program. Finally, the underlying implementations of pointers and references are the same, and there is no need to worry about the performance gap between them. + +From: diff --git a/english/basic_content/pointer_refer/copy_construct b/english/basic_content/pointer_refer/copy_construct new file mode 100755 index 0000000..fa57fa3 Binary files /dev/null and b/english/basic_content/pointer_refer/copy_construct differ diff --git a/english/basic_content/pointer_refer/copy_construct.cpp b/english/basic_content/pointer_refer/copy_construct.cpp new file mode 100644 index 0000000..1fa28ba --- /dev/null +++ b/english/basic_content/pointer_refer/copy_construct.cpp @@ -0,0 +1,35 @@ +/** + * @file copy_construct.cpp + * @brief g++ -o copy_construct copy_construct.cpp -fno-elide-constructors + * -fno-elide-constructors选项(关闭返回值优化) + * @author 光城 + * @version v1 + * @date 2019-08-09 + */ + +#include +using namespace std; + +class Copyable { +public: + Copyable(){} + Copyable(const Copyable &o) { + cout << "Copied" << endl; + } +}; +Copyable ReturnRvalue() { + return Copyable(); //返回一个临时对象 +} +void AcceptVal(Copyable a) { + +} +void AcceptRef(const Copyable& a) { + +} + +int main() { + cout << "pass by value: " << endl; + AcceptVal(ReturnRvalue()); // 应该调用两次拷贝构造函数 + cout << "pass by reference: " << endl; + AcceptRef(ReturnRvalue()); //应该只调用一次拷贝构造函数 +} diff --git a/english/basic_content/pointer_refer/effec b/english/basic_content/pointer_refer/effec new file mode 100755 index 0000000..a7bf86a Binary files /dev/null and b/english/basic_content/pointer_refer/effec differ diff --git a/english/basic_content/pointer_refer/effec.cpp b/english/basic_content/pointer_refer/effec.cpp new file mode 100644 index 0000000..677353a --- /dev/null +++ b/english/basic_content/pointer_refer/effec.cpp @@ -0,0 +1,22 @@ +#include +using namespace std; +void test1(int* p) +{ + *p = 3; //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。 + return; +} + +void test2(int& p) +{ + p = 3; //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。 + return; +} + +int main() { + int a=10; + int *p=&a; + test1(p); + test2(a); + cout< +using namespace std; +class A{}; +int main() +{ + cout< +using namespace std; +class A +{ + public: + char b; + virtual void fun() {}; + static int c; + static int d; + static int f; +}; + +int main() +{ + /** + * @brief 16 + */ + cout< +using namespace std; +class A{ + virtual void fun(); + virtual void fun1(); + virtual void fun2(); + virtual void fun3(); +}; +int main() +{ + cout< + +using namespace std; + +class A +{ + public: + char a; + int b; +}; + +/** + * @brief + * char a + * int b + * short a + * long b + * 根据字节对齐4+4=8+8+8=24 + */ +class B:A +{ + public: + short a; + long b; +}; +class C +{ + A a; + char c; +}; +class A1 +{ + virtual void fun(){} +}; +class C1:public A1 +{ +}; + +int main() +{ + cout< +using namespace std; +class A +{ + virtual void fun() {} +}; +class B +{ + virtual void fun2() {} +}; +class C : virtual public A, virtual public B +{ + public: + virtual void fun3() {} +}; + +int main() +{ + /** + * @brief 8 8 16 The derived class inherits multiple virtual functions and inherits the VPTR of all virtual functions + */ + cout< + +using namespace std; + +class A{}; +int main() +{ + cout< + + +using namespace std; + +class A +{ + public: + A(); + ~A(); + static int a; + static void fun3(); + void fun(); + void fun1(); +}; + +int main() +{ + cout< + +using namespace std; + +class A +{ + public: + char a; + int b; +}; + +/** + * @brief 此时B按照顺序: + * char a + * int b + * short a + * long b + * 根据字节对齐4+4=8+8+8=24 + */ +class B:A +{ + public: + short a; + long b; +}; +class C +{ + A a; + char c; +}; + +class A1 +{ + virtual void fun(){} +}; +class C1:public A +{ +}; + + +int main() +{ + cout< + +using namespace std; + +class A +{ + public: + char a; + int b; +}; + +class B +{ + public: + short a; + long b; +}; + +/** + * @brief 8+16+8=32 + */ +class C:A,B +{ + char c; +}; + + +int main() +{ + cout< + +using namespace std; + +class A{ + + virtual void fun(); + virtual void fun1(); + virtual void fun2(); + virtual void fun3(); +}; +int main() +{ + cout< +using namespace std; +class A +{ + public: + char b; + virtual void fun() {}; + static int c; + static int d; + static int f; +}; + + + +int main() +{ + + /** + * @brief 16 字节对齐、静态变量不影响类的大小、vptr指针=8 + */ + cout< + +using namespace std; + +class A +{ + virtual void fun() {} +}; + +class B +{ + virtual void fun2() {} +}; +class C : virtual public A, virtual public B +{ + public: + virtual void fun3() {} +}; + + +int main() +{ + + /** + * @brief 8 8 16 派生类虚继承多个虚函数,会继承所有虚函数的vptr + */ + cout< + +using namespace std; + +class A +{ + virtual void fun() {} +}; + +class B +{ + virtual void fun2() {} +}; +class C : public A, public B +{ + public: + virtual void fun3() {} +}; + + +int main() +{ + + /** + * @brief 8 8 16 派生类继承多个虚函数,会继承所有虚函数的vptr + */ + cout< +#include +using namespace std; + +void demo() +{ + // static variable + static int count = 0; + cout << count << " "; + + // value is updated and + // will be carried to next + // function calls + count++; +} + +int main() +{ + for (int i=0; i<5; i++) + demo(); + return 0; +} +``` + +Output: + +``` +0 1 2 3 4 +``` + +You can see in the above program that the variable count is declared static.So, Its value is passed through a function call. The variable count is not initialized each time the function is called. + +- Static variables in class + +Because variables declared static are initialized only once,Because they allocate space in separate static storage。Therefore, static variables in a class are **shared by objects。**You cannot have multiple copies of the same static variable for different objects. For this reason, static variables cannot be initialized with constructors. + +```c++ + +#include +using namespace std; + +class Apple +{ +public: + static int i; + + Apple() + { + // Do nothing + }; +}; + +int main() +{ +Apple obj1; +Apple obj2; +obj1.i =2; +obj2.i = 3; + +// prints value of i +cout << obj1.i<<" "< +using namespace std; + +class Apple +{ +public: + static int i; + + Apple() + { + // Do nothing + }; +}; + +int Apple::i = 1; + +int main() +{ + Apple obj; + // prints value of i + cout << obj.i; +} +``` + +Output: + +``` +1 +``` + +**Static member** + +- Class objects are static + +Just like variables, objects have scope when declared static until the life cycle of the program + +Consider the following program, where the object is non static + +```c++ +#include +using namespace std; + +class Apple +{ + int i; + public: + Apple() + { + i = 0; + cout << "Inside Constructor\n"; + } + ~Apple() + { + cout << "Inside Destructor\n"; + } +}; + +int main() +{ + int x = 0; + if (x==0) + { + Apple obj; + } + cout << "End of main\n"; +} + +``` + + +Output: + +```c++ +Inside Constructor +Inside Destructor +End of main +``` + +In the above program, the object is declared non static within the if block. Therefore, the scope of the variable is only within the if block. Therefore, when an object is created, the constructor is called, and is called when the control of the if block passes over the destructor, because the scope of the object is only within the if block in which it is declared. + +If we declare the object static, now let's look at the changes in the output. + +```c++ +#include +using namespace std; + +class Apple +{ + int i; + public: + Apple() + { + i = 0; + cout << "Inside Constructor\n"; + } + ~Apple() + { + cout << "Inside Destructor\n"; + } +}; + +int main() +{ + int x = 0; + if (x==0) + { + static Apple obj; + } + cout << "End of main\n"; +} + +``` + + +Output: + +``` +Inside Constructor +End of main +Inside Destructor +``` + +You can clearly see the output change. Now, call the destructor function after the end of main.This is because the scope of static objects is throughout the life cycle of the program. + +- Static functions in class + +Just like static data members or static variables in a class, Static member functions are also independent of class objects.We are allowed to use objects and "." to call static member functions. However, it is recommended to call static members using class names and range resolution operators. + +Allows static member functions to access only static data members or other static member functions that cannot access non static data members or member functions of a class. + +```c++ +#include +using namespace std; + +class Apple +{ + public: + // static member function + static void printMsg() + { + cout<<"Welcome to Apple!"; + } +}; + +// main function +int main() +{ + // invoking a static member function + Apple::printMsg(); +} +``` + +Output: + +``` +Welcome to Apple! +``` + diff --git a/english/basic_content/static/demo b/english/basic_content/static/demo new file mode 100755 index 0000000..fdd7a26 Binary files /dev/null and b/english/basic_content/static/demo differ diff --git a/english/basic_content/static/nostatic_class.cpp b/english/basic_content/static/nostatic_class.cpp new file mode 100644 index 0000000..b4e56d2 --- /dev/null +++ b/english/basic_content/static/nostatic_class.cpp @@ -0,0 +1,28 @@ +#include +using namespace std; + +class Apple +{ + int i; + public: + Apple() + { + i = 0; + cout << "Inside Constructor\n"; + } + ~Apple() + { + cout << "Inside Destructor\n"; + } +}; + +int main() +{ + int x = 0; + if (x==0) + { + Apple obj; + } + cout << "End of main\n"; +} + diff --git a/english/basic_content/static/static_class.cpp b/english/basic_content/static/static_class.cpp new file mode 100644 index 0000000..007e400 --- /dev/null +++ b/english/basic_content/static/static_class.cpp @@ -0,0 +1,28 @@ +#include +using namespace std; + +class Apple +{ + int i; + public: + Apple() + { + i = 0; + cout << "Inside Constructor\n"; + } + ~Apple() + { + cout << "Inside Destructor\n"; + } +}; + +int main() +{ + int x = 0; + if (x==0) + { + static Apple obj; + } + cout << "End of main\n"; +} + diff --git a/english/basic_content/static/static_demo.cpp b/english/basic_content/static/static_demo.cpp new file mode 100644 index 0000000..1910088 --- /dev/null +++ b/english/basic_content/static/static_demo.cpp @@ -0,0 +1,24 @@ +// the use of static Static +// variables in a Function +#include +#include +using namespace std; + +void demo() +{ + // static variable + static int count = 0; + cout << count << " "; + + // value is updated and + // will be carried to next + // function calls + count++; +} + +int main() +{ + for (int i=0; i<5; i++) + demo(); + return 0; +} diff --git a/english/basic_content/static/static_error_variable.cpp b/english/basic_content/static/static_error_variable.cpp new file mode 100644 index 0000000..47b1319 --- /dev/null +++ b/english/basic_content/static/static_error_variable.cpp @@ -0,0 +1,26 @@ +// variables inside a class + +#include +using namespace std; + +class Apple +{ + public: + static int i; + + Apple() + { + // Do nothing + }; +}; + +int main() +{ + Apple obj1; + Apple obj2; + obj1.i =2; + obj2.i = 3; + + // prints value of i + cout << obj1.i<<" "< +using namespace std; + +class Apple +{ + public: + // static member function + static void printMsg() + { + cout<<"Welcome to Apple!"; + } +}; + +// main function +int main() +{ + // invoking a static member function + Apple::printMsg(); +} + diff --git a/english/basic_content/static/static_variable.cpp b/english/basic_content/static/static_variable.cpp new file mode 100644 index 0000000..b14780e --- /dev/null +++ b/english/basic_content/static/static_variable.cpp @@ -0,0 +1,24 @@ +// variables inside a class + +#include +using namespace std; + +class GfG +{ + public: + static int i; + + GfG() + { + // Do nothing + }; +}; + +int GfG::i = 1; + +int main() +{ + GfG obj; + // prints value of i + cout << obj.i; +} diff --git a/english/basic_content/struct/README.md b/english/basic_content/struct/README.md new file mode 100644 index 0000000..200c698 --- /dev/null +++ b/english/basic_content/struct/README.md @@ -0,0 +1,226 @@ +# Understand C and C + + struct + + + +![](../img/wechat.jpg) + +## 1.Struct In C + +- In C, struct is only used as the composite type of data, that is, in the structure declaration, only data members can be placed inside, but not functions +- C + + access modifiers cannot be used in C structure declarations,such as:public、protected、private. But they can be used in C + + +- Define the structure variable in C. If you use the following definition, you must add struct +- The structure of C cannot be inherited (there is no such concept) +- If the structure name is the same as the function name, it can run normally and call normally! For example, you can define void base() that does not conflict with struct base{}。 + +Example: + +```c +#include + +struct Base { // public + int v1; +// public: //error + int v2; + //private: + int v3; + //void print(){ // c中不能在结构体中嵌入函数 + // printf("%s\n","hello world"); + //}; //error! +}; + +void Base(){ + printf("%s\n","I am Base func"); +} +//struct Base base1; //ok +//Base base2; //error +int main() { + struct Base base; + base.v1=1; + //base.print(); + printf("%d\n",base.v1); + Base(); + return 0; +} +``` + +Finally Output: + +``` +1 +I am Base func +``` + +Code:[struct_func.c](./struct_func.c) + +## 2.C++ struct + +The comparison with C is as follows: + +- In C + + structure, not only data can be defined, but also functions can be defined. +- Access modifiers can be used in C + + structures, such as :public、protected、private 。 +- C + + structure can be used directly without struct. +- C + + inheritance +- If the structure name is the same as the function name, it can run normally and call normally!However, when defining the structure variable, we only use the structure with struct! + +Example: + +> Case 1: + +Before adding a function with the same name: + +```c++ +struct Student { + +}; +Student(){} +Struct Student s; //ok +Student s; //ok +``` + +After adding a function with the same name: + +```c++ +struct Student { + +}; +Student(){} +Struct Student s; //ok +Student s; //error +``` + +> Case 2: + +```c++ +typedef struct Base1 { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}B; +//void B() {} //error! 符号 "B" 已经被定义为一个 "struct Base1" 的别名 +``` + +> The first three cases + +```c++ +#include +#include + +struct Base { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}; + +int main() { + struct Base base1; //ok + Base base2; //ok + Base base; + base.v1=1; + base.v3=2; + base.print(); + printf("%d\n",base.v1); + printf("%d\n",base.v3); + return 0; +} +``` + +Code :[struct_func.cpp](struct_func.cpp) + +> Inheritance cases + +```c++ +#include +#include +struct Base { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + virtual void print(){ + printf("%s\n","Base"); + }; +}; +struct Derived:Base { + + public: + int v2; + void print(){ + printf("%s\n","Derived"); + }; +}; +int main() { + Base *b=new Derived(); + b->print(); + return 0; +} +``` + +Code:[ext_struct_func.cpp](./ext_struct_func.cpp) + +> Functions with the same name + +```c++ +#include +#include + +struct Base { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}; + +typedef struct Base1 { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}B; +void Base(){ + printf("%s\n","I am Base func"); +} +//void B() {} //error! 符号 "B" 已经被定义为一个 "struct Base1" 的别名 +int main() { + struct Base base; //ok + //Base base1; // error! + base.v1=1; + base.v3=2; + base.print(); + printf("%d\n",base.v1); + printf("%d\n",base.v3); + Base(); + return 0; +} +``` +Code:[struct_func_func.cpp](./struct_func_func.cpp) + +## 3.Conclusion + +### The difference of structure between C and C + + + +| C | C++ | +| ------------------------------------------------------ | ------------------------------------------------------------ | +| You cannot place a function in a structure declaration | Function can be declared in structure | +| C + + access modifiers cannot be used in C structure declarations. | public、protected、private They can be used in C + + | +| Define the structure variable in C. If you use the following definition, you must add struct | You can do without struct | +|Not inheritance | you could use inheritance | +| If the structure name is the same as the function name, it can run normally and call normally | If the structure name is the same as the function name, the structure can only be defined with struct | diff --git a/english/basic_content/struct/ext b/english/basic_content/struct/ext new file mode 100755 index 0000000..45104f8 Binary files /dev/null and b/english/basic_content/struct/ext differ diff --git a/english/basic_content/struct/ext_struct_func.cpp b/english/basic_content/struct/ext_struct_func.cpp new file mode 100644 index 0000000..7ab1eb2 --- /dev/null +++ b/english/basic_content/struct/ext_struct_func.cpp @@ -0,0 +1,33 @@ +#include +#include +using namespace std; +struct Base { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + virtual void print(){ + printf("%s\n","Base"); + }; + Base(){cout<<"Base construct"<print(); + delete b; + return 0; +} diff --git a/english/basic_content/struct/sf b/english/basic_content/struct/sf new file mode 100755 index 0000000..321061d Binary files /dev/null and b/english/basic_content/struct/sf differ diff --git a/english/basic_content/struct/stff b/english/basic_content/struct/stff new file mode 100755 index 0000000..de43727 Binary files /dev/null and b/english/basic_content/struct/stff differ diff --git a/english/basic_content/struct/struct_func b/english/basic_content/struct/struct_func new file mode 100755 index 0000000..8541eba Binary files /dev/null and b/english/basic_content/struct/struct_func differ diff --git a/english/basic_content/struct/struct_func.c b/english/basic_content/struct/struct_func.c new file mode 100644 index 0000000..6bea643 --- /dev/null +++ b/english/basic_content/struct/struct_func.c @@ -0,0 +1,27 @@ +#include + +struct Base { // public + int v1; +// public: //error + int v2; + //private: + int v3; + //void print(){ // c中不能在结构体中嵌入函数 + // printf("%s\n","hello world"); + //}; //error! +}; + +void Base(){ + printf("%s\n","I am Base func"); +} +//struct Base base1; //ok +//Base base2; //error + +int main() { + struct Base base; + base.v1=1; + //base.print(); + printf("%d\n",base.v1); + Base(); + return 0; +} diff --git a/english/basic_content/struct/struct_func.cpp b/english/basic_content/struct/struct_func.cpp new file mode 100644 index 0000000..09bef26 --- /dev/null +++ b/english/basic_content/struct/struct_func.cpp @@ -0,0 +1,25 @@ +#include +#include + +struct Base { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}; + +int main() { + struct Base base1; //ok + Base base2; //ok + Base base; + base.v1=1; + base.v3=2; + base.print(); + printf("%d\n",base.v1); + printf("%d\n",base.v3); + return 0; +} diff --git a/english/basic_content/struct/struct_func_func.cpp b/english/basic_content/struct/struct_func_func.cpp new file mode 100644 index 0000000..bce3749 --- /dev/null +++ b/english/basic_content/struct/struct_func_func.cpp @@ -0,0 +1,39 @@ +#include +#include + +struct Base { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}; + +typedef struct Base1 { + int v1; +// private: //error! + int v3; + public: //显示声明public + int v2; + void print(){ + printf("%s\n","hello world"); + }; +}B; +void Base(){ + printf("%s\n","I am Base func"); +} +//void B() {} //error! 符号 "B" 已经被定义为一个 "struct Base1" 的别名 +int main() { + struct Base base; //ok + //Base base1; // error! + base.v1=1; + base.v3=2; + base.print(); + printf("%d\n",base.v1); + printf("%d\n",base.v3); + Base(); + return 0; +} diff --git a/english/basic_content/struct/stu b/english/basic_content/struct/stu new file mode 100755 index 0000000..6dabbe1 Binary files /dev/null and b/english/basic_content/struct/stu differ diff --git a/english/basic_content/struct_class/README.md b/english/basic_content/struct_class/README.md new file mode 100644 index 0000000..aeca0f5 --- /dev/null +++ b/english/basic_content/struct_class/README.md @@ -0,0 +1,19 @@ +# The difference struct and class + + + +![](../img/wechat.jpg) + +More detail about struct [Story about struct](../struct) + +Generally speaking, struct is more suitable to be regarded as an implementation body of data structure, and class is more suitable to be regarded as the implementation body of an object. + +Dif: + +One of the essential differences is the default access control + + + +Default inherited access.Struct is public, while class is private. + +Structure as the implementation body of data structure.Its default data access control is public, while class is the implementation body of the object, and its default member variable access control is private. diff --git a/english/basic_content/this/README.md b/english/basic_content/this/README.md new file mode 100644 index 0000000..1ce99f3 --- /dev/null +++ b/english/basic_content/this/README.md @@ -0,0 +1,87 @@ +# Story about this + +## About Author + + +![](../img/wechat.jpg) + +## 1.This pointer + +I believe that many people sitting here are learning python. For Python, there is self. Analogy to C + + is this pointer. Let's analyze the use of this pointer in class. + +Let's first talk about the use of this pointer: + +(1)This pointer to an object is not part of the object itself and does not affect the result of sizeof. + +(2)This scope is within the class. When accessing the non static members of the class in the non static member function of the class, the compiler will automatically pass the address of the object itself to the function as an implicit parameter. That is to say, even if you don't write this pointer, the compiler will add this when compiling. As the implicit formal parameter of non static member function, the access to each member is carried out through this. + +Second, the use of this pointer: + +(1)When the class object itself is returned in a class's non static member function, directly use return *this。 + +(2)When the parameter is the same as the member variable name, 如this->n = n (不能写成n = n)。 + + +The following examples are available: + +```c++ +#include +#include + + +using namespace std; +class Person{ +public: + typedef enum { + BOY = 0, + GIRL + }SexType; + Person(char *n, int a,SexType s){ + name=new char[strlen(n)+1]; + strcpy(name,n); + age=a; + sex=s; + } + int get_age() const{ + + return this->age; + } + Person& add_age(int a){ + age+=a; + return *this; + } + ~Person(){ + delete [] name; + } +private: + char * name; + int age; + SexType sex; +}; + + +int main(){ + Person p("zhangsan",20,Person::BOY); + cout< +#include + + +using namespace std; +class Person{ +public: + typedef enum { + BOY = 0, + GIRL + }SexType; + Person(char *n, int a,SexType s){ + name=new char[strlen(n)+1]; + strcpy(name,n); + age=a; + sex=s; + } + int get_age() const{ + + return this->age; + } + Person& add_age(int a){ + age+=a; + return *this; + } + ~Person(){ + delete [] name; + } +private: + char * name; + int age; + SexType sex; +}; + + +int main(){ + Person p("zhangsan",20,Person::BOY); + cout< +/** + * 默认访问控制符为public + */ +union UnionTest { + /** + * 可以含有构造函数、析构函数 + */ + UnionTest() : i(10) {print(i);}; + ~UnionTest(){}; + int i; +private: + void print(int i){std::cout< +#define isNs1 1 +//#define isGlobal 2 +using namespace std; +void func() +{ + cout<<"::func"< +using namespace std; + +class Base{ + public: + void f(){ cout<<"f()"< V1; +using V2 = vector; +``` +Code:[using_typedef.cpp](using_typedef.cpp) diff --git a/english/basic_content/using/derived_base b/english/basic_content/using/derived_base new file mode 100755 index 0000000..952568a Binary files /dev/null and b/english/basic_content/using/derived_base differ diff --git a/english/basic_content/using/derived_base.cpp b/english/basic_content/using/derived_base.cpp new file mode 100644 index 0000000..bc57800 --- /dev/null +++ b/english/basic_content/using/derived_base.cpp @@ -0,0 +1,48 @@ +#include + +using namespace std; + +class Base1 { + public: + Base1():value(10) {} + virtual ~Base1() {} + void test1() { cout << "Base test1..." << endl; } + protected: // 保护 + int value; +}; +// 默认为私有继承 +class Derived1 : Base1 { + public: + void test2() { cout << "value is " << value << endl; } +}; + +class Base { + public: + Base():value(20) {} + virtual ~Base() {} + void test1() { cout << "Base test1..." << endl; } + private: //私有 + int value; +}; + +/** + * 子类对父类成员的访问权限跟如何继承没有任何关系, + * “子类可以访问父类的public和protected成员,不可以访问父类的private成员”——这句话对任何一种继承都是成立的。 + * + */ +class Derived : Base { + public: + using Base::value; + void test2() { cout << "value is " << value << endl; } +}; + + +int main() +{ + Derived1 d1; + d1.test2(); + + Derived d; + d.test2(); + return 0; +} diff --git a/english/basic_content/using/using_derived b/english/basic_content/using/using_derived new file mode 100755 index 0000000..641ac8b Binary files /dev/null and b/english/basic_content/using/using_derived differ diff --git a/english/basic_content/using/using_derived.cpp b/english/basic_content/using/using_derived.cpp new file mode 100644 index 0000000..eb7b5ae --- /dev/null +++ b/english/basic_content/using/using_derived.cpp @@ -0,0 +1,36 @@ +/** + * @file using_derived.cpp + * @brief 函数重装 + * @author 光城 + * @version v1 + * @date 2019-08-07 + */ + +#include +using namespace std; + +class Base{ + public: + void f(){ cout<<"f()"< +#define isNs1 1 +//#define isGlobal 2 +using namespace std; +void func() +{ + cout<<"::func"< +#include +using namespace std; + +typedef vector V1; +using V2 = vector; + + +int main() +{ + int nums1[] = {1,2,3,4,5,6}; + V1 vec1(nums1,nums1+sizeof(nums1)/sizeof(int)); + int nums2[] = {5,7,6}; + V2 vec2(nums2,nums2+sizeof(nums2)/sizeof(int)); + + for(auto i:vec1) + cout< +using namespace std; + + +class Employee +{ + public: + virtual void raiseSalary() + { + cout<<0<raiseSalary(); // Polymorphic Call: Calls raiseSalary() + // according to the actual object, not + // according to the type of pointer +} +int main(){ + Employee *emp[]={new Manager(),new Engineer}; + globalRaiseSalary(emp,2); + return 0; +} diff --git a/english/basic_content/virtual/set2/default_arg.cpp b/english/basic_content/virtual/set2/default_arg.cpp new file mode 100644 index 0000000..4797818 --- /dev/null +++ b/english/basic_content/virtual/set2/default_arg.cpp @@ -0,0 +1,39 @@ +/** + * @file first_example.cpp + * @brief 虚函数中默认参数 + * 规则:虚函数是动态绑定的,默认参数是静态绑定的。默认参数的使用需要看指针或者应用本身的类型,而不是对象的类型! + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +#include +using namespace std; + +class Base +{ + public: + virtual void fun ( int x = 10 ) + { + cout << "Base::fun(), x = " << x << endl; + } +}; + +class Derived : public Base +{ + public: + virtual void fun ( int x=20 ) + { + cout << "Derived::fun(), x = " << x << endl; + } +}; + + +int main() +{ + Derived d1; + Base *bp = &d1; + bp->fun(); // 10 + return 0; +} + diff --git a/english/basic_content/virtual/set3/copy_consrtuct.cpp b/english/basic_content/virtual/set3/copy_consrtuct.cpp new file mode 100644 index 0000000..e65ca89 --- /dev/null +++ b/english/basic_content/virtual/set3/copy_consrtuct.cpp @@ -0,0 +1,41 @@ +#include +using namespace std; + +class Base +{ + public: + +}; + +class Derived : public Base +{ + public: + Derived() + { + cout << "Derived created" << endl; + } + + Derived(const Derived &rhs) + { + cout << "Derived created by deep copy" << endl; + } + + ~Derived() + { + cout << "Derived destroyed" << endl; + } +}; + +int main() +{ + Derived s1; + + Derived s2 = s1; // Compiler invokes "copy constructor" + // Type of s1 and s2 are concrete to compiler + + // How can we create Derived1 or Derived2 object + // from pointer (reference) to Base class pointing Derived object? + + return 0; +} + diff --git a/english/basic_content/virtual/set3/full_virde.cpp b/english/basic_content/virtual/set3/full_virde.cpp new file mode 100644 index 0000000..d4ef7a8 --- /dev/null +++ b/english/basic_content/virtual/set3/full_virde.cpp @@ -0,0 +1,39 @@ +/** + * @file full_virde.cpp + * @brief 将基类的析构函数声明为虚函数 + * 输出结果: + * Constructing base + * Constructing derived + * Destructing derived + * Destructing base + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ +#include + +using namespace std; + +class base { + public: + base() + { cout<<"Constructing base \n"; } + virtual ~base() + { cout<<"Destructing base \n"; } +}; + +class derived: public base { + public: + derived() + { cout<<"Constructing derived \n"; } + ~derived() + { cout<<"Destructing derived \n"; } +}; + +int main(void) +{ + derived *d = new derived(); + base *b = d; + delete b; + return 0; +} diff --git a/english/basic_content/virtual/set3/inline_virtual.cpp b/english/basic_content/virtual/set3/inline_virtual.cpp new file mode 100644 index 0000000..1ec1a06 --- /dev/null +++ b/english/basic_content/virtual/set3/inline_virtual.cpp @@ -0,0 +1,35 @@ +#include +using namespace std; +class Base +{ + public: + inline virtual void who() + { + cout << "I am Base\n"; + } + virtual ~Base() {} +}; +class Derived : public Base +{ + public: + inline void who() // 不写inline时隐式内联 + { + cout << "I am Derived\n"; + } +}; + +int main() +{ + // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 + Base b; + b.who(); + + // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。 + Base *ptr = new Derived(); + ptr->who(); + + // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。 + delete ptr; + + return 0; +} diff --git a/english/basic_content/virtual/set3/static_error.cpp b/english/basic_content/virtual/set3/static_error.cpp new file mode 100644 index 0000000..5a79667 --- /dev/null +++ b/english/basic_content/virtual/set3/static_error.cpp @@ -0,0 +1,13 @@ +/** + * @file static_error.cpp + * @brief 静态函数不可以声明为虚函数,同时也不能被const和volatile关键字修饰! + * 原因如下: + * static成员函数不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义 + * 虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,静态成员函数没有this指针,所以无法访问vptr。 + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +virtual static void fun() { } +static void fun() const { } diff --git a/english/basic_content/virtual/set3/vir_con.cpp b/english/basic_content/virtual/set3/vir_con.cpp new file mode 100644 index 0000000..78c4a07 --- /dev/null +++ b/english/basic_content/virtual/set3/vir_con.cpp @@ -0,0 +1,206 @@ +/** + * @file vir_con.cpp + * @brief 构造函数不可以声明为虚函数。同时除了inline之外,构造函数不允许使用其它任何关键字。 + * + * 为什么构造函数不可以为虚函数? + * + * 尽管虚函数表vtable是在编译阶段就已经建立的,但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数,编译器会在构造函数中添加代码来创建vptr。 问题来了,如果构造函数是虚的,那么它需要vptr来访问vtable,可这个时候vptr还没产生。 因此,构造函数不可以为虚函数。 + * 我们之所以使用虚函数,是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的,实例的类型必须是明确的。 + * 因此,构造函数没有必要被声明为虚函数。 + * 尽管构造函数不可以为虚函数,但是有些场景下我们确实需要 “Virtual Copy Constructor”。 “虚复制构造函数”的说法并不严谨,其只是一个实现了对象复制的功能的类内函数。 举一个应用场景,比如剪切板功能。 复制内容作为基类,但派生类可能包含文字、图片、视频等等。 我们只有在程序运行的时候才知道我们需要复制的具体是什么类型的数据。 + * + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +#include +using namespace std; + +//// LIBRARY SRART +class Base +{ + public: + Base() { } + + virtual // Ensures to invoke actual object destructor + ~Base() { } + + virtual void ChangeAttributes() = 0; + + // The "Virtual Constructor" + static Base *Create(int id); + + // The "Virtual Copy Constructor" + virtual Base *Clone() = 0; +}; + +class Derived1 : public Base +{ + public: + Derived1() + { + cout << "Derived1 created" << endl; + } + + Derived1(const Derived1& rhs) + { + cout << "Derived1 created by deep copy" << endl; + } + + ~Derived1() + { + cout << "~Derived1 destroyed" << endl; + } + + void ChangeAttributes() + { + cout << "Derived1 Attributes Changed" << endl; + } + + Base *Clone() + { + return new Derived1(*this); + } +}; + +class Derived2 : public Base +{ + public: + Derived2() + { + cout << "Derived2 created" << endl; + } + + Derived2(const Derived2& rhs) + { + cout << "Derived2 created by deep copy" << endl; + } + + ~Derived2() + { + cout << "~Derived2 destroyed" << endl; + } + + void ChangeAttributes() + { + cout << "Derived2 Attributes Changed" << endl; + } + + Base *Clone() + { + return new Derived2(*this); + } +}; + +class Derived3 : public Base +{ + public: + Derived3() + { + cout << "Derived3 created" << endl; + } + + Derived3(const Derived3& rhs) + { + cout << "Derived3 created by deep copy" << endl; + } + + ~Derived3() + { + cout << "~Derived3 destroyed" << endl; + } + + void ChangeAttributes() + { + cout << "Derived3 Attributes Changed" << endl; + } + + Base *Clone() + { + return new Derived3(*this); + } +}; + +// We can also declare "Create" outside Base. +// But is more relevant to limit it's scope to Base +Base *Base::Create(int id) +{ + // Just expand the if-else ladder, if new Derived class is created + // User need not be recompiled to create newly added class objects + + if( id == 1 ) + { + return new Derived1; + } + else if( id == 2 ) + { + return new Derived2; + } + else + { + return new Derived3; + } +} +//// LIBRARY END + +//// UTILITY SRART +class User +{ + public: + User() : pBase(0) + { + // Creates any object of Base heirarchey at runtime + + int input; + + cout << "Enter ID (1, 2 or 3): "; + cin >> input; + + while( (input != 1) && (input != 2) && (input != 3) ) + { + cout << "Enter ID (1, 2 or 3 only): "; + cin >> input; + } + + // Create objects via the "Virtual Constructor" + pBase = Base::Create(input); + } + + ~User() + { + if( pBase ) + { + delete pBase; + pBase = 0; + } + } + + void Action() + { + // Duplicate current object + Base *pNewBase = pBase->Clone(); + + // Change its attributes + pNewBase->ChangeAttributes(); + + // Dispose the created object + delete pNewBase; + } + + private: + Base *pBase; +}; + +//// UTILITY END + +//// Consumer of User (UTILITY) class +int main() +{ + User *user = new User(); + + user->Action(); + + delete user; +} + diff --git a/english/basic_content/virtual/set3/vir_de.cpp b/english/basic_content/virtual/set3/vir_de.cpp new file mode 100644 index 0000000..79c7c65 --- /dev/null +++ b/english/basic_content/virtual/set3/vir_de.cpp @@ -0,0 +1,41 @@ +/** + * @file vir_de.cpp + * @brief 派生类的析构函数没有被调用! + * 输出结果: + * Constructing base + * Constructing derived + * Destructing base + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +// CPP program without virtual destructor +// causing undefined behavior +#include + +using namespace std; + +class base { + public: + base() + { cout<<"Constructing base \n"; } + ~base() + { cout<<"Destructing base \n"; } +}; + +class derived: public base { + public: + derived() + { cout<<"Constructing derived \n"; } + ~derived() + { cout<<"Destructing derived \n"; } +}; + +int main(void) +{ + derived *d = new derived(); + base *b = d; + delete b; + return 0; +} diff --git a/english/basic_content/virtual/set3/virtual_function.cpp b/english/basic_content/virtual/set3/virtual_function.cpp new file mode 100644 index 0000000..1b6d039 --- /dev/null +++ b/english/basic_content/virtual/set3/virtual_function.cpp @@ -0,0 +1,33 @@ +/** + * @file virtual_function.cpp + * @brief 虚函数可以被私有化,但有一些细节需要注意。 + * 基类指针指向继承类对象,则调用继承类对象的函数; + * int main()必须声明为Base类的友元,否则编译失败。 编译器报错: ptr无法访问私有函数。 + * 当然,把基类声明为public, 继承类为private,该问题就不存在了。----> 见另外一个例子! + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +#include +using namespace std; + +class Derived; + +class Base { + private: + virtual void fun() { cout << "Base Fun"; } + friend int main(); +}; + +class Derived: public Base { + public: + void fun() { cout << "Derived Fun"; } +}; + +int main() +{ + Base *ptr = new Derived; + ptr->fun(); + return 0; +} diff --git a/english/basic_content/virtual/set3/virtual_function1.cpp b/english/basic_content/virtual/set3/virtual_function1.cpp new file mode 100644 index 0000000..27648e1 --- /dev/null +++ b/english/basic_content/virtual/set3/virtual_function1.cpp @@ -0,0 +1,22 @@ +#include +using namespace std; + +class Derived; + +class Base { + public: + virtual void fun() { cout << "Base Fun"; } + // friend int main(); +}; + +class Derived: public Base { + private: + void fun() { cout << "Derived Fun"; } +}; + +int main() +{ + Base *ptr = new Derived; + ptr->fun(); + return 0; +} diff --git a/english/basic_content/virtual/set3/virtual_inline.cpp b/english/basic_content/virtual/set3/virtual_inline.cpp new file mode 100644 index 0000000..ac89964 --- /dev/null +++ b/english/basic_content/virtual/set3/virtual_inline.cpp @@ -0,0 +1,44 @@ +/** + * @file virtual_inline.cpp + * @brief 通常类成员函数都会被编译器考虑是否进行内联。 + * 但通过基类指针或者引用调用的虚函数必定不能被内联。 + * 当然,实体对象调用虚函数或者静态调用时可以被内联,虚析构函数的静态调用也一定会被内联展开。 + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +#include +using namespace std; +class Base +{ + public: + virtual void who() + { + cout << "I am Base\n"; + } +}; +class Derived: public Base +{ + public: + void who() + { + cout << "I am Derived\n"; + } +}; + +int main() +{ + // note here virtual function who() is called through + // object of the class (it will be resolved at compile + // time) so it can be inlined. + Base b; + b.who(); + + // Here virtual function is called through pointer, + // so it cannot be inlined + Base *ptr = new Derived(); + ptr->who(); + + return 0; +} diff --git a/english/basic_content/virtual/set4/rtti b/english/basic_content/virtual/set4/rtti new file mode 100755 index 0000000..c7cb891 Binary files /dev/null and b/english/basic_content/virtual/set4/rtti differ diff --git a/english/basic_content/virtual/set4/rtti.cpp b/english/basic_content/virtual/set4/rtti.cpp new file mode 100644 index 0000000..649687e --- /dev/null +++ b/english/basic_content/virtual/set4/rtti.cpp @@ -0,0 +1,34 @@ +/** + * @file rtti.cpp + * @brief 在面向对象程序设计中,有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof,以及C#的as、is运算符类似,C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++ reinterpret_cast,dynamic_cast提供了类型安全检查,是一种基于能力查询(Capability Query)的转换,所以在多态类型间进行转换更提倡采用dynamic_cast + * @author 光城 + * @version v1 + * @date 2019-07-24 + */ + +// CPP program to illustrate +// // Run Time Type Identification +#include +#include +using namespace std; +class B { virtual void fun() {} }; +class D: public B { }; + +int main() +{ + B *b = new D; // 向上转型 + B &obj = *b; + D *d = dynamic_cast(b); // 向下转型 + if(d != NULL) + cout << "works"<(obj); + cout << "works"< +int main(void) +{ + const int local = 10; + int *ptr = (int*) &local; + + printf("Initial value of local : %d \n", local); + + *ptr = 100; + + printf("Modified value of local: %d \n", local); + + return 0; +} diff --git a/english/basic_content/volatile/nv b/english/basic_content/volatile/nv new file mode 100755 index 0000000..b5eb220 Binary files /dev/null and b/english/basic_content/volatile/nv differ diff --git a/english/basic_content/volatile/volatile.cpp b/english/basic_content/volatile/volatile.cpp new file mode 100644 index 0000000..fcf6115 --- /dev/null +++ b/english/basic_content/volatile/volatile.cpp @@ -0,0 +1,16 @@ +/* Compile code with optimization option */ +#include + +int main(void) +{ + const volatile int local = 10; + int *ptr = (int*) &local; + + printf("Initial value of local : %d \n", local); + + *ptr = 100; + + printf("Modified value of local: %d \n", local); + + return 0; +} diff --git a/english/basic_content/vptr_vtable/README.md b/english/basic_content/vptr_vtable/README.md new file mode 100644 index 0000000..1b82cc6 --- /dev/null +++ b/english/basic_content/vptr_vtable/README.md @@ -0,0 +1,219 @@ +# 深入浅出C++虚函数的vptr与vtable + +## 关于作者: + +个人公众号: + +![](../img/wechat.jpg) + + + +## 1.基础理论 + +为了实现虚函数,C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称,例如“vtable”,“虚函数表”,“虚方法表”或“调度表”。 + +虚拟表实际上非常简单,虽然用文字描述有点复杂。首先,**每个使用虚函数的类(或者从使用虚函数的类派生)都有自己的虚拟表**。该表只是编译器在编译时设置的静态数组。虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针,指向该类可访问的派生函数。 + +其次,编译器还会添加一个隐藏指向基类的指针,我们称之为vptr。vptr在创建类实例时自动设置,以便指向该类的虚拟表。与this指针不同,this指针实际上是编译器用来解析自引用的函数参数,vptr是一个真正的指针。 + +因此,它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承,这很重要。 + +## 2.实现与内部结构 + +下面我们来看自动与手动操纵vptr来获取地址与调用虚函数! + +开始看代码之前,为了方便大家理解,这里给出调用图: + +![base](./img/base.jpg) + +代码全部遵循标准的注释风格,相信大家看了就会明白,不明白的话,可以留言! + +```c++ +/** + * @file vptr1.cpp + * @brief C++虚函数vptr和vtable + * 编译:g++ -g -o vptr vptr1.cpp -std=c++11 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +#include +using namespace std; + +/** + * @brief 函数指针 + */ +typedef void (*Fun)(); + +/** + * @brief 基类 + */ +class Base +{ + public: + Base(){}; + virtual void fun1() + { + cout << "Base::fun1()" << endl; + } + virtual void fun2() + { + cout << "Base::fun2()" << endl; + } + virtual void fun3(){} + ~Base(){}; +}; + +/** + * @brief 派生类 + */ +class Derived: public Base +{ + public: + Derived(){}; + void fun1() + { + cout << "Derived::fun1()" << endl; + } + void fun2() + { + cout << "DerivedClass::fun2()" << endl; + } + ~Derived(){}; +}; +/** + * @brief 获取vptr地址与func地址,vptr指向的是一块内存,这块内存存放的是虚函数地址,这块内存就是我们所说的虚表 + * + * @param obj + * @param offset + * + * @return + */ +Fun getAddr(void* obj,unsigned int offset) +{ + cout<<"======================="<fun1(); + cout<<"基类引用指向基类实例并调用虚函数"<fun1(); +``` + +其过程为:首先程序识别出fun1()是个虚函数,其次程序使用pt->vptr来获取Derived的虚拟表。第三,它查找Derived虚拟表中调用哪个版本的fun1()。这里就可以发现调用的是Derived::fun1()。因此pt->fun1()被解析为Derived::fun1()! + +除此之外,上述代码大家会看到,也包含了手动获取vptr地址,并调用vtable中的函数,那么我们一起来验证一下上述的地址与真正在自动调用vtable中的虚函数,比如上述`pt->fun1()`的时候,是否一致! + +这里采用gdb调试,在编译的时候记得加上`-g`。 + +通过`gdb vptr`进入gdb调试页面,然后输入`b Derived::fun1`对fun1打断点,然后通过输入r运行程序到断点处,此时我们需要查看调用栈中的内存地址,通过`disassemable fun1`可以查看当前有关fun1中的相关汇编代码,我们看到了`0x0000000000400ea8`,然后再对比上述的结果会发现与手动调用的fun1一致,fun2类似,以此证明代码正确! + +gdb调试信息如下: + +```c++ +(gdb) b Derived::fun1 +Breakpoint 1 at 0x400eb4: file vptr1.cpp, line 23. +(gdb) r +Starting program: /home/light/Program/CPlusPlusThings/virtual/pure_virtualAndabstract_class/vptr +基类对象直接调用 +Base::fun1() +基类引用指向派生类实例 +Base::fun1() +基类指针指向派生类实例并调用虚函数 + +Breakpoint 1, Derived::fun1 (this=0x614c20) at vptr1.cpp:23 +23 cout << "Derived::fun1()" << endl; +(gdb) disassemble fun1 +Dump of assembler code for function Derived::fun1(): + 0x0000000000400ea8 <+0>: push %rbp + 0x0000000000400ea9 <+1>: mov %rsp,%rbp + 0x0000000000400eac <+4>: sub $0x10,%rsp + 0x0000000000400eb0 <+8>: mov %rdi,-0x8(%rbp) +=> 0x0000000000400eb4 <+12>: mov $0x401013,%esi + 0x0000000000400eb9 <+17>: mov $0x602100,%edi + 0x0000000000400ebe <+22>: callq 0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> + 0x0000000000400ec3 <+27>: mov $0x400a00,%esi + 0x0000000000400ec8 <+32>: mov %rax,%rdi + 0x0000000000400ecb <+35>: callq 0x4009f0 <_ZNSolsEPFRSoS_E@plt> + 0x0000000000400ed0 <+40>: nop + 0x0000000000400ed1 <+41>: leaveq + 0x0000000000400ed2 <+42>: retq +End of assembler dump. +(gdb) disassemble fun2 +Dump of assembler code for function Derived::fun2(): + 0x0000000000400ed4 <+0>: push %rbp + 0x0000000000400ed5 <+1>: mov %rsp,%rbp + 0x0000000000400ed8 <+4>: sub $0x10,%rsp + 0x0000000000400edc <+8>: mov %rdi,-0x8(%rbp) + 0x0000000000400ee0 <+12>: mov $0x401023,%esi + 0x0000000000400ee5 <+17>: mov $0x602100,%edi + 0x0000000000400eea <+22>: callq 0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> + 0x0000000000400eef <+27>: mov $0x400a00,%esi + 0x0000000000400ef4 <+32>: mov %rax,%rdi + 0x0000000000400ef7 <+35>: callq 0x4009f0 <_ZNSolsEPFRSoS_E@plt> + 0x0000000000400efc <+40>: nop + 0x0000000000400efd <+41>: leaveq + 0x0000000000400efe <+42>: retq +End of assembler dump. +``` + diff --git a/english/basic_content/vptr_vtable/img/base.jpg b/english/basic_content/vptr_vtable/img/base.jpg new file mode 100644 index 0000000..669f2a0 Binary files /dev/null and b/english/basic_content/vptr_vtable/img/base.jpg differ diff --git a/english/basic_content/vptr_vtable/vptr1.cpp b/english/basic_content/vptr_vtable/vptr1.cpp new file mode 100644 index 0000000..31f9209 --- /dev/null +++ b/english/basic_content/vptr_vtable/vptr1.cpp @@ -0,0 +1,105 @@ +/** + * @file vptr1.cpp + * @brief C++虚函数vptr和vtable + * 编译:g++ -g -o vptr vptr1.cpp -std=c++11 + * @author 光城 + * @version v1 + * @date 2019-07-20 + */ + +#include +#include +using namespace std; + +/** + * @brief 函数指针 + */ +typedef void (*Fun)(); + + +/** + * @brief 基类 + */ +class Base +{ + public: + Base(){}; + virtual void fun1() + { + cout << "Base::fun1()" << endl; + } + virtual void fun2() + { + cout << "Base::fun2()" << endl; + } + virtual void fun3(){} + ~Base(){}; +}; + + +/** + * @brief 派生类 + */ +class Derived: public Base +{ + public: + Derived(){}; + void fun1() + { + cout << "Derived::fun1()" << endl; + } + void fun2() + { + cout << "DerivedClass::fun2()" << endl; + } + ~Derived(){}; +}; + +/** + * @brief 获取vptr地址与func地址,vptr指向的是一块内存,这块内存存放的是虚函数地址,这块内存就是我们所说的虚表 + * + * @param obj + * @param offset + * + * @return + */ +Fun getAddr(void* obj,unsigned int offset) +{ + cout<<"======================="<fun1(); + cout<<"基类引用指向基类实例并调用虚函数"< using namespace std; template -//ʵֽ +//实现降序 void sort(T &a, int n){ for (int i=0;i #include using namespace std; -struct Person{ //ְϢĽṹ - double salary; - char *name; +struct Person +{ //职工基本信息的结构 + double salary; + char *name; }; -class SalaryManaege{ - Person *employ; //ְϢ - int max; //±Ͻ - int n; //еʵְ +class SalaryManaege +{ + Person *employ; //存放职工信息的数组 + int max; //数组下标上界 + int n; //数组中的实际职工人数 public: - SalaryManaege(int Max=0){ - max=Max; - n=0; - employ=new Person[max]; - } - //ǿֱڷֱֵʹ - double &operator[](char *Name) { //[] - Person *p; - for(p=employ;pname,Name)==0) - return p->salary; - // - p=employ + n++; - p->name=new char[strlen(Name)+1]; - strcpy(p->name,Name); - p->salary=0; - return p->salary; - } + SalaryManaege(int Max = 0) + { + max = Max; + n = 0; + employ = new Person[max]; + } + //返回引用特性是可以直接在放在左值,直接使用 + double &operator[](char *Name) + { //重载[],返回引用 + Person *p; + for (p = employ; p < employ + n; p++) + //如果存在处理 + if (strcmp(p->name, Name) == 0) + return p->salary; + //不存在情况处理 + p = employ + n++; + p->name = new char[strlen(Name) + 1]; + strcpy(p->name, Name); + p->salary = 0; + return p->salary; + } - void display(){ - for(int i=0;i -char *sPtr; //ַָ -s="" -sPtr = new char[strlen(s)+1]; -strncpy(sPtr,s; \ No newline at end of file +#include + + +int main() { + + char *sPtr; + const char* s="hello"; + sPtr = new char[strlen(s)+1]; + strncpy(sPtr,s,strlen(s)); + std::cout << sPtr << std::endl; + delete sPtr; + return 0; +} diff --git a/src_analysis/stl/hashtable.md b/src_analysis/stl/hashtable.md index 7db3180..dcd6b39 100644 --- a/src_analysis/stl/hashtable.md +++ b/src_analysis/stl/hashtable.md @@ -2,7 +2,7 @@ ## 0.导语 -哈希表,是作为`unordered_map`与`undered_set`等的底层容器,自gcc2.9后源码量大增! +哈希表,是作为`unordered_map`与`unordered_set`等的底层容器,自gcc2.9后源码量大增! 这次阅读的代码仍旧是gcc4.9.1,代码量非常多,就不全部展开,重点研究底层哈希的艺术与技术,似乎这两个词语很押韵哦,哈哈,进入正文~ diff --git a/src_analysis/stl/unordered_map.md b/src_analysis/stl/unordered_map.md index bce0269..943dd94 100644 --- a/src_analysis/stl/unordered_map.md +++ b/src_analysis/stl/unordered_map.md @@ -9,8 +9,8 @@ unordered_map与unordered_multimap的源码在`unordered_map.h`这个文件中。 -## 1.undered_map与unordered_multimap本质区别 -先来看一下undered_map源码: +## 1.unordered_map与unordered_multimap本质区别 +先来看一下unordered_map源码: ```cpp template ``` -默认情况下,undered_map采用: +默认情况下,unordered_map采用: - H1为hash - H2为_Mod_range_hashing - _Hash为_Default_ranged_hash - _RehashPolicy为_Prime_rehash_policy - _Traits为_Tr -对于最后的_Tr,非常重要,因为正是因为这个参数,才有undered_multimap。 +对于最后的_Tr,非常重要,因为正是因为这个参数,才有unordered_multimap。 具体分析看下面: @@ -91,7 +91,7 @@ struct _Hashtable_traits using __unique_keys = __bool_constant<_Unique_keys>; }; ``` -看到有三个using,理解为三个typedef,依次表示:hash code缓存与否,是否是常迭代器,是否是唯一的key,再往上回头看,传递进来的是三个模板参数,分别是false,false,true,也验证了undered_map是唯一的key,那么对应的undered_multimap就是不唯一的key,最后一个参数为false。我们翻阅到相应代码如下: +看到有三个using,理解为三个typedef,依次表示:hash code缓存与否,是否是常迭代器,是否是唯一的key,再往上回头看,传递进来的是三个模板参数,分别是false,false,true,也验证了unordered_map是唯一的key,那么对应的unordered_multimap就是不唯一的key,最后一个参数为false。我们翻阅到相应代码如下: ```cpp /// Base types for unordered_multimap. @@ -101,8 +101,8 @@ using __ummap_traits = __detail::_Hashtable_traits<_Cache, false, false>; 小结,在上面分析,我们知道了unordered_map与unordered_multimap的本质区别,也发现了如何在底层源码上用一个容器实现两个容器适配器! -## 2.undered_set与unordered_multiset本质区别 -分析同前面一样,先看undered_set: +## 2.unordered_set与unordered_multiset本质区别 +分析同前面一样,先看unordered_set: ```cpp template; ``` -可以看到传递给`_Hashtable_traits`的是false,true,true。对于undered_set来说使用的是const iterator与唯一的key,我们再看一下unordered_multiset: +可以看到传递给`_Hashtable_traits`的是false,true,true。对于unordered_set来说使用的是const iterator与唯一的key,我们再看一下unordered_multiset: ```cpp template using __umset_traits = __detail::_Hashtable_traits<_Cache, true, false>; ``` -再将两者对比一下,本质就是undered_set不允许key重复,而undered_multiset允许key重复。 +再将两者对比一下,本质就是unordered_set不允许key重复,而unordered_multiset允许key重复。 ## 3.三大结论 现在,我们有了前面基础,依次列出前面四个容器适配器: -(1) undered_map +(1) unordered_map ```cpp template using __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>; ``` -(2) undered_multimap +(2) unordered_multimap ```cpp template using __umap_traits = __detail::_Hashtable_traits<_Cache, false, false>; ``` -(3) undered_set +(3) unordered_set ```cpp template using __uset_traits = __detail::_Hashtable_traits<_Cache, true, true>; ``` -(4) undered_multiset +(4) unordered_multiset ```cpp template @@ -166,13 +166,13 @@ using __uset_traits = __detail::_Hashtable_traits<_Cache, true, false>; 对比后,得出 -- 结论1:undered_map与undered_set不允许key重复,而带multi的则允许key重复; -- 结论2:undered_map与undered_multimap采用的迭代器是iterator,而undered_set与undered_multiset采用的迭代器是const_iterator。 -- 结论3:undered_map与undered_multimap的key是key,value是key+value;而undered_set与undered_multiset的key是Value,Value也是Key。 +- 结论1:unordered_map与unordered_set不允许key重复,而带multi的则允许key重复; +- 结论2:unordered_map与unordered_multimap采用的迭代器是iterator,而unordered_set与unordered_multiset采用的迭代器是const_iterator。 +- 结论3:unordered_map与unordered_multimap的key是key,value是key+value;而unordered_set与unordered_multiset的key是Value,Value也是Key。 最后一个结论对比看下面(我们看传递给hashtable的第一与第二个参数): -undered_map与undered_multimap: +unordered_map与unordered_multimap: ```cpp using __umap_hashtable = _Hashtable<_Key, std::pair, @@ -182,7 +182,7 @@ __detail::_Mod_range_hashing, __detail::_Default_ranged_hash, __detail::_Prime_rehash_policy, _Tr>; ``` -undered_set与undered_multiset: +unordered_set与unordered_multiset: ```cpp template, @@ -196,13 +196,13 @@ __detail::_Default_ranged_hash, __detail::_Prime_rehash_policy, _Tr>; ``` -## 4.undered_map重要函数 +## 4.unordered_map重要函数 > 初始化 -可以在下面的构造函数中看到undered_map的默认桶数为10。 +可以在下面的构造函数中看到unordered_map的默认桶数为10。 -在undered_map的底层默认采用hasher(),也就是H1,也就是std::hash +在unordered_map的底层默认采用hasher(),也就是H1,也就是std::hash ```cpp unordered_map(size_type __n = 10, @@ -319,7 +319,7 @@ clear() noexcept ``` > hash_function -得到该undered_map的hash_function +得到该unordered_map的hash_function ```cpp hasher hash_function() const @@ -353,4 +353,4 @@ at(const key_type& __k) ``` 除了这些函数还有获取桶,最大桶数、加载因子、rehash等等,就是没有排序,因为hashtable没有提供排序功能。hashtable在查找、删除和插入节点是常数时间,优于RB-Tree红黑树。 -同理,unordered_set、unordered_multiset、unordered_multimap与undered_map一样的函数,所以就不阐述了。 \ No newline at end of file +同理,unordered_set、unordered_multiset、unordered_multimap与unordered_map一样的函数,所以就不阐述了。 \ No newline at end of file