Repository: Light-City/CPlusPlusThings
Branch: master
Commit: 7e98e7ccbf7c
Files: 675
Total size: 745.6 KB
Directory structure:
gitextract_54wk3wyp/
├── .gitignore
├── README.md
├── README_EN.md
├── WORKSPACE
├── basic_content/
│ ├── abstract/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── abstract.cpp
│ │ ├── abstract_base.h
│ │ ├── derived_full.cpp
│ │ ├── interesting_facts1.cpp
│ │ ├── interesting_facts2.cpp
│ │ ├── interesting_facts3.cpp
│ │ ├── interesting_facts4.cpp
│ │ ├── interesting_facts5.cpp
│ │ └── pure_virtual.cpp
│ ├── assert/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── assert.c
│ │ └── ignore_assert.c
│ ├── bit/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── bit.cpp
│ ├── c_poly/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── c++_examp.cpp
│ │ └── c_examp.c
│ ├── const/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── class_const/
│ │ │ ├── c++11_example/
│ │ │ │ ├── BUILD
│ │ │ │ ├── apple.cpp
│ │ │ │ ├── apple.h
│ │ │ │ └── main.cpp
│ │ │ ├── first_example/
│ │ │ │ ├── BUILD
│ │ │ │ ├── apple.cpp
│ │ │ │ ├── apple.h
│ │ │ │ └── main.cpp
│ │ │ ├── overload_example/
│ │ │ │ ├── BUILD
│ │ │ │ ├── apple.cpp
│ │ │ │ ├── apple.h
│ │ │ │ └── main.cpp
│ │ │ └── static_example/
│ │ │ ├── BUILD
│ │ │ ├── apple.cpp
│ │ │ ├── apple.h
│ │ │ └── main.cpp
│ │ ├── const_function.cpp
│ │ ├── const_num.cpp
│ │ ├── extern_const/
│ │ │ ├── BUILD
│ │ │ ├── const_file1.cpp
│ │ │ ├── const_file2.cpp
│ │ │ ├── file1.cpp
│ │ │ └── file2.cpp
│ │ └── funciton_const/
│ │ ├── condition1/
│ │ │ ├── BUILD
│ │ │ ├── condition1.cpp
│ │ │ ├── condition2.cpp
│ │ │ └── condition3.cpp
│ │ ├── condition2/
│ │ │ ├── BUILD
│ │ │ ├── condition1.cpp
│ │ │ ├── condition2.cpp
│ │ │ └── condition3.cpp
│ │ └── condition3/
│ │ ├── BUILD
│ │ └── condition1.cpp
│ ├── decltype/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── decltype.cpp
│ ├── enum/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── classic_practice.cpp
│ │ └── tradition_color.cpp
│ ├── explicit/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── explicit.cpp
│ ├── extern/
│ │ ├── README.md
│ │ ├── extern_c/
│ │ │ ├── BUILD
│ │ │ ├── add.c
│ │ │ ├── add.cpp
│ │ │ └── add.h
│ │ └── extern_c++/
│ │ ├── BUILD
│ │ ├── add.c
│ │ ├── add.cpp
│ │ └── add.h
│ ├── friend/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── friend_class.cpp
│ │ └── friend_func.cpp
│ ├── func_pointer/
│ │ ├── BUILD
│ │ └── func_pointer.cpp
│ ├── inline/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── inline.cpp
│ │ ├── inline.h
│ │ └── inline_virtual.cpp
│ ├── macro/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── do_while.cpp
│ │ └── sig_examp.cpp
│ ├── maohao/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── maohao.cpp
│ ├── pointer_refer/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── copy_construct.cpp
│ │ └── effec.cpp
│ ├── sizeof/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── blackclass.cpp
│ │ ├── genA.cpp
│ │ ├── geninhe.cpp
│ │ ├── moreinhe.cpp
│ │ ├── morevir.cpp
│ │ ├── static.cpp
│ │ ├── virinhe.cpp
│ │ └── virmoreinhe.cpp
│ ├── static/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── nostatic_class.cpp
│ │ ├── static_class.cpp
│ │ ├── static_demo.cpp
│ │ ├── static_error_variable.cpp
│ │ ├── static_funciton.cpp
│ │ └── static_variable.cpp
│ ├── struct/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── ext_struct_func.cpp
│ │ ├── struct_func.c
│ │ ├── struct_func.cpp
│ │ ├── struct_func_func.cpp
│ │ └── stu
│ ├── struct_class/
│ │ └── README.md
│ ├── this/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── person.cpp
│ ├── union/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── union.cpp
│ ├── using/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── derived_base.cpp
│ │ ├── using_derived.cpp
│ │ ├── using_global.cpp
│ │ └── using_typedef.cpp
│ ├── virtual/
│ │ ├── README.md
│ │ ├── set1/
│ │ │ ├── BUILD
│ │ │ └── emp.cpp
│ │ ├── set2/
│ │ │ ├── BUILD
│ │ │ └── default_arg.cpp
│ │ ├── set3/
│ │ │ ├── BUILD
│ │ │ ├── copy_consrtuct.cpp
│ │ │ ├── full_virde.cpp
│ │ │ ├── inline_virtual.cpp
│ │ │ ├── static_error.cpp
│ │ │ ├── vir_con.cpp
│ │ │ ├── vir_de.cpp
│ │ │ ├── virtual_function.cpp
│ │ │ ├── virtual_function1.cpp
│ │ │ └── virtual_inline.cpp
│ │ └── set4/
│ │ ├── BUILD
│ │ ├── rtti.cpp
│ │ └── warn_rtti.cpp
│ ├── volatile/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── noopt_vola.cpp
│ │ └── volatile.cpp
│ └── vptr_vtable/
│ ├── BUILD
│ ├── README.md
│ └── vptr1.cpp
├── codingStyleIdioms/
│ ├── 1_classInitializers/
│ │ ├── 1.1_nest.cpp
│ │ ├── 1.2_nodefault_ctor.cpp
│ │ ├── 1.3_const.cpp
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── initializer.cpp
│ ├── 2_enumclass/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── namespace.cpp
│ ├── 3_RAII/
│ │ ├── BUILD
│ │ ├── RAII.cpp
│ │ ├── RAII.md
│ │ ├── RAII_fstram.cpp
│ │ ├── c++_example.cpp
│ │ ├── c++_example1.cpp
│ │ ├── c++_example2.cpp
│ │ └── c_example.cpp
│ ├── 4_copy-swap/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── copy-swapAndADL.cpp
│ ├── 5_pImpl/
│ │ ├── BUILD
│ │ ├── README.md
│ │ ├── noPimpl.cpp
│ │ ├── pimpl.cpp
│ │ ├── pimplTime.cpp
│ │ └── pimplTime.h
│ └── README.md
├── concurrency/
│ ├── Threading_In_CPlusPlus/
│ │ ├── 1.thread/
│ │ │ ├── BUILD
│ │ │ ├── intro.cpp
│ │ │ └── thread.cpp
│ │ ├── 2.create_type/
│ │ │ ├── 1.function_pointer.cpp
│ │ │ ├── 2.lambda_function.cpp
│ │ │ ├── 3.functor.cpp
│ │ │ ├── 4.no_static_member_function.cpp
│ │ │ ├── 5.static_member_function.cpp
│ │ │ └── BUILD
│ │ ├── 3.join_detach/
│ │ │ ├── BUILD
│ │ │ ├── detach.cpp
│ │ │ └── join.cpp
│ │ └── 4.mutex/
│ │ ├── BUILD
│ │ ├── an_example_of_bank_account.cpp
│ │ └── critical_section.cpp
│ └── concurrency_v1/
│ ├── chapter1/
│ │ ├── 1_helloworld.cpp
│ │ └── BUILD
│ └── chapter2/
│ ├── 2.1_basic.cpp
│ ├── 2.2_transfer.cpp
│ ├── 2.3_ownership.cpp
│ ├── 2.4_runtime.cpp
│ ├── 2_5_id.cpp
│ └── BUILD
├── cpp2.0/
│ └── cpp11/
│ ├── BUILD
│ ├── README.md
│ ├── alias.cpp
│ ├── auto.cpp
│ ├── constexpr.cpp
│ ├── decltype.cpp
│ ├── default_delete.cpp
│ ├── explicit.cpp
│ ├── final.cpp
│ ├── hash.cpp
│ ├── initializer.cpp
│ ├── lambda.cpp
│ ├── move.cpp
│ ├── noexcept.cpp
│ ├── nullptr.cpp
│ ├── override.cpp
│ ├── rvalue.cpp
│ ├── template_template.cpp
│ ├── tuple.cpp
│ ├── type_alias.cpp
│ ├── uniform_initialization.cpp
│ └── variadic/
│ ├── BUILD
│ ├── variadic.cpp
│ ├── variadic1.cpp
│ ├── variadic2.cpp
│ ├── variadic3_4.cpp
│ ├── variadic5.cpp
│ ├── variadic6.cpp
│ └── variadic7.cpp
├── design_pattern/
│ ├── producer_consumer/
│ │ ├── BUILD
│ │ └── producer_consumer.cpp
│ └── singleton/
│ ├── README.md
│ ├── barrier_singleton.cpp
│ ├── cpulpuls11_singleton.cpp
│ ├── dcl_singleton.cpp
│ ├── hungrysingleton.cpp
│ ├── iazysingleton.cpp
│ ├── lock_singleton.cpp
│ ├── pthreadoncesingleton.cpp
│ └── static_local_singleton.cpp
├── effective_cpp/
│ ├── 2.cpp
│ └── BUILD
├── english/
│ └── basic_content/
│ ├── abstract/
│ │ ├── README.md
│ │ ├── abstract.cpp
│ │ ├── derived_full.cpp
│ │ ├── interesting_facts1.cpp
│ │ ├── interesting_facts2.cpp
│ │ ├── interesting_facts3.cpp
│ │ ├── interesting_facts4.cpp
│ │ ├── interesting_facts5.cpp
│ │ ├── pure_virtual.cpp
│ │ └── test.cpp
│ ├── assert/
│ │ ├── README.md
│ │ ├── assert.c
│ │ └── ignore_assert.c
│ ├── bit/
│ │ ├── README.md
│ │ └── learn.cpp
│ ├── c_poly/
│ │ ├── README.md
│ │ ├── c++_examp
│ │ ├── c++_examp.cpp
│ │ ├── c_examp
│ │ └── c_examp.c
│ ├── const/
│ │ ├── README.md
│ │ ├── class_const/
│ │ │ ├── c++11_example/
│ │ │ │ ├── apple.cpp
│ │ │ │ ├── main
│ │ │ │ └── main.cpp
│ │ │ ├── first_example/
│ │ │ │ ├── apple.cpp
│ │ │ │ └── main.cpp
│ │ │ ├── overload_example/
│ │ │ │ ├── apple.cpp
│ │ │ │ ├── main
│ │ │ │ └── main.cpp
│ │ │ └── static_example/
│ │ │ ├── apple.cpp
│ │ │ ├── main
│ │ │ └── main.cpp
│ │ ├── const_function.cpp
│ │ ├── const_num.cpp
│ │ ├── extern_const/
│ │ │ ├── const_file1.cpp
│ │ │ ├── const_file2.cpp
│ │ │ ├── file1.cpp
│ │ │ └── file2.cpp
│ │ └── funciton_const/
│ │ ├── condition1/
│ │ │ ├── condition1.cpp
│ │ │ ├── condition2.cpp
│ │ │ └── condition3.cpp
│ │ ├── condition2/
│ │ │ ├── condition1
│ │ │ ├── condition1.cpp
│ │ │ ├── condition2.cpp
│ │ │ ├── condition3
│ │ │ └── condition3.cpp
│ │ └── condition3/
│ │ ├── condition1
│ │ └── condition1.cpp
│ ├── decltype/
│ │ ├── README.md
│ │ ├── decltype
│ │ └── decltype.cpp
│ ├── enum/
│ │ ├── README.md
│ │ ├── classic_practice
│ │ ├── classic_practice.cpp
│ │ └── tradition_color.cpp
│ ├── explicit/
│ │ ├── README.md
│ │ └── explicit.cpp
│ ├── extern/
│ │ ├── README.md
│ │ ├── extern_c/
│ │ │ ├── add.c
│ │ │ ├── add.cpp
│ │ │ ├── add.h
│ │ │ ├── add.o
│ │ │ └── main
│ │ └── extern_c++/
│ │ ├── add.c
│ │ ├── add.cpp
│ │ ├── add.h
│ │ ├── add.o
│ │ └── main
│ ├── friend/
│ │ ├── README.md
│ │ ├── friend_class
│ │ ├── friend_class.cpp
│ │ ├── friend_func
│ │ └── friend_func.cpp
│ ├── func_pointer/
│ │ ├── func1
│ │ └── func_pointer.cpp
│ ├── inline/
│ │ ├── README.md
│ │ ├── inline
│ │ ├── inline.cpp
│ │ ├── inline.h
│ │ ├── inline_virtual.cpp
│ │ └── iv
│ ├── macro/
│ │ ├── README.md
│ │ ├── do_while
│ │ ├── do_while.cpp
│ │ ├── sig_examp
│ │ └── sig_examp.cpp
│ ├── maohao/
│ │ ├── .README.md.un~
│ │ ├── README.md
│ │ ├── README.md~
│ │ └── maohao.cpp
│ ├── pointer_refer/
│ │ ├── README.md
│ │ ├── copy_construct
│ │ ├── copy_construct.cpp
│ │ ├── effec
│ │ └── effec.cpp
│ ├── sizeof/
│ │ ├── README.md
│ │ ├── blackclass.cpp
│ │ ├── genA.cpp
│ │ ├── geninhe.cpp
│ │ ├── moreinhe.cpp
│ │ ├── morevir.cpp
│ │ ├── static.cpp
│ │ ├── virinhe.cpp
│ │ └── virmoreinhe.cpp
│ ├── static/
│ │ ├── README.md
│ │ ├── demo
│ │ ├── nostatic_class.cpp
│ │ ├── static_class.cpp
│ │ ├── static_demo.cpp
│ │ ├── static_error_variable.cpp
│ │ ├── static_funciton.cpp
│ │ └── static_variable.cpp
│ ├── struct/
│ │ ├── README.md
│ │ ├── ext
│ │ ├── ext_struct_func.cpp
│ │ ├── sf
│ │ ├── stff
│ │ ├── struct_func
│ │ ├── struct_func.c
│ │ ├── struct_func.cpp
│ │ ├── struct_func_func.cpp
│ │ └── stu
│ ├── struct_class/
│ │ └── README.md
│ ├── this/
│ │ ├── README.md
│ │ ├── person
│ │ └── person.cpp
│ ├── union/
│ │ ├── README.md
│ │ ├── union
│ │ └── union.cpp
│ ├── using/
│ │ ├── README.md
│ │ ├── derived_base
│ │ ├── derived_base.cpp
│ │ ├── using_derived
│ │ ├── using_derived.cpp
│ │ ├── using_global
│ │ ├── using_global.cpp
│ │ ├── using_typedef
│ │ └── using_typedef.cpp
│ ├── virtual/
│ │ ├── README.md
│ │ ├── set1/
│ │ │ ├── emp
│ │ │ └── emp.cpp
│ │ ├── set2/
│ │ │ └── default_arg.cpp
│ │ ├── set3/
│ │ │ ├── copy_consrtuct.cpp
│ │ │ ├── full_virde.cpp
│ │ │ ├── inline_virtual.cpp
│ │ │ ├── static_error.cpp
│ │ │ ├── vir_con.cpp
│ │ │ ├── vir_de.cpp
│ │ │ ├── virtual_function.cpp
│ │ │ ├── virtual_function1.cpp
│ │ │ └── virtual_inline.cpp
│ │ └── set4/
│ │ ├── rtti
│ │ ├── rtti.cpp
│ │ └── warn_rtti.cpp
│ ├── volatile/
│ │ ├── README.md
│ │ ├── noopt_vola.cpp
│ │ ├── nv
│ │ └── volatile.cpp
│ └── vptr_vtable/
│ ├── README.md
│ └── vptr1.cpp
├── extension/
│ └── some_problem/
│ └── string_int.md
├── learn_class/
│ └── modern_cpp_30/
│ ├── RAII/
│ │ ├── BUILD
│ │ ├── RAII.cpp
│ │ ├── heap.cpp
│ │ └── stack.cpp
│ ├── SFINAE/
│ │ ├── README.md
│ │ ├── SFINAE.cpp
│ │ └── sfinae paper/
│ │ ├── auto.cpp
│ │ ├── blending1.cpp
│ │ ├── blending2.cpp
│ │ ├── combiningAndGenius.cpp
│ │ ├── constexpr.cpp
│ │ ├── decltype.cpp
│ │ ├── fis_valid.cpp
│ │ ├── hana.cpp
│ │ ├── is_valid.cpp
│ │ ├── lambda.cpp
│ │ ├── overload1.cpp
│ │ ├── overload2.cpp
│ │ ├── p1SFINAE.cpp
│ │ ├── p2SFINAE.cpp
│ │ ├── packis_valid.cpp
│ │ ├── serialize.cpp
│ │ ├── sizeof1.cpp
│ │ ├── sizeof2.cpp
│ │ ├── structData.h
│ │ └── timeGenius.cpp
│ ├── compilercompute/
│ │ ├── BUILD
│ │ ├── IF.cpp
│ │ ├── WhileLoop.cpp
│ │ ├── factorial.cpp
│ │ └── fmap.cpp
│ ├── compilerpoly/
│ │ ├── BUILD
│ │ ├── README.md
│ │ └── template.cpp
│ ├── constexpr/
│ │ ├── BUILD
│ │ ├── container.cpp
│ │ ├── newconstexpr.cpp
│ │ ├── output_container.h
│ │ ├── sqrt.cpp
│ │ └── test3.cpp
│ ├── container1/
│ │ ├── BUILD
│ │ ├── container.cpp
│ │ ├── output_container.h
│ │ └── vector_l.cpp
│ ├── container2/
│ │ ├── BUILD
│ │ ├── array.cpp
│ │ ├── hash.cpp
│ │ ├── priority_queue.cpp
│ │ ├── relacontainer.cpp
│ │ └── unorder.cpp
│ ├── exception/
│ │ ├── BUILD
│ │ └── exception.cpp
│ ├── functionLambda/
│ │ ├── BUILD
│ │ ├── adder.cpp
│ │ ├── autoLambda.cpp
│ │ └── function.cpp
│ ├── literalAssert/
│ │ ├── BUILD
│ │ ├── assert.cpp
│ │ ├── default_delete.cpp
│ │ ├── literal.cpp
│ │ └── overridefinal.cpp
│ ├── memorymodel_atomic/
│ │ ├── barrier_singleton.cpp
│ │ ├── cpulpuls11_singleton.cpp
│ │ ├── dcl_singleton.cpp
│ │ ├── hungrysingleton.cpp
│ │ ├── iazysingleton.cpp
│ │ ├── lock_singleton.cpp
│ │ ├── pthreadoncesingleton.cpp
│ │ └── static_local_singleton.cpp
│ ├── obj/
│ │ ├── BUILD
│ │ ├── all.cpp
│ │ ├── obj1.cpp
│ │ ├── obj2.cpp
│ │ ├── obj3.cpp
│ │ ├── obj4.cpp
│ │ └── obj5.cpp
│ ├── reference/
│ │ ├── BUILD
│ │ ├── collapses.cpp
│ │ ├── forward.cpp
│ │ ├── lifetime.cpp
│ │ ├── ref.cpp
│ │ ├── reference.cpp
│ │ └── shape.h
│ └── smart_ptr/
│ ├── README.md
│ ├── auto_scope.cpp
│ ├── shared_ptr.cpp
│ ├── unique_ptr.cpp
│ └── unique_ptr_U.cpp
├── practical_exercises/
│ ├── 10_day_practice/
│ │ ├── day1/
│ │ │ ├── BUILD
│ │ │ ├── annotate.cpp
│ │ │ ├── print.cpp
│ │ │ ├── runnian.cpp
│ │ │ └── union.cpp
│ │ ├── day10/
│ │ │ ├── file/
│ │ │ │ ├── 10-4.cpp
│ │ │ │ ├── 12-1.cpp
│ │ │ │ ├── 12-2.cpp
│ │ │ │ ├── 12-3.cpp
│ │ │ │ ├── 12-5.cpp
│ │ │ │ ├── 12-6.cpp
│ │ │ │ ├── 12-7.cpp
│ │ │ │ ├── 12-9.cpp
│ │ │ │ ├── BUILD
│ │ │ │ ├── input/
│ │ │ │ │ ├── BUILD
│ │ │ │ │ ├── get.cpp
│ │ │ │ │ ├── get2.cpp
│ │ │ │ │ └── getline.cpp
│ │ │ │ └── practice.cpp
│ │ │ └── readme.md
│ │ ├── day2/
│ │ │ ├── BUILD
│ │ │ ├── compute.cpp
│ │ │ ├── enum.cpp
│ │ │ ├── hanoi.cpp
│ │ │ ├── pow.cpp
│ │ │ ├── rec1.cpp
│ │ │ ├── rec2.cpp
│ │ │ ├── shaizi.cpp
│ │ │ ├── st.cpp
│ │ │ └── static.cpp
│ │ ├── day3/
│ │ │ ├── BUILD
│ │ │ ├── inline.cpp
│ │ │ ├── pratice.cpp
│ │ │ ├── predeclare.cpp
│ │ │ ├── static_data.cpp
│ │ │ ├── static_member1.cpp
│ │ │ ├── static_member2.cpp
│ │ │ └── swap.cpp
│ │ ├── day4/
│ │ │ ├── clock/
│ │ │ │ ├── BUILD
│ │ │ │ ├── operator.cpp
│ │ │ │ └── operator_plus.cpp
│ │ │ ├── const/
│ │ │ │ ├── BUILD
│ │ │ │ ├── obj_func.cpp
│ │ │ │ ├── obj_ref.cpp
│ │ │ │ └── readme.md
│ │ │ ├── copy_ctor/
│ │ │ │ ├── BUILD
│ │ │ │ ├── clock.cpp
│ │ │ │ └── clock.h
│ │ │ └── friend/
│ │ │ ├── BUILD
│ │ │ ├── class.cpp
│ │ │ ├── func.cpp
│ │ │ └── readme.md
│ │ ├── day5/
│ │ │ ├── ctor_dtor/
│ │ │ │ ├── BUILD
│ │ │ │ ├── cseq.cpp
│ │ │ │ ├── ctor.cpp
│ │ │ │ ├── ctor_d.cpp
│ │ │ │ ├── noctor.cpp
│ │ │ │ ├── param.cpp
│ │ │ │ ├── readme.md
│ │ │ │ └── seq.cpp
│ │ │ ├── inherit_access/
│ │ │ │ ├── BUILD
│ │ │ │ ├── private.cpp
│ │ │ │ ├── protected.cpp
│ │ │ │ ├── protected_inherit.cpp
│ │ │ │ ├── public.cpp
│ │ │ │ └── readme.md
│ │ │ ├── rela/
│ │ │ │ ├── BUILD
│ │ │ │ ├── readme.md
│ │ │ │ └── rela.cpp
│ │ │ ├── rule/
│ │ │ │ ├── BUILD
│ │ │ │ ├── direct.cpp
│ │ │ │ └── readme.md
│ │ │ └── virtual/
│ │ │ ├── BUILD
│ │ │ ├── example1.cpp
│ │ │ ├── example2.cpp
│ │ │ ├── init.cpp
│ │ │ ├── readme.md
│ │ │ └── seq.cpp
│ │ ├── day6/
│ │ │ ├── abstract_class/
│ │ │ │ ├── BUILD
│ │ │ │ └── main.cpp
│ │ │ ├── readme.md
│ │ │ └── virtual_func/
│ │ │ ├── BUILD
│ │ │ ├── example.cpp
│ │ │ ├── virtual.cpp
│ │ │ ├── virtual_dtor.cpp
│ │ │ ├── virtual_feature.cpp
│ │ │ └── vis.cpp
│ │ ├── day7/
│ │ │ ├── binary_operator/
│ │ │ │ ├── BUILD
│ │ │ │ ├── friend_operator.cpp
│ │ │ │ └── operator.cpp
│ │ │ ├── brackets/
│ │ │ │ ├── BUILD
│ │ │ │ └── brac.cpp
│ │ │ ├── equal_operator/
│ │ │ │ ├── BUILD
│ │ │ │ └── equal_operator.cpp
│ │ │ ├── example/
│ │ │ │ ├── BUILD
│ │ │ │ └── example.cpp
│ │ │ ├── index_parentheses/
│ │ │ │ ├── BUILD
│ │ │ │ └── example.cpp
│ │ │ ├── readme.md
│ │ │ ├── subscript_operator/
│ │ │ │ ├── BUILD
│ │ │ │ └── subscript_operator.cpp
│ │ │ └── unary_operator/
│ │ │ ├── BUILD
│ │ │ ├── time_counter.cpp
│ │ │ └── time_increase.cpp
│ │ ├── day8/
│ │ │ ├── class_template/
│ │ │ │ ├── BUILD
│ │ │ │ ├── spec.cpp
│ │ │ │ └── stack.cpp
│ │ │ ├── func/
│ │ │ │ ├── BUILD
│ │ │ │ ├── main.cpp
│ │ │ │ ├── max.cpp
│ │ │ │ ├── max_spec.cpp
│ │ │ │ └── sort.cpp
│ │ │ ├── readme.md
│ │ │ └── stl/
│ │ │ ├── BUILD
│ │ │ └── map.cpp
│ │ ├── day9/
│ │ │ ├── exception/
│ │ │ │ ├── 1.cpp
│ │ │ │ ├── 10.cpp
│ │ │ │ ├── 2.cpp
│ │ │ │ ├── 3.cpp
│ │ │ │ ├── 4.cpp
│ │ │ │ ├── 5.cpp
│ │ │ │ ├── 6.cpp
│ │ │ │ ├── 7-1.cpp
│ │ │ │ ├── 7.cpp
│ │ │ │ ├── 8.cpp
│ │ │ │ ├── 9-2.cpp
│ │ │ │ ├── 9.cpp
│ │ │ │ └── BUILD
│ │ │ └── readme.md
│ │ └── readme.md
│ └── key_exercises/
│ ├── BUILD
│ ├── README.md
│ ├── array.cpp
│ ├── array_template.cpp
│ ├── bracket_overloading.cpp
│ ├── clock.cpp
│ ├── func_temp.cpp
│ ├── io_operator.cpp
│ ├── io_operator_overload.cpp
│ ├── map_insert_look.cpp
│ ├── operator_cast.cpp
│ ├── operator_circle.cpp
│ ├── output.cpp
│ ├── override.cpp
│ ├── read_file.cpp
│ ├── stack.cpp
│ └── try.cpp
├── proj/
│ └── README.md
├── src_analysis/
│ └── stl/
│ ├── array.md
│ ├── deque.md
│ ├── hashtable.md
│ ├── iterator.md
│ ├── list.md
│ ├── map_multimap.md
│ ├── myhashtable.md
│ ├── queue_stack.md
│ ├── rb_tree.md
│ ├── set_multiset.md
│ ├── traits.md
│ ├── typename.md
│ ├── unordered_map.md
│ ├── vector.md
│ └── 谈谈STL设计之EBO优化.md
└── tool/
├── C++的Debug工具dbg-macro.md
├── output/
│ ├── BUILD
│ ├── container.cpp
│ └── output_container.h
├── 像Python一样玩CC++.md
└── 用rr来进行debug.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
com.sh
bazel-CPlusPlusThings
bazel-bin
bazel-out
bazel-testlogs
bazel-cplusplus_bazel
.vscode
================================================
FILE: README.md
================================================
- [C++ 那些事](#c-那些事)
- [重磅项目](#重磅项目)
- [关于作者](#关于作者)
- [项目运行](#项目运行)
- [方式1: vscode + bazel](#方式1-vscode--bazel)
- [方式2: docker](#方式2-docker)
- [方式3: g++](#方式3-g)
- [视频学习本项目](#视频学习本项目)
- [飞书知识库](#飞书知识库)
- [基础进阶](#基础进阶)
- [实战系列](#实战系列)
- [10 日狂练](#10-日狂练)
- [重点实战练习](#重点实战练习)
- [C++2.0 新特性](#c20-新特性)
- [概况](#概况)
- [C++11 新特性](#c11-新特性)
- [C++14/17/20](#c141720)
- [设计模式](#设计模式)
- [STL 源码剖析](#stl-源码剖析)
- [并发编程](#并发编程)
- [C++ Concurrency in Action](#c-concurrency-in-action)
- [多线程与多进程](#多线程与多进程)
- [Threading In C++](#threading-in-c)
- [C++ 惯用法](#c-惯用法)
- [你最喜欢的 c++ 编程风格惯用法是什么?](#你最喜欢的-c-编程风格惯用法是什么)
- [学习课程](#学习课程)
- [极客时间《现代 C++ 实战 30 讲》](#极客时间现代-c-实战-30-讲)
- [工具篇](#工具篇)
- [容器快捷输出工具](#容器快捷输出工具)
- [像 Python 一样简单输出(Jupyter Notebook)](#像-python-一样简单输出jupyter-notebook)
- [观察编译过程变化](#观察编译过程变化)
- [C++ 的 Debug 工具 dbg-macro](#c-的-debug-工具-dbg-macro)
- [在 Linux 上的 Debug 工具 rr - 拥有回到过去的能力](#在-linux-上的-debug-工具-rr---拥有回到过去的能力)
- [拓展部分](#拓展部分)
- [一些问题](#一些问题)
- [贡献者](#贡献者)
# C++ 那些事
感谢各位对《C++ 那些事》的支持,现将内容也同步至B站,以视频的方式展示,可以打开网站直接阅读~欢迎大家 star、转发、PR。
在线个人博客:[光城的实验室](https://light-city.github.io/)
在线学习网站:[C++那些事](https://light-city.github.io/stories_things/)
- 中文名:**C++ 那些事**
- 英文名:**Stories About C Plus Plus**
这是一个适合初学者从**入门到进阶**的仓库,解决了**面试者与学习者**想要**深入 C++** 及如何**入坑 C++** 的问题。除此之外,本仓库拓展了更加深入的源码分析,多线程并发等的知识,是一个比较全面的 C++ 学习从入门到进阶提升的仓库。
### 重磅项目
推出一系列重磅项目,带大家实战学习C++,配合本开源项目学习,成长飞快!
直通点:[戳这里](./proj/README.md)
### 关于作者
公众号已开放两大入口,分别是专辑与菜单,可以直接在微信公众号上阅读《C++ 那些事》内容,配上该仓库的代码,将会非常的爽,建议大家关注一波。
个人公众号:guangcity
或者扫下面的二维码,欢迎大家反馈及 C++ 交流,本人在微信创建了 C++ 那些事交流群,优质的 C++ 资源交流区,期待大家关注上面公众号,点击公众号右下角->联系我,拉你入群。
---
>关注我
如果觉得不错,关注我吧~

星球
|

微信公众号
|
### 项目运行
#### 方式1: vscode + bazel
#### 方式2: docker
新推出免开发环境,docker环境,大家可以pull下面镜像:
```
docker pull xingfranics/cplusplusthings:latest
```
#### 方式3: g++
### 视频学习本项目
[第一期:Step By Step上手编译本项目](https://www.bilibili.com/video/BV1Rv4y1H7LB/?vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第二期:免开发环境docker](https://www.bilibili.com/video/BV1oz4y1a7Pu/?vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第三期:手把手共读HashTable,彻底搞懂C++ STL](https://www.bilibili.com/video/BV1o8411U7vy/?vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第四期:手把手共读STL之enable_shared_from_this](https://www.bilibili.com/video/BV1du4y1w7Mg/?spm_id_from=333.788&vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第五期:手把手共读STL之线程,从C++11 thread到C++20 jthread](https://www.bilibili.com/video/BV1DH4y1g7gS/?vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第六期:手把手共读STL之共读condition_variable、condition_variable_any](https://www.bilibili.com/video/BV13b421b7Am/?spm_id_from=333.999.0.0&vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第七期:手把手共读STL之Mutex](https://www.bilibili.com/video/BV1xm42157pq/?spm_id_from=333.999.0.0&vd_source=bb6532dcd5b1d6b26125da900adb618e)
[第八期:手把手共读STL之RAII Lock](https://www.bilibili.com/video/BV1Ls421g7iq/?spm_id_from=333.788&vd_source=bb6532dcd5b1d6b26125da900adb618e)
### 飞书知识库
[互联网大厂面试实录](https://hmpy6adnp5.feishu.cn/docx/OitBdRB4KozIhTxQt7Ec7iFDnkc)
[拿下offer之必备面经](https://hmpy6adnp5.feishu.cn/docx/B1aCdVTUgoyJGYxtWV7cdvgRnxv)
### 基础进阶
- [`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)
### 实战系列
#### [10 日狂练](./practical_exercises/10_day_practice)
- [day1-基本语法](practical_exercises/10_day_practice/day1)
- [day2-递归、结构体、枚举、静态变量等](practical_exercises/10_day_practice/day2)
- [day3-函数1](practical_exercises/10_day_practice/day3)
- [day4-函数2](practical_exercises/10_day_practice/day4)
- [day5-继承多态](practical_exercises/10_day_practice/day5)
- [day6-虚函数与抽象类](practical_exercises/10_day_practice/day6)
- [day7-运算符重载](practical_exercises/10_day_practice/day7)
- [day8-模版与STL](practical_exercises/10_day_practice/day8)
- [day9-异常](practical_exercises/10_day_practice/day9)
- [day10-文件与流](practical_exercises/10_day_practice/day10)
#### [重点实战练习](./practical_exercises/key_exercises/)
- [中括号重载.cpp](./practical_exercises/key_exercises/bracket_overloading.cpp)
- [时钟++运算符重载.cpp](./practical_exercises/key_exercises/clock.cpp)
- [运算符重载之强制转换.cpp](./practical_exercises/key_exercises/operator_cast.cpp)
- [重载圆括号的时钟.cpp](./practical_exercises/key_exercises/operator_circle.cpp)
- [函数模板.cpp](./practical_exercises/key_exercises/func_temp.cpp)
- [动态数组.cpp](./practical_exercises/key_exercises/array.cpp)
- [字典插入与查找.cpp](./practical_exercises/key_exercises/map_insert_look.cpp)
- [异常捕获.cpp](./practical_exercises/key_exercises/try.cpp)
- [类模板之栈.cpp](./practical_exercises/key_exercises/stack.cpp)
- [类模板特化之数组.cpp](./practical_exercises/key_exercises/array_template.cpp)
- [继承与封装.cpp](./practical_exercises/key_exercises/override.cpp)
- [读写文件综合题.cpp](./practical_exercises/key_exercises/read_file.cpp)
- [输入输出运算符重载.cpp](./practical_exercises/key_exercises/io_operator_overload.cpp)
- [输入输出重载.cpp](./practical_exercises/key_exercises/io_operator.cpp)
- [输出格式.cpp](./practical_exercises/key_exercises/output.cpp)
### [C++2.0 新特性](./cpp2.0)
#### 概况
C++2.0 是一个简称,意为「现代 C++」,包括 C++11/14/17/20。
#### [C++11 新特性](./cpp2.0/cpp11)
- [Variadic Templates](./cpp2.0/cpp11/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](./cpp2.0/cpp11/nullptr.cpp)
- [Automatic Type Deduction with auto](./cpp2.0/cpp11/auto.cpp)
- [Uniform Initialization ](./cpp2.0/cpp11/uniform_initialization.cpp)
- [initializer_list](./cpp2.0/cpp11/initializer.cpp)
- [explicit for ctors taking more than one argument](./cpp2.0/cpp11/explicit.cpp)
- [range-based for statement](./cpp2.0/cpp11/auto.cpp)
```cpp
for(decl:col) {
statement
}
```
- [=default,=delete](./cpp2.0/cpp11/default_delete.cpp)
如果你自行定义了一个 `ctor`, 那么编译器就不会给你一个 `default ctor`
如果强制加上 `=default`, 就可以重新获得并使用 `default ctor`.
- Alias(化名) Template (template typedef)
- [alias.cpp](./cpp2.0/cpp11/alias.cpp)
- [template_template.cpp](./cpp2.0/cpp11/template_template.cpp)
- [template template parameter](./cpp2.0/template_template.cpp)
- [type alias](./cpp2.0/cpp11/type_alias.cpp)
- [noexcept](./cpp2.0/cpp11/noexcept.cpp)
- [override](./cpp2.0/cpp11/override.cpp)
- [final](./cpp2.0/cpp11/final.cpp)
- [decltype](./cpp2.0/cpp11/decltype.cpp)
- [lambda](./cpp2.0/cpp11/lambda.cpp)
- [Rvalue reference](./cpp2.0/cpp11/rvalue.cpp)
- [move aware class](./cpp2.0/cpp11/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](./cpp2.0/cpp11/hash.cpp)
- [tuple](./cpp2.0/cpp11/tuple.cpp)
学习资料:https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061
#### C++14/17/20
待更新...
### 设计模式
- [单例模式](./design_pattern/singleton)
- [生产消费者模式](./design_pattern/producer_consumer)
### [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](./src_analysis/stl/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)
### [并发编程](./concurrency)
#### C++ Concurrency in Action
- [第一章](./concurrency/concurrency_v1/chapter1)
- [第二章](./concurrency/concurrency_v1/chapter2)
学习资料:https://downdemo.gitbook.io/cpp-concurrency-in-action-2ed/
#### 多线程与多进程
##### 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
### [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)
### 学习课程
#### [极客时间《现代 C++ 实战 30 讲》](https://time.geekbang.org/channel/home)
- [堆、栈、RAII:C++ 里该如何管理资源?](./learn_class/modern_cpp_30/RAII)
- [堆](./learn_class/modern_++_30/RAII/heap.cpp)
- [栈](./learn_class/modern_cpp_30/RAII/stack.cpp)
- [RAII](./learn_class/modern_cpp_30/RAII/RAII.cpp)
- [自己动手,实现 C++ 的智能指针](./learn_class/modern_cpp_30/smart_ptr)
- [auto_ptr、scope_ptr](./learn_class/modern_cpp_30/smart_ptr/auto_scope.cpp)
- [unique_ptr](./learn_class/modern_cpp_30/smart_ptr/unique_ptr.cpp)
- [shared_ptr](./learn_class/modern_cpp_30/smart_ptr/shared_ptr.cpp)
- [右值和移动究竟解决了什么问题?](./learn_class/modern_cpp_30/reference)
- [左值与右值](./learn_class/modern_cpp_30/reference/reference.cpp)
- [延长声明周期](./learn_class/modern_cpp_30/reference/lifetime.cpp)
- [引用折叠](./learn_class/modern_cpp_30/reference/collapses.cpp)
- [完美转发](./learn_class/modern_cpp_30/reference/forward.cpp)
- [不要返回本地变量的引用](./learn_class/modern_cpp_30/reference/don'treturnReference.cpp)
- [容器 1](./learn_class/modern_cpp_30/container1)
- [容器 2](./learn_class/modern_cpp_30/container2)
- [异常](./learn_class/modern_cpp_30/exception)
- [字面量、静态断言和成员函数说明符](./learn_class/modern_cpp_30/literalAssert)
- [是不是应该返回对象?](./learn_class/modern_cpp_30/returnObj)
- [编译期多态:泛型编程和模板入门](./learn_class/modern_cpp_30/compilerpoly)
- [译期能做些什么?一个完整的计算世界](./learn_class/modern_cpp_30/compilercompute)
- [SFINAE:不是错误的替换失败是怎么回事?](./learn_class/modern_cpp_30/SFINAE)
- [constexpr:一个常态的世界](./learn_class/modern_cpp_30/constexpr)
- [函数对象和 lambda:进入函数式编程](./learn_class/modern_cpp_30/functionLambda)
- [内存模型和 atomic:理解并发的复杂性](./learn_class/modern_cpp_30/memorymodel_atomic)
### 工具篇
#### [容器快捷输出工具](./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 }
```
#### 像 Python 一样简单输出(Jupyter Notebook)
- [像 Python 一样玩 C/C++](./tool/像Python一样玩CC++.md)
#### 观察编译过程变化
- [https://cppinsights.io](https://cppinsights.io/)
#### C++ 的 Debug 工具 dbg-macro
- [C++ 的 Debug 工具 dbg-macro](./tool/C++的Debug工具dbg-macro.md)
#### 在 Linux 上的 Debug 工具 rr - 拥有回到过去的能力
- [用 rr 来进行 debug](./tool/用rr来进行debug.md)
### 拓展部分
#### 一些问题
- [C++ 中如何将 string 类型转换为 int 类型?](./extension/some_problem/string_int.md)
### 贡献者
| 贡献人 | 地址 |
| ------- | ----------------------------- |
| 光城 | https://github.com/Light-City |
| ChungZH | https://github.com/ChungZH |
| xliu79 | https://github.com/xliu79 |
================================================
FILE: README_EN.md
================================================
# 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 welcomed.
**Notice: The website is not available now.**
-----------------------
### 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、Struct、Enumerate、Static Variable
- [x] day3
Function
- [x] day4
Thorough About Function
- [x] day5
Inheritance and Polymorphism
- [x] day6
Virtual 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 Detaches](./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_cpp_30/RAII)
- [heap](./modern_++_30/RAII/heap.cpp)
- [stack](./learn_class/modern_cpp_30/RAII/stack.cpp)
- [RAII](./learn_class/modern_cpp_30/RAII/RAII.cpp)
- [Implementing smart pointers for C ++](./learn_class/modern_cpp_30/smart_ptr)
- [auto_ptr、scope_ptr](./learn_class/modern_cpp_30/smart_ptr/auto_scope.cpp)
- [unique_ptr](./learn_class/modern_cpp_30/smart_ptr/unique_ptr.cpp)
- [shared_ptr](./learn_class/modern_cpp_30/smart_ptr/shared_ptr.cpp)
- [What exactly does r value and move solve?](./learn_class/modern_cpp_30/reference)
- [L value and R value](./learn_class/modern_cpp_30/reference/reference.cpp)
- [Extend the declaration cycle](./learn_class/modern_cpp_30/reference/lifetime.cpp)
- [Reference folding](./learn_class/modern_cpp_30/reference/collapses.cpp)
- [Perfect forward](./learn_class/modern_cpp_30/reference/forward.cpp)
- [Do not return Reference](./learn_class/modern_cpp_30/reference/don'treturnReference.cpp)
- [Container 1](./learn_class/modern_cpp_30/container1)
- [Container 2](./learn_class/modern_cpp_30/container2)
- [Exception](./learn_class/modern_cpp_30/exception)
- [Literal、Static Assertion And Member Function Specifier](./learn_class/modern_cpp_30/literalAssert)
- [Return Object?](./learn_class/modern_cpp_30/returnObj)c
- [Getting started with generic programming and templates](./learn_class/modern_cpp_30/compilerpoly)
- [A whole Compiler Compute World](./learn_class/modern_cpp_30/compilercompute)
- [SFINAE:What is it if it is not replace error?](./learn_class/modern_cpp_30/SFINAE)
- [constexpr:A Normal World](./learn_class/modern_cpp_30/constexpr)
- [Function object and Lambda:functionLambda](./learn_class/modern_cpp_30/functionLambda)
- [Memory Model and Atomic:Understanding the complexity of concurrency](./learn_class/modern_cpp_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
个人公众号:

================================================
FILE: WORKSPACE
================================================
================================================
FILE: basic_content/abstract/BUILD
================================================
# you can run some main program, just replace binary name
# such as: `bazel run basic_content/abstract:interesting_facts1`
# `bazel run basic_content/abstract:interesting_facts2`
# etc...
load("@rules_cc//cc:defs.bzl", "cc_binary")
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "interesting_facts1",
srcs = ["interesting_facts1.cpp"],
)
cc_binary(
name = "interesting_facts2",
srcs = ["interesting_facts2.cpp"],
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "interesting_facts3",
srcs = ["interesting_facts3.cpp"],
)
cc_binary(
name = "interesting_facts4",
srcs = ["interesting_facts4.cpp"],
)
cc_binary(
name = "interesting_facts5",
srcs = ["interesting_facts5.cpp"],
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "pure_virtual",
srcs = ["pure_virtual.cpp"],
)
cc_binary(
name = "derived_full",
srcs = ["derived_full.cpp"],
)
================================================
FILE: basic_content/abstract/README.md
================================================
# 纯虚函数和抽象类
## 关于作者:
个人公众号:

## 1.纯虚函数与抽象类
C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它! 通过声明中赋值0来声明纯虚函数!
```cpp
// 抽象类
Class A {
public:
virtual void show() = 0; // 纯虚函数
/* Other members */
};
```
* 纯虚函数:没有函数体的虚函数
* 抽象类:包含纯虚函数的类
抽象类只能作为基类来派生新类使用,不能创建抽象类的对象,抽象类的指针和引用->由抽象类派生出来的类的对象!
> 代码样例:[abstract_base.h](./abstract_base.h)、[pure_virtual.cpp](./pure_virtual.cpp)
## 2.实现抽象类
抽象类中:在成员函数内可以调用纯虚函数,在构造函数/析构函数内部不能使用纯虚函数。
如果一个类从抽象类派生而来,它必须实现了基类中的所有纯虚函数,才能成为非抽象类。
```cpp
// A为抽象类
class A {
public:
virtual void f() = 0; // 纯虚函数
void g(){ this->f(); }
A(){} // 构造函数
};
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; } // 普通成员函数
private:
int x;
};
```
- [抽象类类型的指针和引用](./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;
}
```
- [如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类](./interesting_facts3.cpp)
```cpp
// Derived为抽象类
class Derived: public Base
{
public:
// void show() {}
};
```
- [抽象类可以有构造函数](./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;
};
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)
================================================
FILE: basic_content/abstract/abstract.cpp
================================================
/**
* @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()" << endl; }
};
int main() {
B b;
b.g();
return 0;
}
================================================
FILE: basic_content/abstract/abstract_base.h
================================================
/**
* @file abstreact_base.cpp
* @brief
* C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它!通过声明中赋值0来声明纯虚函数!
* 纯虚函数:没有函数体的虚函数
* @author 光城
* @version v1
* @date 2019-07-20
*/
/**
* @brief 抽象类
*/
class AbstractBase {
// Data members of class
public:
// Pure Virtual Function
virtual void show() = 0;
/* Other members */
};
================================================
FILE: basic_content/abstract/derived_full.cpp
================================================
/**
* @file derived_full.cpp
* @brief 完整示例!抽象类由派生类继承实现!
* @author 光城
* @version v1
* @date 2019-07-20
*/
#include
using namespace std;
class Base {
int x;
public:
virtual void fun() = 0;
int getX() { return x; }
};
class Derived : public Base {
public:
void fun() { cout << "fun() called"; } // 实现了fun()函数
};
int main(void) {
Derived d;
d.fun();
return 0;
}
================================================
FILE: basic_content/abstract/interesting_facts1.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/abstract/interesting_facts2.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/abstract/interesting_facts3.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/abstract/interesting_facts4.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/abstract/interesting_facts5.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/abstract/pure_virtual.cpp
================================================
/**
* @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是抽象类,不能创建对象
}
================================================
FILE: basic_content/assert/BUILD
================================================
# you can run some main program, just replace binary name
# such as: `bazel run basic_content/assert:ignore_assert`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "ignore_assert",
srcs = ["ignore_assert.c"],
)
cc_binary(
name = "assert",
srcs = ["assert.c"],
)
================================================
FILE: basic_content/assert/README.md
================================================
# assert那些事
## 关于作者:
个人公众号:

## 1.第一个断言案例
断言,**是宏,而非函数**。
assert 宏的原型定义在 (C)、(C++)中。其作用是如果它的条件返回错误,则终止程序执行。
可以通过定义 `NDEBUG` 来关闭 assert,**但是需要在源代码的开头,include 之前。**
```c
void assert(int expression);
```
> 代码样例:[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;
}
```
输出:
```c
assert: assert.c:13: main: Assertion 'x==7' failed.
```
可以看到输出会把源码文件,行号错误位置,提示出来!
## 2.断言与正常错误处理
+ 断言主要用于检查逻辑上不可能的情况。
>例如,它们可用于检查代码在开始运行之前所期望的状态,或者在运行完成后检查状态。与正常的错误处理不同,断言通常在运行时被禁用。
+ 忽略断言,在代码开头加上:
```c++
#define NDEBUG // 加上这行,则 assert 不可用
```
> 样例代码:[ignore_assert.c](./ignore_assert.c)
================================================
FILE: basic_content/assert/assert.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;
}
================================================
FILE: basic_content/assert/ignore_assert.c
================================================
/**
* @file ignore_assert.c
* @brief 忽略断言
* @author 光城
* @version v1
* @date 2019-07-25
*/
# define NDEBUG // 忽略断言
#include
int main(){
assert(x==5);
return 0;
}
================================================
FILE: basic_content/bit/BUILD
================================================
# please run `bazel run basic_content/bit:bit`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "bit",
srcs = ["bit.cpp"],
)
================================================
FILE: basic_content/bit/README.md
================================================
## 关于作者:
个人公众号:

## Bit field 是什么?
“ 位域 “ 或 “ 位段 “(Bit field)为一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。这种数据结构的一个好处是它可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。第二个好处是位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。而这种数据结构的缺点在于,位段实现依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位段在本质上是不可移植的。
- 位域在内存中的布局是与机器有关的
- 位域的类型必须是整型或枚举类型,带符号类型中的位域的行为将因具体实现而定
- 取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域
## 位域使用
位域通常使用结构体声明, 该结构声明为每个位域成员设置名称,并决定其宽度:
```
struct bit_field_name
{
type member_name : width;
};
```
| Elements | Description |
| -------------- | ------------------------------------------------------------ |
| bit_field_name | 位域结构名 |
| type | 位域成员的类型,必须为 int、signed int 或者 unsigned int 类型 |
| member_name | 位域成员名 |
| width | 规定成员所占的位数 |
例如声明如下一个位域:
```
struct _PRCODE
{
unsigned int code1: 2;
unsigned int cdde2: 2;
unsigned int code3: 8;
};
struct _PRCODE prcode;
```
该定义使 `prcode`包含 2 个 2 Bits 位域和 1 个 8 Bits 位域,我们可以使用结构体的成员运算符对其进行赋值
```
prcode.code1 = 0;
prcode.code2 = 3;
procde.code3 = 102;
```
赋值时要注意值的大小不能超过位域成员的容量,例如 prcode.code3 为 8 Bits 的位域成员,其容量为 2^8 = 256,即赋值范围应为 [0,255]。
## 位域的大小和对齐
### 位域的大小
例如以下位域:
```
struct box
{
unsigned int a: 1;
unsigned int : 3;
unsigned int b: 4;
};
```
该位域结构体中间有一个未命名的位域,占据 3 Bits,仅起填充作用,并无实际意义。 填充使得该结构总共使用了 8 Bits。但 C 语言使用 unsigned int 作为位域的基本单位,即使一个结构的唯一成员为 1 Bit 的位域,该结构大小也和一个 unsigned int 大小相同。 有些系统中,unsigned int 为 16 Bits,在 x86 系统中为 32 Bits。文章以下均默认 unsigned int 为 32 Bits。
### 位域的对齐
一个位域成员不允许跨越两个 unsigned int 的边界,如果成员声明的总位数超过了一个 unsigned int 的大小, 那么编辑器会自动移位位域成员,使其按照 unsigned int 的边界对齐。
例如:
```
struct stuff
{
unsigned int field1: 30;
unsigned int field2: 4;
unsigned int field3: 3;
};
```
`field1` + `field2` = 34 Bits,超出 32 Bits, 编译器会将`field2`移位至下一个 unsigned int 单元存放, stuff.field1 和 stuff.field2 之间会留下一个 2 Bits 的空隙, stuff.field3 紧跟在 stuff.field2 之后,该结构现在大小为 2 * 32 = 64 Bits。
这个空洞可以用之前提到的未命名的位域成员填充,我们也可以使用一个宽度为 0 的未命名位域成员令下一位域成员与下一个整数对齐。
例如:
```
struct stuff
{
unsigned int field1: 30;
unsigned int : 2;
unsigned int field2: 4;
unsigned int : 0;
unsigned int field3: 3;
};
```
这里 stuff.field1 与 stuff.field2 之间有一个 2 Bits 的空隙,stuff.field3 则存储在下一个 unsigned int 中,该结构现在大小为 3 * 32 = 96 Bits。
学习代码见:[bit.cpp](bit.cpp)
## 位域的初始化和位的重映射
### 初始化
位域的初始化与普通结构体初始化的方法相同,这里列举两种,如下:
```
struct stuff s1= {20,8,6};
```
或者直接为位域成员赋值
```
struct stuff s1;
s1.field1 = 20;
s1.field2 = 8;
s1.field3 = 4;
```
### 位域的重映射 (Re-mapping)
声明一个 大小为 32 Bits 的位域
```
struct box {
unsigned int ready: 2;
unsigned int error: 2;
unsigned int command: 4;
unsigned int sector_no: 24;
}b1;
```
#### 利用重映射将位域归零
```
int* p = (int *) &b1; // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址"
*p = 0; // 清除 s1,将各成员归零
```
#### 利用联合 (union) 将 32 Bits 位域 重映射至 unsigned int 型
先简单介绍一下联合
> “联合” 是一种特殊的类,也是一种构造类型的数据结构。在一个 “联合” 内可以定义多种不同的数据类型, 一个被说明为该 “联合” 类型的变量中,允许装入该 “联合” 所定义的任何一种数据,这些数据共享同一段内存,以达到节省空间的目的
>
> “联合” 与 “结构” 有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在 “联合” 中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。
我们可以声明以下联合:
```
union u_box {
struct box st_box;
unsigned int ui_box;
};
```
x86 系统中 unsigned int 和 box 都为 32 Bits, 通过该联合使 st_box 和 ui_box 共享一块内存。具体位域中哪一位与 unsigned int 哪一位相对应,取决于编译器和硬件。
利用联合将位域归零,代码如下:
```
union u_box u;
u.ui_box = 0;
```
> 学习参考自:
================================================
FILE: basic_content/bit/bit.cpp
================================================
#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 << s.field1 << endl;
cout << s.field2 << endl;
cout << s.field3 << endl;
cout << sizeof(s) << endl;
return 0;
}
================================================
FILE: basic_content/c_poly/BUILD
================================================
# please run `bazel run basic_content/c_poly:c_examp`
# please run `bazel run basic_content/c_poly:c++_examp`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "c_examp",
srcs = ["c_examp.c"],
)
cc_binary(
name = "c++_examp",
srcs = ["c++_examp.cpp"],
)
================================================
FILE: basic_content/c_poly/README.md
================================================
# C实现C++的面向对象特性
## 关于作者:
个人公众号:

## 1.C++实现案例
C++中的多态:在C++中会维护一张虚函数表,根据赋值兼容规则,我们知道父类的指针或者引用是可以指向子类对象的。
如果一个父类的指针或者引用调用父类的虚函数则该父类的指针会在自己的虚函数表中查找自己的函数地址,如果该父类对象的指针或者引用指向的是子类的对象,而且该子类已经重写了父类的虚函数,则该指针会调用子类的已经重写的虚函数。
学习案例代码见:[c++_examp.cpp](./c++_examp.cpp)
## 2.C实现
- 封装
C语言中是没有class类这个概念的,但是有struct结构体,我们可以考虑使用struct来模拟;
使用函数指针把属性与方法封装到结构体中。
- 继承
结构体嵌套
- 多态
类与子类方法的函数指针不同
在C语言的结构体内部是没有成员函数的,如果实现这个父结构体和子结构体共有的函数呢?我们可以考虑使用函数指针来模拟。但是这样处理存在一个缺陷就是:父子各自的函数指针之间指向的不是类似C++中维护的虚函数表而是一块物理内存,如果模拟的函数过多的话就会不容易维护了。
模拟多态,必须保持函数指针变量对齐(在内容上完全一致,而且变量对齐上也完全一致)。否则父类指针指向子类对象,运行崩溃!
学习案例代码见:[c_examp.c](./c_examp.c)
================================================
FILE: basic_content/c_poly/c++_examp.cpp
================================================
/**
* @file c++_examp.cpp
* @brief c++中的多态
* @author 光城
* @version v1
* @date 2019-08-06
*/
#include
using namespace std;
class A {
public:
virtual void f() //虚函数实现
{
cout << "Base A::f() " << endl;
}
};
class B : public A // 必须为共有继承,否则后面调用不到,class默认为私有继承!
{
public:
virtual void f() //虚函数实现,子类中virtual关键字可以没有
{
cout << "Derived B::f() " << endl;
}
};
int main() {
A a; //基类对象
B b; //派生类对象
A *pa = &a; //父类指针指向父类对象
pa->f(); //调用父类的函数
pa = &b; //父类指针指向子类对象,多态实现
pa->f(); //调用派生类同名函数
return 0;
}
================================================
FILE: basic_content/c_poly/c_examp.c
================================================
/**
* @file c_examp.c
* @brief C实现多态
* @author 光城
* @version v1
* @date 2019-08-06
*/
#include
/// 重定义一个函数指针类型
typedef void (*pf) ();
/**
* @brief 父类
*/
typedef struct _A
{
pf _f;
}A;
/**
* @brief 子类
*/
typedef struct _B
{
A _b; ///< 在子类中定义一个基类的对象即可实现对父类的继承。
}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; /// 让父类指针指向子类的对象,由于类型不匹配所以要进行强转
pa->_f();
return 0;
}
================================================
FILE: basic_content/const/BUILD
================================================
# please run `bazel run basic_content/const:const_function`
# please run `bazel run basic_content/const:const_num`
load("@rules_cc//cc:defs.bzl", "cc_binary")
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "const_function",
srcs = ["const_function.cpp"],
copts = ["-std=c++11"]
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "const_num",
srcs = ["const_num.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/README.md
================================================
## 关于作者
微信公众号:

## 1.const含义
常类型是指使用类型修饰符**const**说明的类型,常类型的变量或对象的值是不能被更新的。
## 2.const作用
+ 可以定义常量
```cpp
const int a=100;
```
+ 类型检查
+ const常量与`#define`宏定义常量的区别:
> const常量具有类型,编译器可以进行安全检查;#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查。感谢两位大佬指出这里问题,见:[issue](https://github.com/Light-City/CPlusPlusThings/issues/5)
+ const常量支持所有类型。
+ 其他情况下它只是一个 `const` 限定的变量,不要将与常量混淆。
+ 防止修改,起保护作用,增加程序健壮性
```cpp
void f(const int i){
i++; // error!
}
```
+ 可以节省空间,避免不必要的内存分配
+ const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像`#define`一样给出的是立即数。
+ const定义的常量在程序运行过程中只有一份拷贝,而`#define`定义的常量在内存中有若干个拷贝。
## 3.const对象默认为文件局部变量
注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。
> 未被const修饰的变量在不同文件的访问
```cpp
// file1.cpp
int ext;
// file2.cpp
#include
extern int ext;
int main(){
std::cout<<(ext+10)< const常量在不同文件的访问
```cpp
// extern_file1.cpp
extern const int ext=12;
// extern_file2.cpp
#include
extern const int ext;
int main(){
std::cout< 小结:
可以发现未被const修饰的变量不需要extern显式声明!而const常量需要显式声明extern,并且需要做初始化!因为常量在定义后就不能被修改,所以定义时必须初始化。
## 4.定义常量
```c++
const int b = 10;
b = 0; // error: assignment of read-only variable ‘b’
const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’
```
上述有两个错误:
+ b 为常量,不可更改!
+ i 为常量,必须进行初始化!(因为常量在定义后就不能被修改,所以定义时必须初始化。)
## 5.指针与const
与指针相关的const有四种:
```c++
const char * a; // 指向const对象的指针或者说指向常量的指针。
char const * a; // 同上
char * const a; // 指向类型对象的const指针。或者说常指针、const指针。
const char * const a; // 指向const对象的const指针。
```
> **小结:**
> 如果*const*位于`*`的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于`*`的右侧,*const*就是修饰指针本身,即指针本身是常量。
**另一种解读方式**
利用英文从右边往左边读,并且以to为分界,to之前为描述指针的特性,to之后为描述目标的特性
```c++
const char * p; //p is a pointer to const char
char const * p; //同上
char * const p; //p is a const pointer to char
const char * const p; //p is a const pointer to const char
```
当指针被加上const特性,则指针不可改变指向的地址
当指向的目标特性为char,则内容可以透过指针被修改,如: *char='y';
当指向的目标特性为const char,则内容不可透过指针修改
具体使用如下:
(1) **指向常量的指针**
```cpp
const int *ptr;
*ptr = 10; // error
```
ptr是一个指向int类型const对象的指针,const定义的是int类型,也就是ptr所指向的对象类型,而不是ptr本身,所以ptr可以不用赋初始值。但是不能通过ptr去修改所指对象的值。
除此之外,也不能使用void`*`指针保存const对象的地址,必须使用const void`*`类型的指针保存const对象的地址。
```c++
const int p = 10;
const void * vp = &p;
void *vp = &p; // error
```
另外一个重点是:**允许把非const对象的地址赋给指向const对象的指针**。
将非const对象的地址赋给const对象的指针:
```c++
const int *ptr;
int val = 3;
ptr = &val; // ok
```
我们不能通过ptr指针来修改val的值,即使它指向的是非const对象!
我们不能使用指向const对象的指针修改基础对象,然而如果该指针指向了非const对象,可用其他方式修改其所指的对象。可以修改const指针所指向的值的,但是不能通过const对象指针来进行而已!如下修改:
```c++
int *ptr1 = &val;
*ptr1=4;
cout<<*ptr< 小结:
1.对于指向常量的指针,不能通过指针来修改对象的值。
2.不能使用void`*`指针保存const对象的地址,必须使用const void`*`类型的指针保存const对象的地址。
3.允许把非const对象的地址赋值给const对象的指针,如果要修改指针所指向的对象值,必须通过其他方式修改,不能直接通过当前指针直接修改。
(2) **常指针**
const指针必须进行初始化,且const指针指向的值能修改,但指向不能修改。
```cpp
#include
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
ptr = &num1; // error! const指针不能修改指向!
cout<<*ptr<
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
*ptr = 1;
cout<<*ptr< int `*`错误!
```cpp
#include
using namespace std;
int main(){
const int num=0;
int * const ptr=# // error! const int* -> int*
cout<<*ptr< const修饰函数返回值
这个跟const修饰普通变量以及指针的含义基本相同:
(1)**const int**
```cpp
const int func1();
```
这个本身无意义,因为参数返回本身就是赋值给其他的变量!
(2)**const int***
```cpp
const int* func2();
```
指针指向的内容不变。
(3)int *const
```cpp
int *const func2();
```
指针本身不可变。
> const修饰函数参数
(1)传递过来的参数及指针本身在函数内不可变,无意义!
```cpp
void func(const int var); // 传递过来的参数不可变
void func(int *const var); // 指针本身不可变
```
表明参数在函数体内不能被修改,但此处没有任何意义,var本身就是形参,在函数内不会改变。包括传入的形参是指针也是一样。
输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。
(2)**参数指针所指内容为常量不可变**
```cpp
void StringCopy(char *dst, const char *src);
```
其中src 是输入参数,dst 是输出参数。给src加上const修饰后,如果函数体内的语句试图改动src的内容,编译器将指出错误。这就是加了const的作用之一。
(3)**参数为引用,为了增加效率同时防止修改。**
```cpp
void func(const A &a)
```
对于非内部数据类型的参数而言,像void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。
为了提高效率,可以将函数声明改为void func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临
时对象。
> 但是函数void func(A &a) 存在一个缺点:
“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为
void func(const A &a)。
以此类推,是否应将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)。
以上解决了两个面试问题:
+ 如果函数需要传入一个指针,是否需要为该指针加上const,把const加在指针不同的位置有什么区别;
+ 如果写的函数需要传入的参数是一个复杂类型的实例,传入值参数或者引用参数有什么区别,什么时候需要为传入的引用参数加上const。
## 7.类中使用const
在一个类中,任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改 数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字进行说明的成员函数不能用来操作常对象。
对于类中的const成员变量必须通过初始化列表进行初始化,如下所示:
```cpp
class Apple{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
};
Apple::Apple(int i):apple_number(i)
{
}
```
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
例如:
```cpp
// apple.cpp
class Apple
{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
void take(int num) const;
int add();
int add(int num) const;
int getCount() const;
};
// apple.cpp
Apple::Apple(int i) : apple_number(i)
{
}
int Apple::add(int num)
{
take(num);
return 0;
}
int Apple::add(int num) const
{
take(num);
return 0;
}
void Apple::take(int num) const
{
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const
{
take(1);
// add(); // error
return apple_number;
}
int main()
{
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
// main.cpp
```
> 编译:bazel run basic_content/const/class_const/first_example:main
此时报错,上面getCount()方法中调用了一个add方法,而add方法并非const修饰,所以运行报错。也就是说const成员函数只能访问const成员函数。
当调用改为:
```
const Apple b(3);
b.add(); // error
```
此时,可以证明的是const对象只能访问const成员函数。
我们除了上述的初始化const常量用初始化列表方式外,也可以通过下面方法:
第一:将常量定义与static结合,也就是:
```cpp
static const int apple_number
```
第二:在外面初始化:
```cpp
const int Apple::apple_number=10;
```
当然,如果你使用c++11进行编译,直接可以在定义出初始化,可以直接写成:
```cpp
static const int apple_number=10;
// 或者
const int apple_number=10;
```
这两种都在c++11中支持!
编译的时候加上`-std=c++11`即可!
这里提到了static,下面简单的说一下:
在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化。
在类中声明:
```cpp
static int ap;
```
在类实现文件中使用:
```cpp
int Apple::ap=666
```
对于此项,c++11不能进行声明并初始化,也就是上述使用方法。
================================================
FILE: basic_content/const/class_const/c++11_example/BUILD
================================================
# please run `bazel run basic_content/const/class_const/c++11_example:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "apple",
srcs = ["apple.cpp"],
hdrs = ["apple.h"],
)
cc_binary(
name = "main",
srcs = ["main.cpp"],
deps = [
":apple",
],
)
================================================
FILE: basic_content/const/class_const/c++11_example/apple.cpp
================================================
#include "apple.h"
#include
using namespace std;
Apple::Apple(int i) {}
int Apple::add(int num) {
take(num);
return 0;
}
int Apple::add(int num) const {
take(num);
return 0;
}
void Apple::take(int num) const { cout << "take func " << num << endl; }
int Apple::getCount() const {
take(1);
// add(); //error
return apple_number;
}
================================================
FILE: basic_content/const/class_const/c++11_example/apple.h
================================================
#pragma once
class Apple {
public:
Apple(int i);
// 使用c++11标准编译
static const int apple_number = 10;
// const int apple_number=10;
void take(int num) const;
int add(int num);
int add(int num) const;
int getCount() const;
};
================================================
FILE: basic_content/const/class_const/c++11_example/main.cpp
================================================
#include "apple.h"
#include
using namespace std;
int main() {
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
================================================
FILE: basic_content/const/class_const/first_example/BUILD
================================================
# please run `bazel run basic_content/const/class_const/first_example:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "apple",
srcs = ["apple.cpp"],
hdrs = ["apple.h"],
)
cc_binary(
name = "main",
srcs = ["main.cpp"],
deps = [
":apple",
],
)
================================================
FILE: basic_content/const/class_const/first_example/apple.cpp
================================================
#include "apple.h"
#include
Apple::Apple(int i) : apple_number(i) {}
int Apple::add(int num) {
take(num);
return 0;
}
int Apple::add(int num) const {
take(num);
return 0;
}
void Apple::take(int num) const {
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const {
take(1);
// add(); //error
return apple_number;
}
================================================
FILE: basic_content/const/class_const/first_example/apple.h
================================================
#pragma once
class Apple {
public:
Apple(int i);
const int apple_number;
void take(int num) const;
int add(int num);
int add(int num) const;
int getCount() const;
};
================================================
FILE: basic_content/const/class_const/first_example/main.cpp
================================================
#include "apple.h"
#include
using namespace std;
int main() {
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
================================================
FILE: basic_content/const/class_const/overload_example/BUILD
================================================
# please run `bazel run basic_content/const/class_const/overload_example:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "apple",
srcs = ["apple.cpp"],
hdrs = ["apple.h"],
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "main",
srcs = ["main.cpp"],
deps = [
":apple",
],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/class_const/overload_example/apple.cpp
================================================
#include "apple.h"
#include
int Apple::apple_number = 10;
Apple::Apple(int i) { apple_number = i; }
int Apple::add() {
take(1);
return 0;
}
int Apple::add(int num) const {
take(num);
return num;
}
void Apple::take(int num) const {
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const {
take(1);
add(); // error
return apple_number;
}
================================================
FILE: basic_content/const/class_const/overload_example/apple.h
================================================
#pragma once
class Apple {
public:
Apple(int i);
static int apple_number;
void take(int num) const;
int add();
int add(int num) const;
int getCount() const;
};
================================================
FILE: basic_content/const/class_const/overload_example/main.cpp
================================================
#include "apple.h"
#include
using namespace std;
int main() {
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
// const Apple b(3);
// b.add(); // error
return 0;
}
================================================
FILE: basic_content/const/class_const/static_example/BUILD
================================================
# please run `bazel run basic_content/const/class_const/static_example:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "apple",
srcs = ["apple.cpp"],
hdrs = ["apple.h"],
)
cc_binary(
name = "main",
srcs = ["main.cpp"],
deps = [
":apple",
],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/class_const/static_example/apple.cpp
================================================
#include "apple.h"
#include
const int Apple::apple_number = 10;
int Apple::ap = 666;
Apple::Apple(int i) {}
int Apple::add(int num) {
take(num);
return 0;
}
int Apple::add(int num) const {
take(num);
return 0;
}
void Apple::take(int num) const {
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const {
take(1);
// add(); //error
return apple_number;
}
================================================
FILE: basic_content/const/class_const/static_example/apple.h
================================================
#pragma once
class Apple {
public:
Apple(int i);
static int ap; // 在类实现文件中定义并初始化
static const int apple_number;
void take(int num) const;
int add(int num);
int add(int num) const;
int getCount() const;
};
================================================
FILE: basic_content/const/class_const/static_example/main.cpp
================================================
#include "apple.h"
#include
int main() {
Apple a(2);
std::cout << a.getCount() << std::endl;
std::cout << a.ap << std::endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
================================================
FILE: basic_content/const/const_function.cpp
================================================
#include
using namespace std;
void f(const int i) {
i = 10; // error: assignment of read-only parameter ‘i’
cout << i << endl;
}
int main() { f(1); }
================================================
FILE: basic_content/const/const_num.cpp
================================================
#include
using namespace std;
int main() {
const int b = 10;
b = 0; // error
const string s = "helloworld";
const int i, j = 0;
}
================================================
FILE: basic_content/const/extern_const/BUILD
================================================
# please run `bazel run basic_content/const/extern_const:const_file1` and `# please run `bazel run basic_content/const/extern_const:file`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "const_file1",
srcs = ["const_file1.cpp", "const_file2.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "file",
srcs = ["file1.cpp", "file2.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/extern_const/const_file1.cpp
================================================
extern const int ext = 12;
================================================
FILE: basic_content/const/extern_const/const_file2.cpp
================================================
#include
/**
* by 光城
* compile: g++ -o file const_file2.cpp const_file1.cpp
* execute: ./file
*/
extern const int ext;
int main() { std::cout << ext << std::endl; }
================================================
FILE: basic_content/const/extern_const/file1.cpp
================================================
int ext;
================================================
FILE: basic_content/const/extern_const/file2.cpp
================================================
#include
/**
* by 光城
* compile: g++ -o file file2.cpp file1.cpp
* execute: ./file
*/
extern int ext;
int main() { std::cout << (ext + 10) << std::endl; }
================================================
FILE: basic_content/const/funciton_const/condition1/BUILD
================================================
# please run `bazel run basic_content/const/funciton_const/condition2:condition1`
# please run `bazel run basic_content/const/funciton_const/condition2:condition2`
# please run `bazel run basic_content/const/funciton_const/condition2:condition3`
load("@rules_cc//cc:defs.bzl", "cc_binary")
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "condition1",
srcs = ["condition1.cpp"],
copts = ["-std=c++11"]
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "condition2",
srcs = ["condition2.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "condition3",
srcs = ["condition3.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/funciton_const/condition1/condition1.cpp
================================================
#include
using namespace std;
int main() {
const int *ptr;
*ptr = 10; // error
}
================================================
FILE: basic_content/const/funciton_const/condition1/condition2.cpp
================================================
#include
using namespace std;
int main() {
const int p = 10;
// const void *vp = &p; // const pointer to const data
void *vp = (void*)&p; // ✅ C-style cast
}
================================================
FILE: basic_content/const/funciton_const/condition1/condition3.cpp
================================================
#include
using namespace std;
int main() {
const int *ptr;
int val = 3;
ptr = &val; // ok
int *ptr1 = &val;
*ptr1 = 4;
cout << *ptr << endl;
}
================================================
FILE: basic_content/const/funciton_const/condition2/BUILD
================================================
# please run `bazel run basic_content/const/funciton_const/condition2:condition1`
# please run `bazel run basic_content/const/funciton_const/condition2:condition2`
# please run `bazel run basic_content/const/funciton_const/condition2:condition3`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "condition1",
srcs = ["condition1.cpp"],
copts = ["-std=c++11"]
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "condition2",
srcs = ["condition2.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "condition3",
srcs = ["condition3.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/funciton_const/condition2/condition1.cpp
================================================
#include
using namespace std;
int main() {
int num = 0;
int *const ptr = # // const指针必须初始化!且const指针的指向不能修改
int *t = #
*t = 1;
cout << *ptr << endl;
}
================================================
FILE: basic_content/const/funciton_const/condition2/condition2.cpp
================================================
#include
using namespace std;
int main() {
const int num = 0;
int *const ptr = # // error! const int* -> int*
cout << *ptr << endl;
}
================================================
FILE: basic_content/const/funciton_const/condition2/condition3.cpp
================================================
#include
using namespace std;
int main() {
const int num = 10;
const int *const ptr = #
cout << *ptr << endl;
}
================================================
FILE: basic_content/const/funciton_const/condition3/BUILD
================================================
# please run `bazel run basic_content/const/funciton_const/condition3:condition1`
load("@rules_cc//cc:defs.bzl", "cc_binary")
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "condition1",
srcs = ["condition1.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/const/funciton_const/condition3/condition1.cpp
================================================
#include
using namespace std;
int main() {
const int p = 3;
const int *const ptr = &p;
cout << *ptr << endl;
}
================================================
FILE: basic_content/decltype/BUILD
================================================
# please run `bazel run basic_content/decltype:decltype`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "decltype",
srcs = ["decltype.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/decltype/README.md
================================================
# decltype那些事
## 关于作者:
个人公众号:

## 1.基本使用
decltype的语法是:
```
decltype (expression)
```
这里的括号是必不可少的,decltype的作用是“查询表达式的类型”,因此,上面语句的效果是,返回 expression 表达式的类型。注意,decltype 仅仅“查询”表达式的类型,并不会对表达式进行“求值”。
### 1.1 推导出表达式类型
```
int i = 4;
decltype(i) a; //推导结果为int。a的类型为int。
```
### 1.2 与using/typedef合用,用于定义类型。
```c++
using size_t = decltype(sizeof(0));//sizeof(a)的返回值为size_t类型
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);
vectorvec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++)
{
//...
}
```
这样和auto一样,也提高了代码的可读性。
### 1.3 重用匿名类型
在C++中,我们有时候会遇上一些匿名类型,如:
```c++
struct
{
int d ;
double b;
}anon_s;
```
而借助decltype,我们可以重新使用这个匿名的结构体:
```c++
decltype(anon_s) as ;//定义了一个上面匿名的结构体
```
### 1.4 泛型编程中结合auto,用于追踪函数的返回值类型
这也是decltype最大的用途了。
```c++
template
auto multiply(T x, T y)->decltype(x*y)
{
return x*y;
}
```
完整代码见:[decltype.cpp](decltype.cpp)
## 2.判别规则
对于decltype(e)而言,其判别结果受以下条件的影响:
如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误。
否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&。
否则,假设e的类型是T,则decltype(e)为T。
标记符指的是除去关键字、字面量等编译器需要使用的标记之外的程序员自己定义的标记,而单个标记符对应的表达式即为标记符表达式。例如:
```c++
int arr[4]
```
则arr为一个标记符表达式,而arr[3]+0不是。
举例如下:
```c++
int i = 4;
int arr[5] = { 0 };
int *ptr = arr;
struct S{ double d; }s ;
void Overloaded(int);
void Overloaded(char);//重载的函数
int && RvalRef();
const bool Func(int);
//规则一:推导为其类型
decltype (arr) var1; //int[] 标记符表达式
decltype (ptr) var2;//int * 标记符表达式
decltype(s.d) var3;//doubel 成员访问表达式
//decltype(Overloaded) var4;//重载函数。编译错误。
//规则二:将亡值。推导为类型的右值引用。
decltype (RvalRef()) var5 = 1;
//规则三:左值,推导为类型的引用。
decltype ((i))var6 = i; //int&
decltype (true ? i : i) var7 = i; //int& 条件表达式返回左值。
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左值。
//规则四:以上都不是,则推导为本类型
decltype(1) var12;//const int
decltype(Func(1)) var13=true;//const bool
decltype(i++) var14 = i;//int i++返回右值
```
学习参考:https://www.cnblogs.com/QG-whz/p/4952980.html
================================================
FILE: basic_content/decltype/decltype.cpp
================================================
#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 << endl;
using nullptr_t = decltype(nullptr);
nullptr_t nu;
int *p = NULL;
if (p == nu)
cout << "NULL" << endl;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin(); i != vec.end(); i++)
cout << *i << " ";
cout << endl;
/**
* 匿名结构体
*/
struct {
int d;
double b;
} anon_s;
decltype(anon_s) as{1, 2.0}; // 定义了一个上面匿名的结构体
cout << multiply(11, 2) << ":" << as.b << endl;
return 0;
}
================================================
FILE: basic_content/enum/BUILD
================================================
# please run `bazel run basic_content/enum:classic_practice`
# please run `bazel run basic_content/enum:tradition_color`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "classic_practice",
srcs = ["classic_practice.cpp"],
copts = ["-std=c++11"]
)
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "tradition_color",
srcs = ["tradition_color.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/enum/README.md
================================================
# 从初级到高级的enum那些事
## 关于作者:
个人公众号:

## 传统行为
枚举有如下问题:
- 作用域不受限,会容易引起命名冲突。例如下面无法编译通过的:
```c++
enum Color
{
RED,
BLUE
};
enum Feeling
{
EXCITED,
BLUE
};
int main()
{
Color a = BLUE; // error
Feeling b = EXCITED;
std::cout << a << ":" << b << std::endl;
return 0;
}
```
- 会隐式转换为int
- 用来表征枚举变量的实际类型不能明确指定,从而无法支持枚举类型的前向声明。
具体实现见:[tradition_color.cpp](tradition_color.cpp)
## 经典做法
解决作用域不受限带来的命名冲突问题的一个简单方法是,给枚举变量命名时加前缀,如上面例子改成 COLOR_BLUE 以及 FEELING_BLUE。
一般说来,为了一致性我们会把所有常量统一加上前缀。但是这样定义枚举变量的代码就显得累赘。C 程序中可能不得不这样做。不过 C++ 程序员恐怕都不喜欢这种方法。替代方案是命名空间:
```c++
namespace Color
{
enum Type
{
RED=15,
YELLOW,
BLUE
};
};
```
这样之后就可以用 `Color::Type c = Color::RED;` 来定义新的枚举变量了。如果 `using namespace Color` 后,前缀还可以省去,使得代码简化。不过,因为命名空间是可以随后被扩充内容的,所以它提供的作用域封闭性不高。在大项目中,还是有可能不同人给不同的东西起同样的枚举类型名。
更“有效”的办法是用一个类或结构体来限定其作用域,例如:定义新变量的方法和上面命名空间的相同。不过这样就不用担心类在别处被修改内容。这里用结构体而非类,是因为本身希望这些常量可以公开访问。
```c++
struct Color1
{
enum Type
{
RED=102,
YELLOW,
BLUE
};
};
```
具体实现见:[classic_practice.cpp](classic_practice.cpp)
## C++11 的枚举类
上面的做法解决了第一个问题,但对于后两个仍无能为力。庆幸的是,C++11 标准中引入了“枚举类”(enum class),可以较好地解决上述问题。
- 新的enum的作用域不在是全局的
- 不能隐式转换成其他类型
```c++
/**
* @brief C++11的枚举类
* 下面等价于enum class Color2:int
*/
enum class Color2
{
RED=2,
YELLOW,
BLUE
};
Color2 c2 = Color2::RED;
cout << static_cast(c2) << endl; //必须转!
```
- 可以指定用特定的类型来存储enum
```c++
enum class Color3:char; // 前向声明
// 定义
enum class Color3:char
{
RED='r',
BLUE
};
char c3 = static_cast(Color3::RED);
```
具体实现见:[classic_practice.cpp](classic_practice.cpp)
## 类中的枚举类型
有时我们希望某些常量只在类中有效。 由于#define 定义的宏常量是全局的,不能达到目的,于是想到使用const 修饰数据成员来实现。而const 数据成员的确是存在的,但其含义却不是我们所期望的。
const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其 const 数据成员的值可以不同。
不能在类声明中初始化 const 数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道 SIZE 的值是什么。(c++11标准前)
```c++
class A
{
const int SIZE = 100; // 错误,企图在类声明中初始化 const 数据成员
int array[SIZE]; // 错误,未知的 SIZE
};
```
正确应该在类的构造函数的初始化列表中进行:
```c++
class A
{
A(int size); // 构造函数
const int SIZE ;
};
A::A(int size) : SIZE(size) // 构造函数的定义
{
}
A a(100); // 对象 a 的 SIZE 值为 100
A b(200); // 对象 b 的 SIZE 值为 200
```
怎样才能建立在整个类中都恒定的常量呢?
别指望 const 数据成员了,应该用类中的枚举常量来实现。例如:
```c++
class Person{
public:
typedef enum {
BOY = 0,
GIRL
}SexType;
};
//访问的时候通过,Person::BOY或者Person::GIRL来进行访问。
```
枚举常量不会占用对象的存储空间,它们在编译时被全部求值。
枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点。
================================================
FILE: basic_content/enum/classic_practice.cpp
================================================
#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 << c << endl;
/**
* 上述的另一种方法:
* using namespace Color; // 定义新的枚举变量
* Type c = RED;
*/
Color1 c1;
cout << c1.RED << endl;
Color1::Type c11 = Color1::BLUE;
cout << c11 << endl;
Color2 c2 = Color2::RED;
cout << static_cast(c2) << endl;
char c3 = static_cast(Color3::RED);
cout << c3 << endl;
return 0;
}
================================================
FILE: basic_content/enum/tradition_color.cpp
================================================
#include
using namespace std;
enum Color { RED, BLUE };
enum Feeling { EXCITED, BLUE };
int main() {
Color a = BLUE; // error
Feeling b = EXCITED;
std::cout << a << ":" << b << std::endl;
return 0;
}
================================================
FILE: basic_content/explicit/BUILD
================================================
# please run `bazel run basic_content/explicit:explicit`
load("@rules_cc//cc:defs.bzl", "cc_binary")
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "explicit",
srcs = ["explicit.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/explicit/README.md
================================================
# explicit(显式)关键字那些事
## 关于作者:
个人公众号:

- explicit 修饰构造函数时,可以防止隐式转换和复制初始化
- explicit 修饰转换函数时,可以防止隐式转换,但按语境转换除外
代码参见:[.explicit.cpp](./explicit.cpp)
参考链接:
> https://stackoverflow.com/questions/4600295/what-is-the-meaning-of-operator-bool-const
================================================
FILE: basic_content/explicit/explicit.cpp
================================================
#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;
}
================================================
FILE: basic_content/extern/README.md
================================================
# extern "C"
## 关于作者:
个人公众号:

## 1.C++与C编译区别
在C++中常在头文件见到extern "C"修饰函数,那有什么作用呢? 是用于C++链接在C语言模块中定义的函数。
C++虽然兼容C,但C++文件中函数编译后生成的符号与C语言生成的不同。因为C++支持函数重载,C++函数编译后生成的符号带有函数参数类型的信息,而C则没有。
例如`int add(int a, int b)`函数经过C++编译器生成.o文件后,`add`会变成形如`add_int_int`之类的, 而C的话则会是形如`_add`, 就是说:相同的函数,在C和C++中,编译后生成的符号不同。
这就导致一个问题:如果C++中使用C语言实现的函数,在编译链接的时候,会出错,提示找不到对应的符号。此时`extern "C"`就起作用了:告诉链接器去寻找`_add`这类的C语言符号,而不是经过C++修饰的符号。
## 2.C++调用C函数
C++调用C函数的例子: 引用C的头文件时,需要加`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;
}
```
编译:
```
//Generate add.o file
gcc -c add.c
```
链接:
```
g++ add.cpp add.o -o main
```
没有添加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
```
添加extern "C"后:
`add.cpp`
```c++
#include
using namespace std;
extern "C" {
#include "add.h"
}
int main() {
std::cout << add(2, 3) << std::endl;
return 0;
}
```
编译的时候一定要注意,先通过gcc生成中间文件add.o。
```
gcc -c add.c
```
然后编译:
```
g++ add.cpp add.o -o main
```
如果使用bazel编译,运行:`bazel run basic_content/extern/extern_c++: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)
- [add.c](extern_c++/add.c)
- [add.cpp](extern_c++/add.cpp)
## 2.C中调用C++函数
`extern "C"`在C中是语法错误,需要放在C++头文件中。
```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;
}
```
编译:
```c
g++ -c add.cpp
```
链接:
```
gcc add.c add.o -o main
```
上述案例源代码见:
- [add.h](extern_c/add.h)
- [add.c](extern_c/add.c)
- [add.cpp](extern_c/add.cpp)
综上,总结出使用方法,在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。所以使用extern "C"全部都放在于cpp程序相关文件或其头文件中。
总结出如下形式:
(1)C++调用C函数:
```c++
//xx.h
extern int add(...)
//xx.c
int add(){
}
//xx.cpp
extern "C" {
#include "xx.h"
}
```
(2)C调用C++函数
```c
//xx.h
extern "C"{
int add();
}
//xx.cpp
int add(){
}
//xx.c
extern int add();
```
不过与C++调用C接口不同,C++确实是能够调用编译好的C函数,而这里C调用C++,不过是把C++代码当成C代码编译后调用而已。也就是说,C并不能直接调用C++库函数。
================================================
FILE: basic_content/extern/extern_c/BUILD
================================================
# please run `bazel run basic_content/extern/extern_c:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "add",
srcs = ["add.cpp"],
hdrs = ["add.h"],
)
cc_binary(
name = "main",
srcs = ["add.c"],
deps = [":add"],
)
================================================
FILE: basic_content/extern/extern_c/add.c
================================================
extern int add(int x,int y);
int main() {
add(2,3);
return 0;
}
================================================
FILE: basic_content/extern/extern_c/add.cpp
================================================
#include "add.h"
int add(int x, int y) { return x + y; }
================================================
FILE: basic_content/extern/extern_c/add.h
================================================
#ifndef ADD_H
#define ADD_H
extern "C" {
int add(int x, int y);
}
#endif
================================================
FILE: basic_content/extern/extern_c++/BUILD
================================================
# please run `bazel run basic_content/extern/extern_c++:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "add",
srcs = ["add.c"],
hdrs = ["add.h"],
)
cc_binary(
name = "main",
srcs = ["add.cpp"],
deps = [":add"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/extern/extern_c++/add.c
================================================
#include "add.h"
int add(int x,int y) {
return x+y;
}
================================================
FILE: basic_content/extern/extern_c++/add.cpp
================================================
#include
using namespace std;
extern "C" {
#include "add.h"
}
int main() {
std::cout << add(2, 3) << std::endl;
return 0;
}
================================================
FILE: basic_content/extern/extern_c++/add.h
================================================
#ifndef ADD_H
#define ADD_H
extern int add(int x, int y);
#endif
================================================
FILE: basic_content/friend/BUILD
================================================
# please run `bazel run basic_content/friend:friend_class`
# please run `bazel run basic_content/friend:friend_func`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "friend_class",
srcs = ["friend_class.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "friend_func",
srcs = ["friend_func.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/friend/README.md
================================================
# 友元函数与友元类
## 关于作者:
个人公众号:

## 0.概述
友元提供了一种 普通函数或者类成员函数 访问另一个类中的私有或保护成员 的机制。也就是说有两种形式的友元:
(1)友元函数:普通函数对一个访问某个类中的私有或保护成员。
(2)友元类:类A中的成员函数访问类B中的私有或保护成员
优点:提高了程序的运行效率。
缺点:破坏了类的封装性和数据的透明性。
总结:
- 能访问私有成员
- 破坏封装性
- 友元关系不可传递
- 友元关系的单向性
- 友元声明的形式及数量不受限制
## 1.友元函数
在类声明的任何区域中声明,而定义则在类的外部。
```
friend <类型><友元函数名>(<参数表>);
```
注意,友元函数只是一个普通函数,并不是该类的类成员函数,它可以在任何地方调用,友元函数中通过对象名来访问该类的私有或保护成员。
具体代码见:[friend_func.cpp](friend_func.cpp)
```c++
#include
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<;
```
类B是类A的友元,那么类B可以直接访问A的私有成员。
具体代码见:[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 << b.getb(a) << endl;
return 0;
}
================================================
FILE: basic_content/friend/friend_func.cpp
================================================
#include
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 << geta(a) << endl;
return 0;
}
================================================
FILE: basic_content/func_pointer/BUILD
================================================
# please run `bazel run basic_content/func_pointer:func_pointer`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "func_pointer",
srcs = ["func_pointer.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/func_pointer/func_pointer.cpp
================================================
/**
* @file func_pointer.cpp
* @brief 函数指针的使用!
* @author 光城
* @version v1
* @date 2019-07-20
*/
#include
using namespace std;
/**
* @brief
* 定义了一个变量pFun,这个变量是个指针,指向返回值为空和参数为int的函数的指针!
*/
void (*pFun)(int);
/**
* @brief 代表一种新类型,不是变量!所以与上述的pFun不一样!
*/
typedef void (*func)(void);
void myfunc(void) { cout << "asda" << endl; }
void glFun(int a) { cout << a << endl; }
int main() {
func pfun = myfunc; /*赋值*/
pfun(); /*调用*/
pFun = glFun;
(*pFun)(2);
}
================================================
FILE: basic_content/inline/BUILD
================================================
# please run `bazel run basic_content/inline:inline_virtual`
# please run `bazel run basic_content/inline:main`
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_library(
name = "inline",
hdrs = ["inline.h"],
)
cc_binary(
name = "inline_virtual",
srcs = ["inline_virtual.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "main",
srcs = ["inline.cpp"],
deps = [
":inline",
],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/inline/README.md
================================================
# inline那些事
## 关于作者:
个人公众号:

## 1.类中内联
头文件中声明方法
```c++
class A
{
public:
void f1(int x);
/**
* @brief 类中定义了的函数是隐式内联函数,声明要想成为内联函数,必须在实现处(定义处)加inline关键字。
*
* @param x
* @param y
*/
void Foo(int x,int y) ///< 定义即隐式内联函数!
{
};
void f2(int x); ///< 声明后,要想成为内联函数,必须在定义处加inline关键字。
};
```
实现文件中定义内联函数:
```c++
#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){
}
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;
ptr = nullptr;
return 0;
}
```
================================================
FILE: basic_content/inline/inline.cpp
================================================
#include "inline.h"
#include
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 << Foo(1, 2) << endl; }
/**
* 编译器对 inline 函数的处理步骤
* 将 inline 函数体复制到 inline 函数调用点处;
* 为所用 inline 函数中的局部变量分配内存空间;
* 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中;
* 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用
* GOTO)。
*/
================================================
FILE: basic_content/inline/inline.h
================================================
#pragma once
class A {
public:
void f1(int x);
/**
* @brief
* 类中定义了的函数是隐式内联函数,声明要想成为内联函数,必须在实现处(定义处)加inline关键字。
*
* @param x
* @param y
*/
void Foo(int x, int y) ///< 定义即隐式内联函数!
{
};
void f2(int x); ///< 声明后,要想成为内联函数,必须在定义处加inline关键字。
};
================================================
FILE: basic_content/inline/inline_virtual.cpp
================================================
#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;
}
================================================
FILE: basic_content/macro/BUILD
================================================
# please run `bazel run basic_content/macro:do_while`
# please run `bazel run basic_content/sig_examp:sig_examp`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "do_while",
srcs = ["do_while.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "sig_examp",
srcs = ["sig_examp.cpp"],
copts = ["-std=c++11"],
)
================================================
FILE: basic_content/macro/README.md
================================================
# 宏那些事
## 关于作者:
个人公众号:

## 1.宏中包含特殊符号
分为几种:`#`,`##`,`\`
### 1.1 字符串化操作符(#)
**在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组**,换言之就是:**#是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串**。
**注意:其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。**
例如:
```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()
```
这个宏被展开后就是:
```
if(a>0)
f1();
f2();
```
本意是a>0执行f1 f2,而实际是f2每次都会执行,所以就错误了。
为了解决这种问题,在写代码的时候,通常可以采用`{}`块。
如:
```c++
#define fun() {f1();f2();}
if(a>0)
fun();
// 宏展开
if(a>0)
{
f1();
f2();
};
```
但是会发现上述宏展开后多了一个分号,实际语法不太对。(虽然编译运行没问题,正常没分号)。
### 2.2避免使用goto控制流
在一些函数中,我们可能需要在return语句之前做一些清理工作,比如释放在函数开始处由malloc申请的内存空间,使用goto总是一种简单的方法:
```c++
int f() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout<<*p<
================================================
FILE: basic_content/macro/do_while.cpp
================================================
#include
#include
using namespace std;
#define f1() cout << "f1()" << endl;
#define f2() cout << "f2()" << endl;
#define fun() \
{ \
f1(); \
f2(); \
}
#define fun1() \
do { \
f1(); \
f2(); \
} while (0)
int f() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout << *p << endl;
#ifndef DEBUG
int error = 1;
#endif
if (error)
goto END;
// dosomething
END:
cout << "free" << endl;
free(p);
return 0;
}
int ff() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout << *p << endl;
do {
#ifndef DEBUG
int error = 1;
#endif
if (error)
break;
// dosomething
} while (0);
cout << "free" << endl;
free(p);
return 0;
}
int fc() {
int k1 = 10;
cout << k1 << endl;
do {
int k1 = 100;
cout << k1 << endl;
} while (0);
cout << k1 << endl;
return 0;
}
int main() {
if (1 > 0)
fun();
if (2 > 0)
fun1();
f();
ff();
fc();
return 0;
}
================================================
FILE: basic_content/macro/sig_examp.cpp
================================================
#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 << str << " " << str.size() << endl;
/**
* 忽略传入参数名前面和后面的空格。
*/
string str1 = exp2(asda bac);
/**
* 当传入参数名间存在空格时,编译器将会自动连接各个子字符串,
* 用每个子字符串之间以一个空格连接,忽略剩余空格。
*/
cout << str1 << " " << str1.size() << endl;
///===========================================
/**
* (#)字符串操作符
*/
///===========================================
const char *gc_hello = "I am gc_hello";
expA(hello);
expB(hello1);
char var1_p[20];
char var2_p[20];
// 连接后的实际参数名赋值
strcpy(var1_p, "aaaa");
strcpy(var2_p, "bbbb");
///===========================================
/**
* (\)续行操作符
*/
///===========================================
int max_val = MAX(3, 6);
cout << max_val << endl;
return 0;
}
================================================
FILE: basic_content/maohao/BUILD
================================================
# please run `bazel run basic_content/maohao:maohao`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "maohao",
srcs = ["maohao.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/maohao/README.md
================================================
# :: 范围解析运算符那些事
## 关于作者:
个人公众号:

- 全局作用域符(::name):用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间
- 类作用域符(class::name):用于表示指定类型的作用域范围是具体某个类的
- 命名空间作用域符(namespace::name):用于表示指定类型的作用域范围是具体某个命名空间的
具体代码见:[maohao.cpp](maohao.cpp)
================================================
FILE: basic_content/maohao/maohao.cpp
================================================
#include
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 << A::count << endl;
// int count=3; // 局部count
// count=4; // 设置局部的count为4
return 0;
}
================================================
FILE: basic_content/pointer_refer/BUILD
================================================
# please run `bazel run basic_content/pointer_refer:copy_construct`
# please run `bazel run basic_content/pointer_refer:effec`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "copy_construct",
srcs = ["copy_construct.cpp"],
copts = ["-std=c++11", "-fno-elide-constructors"]
)
cc_binary(
name = "effec",
srcs = ["effec.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/pointer_refer/README.md
================================================
# 引用与指针那些事
## 关于作者:
个人公众号:

## 1.引用与指针
总论:
| 引用 | 指针 |
| ------------ | ------------ |
| 必须初始化 | 可以不初始化 |
| 不能为空 | 可以为空 |
| 不能更换目标 | 可以更换目标 |
> 引用必须初始化,而指针可以不初始化。
我们在定义一个引用的时候必须为其指定一个初始值,但是指针却不需要。
```c++
int &r; //不合法,没有初始化引用
int *p; //合法,但p为野指针,使用需要小心
```
> 引用不能为空,而指针可以为空。
由于引用不能为空,所以我们在使用引用的时候不需要测试其合法性,而在使用指针的时候需要首先判断指针是否为空指针,否则可能会引起程序崩溃。
```c++
void test_p(int* p)
{
if(p != null_ptr) //对p所指对象赋值时需先判断p是否为空指针
*p = 3;
return;
}
void test_r(int& r)
{
r = 3; //由于引用不能为空,所以此处无需判断r的有效性就可以对r直接赋值
return;
}
```
> 引用不能更换目标
指针可以随时改变指向,但是引用只能指向初始化时指向的对象,无法改变。
```
int a = 1;
int b = 2;
int &r = a; //初始化引用r指向变量a
int *p = &a; //初始化指针p指向变量a
p = &b; //指针p指向了变量b
r = b; //引用r依然指向a,但a的值变成了b
```
## 2.引用
#### 左值引用
常规引用,一般表示对象的身份。
#### 右值引用
右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。
右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:
- 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
- 能够更简洁明确地定义泛型函数。
#### 引用折叠
- `X& &`、`X& &&`、`X&& &` 可折叠成 `X&`
- `X&& &&` 可折叠成 `X&&`
C++的引用**在减少了程序员自由度的同时提升了内存操作的安全性和语义的优美性**。比如引用强制要求必须初始化,可以让我们在使用引用的时候不用再去判断引用是否为空,让代码更加简洁优美,避免了指针满天飞的情形。除了这种场景之外引用还用于如下两个场景:
> 引用型参数
一般我们使用const reference参数作为只读形参,这种情况下既可以避免参数拷贝还可以获得与传值参数一样的调用方式。
```c++
void test(const vector &data)
{
//...
}
int main()
{
vector data{1,2,3,4,5,6,7,8};
test(data);
}
```
> 引用型返回值
C++提供了重载运算符的功能,我们在重载某些操作符的时候,使用引用型返回值可以获得跟该操作符原来语法相同的调用方式,保持了操作符语义的一致性。一个例子就是operator []操作符,这个操作符一般需要返回一个引用对象,才能正确的被修改。
```c++
vector v(10);
v[5] = 10; //[]操作符返回引用,然后vector对应元素才能被修改
//如果[]操作符不返回引用而是指针的话,赋值语句则需要这样写
*v[5] = 10; //这种书写方式,完全不符合我们对[]调用的认知,容易产生误解
```
## 3.指针与引用的性能差距
指针与引用之间有没有性能差距呢?这种问题就需要进入汇编层面去看一下。我们先写一个test1函数,参数传递使用指针:
```c++
void test1(int* p)
{
*p = 3; //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。
return;
}
```
该代码段对应的汇编代码如下:
```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.
```
上述代码1、2行是参数调用保存现场操作;第3行是参数传递,函数调用第一个参数一般放在rdi寄存器,此行代码把rdi寄存器值(指针p的值)写入栈中;第4行是把栈中p的值写入rax寄存器;第5行是把立即数3写入到**rax寄存器值所指向的内存**中,此处要注意(%rax)两边的括号,这个括号并并不是可有可无的,(%rax)和%rax完全是两种意义,(%rax)代表rax寄存器中值所代表地址部分的内存,即相当于C++代码中的*p,而%rax代表rax寄存器,相当于C++代码中的p值,所以汇编这里使用了(%rax)而不是%rax。
我们再写出参数传递使用引用的C++代码段test2:
```c++
void test2(int& r)
{
r = 3; //赋值前无需判断reference是否为空
return;
}
```
这段代码对应的汇编代码如下:
```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.
```
我们发现test2对应的汇编代码和test1对应的汇编代码完全相同,这说明C++编译器在编译程序的时候将指针和引用编译成了完全一样的机器码。所以C++中的引用只是C++对指针操作的一个“语法糖”,在底层实现时C++编译器实现这两种操作的方法完全相同。
## 3.总结
C++中引入了引用操作,在对引用的使用加了更多限制条件的情况下,保证了引用使用的安全性和便捷性,还可以保持代码的优雅性。在适合的情况使用适合的操作,引用的使用可以一定程度避免“指针满天飞”的情况,对于提升程序稳定性也有一定的积极意义。最后,指针与引用底层实现都是一样的,不用担心两者的性能差距。
上述部分参考自:
================================================
FILE: basic_content/pointer_refer/copy_construct.cpp
================================================
/**
* @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()); // 应该只调用一次拷贝构造函数
}
================================================
FILE: basic_content/pointer_refer/effec.cpp
================================================
#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 << a << endl;
return 0;
}
================================================
FILE: basic_content/sizeof/BUILD
================================================
# please run `bazel run basic_content/sizeof:blackclass`
# please run `bazel run basic_content/sizeof:genA`
# please run `bazel run basic_content/sizeof:geninhe`
# please run `bazel run basic_content/sizeof:moreinhe`
# please run `bazel run basic_content/sizeof:morevir`
# please run `bazel run basic_content/sizeof:static`
# please run `bazel run basic_content/sizeof:virinhe`
# please run `bazel run basic_content/sizeof:virmoreinhe`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "blackclass",
srcs = ["blackclass.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "genA",
srcs = ["genA.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "geninhe",
srcs = ["geninhe.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "moreinhe",
srcs = ["moreinhe.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "static",
srcs = ["static.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "virinhe",
srcs = ["virinhe.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "virmoreinhe",
srcs = ["virmoreinhe.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/sizeof/README.md
================================================
# 类大小计算
首先来个总结,然后下面给出实际例子,实战!
- 空类的大小为1字节
- 一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间。
- 对于包含虚函数的类,不管有多少个虚函数,只有一个虚指针,vptr的大小。
- 普通继承,派生类继承了所有基类的函数与成员,要按照字节对齐来计算大小
- 虚函数继承,不管是单继承还是多继承,都是继承了基类的vptr。(32位操作系统4字节,64位操作系统 8字节)!
- 虚继承,继承基类的vptr。
## 1.原则1
```c++
/**
* @file blackclass.cpp
* @brief 空类的大小为1字节
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
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 字节对齐、静态变量不影响类的大小、vptr指针=8
*/
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 此时B按照顺序:
* char a
* int b
* short a
* long b
* 根据字节对齐4+4+8+8=24
*
* 或编译器优化
* char a
* short a
* int b
* long b
* 根据字节对齐2+2+4+8=16
*/
class B:A
{
public:
short a;
long b;
};
/**
* 把A的成员拆开看,char为1,int为4,所以是1+(3)+4+1+(3)=12,()为字节补齐
*/
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 派生类虚继承多个虚函数,会继承所有虚函数的vptr
*/
cout<
#include
using namespace std;
class A {};
int main() {
cout << sizeof(A) << endl;
return 0;
}
================================================
FILE: basic_content/sizeof/genA.cpp
================================================
/**
* @file genA.cpp
* @brief
* 普通成员函数,大小为1,一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间。
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
using namespace std;
class A {
public:
A();
~A();
static int a;
static void fun3();
void fun();
void fun1();
};
int main() {
cout << sizeof(A) << endl; // 1
return 0;
}
================================================
FILE: basic_content/sizeof/geninhe.cpp
================================================
/**
* @file geninhe.cpp
* @brief 1.普通单继承,继承就是基类+派生类自身的大小(注意字节对齐)
* 注意:类的数据成员按其声明顺序加入内存,无访问权限无关,只看声明顺序。
* 2.虚单继承,派生类继承基类vptr
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
using namespace std;
class A {
public:
char a;
int b;
};
/**
* @brief 此时B按照顺序:
* char a
* int b
* short a
* long b
* 根据字节对齐4+4+8+8=24
*
* 或编译器优化
* char a
* short a
* int b
* long b
* 根据字节对齐2+2+4+8=16
*/
class B : A {
public:
short a;
long b;
};
/**
* 把A的成员拆开看,char为1,int为4,所以是1+(3)+4+1+(3)=12,()为字节补齐
*/
class C {
A a;
char c;
};
class A1 {
virtual void fun() {}
};
class C1 : public A1 {};
int main() {
cout << sizeof(A) << endl; // 8
cout << sizeof(B) << endl; // 16
cout << sizeof(C) << endl; // 12
/**
* @brief 对于虚单函数继承,派生类也继承了基类的vptr,所以是8字节
*/
cout << sizeof(C1) << endl; // 8
return 0;
}
================================================
FILE: basic_content/sizeof/moreinhe.cpp
================================================
/**
* @file moreinhe.cpp
* @brief 普通多继承与虚函数多继承
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
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 << sizeof(A) << endl; // 8
cout << sizeof(B) << endl; // 16
cout << sizeof(C) << endl; // 32
return 0;
}
================================================
FILE: basic_content/sizeof/morevir.cpp
================================================
/**
* @file morevir.cpp
* @brief 对于包含虚函数的类,不管有多少个虚函数,只有一个虚指针,vptr的大小。
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
using namespace std;
class A {
virtual void fun();
virtual void fun1();
virtual void fun2();
virtual void fun3();
};
int main() {
cout << sizeof(A) << endl;
return 0;
}
================================================
FILE: basic_content/sizeof/static.cpp
================================================
/**
* @file static.cpp
* @brief 静态数据成员
* 静态数据成员被编译器放在程序的一个global data
* members中,它是类的一个数据成员,但不影响类的大小。不管这个类产生了多少个实例,还是派生了多少新的类,静态数据成员只有一个实例。静态数据成员,一旦被声明,就已经存在。
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
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 << sizeof(A) << endl;
return 0;
}
================================================
FILE: basic_content/sizeof/virinhe.cpp
================================================
/**
* @file virnhe.cpp
* @brief 虚继承
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
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 << sizeof(A) << " " << sizeof(B) << " " << sizeof(C);
return 0;
}
================================================
FILE: basic_content/sizeof/virmoreinhe.cpp
================================================
/**
* @file virmoreinhe.cpp
* @brief 虚函数多继承
* @author 光城
* @version v1
* @date 2019-07-21
*/
#include
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 << sizeof(A) << " " << sizeof(B) << " " << sizeof(C);
return 0;
}
================================================
FILE: basic_content/static/BUILD
================================================
# please run `bazel run basic_content/static:nostatic_class`
# please run `bazel run basic_content/static:static_class`
# please run `bazel run basic_content/static:static_demo`
# please run `bazel run basic_content/static:static_error_variable`
# please run `bazel run basic_content/static:static_funciton`
# please run `bazel run basic_content/static:static_variable`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "nostatic_class",
srcs = ["nostatic_class.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "static_class",
srcs = ["static_class.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "static_demo",
srcs = ["static_demo.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "sstatic_error_variable",
srcs = ["static_error_variable.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "static_funciton",
srcs = ["static_funciton.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "static_variable",
srcs = ["static_variable.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/static/README.md
================================================
# static那些事
## 关于作者
微信公众号:

当与不同类型一起使用时,Static关键字具有不同的含义。我们可以使用static关键字:
**静态变量:** 函数中的变量,类中的变量
**静态类的成员:** 类对象和类中的函数
现在让我们详细看一下静态的这些用法:
**静态变量**
- 函数中的静态变量
当变量声明为static时,空间**将在程序的生命周期内分配**。即使多次调用该函数,静态变量的空间也**只分配一次**,前一次调用中的变量值通过下一次函数调用传递。这对于在C / C ++或需要存储先前函数状态的任何其他应用程序非常有用。
```c++
#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;
}
```
输出:
```
0 1 2 3 4
```
您可以在上面的程序中看到变量count被声明为static。因此,它的值通过函数调用来传递。每次调用函数时,都不会对变量计数进行初始化。
- 类中的静态变量
由于声明为static的变量只被初始化一次,因为它们在单独的静态存储中分配了空间,因此类中的静态变量**由对象共享**。对于不同的对象,不能有相同静态变量的多个副本。也是因为这个原因,静态变量不能使用构造函数初始化。
```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;
}
```
输出:
```
1
```
**静态成员**
- 类对象为静态
就像变量一样,对象也在声明为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";
}
```
输出:
```c++
Inside Constructor
Inside Destructor
End of main
```
在上面的程序中,对象在if块内声明为非静态。因此,变量的范围仅在if块内。因此,当创建对象时,将调用构造函数,并且在if块的控制权越过析构函数的同时调用,因为对象的范围仅在声明它的if块内。
如果我们将对象声明为静态,现在让我们看看输出的变化。
```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";
}
```
输出:
```
Inside Constructor
End of main
Inside Destructor
```
您可以清楚地看到输出的变化。现在,在main结束后调用析构函数。这是因为静态对象的范围是贯穿程序的生命周期。
- 类中的静态函数
就像类中的静态数据成员或静态变量一样,静态成员函数也不依赖于类的对象。我们被允许使用对象和'.'来调用静态成员函数。但建议使用类名和范围解析运算符调用静态成员。
允许静态成员函数仅访问静态数据成员或其他静态成员函数,它们无法访问类的非静态数据成员或成员函数。
```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();
}
```
输出:
```
Welcome to Apple!
```
**限定访问范围**
static还有限定访问范围的作用(类似于匿名名字空间)。来自issue:https://github.com/Light-City/CPlusPlusThings/issues/142
```cpp
// source1.cpp
extern void sayHello();
const char* msg = "Hello World!\n";
int main()
{
sayHello();
return 0;
}
// source2.cpp
#include
extern char* msg;
void sayHello()
{
printf("%s", msg);
}
```
g++对于上面两个代码文件是可以正常编译并且打印Hello World!,但如果给source1.cpp中的msg加上static,则会导致undefined reference to 'msg'的编译错误:
```cpp
// source1.cpp
extern void sayHello();
static const char* msg = "Hello World!\n";
int main()
{
sayHello();
return 0;
}
```
================================================
FILE: basic_content/static/nostatic_class.cpp
================================================
#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";
}
================================================
FILE: basic_content/static/static_class.cpp
================================================
#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";
}
================================================
FILE: basic_content/static/static_demo.cpp
================================================
// 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;
}
================================================
FILE: basic_content/static/static_error_variable.cpp
================================================
// 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 << " " << obj2.i;
}
================================================
FILE: basic_content/static/static_funciton.cpp
================================================
#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();
}
================================================
FILE: basic_content/static/static_variable.cpp
================================================
// 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;
}
================================================
FILE: basic_content/struct/BUILD
================================================
# please run `bazel run basic_content/struct:ext_struct_func`
# please run `bazel run basic_content/struct:struct_func_func`
# please run `bazel run basic_content/struct:struct_func`
# please run `bazel run basic_content/struct:struct_func_cpp`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "ext_struct_func",
srcs = ["ext_struct_func.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "struct_func_func",
srcs = ["struct_func_func.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "struct_func",
srcs = ["struct_func.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "struct_func_cpp",
srcs = ["struct_func.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/struct/README.md
================================================
# 一文搞懂C和C++中struct
## 关于作者:
个人公众号:

## 1.C中struct
- 在C中struct只单纯的用作数据的复合类型,也就是说,在结构体声明中只能将数据成员放在里面,而不能将函数放在里面。
- 在C结构体声明中不能使用C++访问修饰符,如:public、protected、private 而在C++中可以使用。
- 在C中定义结构体变量,如果使用了下面定义必须加struct。
- C的结构体不能继承(没有这一概念)。
- 若结构体的名字与函数名相同,可以正常运行且正常的调用!例如:可以定义与 struct Base 不冲突的 void Base() {}。
完整案例:
```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;
}
```
最后输出:
```
1
I am Base func
```
完整代码见:[struct_func.c](./struct_func.c)
## 2.C++中struct
与C对比如下:
- C++结构体中不仅可以定义数据,还可以定义函数。
- C++结构体中可以使用访问修饰符,如:public、protected、private 。
- C++结构体使用可以直接使用不带struct。
- C++继承
- 若结构体的名字与函数名相同,可以正常运行且正常的调用!但是定义结构体变量时候只能用带struct的!
例如:
> 情形1:不适用typedef定义结构体别名
未添加同名函数前:
```c++
struct Student {
};
//Student(){}
struct Student s; //ok
Student s; //ok
```
添加同名函数后:
```c++
struct Student {
};
Student(){}
struct Student s; //ok
Student s; //error
```
> 情形二:使用typedef定义结构体别名
```c++
typedef struct Base1 {
int v1;
int v3;
public: //显示声明public
int v2;
void print(){
printf("%s\n","hello world");
};
}B;
//void B() {} //error! 符号 "B" 已经被定义为一个 "struct Base1" 的别名
```
> 前三种案例
```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;
}
```
完整代码见:[struct_func.cpp](struct_func.cpp)
> 继承案例
```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;
}
```
完整代码见:[ext_struct_func.cpp](./ext_struct_func.cpp)
> 同名函数
```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;
}
```
完整代码见:[struct_func_func.cpp](./struct_func_func.cpp)
## 3.总结
### C和C++中的Struct区别
| C | C++ |
| ------------------------------------------------------ | ------------------------------------------------------------ |
| 不能将函数放在结构体声明 | 能将函数放在结构体声明 |
| 在C结构体声明中不能使用C++访问修饰符。 | public、protected、private 在C++中可以使用。 |
| 在C中定义结构体变量,如果使用了下面定义必须加struct。 | 可以不加struct |
| 结构体不能继承(没有这一概念)。 | 可以继承 |
| 若结构体的名字与函数名相同,可以正常运行且正常的调用! | 若结构体的名字与函数名相同,使用结构体,只能使用带struct定义! |
================================================
FILE: basic_content/struct/ext_struct_func.cpp
================================================
#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" << endl; };
virtual ~Base() { cout << "Base deconstruct" << endl; };
};
struct Derived : Base {
Derived() { cout << "Derived construct" << endl; };
virtual ~Derived() { cout << "Derived deconstruct" << endl; };
public:
int v2;
void print() { printf("%s\n", "Derived"); };
};
int main() {
Base *b = new Derived();
b->print();
delete b;
return 0;
}
================================================
FILE: basic_content/struct/struct_func.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;
}
================================================
FILE: basic_content/struct/struct_func.cpp
================================================
#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;
}
================================================
FILE: basic_content/struct/struct_func_func.cpp
================================================
#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;
}
================================================
FILE: basic_content/struct_class/README.md
================================================
# struct与class区别
## 关于作者:
个人公众号:

关于C与C++中struct内容:见[struct那些事](../struct)
总的来说,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。
区别:
最本质的一个区别就是默认的访问控制
默认的继承访问权限。struct 是 public 的,class 是 private 的。
struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。
================================================
FILE: basic_content/this/BUILD
================================================
# please run `bazel run basic_content/this:person`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "person",
srcs = ["person.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/this/README.md
================================================
# this指针那些事
## 关于作者
微信公众号:

## 1.this指针
相信在坐的很多人,都在学Python,对于Python来说有self,类比到C++中就是this指针,那么下面一起来深入分析this指针在类中的使用!
首先来谈谈this指针的用处:
(1)一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
(2)this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。
其次,this指针的使用:
(1)在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this。
(2)当参数与成员变量名相同时,如this->n = n (不能写成n = n)。
另外,在网上大家会看到this会被编译器解析成`A *const `,`A const * `,究竟是哪一个呢?下面通过断点调试分析:
现有如下例子:
```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 << p.get_age() << endl;
return 0;
}
================================================
FILE: basic_content/union/BUILD
================================================
# please run `bazel run basic_content/union:union`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "union",
srcs = ["union.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/union/README.md
================================================
# UNION那些事
## 关于作者:
个人公众号:

联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点:
- 默认访问控制符为 public
- 可以含有构造函数、析构函数
- 不能含有引用类型的成员
- 不能继承自其他类,不能作为基类
- 不能含有虚函数
- 匿名 union 在定义所在作用域可直接访问 union 成员
- 匿名 union 不能包含 protected 成员或 private 成员
- 全局匿名联合必须是静态(static)的
================================================
FILE: basic_content/union/union.cpp
================================================
/**
* @file union.cpp
* @brief UNION
* @author 光城
* @version v1
* @date 2019-08-06
*/
#include
/**
* 默认访问控制符为public
*/
union UnionTest {
/**
* 可以含有构造函数、析构函数
*/
UnionTest() : i(10) { print(i); };
~UnionTest(){};
int i;
private:
void print(int i) { std::cout << i << std::endl; };
};
/**
* 全局匿名联合必须是静态的
*/
static union {
int i;
double d;
};
int main() {
UnionTest u;
union {
int i;
double d;
};
std::cout << u.i << std::endl; // 输出 UnionTest 联合的 10
::i = 20;
std::cout << ::i << std::endl; // 输出全局静态匿名联合的 20
/**
* 匿名union在定义所在作用域可直接访问union成员
*/
i = 30;
std::cout << i << std::endl; // 输出局部匿名联合的 30
return 0;
}
================================================
FILE: basic_content/using/BUILD
================================================
# please run `bazel run basic_content/using:derived_base`
# please run `bazel run basic_content/using:using_derived`
# please run `bazel run basic_content/using:using_global`
# please run `bazel run basic_content/using:using_typedef`
load("@rules_cc//cc:defs.bzl", "cc_binary")
# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.
cc_binary(
name = "derived_base",
srcs = ["derived_base.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "using_derived",
srcs = ["using_derived.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "using_global",
srcs = ["using_global.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "using_typedef",
srcs = ["using_typedef.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/using/README.md
================================================
# using那些事
## 关于作者:
个人公众号:

## 基本使用
局部与全局using,具体操作与使用见下面案例:
```c++
#include
#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;
```
完整代码见:[using_typedef.cpp](using_typedef.cpp)
================================================
FILE: basic_content/using/derived_base.cpp
================================================
#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;
}
================================================
FILE: basic_content/using/using_derived.cpp
================================================
/**
* @file using_derived.cpp
* @brief 函数重装
* @author 光城
* @version v1
* @date 2019-08-07
*/
#include
using namespace std;
class Base {
public:
void f() { cout << "f()" << endl; }
void f(int n) { cout << "Base::f(int)" << endl; }
};
class Derived : private Base {
public:
using Base::f;
void f(int n) { cout << "Derived::f(int)" << endl; }
};
int main() {
Derived d;
d.f();
d.f(1);
return 0;
}
================================================
FILE: basic_content/using/using_global.cpp
================================================
/**
* @file using_global.cpp
* @brief using各种使用
* @author 光城
* @version v1
* @date 2019-08-07
*/
#include
#define isNs1 1
// #define isGlobal 2
using namespace std;
void func() { cout << "::func" << endl; }
namespace ns1 {
void func() { cout << "ns1::func" << endl; }
} // namespace ns1
namespace ns2 {
#ifdef isNs1
using ns1::func; /// ns1中的函数
#elif isGlobal
using ::func; /// 全局中的函数
#else
void func() { cout << "other::func" << endl; }
#endif
} // namespace ns2
int main() {
/**
* 这就是为什么在c++中使用了cmath而不是math.h头文件
*/
ns2::func(); // 会根据当前环境定义宏的不同来调用不同命名空间下的func()函数
return 0;
}
================================================
FILE: basic_content/using/using_typedef.cpp
================================================
/**
* @file using_typedef.cpp
* @brief bazel run basic_content/using:using_typedef
* 取代typedef,使用using来定义别名
* @author 光城
* @version v1
* @date 2019-08-07
*/
#include
#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 << i << " ";
cout << endl;
for (auto i : vec2)
cout << i << " ";
cout << endl;
return 0;
}
================================================
FILE: basic_content/virtual/README.md
================================================
# virtual那些事
## 关于作者:
个人公众号:

## 1.虚函数与运行多态
对应的代码:[emp.cpp](./set1/emp.cpp)
**虚函数的调用取决于指向或者引用的对象的类型,而不是指针或者引用自身的类型。**
## 2.vptr与vtable
见[vptr_vtable那些事](../vptr_vtable)
## 3.虚函数中默认参数
对应的代码:[default_arg.cpp](./set2/default_arg.cpp)
**默认参数是静态绑定的,虚函数是动态绑定的。 默认参数的使用需要看指针或者引用本身的类型,而不是对象的类型**。
## 4.可以不可以
(1) **静态函数可以声明为虚函数吗?**
原因主要有两方面:
**静态函数不可以声明为虚函数,同时也不能被const 和 volatile关键字修饰**
static成员函数不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义
虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,静态成员函数没有this指针,所以无法访问vptr。
代码学习:[static_error.cpp ](./set3/static_error.cpp )
(2)**构造函数可以为虚函数吗?**
构造函数不可以声明为虚函数。同时除了inline|explicit之外,构造函数不允许使用其它任何关键字。
为什么构造函数不可以为虚函数?
尽管虚函数表vtable是在编译阶段就已经建立的,但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数,编译器会在构造函数中添加代码来创建vptr。 问题来了,如果构造函数是虚的,那么它需要vptr来访问vtable,可这个时候vptr还没产生。 因此,构造函数不可以为虚函数。
我们之所以使用虚函数,是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的,实例的类型必须是明确的。 因此,构造函数没有必要被声明为虚函数。
代码学习:
- [copy_consrtuct.cpp](./set3/copy_consrtuct.cpp)
- [vir_con.cpp](./set3/vir_con.cpp)
(3)**析构函数可以为虚函数吗?**
**析构函数可以声明为虚函数。如果我们需要删除一个指向派生类的基类指针时,应该把析构函数声明为虚函数。 事实上,只要一个类有可能会被其它类所继承, 就应该声明虚析构函数(哪怕该析构函数不执行任何操作)。**
代码学习:
- [full_virde.cpp](./set3/full_virde.cpp)
- [vir_de.cpp ](./set3/vir_de.cpp)
(4)**虚函数可以为私有函数吗?**
- 基类指针指向继承类对象,则调用继承类对象的函数;
- int main()必须声明为Base类的友元,否则编译失败。 编译器报错: ptr无法访问私有函数。 当然,把基类声明为public, 继承类为private,该问题就不存在了。
代码学习:
- [virtual_function.cpp](./set3/virtual_function.cpp)
- [virtual_function1.cpp](./set3/virtual_function1.cpp)
(5)**虚函数可以被内联吗?**
**通常类成员函数都会被编译器考虑是否进行内联。 但通过基类指针或者引用调用的虚函数必定不能被内联。 当然,实体对象调用虚函数或者静态调用时可以被内联,虚析构函数的静态调用也一定会被内联展开。**
- 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
- 内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。
- `inline virtual` 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 `Base::who()`),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。
代码学习:
- [virtual_inline.cpp](./set3/virtual_inline.cpp)
- [inline_virtual.cpp](./set3/inline_virtual.cpp)
## 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)型。
在面向对象程序设计中,有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof,以及C#的as、is运算符类似,C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++ reinterpret_cast,dynamic_cast提供了类型安全检查,是一种基于能力查询(Capability Query)的转换,所以在多态类型间进行转换更提倡采用dynamic_cast。
代码学习:
- [rtti.cpp](./set4/rtti.cpp)
- [warn_rtti.cpp](./set4/warn_rtti.cpp)
## 6.纯虚函数和抽象类
见[纯虚函数和抽象类那些事](../abstract)
================================================
FILE: basic_content/virtual/set1/BUILD
================================================
# please run `bazel run basic_content/virtual/set1:emp`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "emp",
srcs = ["emp.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/virtual/set1/emp.cpp
================================================
#include
using namespace std;
class Employee {
public:
virtual void raiseSalary() { cout << 0 << endl; }
virtual void promote() { /* common promote code */
}
};
class Manager : public Employee {
virtual void raiseSalary() { cout << 100 << endl; }
virtual void promote() { /* Manager specific promote */
}
};
class Engineer : public Employee {
virtual void raiseSalary() { cout << 200 << endl; }
virtual void promote() { /* Manager specific promote */
}
};
// Similarly, there may be other types of employees
// We need a very simple function to increment salary of all employees
// Note that emp[] is an array of pointers and actual pointed objects can
// be any type of employees. This function should ideally be in a class
// like Organization, we have made it global to keep things simple
void globalRaiseSalary(Employee *emp[], int n) {
for (int i = 0; i < n; i++)
emp[i]->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;
}
================================================
FILE: basic_content/virtual/set2/BUILD
================================================
# please run `bazel run basic_content/virtual/set2:default_arg`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "default_arg",
srcs = ["default_arg.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/virtual/set2/default_arg.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/virtual/set3/BUILD
================================================
# please run `bazel run //basic_content/virtual/set3:copy_consrtuct`
# please run `bazel run //basic_content/virtual/set3:full_virde`
# please run `bazel run //basic_content/virtual/set3:inline_virtual`
# please run `bazel run //basic_content/virtual/set3:vir_con`
# please run `bazel run //basic_content/virtual/set3:vir_de`
# please run `bazel run //basic_content/virtual/set3:virtual_function`
# please run `bazel run //basic_content/virtual/set3:virtual_function1`
# please run `bazel run //basic_content/virtual/set3:virtual_inline`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "copy_consrtuct",
srcs = ["copy_consrtuct.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "full_virde",
srcs = ["full_virde.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "inline_virtual",
srcs = ["inline_virtual.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "vir_con",
srcs = ["vir_con.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "vir_de",
srcs = ["vir_de.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "virtual_function",
srcs = ["virtual_function.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "virtual_function1",
srcs = ["virtual_function1.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "virtual_inline",
srcs = ["virtual_inline.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/virtual/set3/copy_consrtuct.cpp
================================================
#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;
}
================================================
FILE: basic_content/virtual/set3/full_virde.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/virtual/set3/inline_virtual.cpp
================================================
#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;
}
================================================
FILE: basic_content/virtual/set3/static_error.cpp
================================================
/**
* @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 {}
================================================
FILE: basic_content/virtual/set3/vir_con.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/virtual/set3/vir_de.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/virtual/set3/virtual_function.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/virtual/set3/virtual_function1.cpp
================================================
#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;
}
================================================
FILE: basic_content/virtual/set3/virtual_inline.cpp
================================================
/**
* @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;
}
================================================
FILE: basic_content/virtual/set4/BUILD
================================================
# please run `bazel run basic_content/virtual/set4:rtti`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "rtti",
srcs = ["rtti.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/virtual/set4/rtti.cpp
================================================
/**
* @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" << endl;
else
cout << "cannot cast B* to D*";
try {
D &dobj = dynamic_cast(obj);
cout << "works" << endl;
} catch (bad_cast bc) { // ERROR
cout << bc.what() << endl;
}
return 0;
}
================================================
FILE: basic_content/virtual/set4/warn_rtti.cpp
================================================
// 在使用时需要注意:被转换对象obj的类型T1必须是多态类型,即T1必须公有继承自其它类,或者T1拥有虚函数(继承或自定义)。若T1为非多态类型,使用dynamic_cast会报编译错误。
// A为非多态类型
class A {};
// B为多态类型
class B {
public:
virtual ~B() {}
};
// D为非多态类型
class D : public A {};
// E为非多态类型
class E : private A {};
// F为多态类型
class F : private B {};
================================================
FILE: basic_content/volatile/BUILD
================================================
# please run `bazel run basic_content/volatile:noopt_vola`
# please run `bazel run basic_content/volatile:volatile`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "noopt_vola",
srcs = ["noopt_vola.cpp"],
copts = ["-std=c++11"]
)
cc_binary(
name = "volatile",
srcs = ["volatile.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/volatile/README.md
================================================
## 关于作者:
个人公众号:

## 1.volatile
被 `volatile` 修饰的变量,在对其进行读写操作时,会引发一些**可观测的副作用**。而这些可观测的副作用,是由**程序之外的因素决定的**。
## 2.volatile应用
(1)并行设备的硬件寄存器(如状态寄存器)。
假设要对一个设备进行初始化,此设备的某一个寄存器为0xff800000。
```c++
int *output = (unsigned int *)0xff800000; //定义一个IO端口;
int init(void)
{
int i;
for(i=0;i< 10;i++)
{
*output = i;
}
}
```
经过编译器优化后,编译器认为前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将output这个指针赋值为 9,所以编译器最后给你编译编译的代码结果相当于:
```c++
int init(void)
{
*output = 9;
}
```
如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值,显然优化过程并不能达到目的。反之如果你不是对此端口反复写操作,而是反复读操作,其结果是一样的,编译器在优化后,也许你的代码对此地址的读操作只做了一次。然而从代码角度看是没有任何问题的。这时候就该使用volatile通知编译器这个变量是一个不稳定的,在遇到此变量时候不要优化。
(2)一个中断服务子程序中访问到的变量;
```c++
static int i=0;
int main()
{
while(1)
{
if(i) dosomething();
}
}
/* Interrupt service routine */
void IRS()
{
i=1;
}
```
上面示例程序的本意是产生中断时,由中断服务子程序IRS响应中断,变更程序变量i,使在main函数中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远不会被调用。如果将变量i加上volatile修饰,则编译器保证对变量i的读写操作都不会被优化,从而保证了变量i被外部程序更改后能及时在原程序中得到感知。
(3)多线程应用中被多个任务共享的变量。
当多个线程共享某一个变量时,该变量的值会被某一个线程更改,应该用 volatile 声明。作用是防止编译器优化把变量从内存装入CPU寄存器中,当一个线程更改变量后,未及时同步到其它线程中导致程序出错。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。示例如下:
```c++
volatile bool bStop=false; //bStop 为共享全局变量
//第一个线程
void threadFunc1()
{
...
while(!bStop){...}
}
//第二个线程终止上面的线程循环
void threadFunc2()
{
...
bStop = true;
}
```
要想通过第二个线程终止第一个线程循环,如果bStop不使用volatile定义,那么这个循环将是一个死循环,因为bStop已经读取到了寄存器中,寄存器中bStop的值永远不会变成FALSE,加上volatile,程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。
是否了解volatile的应用场景是区分C/C++程序员和嵌入式开发程序员的有效办法,搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,这些都要求用到volatile变量,不懂得volatile将会带来程序设计的灾难。
## 3.volatile常见问题
下面的问题可以看一下面试者是不是真正了解volatile。
(1)一个参数既可以是const还可以是volatile吗?为什么?
可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
(2)一个指针可以是volatile吗?为什么?
可以。尽管这并不常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。
(3)下面的函数有什么错误?
```c++
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
```
这段代码有点变态,其目的是用来返回指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码:
```c++
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
```
由于*ptr的值可能被意想不到地改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
```c++
long square(volatile int *ptr)
{
int a=*ptr;
return a * a;
}
```
## 4.volatile使用
- volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。
- volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)
- const 可以是 volatile (如只读的状态寄存器)
- 指针可以是 volatile
代码学习:
- [noopt_vola.cpp](./noopt_vola.cpp)
- [volatile.cpp](./volatile.cpp)
================================================
FILE: basic_content/volatile/noopt_vola.cpp
================================================
/* Compile code without optimization option */
#include
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;
}
================================================
FILE: basic_content/volatile/volatile.cpp
================================================
/* 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;
}
================================================
FILE: basic_content/vptr_vtable/BUILD
================================================
# please run `bazel run basic_content/vptr_vtable:vptr1`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "vptr1",
srcs = ["vptr1.cpp"],
copts = ["-std=c++11"]
)
================================================
FILE: basic_content/vptr_vtable/README.md
================================================
# 深入浅出C++虚函数的vptr与vtable
## 关于作者:
个人公众号:

## 1.基础理论
为了实现虚函数,C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称,例如“vtable”,“虚函数表”,“虚方法表”或“调度表”。
虚拟表实际上非常简单,虽然用文字描述有点复杂。首先,**每个使用虚函数的类(或者从使用虚函数的类派生)都有自己的虚拟表**。该表只是编译器在编译时设置的静态数组。虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针,指向该类可访问的派生函数。
其次,编译器还会添加一个隐藏指向基类的指针,我们称之为vptr。vptr在创建类实例时自动设置,以便指向该类的虚拟表。与this指针不同,this指针实际上是编译器用来解析自引用的函数参数,vptr是一个真正的指针。
因此,它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承,这很重要。
## 2.实现与内部结构
下面我们来看自动与手动操纵vptr来获取地址与调用虚函数!
开始看代码之前,为了方便大家理解,这里给出调用图:

代码全部遵循标准的注释风格,相信大家看了就会明白,不明白的话,可以留言!
```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.
```
================================================
FILE: basic_content/vptr_vtable/vptr1.cpp
================================================
#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 << "=======================" << endl;
void *vptr_addr =
(void *)*(unsigned long *)obj; // 64位操作系统,占8字节,通过*(unsigned
// long *)obj取出前8字节,即vptr指针
printf("vptr_addr:%p\n", vptr_addr);
/**
* @brief 通过vptr指针访问virtual
* table,因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节,因此通过*(unsigned
* long *)vptr_addr取出前8字节, 后面加上偏移量就是每个函数的地址!
*/
void *func_addr = (void *)*((unsigned long *)vptr_addr + offset);
printf("func_addr:%p\n", func_addr);
return (Fun)func_addr;
}
int main(void) {
Base ptr;
Derived d;
Base *pt = new Derived(); // 基类指针指向派生类实例
Base &pp = ptr; // 基类引用指向基类实例
Base &p = d; // 基类引用指向派生类实例
cout << "基类对象直接调用" << endl;
ptr.fun1();
cout << "基类引用指向基类实例" << endl;
pp.fun1();
cout << "基类指针指向派生类实例并调用虚函数" << endl;
pt->fun1();
cout << "基类引用指向派生类实例并调用虚函数" << endl;
p.fun1();
// 手动查找vptr 和 vtable
Fun f1 = getAddr(pt, 0);
(*f1)();
Fun f2 = getAddr(pt, 1);
(*f2)();
delete pt;
return 0;
}
================================================
FILE: codingStyleIdioms/1_classInitializers/1.1_nest.cpp
================================================
/* 1.1_类之间嵌套.cpp */
//
// Created by light on 19-12-9.
//
#include
class Animal {
public:
Animal() {
std::cout << "Animal() is called" << std::endl;
}
Animal(const Animal &) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal &) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog {
public:
// 第一种: 使用初始化列表。
Dog(const Animal &animal) : __animal(animal) {
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
// 第二种:构造函数赋值来初始化对象。
// Dog(const Animal &animal) {
// __animal = animal;
// std::cout << "Dog(const Animal &animal) is called" << std::endl;
// }
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
private:
Animal __animal;
};
int main() {
Animal animal;
std::cout << std::endl;
Dog d(animal);
std::cout << std::endl;
return 0;
}
================================================
FILE: codingStyleIdioms/1_classInitializers/1.2_nodefault_ctor.cpp
================================================
/* 1.2_无默认构造的继承.cpp */
//
// Created by light on 19-12-9.
//
#include
class Animal {
public:
Animal(int age) {
std::cout << "Animal(int age) is called" << std::endl;
}
Animal(const Animal & animal) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal & amimal) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog : Animal {
public:
Dog(int age) : Animal(age) {
std::cout << "Dog(int age) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
};
int main() {
Animal animal(10);
std::cout << std::endl;
Dog d(100);
std::cout << std::endl;
return 0;
}
================================================
FILE: codingStyleIdioms/1_classInitializers/1.3_const.cpp
================================================
/* 1.3_类中const数据成员、引用数据成员.cpp */
//
// Created by light on 19-12-9.
//
#include
class Animal {
public:
Animal(int& age,std::string name):age_(age),name_(name) {
std::cout << "Animal(int age) is called" << std::endl;
}
private:
int &age_;
const std::string name_;
};
int main() {
int x = 10;
Animal animal(x,"hh");
return 0;
}
================================================
FILE: codingStyleIdioms/1_classInitializers/BUILD
================================================
# please run `bazel run //codingStyleIdioms/1_classInitializers:1.2_nodefault_ctor`
# please run `bazel run //codingStyleIdioms/1_classInitializers:initializer`
# please run `bazel run //codingStyleIdioms/1_classInitializers:1.1_nest`
# please run `bazel run //codingStyleIdioms/1_classInitializers:1.3_const`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "1.2_nodefault_ctor",
srcs = ["1.2_nodefault_ctor.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "initializer",
srcs = ["initializer.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "1.1_nest",
srcs = ["1.1_nest.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "1.3_const",
srcs = ["1.3_const.cpp"],
copts = ["-std=c++11"],
)
================================================
FILE: codingStyleIdioms/1_classInitializers/README.md
================================================
# 初始化列表与赋值
- const成员的初始化只能在构造函数初始化列表中进行
- 引用成员的初始化也只能在构造函数初始化列表中进行
- 对象成员(对象成员所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行
## 类之间嵌套
**第一种: 使用初始化列表。**
```cpp
class Animal {
public:
Animal() {
std::cout << "Animal() is called" << std::endl;
}
Animal(const Animal &) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal &) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog {
public:
Dog(const Animal &animal) : __animal(animal) {
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
private:
Animal __animal;
};
int main() {
Animal animal;
std::cout << std::endl;
Dog d(animal);
std::cout << std::endl;
return 0;
}
```
运行结果:
```cpp
Animal() is called
Animal (const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
```
依次分析从上到下:
main函数中`Animal animal;`调用默认构造。
`Dog d(animal);`等价于:
```
Animal __animal = animal;
```
实际上就是调用了拷贝构造,因此输出了:
```
Animal (const Animal &) is called
```
再然后打印Dog的构造函数里面的输出。
最后调用析构,程序结束。
**第二种:构造函数赋值来初始化对象。**
构造函数修改如下:
```cpp
Dog(const Animal &animal) {
__animal = animal;
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
```
此时输出结果:
```
Animal() is called
Animal() is called
Animal & operator=(const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
```
于是得出:
当调用`Dog d(animal);`时,等价于:
先定义对象,再进行赋值,因此先调用了默认构造,再调用=操作符重载函数。
```cpp
// 假设之前已经有了animal对象
Animal __animal;
__animal = animal;
```
> 小结
通过上述我们得出如下结论:
- **类中包含其他自定义的class或者struct,采用初始化列表,实际上就是创建对象同时并初始化**
- **而采用类中赋值方式,等价于先定义对象,再进行赋值,一般会先调用默认构造,在调用=操作符重载函数。**
## 无默认构造函数的继承关系中
现考虑把上述的关系改为继承,并修改Animal与Dog的构造函数,如下代码:
```cpp
class Animal {
public:
Animal(int age) {
std::cout << "Animal(int age) is called" << std::endl;
}
Animal(const Animal & animal) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal & amimal) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog : Animal {
public:
Dog(int age) : Animal(age) {
std::cout << "Dog(int age) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
};
```
上述是通过初始化列表给基类带参构造传递参数,如果不通过初始化列表传递,会发生什么影响?
去掉初始化列表
```
Dog(int age) {
std::cout << "Dog(int age) is called" << std::endl;
}
```
运行程序:
```
error: no matching function for call to ‘Animal::Animal()’
```
由于在Animal中没有默认构造函数,所以报错,遇到这种问题属于灾难性的,我们应该尽量避免,可以通过初始化列表给基类的构造初始化。
## 类中const数据成员、引用数据成员
特别是引用数据成员,必须用初始化列表初始化,而不能通过赋值初始化!
例如:在上述的Animal中添加私有成员,并修改构造函数:
```cpp
class Animal {
public:
Animal(int age,std::string name) {
std::cout << "Animal(int age) is called" << std::endl;
}
private:
int &age_;
const std::string name_;
};
```
报下面错误:
```cpp
error: uninitialized reference member in ‘int&’
```
应该改为下面:
```cpp
Animal(int age, std::string name) : age_(age), name_(name) {
std::cout << "Animal(int age) is called" << std::endl;
}
```
================================================
FILE: codingStyleIdioms/1_classInitializers/initializer.cpp
================================================
/* initializer.cpp */
//
// Created by light on 19-12-9.
//
#include
using namespace std;
class A {
public:
A(int a) : _a(a), _p(nullptr) { // 初始化列表
}
private:
int _a;
int *_p;
};
int main() {
A aa(10);
return 0;
}
================================================
FILE: codingStyleIdioms/2_enumclass/BUILD
================================================
# please run `bazel run //codingStyleIdioms/2_enumclass:namespace`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "namespace",
srcs = ["namespace.cpp"],
copts = ["-std=c++11"],
)
================================================
FILE: codingStyleIdioms/2_enumclass/README.md
================================================
# C++惯用法之enum class
在Effective modern C++中Item 10: Prefer scoped enums to unscoped enum,调到要用有范围的enum class代替无范围的enum。
例如:
```cpp
enum Shape {circle,retangle};
auto circle = 10; // error
```
上述错误是因为两个circle在同一范围。
对于enum等价于:
```cpp
#define circle 0
#define retangle 1
```
因此后面再去定义circle就会出错。
所以不管枚举名是否一样,里面的成员只要有一致的,就会出问题。
例如:
```cpp
enum A {a,b};
enum B {c,a};
```
a出现两次,在enum B的a处报错。
根据前面我们知道,enum名在范围方面没有什么作用,因此我们想到了namespace,如下例子:
```cpp
// 在创建枚举时,将它们放在名称空间中,以便可以使用有意义的名称访问它们:
namespace EntityType {
enum Enum {
Ground = 0,
Human,
Aerial,
Total
};
}
void foo(EntityType::Enum entityType)
{
if (entityType == EntityType::Ground) {
/*code*/
}
}
```
将命名空间起的有意思点,就可以达到想要的效果。
但是不断的使用命名空间,势必太繁琐,而且如果我不想使用namespace,要达到这样的效果,便会变得不安全,也没有约束。
因此在c++11后,引入enum class。
enum class 解决了为enum成员定义类型、类型安全、约束等问题。
回到上述例子:
```cpp
// enum class
enum class EntityType {
Ground = 0,
Human,
Aerial,
Total
};
void foo(EntityType entityType)
{
if (entityType == EntityType::Ground) {
/*code*/
}
}
```
这便是这一节要阐述的惯用法:enum class。
================================================
FILE: codingStyleIdioms/2_enumclass/namespace.cpp
================================================
//
// Created by light on 19-12-9.
//
#include
using namespace std;
// 在创建枚举时,将它们放在名称空间中,以便可以使用有意义的名称访问它们:
namespace EntityType {
enum Enum {
Ground = 0,
Human,
Aerial,
Total
};
}
void foo(EntityType::Enum entityType)
{
if (entityType == EntityType::Ground) {
/*code*/
}
}
// enum class
enum class EntityType1 {
Ground = 0,
Human,
Aerial,
Total
};
void foo(EntityType1 entityType)
{
if (entityType == EntityType1::Ground) {
/*code*/
}
}
int main() {
return 0;
}
================================================
FILE: codingStyleIdioms/3_RAII/BUILD
================================================
# please run `bazel run //codingStyleIdioms/3_RAII:RAII_fstram`
# please run `bazel run //codingStyleIdioms/3_RAII:c++_example1`
# please run `bazel run //codingStyleIdioms/3_RAII:c++_example`
# please run `bazel run //codingStyleIdioms/3_RAII:RAII`
# please run `bazel run //codingStyleIdioms/3_RAII:c_example`
# please run `bazel run //codingStyleIdioms/3_RAII:c++_example2`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "RAII_fstram",
srcs = ["RAII_fstram.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "c++_example1",
srcs = ["c++_example1.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "c++_example",
srcs = ["c++_example.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "RAII",
srcs = ["RAII.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "c_example",
srcs = ["c_example.cpp"],
copts = ["-std=c++11"],
)
cc_binary(
name = "c++_example2",
srcs = ["c++_example2.cpp"],
copts = ["-std=c++11"],
)
================================================
FILE: codingStyleIdioms/3_RAII/RAII.cpp
================================================
#include
#include
#include
using namespace std;
// RAII 资源获取即初始化,例1
enum class shape_type {
circle,
triangle,
rectangle,
};
class shape {
public:
shape() { cout << "shape" << endl; }
virtual void print() {
cout << "I am shape" << endl;
}
virtual ~shape() {}
};
class circle : public shape {
public:
circle() { cout << "circle" << endl; }
void print() {
cout << "I am circle" << endl;
}
};
class triangle : public shape {
public:
triangle() { cout << "triangle" << endl; }
void print() {
cout << "I am triangle" << endl;
}
};
class rectangle : public shape {
public:
rectangle() { cout << "rectangle" << endl; }
void print() {
cout << "I am rectangle" << endl;
}
};
// 利用多态 上转 如果返回值为shape,会存在对象切片问题。
shape *create_shape(shape_type type) {
switch (type) {
case shape_type::circle:
return new circle();
case shape_type::triangle:
return new triangle();
case shape_type::rectangle:
return new rectangle();
}
}
class shape_wrapper {
public:
explicit shape_wrapper(shape *ptr = nullptr) : ptr_(ptr) {}
~shape_wrapper() {
delete ptr_;
}
shape *get() const {
return ptr_;
}
private:
shape *ptr_;
};
void foo() {
shape_wrapper ptr(create_shape(shape_type::circle));
ptr.get()->print();
}
int main() {
// 第一种方式
shape *sp = create_shape(shape_type::circle);
sp->print();
delete sp;
// 第二种方式 RAII
foo();
return 0;
}
================================================
FILE: codingStyleIdioms/3_RAII/RAII.md
================================================
# C++惯用法之消除垃圾收集器-资源获取即初始化方法(RAII)
## 0.导语
在C语言中,有三种类型的内存分配:静态、自动和动态。静态变量是嵌入在源文件中的常数,因为它们有已知的大小并且从不改变,所以它们并不那么有趣。自动分配可以被认为是堆栈分配——当一个词法块进入时分配空间,当该块退出时释放空间。它最重要的特征与此直接相关。在C99之前,自动分配的变量需要在编译时知道它们的大小。这意味着任何字符串、列表、映射以及从这些派生的任何结构都必须存在于堆中的动态内存中。
程序员使用四个基本操作明确地分配和释放动态内存:malloc、realloc、calloc和free。前两个不执行任何初始化,内存可能包含碎片。除了自由,他们都可能失败。在这种情况下,它们返回一个空指针,其访问是未定义的行为;在最好的情况下,你的程序会崩溃。在最坏的情况下,你的程序看起来会工作一段时间,在崩溃前处理垃圾数据。
例如:
```cpp
int main() {
char *str = (char *) malloc(7);
strcpy(str, "toptal");
printf("char array = \"%s\" @ %u\n", str, str);
str = (char *) realloc(str, 11);
strcat(str, ".com");
printf("char array = \"%s\" @ %u\n", str, str);
free(str);
return(0);
}
```
输出:
```cpp
char array = "toptal" @ 2762894960
char array = "toptal.com" @ 2762894960
```
尽管代码很简单,但它已经包含了一个反模式和一个有问题的决定。在现实生活中,你不应该直接写字节数,而应该使用sizeof函数。类似地,我们将char *数组精确地分配给我们需要的字符串大小的两倍(比字符串长度多一倍,以说明空终止),这是一个相当昂贵的操作。一个更复杂的程序可能会构建一个更大的字符串缓冲区,允许字符串大小增长。
## 1.RAII的发明:新希望
至少可以说,所有手动管理都是令人不快的。 在80年代中期,Bjarne Stroustrup为他的全新语言C ++发明了一种新的范例。 他将其称为“资源获取就是初始化”,其基本见解如下:**可以指定对象具有构造函数和析构函数,这些构造函数和析构函数在适当的时候由编译器自动调用,这为管理给定对象的内存提供了更为方便的方法。** 需要,并且该技术对于不是内存的资源也很有用。
意味着上面的例子在c++中更简洁:
```cpp
int main() {
std::string str = std::string ("toptal");
std::cout << "string object: " << str << " @ " << &str << "\n";
str += ".com";
std::cout << "string object: " << str << " @ " << &str << "\n";
return(0);
}
```
输出:
```cpp
string object: toptal @ 0x7fffa67b9400
string object: toptal.com @ 0x7fffa67b9400
```
在上述例子中,我们没有手动内存管理!构造string对象,调用重载方法,并在函数退出时自动销毁。不幸的是,同样的简单也会导致其他问题。让我们详细地看一个例子:
```cpp
vector read_lines_from_file(string &file_name) {
vector lines;
string line;
ifstream file_handle (file_name.c_str());
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines.push_back(line);
}
file_handle.close();
return lines;
}
int main(int argc, char* argv[]) {
// get file name from the first argument
string file_name (argv[1]);
int count = read_lines_from_file(file_name).size();
cout << "File " << file_name << " contains " << count << " lines.";
return 0;
}
```
输出:
```cpp
File makefile contains 37 lines.
```
这看起来很简单。`vector`被填满、返回和调用。然而,作为关心性能的高效程序员,这方面的一些问题困扰着我们:在return语句中,由于使用了值语义,`vector`在销毁之前不久就被复制到一个新`vector`中。
> 在现代C ++中,这不再是严格的要求了。 C ++ 11引入了移动语义的概念,其中将原点保留在有效状态(以便仍然可以正确销毁)但未指定状态。 对于编译器而言,返回调用是最容易优化以优化语义移动的情况,因为它知道在进行任何进一步访问之前不久将销毁源。 但是,该示例的目的是说明为什么人们在80年代末和90年代初发明了一大堆垃圾收集的语言,而在那个时候C ++ move语义不可用。
对于数据量比较大的文件,这可能会变得昂贵。 让我们对其进行优化,只返回一个指针。 语法进行了一些更改,但其他代码相同:
```cpp
vector * read_lines_from_file(string &file_name) {
vector * lines;
string line;
ifstream file_handle (file_name.c_str());
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines->push_back(line);
}
file_handle.close();
return lines;
}
int main(int argc, char* argv[]) {
// get file name from the first argument
string file_name (argv[1]);
int count = read_lines_from_file(file_name).size();
cout << "File " << file_name << " contains " << count << " lines.";
return 0;
}
```
输出:
```
Segmentation fault (core dumped)
```
程序崩溃!我们只需要将上述的`lines`进行内存分配:
```cpp
vector * lines = new vector;
```
这样就可以运行了!
不幸的是,尽管这看起来很完美,但它仍然有一个缺陷:它会泄露内存。在C++中,指向堆的指针在不再需要后必须手动删除;否则,一旦最后一个指针超出范围,该内存将变得不可用,并且直到进程结束时操作系统对其进行管理后才会恢复。惯用的现代C++将在这里使用`unique_ptr`,它实现了期望的行为。它删除指针超出范围时指向的对象。然而,这种行为直到C++11才成为语言的一部分。
在这里,可以直接使用C++11之前的语法,只是把main中改一下即可:
```cpp
vector * read_lines_from_file(string &file_name) {
vector * lines = new vector;
string line;
ifstream file_handle (file_name.c_str());
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines->push_back(line);
}
file_handle.close();
return lines;
}
int main(int argc, char* argv[]) {
// get file name from the first argument
string file_name (argv[1]);
vector * file_lines = read_lines_from_file(file_name);
int count = file_lines->size();
delete file_lines;
cout << "File " << file_name << " contains " << count << " lines.";
return 0;
}
```
手动去分配内存与释放内存。
**不幸的是,随着程序扩展到上述范围之外,很快就变得更加难以推理指针应该在何时何地被删除。当一个函数返回指针时,你现在拥有它吗?您应该在完成后自己删除它,还是它属于某个稍后将被一次性释放的数据结构?一方面出错,内存泄漏,另一方面出错,你已经破坏了正在讨论的数据结构和其他可能的数据结构,因为它们试图取消引用现在不再有效的指针。**
## 2.“使用垃圾收集器,flyboy!”
垃圾收集器不是一项新技术。 它们由John McCarthy在1959年为Lisp发明。 1980年,随着Smalltalk-80的出现,垃圾收集开始成为主流。 但是,1990年代代表了该技术的真正发芽:在1990年至2000年之间,发布了多种语言,所有语言都使用一种或另一种垃圾回收:Haskell,Python,Lua,Java,JavaScript,Ruby,OCaml 和C#是最著名的。
什么是垃圾收集? 简而言之,这是一组用于自动执行手动内存管理的技术。 它通常作为具有手动内存管理的语言(例如C和C ++)的库提供,但在需要它的语言中更常用。 最大的优点是程序员根本不需要考虑内存。 都被抽象了。 例如,相当于我们上面的文件读取代码的Python就是这样:
```python
def read_lines_from_file(file_name):
lines = []
with open(file_name) as fp:
for line in fp:
lines.append(line)
return lines
if __name__ == '__main__':
import sys
file_name = sys.argv[1]
count = len(read_lines_from_file(file_name))
print("File {} contains {} lines.".format(file_name, count))
```
行数组是在第一次分配给它时出现的,并且不复制到调用范围就返回。 由于时间不确定,它会在超出该范围后的某个时间被垃圾收集器清理。 有趣的是,在Python中,用于非内存资源的RAII不是惯用语言。 允许-我们可以简单地编写`fp = open(file_name)`而不是使用with块,然后让GC清理。 但是建议的模式是在可能的情况下使用上下文管理器,以便可以在确定的时间释放它们。
尽管简化了内存管理,但要付出很大的代价。 在引用计数垃圾回收中,所有变量赋值和作用域出口都会获得少量成本来更新引用。 在标记清除系统中,在GC清除内存的同时,所有程序的执行都以不可预测的时间间隔暂停。 这通常称为世界停止事件。 同时使用这两种系统的Python之类的实现都会受到两种惩罚。 这些问题降低了垃圾收集语言在性能至关重要或需要实时应用程序的情况下的适用性。 即使在以下玩具程序上,也可以看到实际的性能下降:
```cpp
$ make cpp && time ./c++ makefile
g++ -o c++ c++.cpp
File makefile contains 38 lines.
real 0m0.016s
user 0m0.000s
sys 0m0.015s
$ time python3 python3.py makefile
File makefile contains 38 lines.
real 0m0.041s
user 0m0.015s
sys 0m0.015s
```
Python版本的实时时间几乎是C ++版本的三倍。 尽管并非所有这些差异都可以归因于垃圾收集,但它仍然是可观的。
## 3.所有权:RAII觉醒
我们知道对象的生存期由其范围决定。 但是,有时我们需要创建一个对象,该对象与创建对象的作用域无关,这是有用的,或者很有用。 在C ++中,运算符new用于创建这样的对象。 为了销毁对象,可以使用运算符delete。 由new操作员创建的对象是动态分配的,即在动态内存(也称为堆或空闲存储)中分配。 因此,由new创建的对象将继续存在,直到使用delete将其明确销毁为止。
使用new和delete时可能发生的一些错误是:
- 对象(或内存)泄漏:使用new分配对象,而忘记删除该对象。
- 过早删除(或悬挂引用):持有指向对象的另一个指针,删除该对象,然而还有其他指针在引用它。
- 双重删除:尝试两次删除一个对象。
通常,范围变量是首选。 但是,RAII可以用作new和delete的替代方法,以使对象独立于其范围而存在。 这种技术包括将指针分配到在堆上分配的对象,并将其放在句柄/管理器对象中。 后者具有一个析构函数,将负责销毁该对象。 这将确保该对象可用于任何想要访问它的函数,并且该对象在句柄对象的生存期结束时将被销毁,而无需进行显式清理。
来自C ++标准库的使用RAII的示例为std :: string和std :: vector。
考虑这段代码:
```cpp
void fn(const std::string& str)
{
std::vector vec;
for (auto c : str)
vec.push_back(c);
// do something
}
```
当创建`vector`,并将元素推入`vector`时,您不必担心分配和取消分配此类元素内存。 `vector`使用new为其堆上的元素分配空间,并使用delete释放该空间。 作为vector的用户,您无需关心实现细节,并且会相信vector不会泄漏。 在这种情况下,向量是其元素的句柄对象。
标准库中使用RAII的其他示例是std :: shared_ptr,std :: unique_ptr和std :: lock_guard。
该技术的另一个名称是SBRM,是范围绑定资源管理的缩写。
现在,我们将上述读取文件例子,进行修改:
```cpp
#include
#include
#include
#include
#include
using namespace std;
unique_ptr> read_lines_from_file(string &file_name) {
unique_ptr> lines(new vector);
string line;
ifstream file_handle (file_name.c_str());
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines->push_back(line);
}
file_handle.close();
return lines;
}
int main(int argc, char* argv[]) {
// get file name from the first argument
string file_name (argv[1]);
int count = read_lines_from_file(file_name).get()->size();
cout << "File " << file_name << " contains " << count << " lines.";
return 0;
}
```
## 4.只有在最后,你才意识到RAII的真正力量。
自从编译器发明以来,手动内存管理是程序员一直在想办法避免的噩梦。 RAII是一种很有前途的模式,但由于没有一些奇怪的解决方法,它根本无法用于堆分配的对象,因此在C ++中会受到影响。 因此,在90年代出现了垃圾收集语言的爆炸式增长,旨在使程序员生活更加愉快,即使以性能为代价。
最后,RAII总结如下:
- 资源在析构函数中被释放
- 该类的实例是堆栈分配的
- 资源是在构造函数中获取的。
RAII代表“资源获取是初始化”。
常见的例子有:
- 文件操作
- 智能指针
- 互斥量
## 5.参考文章
> 1.https://www.toptal.com/software/eliminating-garbage-collector#remote-developer-job
> 2.https://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii
================================================
FILE: codingStyleIdioms/3_RAII/RAII_fstram.cpp
================================================
//
// Created by light on 19-12-9.
//
#include
#include
// RAII 资源获取即初始化,例2
// C ++保证在对象超出范围时调用析构函数,而不管控制如何离开该范围。
// 即使抛出异常,所有本地对象也会超出范围,因此它们的相关资源将被清除。
void foo() {
std::fstream file("bar.txt"); // open a file "bar.txt"
if (rand() % 2) {
// if this exception is thrown, we leave the function, and so
// file's destructor is called, which closes the file handle.
throw std::exception();
}
// if the exception is not called, we leave the function normally, and so
// again, file's destructor is called, which closes the file handle.
}
int main() {
try {
foo();
} catch (std::exception) {
puts("exception!");
}
}
================================================
FILE: codingStyleIdioms/3_RAII/c++_example.cpp
================================================
//
// Created by light on 19-12-12.
//
#include
int main() {
std::string str = std::string ("toptal");
std::cout << "string object: " << str << " @ " << &str << "\n";
str += ".com";
std::cout << "string object: " << str << " @ " << &str << "\n";
return(0);
}
================================================
FILE: codingStyleIdioms/3_RAII/c++_example1.cpp
================================================
//
// Created by light on 19-12-12.
//
#include
#include
#include
#include
using namespace std;
vector read_lines_from_file(string &file_name) {
vector lines;
string line;
ifstream file_handle (file_name.c_str());
// file_handle.peek()!=EOF 解决多读一行问题
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines.push_back(line);
}
file_handle.close();
return lines;
}
vector * read_lines_from_file1(string &file_name) {
vector * lines;
string line;
ifstream file_handle (file_name.c_str());
// file_handle.peek()!=EOF
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines->push_back(line);
}
file_handle.close();
return lines;
}
vector * read_lines_from_file1_1(string &file_name) {
vector * lines=new vector;
string line;
ifstream file_handle (file_name.c_str());
while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {
getline(file_handle, line);
lines->push_back(line);
}
file_handle.close();
return lines;
}
int main() {
// get file name from the first argument
string file_name ("/home/light/CLionProjects/Morden_C++/CMakeLists.txt");
int count = read_lines_from_file(file_name).size();
cout << "File " << file_name << " contains " << count << " lines.";
cout<size();
// cout << "File " << file_name << " contains " << count1 << " lines.";
string file_name1 ("/home/light/CLionProjects/Morden_C++/CMakeLists.txt");
int count1 = read_lines_from_file1_1(file_name1)->size();
cout << "File " << file_name << " contains " << count1 << " lines.";
return 0;
}
================================================
FILE: codingStyleIdioms/3_RAII/c++_example2.cpp
================================================
//
// Created by light on 19-12-12.
//
#include
#include
#include
#include
#include
using namespace std;
unique_ptr> read_lines_from_file(string &file_name) {
unique_ptr> lines(new vector);
string line;
ifstream file_handle (file_name.c_str());
while (file_handle.good() && !file_handle.eof()) {
getline(file_handle, line);
lines->push_back(line);
}
file_handle.close();
return lines;
}
int main() {
// get file name from the first argument
string file_name ("/home/light/CLionProjects/Morden_C++/CMakeLists.txt");
int count = read_lines_from_file(file_name).get()->size();
cout << "File " << file_name << " contains " << count << " lines.";
cout<
#include
#include
int main() {
char *str = (char *) malloc(7);
strcpy(str, "toptal");
printf("char array = \"%s\" @ %u\n", str, str);
str = (char *) realloc(str, 11);
strcat(str, ".com");
printf("char array = \"%s\" @ %u\n", str, str);
free(str);
return(0);
}
================================================
FILE: codingStyleIdioms/4_copy-swap/BUILD
================================================
# please run `bazel run //codingStyleIdioms/4_copy-swap:copy-swapAndADL`
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "copy-swapAndADL",
srcs = ["copy-swapAndADL.cpp"],
copts = ["-std=c++11"],
)
================================================
FILE: codingStyleIdioms/4_copy-swap/README.md
================================================
> 为什么我们需要复制和交换习惯?
任何管理资源的类(包装程序,如智能指针)都需要实现big three。尽管拷贝构造函数和析构函数的目标和实现很简单。
但是复制分配运算符无疑是最细微和最困难的。
> 应该怎么做?需要避免什么陷阱?
copy-swap是解决方案,可以很好地协助赋值运算符实现两件事:避免代码重复,并提供强大的**异常保证**。
>它是如何工作的?
**从概念上讲,它通过使用拷贝构造函数的功能来创建数据的本地副本,然后使用交换功能获取复制的数据,将旧数据与新数据交换来工作。然后,临时副本将销毁,并随身携带旧数据。我们剩下的是新数据的副本。**
为了使用copy-swap,我们需要三件事:
- 一个有效的拷贝构造函数
- 一个有效的析构函数(两者都是任何包装程序的基础,因此无论如何都应完整)以及交换功能。
交换函数是一种不抛异常函数,它交换一个类的两个对象或者成员。我们可能很想使用std :: swap而不是提供我们自己的方法,但这是不可能的。 std :: swap在实现中使用了copy-constructor和copy-assignment运算符,我们最终将尝试根据自身定义赋值运算符!
(不仅如此,对swap的无条件调用将使用我们的自定义swap运算符,从而跳过了std :: swap会导致的不必要的类构造和破坏。)
具体例子如下:
```cpp
namespace A {
template
class smart_ptr {
public:
smart_ptr() noexcept : ptr_(new T()) {
}
smart_ptr(const T &ptr) noexcept : ptr_(new T(ptr)) {
}
smart_ptr(smart_ptr &rhs) noexcept {
ptr_ = rhs.release(); // 释放所有权,此时rhs的ptr_指针为nullptr
}
void swap(smart_ptr &rhs) noexcept { // noexcept == throw() 保证不抛出异常
using std::swap;
swap(ptr_, rhs.ptr_);
}
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
T *get() const noexcept {
return ptr_;
}
private:
T *ptr_;
};
// 提供一个非成员swap函数for ADL(Argument Dependent Lookup)
template
void swap(A::smart_ptr &lhs, A::smart_ptr &rhs) noexcept {
lhs.swap(rhs);
}
}
// 注释开启,会引发ADL冲突
//namespace std {
// // 提供一个非成员swap函数for ADL(Argument Dependent Lookup)
// template
// void swap(A::smart_ptr &lhs, A::smart_ptr &rhs) noexcept {
// lhs.swap(rhs);
// }
//
//}
int main() {
using std::swap;
A::smart_ptr s1("hello"), s2("world");
// 交换前
std::cout << *s1.get() << " " << *s2.get() << std::endl;
swap(s1, s2); // 这里swap 能够通过Koenig搜索或者说ADL根据s1与s2的命名空间来查找swap函数
// 交换后
std::cout << *s1.get() << " " << *s2.get() << std::endl;
s1=s2;
}
```
现在为了让上述的`s1=s2`完成工作,必须实现赋值运算符。
> 方法1
为了避免自赋值,通常采用下面写法 。
不好!
不具备异常安全,只具备自我赋值安全性
```cpp
smart_ptr &operator=(const smart_ptr &rhs) {
if (*this != rhs) {
delete ptr_;
ptr_ = new T(rhs.ptr_); // 当new 发生异常,此时ptr_指向的而是一块被删除区域,而不是被赋值对象的区域
return *this;
}
return *this;
}
```
> 方法2
如果new出现异常,ptr_会保持原装! 也可以处理自我赋值! 还是不够好!
**这样就会导致代码膨胀,于是导致了另一个问题:代码冗余**
```cpp
// 方法2:如果new出现异常,ptr_会保持原装! 也可以处理自我赋值! 还是不够好!
smart_ptr &operator=(const smart_ptr &rhs) {
T *origin = ptr_;
ptr_ = new T(rhs.ptr_);
delete origin;
return *this;
}
```
> 方法3
copy and swap 很好!
```cpp
smart_ptr &operator=(smart_ptr &rhs) noexcept {
smart_ptr tmp(rhs);
swap(tmp);
return *this;
}
```
> 方法4
改为传值,同方法3!
```cpp
smart_ptr &operator=(smart_ptr rhs) noexcept {
swap(rhs);
return *this;
}
```
> C++11 move
我们在big three上加上move ctor与move assignment就构成了big five。
此时再次拓展上述的代码:
```cpp
// move ctor
smart_ptr(smart_ptr &&rhs) noexcept {
std::cout << "move ctor" << std::endl;
ptr_ = rhs.ptr_;
if (ptr_)
rhs.ptr_ = nullptr;
}
// move assignment
smart_ptr &operator=(smart_ptr &&rhs) noexcept {
std::cout << "move assignment" << std::endl;
smart_ptr tmp(rhs);
swap(rhs);
return *this;
}
```
**实际上,我们比那个不需要多写代码move assignment,copy-and-swap 技巧 和 move-and-swap 技巧是共享同一个函数的。**当copy构造为上述的方法4时,**对于C++ 11,编译器会依据参数是左值还是右值在拷贝构造函数和移动构造函数间进行选择:**
```cpp
smart_ptr &operator=(smart_ptr rhs) noexcept {
swap(rhs);
return *this;
}
```
所以当这个同上述写的
```cpp
smart_ptr &operator=(smart_ptr &&rhs) noexcept{}
```
同时存在,就会出现error: ambiguous overload for ‘operator=’ 。
调用处如下:
```cpp
A::smart_ptr s1("hello"), s2("world");
A::smart_ptr s3 = s1;
A::smart_ptr s4 = std::move(s1);
```
- 如果是 s3 = s1,这样就会调用拷贝构造函数来初始化other(因为s1是左值),赋值操作符会与新创建的对象交换数据,深度拷贝。这就是copy and swap 惯用法的定义:构造一个副本,与副本交换数据,并让副本在作用域内自动销毁。
- 如果是s4 = std::move(s1),这样就会调用移动构造函数来初始化rhs(因为std::move(s1)是右值),所以这里没有深度拷贝,只有高效的数据转移。
因此也可以称呼它为“统一赋值操作符”,因为它合并了"拷贝赋值"与"移动赋值"。
================================================
FILE: codingStyleIdioms/4_copy-swap/copy-swapAndADL.cpp
================================================
//
// Created by light on 19-12-9.
//
#include
// copy and swap : https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
// ADL : https://stackoverflow.com/questions/8111677/what-is-argument-dependent-lookup-aka-adl-or-koenig-lookup
namespace A {
template
class smart_ptr {
public:
smart_ptr() noexcept : ptr_(new T()) {
}
smart_ptr(const T &ptr) noexcept : ptr_(new T(ptr)) {
}
smart_ptr(smart_ptr &rhs) noexcept {
std::cout << "copy ctor" << std::endl;
ptr_ = rhs.release(); // 释放所有权,此时rhs的ptr_指针为nullptr
}
// 方法1:为了避免自赋值,通常采用下面写法 不好! 不具备异常安全,只具备自我赋值安全性
// smart_ptr &operator=(const smart_ptr &rhs) {
// if (*this != rhs) {
// delete ptr_;
// ptr_ = new T(rhs.ptr_); // 当new 发生异常,此时ptr_指向的而是一块被删除区域,而不是被赋值对象的区域
// return *this;
// }
// return *this;
// }
// 方法2:如果new出现异常,ptr_会保持原装! 也可以处理自我赋值! 还是不够好!
// smart_ptr &operator=(const smart_ptr &rhs) {
// T *origin = ptr_;
// ptr_ = new T(rhs.ptr_);
// delete origin;
// return *this;
// }
// 方法3:copy and swap 很好!
// smart_ptr &operator=(smart_ptr &rhs) noexcept {
// smart_ptr tmp(rhs);
// swap(tmp);
// return *this;
// }
// 方法4:同方法3,改为传值
// 既适用于copy ctor也适用于 move ctor
smart_ptr &operator=(smart_ptr rhs) noexcept {
swap(rhs);
return *this;
}
// move ctor
smart_ptr(smart_ptr &&rhs) noexcept {
std::cout << "move ctor" << std::endl;
ptr_ = rhs.ptr_;
if (ptr_)
rhs.ptr_ = nullptr;
}
// move assignment
// smart_ptr &operator=(smart_ptr &&rhs) noexcept {
// std::cout << "move assignment" << std::endl;
// smart_ptr tmp(rhs);
// swap(rhs);
// return *this;
// }
void swap(smart_ptr &rhs) noexcept { // noexcept == throw() 保证不抛出异常
using std::swap;
swap(ptr_, rhs.ptr_);
}
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
T *get() const noexcept {
return ptr_;
}
private:
T *ptr_;
};
// 提供一个非成员swap函数for ADL(Argument Dependent Lookup)
template
void swap(A::smart_ptr &lhs, A::smart_ptr &rhs) noexcept {
lhs.swap(rhs);
}
}
// 注释开启,会引发ADL冲突
//namespace std {
// // 提供一个非成员swap函数for ADL(Argument Dependent Lookup)
// template
// void swap(A::smart_ptr &lhs, A::smart_ptr &rhs) noexcept {
// lhs.swap(rhs);
// }
//
//}
int main() {
using std::swap;
A::smart_ptr