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 个人公众号: ![](./img/wechat.jpg) ================================================ 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 ================================================ # 纯虚函数和抽象类 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ================================================ ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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++的面向对象特性 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ================================================ ## 关于作者 微信公众号: ![](../img/wechat.jpg) ## 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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 传统行为 枚举有如下问题: - 作用域不受限,会容易引起命名冲突。例如下面无法编译通过的: ```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(显式)关键字那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) - 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" ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ================================================ # 友元函数与友元类 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ================================================ # 宏那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ================================================ # :: 范围解析运算符那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) - 全局作用域符(::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 ================================================ # 引用与指针那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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那些事 ## 关于作者 微信公众号: ![](../img/wechat.jpg) 当与不同类型一起使用时,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 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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区别 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) 关于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指针那些事 ## 关于作者 微信公众号: ![](../img/wechat.jpg) ## 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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) 联合(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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 基本使用 局部与全局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那些事 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ================================================ ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 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 ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 1.基础理论 为了实现虚函数,C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称,例如“vtable”,“虚函数表”,“虚方法表”或“调度表”。 虚拟表实际上非常简单,虽然用文字描述有点复杂。首先,**每个使用虚函数的类(或者从使用虚函数的类派生)都有自己的虚拟表**。该表只是编译器在编译时设置的静态数组。虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针,指向该类可访问的派生函数。 其次,编译器还会添加一个隐藏指向基类的指针,我们称之为vptr。vptr在创建类实例时自动设置,以便指向该类的虚拟表。与this指针不同,this指针实际上是编译器用来解析自引用的函数参数,vptr是一个真正的指针。 因此,它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承,这很重要。 ## 2.实现与内部结构 下面我们来看自动与手动操纵vptr来获取地址与调用虚函数! 开始看代码之前,为了方便大家理解,这里给出调用图: ![base](./img/base.jpg) 代码全部遵循标准的注释风格,相信大家看了就会明白,不明白的话,可以留言! ```c++ /** * @file vptr1.cpp * @brief C++虚函数vptr和vtable * 编译:g++ -g -o vptr vptr1.cpp -std=c++11 * @author 光城 * @version v1 * @date 2019-07-20 */ #include #include using namespace std; /** * @brief 函数指针 */ typedef void (*Fun)(); /** * @brief 基类 */ class Base { public: Base(){}; virtual void fun1() { cout << "Base::fun1()" << endl; } virtual void fun2() { cout << "Base::fun2()" << endl; } virtual void fun3(){} ~Base(){}; }; /** * @brief 派生类 */ class Derived: public Base { public: Derived(){}; void fun1() { cout << "Derived::fun1()" << endl; } void fun2() { cout << "DerivedClass::fun2()" << endl; } ~Derived(){}; }; /** * @brief 获取vptr地址与func地址,vptr指向的是一块内存,这块内存存放的是虚函数地址,这块内存就是我们所说的虚表 * * @param obj * @param offset * * @return */ Fun getAddr(void* obj,unsigned int offset) { cout<<"======================="<fun1(); cout<<"基类引用指向派生类实例并调用虚函数"<fun1(); ``` 其过程为:首先程序识别出fun1()是个虚函数,其次程序使用pt->vptr来获取Derived的虚拟表。第三,它查找Derived虚拟表中调用哪个版本的fun1()。这里就可以发现调用的是Derived::fun1()。因此pt->fun1()被解析为Derived::fun1()! 除此之外,上述代码大家会看到,也包含了手动获取vptr地址,并调用vtable中的函数,那么我们一起来验证一下上述的地址与真正在自动调用vtable中的虚函数,比如上述`pt->fun1()`的时候,是否一致! 这里采用gdb调试,在编译的时候记得加上`-g`。 通过`gdb vptr`进入gdb调试页面,然后输入`b Derived::fun1`对fun1打断点,然后通过输入r运行程序到断点处,此时我们需要查看调用栈中的内存地址,通过`disassemable fun1`可以查看当前有关fun1中的相关汇编代码,我们看到了`0x0000000000400ea8`,然后再对比上述的结果会发现与手动调用的fun1一致,fun2类似,以此证明代码正确! gdb调试信息如下: ```c++ (gdb) b Derived::fun1 Breakpoint 1 at 0x400eb4: file vptr1.cpp, line 23. (gdb) r Starting program: /home/light/Program/CPlusPlusThings/virtual/pure_virtualAndabstract_class/vptr 基类对象直接调用 Base::fun1() 基类引用指向派生类实例 Base::fun1() 基类指针指向派生类实例并调用虚函数 Breakpoint 1, Derived::fun1 (this=0x614c20) at vptr1.cpp:23 23 cout << "Derived::fun1()" << endl; (gdb) disassemble fun1 Dump of assembler code for function Derived::fun1(): 0x0000000000400ea8 <+0>: push %rbp 0x0000000000400ea9 <+1>: mov %rsp,%rbp 0x0000000000400eac <+4>: sub $0x10,%rsp 0x0000000000400eb0 <+8>: mov %rdi,-0x8(%rbp) => 0x0000000000400eb4 <+12>: mov $0x401013,%esi 0x0000000000400eb9 <+17>: mov $0x602100,%edi 0x0000000000400ebe <+22>: callq 0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x0000000000400ec3 <+27>: mov $0x400a00,%esi 0x0000000000400ec8 <+32>: mov %rax,%rdi 0x0000000000400ecb <+35>: callq 0x4009f0 <_ZNSolsEPFRSoS_E@plt> 0x0000000000400ed0 <+40>: nop 0x0000000000400ed1 <+41>: leaveq 0x0000000000400ed2 <+42>: retq End of assembler dump. (gdb) disassemble fun2 Dump of assembler code for function Derived::fun2(): 0x0000000000400ed4 <+0>: push %rbp 0x0000000000400ed5 <+1>: mov %rsp,%rbp 0x0000000000400ed8 <+4>: sub $0x10,%rsp 0x0000000000400edc <+8>: mov %rdi,-0x8(%rbp) 0x0000000000400ee0 <+12>: mov $0x401023,%esi 0x0000000000400ee5 <+17>: mov $0x602100,%edi 0x0000000000400eea <+22>: callq 0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x0000000000400eef <+27>: mov $0x400a00,%esi 0x0000000000400ef4 <+32>: mov %rax,%rdi 0x0000000000400ef7 <+35>: callq 0x4009f0 <_ZNSolsEPFRSoS_E@plt> 0x0000000000400efc <+40>: nop 0x0000000000400efd <+41>: leaveq 0x0000000000400efe <+42>: retq End of assembler dump. ``` ================================================ 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 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; A::smart_ptr s3 = s1; A::smart_ptr s4 = std::move(s1); } ================================================ FILE: codingStyleIdioms/5_pImpl/BUILD ================================================ # please run `bazel run //codingStyleIdioms/5_pImpl:pimpl` # please run `bazel run //codingStyleIdioms/5_pImpl:pimplTime` # please run `bazel run //codingStyleIdioms/5_pImpl:noPimpl` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "pimpl", srcs = ["pimpl.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "pimplTime", srcs = ["pimplTime.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "noPimpl", srcs = ["noPimpl.cpp"], copts = ["-std=c++11"], ) ================================================ FILE: codingStyleIdioms/5_pImpl/README.md ================================================ # C++惯用法之pImpl “指向实现的指针”或“pImpl”是一种 C++ 编程技巧,它将类的实现细节从对象表示中移除,放到一个分离的类中,并以一个不透明的指针进行访问。 使用pImpl惯用法的原因如下: 考虑如下例子: ```cpp class X { private: C c; D d; } ; ``` 变成pImpl就是下面这样子 ```cpp class X { private: struct XImpl; XImpl* pImpl; }; ``` CPP定义: ```cpp struct X::XImpl { C c; D d; }; ``` - 二进制兼容性 开发库时,可以在不破坏与客户端的二进制兼容性的情况下向XImpl添加/修改字段(这将导致崩溃!)。 由于在向Ximpl类添加新字段时X类的二进制布局不会更改,因此可以安全地在次要版本更新中向库添加新功能。 当然,您也可以在不破坏二进制兼容性的情况下向X / XImpl添加新的公共/私有非虚拟方法,但这与标准的标头/实现技术相当。 - 数据隐藏 如果您正在开发一个库,尤其是专有库,则可能不希望公开用于实现库公共接口的其他库/实现技术。 要么是由于知识产权问题,要么是因为您认为用户可能会被诱使对实现进行危险的假设,或者只是通过使用可怕的转换技巧来破坏封装。 PIMPL解决/缓解了这一难题。 - 编译时间 编译时间减少了,因为当您向XImpl类添加/删除字段和/或方法时(仅映射到标准技术中添加私有字段/方法的情况),仅需要重建X的源(实现)文件。 实际上,这是一种常见的操作。 使用标准的标头/实现技术(没有PIMPL),当您向X添加新字段时,曾经重新分配X(在堆栈或堆上)的每个客户端都需要重新编译,因为它必须调整分配的大小 。 好吧,每个从未分配X的客户端也都需要重新编译,但这只是开销(客户端上的结果代码是相同的)。 > https://stackoverflow.com/questions/8972588/is-the-pimpl-idiom-really-used-in-practices ================================================ FILE: codingStyleIdioms/5_pImpl/noPimpl.cpp ================================================ #include #include using namespace std; class C { vector v; string s; }; class D { string s; }; class X { private: C c; D d; }; int main() { X x; } ================================================ FILE: codingStyleIdioms/5_pImpl/pimpl.cpp ================================================ // // Created by light on 19-12-9. // #include using namespace std; // pImpl: Pointer-to-Implementation class private_foo; class foo { public: foo(); ~foo(); void bar(); private: private_foo *pImpl; }; class private_foo { public: void bar() { cout<<"private_foo invoke bar funciton."<bar(); } int main() { foo f; f.bar(); } ================================================ FILE: codingStyleIdioms/5_pImpl/pimplTime.cpp ================================================ #include #include using namespace std; class C { vector v; string s; }; class D { string s; }; class X { private: struct XImpl; XImpl* pImpl; }; struct X::XImpl { C c; D d; }; int main() { X x; } ================================================ FILE: codingStyleIdioms/5_pImpl/pimplTime.h ================================================ #include #include using namespace std; class C { vector v; string s; }; class D { string s; }; class X { private: struct XImpl; XImpl* pImpl; }; ================================================ FILE: codingStyleIdioms/README.md ================================================ # 你最喜欢的c++编程风格惯用法是什么? 在stackoverflow上找到了一篇文章,写的蛮好的,地址如下: > https://stackoverflow.com/questions/276173/what-are-your-favorite-c-coding-style-idioms#comment60171463_2034439 由于是英文的,且比较重要,于是总结成下面几条! - [1.类初始化列表](./1_classInitializers) - [2.枚举类替换命名空间](./2_enumclass_namespace) - [3.RAII(资源获取即初始化)](./3_RAII) - [4.copy and swap](./4_copy-swap) - [5.pImpl(指针指向具体实现)](./5_pImpl) ================================================ FILE: concurrency/Threading_In_CPlusPlus/1.thread/BUILD ================================================ # please run `bazel run //concurrency/Threading_In_CPlusPlus/1.thread:intro` # please run `bazel run //concurrency/Threading_In_CPlusPlus/1.thread:thread` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "intro", srcs = ["intro.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "thread", srcs = ["thread.cpp"], copts = ["-std=c++11"], ) ================================================ FILE: concurrency/Threading_In_CPlusPlus/1.thread/intro.cpp ================================================ // // Created by light on 20-1-31. // #include #include using namespace std::chrono; using namespace std; using ull = unsigned long long; ull OddSum = 0; ull EvenSum = 0; void findEven(ull start, ull end) { for (ull i = start; i <= end; ++i) if ((i & 1) == 0) EvenSum += i; } void findOdd(ull start, ull end) { for (ull i = start; i <= end; ++i) if ((i & 1) == 1) OddSum += i; } int main() { ull start = 0, end = 1900000000; auto startTime = high_resolution_clock::now(); findOdd(start, end); findEven(start, end); auto stopTime = high_resolution_clock::now(); auto duration = duration_cast(stopTime - startTime); cout << "OddSum : " << OddSum << endl; cout << "EvenSum: " << EvenSum << endl; cout << "Sec: " << duration.count() / 1000000 << endl; return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/1.thread/thread.cpp ================================================ // // Created by light on 20-1-31. // #include #include #include using namespace std::chrono; using namespace std; /** * 1.普通函数指针 * 2.Lambda函数 * 3.Functors * 4.非静态成员函数 * 5.静态成员函数 */ using ull = unsigned long long; ull OddSum = 0; ull EvenSum = 0; void findEven(ull start, ull end) { for (ull i = start; i <= end; ++i) if ((i & 1) == 0) EvenSum += i; } void findOdd(ull start, ull end) { for (ull i = start; i <= end; ++i) if ((i & 1) == 1) OddSum += i; } int main() { ull start = 0, end = 1900000000; auto startTime = high_resolution_clock::now(); std::thread t1(findEven,start,end); std::thread t2(findOdd,start,end); t1.join(); t2.join(); auto stopTime = high_resolution_clock::now(); auto duration = duration_cast(stopTime - startTime); cout << "OddSum : " << OddSum << endl; cout << "EvenSum: " << EvenSum << endl; cout << "Sec: " << duration.count() / 1000000 << endl; return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/2.create_type/1.function_pointer.cpp ================================================ // // Created by light on 20-1-31. // // 1.函数指针 #include #include using namespace std; void fun(int x) { while (x-- > 0) { cout << x << endl; } } // 注意:如果我们创建多线程 并不会保证哪一个先开始 int main() { std::thread t1(fun, 10); // std::1.thread t2(fun, 10); t1.join(); // t2.join(); return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/2.create_type/2.lambda_function.cpp ================================================ // // Created by light on 20-1-31. // // 1.函数指针 #include #include using namespace std; // 注意:如果我们创建多线程 并不会保证哪一个先开始 int main() { // 2.Lambda函数 auto fun = [](int x) { while (x-- > 0) { cout << x << endl; } }; // std::1.thread t1(fun, 10); // 也可以写成下面: std::thread t1_1([](int x) { while (x-- > 0) { cout << x << endl; } }, 11); // std::1.thread t2(fun, 10); // t1.join(); t1_1.join(); // t2.join(); return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/2.create_type/3.functor.cpp ================================================ // // Created by light on 20-1-31. // #include #include using namespace std; // 3.functor (Funciton Object) class Base { public: void operator()(int x) { while (x-- > 0) { cout << x << endl; } } }; int main() { thread t(Base(), 10); t.join(); return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/2.create_type/4.no_static_member_function.cpp ================================================ // // Created by light on 20-1-31. // #include #include using namespace std; // 4.Non-static member function class Base { public: void fun(int x) { while (x-- > 0) { cout << x << endl; } } }; int main() { Base b; thread t(&Base::fun,&b, 10); t.join(); return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/2.create_type/5.static_member_function.cpp ================================================ // // Created by light on 20-1-31. // #include #include using namespace std; // 4.Non-static member function class Base { public: static void fun(int x) { while (x-- > 0) { cout << x << endl; } } }; int main() { thread t(&Base::fun, 10); t.join(); return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/2.create_type/BUILD ================================================ # please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:4.no_static_member_function` # please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:2.lambda_function` # please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:1.function_pointer` # please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:3.functor` # please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:5.static_member_function` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "4.no_static_member_function", srcs = ["4.no_static_member_function.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "2.lambda_function", srcs = ["2.lambda_function.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "1.function_pointer", srcs = ["1.function_pointer.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "3.functor", srcs = ["3.functor.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "5.static_member_function", srcs = ["5.static_member_function.cpp"], copts = ["-std=c++11"], ) ================================================ FILE: concurrency/Threading_In_CPlusPlus/3.join_detach/BUILD ================================================ # please run `bazel run //concurrency/Threading_In_CPlusPlus/3.join_detach:join` # please run `bazel run //concurrency/Threading_In_CPlusPlus/3.join_detach:detach` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "join", srcs = ["join.cpp"], copts = ["-std=c++11"], ) cc_binary( name = "detach", srcs = ["detach.cpp"], copts = ["-std=c++11"], ) ================================================ FILE: concurrency/Threading_In_CPlusPlus/3.join_detach/detach.cpp ================================================ // // Created by light on 20-1-31. // // join 注意点 /** * 这用于从父线程分离新创建的线程 * 在分离线程之前,请务必检查它是否可以joinable, * 否则可能会导致两次分离,并且双重detach()将导致程序终止 * 如果我们有分离的线程并且main函数正在返回,那么分离的线程执行将被挂起 */ #include #include #include #include using namespace std; void run(int count) { while (count-- > 0) { cout << count << endl; } std::this_thread::sleep_for(chrono::seconds(3)); } int main() { thread t1(run, 10); cout << "main()" << endl; t1.detach(); if(t1.joinable()) t1.detach(); cout << "main() after" << endl; return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/3.join_detach/join.cpp ================================================ // // Created by light on 20-1-31. // // join 注意点 /** * 一旦线程开始,我们要想等待线程完成,需要在该对象上调用join() * 双重join将导致程序终止 * 在join之前我们应该检查显示是否可以被join,通过使用joinable() */ #include #include #include using namespace std; void run(int count) { while (count-- > 0) { cout << count << endl; } std::this_thread::sleep_for(chrono::seconds(3)); } int main() { thread t1(run, 10); cout << "main()" << endl; t1.join(); if (t1.joinable()) { t1.join(); } cout << "main() after" << endl; return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/4.mutex/BUILD ================================================ # please run `bazel run //concurrency/Threading_In_CPlusPlus/4.mutex:critical_section` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "critical_section", srcs = ["critical_section.cpp"], copts = ["-std=c++11"], ) ================================================ FILE: concurrency/Threading_In_CPlusPlus/4.mutex/an_example_of_bank_account.cpp ================================================ #include #include #include #include using namespace std; class BankAccount { public: BankAccount() : balance(0) {} void deposit(int amount) { lock_guard lock(mtx); balance += amount; } void withdraw(int amount) { lock_guard lock(mtx); if (balance >= amount) { balance -= amount; } else { cout << "Not enough funds to withdraw " << amount << endl; } } int get_balance() const { return balance; } private: int balance; mutable mutex mtx; }; void deposit_to_account(BankAccount& account) { for (int i = 0; i < 1000; ++i) { account.deposit(1); } } void withdraw_from_account(BankAccount& account) { for (int i = 0; i < 500; ++i) { account.withdraw(1); } } int main() { BankAccount account; thread t1(deposit_to_account, ref(account)); thread t2(withdraw_from_account, ref(account)); thread t3(deposit_to_account, ref(account)); thread t4(withdraw_from_account, ref(account)); t1.join(); t2.join(); t3.join(); t4.join(); cout << "Final balance: " << account.get_balance() << endl; return 0; } ================================================ FILE: concurrency/Threading_In_CPlusPlus/4.mutex/critical_section.cpp ================================================ // // Created by light on 20-2-1. // #include #include #include using namespace std; int sum = 0; //shared mutex m; void *countgold() { int i; //local to each thread for (i = 0; i < 10000000; i++) { m.lock(); sum += 1; m.unlock(); } return NULL; } int main() { thread t1(countgold); thread t2(countgold); //Wait for both threads to finish t1.join(); t2.join(); cout << "sum = " << sum << endl; return 0; } ================================================ FILE: concurrency/concurrency_v1/chapter1/1_helloworld.cpp ================================================ // // Created by light on 19-11-5. // #include #include #include using namespace std; void hello() { cout << "hello world" << endl; } int main() { thread t(hello); t.join(); // must add this line otherwise will failed! // 需要注意的是线程对象执行了join后就不再joinable了,所以只能调用join一次。 return 0; } ================================================ FILE: concurrency/concurrency_v1/chapter1/BUILD ================================================ # please run `bazel run //concurrency/concurrency_v1/chapter1:1_helloworld` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "1_helloworld", srcs = ["1_helloworld.cpp"], ) ================================================ FILE: concurrency/concurrency_v1/chapter2/2.1_basic.cpp ================================================ // // Created by light on 19-11-5. // #include #include #include #include using namespace std; class background_task { public: void operator()() const { cout << "ok" << endl; } }; void do_something(int &i) { cout << "do_something" << endl; } struct func { int &i; func(int &i_) : i(i_) {} void operator()() { for (unsigned j = 0; j < 1000000; ++j) { do_something(i); // 1. 潜在访问隐患:悬空引用 } } }; // 特殊情况下的等待 void f() { int some_local_state = 0; func my_func(some_local_state); std::thread t(my_func); try { // do_something_in_current_thread(); } catch (...) { t.join(); // 1 throw; } t.join(); // 2 } // try catch 只能捕获轻量级错误,所以如需确保线程在函数之前结束——查看是否因为线程函数使用了局部变量的引用, // 以及其他原因——而后再确定一下程序可能会退出的途径,无论正常与否,可以提供一个简洁的机制,来做解决这个问题。 // 一种方式是使用“资源获取即初始化方式”(RAII,Resource Acquisition Is Initialization),并且提供一个类,在析构函数中使用join(), // std::thread支持移动的好处是可以创建thread_guard类的实例,并且拥有其线程的所有权。 class thread_guard { std::thread &t; public: explicit thread_guard(std::thread &t_) : t(t_) {} ~thread_guard() { if (t.joinable()) // 1 { t.join(); // 2 } } thread_guard(thread_guard const &) = delete; // 3 thread_guard &operator=(thread_guard const &) = delete; }; void f1() { int some_local_state=0; func my_func(some_local_state); std::thread t(my_func); thread_guard g(t); // do_something_in_current_thread(); } // 4 // 当线程执行到4处时,局部对象就要被逆序销毁了。因此,thread_guard对象g是第一个被销毁的, // 这时线程在析构函数中被加入2到原始线程中。 // 即使do_something_in_current_thread抛出一个异常,这个销毁依旧会发生。 int main() { background_task f; // thread t(f); // ok // t.join(); //声明一个名为my_threadx的函数,这个函数带有一个参数(函数指针指向没有参数并返回background_task对象的函数),返回一个std::thread对象的函数 // thread my_thread1(background_task()); // 针对Most Vexing Parse问题解决如下: // thread my_thread1((background_task())); // 多组括号 // my_thread1.join(); // thread my_thread2{background_task()}; // 新的初始化语法 // my_thread2.join(); // thread myThread([](){ // cout<<"ok"< #include using namespace std; class X { public: void do_length_work() {}; }; void process_big_object(std::unique_ptr) { // TODO } int main() { X my_x; thread t(&X::do_length_work, &my_x); // 1 std::unique_ptr p(new X); p->do_length_work(); std::thread tt(process_big_object,std::move(p)); //std::thread实例的可移动且不可复制性。不可复制保性证了在同一时间点, // 一个std::thread实例只能关联一个执行线程;可移动性使得程序员可以自己决定,哪个实例拥有实际执行线程的所有权。 return 0; } ================================================ FILE: concurrency/concurrency_v1/chapter2/2.3_ownership.cpp ================================================ // // Created by light on 19-11-5. // #include #include #include #include #include #include using namespace std; void some_function() {} void some_other_function() {} // std::thread不支持拷贝语义。 // std::thread支持移动语义。 // scoped_thread实例 void do_something(int i) { cout << i << endl; } struct func { int &i; func(int &i_) : i(i_) {} void operator()() { for (unsigned j = 0; j < 1000000; ++j) { do_something(i); } } }; class scoped_thread { std::thread t; public: explicit scoped_thread(std::thread t_) : // 1 t(std::move(t_)) { if (!t.joinable()) // 2 throw std::logic_error("No thread"); } ~scoped_thread() { t.join(); // 3 } scoped_thread(scoped_thread const &) = delete; scoped_thread &operator=(scoped_thread const &) = delete; }; void do_work(unsigned id) {} void f() { std::vector threads; for (unsigned i = 0; i < 20; ++i) { threads.push_back(std::thread(do_work, i)); // 产生线程 } std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); // 对每个线程调用join() } int main() { // std::thread t1(some_function); // 构造一个thread对象t1 // std::thread t2 = std::move(t1); // 把t1 move给另外一个thread对象t2,t1不再管理之前的线程了。 // // 这句不需要std::move(),从临时变量进行移动是自动和隐式的。调用的是operator=(std::thread&&) // t1 = std::thread(some_other_function); // std::thread t3; // t3 = std::move(t2); // 把t2 move给t3 // // 把t3 move给t1,非法。因为`t1`已经有了一个相关的线程,会调用`std::terminate()`来终止程序。 // t1 = std::move(t3); f(); return 0; } ================================================ FILE: concurrency/concurrency_v1/chapter2/2.4_runtime.cpp ================================================ // // Created by light on 19-11-5. // #include #include #include #include #include #include using namespace std; //使得每个线程具有最小数目的元素以避免过多的线程开销 template struct accumulate_block { void operator()(Iterator first, Iterator last, T &result) { result = std::accumulate(first, last, result); } }; template T parallel_accumlate(Iterator first, Iterator last, T init) { unsigned long const length = std::distance(first, last); if (!length) return init; unsigned long const min_per_thread = 25; unsigned long const max_threads = (length + min_per_thread - 1) / min_per_thread; cout< results(num_threads); std::vector threads(num_threads - 1); Iterator block_start = first; for (unsigned long i = 0; i < (num_threads - 1); ++i) { Iterator block_end = block_start; std::advance(block_end, block_size); threads[i] = std::thread(accumulate_block(), block_start, block_end, std::ref(results[i])); block_start = block_end; } accumulate_block()(block_start, last, results[num_threads - 1]); std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); return std::accumulate(results.begin(), results.end(), init); } int main() { vector v{3,4,5,6}; int res=0; cout< #include using namespace std; // 线程的通用标识符 std::thread::id master_thread; void do_master_thread_work() { cout << "master" << endl; } void do_common_work() { cout << "common" << endl; } void some_core_part_of_algorithm() { if (std::this_thread::get_id() == master_thread) { do_master_thread_work(); } do_common_work(); } int main() { master_thread = std::this_thread::get_id(); std::cout << "master_thread: " << master_thread << endl; cout << "master_thread 中运行:" << endl; some_core_part_of_algorithm(); cout << "thread 中运行:" << endl; thread t(some_core_part_of_algorithm); t.join(); return 0; } ================================================ FILE: concurrency/concurrency_v1/chapter2/BUILD ================================================ # please run `bazel run //concurrency/concurrency_v1/chapter2:2.4_runtime` # please run `bazel run //concurrency/concurrency_v1/chapter2:2.1_basic` # please run `bazel run //concurrency/concurrency_v1/chapter2:2.3_ownership` # please run `bazel run //concurrency/concurrency_v1/chapter2:2_5_id` # please run `bazel run //concurrency/concurrency_v1/chapter2:2.2_transfer` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "2.4_runtime", srcs = ["2.4_runtime.cpp"], ) cc_binary( name = "2.1_basic", srcs = ["2.1_basic.cpp"], ) cc_binary( name = "2.3_ownership", srcs = ["2.3_ownership.cpp"], ) cc_binary( name = "2_5_id", srcs = ["2_5_id.cpp"], copts = ["-std=c++11"] ) cc_binary( name = "2.2_transfer", srcs = ["2.2_transfer.cpp"], copts = ["-std=c++11"] ) ================================================ FILE: cpp2.0/cpp11/BUILD ================================================ #please run `bazel run //cpp2.0/cpp11:decltype` #please run `bazel run //cpp2.0/cpp11:auto` # please run `bazel run //cpp2.0/cpp11:override` # please run `bazel run //cpp2.0/cpp11:tuple` # please run `bazel run //cpp2.0/cpp11:noexcept` # please run `bazel run //cpp2.0/cpp11:constexpr` # please run `bazel run //cpp2.0/cpp11:nullptr` # please run `bazel run //cpp2.0/cpp11:initializer` # please run `bazel run //cpp2.0/cpp11:type_alias` # please run `bazel run //cpp2.0/cpp11:hash` # please run `bazel run //cpp2.0/cpp11:lambda` # please run `bazel run //cpp2.0/cpp11:uniform_initialization` # please run `bazel run //cpp2.0/cpp11:final` # please run `bazel run //cpp2.0/cpp11:default_delete` # please run `bazel run //cpp2.0/cpp11:move` # please run `bazel run //cpp2.0/cpp11:alias` # please run `bazel run //cpp2.0/cpp11:rvalue` # please run `bazel run //cpp2.0/cpp11:template_template` # please run `bazel run //cpp2.0/cpp11:explicit` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "decltype", srcs = ["decltype.cpp"], ) cc_binary( name = "auto", srcs = ["auto.cpp"], ) cc_binary( name = "override", srcs = ["override.cpp"], ) cc_binary( name = "tuple", srcs = ["tuple.cpp"], ) cc_binary( name = "noexcept", srcs = ["noexcept.cpp"], ) cc_binary( name = "constexpr", srcs = ["constexpr.cpp"], ) cc_binary( name = "nullptr", srcs = ["nullptr.cpp"], ) cc_binary( name = "initializer", srcs = ["initializer.cpp"], ) cc_binary( name = "type_alias", srcs = ["type_alias.cpp"], ) cc_binary( name = "hash", srcs = ["hash.cpp"], ) cc_binary( name = "lambda", srcs = ["lambda.cpp"], ) cc_binary( name = "uniform_initialization", srcs = ["uniform_initialization.cpp"], ) cc_binary( name = "final", srcs = ["final.cpp"], ) cc_binary( name = "default_delete", srcs = ["default_delete.cpp"], ) cc_binary( name = "move", srcs = ["move.cpp"], ) cc_binary( name = "alias", srcs = ["alias.cpp"], ) cc_binary( name = "rvalue", srcs = ["rvalue.cpp"], ) cc_binary( name = "template_template", srcs = ["template_template.cpp"], ) cc_binary( name = "explicit", srcs = ["explicit.cpp"], ) ================================================ FILE: cpp2.0/cpp11/README.md ================================================ # C++新特性 ## 1.C++11新特性 - [Variadic Templates](./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](./nullptr.cpp) - [Automatic Type Deduction with auto](./auto.cpp) - [Uniform Initialization ](./uniform_initialization.cpp) - [initializer_list](./initializer.cpp) - [explicit for ctors taking more than one argument](./explicit.cpp) - [range-based for statement](./auto.cpp) ```cpp for(decl:col) { statement } ``` - [=default,=delete](./default_delete.cpp) 如果你自行定义了一个ctor,那么编译器就不会给你一个default ctor 如果强制加上=default,就可以重新获得并使用default ctor. - Alias(化名)Template(template typedef) - [alias.cpp](./alias.cpp) - [template_template.cpp](./template_template.cpp) - [template template parameter](./template_template.cpp) - [type alias](./type_alias.cpp) - [noexcept](./noexcept.cpp) - [override](./override.cpp) - [final](./final.cpp) - [decltype](./decltype.cpp) - [lambda](./lambda.cpp) - [Rvalue reference](./rvalue.cpp) - [move aware class](./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](./hash.cpp) - [tuple](./tuple.cpp) 学习资料:https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061 ================================================ FILE: cpp2.0/cpp11/alias.cpp ================================================ // // Created by light on 19-11-3. // #include using namespace std; // 不可以对alias template做偏特化或全特化 template using Vec=vector>; // alias template using新用法 //# define Vec template vector> // 使用宏不行 int main() { Vec col; // 如果使用宏上述会被替换为template vector> error 不是我们想要的 // typedef vector> Vec; // typedef也无法做到,没法指定模板参数 return 0; } ================================================ FILE: cpp2.0/cpp11/auto.cpp ================================================ // // Created by light on 19-11-2. // #include #include #include #include #include #include using namespace std; // iterator stl_iterator.h里面的源码 使用auto template #if __cplusplus < 201103L inline typename reverse_iterator<_Iterator>::difference_type operator-(const reverse_iterator<_Iterator>& __x, const reverse_iterator<_Iterator>& __y) #else inline auto operator-(const reverse_iterator<_Iterator> &__x, const reverse_iterator<_Iterator> &__y) -> decltype(__x.base() - __y.base()) #endif { return __y.base() - __x.base(); } // auto 关键字(c++11) 语法糖 // for 循环 ranged-base for class C { public: explicit C(const string &s) : mystr(s) { } friend ostream &operator<<(ostream out, const C &c) { out << c.mystr << endl; } private: string mystr; }; int main() { auto i = 42; // 编译器具备实参推导 auto ll1 = [](int x) -> int { return x + 10; }; // lambda 匿名函数 function ll = [](int x) -> int { return x + 10; }; cout << ll(10) << endl; list l{1, 2, 3}; list::iterator iterator; iterator = find(l.begin(), l.end(), 10); auto ite = find(l.begin(), l.end(), 11); // auto 关键字 vector vec; //=====================range-based for statement========================= // (1) for (auto elem:vec) // pass by value cout << elem << endl; // c++2.0 之前 (2) 编译器会把(1)转换为(2)或(3) for (auto _pos = l.begin(), _end = l.end(); _pos != _end; ++_pos) cout << *_pos << " "; cout << endl; // (3) // c++2.0之后 全局函数begin(container)与end(container) 可以接受容器 for (auto _pos = begin(l), _end = end(l); _pos != _end; ++_pos) cout << *_pos << " "; cout << endl; // (4) for (auto &elem:vec) // pass by reference elem *= 3; // (5) 编译器会把(4)转换为(5) for (auto _pos = l.begin(), _end = l.end(); _pos != _end; ++_pos) { auto &elem = *_pos; elem *= 3; } cout << endl; vector vs = {"hello", "world"}; // 加了explicit就不能转了 error // for (const C &elem:vs) { // } // auto 不利:降低代码可读性、可能得不到你预想的类型、配合decltype有意想不到的结果 // auto可能得不到你预想的类型,如vector[]的返回类型。 vector v(true); auto var = v.at(0); cout<< typeid(var).name()< #include using namespace std; // 对于修饰Object来说: // const并未区分出编译期常量和运行期常量 // constexpr限定在了编译期常量 // constexpr作用:优化!效率! // constexpr修饰的函数,返回值不一定是编译期常量。It is not a bug, it is a feature. // const修饰的是类型,constexpr修饰的是用来算出值的那段代码。 /** * constexpr修饰的函数,简单的来说,如果其传入的参数可以在编译时期计算出来,那么这个函数就会产生编译时期的值。 * 但是,传入的参数如果不能在编译时期计算出来,那么constexpr修饰的函数就和普通函数一样了。不 * @param i * @return */ constexpr int foo(int i) { return i + 10; } int main() { int i = 10; std::array arr; // OK foo(i); // Call is Ok std::array arr1; // error: the value of ‘i’ is not usable in a constant expression } ================================================ FILE: cpp2.0/cpp11/decltype.cpp ================================================ // // Created by light on 19-11-3. // #include #include #include using namespace std; // 1.used to declare return tyoes 返回类型 // c++2.0之前编译不出来 // template // decltype(x + y) add(T1 x, T2 y); // 编译器编不出来在2.0之后变成下面这个,先用auto暂定,后面返回类型为decltype(x+y) template auto add(T1 x, T2 y) -> decltype(x + y) { // -> 也用在lambda return x + y; } class Person { public: string firstname; string lastname; }; int main() { // 1.used to declare return tyoes cout << add(1, 2) << endl; // 2.模板元编程 例如在一个模板函数或类获取容器的value_type,这里就不封装了,直接写在main函数里面 // 获得表达式的type 有点像typeof()特点 map coll; // 获取上述类型 decltype(coll)::value_type m{"as", 1}; // value_type为pair m cout << m.first << " " << m.second << endl; pair p{"a", 2}; cout << p.first << " " << p.second << endl; // 3.used to pass the type of a lambda // 比大小 auto cmp = [](const Person &p1, const Person &p2) { return p1.lastname < p2.lastname; }; // 对于lambda,我们往往只有object,很少有人能够写出它的类型,而有时就需要知道它的类型,要获得其type,就要借助其decltype set col(cmp); return 0; } ================================================ FILE: cpp2.0/cpp11/default_delete.cpp ================================================ // // Created by light on 19-11-3. // #include #include using namespace std; class Zoo { public: Zoo(int a, int b) : a(a), b(b) { cout << "with param ctor \n"; } Zoo() = default; Zoo(const Zoo &) = delete; // copy ctor Zoo(Zoo &&) = default; // move ctor Zoo &operator=(const Zoo &) = default; // copy assignment Zoo &operator=(const Zoo &&) = delete; // move assignment // 一般函数可以使用=default或=delete? // void fun1()= default; // error: ‘void Zoo::fun1()’ cannot be defaulted void fun1() = delete; // compile ok // 析构函数可以使用=default或=delete? // ~Zoo()= delete; // ok,但是造成使用Zoo object出错 ~Zoo() = default; // 上述得出=delete可以使用在任何函数身上,而=default不可以使用在普通函数上 // 上述会联想到=0,=0只能用在virtual funciton上 private: int a, b; }; class Empty { }; // 等价于 编译器给出的默认版本函数都是public且inline class Empty1 { public: Empty1() {} Empty1(const Empty1 &rhs) {} ~Empty1() {} }; // no-copy // copy ctor与copy assignment都delete掉 struct NoCopy { NoCopy() = default; NoCopy(const NoCopy &) = delete; NoCopy &operator=(const NoCopy &) = delete; ~NoCopy() = default; }; // no-dtor struct NoDtor { NoDtor() = default; ~NoDtor() = delete; }; // 对no-copy的改进 将copy ctor与copy assignment 放到private ,但是可以对member与friend调用 // boost::noncopyable 就用到了PrivateCopy class PrivateCopy { private: PrivateCopy(const PrivateCopy &) {}; PrivateCopy &operator=(const PrivateCopy &) {}; public: PrivateCopy() = default; ~PrivateCopy() {}; }; // 继承了的都拥有PrivateCopy性质 class Foo : public PrivateCopy { }; int main() { Zoo z; Zoo z1(1, 2); // Zoo z2=z; // error copy ctor delete z = z1; complex a; // 内部不带指针的可以不用写 big three 没有析构 有copy ctor与copy assignmetn 都是使用=default,没有自己写 string s; // 内部带指针的有big five // NoDtor n; //error no-dtor 不能够自动delete // NoDtor *p=new NoDtor; // ok // delete p; // error no-dtor 不能够delete Foo foo; Foo foo1; // foo = foo1; // 继承了的都拥有PrivateCopy性质 return 0; } ================================================ FILE: cpp2.0/cpp11/explicit.cpp ================================================ // // Created by light on 19-11-3. // #include using namespace std; // non-explicit-one-argument ctor class Fraction { public: // non-explicit-one-argument ctor 可以把别的东西转换为Fraction这种 Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {} Fraction operator+(const Fraction &f) { return Fraction(this->m_numerator + f.m_numerator); } void getNumAndDen() { cout << m_numerator << " " << m_denominator << endl; } private: int m_numerator; // 分子 int m_denominator; // 分母 }; // explicit-one-argument ctor class Fraction1 { public: // explicit-one-argument ctor 可以把别的东西转换为Fraction这种 explicit Fraction1(int num, int den = 1) : m_numerator(num), m_denominator(den) {} Fraction1 operator+(const Fraction1 &f) { return Fraction1(this->m_numerator + f.m_numerator); } void getNumAndDen() { cout << m_numerator << " " << m_denominator << endl; } private: int m_numerator; // 分子 int m_denominator; // 分母 }; class P { public: P(int a, int b) { cout << "P(int a,int b)\n"; } P(int a, int b, int c) { cout << "non-explicit P(int a,int b,int c)\n"; } explicit P(int a, int b, int c, int d) { cout << "explicit P(int a,int b,int c,int d)\n"; } }; void fp(const P &) { } int main() { Fraction f(3, 5); Fraction ff = f + 3; // 会将3转换为(3,1) 隐式转换 ff.getNumAndDen(); // 如果不想编译器进行隐式转换,可以在前面添加explicit关键字 // c++2.0之前explicit只禁止"存在单一实参"转换 // c++2.0之后explicit可以进制"多个实参"转换 // Fraction1 f1(3,5); // Fraction1 f2 = f1+3; // 会将3转换为(3,1) 不会隐式转换 error: no match for ‘operator+’ (operand types are ‘Fraction1’ and ‘int’) // f2.getNumAndDen(); P p1 = {77, 5}; P p2 = {77, 5, 89}; // P p3 = {77, 5, 89,99}; //error: converting to ‘P’ from initializer list would use explicit constructor ‘P::P(int, int, int, int)’ return 0; } ================================================ FILE: cpp2.0/cpp11/final.cpp ================================================ // // Created by light on 19-11-3. // #include using namespace std; // final关键字用于两个地方 // 第一个用在类,用于说明该类是继承体系下最后的一个类,不要其他类继承我,当继承时就会报错。 class Base final { public: Base() {} virtual void func() {} }; class Derivered : public Base { // error: cannot derive from ‘final’ base ‘Base’ in derived type ‘Derivered’ }; // 第二个用在虚函数,表示这个虚函数不能再被override了,再override就会报错。 class Base1 { public: Base1() {} virtual void func() final {} }; class Derivered1 : public Base1 { virtual void func() {} // error: overriding final function ‘virtual void Base1::func()’ }; int main() { } ================================================ FILE: cpp2.0/cpp11/hash.cpp ================================================ // // Created by light on 19-11-4. // #include #include #include using namespace std; class Customer { }; // 形式一 通过一个类,重载了操作符()的方式,形成了一个仿函数(函数对象) class CustomerHash { public: size_t operator()(const Customer &c) const { // return ...; } }; // 形式二 这个方法实际是通过指定了一个函数指针的方式来通知容器,对于这个自定义类到底应该使用哪个hash function size_t customer_hash_func(const Customer &t) { // return ...; } // 形式三 以struct hash 偏特化形式实现hash function namespace std { // 必须放在std里面 template<> struct hash : public __hash_base { // 继承不继承都可以! size_t operator()(const Customer&s) const noexcept { // return ...; } }; } // 万能的HashFunction 使用variadic templates // (4) template inline void hash_combine(size_t& seed, const T& val){ seed = std::hash()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } // (3) template inline void hash_val(size_t& seed, const T& val){ hash_combine(seed, val); } // (2) template inline void hash_val(size_t& seed, const T& val, const Types&... args){ hash_combine(seed, val); hash_val(seed, args...); } // (1) template inline size_t hash_val(const Types&... args){ size_t seed = 0; hash_val(seed, args...); return seed; } int main() { unordered_set unorderedSet; unordered_setcustset(20,customer_hash_func); unordered_set unset; int *p= reinterpret_cast(new Customer()); cout< #include using namespace std; int main() { int i; int j(); // 0 int *p; int *q(); // nullptr // int x1{5.0}; // error:narrowing conversion // 编译器调用initializer_list私有构造之前,编译器准备好array(array头)与len,传递给该构造,并没有内含这个array, // 指针指向array,copy只是浅copy. // 如今所有容器都接受任意数量的值 insert()或assign() max() min() cout< #include #include #include #include using namespace std; // [introducer](optional)mutable throwSpec->retType {} // mutable决定[]能够被改写 mutable throwSpec retType都是选择的,只要有一个存在就得写() // retType 返回类型 // ()放参数 // []放外面变量 passed by value or reference class UnNamedLocalFunction { private: int localVar; public: UnNamedLocalFunction(int var) : localVar(var) {} bool operator()(int val) { return val == localVar; } }; class Person { public: string firstname; string lastname; }; class LambdaFunctor { public: LambdaFunctor(int a, int b) : m_a(a), m_b(b) {} bool operator()(int n) const { return m_a < n && n < m_b; } private: int m_a; int m_b; }; class X { private: int __x, __y; public: X(int x, int y) : __x(x), __y(y) {} int operator()(int a) { return a; } int f() { // 下列 lambda 的语境是成员函数 X::f // 对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针 return [&]() -> int { return operator()(this->__x + __y); // X::operator()(this->x + (*this).y) // 拥有类型 X* }(); } int ff() { return [this]() { return this->__x; }(); } }; int main() { [] { cout << "hello" << endl; }(); auto I = [] { cout << "hello" << endl; }; I(); int id = 0; // 先看前面的id 如果没有mutable error: increment of read-only variable ‘id’ auto f = [id]()mutable { cout << "id=" << id << endl; ++id; }; id = 42; f(); // 0 f(); // 1 f(); // 2 cout << id << endl; // 上述lambda就相当于 // class Functor { // private: // int id; // public: // void operator() { // cout << "id=" << id << endl; // ++id; // } // }; // Functor f; int id1 = 0; // 加不加mutable没影响,且传引用只要后面id1被修改了,就会使用修改后的值进行操作 auto f1 = [&id1]() { cout << "id1=" << id1 << endl; ++id1; }; id1 = 42; f1(); // 42 f1(); // 43 f1(); // 44 cout << id1 << endl; // 传参与返回 int id2 = 0; auto f2 = [&id2](int ¶m) { cout << "id2=" << id2 << endl; ++id2; ++param; cout << "param=" << param << endl; static int x = 5; return id2; }; int param = 1; f2(param); cout << "param=" << param << endl; // [=] =表示默认以by value传递外部所有变量 // [&] &表示默认以by reference传递外部所有变量 auto f3 = [&]() { cout << "id=" << id << endl; cout << "id1=" << id1 << endl; cout << "id2=" << id2 << endl; cout << "param=" << param << endl; }; f3(); // 一部分传引用,其余传值 cout << "id=" << id << endl; auto f4 = [=, &id]() { // =不可以放在&id后面 cout << "id=" << id << endl; id++; cout << "id1=" << id1 << endl; cout << "id2=" << id2 << endl; cout << "param=" << param << endl; }; f4(); // 一部分传值,其余传引用 cout << "id=" << id << endl; auto f5 = [&, id]() { // &不可以放在id后面 cout << "id=" << id << endl; cout << "id1=" << id1 << endl; cout << "id2=" << id2 << endl; cout << "param=" << param << endl; }; f5(); // this 指针 X x_(1, 2); cout << "x_.f()=" << x_.f() << endl; // 1+2=3 cout << "x_.ff()=" << x_.ff() << endl; // 1 // 下面lambda函数等价于上述的UnNamedLocalFunction int tobefound = 5; auto lambda1 = [tobefound](int val) { return val == tobefound; }; bool b1 = lambda1(5); UnNamedLocalFunction lambda2(tobefound); bool b2 = lambda2(5); cout << b1 << " " << b2 << endl; auto ll1 = [](int x) -> int { return x + 10; }; // lambda 匿名函数 function ll = [](int x) -> float { return x + 10.0; }; cout< cmp = [](const Person &p1, const Person &p2) { return p1.lastname < p2.lastname; }; // 对于lambda,我们往往只有object,很少有人能够写出它的类型,而有时就需要知道它的类型,要获得其type,就要借助其decltype set col(cmp); // 要申明lambda对象的类型,可以使用template或者auto进行自动推导。 // 如果需要知道类型,可以使用decltype,比如,让lambda函数作为关联容器或者无序容器的排序函数或者哈希函数。 // 上面代码给出了事例(decltype的第三种用法中的事例),定义了一个lambda函数用cmp表示,用来比较Person对象的大小,传入到Set容器中去, // 但根据右边的set容器的定义,我们传入的不仅是cmp(构造函数),还要传入模板的cmp类型(Set内部需要声明cmp类型), // 所以必须使用decltype来推导出类型。 // (如果没有向构造函数传入cmp,调用的是默认的构造函数,也就是set() : t(Compare()), 这里会报错, 现在不会出问题了! // 因为Compare()指的是调用默认的lambda构造函数,而lambda函数没有默认构造函数和赋值函数) vector vec{5, 28, 50, 83, 70, 590, 245, 59, 24}; int x = 30, y = 100; // 函数对象是很强大的,封装代码和数据来自定义标准库的行为,但需要写出函数对象需要写出整个class,这是不方便的,而且是非本地的, // 用起来也麻烦,需要去看怎样使用,另外编译出错的信息也不友好,而且它们不是inline的,效率会低一些(算法效率还是最重要的)。 // vec.erase(remove_if(vec.begin(), vec.end(), LambdaFunctor(x, y)), vec.end()); // for(auto i:vec) cout< #include #include #include #include #include #include #include #include #include using namespace std; class MyStringNoMove { public: static size_t DCtor; // default-ctor static size_t Ctor; // ctor static size_t CCtor; // copy-ctor static size_t CAsgn; // copy-assignment static size_t MCtor; // move-ctor static size_t MAsgn; // move-assignment static size_t Dtor; // dtor private: char *_data; size_t _len; void _init_data(const char *s) { _data = new char[_len]; memcpy(_data, s, _len); _data[_len] = '\0'; }; public: MyStringNoMove() : _data(NULL), _len(0) { ++DCtor; } MyStringNoMove(const char *p) : _len(strlen(p)) { ++Ctor; _init_data(p); } // copy ctor MyStringNoMove(const MyStringNoMove &str) : _len(str._len) { ++CCtor; _init_data(str._data); } // copy assignment MyStringNoMove &operator=(const MyStringNoMove &str) { ++CAsgn; if (this != &str) { if (_data) delete _data; _len = str._len; _init_data(str._data); } else { } return *this; } ~MyStringNoMove() { // dtor 默认是noexcept ++Dtor; if (_data) delete _data; } bool operator<(const MyStringNoMove &rhs) const { return string(this->_data) < string(rhs._data); } bool operator==(const MyStringNoMove &rhs) const { return string(this->_data) == string(rhs._data); } char *get() const { return _data; } }; size_t MyStringNoMove::DCtor = 0; // default-ctor size_t MyStringNoMove::Ctor = 0; // ctor size_t MyStringNoMove::CCtor = 0; // copy-ctor size_t MyStringNoMove::CAsgn = 0; // copy-assignment size_t MyStringNoMove::MAsgn = 0; // move-assignment size_t MyStringNoMove::MCtor = 0; // move-ctor size_t MyStringNoMove::Dtor = 0; // dtor // move aware class class MyString { public: static size_t DCtor; // default-ctor static size_t Ctor; // ctor static size_t CCtor; // copy-ctor static size_t CAsgn; // copy-assignment static size_t MCtor; // move-ctor static size_t MAsgn; // move-assignment static size_t Dtor; // dtor private: char *_data; size_t _len; void _init_data(const char *s) { _data = new char[_len]; memcpy(_data, s, _len); _data[_len] = '\0'; }; public: MyString() : _data(NULL), _len(0) { ++DCtor; } MyString(const char *p) : _len(strlen(p)) { ++Ctor; _init_data(p); } // copy ctor MyString(const MyString &str) : _len(str._len) { ++CCtor; _init_data(str._data); } // move ctor MyString(MyString &&str) noexcept: _data(str._data), _len(str._len) { ++MCtor; str._len = 0; str._data = NULL; // 重要 } // copy assignment MyString &operator=(const MyString &str) { ++CAsgn; if (this != &str) { if (_data) delete _data; _len = str._len; _init_data(str._data); } else { } return *this; } // move assignment MyString &operator=(MyString &&str) { ++MAsgn; if (this != &str) { if (_data) delete _data; _data = str._data; _len = str._len; str._len = 0; str._data = NULL; } else { } return *this; } ~MyString() { // dtor 默认是noexcept ++Dtor; if (_data) delete _data; } bool operator<(const MyString &rhs) const { return string(this->_data) < string(rhs._data); } bool operator==(const MyString &rhs) const { return string(this->_data) == string(rhs._data); } char *get() const { return _data; } }; size_t MyString::DCtor = 0; // default-ctor size_t MyString::Ctor = 0; // ctor size_t MyString::CCtor = 0; // copy-ctor size_t MyString::CAsgn = 0; // copy-assignment size_t MyString::MAsgn = 0; // move-assignment size_t MyString::MCtor = 0; // move-ctor size_t MyString::Dtor = 0; // dtor namespace std { template<> struct hash : public __hash_base { size_t operator()(const MyStringNoMove &s) const noexcept { return hash()(string(s.get())); } }; template<> struct hash : public __hash_base { size_t operator()(const MyString &s) const noexcept { return hash()(string(s.get())); } }; } template void output_static_data(const T &myStr) { cout << typeid(myStr).name() << "==" << endl; cout << "CCtor=" << T::CCtor << ",MCtor=" << T::MCtor << ",CAsgn=" << T::CAsgn << ",MAsgn=" << T::MAsgn << ",Dtor=" << T::Dtor << ",Ctor=" << T::Ctor << ",DCtor=" << T::DCtor << endl; } template void test_moveable(M c1, NM c2, long &value) { cout << "\n\ntest, with moveable elements" << endl; typedef typename iterator_traits::value_type V1type; clock_t timeStart = clock(); for (long i = 0; i < value; i++) { string buf = to_string(i); auto ite = c1.end(); c1.insert(ite, V1type(buf.c_str())); } cout << "construction,milli-seconds:" << clock() - timeStart << endl; cout << "size()=" << c1.size() << endl; output_static_data(*(c1.begin())); // 容器本身操作 timeStart = clock(); M c11(c1); cout << "copy, milli-seconds: " << (clock() - timeStart) << endl; timeStart = clock(); M c12(move(c1)); cout << "move copy, milli-seconds: " << (clock() - timeStart) << endl; timeStart = clock(); c11.swap(c12); cout << "swap, milli-seconds: " << (clock() - timeStart) << endl; //測試 non-moveable cout << "\n\ntest, with non-moveable elements" << endl; typedef typename iterator_traits::value_type V2type; timeStart = clock(); for (long i = 0; i < value; ++i) { string buf = to_string(i); auto ite = c2.end(); c2.insert(ite, V2type(buf.c_str())); } cout << "construction, milli-seconds : " << (clock() - timeStart) << endl; cout << "size()= " << c2.size() << endl; output_static_data(*(c2.begin())); // 容器本身操作 timeStart = clock(); NM c21(c2); cout << "copy, milli-seconds : " << (clock() - timeStart) << endl; timeStart = clock(); NM c22(std::move(c2)); cout << "move copy, milli-seconds : " << (clock() - timeStart) << endl; timeStart = clock(); c21.swap(c22); cout << "swap, milli-seconds: " << (clock() - timeStart) << endl; } int main() { long value = 3000; // vector 测试结果的MCtor与CCtor结果大于3000000,是因为vector的动态扩容,当容量不够的时候,会动态分配并进行拷贝。 // test_moveable(vector(), vector(), value); // test_moveable(list(), list(), value); // test_moveable(deque(), deque(), value); // test_moveable(multiset(), multiset(), value); // test_moveable(unordered_multiset(), unordered_multiset(), value); return 0; } ================================================ FILE: cpp2.0/cpp11/noexcept.cpp ================================================ // // Created by light on 19-11-3. // #include #include using namespace std; // noexcepte ()中可以加条件 // 异常可传递 a调用b b抛出异常,a没处理,就会抛出异常 // 1.异常的回传机制:调用foo如果抛出异常,foo会接着往上层抛出异常, // 如果最上层没有处理,则会调用terminate函数,terminate函数内部调用abort,使程序退出 // 2.在使用vector deque等容器的移动构造和移动赋值的时候,如果移动构造和移动赋值没有加上noexcept, // 则容器增长的时候不会调用move constructor,效率就会偏低,所以后面需要加上noexcept,安心使用。 void foo() noexcept(true) { } int main() { foo(); vector vec; return 0; } ================================================ FILE: cpp2.0/cpp11/nullptr.cpp ================================================ // // Created by light on 19-11-2. // #include using namespace std; void f(int i) { cout<<"void f(int i)" < using namespace std; class Base { public: Base() {} virtual void func() {} }; class Derivered : public Base { virtual void func(int) override {} // error: ‘virtual void Derivered::func(int)’ marked ‘override’, but does not override }; // override用于虚函数,上面的virtual void func(int)实际上不是重写父类的虚函数,而是定义一个新的虚函数, // 我们的本意是重写虚函数,当不加overrride的时候,这样写编译器不会报错, // 那如果像下面加上override的话,则会报错,表示告诉了编译器,我确实要重写,但写错了,没有重写,于是就报错了, // 这样就能给我们对虚函数的重写做检查! int main() { } ================================================ FILE: cpp2.0/cpp11/rvalue.cpp ================================================ // // Created by light on 19-11-4. // #include #include #include using namespace std; // Rvalue references 解决非必要的拷贝 当赋值右手边是一个"右值",左手边对象可以偷右手边对象,而不需要重新分配内存。 // 浅copy int foo() { return 5; } class Foo { public: Foo() = default; Foo(const Foo &foo) = default; Foo(Foo &&foo) noexcept {} }; // 判断调用哪一个函数 void process(int &i) { cout << "左值传入" << endl; } void process(int &&i) { cout << "右值传入" << endl; } void UnPerfectForward(int &&i) { cout << "forward(int&& i)" << endl; process(i); } // std::forward()实现就是下面这样 void PerfectForward(int &&i) { cout << "forward(int&& i)" << endl; process(static_cast(i)); } // Lvalue: 变量 // Rvalue: 临时对象就是个右值,右值不可以放在左边 int main() { int a = 9, b = 4; a = b; b = a; // a+b=42;// error Rvalue string s1("hello"); string s2("world"); s1 + s2 = s2; // ok string() = "ok"; // ok cout << "s2:" << s1 + s2 << endl; cout << "s1:" << s1 << endl; cout << "s2:" << s2 << endl; complex c1(3, 8), c2(1, 0); c1 + c2 = complex(3, 4); // ok complex() = complex(1, 2); // ok int x = foo(); // int *p=&foo(); //error! Rvalue不可以取地址 // foo()=7; // error // Rvalue references // vector vec; // vec.insert(vec.begin(), Foo()); // Rvalue references and Move Semantics // 原先是下面这个 // iterator insert(const_iterator __position, const value_type& __x); // 调用下面这个Move Semantics // iterator insert(const_iterator __position, value_type&& __x) // 然后转交给Foo(Foo&& foo) // { return emplace(__position, std::move(__x)); } // Foo()这个临时对象为右值交给insert的move assignment然后再交给Foo的move ctor。中间有可能会丢失一些信息。 int aa = 1; process(aa); // L process(1); // R process(move(aa)); // R UnPerfectForward(2); // 希望通过转交调用的是右值传入函数,可是调用的是左值传入 这就是个Unperfect Forwarding UnPerfectForward(move(aa)); // 同上 // 那如何设计Perfect Forwarding? // 为传递加上static_cast强转或者直接使用std::forward() PerfectForward(2); PerfectForward(move(aa)); return 0; } ================================================ FILE: cpp2.0/cpp11/template_template.cpp ================================================ // // Created by light on 19-11-3. // #include #include using namespace std; // template template parameter 模板模板参数 template class Container> class XCls { private: Container c; }; // alias template template using Lst = list; int main() { // XCls mylist1; // error XCls mylist2; return 0; } ================================================ FILE: cpp2.0/cpp11/tuple.cpp ================================================ // // Created by light on 19-11-4. // #include #include using namespace std; // tuple使用 int main() { tuple t, t1, t2; t = make_tuple(1, 2, "qwe"); t1 = make_tuple(3, 2, "qwe"); t2 = make_tuple(3, 2, "qwe"); int a = get<0>(t); get<0>(t) = get<1>(t); // 可以修改 cout << a << endl; cout << (get<0>(t) > get<1>(t) ? "true" : "false") << endl; // 比较大小 cout << (t1 == t2 ? "true" : "false") << endl; typedef tuple T; cout << tuple_size::value << endl; // meta programming 处理类型 cout << tuple_size::value << endl; tuple_element<1, T>::type a1 = 10; cout << a1 << endl; return 0; } ================================================ FILE: cpp2.0/cpp11/type_alias.cpp ================================================ // // Created by light on 19-11-3. // #include #include using namespace std; // 第一种使用 // type alias // 等价于typedef void(*func)(int, int); using fun=void (*)(int, int); void example(int, int) {} // 第二种使用 template struct Container { using value_type=T; // 等价于typedef T value_type; }; template void func2(const Cn &cn) { typename Cn::value_type n; } // 第三种使用 // alias template template using string=basic_string>; // 等价于 typedef basic_string string; // 综上:type alias等价于typedef,没有什么不同 // using 的所有用法拓展如下: // 1.using-directives // using namespace std; or using std::cout; // 2. using-declaration // protected: // using _Base::_M_alloc; // 3.c++2.0特性 type alias and alias template int main() { fun fn = example; // 函数指针 return 0; } ================================================ FILE: cpp2.0/cpp11/uniform_initialization.cpp ================================================ // // Created by light on 19-11-2. // #include #include using namespace std; int main() { int value[]{1, 2, 3}; // initializer_list 会关联一个array 里面元素被编译器逐一分解传给函数 vector v{2, 3, 5}; return 0; } ================================================ FILE: cpp2.0/cpp11/variadic/BUILD ================================================ # please run `bazel run //cpp2.0/cpp11/variadic:variadic7` # please run `bazel run //cpp2.0/cpp11/variadic:variadic3_4` # please run `bazel run //cpp2.0/cpp11/variadic:variadic2` # please run `bazel run //cpp2.0/cpp11/variadic:variadic6` # please run `bazel run //cpp2.0/cpp11/variadic:variadic5` # please run `bazel run //cpp2.0/cpp11/variadic:variadic` # please run `bazel run //cpp2.0/cpp11/variadic:variadic1` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "variadic7", srcs = ["variadic7.cpp"], ) cc_binary( name = "variadic3_4", srcs = ["variadic3_4.cpp"], ) cc_binary( name = "variadic2", srcs = ["variadic2.cpp"], ) cc_binary( name = "variadic6", srcs = ["variadic6.cpp"], ) cc_binary( name = "variadic5", srcs = ["variadic5.cpp"], ) cc_binary( name = "variadic", srcs = ["variadic.cpp"], ) cc_binary( name = "variadic1", srcs = ["variadic1.cpp"], ) ================================================ FILE: cpp2.0/cpp11/variadic/variadic.cpp ================================================ // // Created by light on 19-11-2. // // variadic template(c++11) 数量不定的模板参数 #include #include #include using namespace std; // 参数个数 参数个数逐一递减 // 参数类型 参数类型也逐一递减 // (3) // 需要重载一个终止函数 template void _hash(size_t &seed, const T &val) { cout << "hash over " << val << endl; } // (2) // 展开函数, 把参数分为一个普通参数和一个参数包,调用一次,参数包大小就减一 template void _hash(size_t &seed, const T& val,const Args &... args) { cout << "parameter " << val<< endl; // 递归调用自己 _hash(seed, args...); // 上面如果不给T参数,会自己调用自己成死循环 } // 泛化版 (1) template size_t _hash(const Args &... args) { cout << "hash start " << endl; size_t seed = 10; // 递归调用自己 _hash(seed, args...); return seed; } template class A { private: int size = 0; // c++11 支持类内初始化 public: A() { size = sizeof...(Args); cout << size << endl; } }; int main(void) { size_t f = 10; _hash("asdas", 2, 3, 4); A> a; // 类型任意 // Tuple就是利用这个特性(变长参数模板) tuple t = make_tuple(1, "hha"); int firstArg = get<0>(t); string secondArg = get<1>(t); cout << firstArg << " " << secondArg << endl; return 0; } ================================================ FILE: cpp2.0/cpp11/variadic/variadic1.cpp ================================================ // // Created by light on 19-11-4. // #include #include using namespace std; // example 1 void printX() { } // 特化 template void printX(const T &firstArg, const Type &...args) { cout << firstArg << endl; // 先减少一个做操作 printX(args...); // 其余的继续分为一个+一包进行递归知道没有了 调用printX() } // 如果写了下面接收任意参数,下面这个跟上面可以共存 泛化版 永远不会被调用,前面特化把它取代了 template void printX(const Type &...args) { printX(args...); } int main() { printX(1, "hello", bitset<16>(377), 42); return 0; } ================================================ FILE: cpp2.0/cpp11/variadic/variadic2.cpp ================================================ // // Created by light on 19-11-4. // #include using namespace std; void print(const char *s) { while (*s) { if (*s == '%' && *(++s) != '%') throw std::runtime_error("invalid format string: missing arguments"); std::cout << *s++; } } template void print(const char *s, T value, Args... args) { while (*s) { if (*s == '%' && *(++s) != '%') { std::cout << value; print(++s, args...); // 即便当 *s == 0 也会产生调用,以检测更多的类型参数。 return; } std::cout << *s++; } throw std::logic_error("extra arguments provided to printf"); } int main() { int *pi = new int; print("%d %s %p %f\n", 15, "Acc", pi, 3.14); return 0; } ================================================ FILE: cpp2.0/cpp11/variadic/variadic3_4.cpp ================================================ // // Created by light on 19-11-4. // #include using namespace std; // 参数type相同的max int max(initializer_list initializerList) { int res = *initializerList.begin(); for (auto elem:initializerList) res = max(res, elem); return res; } // 参数type相同的maximum int maximum(int n) { return n; } template int maximum(int n, Args...args) { return std::max(n, maximum(args...)); } int main() { cout << max({10, 8, 100, 1}) << endl; cout << maximum(57, 48, 60, 100, 20, 18) << endl; } ================================================ FILE: cpp2.0/cpp11/variadic/variadic5.cpp ================================================ // // Created by light on 19-11-4. // #include #include #include using namespace std; // tuple递归调用 // 得出这种打印[7,5....,42] // 需要知道有几个以及现在操作的是第几个 sizeof...() // cout<< make_tuple(7.5,string("hello"),bitset<16>(377),47); template struct print_tuple { static void print(ostream &os, const tuple &t) { os << get(t) << (IDX + 1 == MAX ? "" : ","); print_tuple::print(os, t); } }; // 偏特化 template struct print_tuple { static void print(ostream &os, const tuple &t) { } }; template ostream &operator<<(ostream &os, const tuple &t) { os << "["; print_tuple<0, sizeof...(Args), Args...>::print(os, t); return os << "]"; } int main() { cout << make_tuple(7.5, string("hello"), bitset<16>(377), 47) << endl; } ================================================ FILE: cpp2.0/cpp11/variadic/variadic6.cpp ================================================ // // Created by light on 19-11-4. // #include using namespace std; // 上一个例子:variadic5.cpp为递归调用 // 当前这个例子为递归继承 namespace light { template class tuple; template<> class tuple<> { }; template class tuple : private tuple { typedef tuple inherited; protected: Head m_head; public: tuple() {} tuple(Head h, Tail...tail) : m_head(h), inherited(tail...) {} // decltype()中的m_head必须放到前面,否则编译器找不到 auto head() -> decltype(m_head) { return m_head; } // 或者 Head head() { return m_head; } inherited &tail() { return *this; } }; } /** * string 32 8字节对齐 * float 4 * int 4 * 4+4+32=40 自底向上 */ int main() { using light::tuple; tuple t(41, 6.3, "nico"); cout << sizeof(t) << endl; cout << t.head() << endl; cout << t.tail().head() << endl; cout << t.tail().tail().head() << endl; return 0; } ================================================ FILE: cpp2.0/cpp11/variadic/variadic7.cpp ================================================ // // Created by light on 19-11-4. // #include using namespace std; namespace light { template class tuple; template<> class tuple<> { }; template class tuple { typedef tuple composited; protected: Head m_head; composited m_tai; public: tuple() {} int get() { return sizeof(composited);} tuple(Head h, Tail...tail) : m_head(h), m_tai(tail...) {} // decltype()中的m_head必须放到前面,否则编译器找不到 auto head() -> decltype(m_head) { return m_head; } // 或者 Head head() { return m_head; } composited &tail() { return m_tai; } }; } // 根据string为8字节对齐,我们得出8字节对齐 // 第一次 int 4字节Head调整到边界8 那composite呢?继续往下分析 // 第二次 把第一次的composited拆分为float与一包, float也占4字节,调整到8边界为8字节,那这一步的composited呢?继续往下分析 // 第三次 把第二次的compoisted拆分成sting与一包, string占32字节,已经为8倍数,无需调整,而这一步的composited呢?由于到这里一包已经没了,就会调用它的全特化版本,全特环版本为空,sizeof为1, // 调整到8的倍数为8. // 因此最后的内存结构(自底向上)为:8 + 8 + 32 + 8 = 56 64位机器结果 struct A { string a; float b; }; int main() { using light::tuple; tuple t(41, 6.3, "nico"); cout< #include #include #include #include #include using namespace std; const int MAX_NUM = 10000; class BoundedBuffer { public: BoundedBuffer(size_t n) { array_.resize(n); start_pos_ = 0; end_pos_ = 0; pos_ = 0; } void Produce(int n) { { std::unique_lock lock(mtx_); // wait for not full not_full_.wait(lock, [=] { return pos_ < array_.size(); }); usleep(1000 * 400); array_[end_pos_] = n; end_pos_ = (end_pos_ + 1) % array_.size(); ++pos_; cout << "Produce pos:" << pos_ << endl; } // auto unlock not_empty_.notify_one(); } int Consume() { std::unique_lock lock(mtx_); // wait for not empty not_empty_.wait(lock, [=] { return pos_ > 0; }); usleep(1000 * 400); int n = array_[start_pos_]; start_pos_ = (start_pos_ + 1) % array_.size(); --pos_; cout << "Consume pos:" << pos_ << endl; lock.unlock(); not_full_.notify_one(); return n; } private: std::vector array_; size_t start_pos_; size_t end_pos_; size_t pos_; std::mutex mtx_; std::condition_variable not_full_; std::condition_variable not_empty_; }; BoundedBuffer bb(10); std::mutex g_mtx; void Producer() { int n = 0; while (n < 100) { bb.Produce(n); cout << "Producer:" << n << endl; n++; } bb.Produce(-1); } void Consumer() { std::thread::id thread_id = std::this_thread::get_id(); int n = 0; do { n = bb.Consume(); cout << "Consumer thread:" << thread_id << "=====> " << n << endl; } while (-1 != n); bb.Produce(-1); } int main() { std::vector t; t.push_back(std::thread(&Producer)); t.push_back(std::thread(&Consumer)); t.push_back(std::thread(&Consumer)); t.push_back(std::thread(&Consumer)); for (auto &one : t) { one.join(); } return 0; } ================================================ FILE: design_pattern/singleton/README.md ================================================ ## C++设计模式之单例模式 ## 0.导语 相信大家面试都逃不开设计模式话题,本节将阐述面试中的最常用的设计模式(单例模式),从分类,线程安全,不基于C++11标准的角度与基于C++11标准的角度,有哪些解决线程安全的单例模式方案,相信认真看完本篇文章,在以后面试中就不用担忧了。 ## 1.众所周知的单例 在一般书籍中或者大家比较是熟知的单例模式是下面这样: ```cpp class singleton { private: singleton() {} static singleton *p; public: static singleton *instance(); }; singleton *singleton::p = nullptr; singleton* singleton::instance() { if (p == nullptr) p = new singleton(); return p; } ``` 这是一个非常简单的实现,将构造函数声明为private或protect防止被外部函数实例化,内部有一个静态的类指针保存唯一的实例,实例的实现由一个public方法来实现,该方法返回该类的唯一实例。 当然这个代码只适合在单线程下,当多线程时,是不安全的。考虑两个线程同时首次调用instance方法且同时检测到p是nullptr,则两个线程会同时构造一个实例给p,这将违反了单例的准则。 ## 2.懒汉与饿汉 单例分为两种实现方法: - 懒汉 - 第一次用到类实例的时候才会去实例化,上述就是懒汉实现。 - 饿汉 - 单例类定义的时候就进行了实例化。 这里也给出饿汉的实现: ```cpp class singleton { private: singleton() {} static singleton *p; public: static singleton *instance(); }; singleton *singleton::p = new singleton(); singleton* singleton::instance() { return p; } ``` 当然这个是线程安全的,对于我们通常阐述的线程不安全,为懒汉模式,下面会阐述懒汉模式的线程安全代码优化。 ## 3.多线程加锁 在C++中加锁有个类实现原理采用RAII,不用手动管理unlock,那就是lock_guard,这里采用其进行加锁。 ```cpp class singleton { private: singleton() {} static singleton *p; static mutex lock_; public: static singleton *instance(); }; singleton *singleton::p = nullptr; singleton* singleton::instance() { lock_guard guard(lock_); if (p == nullptr) p = new singleton(); return p; } ``` 这种写法不会出现上面两个线程都执行到p=nullptr里面的情况,当线程A在执行`p = new Singleton()`的时候,线程B如果调用了`instance()`,一定会被阻塞在加锁处,等待线程A执行结束后释放这个锁。从而是线程安全的。 但是这种写法性能非常低下,因为每次调用`instance()`都会加锁释放锁,而这个步骤只有在第一次`new Singleton()`才是有必要的,只要`p`被创建出来了,不管多少线程同时访问,使用`if (p == nullptr) `进行判断都是足够的(只是读操作,不需要加锁),没有线程安全问题,加了锁之后反而存在性能问题。 因此引出DCL。 ## 4.双重检查锁模式 上面写法是不管任何情况都会去加锁,然后释放锁,而对于读操作是不存在线程安全的,故只需要在第一次实例创建的时候加锁,以后不需要。下面先看一下DCLP的实现: ```cpp singleton* singleton::instance() { if(p == nullptr) { // 第一次检查 Lock lock; if(p == nullptr){ // 第二次检查 p = new singleton; } } return p; } ``` 基于上述,我们可以写出双重检查锁+自动回收 ```cpp class singleton { private: singleton() {} static singleton *p; static mutex lock_; public: static singleton *instance(); // 实现一个内嵌垃圾回收类 class CGarbo { public: ~CGarbo() { if(singleton::p) delete singleton::p; } }; static CGarbo Garbo; // 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象 }; singleton *singleton::p = nullptr; singleton::CGarbo Garbo; std::mutex singleton::lock_; singleton* singleton::instance() { if (p == nullptr) { lock_guard guard(lock_); if (p == nullptr) p = new singleton(); } return p; } ``` DCLP的关键在于,大多数对instance的调用会看到p是非空的,因此甚至不用尝试去初始化它。因此,DCLP在尝试获取锁之前检查p是否为空。只有当检查成功(也就是p还没有被初始化)时才会去获得锁,然后再次检查p是否仍然为空(因此命名为双重检查锁)。**第二次检查是必要,因为就像我们刚刚看到的,很有可能另一个线程偶然在第一次检查之后,获得锁成功之前初始化p。** 看起来上述代码非常美好,可是过了相当一段时间后,才发现这个漏洞,原因是:**内存读写的乱序执行(编译器问题)**。 再次考虑初始化`p`的那一行: ```cpp p = new singleton; ``` 这条语句会导致三个事情的发生: 1. 分配能够存储`singleton`对象的内存; 2. 在被分配的内存中构造一个`singleton`对象; 3. 让`p`指向这块被分配的内存。 可能会认为这三个步骤是按顺序执行的,但实际上只能确定步骤`1`是最先执行的,步骤`2`,`3`却不一定。问题就出现在这。 - 线程A调用instance,执行第一次p的测试,获得锁,按照1,3,执行,然后被挂起。此时p是非空的,但是p指向的内存中还没有Singleton对象被构造。 - 线程B调用instance,判定p非空, 将其返回给instance的调用者。调用者对指针解引用以获得singleton,噢,一个还没有被构造出的对象。bug就出现了。 DCLP能够良好的工作仅当步骤一和二在步骤三之前被执行,但是并没有方法在C或C++中表达这种限制。这就像是插在DCLP心脏上的一把匕首:我们需要在相对指令顺序上定义限制,但是我们的语言没有给出表达这种限制的方法。 ## 5.memory barrier指令 DCLP问题在C++11中,这个问题得到了解决。 因为新的C++11规定了新的内存模型,保证了执行上述3个步骤的时候不会发生线程切换,相当这个初始化过程是“原子性”的的操作,DCL又可以正确使用了,不过在C++11下却有更简洁的多线程singleton写法了,这个留在后面再介绍。 C++11之前解决方法是barrier指令。要使其正确执行的话,就得在步骤2、3直接加上一道memory barrier。强迫CPU执行的时候按照1、2、3的步骤来运行。 **第一种实现:** 基于operator new+placement new,遵循1,2,3执行顺序依次编写代码。 ``` // method 1 operator new + placement new singleton *instance() { if (p == nullptr) { lock_guard guard(lock_); if (p == nullptr) { singleton *tmp = static_cast(operator new(sizeof(singleton))); new(tmp)singleton(); p = tmp; } } return p; } ``` **第二种实现:** 基于直接嵌入ASM汇编指令mfence,uninx的barrier宏也是通过该指令实现的。 ```cpp #define barrier() __asm__ volatile ("lwsync") singleton *singleton::instance() { if (p == nullptr) { lock_guard guard(lock_); barrier(); if (p == nullptr) { p = new singleton(); } } return p; } ``` 通常情况下是调用cpu提供的一条指令,这条指令的作用是会阻止cpu将该指令之前的指令交换到该指令之后,这条指令也通常被叫做barrier。 上面代码中的**asm**表示这个是一条汇编指令,volatile是可选的,如果用了它,则表示向编译器声明不允许对该汇编指令进行优化。lwsync是POWERPC提供的barrier指令。 ## 6.静态局部变量 Scott Meyer在《Effective C++》中提出了一种简洁的singleton写法 ```cpp singleton &singleton::instance() { static singleton p; return p; } ``` - 单线程下,正确。 - C++11及以后的版本(如C++14)的多线程下,正确。 - C++11之前的多线程下,不一定正确。 原因在于在C++11之前的标准中并没有规定local static变量的内存模型。于是乎它就是不是线程安全的了。但是在C++11却是线程安全的,这是因为新的C++标准规定了当一个线程正在初始化一个变量的时候,其他线程必须得等到该初始化完成以后才能访问它。 上述使用的内存序: - memory_order_relaxed:松散内存序,只用来保证对原子对象的操作是原子的 - memory_order_acquire:获得操作,在读取某原子对象时,当前线程的任何后面的读写操作都不允许重排到这个操作的前面去,并且其他线程在对同一个原子对象释放之前的所有内存写入都在当前线程可见 - memory_order_release:释放操作,在写入某原子对象时,当前线程的任何前面的读写操作都不允许重排到这个操作的后面去,并且当前线程的所有内存写入都在对同一个原子对象进行获取的其他线程可见 ## 7.Atomic 在C++11之前的版本下,除了通过锁实现线程安全的Singleton外,还可以利用各个编译器内置的atomic operation来实现。 `java`和`c#`发现乱序问题后,就加了一个关键字`volatile`,在声明`p`变量的时候,要加上`volatile`修饰,编译器看到之后,就知道这个地方不能够reorder(一定要先分配内存,在执行构造器,都完成之后再赋值)。 而对于`c++`标准却一直没有改正,所以`VC++`在`2005`版本也加入了这个关键字,但是这并不能够跨平台(只支持微软平台)。 而到了`c++ 11`版本,为了从根本上消除这些漏洞,引入了适合多线程的内存模型。终于有了这样的机制帮助我们实现跨平台的方案。 ```cpp mutex singleton::lock_; atomic singleton::p; /* * std::atomic_thread_fence(std::memory_order_acquire); * std::atomic_thread_fence(std::memory_order_release); * 这两句话可以保证他们之间的语句不会发生乱序执行。 */ singleton *singleton::instance() { singleton *tmp = p.load(memory_order_relaxed); atomic_thread_fence(memory_order_acquire); if (tmp == nullptr) { lock_guard guard(lock_); tmp = p.load(memory_order_relaxed); if (tmp == nullptr) { tmp = new singleton(); atomic_thread_fence(memory_order_release); p.store(tmp, memory_order_relaxed); } } return p; } ``` 值得注意的是,上述代码使用两个比较关键的术语,获得与释放: - 获得是一个对内存的读操作,当前线程的任何后面的读写操作都不允许重排到这个操作的前面去。 - 释放是一个对内存的写操作,当前线程的任何前面的读写操作都不允许重排到这个操作的后面去。 acquire 和 release 通常都是配对出现的,目的是保证如果对同一个原子对象的 release 发生在 acquire 之前的话,release 之前发生的内存修改能够被 acquire 之后的内存读取全部看到。 ## 8.pthread_once 如果是在unix平台的话,除了使用atomic operation外,在不适用C++11的情况下,还可以通过pthread_once来实现Singleton。 原型如下: ``` int pthread_once(pthread_once_t once_control, void (init_routine) (void)); ``` 实现: ```cpp class singleton { private: singleton(); //私有构造函数,不允许使用者自己生成对象 singleton(const singleton &other); //要写成静态方法的原因:类成员函数隐含传递this指针(第一个参数) static void init() { p = new singleton(); } static pthread_once_t ponce_; static singleton *p; //静态成员变量 public: singleton *instance() { // init函数只会执行一次 pthread_once(&ponce_, &singleton::init); return p; } }; ``` ## 9.总结 本文讲解了几种单例模式,并讲解了线程安全的单例模式,以及不用C++11实现的几种线程安全的单例模式:memory barrier,静态局部变量,pthread_once方式,C++11的atomic实现等。 最后值得注意的是,针对上述单例类的析构函数请参考双重锁检查模式+自动回收实现,必须在类中声明一个静态局部变量,静态局部变量可以理解为全局变量,在程序结束时,自动调用该静态局部变量的析构函数,这就是为什么要在类中声明与定义嵌套类,而不是直接编写单例的析构函数。 ## 10.参考资料 > https://thorns.cn/2019/03/09/DCLP.html > https://www.cnblogs.com/liyuan989/p/4264889.html > https://segmentfault.com/a/1190000015950693 ================================================ FILE: design_pattern/singleton/barrier_singleton.cpp ================================================ // // Created by light on 20-2-7. // #include using namespace std; #include #define barrier() __asm__ volatile("lwsync") // method 1 operator new + placement new // singleton *instance() { // if (p == nullptr) { // lock_guard guard(lock_); // if (p == nullptr) { // singleton *tmp = static_cast(operator // new(sizeof(singleton))); new(p)singleton(); p = tmp; // } // } // return p; //} class singleton { private: singleton() {} static singleton *p; static mutex lock_; public: static singleton *instance(); }; singleton *singleton::p = nullptr; singleton *singleton::instance() { if (p == nullptr) { lock_guard guard(lock_); barrier(); if (p == nullptr) { p = new singleton(); } } return p; } ================================================ FILE: design_pattern/singleton/cpulpuls11_singleton.cpp ================================================ // // Created by light on 20-2-7. // #include using namespace std; #include #include // C++ 11版本之后的跨平台实现 class singleton { private: singleton() {} static mutex lock_; static atomic p; public: singleton *instance(); }; mutex singleton::lock_; atomic singleton::p; /* * std::atomic_thread_fence(std::memory_order_acquire); * std::atomic_thread_fence(std::memory_order_release); * 这两句话可以保证他们之间的语句不会发生乱序执行。 */ singleton *singleton::instance() { singleton *tmp = p.load(memory_order_relaxed); atomic_thread_fence(memory_order_acquire); if (tmp == nullptr) { lock_guard guard(lock_); tmp = p.load(memory_order_relaxed); if (tmp == nullptr) { tmp = new singleton(); atomic_thread_fence(memory_order_release); p.store(tmp, memory_order_relaxed); } } return p; } ================================================ FILE: design_pattern/singleton/dcl_singleton.cpp ================================================ // // Created by light on 20-2-7. // #include using namespace std; #include class singleton { private: singleton() {} static singleton *p; static mutex lock_; public: static singleton *instance(); // 实现一个内嵌垃圾回收类 class CGarbo { public: ~CGarbo() { if (singleton::p) delete singleton::p; } }; static CGarbo Garbo; // 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象 }; singleton *singleton::p = nullptr; singleton::CGarbo Garbo; std::mutex singleton::lock_; singleton *singleton::instance() { if (p == nullptr) { lock_guard guard(lock_); if (p == nullptr) p = new singleton(); } return p; } ================================================ FILE: design_pattern/singleton/hungrysingleton.cpp ================================================ // // Created by light on 20-2-6. // class singleton { private: singleton() {} static singleton *p; public: static singleton *instance(); }; singleton *singleton::p = new singleton(); singleton *singleton::instance() { return p; } ================================================ FILE: design_pattern/singleton/iazysingleton.cpp ================================================ // // Created by light on 20-2-6. // class singleton { private: singleton() {} static singleton *p; public: static singleton *instance(); }; singleton *singleton::p = nullptr; singleton *singleton::instance() { if (p == nullptr) p = new singleton(); return p; } ================================================ FILE: design_pattern/singleton/lock_singleton.cpp ================================================ // // Created by light on 20-2-7. // #include using namespace std; #include class singleton { private: singleton() {} static singleton *p; static mutex lock_; public: static singleton *instance(); }; singleton *singleton::p = nullptr; singleton *singleton::instance() { lock_guard guard(lock_); if (p == nullptr) p = new singleton(); return p; } ================================================ FILE: design_pattern/singleton/pthreadoncesingleton.cpp ================================================ // // Created by light on 20-2-6. // #include #include class singleton { private: singleton(); //私有构造函数,不允许使用者自己生成对象 singleton(const singleton &other); //要写成静态方法的原因:类成员函数隐含传递this指针(第一个参数) static void init() { p = new singleton(); } static pthread_once_t ponce_; static singleton *p; //静态成员变量 public: singleton *instance() { // init函数只会执行一次 pthread_once(&ponce_, &singleton::init); return p; } }; ================================================ FILE: design_pattern/singleton/static_local_singleton.cpp ================================================ // // Created by light on 20-2-7. // 在C++11标准下,《Effective C++》提出了一种更优雅的单例模式实现,使用函数内的 // local static 对象。 这种方法也被称为Meyers' Singleton。 // #include using namespace std; class singleton { private: singleton() {} public: static singleton &instance(); }; singleton &singleton::instance() { static singleton p; return p; } ================================================ FILE: effective_cpp/2.cpp ================================================ // // Created by light on 19-9-11. // #include using namespace std; struct A { #define p "hello" }; class C { public: // static const 可以直接在类内部初始化 // no-const static 只能在外面初始化 static const int NUM = 3; //声明 enum con { NUM1 = 3 }; }; #define MAX(a,b) ((a) > (b) ? (a) : (b)) template inline int Max(const T& a, const T& b){ return (a>b ? a:b); } const int C::NUM; // 定义 int main() { cout << p << endl; // macro is global C c; cout << &c.NUM << endl; // 未定义的引用,需要定义 cout << C::NUM1 << endl; // cout << &C:NUM1 << endl; //error enum no address int a=5, b=0; cout<An object of a class derived from an abstract class! > Code example:[test.cpp](./test.cpp)、[pure_virtual.cpp](./pure_virtual.cpp) ## 2.Implement abstract classes Abstract class:Pure virtual functions can be called within member functions.Pure virtual functions cannot be used inside constructors / destructors. If a class derives from an abstract class, it must implement all pure virtual functions in the base class to become a non abstract class. ```cpp // A is abstract class class A { public: virtual void f() = 0; // pure virtual function void g(){ this->f(); } A(){} // 构造函数 }; class B : public A{ public: void f(){ cout<<"B:f()"< Code Example:[abstract.cpp](./abstract.cpp) ## 3.Key point - [Pure virtual functions make a class abstract](./interesting_facts1.cpp) ```cpp // Abstract class contains at least one pure virtual function class Base{ public: virtual void show() = 0; // 纯虚函数 int getX() { return x; } // 普通成员函数 private: int x; }; ``` - [Pointers and references to abstract class types](./interesting_facts2.cpp) ```cpp class Derived : public Base { public: void show() { cout << "In Derived \n"; } // 实现抽象类的纯虚函数 Derived(){} // 构造函数 }; int main(void) { //Base b; // error! 不能创建抽象类的对象 //Base *b = new Base(); error! Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象 bp->show(); return 0; } ``` - [If we do not override pure virtual functions in the derived class, the derived class will also become an abstract class](./interesting_facts3.cpp) ```cpp // Derived为抽象类 class Derived: public Base { public: // void show() {} }; ``` - [Abstract classes can have constructors](./interesting_facts4.cpp) ```cpp // abstract class class Base { protected: int x; public: virtual void fun() = 0; Base(int i) { x = i; } // constructor function }; // 派生类 class Derived: public Base { int y; public: Derived(int i, int j) : Base(i) { y = j; } // constructor function void fun() { cout << "x = " << x << ", y = " << y; } }; ``` - [A constructor cannot be a virtual function, and a destructor can be a virtual destructor](./interesting_facts5.cpp) ```cpp // 抽象类 class Base { public: Base(){ cout << "Constructor: Base" << endl; } virtual ~Base(){ cout << "Destructor : Base" << endl; } virtual void func() = 0; }; class Derived: public Base { public: Derived(){ cout << "Constructor: Derived" << endl; } ~Derived(){ cout << "Destructor : Derived" << endl;} void func(){cout << "In Derived.func()." << endl;} }; ``` >When the base class pointer points to a derived class object and removes the object, we may want to call the appropriate destructor. >The destructor can only be called if it is not a virtual destructor. ## 4.Complete example Abstract classes are inherited and implemented by derived classes! > Code Example:[derived_full.cpp](./derived_full.cpp) ================================================ FILE: english/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()"< using namespace std; class Base { int x; public: virtual void fun() = 0; int getX() { return x; } }; class Derived: public Base { int y; public: void fun() { cout << "fun() called"; } // 实现了fun()函数 }; int main(void) { Derived d; d.fun(); return 0; } ================================================ FILE: english/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: english/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: english/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: english/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: english/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: english/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: english/basic_content/abstract/test.cpp ================================================ /** * @file test.cpp * @brief C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它!通过声明中赋值0来声明纯虚函数! * 纯虚函数:没有函数体的虚函数 * @author 光城 * @version v1 * @date 2019-07-20 */ /** * @brief 抽象类 */ class Test { // Data members of class public: // Pure Virtual Function virtual void show() = 0; /* Other members */ }; ================================================ FILE: english/basic_content/assert/README.md ================================================ # Things about assert ## About Author: ![](../img/wechat.jpg) ## 1.First assertion case Assert, **is macro,rather than function**. Assert the prototype of a macro is defined in (C) or (C++). If its condition is false or returns an error, program execution will terminate. You can disable assert by defining macro `NDEBUG`, **But it needs to be at the beginning of the source code,before include .** ```c void assert(int expression); ``` > Code Example:[assert.c](./assert.c) ```c #include #include int main() { int x = 7; /* Some big code in between and let's say x is accidentally changed to 9 */ x = 9; // Programmer assumes x to be 7 in rest of the code assert(x==7); /* Rest of the code */ return 0; } ``` Output: ```c assert: assert.c:13: main: Assertion 'x==7' failed. ``` ## 2.Assertion and normal error handling + Assertions are mainly used to check for logically impossible situations. > For example, they can be used to check the state that code expects before it starts to run, or after the run is complete. Unlike normal error handling, assertions are usually disabled at run time. + Ignore the assertion and add at the beginning of the code: ```c++ #define NDEBUG // Adding this line,then assert will be disable ``` > Code Example:[ignore_assert.c](./ignore_assert.c) ================================================ FILE: english/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: english/basic_content/assert/ignore_assert.c ================================================ /** * @file ignore_assert.c * @brief Ignore assertion * @author Light-City * @version v1 * @date 2019-07-25 */ #define NDEBUG // ignore assertion #include int main(){ int x=7; assert(x==5); return 0; } ================================================ FILE: english/basic_content/bit/README.md ================================================ ## About Author: ![](../img/wechat.jpg) ## What is Bit field ? “ Bit field is a kind of data structure.Data can be stored compactly in bits, And allows the programmer to operate on the bits of this structure. One of the advantages of this data structure is that it can save storage space in data units, which is particularly important when programs need thousands of data units. The second advantage is that bit segments can easily access part of the contents of an integer value, which can simplify the program source code. The disadvantage of this data structure is that bit segment implementation depends on specific machines and systems, and different results may be obtained in different platforms, which leads to the fact that bit segments are not portable in nature - The layout of bit fields in memory is machine dependent - The type of bit field must be integer or enumeration type. The behavior of bit field in signed type depends on the implementation - The addressing operator (&) cannot be applied to a bit field, and no pointer can point to a bit field of a class ## Bit field usage Bit fields usually use struct declarations, which set the name of each bit field member and determine its width: ``` struct bit_field_name { type member_name : width; }; ``` | Elements | Description | | -------------- | ------------------------------------------------------------ | | bit_field_name | Bit field structure name | | type | must be int、signed int or unsigned int type | | member_name | | | width | | For example, declare the following bit field: ``` struct _PRCODE { unsigned int code1: 2; unsigned int cdde2: 2; unsigned int code3: 8; }; struct _PRCODE prcode; ``` This definition makes' prcode 'contain two 2-bit fields and one 8-bit field. We can use the member operator of the structure to assign values to it ``` prcode.code1 = 0; prcode.code2 = 3; procde.code3 = 102; ``` When assigning a value, it should be noted that the size of the value should not exceed the capacity of the bit field member,For example prcode.code3 Is a bit domain member of 8 bits. Its capacity is 2^8 = 256,Assignment range should be [0,255]。 ## Size and alignment of bit fields ### Size of bit field For example, the following bit field: ``` struct box { unsigned int a: 1; unsigned int : 3; unsigned int b: 4; }; ``` There is an unnamed bit field in the middle of the bit field structure, which occupies 3 bits and only plays a filling role and has no practical significance. The padding makes the structure use 8 bits in total. But C language uses unsigned int as the basic unit of bit field,Even if the only member of a structure is a bit field of 1 bit, the size of the structure is the same as that of an unsigned int.In some systems, the unsigned int is 16 bits, and in x86 systems it is 32 bits.For the following articles, the default value of unsigned int is 32 bits. ### Alignment of bit fields A bit field member is not allowed to cross the boundary of two unsigned ints. If the total number of bits declared by a member exceeds the size of an unsigned int, the editor will automatically shift the bit field members to align them according to the boundary of the unsigned int For example: ``` struct stuff { unsigned int field1: 30; unsigned int field2: 4; unsigned int field3: 3; }; ``` `field1` + `field2` = 34 Bits,beyond 32 Bits, Complier`field2` move to the next unsigned int unit. stuff.field1 and stuff.field2 will leave the 2 Bits space, stuff.field3 follow closely stuff.field2.The structure is now of size 2 * 32 = 64 Bits。 This hole can be filled with the unnamed bit field member mentioned earlier, or we can use an unnamed bit field member with a width of 0 to align the next field member with the next integer. For example: ``` struct stuff { unsigned int field1: 30; unsigned int : 2; unsigned int field2: 4; unsigned int : 0; unsigned int field3: 3; }; ``` Between stuff.field1 and stuff.field2 there is a 2 Bits space. Stuff.field3 will be stored in the next unsigned in. The size of this structure is 3 * 32 = 96 Bits。 Code:[learn.cpp](learn.cpp) ## Initialization of bit field and remapping of bit ### Initialization The initialization method of bit field is the same as that of ordinary structure. Here, two methods are listed as follows: ``` struct stuff s1= {20,8,6}; ``` Or directly assign values to the bit field members: ``` struct stuff s1; s1.field1 = 20; s1.field2 = 8; s1.field3 = 4; ``` ### Re-mapping of bit field Declare a bit field with a size of 32 bits ``` struct box { unsigned int ready: 2; unsigned int error: 2; unsigned int command: 4; unsigned int sector_no: 24; }b1; ``` #### Zeroing bit field by remapping ``` int* p = (int *) &b1; // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址" *p = 0; // clear s1, set all members to zero ``` #### The 32 bits bit field is remapped to the unsigned int type by union Let's briefly introduce the alliance > "Union" is a special class and a data structure of construction type.Many different data types can be defined in a "union". Any kind of data defined by the "union" can be loaded into a variable described as the "union" type. These data share the same memory to save space > > There are some similarities between "union" and "structure". But there are essential differences between them.In a structure, each member has its own memory space. The total length of a structure variable is the sum of the length of each member (except for empty structure, and boundary adjustment is not considered).In Union, members share a certain amount of memory space, and the length of a union variable is equal to the longest length of each member.It should be noted that the so-called sharing does not mean that multiple members are loaded into a joint variable at the same time, but that the joint variable can be assigned any member value, but only one value can be assigned at a time. We can declare the following Union: ``` union u_box { struct box st_box; unsigned int ui_box; }; ``` In x86 system, unsigned int and box are 32 Bits, Through this combination, St_ Box and UI_ Box shares a piece of memory.Which bit in the specific bit field corresponds to which bit of the unsigned int depends on the compiler and hardware.Use union to return the bit field to zero. The code is as follows: ``` union u_box u; u.ui_box = 0; ``` > From: ================================================ FILE: english/basic_content/bit/learn.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< using namespace std; class A { public: virtual void f()//Implement a virtual function { cout << "Base A::f() " << endl; } }; class B:public A // 必须为共有继承,否则后面调用不到,class默认为私有继承! { public: virtual void f()//Virtual function implementation, virtual keyword in subclass can not be appearence { cout << "Derived B::f() " << endl; } }; int main() { A a;//Base class object B b;//an object of derived type A* pa = &a;//The parent class pointer points to the parent object pa->f();//Call the function of the parent class pa = &b; //The parent class pointer points to the subclass object, which is implemented in polymorphism pa->f();//Call the function with the same name of the derived class return 0; } ================================================ FILE: english/basic_content/c_poly/c_examp.c ================================================ /** * @file c_examp.c * @brief C实现多态 * @author 光城 * @version v1 * @date 2019-08-06 */ #include /// Define a pointer of function typedef void (*pf) (); /** * @brief parent class */ typedef struct _A { pf _f; }A; /** * @brief child class */ typedef struct _B { A _b; ///< The inheritance of the parent class can be realized by defining an object of the base class in the subclass }B; void FunA() { printf("%s\n","Base A::fun()"); } void FunB() { printf("%s\n","Derived B::fun()"); } int main() { A a; B b; a._f = FunA; b._b._f = FunB; A *pa = &a; pa->_f(); pa = (A *)&b; /// The parent class pointer points to the object of the subclass. Because of the type mismatch, it needs to be forced pa->_f(); return 0; } ================================================ FILE: english/basic_content/const/README.md ================================================ ## 1.The Definition Of const Const type is that people use type specifier **const** signiture type to demonstrate,const variables or objects can not be updated。 ## 2. Effect Of Const (1)define variable ``` const int a=100; ``` (2) check type The difference between const variable and define variable: ~~**Constants have types, which can be checked by the compiler; the define macro definition has no data type, it is just a simple string replacement, and cannot be checked for security **~~ Thanks for point this bug out. > https://github.com/Light-City/CPlusPlusThings/issues/5 Only variables of type integer or enumeration are defined by `const`. In other cases, it is only a 'const' qualified variable and should not be confused with constants. (3)prevent modification, protect and increase program robustness ``` void f(const int i){ i++; //error! } ``` (4)Save space and avoid unnecessary memory allocation From the compile point of view, the variables can be defined by const only give the corresponding memory address. Meanwhile the #define gives an immediate number. ## 3.const object is the file local variable by default

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

> Access of variables not modified by const in different files ``` // file1.cpp int ext // file2.cpp #include /** * by 光城 * compile: g++ -o file file2.cpp file1.cpp * execute: ./file */ extern int ext; int main(){ std::cout<<(ext+10)< using namespace std; int main(){ int num=0; int * const ptr=# //const指针必须初始化!且const指针的值不能修改 int * t = # *t = 1; cout<<*ptr< using namespace std; int main(){ const int num=0; int * const ptr=# //error! const int* -> int* cout<<*ptr< #include"apple.cpp" using namespace std; Apple::Apple(int i):apple_number(i) { } int Apple::add(int num){ take(num); } int Apple::add(int num) const{ take(num); } void Apple::take(int num) const { cout<<"take func "< #include"apple.cpp" using namespace std; Apple::Apple(int i) { } int Apple::add(int num){ take(num); } int Apple::add(int num) const{ take(num); } void Apple::take(int num) const { cout<<"take func "< #include"apple.cpp" using namespace std; Apple::Apple(int i):apple_number(i) { } int Apple::add(int num){ take(num); } int Apple::add(int num) const{ take(num); } void Apple::take(int num) const { cout<<"take func "< #include"apple.cpp" using namespace std; const int Apple::apple_number=10; Apple::Apple(int i) { } int Apple::add(int num){ take(num); } int Apple::add(int num) const{ take(num); } void Apple::take(int num) const { cout<<"take func "< #include"apple.cpp" using namespace std; const int Apple::apple_number=10; int Apple::ap=666; Apple::Apple(int i) { } int Apple::add(int num){ take(num); } int Apple::add(int num) const{ take(num); } void Apple::take(int num) const { cout<<"take func "< using namespace std; void f(const int i){ i=10; // error: assignment of read-only parameter ‘i’ cout< using namespace std; int main(){ const int b = 10; b = 0; //error const string s = "helloworld"; const int i,j=0; } ================================================ FILE: english/basic_content/const/extern_const/const_file1.cpp ================================================ extern const int ext=12; ================================================ FILE: english/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< /** * by 光城 * compile: g++ -o file file2.cpp file1.cpp * execute: ./file */ extern int ext; int main(){ std::cout<<(ext+10)< using namespace std; int main(){ const int *ptr; *ptr=10; //error } ================================================ FILE: english/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: english/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< using namespace std; int main(){ int num=0; int * const ptr=# //const指针必须初始化!且const指针的值不能修改 int * t = # *t = 1; cout<<*ptr< using namespace std; int main(){ const int num=0; int * const ptr=# //error! const int* -> int* cout<<*ptr< using namespace std; int main(){ const int num=10; const int * const ptr=# //error! const int* -> int* cout<<*ptr< using namespace std; int main(){ const int p = 3; const int * const ptr = &p; cout<<*ptr<vec; typedef decltype(vec.begin()) vectype; for (vectype i = vec.begin; i != vec.end(); i++) { //... } ``` like auto, improves the readability of the code. ### 1.3 Reuse anonymous types In C + +, we sometimes encounter some anonymous types, such as: ```c++ struct { int d ; doubel b; }anon_s; ``` With decltype, we can reuse this anonymous structure: ```c++ decltype(anon_s) as ;//Defines an anonymous structure above ``` ### 1.4 In generic programming, auto is used to trace the return value type of function ```c++ template auto multiply(T x, T y)->decltype(x*y) { return x*y; } ``` Code:[decltype.cpp](decltype.cpp) ## 2.Discriminant rules For decltype (E), the results are affected by the following conditions: If e is a marker expression or class member access expression without parentheses, decltype (E) of is the type of entity named by E.In addition, if e is an overloaded function, it will result in compilation errors. Otherwise, assume that the type of E is t, and if e is a dying value, then decltype (E) is t&& Otherwise, assume that the type of E is t, and if e is a dying value, then decltype (E) is t&& Otherwise, assuming that the type of E is t, decltype (E) is t. Markers are defined by programmers except for keywords, literals and other tags that the compiler needs to use. The expression corresponding to a single marker is a marker expression. For example: ```c++ int arr[4] ``` Then arr is a marker expression,ranther than arr[3]+0 Example: ```c++ int i = 4; int arr[5] = { 0 }; int *ptr = arr; struct S{ double d; }s ; void Overloaded(int); void Overloaded(char);// reload function int && RvalRef(); const bool Func(int); //Rule 1: derivation is its type decltype (arr) var1; //int 标记符表达式 decltype (ptr) var2;//int * 标记符表达式 decltype(s.d) var3;//doubel 成员访问表达式 //decltype(Overloaded) var4;//重载函数。编译错误。 //规则二:将亡值。推导为类型的右值引用。 decltype (RvalRef()) var5 = 1; //规则三:Lvalue, derived as a reference to the type decltype ((i))var6 = i; //int& decltype (true ? i : i) var7 = i; //int& A conditional expression returns an lvalue decltype (++i) var8 = i; //int& ++i返回i的左值。 decltype(arr[5]) var9 = i;//int&. []操作返回左值 decltype(*ptr)var10 = i;//int& *操作返回左值 decltype("hello")var11 = "hello"; //const char(&)[9] 字符串字面常量为左值,且为const左值。 //Rule 4: if none of the above is true, this type is derived decltype(1) var12;//const int decltype(Func(1)) var13=true;//const bool decltype(i++) var14 = i;//int i++返回右值 ``` From:https://www.cnblogs.com/QG-whz/p/4952980.html ================================================ FILE: english/basic_content/decltype/decltype.cpp ================================================ /** * @file decltype.cpp * @brief g++ -o decltype decltype.cpp -std=c++11 * @author 光城 * @version v1 * @date 2019-08-08 */ #include #include using namespace std; /** * 泛型编程中结合auto,用于追踪函数的返回值类型 */ template auto multiply(T x, T y)->decltype(x*y) { return x*y; } int main() { int nums[] = {1,2,3,4}; vector vec(nums,nums+4); vector::iterator it; for(it=vec.begin();it!=vec.end();it++) cout<<*it<<" "; cout< using namespace std; enum Color {RED,BLUE}; enum Feeling {EXCITED,BLUE}; int main() { return 0; } ``` - Implicit conversion to int - The actual type used to represent an enumerated variable cannot be explicitly specified, Therefore, forward declaration of enumeration types cannot be supported. Implementation:[tradition_color.cpp](tradition_color.cpp) ## Classic Method A simple way to solve the problem of naming conflicts caused by unlimited scope is to prefix the enumeration variables. Change the above example to COLOR_BLUE and FEELING_BLUE。 Generally speaking, we usually prefix all constants for uniformity.But the code for defining enumeration variables is cumbersome.This may have to be done in the C program. But C++ coder do not like this method。Alternatives is namespace: ```c++ namespace Color { enum Type { RED=15, YELLOW, BLUE }; }; ``` Then you can use `Color::Type c = Color::RED;` to define the new enumeration。If after`using namespace Color` ,the prefix can also be omitted to simplify the code.However, the scope closure provided by a namespace is not high because it can be subsequently extended.In large projects, it is still possible for different people to give different things the same enumeration type names. A more "effective" approach is to limit its scope with a class or struct.For example:The new variable is defined in the same way as in the namespace above. This way, you don't have to worry about the class being modified elsewhere.We use structs instead of classes because we want these constants to be publicly accessible. ```c++ struct Color1 { enum Type { RED=102, YELLOW, BLUE }; }; ``` Implementation:[classic_practice.cpp](classic_practice.cpp) ## C++11 Enum class The above approach solves the first problem, but it can not do anything for the latter two.Fortunately,C ++ 11 standard introduces enum class. It can solve the above problems. - The scope of the new enum is no longer global - Cannot be implicitly converted to another type ```c++ /** * @brief C++11 enum class * Equals to enum class Color2:int */ enum class Color2 { RED=2, YELLOW, BLUE }; Color2 c2 = Color2::RED; cout << static_cast(c2) << endl; //! ``` - You can specify a specific type to store enum ```c++ enum class Color3:char; // Forward statement // Definition enum class Color3:char { RED='r', BLUE }; char c3 = static_cast(Color3::RED); ``` Implementation:[classic_practice.cpp](classic_practice.cpp) ## Enum types in class Sometimes we want certain constants to work only in classes. Because the macro constant defined by a is global, it can not achieve the purpose, so we want to use const to modify data members.The const data member does exist, but its meaning is not what we expected. Data members are constants only for the lifetime of an objec. However, it is variable for the whole class, because the class can create multiple objects, and the values of const data members of different objects can be different. Cannot be initialized in a class declaration const data memeber。The following usage is incorrect,Because the compiler does not know what the value of size is when the object of the class is not created.(c++11) ```c++ class A { const int SIZE = 100; // Error,Attempt to initialize const data member in class declaration int array[SIZE]; // Error,Unknown size }; ``` This should be done in the initialization list of the class's constructor: ```c++ class A { A(int size); // Constructor const int SIZE ; }; A::A(int size) : SIZE(size) // The definition of Struct { } A a(100); // The size of Object A is 100 A b(200); // The size of Object B is 200 ``` How can I establish constants that are constant throughout a class? It should be implemented with enumeration constants in the class. Such as: ```c++ class Person{ public: typedef enum { BOY = 0, GIRL }SexType; }; //Access via Person::BOY or Person::GIRL. ``` Enum constants do not take up the storage space of the object . They are all evaluated at compile time Drawback of Enum:Its implied data type is an integer, the maximum is limited, and it cannot represent floating point. ================================================ FILE: english/basic_content/enum/classic_practice.cpp ================================================ /** * @file classic_practice.cpp * @brief g++ -o classic_practice classic_practice.cpp -std=c++11 * @author 光城 * @version v1 * @date 2019-08-07 */ #include using namespace std; /** * @brief namespace解决作用域不受限 */ namespace Color { enum Type { RED=15, YELLOW, BLUE }; }; /** * @brief 上述如果 using namespace Color 后,前缀还可以省去,使得代码简化。 * 不过,因为命名空间是可以随后被扩充内容的,所以它提供的作用域封闭性不高。 * 在大项目中,还是有可能不同人给不同的东西起同样的枚举类型名。 * 更“有效”的办法是用一个类或结构体来限定其作用域。 * * 定义新变量的方法和上面命名空间的相同。 * 不过这样就不用担心类在别处被修改内容。 * 这里用结构体而非类,一是因为本身希望这些常量可以公开访问, * 二是因为它只包含数据没有成员函数。 */ struct Color1 { enum Type { RED=102, YELLOW, BLUE }; }; /** * @brief C++11的枚举类 * 下面等价于enum class Color2:int */ enum class Color2 { RED=2, YELLOW, BLUE }; enum class Color3:char; // 前向声明 // 定义 enum class Color3:char { RED='r', BLUE }; int main() { // 定义新的枚举变量 Color::Type c = Color::RED; cout<(c2) << endl; char c3 = static_cast(Color3::RED); cout< using namespace std; enum Color {RED,BLUE}; enum Feeling {EXCITED,BLUE}; int main() { return 0; } ================================================ FILE: english/basic_content/explicit/README.md ================================================ # Things about explicit ## About Author: ![](../img/wechat.jpg) - explicit When decorating a constructor, you can prevent implicit conversion and copy initialization - explicit When modifying a conversion function, you can prevent implicit conversion, except for context conversion Code :[.explicit.cpp](./explicit.cpp) Reference Link: > https://stackoverflow.com/questions/4600295/what-is-the-meaning-of-operator-bool-const ================================================ FILE: english/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: english/basic_content/extern/README.md ================================================ # extern "C" ## About Author: ![](../img/wechat.jpg) ## 1. Compiler difference between C and C ++ In C + +, we often see extern "C" modifier function in the header file. What's the effect. Is a function defined in C language module for C + + link. Although C + + is compatible with C, the symbols generated by function compilation in C + + files are different from those generated by C language.Because C + + supports function overloading, the symbols generated by C + + function compilation have the information of function parameter type, while C does not. Take `int add(int a, int b)` for example. The C + + compiler generates the. O file, `add` becomes `add_int_int` and so on, while C would be like this `_add`, that is:For the same function, in C and C + +, the symbols generated after compilation are different. This leads to a problem: if the function implemented in C + + is implemented in C language, an error will occur when compiling the link, indicating that the corresponding symbol cannot be found. At this time`extern "C"` works:Tell the linker to find its `_ C `language symbols such as `add` are not modified by C ++. ## 2.C ++ calls C functions When referring to the header file of C, you need to add `extern "C"` ```c++ //add.h #ifndef ADD_H #define ADD_H int add(int x,int y); #endif //add.c #include "add.h" int add(int x,int y) { return x+y; } //add.cpp #include #include "add.h" using namespace std; int main() { add(2,3); return 0; } ``` Compile: ``` //Generate add.o file gcc -c add.c ``` Link: ``` g++ add.cpp add.o -o main ``` Without extern "C": ```c++ > g++ add.cpp add.o -o main add.o:在函数‘main’中: add.cpp:(.text+0x0): `main'被多次定义 /tmp/ccH65yQF.o:add.cpp:(.text+0x0):第一次在此定义 /tmp/ccH65yQF.o:在函数‘main’中: add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用 add.o:在函数‘main’中: add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用 collect2: error: ld returned 1 exit status ``` With extern "C": `add.cpp` ```c++ #include using namespace std; extern "C" { #include "add.h" } int main() { add(2,3); return 0; } ``` When compiling, you must pay attention to generating intermediate files add.o through GCC ``` gcc -c add.c ``` Compile: ``` g++ add.cpp add.o -o main ``` Code: - [add.h](extern_c++/add.h) - [add.c](extern_c++/add.c) - [add.cpp](extern_c++/add.cpp) ## 2.Calling C++ function in C `extern "C"` It is a syntax error in C, which needs to be put in the C + + header file. ```c // add.h #ifndef ADD_H #define ADD_H extern "C" { int add(int x,int y); } #endif // add.cpp #include "add.h" int add(int x,int y) { return x+y; } // add.c extern int add(int x,int y); int main() { add(2,3); return 0; } ``` Compile: ```c g++ -c add.cpp ``` Link: ``` gcc add.c add.o -o main ``` Code: - [add.h](extern_c/add.h) - [add.c](extern_c/add.c) - [add.cpp](extern_c/add.cpp) In the header file of C language, the external function can only be specified as extern type. The declaration of extern "C" is not supported in C language. There will be compiler syntax error when the. C file contains extern "C". Therefore, the use of external "C" is all placed in CPP program related files or its header files. The following forms are summarized: (1)C + + calls C functions: ```c++ //xx.h extern int add(...) //xx.c int add(){ } //xx.cpp extern "C" { #include "xx.h" } ``` (2)C calls C + + functions ```c //xx.h extern "C"{ int add(); } //xx.cpp int add(){ } //xx.c extern int add(); ``` ================================================ FILE: english/basic_content/extern/extern_c/add.c ================================================ extern int add(int x,int y); int main() { add(2,3); return 0; } ================================================ FILE: english/basic_content/extern/extern_c/add.cpp ================================================ #include "add.h" int add(int x,int y) { return x+y; } ================================================ FILE: english/basic_content/extern/extern_c/add.h ================================================ #ifndef ADD_H #define ADD_H extern "C" { int add(int x,int y); } #endif ================================================ FILE: english/basic_content/extern/extern_c++/add.c ================================================ #include "add.h" int add(int x,int y) { return x+y; } ================================================ FILE: english/basic_content/extern/extern_c++/add.cpp ================================================ #include using namespace std; extern "C" { #include "add.h" } int main() { add(2,3); return 0; } ================================================ FILE: english/basic_content/extern/extern_c++/add.h ================================================ #ifndef ADD_H #define ADD_H extern int add(int x,int y); #endif ================================================ FILE: english/basic_content/friend/README.md ================================================ # Friend and Friend Class ## About Author: ![](../img/wechat.jpg) ## 0.Summary Friends provide a mechanism for ordinary functions or class member functions to access private or protected members in another class.In other words, there are two forms of friends: (1)Friend Function:Ordinary functions access a private or protected member of a class. (2)Friend Class:Member functions in class a access private or protected members in class B Advantages: improve the efficiency of the program. Disadvantages: it destroys the encapsulation of classes and the transparency of data. Conclusion: - Access to private members - Breaking encapsulation - Friendship is not transitive - The unidirectionality of friend relationship - There are no restrictions on the form and number of friend declarations ## 1.Friend function It is declared in any region of the class declaration, and the definition is outside the class. ``` friend (); ``` Note that the friend function is only a common function, not a class member function of this class. It can be called anywhere. In the friend function, private or protected members of the class can be accessed through the object name. Code:[friend_func.cpp](friend_func.cpp) ```c++ #include using namespace std; class A { public: A(int _a):a(_a){}; friend int geta(A &ca); ///< Friend function private: int a; }; int geta(A &ca) { return ca.a; } int main() { A a(3); cout<; ``` Class B is a friend of class A, so class B can directly access private members of A. Code:[friend_class.cpp](friend_class.cpp) ```c++ #include using namespace std; class A { public: A(int _a):a(_a){}; friend class B; private: int a; }; class B { public: int getb(A ca) { return ca.a; }; }; int main() { A a(3); B b; cout< using namespace std; class A { public: A(int _a):a(_a){}; friend class B; private: int a; }; class B { public: int getb(A ca) { return ca.a; }; }; int main() { A a(3); B b; cout< using namespace std; class A { public: A(int _a):a(_a){}; friend int geta(A &ca); ///< 友元函数 private: int a; }; int geta(A &ca) { return ca.a; } int main() { A a(3); cout< using namespace std; /** * @brief 定义了一个变量pFun,这个变量是个指针,指向返回值和参数都是空的函数的指针! */ void (*pFun)(int); /** * @brief 代表一种新类型,不是变量!所以与上述的pFun不一样! */ typedef void (*func)(void); void myfunc(void) { cout<<"asda"< #include "inline.h" using namespace std; /** * @brief To work, inline should be placed with function definition. Inline is a kind of "keyword for implementation, not for declaration" * * @param x * @param y * * @return */ int Foo(int x,int y); // Function declaration inline int Foo(int x,int y) // Function definition { return x+y; } // It is recommended to add the keyword "inline" to the definition! inline void A::f1(int x){ } int main() { cout< using namespace std; class Base { public: inline virtual void who() { cout << "I am Base\n"; } virtual ~Base() {} }; class Derived : public Base { public: inline void who() // { cout << "I am Derived\n"; } }; int main() { // Base b; b.who(); // Base *ptr = new Derived(); ptr->who(); // delete ptr; ptr = nullptr; return 0; } ``` ================================================ FILE: english/basic_content/inline/inline.cpp ================================================ #include #include "inline.h" using namespace std; /** * @brief inline要起作用,inline要与函数定义放在一起,inline是一种“用于实现的关键字,而不是用于声明的关键字” * * @param x * @param y * * @return */ int Foo(int x,int y); // 函数声明 inline int Foo(int x,int y) // 函数定义 { return x+y; } // 定义处加inline关键字,推荐这种写法! inline void A::f1(int x){ } /** * @brief 内联能提高函数效率,但并不是所有的函数都定义成内联函数!内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。 * 如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收货会更少!另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。 * 以下情况不宜用内联: * (1) 如果函数体内的代码比较长,使得内联将导致内存消耗代价比较高。 * (2) 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。 * * @return */ int main() { cout< using namespace std; class Base { public: inline virtual void who() { cout << "I am Base\n"; } virtual ~Base() {} }; class Derived : public Base { public: inline void who() // 不写inline时隐式内联 { cout << "I am Derived\n"; } }; int main() { // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 Base b; b.who(); // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。 Base *ptr = new Derived(); ptr->who(); // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。 delete ptr; return 0; } ================================================ FILE: english/basic_content/macro/README.md ================================================ # Story about Macro ![](../img/wechat.jpg) ## 1.The macro contains special symbols Several type:`#`,`##`,`\` ### 1.1 String operator(#) **Using a # before macro parameter,The preprocessor converts this parameter into an array of characters**,In other words:**# is “stringlize”,The#, which appears in the macro definition, is to convert the following parameter into a string **。 **Attention:It can only be used in macro definitions that have passed in parameters, and must be placed before the parameter name in the macro definition body.** For example: ```c++ #define exp(s) printf("test s is:%s\n",s) #define exp1(s) printf("test s is:%s\n",#s) #define exp2(s) #s int main() { exp("hello"); exp1(hello); string str = exp2( bac ); cout<(b) ? (a) \ :(b)) int main() { int max_val = MAX(3,6); cout<0) fun() ``` When this macro is expanded, it will be: ``` if(a>0) f1(); f2(); ``` In order to solve this problem, when writing code, usually can adopt `{}`。 ex: ```c++ #define fun() {f1();f2();} if(a>0) fun(); // 宏展开 if(a>0) { f1(); f2(); }; ``` However, you will find that there is a semicolon after the macro is expanded, so the actual syntax is not correct.(Although the compiler runs well, there is no semicolon). ### 2.2 Avoid using goto to control flow In some functions, we may need to do some cleaning before the return statement, such as releasing the memory space requested by malloc at the beginning of the function. Using goto is always a simple method: ```c++ int f() { int *p = (int *)malloc(sizeof(int)); *p = 10; cout<<*p< ================================================ FILE: english/basic_content/macro/do_while.cpp ================================================ #include #include using namespace std; #define f1() cout<<"f1()"<0) fun(); if(2>0) fun1(); f(); ff(); fc(); return 0; } ================================================ FILE: english/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< using namespace std; int count=0; // 全局(::)的count class A { public: static int count; // 类A的count (A::count) }; // 静态变量必须在此处定义 int A::count; int main() { ::count=1; // 设置全局的count为1 A::count=5; // 设置类A的count为2 cout< References must be initialized, and pointers can be uninitialized When we define a reference, we must specify an initial value for it, but the pointer does not ```c++ int &r; //Illegal, no initialization reference int *p; //It is legal, but p is a wild pointer. You should be careful when using it ``` > Reference cannot be null and pointer can be null Since the reference cannot be null, we do not need to test its validity when using a reference. When using a pointer, we need to first judge whether the pointer is a null pointer, otherwise it may cause the program to crash. ```c++ void test_p(int* p) { if(p != null_ptr) // *p = 3; return; } void test_r(int& r) { r = 3; // return; } ``` > References cannot change targets The pointer can be changed at any time, but the reference can only point to the object pointed to during initialization, and cannot be changed. ``` int a = 1; int b = 2; int &r = a; // int *p = &a; // p = &b; // r = b; //引 ``` ## 2.Reference #### lvalue reference General reference, which generally represents the identity of an object. #### rvalue reference An R-value reference is a reference that must be bound to an R-value (a temporary object, an object to be destroyed), and generally represents the value of an object. An R-value reference is a reference that must be bound to an R-value (a temporary object, an object to be destroyed), and generally represents the value of an object. - It eliminates unnecessary copy of objects when two objects interact, saves operation storage resources and improves efficiency - It can define generic functions more concisely and clearly #### Reference collapse - `X& &`、`X& &&`、`X&& &` can be folded into `X&` - `X&& &&` can be floded into `X&&` The reference of C ++ **At the same time, it improves the security of memory operation and the beauty of semantics**。For example, the mandatory requirement of reference must be initialized, so that we don't have to judge whether the reference is empty when using the reference, which makes the code more concise and elegant, and avoids the situation of pointer flying all over the sky. In addition to this scenario, references are used for the following two scenarios: > Reference type parameter In general, we use const reference parameter as a read-only formal parameter. In this case, we can not only avoid parameter copy, but also get the same call method as value passing parameter. ```c++ void test(const vector &data) { //... } int main() { vector data{1,2,3,4,5,6,7,8}; test(data); } ``` > Reference type return value C++ Provides the ability to overload operators.The syntax of the overloaded operator is the same as that of the original operator.An example is the operator [] operator, which generally needs to return a reference object in order to be modified correctly. ```c++ vector v(10); v[5] = 10; //[]Operator returns the reference, and then the corresponding element of vector can be modified //If[] operator do not return a reference but a pointer, the assignment statement needs to be written like this *v[5] = 10; // This way of writing is totally inconsistent with our understanding of the call of [], which is easy to be misunderstood ``` ## 3.Performance gap between pointer and reference Is there a performance gap between pointers and references?This kind of problem needs to enter the assembly level to have a look. Let's first write a test1 function, which uses pointers for parameter passing: ```c++ void test1(int* p) { *p = 3; //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。 return; } ``` The assembly code corresponding to this code segment is as follows: ```c++ (gdb) disassemble Dump of assembler code for function test1(int*): 0x0000000000400886 <+0>: push %rbp 0x0000000000400887 <+1>: mov %rsp,%rbp 0x000000000040088a <+4>: mov %rdi,-0x8(%rbp) => 0x000000000040088e <+8>: mov -0x8(%rbp),%rax 0x0000000000400892 <+12>: movl $0x3,(%rax) 0x0000000000400898 <+18>: nop 0x0000000000400899 <+19>: pop %rbp 0x000000000040089a <+20>: retq End of assembler dump. ``` The above code lines 1 and 2 are the field operation of parameter calling and saving;The third line is parameter passing. The first parameter of the function call is usually placed in the RDI register. This line of code writes the RDI register value (the value of pointer P) to the stack;Line 4 writes the value of P in the stack to the rax register;Line 5 is to write the immediate number 3 to the**Memory pointed to by the value of the rax register**. Let's write out the reference C + + code segment test2 for parameter passing: ```c++ void test2(int& r) { r = 3; // do not need to judge whether reference is null. return; } ``` This code corresponds to the following assembly code: ```c++ (gdb) disassemble Dump of assembler code for function test2(int&): 0x000000000040089b <+0>: push %rbp 0x000000000040089c <+1>: mov %rsp,%rbp 0x000000000040089f <+4>: mov %rdi,-0x8(%rbp) => 0x00000000004008a3 <+8>: mov -0x8(%rbp),%rax 0x00000000004008a7 <+12>: movl $0x3,(%rax) 0x00000000004008ad <+18>: nop 0x00000000004008ae <+19>: pop %rbp 0x00000000004008af <+20>: retq End of assembler dump. ``` We find that the assembly code corresponding to test2 is exactly the same as that of test1.This shows that the C + + compiler compiles the pointer and reference into exactly the same machine code when compiling the program. Therefore, the reference in C + + is just a "syntax sugar" of pointer operation in C ++,In the underlying implementation, the C + + compiler implements these two operations in the same way. ## 3. Conclusion The introduction of reference operation in C + + ensures the security and convenience of reference use and maintains the elegance of code under the condition of adding more restrictions on the use of reference. The use of reference can avoid the situation of "pointer flying all over the sky" to a certain extent, and it has a positive significance to improve the stability of the program. Finally, the underlying implementations of pointers and references are the same, and there is no need to worry about the performance gap between them. From: ================================================ FILE: english/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: english/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< using namespace std; class A{}; int main() { cout< using namespace std; class A { public: char b; virtual void fun() {}; static int c; static int d; static int f; }; int main() { /** * @brief 16 */ cout< using namespace std; class A{ virtual void fun(); virtual void fun1(); virtual void fun2(); virtual void fun3(); }; int main() { cout< using namespace std; class A { public: char a; int b; }; /** * @brief * char a * int b * short a * long b * 根据字节对齐4+4=8+8+8=24 */ class B:A { public: short a; long b; }; class C { A a; char c; }; class A1 { virtual void fun(){} }; class C1:public A1 { }; int main() { cout< using namespace std; class A { virtual void fun() {} }; class B { virtual void fun2() {} }; class C : virtual public A, virtual public B { public: virtual void fun3() {} }; int main() { /** * @brief 8 8 16 The derived class inherits multiple virtual functions and inherits the VPTR of all virtual functions */ cout< using namespace std; class A{}; int main() { cout< using namespace std; class A { public: A(); ~A(); static int a; static void fun3(); void fun(); void fun1(); }; int main() { cout< using namespace std; class A { public: char a; int b; }; /** * @brief 此时B按照顺序: * char a * int b * short a * long b * 根据字节对齐4+4=8+8+8=24 */ class B:A { public: short a; long b; }; class C { A a; char c; }; class A1 { virtual void fun(){} }; class C1:public A { }; int main() { cout< using namespace std; class A { public: char a; int b; }; class B { public: short a; long b; }; /** * @brief 8+16+8=32 */ class C:A,B { char c; }; int main() { cout< using namespace std; class A{ virtual void fun(); virtual void fun1(); virtual void fun2(); virtual void fun3(); }; int main() { cout< using namespace std; class A { public: char b; virtual void fun() {}; static int c; static int d; static int f; }; int main() { /** * @brief 16 字节对齐、静态变量不影响类的大小、vptr指针=8 */ cout< using namespace std; class A { virtual void fun() {} }; class B { virtual void fun2() {} }; class C : virtual public A, virtual public B { public: virtual void fun3() {} }; int main() { /** * @brief 8 8 16 派生类虚继承多个虚函数,会继承所有虚函数的vptr */ cout< using namespace std; class A { virtual void fun() {} }; class B { virtual void fun2() {} }; class C : public A, public B { public: virtual void fun3() {} }; int main() { /** * @brief 8 8 16 派生类继承多个虚函数,会继承所有虚函数的vptr */ cout< #include using namespace std; void demo() { // static variable static int count = 0; cout << count << " "; // value is updated and // will be carried to next // function calls count++; } int main() { for (int i=0; i<5; i++) demo(); return 0; } ``` Output: ``` 0 1 2 3 4 ``` You can see in the above program that the variable count is declared static.So, Its value is passed through a function call. The variable count is not initialized each time the function is called. - Static variables in class Because variables declared static are initialized only once,Because they allocate space in separate static storage。Therefore, static variables in a class are **shared by objects。**You cannot have multiple copies of the same static variable for different objects. For this reason, static variables cannot be initialized with constructors. ```c++ #include using namespace std; class Apple { public: static int i; Apple() { // Do nothing }; }; int main() { Apple obj1; Apple obj2; obj1.i =2; obj2.i = 3; // prints value of i cout << obj1.i<<" "< using namespace std; class Apple { public: static int i; Apple() { // Do nothing }; }; int Apple::i = 1; int main() { Apple obj; // prints value of i cout << obj.i; } ``` Output: ``` 1 ``` **Static member** - Class objects are static Just like variables, objects have scope when declared static until the life cycle of the program Consider the following program, where the object is non static ```c++ #include using namespace std; class Apple { int i; public: Apple() { i = 0; cout << "Inside Constructor\n"; } ~Apple() { cout << "Inside Destructor\n"; } }; int main() { int x = 0; if (x==0) { Apple obj; } cout << "End of main\n"; } ``` Output: ```c++ Inside Constructor Inside Destructor End of main ``` In the above program, the object is declared non static within the if block. Therefore, the scope of the variable is only within the if block. Therefore, when an object is created, the constructor is called, and is called when the control of the if block passes over the destructor, because the scope of the object is only within the if block in which it is declared. If we declare the object static, now let's look at the changes in the output. ```c++ #include using namespace std; class Apple { int i; public: Apple() { i = 0; cout << "Inside Constructor\n"; } ~Apple() { cout << "Inside Destructor\n"; } }; int main() { int x = 0; if (x==0) { static Apple obj; } cout << "End of main\n"; } ``` Output: ``` Inside Constructor End of main Inside Destructor ``` You can clearly see the output change. Now, call the destructor function after the end of main.This is because the scope of static objects is throughout the life cycle of the program. - Static functions in class Just like static data members or static variables in a class, Static member functions are also independent of class objects.We are allowed to use objects and "." to call static member functions. However, it is recommended to call static members using class names and range resolution operators. Allows static member functions to access only static data members or other static member functions that cannot access non static data members or member functions of a class. ```c++ #include using namespace std; class Apple { public: // static member function static void printMsg() { cout<<"Welcome to Apple!"; } }; // main function int main() { // invoking a static member function Apple::printMsg(); } ``` Output: ``` Welcome to Apple! ``` ================================================ FILE: english/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: english/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: english/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: english/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<<" "< 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: english/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: english/basic_content/struct/README.md ================================================ # Understand C and C + + struct ![](../img/wechat.jpg) ## 1.Struct In C - In C, struct is only used as the composite type of data, that is, in the structure declaration, only data members can be placed inside, but not functions - C + + access modifiers cannot be used in C structure declarations,such as:public、protected、private. But they can be used in C + + - Define the structure variable in C. If you use the following definition, you must add struct - The structure of C cannot be inherited (there is no such concept) - If the structure name is the same as the function name, it can run normally and call normally! For example, you can define void base() that does not conflict with struct base{}。 Example: ```c #include struct Base { // public int v1; // public: //error int v2; //private: int v3; //void print(){ // c中不能在结构体中嵌入函数 // printf("%s\n","hello world"); //}; //error! }; void Base(){ printf("%s\n","I am Base func"); } //struct Base base1; //ok //Base base2; //error int main() { struct Base base; base.v1=1; //base.print(); printf("%d\n",base.v1); Base(); return 0; } ``` Finally Output: ``` 1 I am Base func ``` Code:[struct_func.c](./struct_func.c) ## 2.C++ struct The comparison with C is as follows: - In C + + structure, not only data can be defined, but also functions can be defined. - Access modifiers can be used in C + + structures, such as :public、protected、private 。 - C + + structure can be used directly without struct. - C + + inheritance - If the structure name is the same as the function name, it can run normally and call normally!However, when defining the structure variable, we only use the structure with struct! Example: > Case 1: Before adding a function with the same name: ```c++ struct Student { }; Student(){} Struct Student s; //ok Student s; //ok ``` After adding a function with the same name: ```c++ struct Student { }; Student(){} Struct Student s; //ok Student s; //error ``` > Case 2: ```c++ typedef struct Base1 { int v1; // private: //error! int v3; public: //显示声明public int v2; void print(){ printf("%s\n","hello world"); }; }B; //void B() {} //error! 符号 "B" 已经被定义为一个 "struct Base1" 的别名 ``` > The first three cases ```c++ #include #include struct Base { int v1; // private: //error! int v3; public: //显示声明public int v2; void print(){ printf("%s\n","hello world"); }; }; int main() { struct Base base1; //ok Base base2; //ok Base base; base.v1=1; base.v3=2; base.print(); printf("%d\n",base.v1); printf("%d\n",base.v3); return 0; } ``` Code :[struct_func.cpp](struct_func.cpp) > Inheritance cases ```c++ #include #include struct Base { int v1; // private: //error! int v3; public: //显示声明public int v2; virtual void print(){ printf("%s\n","Base"); }; }; struct Derived:Base { public: int v2; void print(){ printf("%s\n","Derived"); }; }; int main() { Base *b=new Derived(); b->print(); return 0; } ``` Code:[ext_struct_func.cpp](./ext_struct_func.cpp) > Functions with the same name ```c++ #include #include struct Base { int v1; // private: //error! int v3; public: //显示声明public int v2; void print(){ printf("%s\n","hello world"); }; }; typedef struct Base1 { int v1; // private: //error! int v3; public: //显示声明public int v2; void print(){ printf("%s\n","hello world"); }; }B; void Base(){ printf("%s\n","I am Base func"); } //void B() {} //error! 符号 "B" 已经被定义为一个 "struct Base1" 的别名 int main() { struct Base base; //ok //Base base1; // error! base.v1=1; base.v3=2; base.print(); printf("%d\n",base.v1); printf("%d\n",base.v3); Base(); return 0; } ``` Code:[struct_func_func.cpp](./struct_func_func.cpp) ## 3.Conclusion ### The difference of structure between C and C + + | C | C++ | | ------------------------------------------------------ | ------------------------------------------------------------ | | You cannot place a function in a structure declaration | Function can be declared in structure | | C + + access modifiers cannot be used in C structure declarations. | public、protected、private They can be used in C + + | | Define the structure variable in C. If you use the following definition, you must add struct | You can do without struct | |Not inheritance | you could use inheritance | | If the structure name is the same as the function name, it can run normally and call normally | If the structure name is the same as the function name, the structure can only be defined with struct | ================================================ FILE: english/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"<print(); delete b; return 0; } ================================================ FILE: english/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: english/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: english/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: english/basic_content/struct_class/README.md ================================================ # The difference struct and class ![](../img/wechat.jpg) More detail about struct [Story about struct](../struct) Generally speaking, struct is more suitable to be regarded as an implementation body of data structure, and class is more suitable to be regarded as the implementation body of an object. Dif: One of the essential differences is the default access control Default inherited access.Struct is public, while class is private. Structure as the implementation body of data structure.Its default data access control is public, while class is the implementation body of the object, and its default member variable access control is private. ================================================ FILE: english/basic_content/this/README.md ================================================ # Story about this ## About Author ![](../img/wechat.jpg) ## 1.This pointer I believe that many people sitting here are learning python. For Python, there is self. Analogy to C + + is this pointer. Let's analyze the use of this pointer in class. Let's first talk about the use of this pointer: (1)This pointer to an object is not part of the object itself and does not affect the result of sizeof. (2)This scope is within the class. When accessing the non static members of the class in the non static member function of the class, the compiler will automatically pass the address of the object itself to the function as an implicit parameter. That is to say, even if you don't write this pointer, the compiler will add this when compiling. As the implicit formal parameter of non static member function, the access to each member is carried out through this. Second, the use of this pointer: (1)When the class object itself is returned in a class's non static member function, directly use return *this。 (2)When the parameter is the same as the member variable name, 如this->n = n (不能写成n = n)。 The following examples are available: ```c++ #include #include using namespace std; class Person{ public: typedef enum { BOY = 0, GIRL }SexType; Person(char *n, int a,SexType s){ name=new char[strlen(n)+1]; strcpy(name,n); age=a; sex=s; } int get_age() const{ return this->age; } Person& add_age(int a){ age+=a; return *this; } ~Person(){ delete [] name; } private: char * name; int age; SexType sex; }; int main(){ Person p("zhangsan",20,Person::BOY); cout< #include using namespace std; class Person{ public: typedef enum { BOY = 0, GIRL }SexType; Person(char *n, int a,SexType s){ name=new char[strlen(n)+1]; strcpy(name,n); age=a; sex=s; } int get_age() const{ return this->age; } Person& add_age(int a){ age+=a; return *this; } ~Person(){ delete [] name; } private: char * name; int age; SexType sex; }; int main(){ Person p("zhangsan",20,Person::BOY); cout< /** * 默认访问控制符为public */ union UnionTest { /** * 可以含有构造函数、析构函数 */ UnionTest() : i(10) {print(i);}; ~UnionTest(){}; int i; private: void print(int i){std::cout< #define isNs1 1 //#define isGlobal 2 using namespace std; void func() { cout<<"::func"< using namespace std; class Base{ public: void f(){ cout<<"f()"< V1; using V2 = vector; ``` Code:[using_typedef.cpp](using_typedef.cpp) ================================================ FILE: english/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: english/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()"< #define isNs1 1 //#define isGlobal 2 using namespace std; void func() { cout<<"::func"< #include using namespace std; typedef vector V1; using V2 = vector; int main() { int nums1[] = {1,2,3,4,5,6}; V1 vec1(nums1,nums1+sizeof(nums1)/sizeof(int)); int nums2[] = {5,7,6}; V2 vec2(nums2,nums2+sizeof(nums2)/sizeof(int)); for(auto i:vec1) cout< using namespace std; class Employee { public: virtual void raiseSalary() { cout<<0<raiseSalary(); // Polymorphic Call: Calls raiseSalary() // according to the actual object, not // according to the type of pointer } int main(){ Employee *emp[]={new Manager(),new Engineer}; globalRaiseSalary(emp,2); return 0; } ================================================ FILE: english/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: english/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: english/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: english/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: english/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: english/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: english/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: english/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: english/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: english/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: english/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"<(obj); cout << "works"< int main(void) { const int local = 10; int *ptr = (int*) &local; printf("Initial value of local : %d \n", local); *ptr = 100; printf("Modified value of local: %d \n", local); return 0; } ================================================ FILE: english/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: english/basic_content/vptr_vtable/README.md ================================================ # 深入浅出C++虚函数的vptr与vtable ## 关于作者: 个人公众号: ![](../img/wechat.jpg) ## 1.基础理论 为了实现虚函数,C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称,例如“vtable”,“虚函数表”,“虚方法表”或“调度表”。 虚拟表实际上非常简单,虽然用文字描述有点复杂。首先,**每个使用虚函数的类(或者从使用虚函数的类派生)都有自己的虚拟表**。该表只是编译器在编译时设置的静态数组。虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针,指向该类可访问的派生函数。 其次,编译器还会添加一个隐藏指向基类的指针,我们称之为vptr。vptr在创建类实例时自动设置,以便指向该类的虚拟表。与this指针不同,this指针实际上是编译器用来解析自引用的函数参数,vptr是一个真正的指针。 因此,它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承,这很重要。 ## 2.实现与内部结构 下面我们来看自动与手动操纵vptr来获取地址与调用虚函数! 开始看代码之前,为了方便大家理解,这里给出调用图: ![base](./img/base.jpg) 代码全部遵循标准的注释风格,相信大家看了就会明白,不明白的话,可以留言! ```c++ /** * @file vptr1.cpp * @brief C++虚函数vptr和vtable * 编译:g++ -g -o vptr vptr1.cpp -std=c++11 * @author 光城 * @version v1 * @date 2019-07-20 */ #include #include using namespace std; /** * @brief 函数指针 */ typedef void (*Fun)(); /** * @brief 基类 */ class Base { public: Base(){}; virtual void fun1() { cout << "Base::fun1()" << endl; } virtual void fun2() { cout << "Base::fun2()" << endl; } virtual void fun3(){} ~Base(){}; }; /** * @brief 派生类 */ class Derived: public Base { public: Derived(){}; void fun1() { cout << "Derived::fun1()" << endl; } void fun2() { cout << "DerivedClass::fun2()" << endl; } ~Derived(){}; }; /** * @brief 获取vptr地址与func地址,vptr指向的是一块内存,这块内存存放的是虚函数地址,这块内存就是我们所说的虚表 * * @param obj * @param offset * * @return */ Fun getAddr(void* obj,unsigned int offset) { cout<<"======================="<fun1(); cout<<"基类引用指向基类实例并调用虚函数"<fun1(); ``` 其过程为:首先程序识别出fun1()是个虚函数,其次程序使用pt->vptr来获取Derived的虚拟表。第三,它查找Derived虚拟表中调用哪个版本的fun1()。这里就可以发现调用的是Derived::fun1()。因此pt->fun1()被解析为Derived::fun1()! 除此之外,上述代码大家会看到,也包含了手动获取vptr地址,并调用vtable中的函数,那么我们一起来验证一下上述的地址与真正在自动调用vtable中的虚函数,比如上述`pt->fun1()`的时候,是否一致! 这里采用gdb调试,在编译的时候记得加上`-g`。 通过`gdb vptr`进入gdb调试页面,然后输入`b Derived::fun1`对fun1打断点,然后通过输入r运行程序到断点处,此时我们需要查看调用栈中的内存地址,通过`disassemable fun1`可以查看当前有关fun1中的相关汇编代码,我们看到了`0x0000000000400ea8`,然后再对比上述的结果会发现与手动调用的fun1一致,fun2类似,以此证明代码正确! gdb调试信息如下: ```c++ (gdb) b Derived::fun1 Breakpoint 1 at 0x400eb4: file vptr1.cpp, line 23. (gdb) r Starting program: /home/light/Program/CPlusPlusThings/virtual/pure_virtualAndabstract_class/vptr 基类对象直接调用 Base::fun1() 基类引用指向派生类实例 Base::fun1() 基类指针指向派生类实例并调用虚函数 Breakpoint 1, Derived::fun1 (this=0x614c20) at vptr1.cpp:23 23 cout << "Derived::fun1()" << endl; (gdb) disassemble fun1 Dump of assembler code for function Derived::fun1(): 0x0000000000400ea8 <+0>: push %rbp 0x0000000000400ea9 <+1>: mov %rsp,%rbp 0x0000000000400eac <+4>: sub $0x10,%rsp 0x0000000000400eb0 <+8>: mov %rdi,-0x8(%rbp) => 0x0000000000400eb4 <+12>: mov $0x401013,%esi 0x0000000000400eb9 <+17>: mov $0x602100,%edi 0x0000000000400ebe <+22>: callq 0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x0000000000400ec3 <+27>: mov $0x400a00,%esi 0x0000000000400ec8 <+32>: mov %rax,%rdi 0x0000000000400ecb <+35>: callq 0x4009f0 <_ZNSolsEPFRSoS_E@plt> 0x0000000000400ed0 <+40>: nop 0x0000000000400ed1 <+41>: leaveq 0x0000000000400ed2 <+42>: retq End of assembler dump. (gdb) disassemble fun2 Dump of assembler code for function Derived::fun2(): 0x0000000000400ed4 <+0>: push %rbp 0x0000000000400ed5 <+1>: mov %rsp,%rbp 0x0000000000400ed8 <+4>: sub $0x10,%rsp 0x0000000000400edc <+8>: mov %rdi,-0x8(%rbp) 0x0000000000400ee0 <+12>: mov $0x401023,%esi 0x0000000000400ee5 <+17>: mov $0x602100,%edi 0x0000000000400eea <+22>: callq 0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x0000000000400eef <+27>: mov $0x400a00,%esi 0x0000000000400ef4 <+32>: mov %rax,%rdi 0x0000000000400ef7 <+35>: callq 0x4009f0 <_ZNSolsEPFRSoS_E@plt> 0x0000000000400efc <+40>: nop 0x0000000000400efd <+41>: leaveq 0x0000000000400efe <+42>: retq End of assembler dump. ``` ================================================ FILE: english/basic_content/vptr_vtable/vptr1.cpp ================================================ /** * @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<<"基类引用指向基类实例并调用虚函数"< C语言风格函数 atoi与strtol对比: ```cpp string str = "16s"; int a = atoi(str.c_str()); int b = strtol(str.c_str(), nullptr, 10); ``` 输出: ``` atoi的结果为:16 strtol的结果为:16 ``` 这两个函数都是从字符串开始寻找数字或者正负号或者小数点,遇到非法字符终止。 所以到上述s字符就不输出了,提前结束,也就是说当你的字符串不是数字的时候,或者小数点等非数字,不会报异常!直接输出0. 例如: ```cpp string str = "asdsa"; int a = atoi(str.c_str()); int b = strtol(str.c_str(), nullptr, 10); ``` 输出: ``` 0 0 ``` strtol相比与atoi来说,支持多种进制转换,例如8进制等 例如: ``` int b = strtol(str.c_str(), nullptr, 8); ``` > C++风格 在C++中可以使用stoi来转int,这个函数相比于前两个一个最大特点是:异常! 我们知道C++相比于C语言多了异常,这也是这个函数在C++中具有的最显著功能。 例如: ```cpp string str1 = "asq,"; // int c = stoi(str1); // 报异常 string str2 = "12312"; int c = stoi(str2); // ok cout << c << endl; ``` 异常如下: ``` terminate called after throwing an instance of 'std::invalid_argument' what(): stoi ``` > 自定义 也就是自己写呗,如下: ```cpp int stringToInt(const string &s) { int v; stringstream ss; ss << s; ss >> v; return v; } int main() { int i = stringToInt("2.3"); cout< #include #include using namespace std; 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; // 第二种方式 foo(); return 0; } ================================================ FILE: learn_class/modern_cpp_30/RAII/heap.cpp ================================================ #include using namespace std; class bar {}; // java 程序员风格 void foo() { cout << "method 1" << endl; bar *ptr = new bar(); delete ptr; } bar *make_bar() { bar *ptr = nullptr; try { ptr = new bar(); } catch (...) { delete ptr; throw; } return ptr; } // 独立出函数 分配和释放不在一个函数里 void foo1() { cout << "method 2" << endl; bar *ptr = make_bar(); delete ptr; } int main() { foo(); foo1(); return 0; } ================================================ FILE: learn_class/modern_cpp_30/RAII/stack.cpp ================================================ // // Created by light on 19-12-9. // #include class Obj { public: Obj() { puts("Obj()"); } ~Obj() { puts("~Obj()"); } }; void foo(int n) { Obj obj; if (n == 42) throw "life, the universe and everything"; } // 不管是否发生了异常,obj 的析构函数都会得到执行。 int main() { try { foo(41); foo(42); } catch (const char *s) { puts(s); } } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/README.md ================================================ 学习自:https://jguegant.github.io/blogs/tech/sfinae-introduction.html ================================================ FILE: learn_class/modern_cpp_30/SFINAE/SFINAE.cpp ================================================ // // Created by light on 20-1-5. // #include using namespace std; template class IsClassT { private: typedef char One; typedef struct { char a[2]; } Two; template static One test(int C::*); // Will be chosen if T is anything except a class. template static Two test(...); public: enum { Yes = sizeof(IsClassT::test(0)) == 1 }; enum { No = !Yes }; }; struct A {}; int main() { // 0不能转换为int int::*因为int不是类,所以它不能有成员指针。 cout << IsClassT::Yes << endl; cout << IsClassT::Yes << endl; return 0; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/auto.cpp ================================================ // // Created by light on 20-1-6. // #include bool f(); auto test = f(); // Famous usage, auto deduced that test is a boolean, hurray! // vvv t wasn't declare at that point, it will be after as a parameter! template decltype(t.serialize()) g(const T &t) {} // Compilation error // Less famous usage: // vvv auto delayed the return type specification! // vvv vvv the return type is specified here and use t! template auto g(const T &t) -> decltype(t.serialize()) {} // No compilation error. auto myFunction() // Automagically figures out that myFunction returns ints. { return int(); } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/blending1.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" template struct hasSerialize { // We test if the type has serialize using decltype and declval. // decltype将根据逗号表达式选择最后一个类型作为返回值 // 如果C有serialize将调用当前版本,没有serialize,则失败,此时编译时不报错,继续寻找重载函数,也就是SFINAE. template static constexpr decltype(std::declval().serialize(), bool()) test(int /* unused */) { // We can return values, thanks to constexpr instead of playing with sizeof. return true; } template static constexpr bool test(...) { return false; } // int is used to give the precedence! static constexpr bool value = test(int()); }; template std::string serialize(const T &obj) { // 不加constexpr 报错:error: no member named 'serialize' in 'A'. if constexpr (hasSerialize::value) return obj.serialize(); else return to_string(obj); } int main() { A a; B b; C c; // The following lines work like a charm! std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/blending2.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" template struct hasSerialize : std::false_type { }; template struct hasSerialize().serialize())> : std::true_type { }; template std::string serialize(const T &obj) { // 不加constexpr 报错:error: no member named 'serialize' in 'A'. if constexpr (hasSerialize::value) // c++17特性 return obj.serialize(); else return to_string(obj); } int main() { A a; B b; C c; // The following lines work like a charm! std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/combiningAndGenius.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" template struct hasSerialize { // 编译时比较 typedef char yes[1]; typedef char no[2]; // 允许我们检查序列化确实是一种方法 // 第二个参数必须是第一个参数的类型 // 例如: reallyHas 替换为 reallyHas 并起作用 // 注意:它仅适用于整数常量和指针(因此函数指针可以使用) // 例如:reallyHas 替换为 // reallyHas 并起作用 template struct reallyHas; // std::string (C::*)() 是函数指针声明 template static yes &test(reallyHas * /*unused*/) {} // ()()const 函数指针 -> std::string serialize() const template static yes &test(reallyHas * /*unused*/) {} // The famous C++ sink-hole. // Note that sink-hole must be templated too as we are testing test(0). // If the method serialize isn't available, we will end up in this method. template static no &test(...) { /* dark matter */ } //用作测试的返回值的常数。 //由于编译时评估的大小,因此实际上在这里完成了测试。 static const bool value = sizeof(test(0)) == sizeof(yes); }; // Using the previous A struct and hasSerialize helper. struct D : A { std::string serialize() const { return "I am a D!"; } }; template bool testHasSerialize(const T & /*t*/) { return hasSerialize::value; } // 不能够判断仿函数里面的serialize struct E { struct Functor { std::string operator()() { return "I am a E!"; } }; Functor serialize; }; template std::string serialize(const T &obj) { // 不加constexpr 报错:error: no member named 'serialize' in 'A'. if constexpr (hasSerialize::value) return obj.serialize(); else return to_string(obj); } int main() { // 检测结构体是否有serialize方法 // Using the struct A, B, C defined in the previous hasSerialize example. std::cout << hasSerialize::value << std::endl; std::cout << hasSerialize::value << std::endl; std::cout << hasSerialize::value << std::endl; D d; A &a = d; // Here we lost the type of d at compile time. std::cout << testHasSerialize(d) << std::endl; // Output 1. std::cout << testHasSerialize(a) << std::endl; // Output 0. E e; std::cout << e.serialize() << std::endl; // Succefully call the functor. std::cout << testHasSerialize(e) << std::endl; // Output 0. A a_; B b_; C c_; std::cout << serialize(a_) << std::endl; std::cout << serialize(b_) << std::endl; std::cout << serialize(c_) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/constexpr.cpp ================================================ // // Created by light on 20-1-6. // #include constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } struct testStruct : std::true_type { }; // Inherit from the true type. int main() { int i = factorial(5); // Call to a constexpr function. // Will be replace by a good compiler by: // int i = 120; std::cout << i << std::endl; constexpr bool testVar = testStruct(); // Generate a compile-time testStruct. bool test = testStruct::value; // Equivalent to: test = true; std::cout << test << std::endl; test = testVar; // true_type has a constexpr converter operator, equivalent to: test = true; std::cout << test << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/decltype.cpp ================================================ // // Created by light on 20-1-6. // #include struct Default { int foo() const {return 1;} }; struct NonDefault { NonDefault(const NonDefault&) {} int foo() const {return 1;} }; int main() { decltype(Default().foo()) n1 = 1; // int n1 // decltype(NonDefault().foo()) n2 = n1; // error: no default constructor decltype(std::declval().foo()) n2 = n1; // int n2 std::cout << "n2 = " << n2 << '\n'; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/fis_valid.cpp ================================================ // // Created by light on 20-1-6. // #include #include "boost/hana.hpp" #include "structData.h" template struct container { // Let's put the test in private. private: // We use std::declval to 'recreate' an object of 'UnnamedType'. // We use std::declval to also 'recreate' an object of type 'Param'. // We can use both of these recreated objects to test the validity! template constexpr auto testValidity(int /* unused */) -> decltype(std::declval()(std::declval()),boost::hana::true_c) { // If substitution didn't fail, we can return a true_type. return boost::hana::true_c; } template constexpr std::false_type testValidity(...) { // Our sink-hole returns a false_type. return boost::hana::false_c; } public: // A public operator() that accept the argument we wish to test onto the UnnamedType. // Notice that the return type is automatic! template constexpr auto operator()(const Param &p) { // The argument is forwarded to one of the two overloads. // The SFINAE on the 'true_type' will come into play to dispatch. // Once again, we use the int for the precedence. return testValidity(int()); } }; template constexpr auto is_valid(const UnnamedType &t) { // We used auto for the return type: it will be deduced here. return container(); } // Check if a type has a serialize method. auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {}); // Notice how I simply swapped the return type on the right? template auto serialize(T &obj) -> typename std::enable_if::type { return obj.serialize(); } template auto serialize(T &obj) -> typename std::enable_if::type { return to_string(obj); } int main() { A a; B b; C c; auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {}); std::cout << test(a) << std::endl; std::cout << test(b) << std::endl; std::cout << test(c) << std::endl; // The following lines work like a charm! std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/hana.cpp ================================================ // // Created by light on 20-1-6. // // https://www.boost.org/doc/libs/1_62_0/libs/hana/doc/html/structboost_1_1hana_1_1type.html#a2d2e7e08e284f7e0bd1bd9c3ad0e0a2b #include #include #include namespace hana = boost::hana; int main() { // 检查成员 struct Person { std::string name; }; auto has_name = hana::is_valid([](auto &&p) -> decltype(p.name) {}); Person joe{"Joe"}; static_assert(has_name(joe), ""); static_assert(!has_name(1), ""); // 检查嵌套类型 auto has_value_type = hana::is_valid([](auto t) -> hana::type {}); static_assert(has_value_type(hana::type_c>), ""); static_assert(!has_value_type(hana::type_c), ""); return 0; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/is_valid.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" template struct container { // Let's put the test in private. private: // We use std::declval to 'recreate' an object of 'UnnamedType'. // We use std::declval to also 'recreate' an object of type 'Param'. // We can use both of these recreated objects to test the validity! template constexpr auto testValidity(int /* unused */) -> decltype(std::declval()(std::declval()), std::true_type()) { // If substitution didn't fail, we can return a true_type. return std::true_type(); } template constexpr std::false_type testValidity(...) { // Our sink-hole returns a false_type. return std::false_type(); } public: // A public operator() that accept the argument we wish to test onto the UnnamedType. // Notice that the return type is automatic! template constexpr auto operator()(const Param &p) { // The argument is forwarded to one of the two overloads. // The SFINAE on the 'true_type' will come into play to dispatch. // Once again, we use the int for the precedence. return testValidity(int()); } }; template constexpr auto is_valid(const UnnamedType &t) { // We used auto for the return type: it will be deduced here. return container(); } // Check if a type has a serialize method. auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {}); // Notice how I simply swapped the return type on the right? template auto serialize(T &obj) -> typename std::enable_if::type { return obj.serialize(); } template auto serialize(T &obj) -> typename std::enable_if::type { return to_string(obj); } int main() { A a; B b; C c; auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {}); std::cout << test(a) << std::endl; std::cout << test(b) << std::endl; std::cout << test(c) << std::endl; // The following lines work like a charm! std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/lambda.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" // Equivalent to: struct l5UnamedType { template auto operator()(T &t) const -> decltype(t.serialize()) // /!\ This signature is nice for a SFINAE! { return t.serialize(); } }; void fun(A a,B b,C c) { // ***** Simple lambda unamed type ***** auto l4 = [](int a, int b) { return a + b; }; std::cout << l4(4, 5) << std::endl; // Output 9. // Equivalent to: struct l4UnamedType { int operator()(int a, int b) const { return a + b; } }; l4UnamedType l4Equivalent = l4UnamedType(); std::cout << l4Equivalent(4, 5) << std::endl; // Output 9 too. // ***** auto parameters lambda unnamed type ***** // b's type is automagically deduced! auto l5 = [](auto &t) -> decltype(t.serialize()) { return t.serialize(); }; std::cout << l5(b) << std::endl; // Output: I am a B! // std::cout << l5(a) << std::endl; // Error: no member named 'serialize' in 'A'. l5UnamedType l5Equivalent = l5UnamedType(); std::cout << l5Equivalent(b) << std::endl; // Output: I am a B! // std::cout << l5Equivalent(a) << std::endl; // Error: no member named 'serialize' in 'A'. } int main() { A a; B b; C c; auto l1 = [](B &b) { return b.serialize(); }; // Return type figured-out by the return statement. auto l3 = [](B &b) -> std::string { return b.serialize(); }; // Fixed return type. auto l2 = [](B &b) -> decltype(b.serialize()) { return b.serialize(); }; // Return type dependant to the B type. std::cout << l1(b) << std::endl; // Output: I am a B! std::cout << l2(b) << std::endl; // Output: I am a B! std::cout << l3(b) << std::endl; // Output: I am a B! fun(a,b,c); } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/overload1.cpp ================================================ // // Created by light on 20-1-6. // #include void f(std::string s); // int can't be convert into a string. void f(double d); // int can be implicitly convert into a double, so this version could be selected, but... void f(int i); // ... this version using the type int directly is even more close! int main() { f(1); // Call f(int i); } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/overload2.cpp ================================================ // // Created by light on 20-1-6. // #include std::string f(...) // Variadic functions are so "untyped" that... { return "..."; } template std::string f(const T &t)// ...this templated function got the precedence! { return "T"; } int main() { std::cout << f(1) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/p1SFINAE.cpp ================================================ // // Created by light on 20-1-6. // /* The compiler will try this overload since it's less generic than the variadic. T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr); int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors. It simply tries the next overload. 编译器尝试此重载,因为模板化函数比可变参数函数更精确(通用)。T将被int取代,这将使我们得到 void f(const int& t, int::iterator* b = nullptr); int 没有迭代器子类型,但是编译器不会抛出一堆错误。 它只是尝试下一个重载。 */ template void f(const T &t, typename T::iterator *it = nullptr) {} // The sink-hole. void f(...) {} int main() { f(1); // Calls void f(...) { } } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/p2SFINAE.cpp ================================================ // // Created by light on 20-1-6. // // The compiler will be really unhappy when it will later discover the call to hahahaICrash. // 当以后发现对hahahaICrash的调用时,编译器将非常不满意。 template void f(T t) { t.hahahaICrash(); } void f(...) { } // The sink-hole wasn't even considered. int main() { f(1); } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/packis_valid.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" template struct container { // Let's put the test in private. private: // We use std::declval to 'recreate' an object of 'UnnamedType'. // We use std::declval to also 'recreate' an object of type 'Param'. // We can use both of these recreated objects to test the validity! template constexpr auto test_validity(int /* unused */) -> decltype(std::declval()(std::declval()...), std::true_type()) { // If substitution didn't fail, we can return a true_type. return std::true_type(); } template constexpr std::false_type test_validity(...) { // Our sink-hole returns a false_type. return std::false_type(); } public: // A public operator() that accept the argument we wish to test onto the UnnamedType. // Notice that the return type is automatic! template constexpr auto operator()(Params&& ...) { // The argument is forwarded to one of the two overloads. // The SFINAE on the 'true_type' will come into play to dispatch. return test_validity(int()); } }; template constexpr auto is_valid(UnnamedType&& t) { // We used auto for the return type: it will be deduced here. return container(); } // Check if a type has a serialize method. auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {}); // Notice how I simply swapped the return type on the right? template auto serialize(T &obj) -> typename std::enable_if::type { return obj.serialize(); } template auto serialize(T &obj) -> typename std::enable_if::type { return to_string(obj); } int main() { A a; B b; C c; auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {}); std::cout << test(a,b) << std::endl; std::cout << test(b) << std::endl; std::cout << test(c) << std::endl; // The following lines work like a charm! std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/serialize.cpp ================================================ // // Created by light on 20-1-6. // #include #include #include #include "structData.h" using namespace std; namespace hana = boost::hana; // 检查类型是否有一个serialize方法 auto hasSerialize = hana::is_valid([](auto &&x) -> decltype(x.serialize()) {}); // 序列化任意对象 template std::string serialize(T const &obj) { return hana::if_(hasSerialize(obj), [](auto &x) { return x.serialize(); }, [](auto &x) { return to_string(x); } )(obj); } int main() { A a; B b; C c; std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/sizeof1.cpp ================================================ // // Created by light on 20-1-6. // #include typedef char type_test[42]; type_test &f() {} int main() { // In the following lines f won't even be truly called but we can still access to the size of its return type. // Thanks to the "fake evaluation" of the sizeof operator. char arrayTest[sizeof(f())]; std::cout << sizeof(f()) << std::endl; // Output 42. } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/sizeof2.cpp ================================================ #include // // Created by light on 20-1-6. // typedef char yes; // Size: 1 byte. typedef yes no[2]; // Size: 2 bytes. // Two functions using our type with different size. yes &f1() {} no &f2() {} int main() { std::cout << sizeof(f1()) << std::endl; std::cout << sizeof(f2()) << std::endl; std::cout << (sizeof(f1()) == sizeof(f2())) << std::endl; // Output 0. } ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/structData.h ================================================ // // Created by light on 20-1-6. // #ifndef MORDEN_C_STRUCTDATA_H #define MORDEN_C_STRUCTDATA_H // 类型A只有to_string 方法 struct A { }; std::string to_string(const A &) { return "I am A"; } // 类型B有serialize方法 struct B { std::string serialize() const { return "I am B"; } }; // 类型C有个serialize数据成员与to_string方法 struct C { std::string serialize; }; std::string to_string(const C &) { return "I am C"; } #endif //MORDEN_C_STRUCTDATA_H ================================================ FILE: learn_class/modern_cpp_30/SFINAE/sfinae paper/timeGenius.cpp ================================================ // // Created by light on 20-1-6. // #include #include "structData.h" template struct hasSerialize { // 编译时比较 typedef char yes[1]; typedef char no[2]; // 允许我们检查序列化确实是一种方法 // 第二个参数必须是第一个参数的类型 // 例如: reallyHas 替换为 reallyHas 并起作用 // 注意:它仅适用于整数常量和指针(因此函数指针可以使用) // 例如:reallyHas 替换为 // reallyHas 并起作用 template struct reallyHas; // std::string (C::*)() 是函数指针声明 template static yes &test(reallyHas * /*unused*/) {} // ()()const 函数指针 -> std::string serialize() const template static yes &test(reallyHas * /*unused*/) {} // The famous C++ sink-hole. // Note that sink-hole must be templated too as we are testing test(0). // If the method serialize isn't available, we will end up in this method. template static no &test(...) { /* dark matter */ } //用作测试的返回值的常数。 //由于编译时评估的大小,因此实际上在这里完成了测试。 static const bool value = sizeof(test(0)) == sizeof(yes); }; template // Default template version. struct enable_if { }; // This struct doesn't define "type" and the substitution will fail if you try to access it. template // A specialisation used if the expression is true. struct enable_if { typedef T type; }; // This struct do have a "type" and won't fail on access. template typename enable_if::value, std::string>::type serialize(const T &obj) { return obj.serialize(); } template typename enable_if::value, std::string>::type serialize(const T &obj) { return to_string(obj); } int main() { // Usage: // enable_if::type t1; // Compiler happy. t's type is int. // enable_if::value, int>::type t2; // Compiler happy. t's type is int. // // enable_if::type t3; // Compiler unhappy. no type named 'type' in 'enable_if'; // enable_if::value, int>::type t4; // no type named 'type' in 'enable_if'; A a; B b; C c; // The following lines work like a charm! std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; } ================================================ FILE: learn_class/modern_cpp_30/compilercompute/BUILD ================================================ # please run `bazel run //learn_class/modern_cpp_30/compilercompute:IF` # please run `bazel run //learn_class/modern_cpp_30/compilercompute:factorial` # please run `bazel run //learn_class/modern_cpp_30/compilercompute:fmap` # please run `bazel run //learn_class/modern_cpp_30/compilercompute:WhileLoop` load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "IF", srcs = ["IF.cpp"], copts = ["-std=c++17"], ) cc_binary( name = "factorial", srcs = ["factorial.cpp"], copts = ["-std=c++17"], ) cc_binary( name = "fmap", srcs = ["fmap.cpp"], copts = ["-std=c++17"], ) cc_binary( name = "WhileLoop", srcs = ["WhileLoop.cpp"], copts = ["-std=c++17"], ) ================================================ FILE: learn_class/modern_cpp_30/compilercompute/IF.cpp ================================================ // // Created by light on 19-12-28. // #include using namespace std; // 使用template实现IF条件判断 template struct IF; template struct IF { typedef Then result; }; template struct IF { typedef Else result; }; // 判断奇数与偶数 template struct isEven { static const auto RES = IF<(N & 1) == 0, true_type, false_type>::result::value; }; template struct Add_ { static const int value = nums1 + nums2; }; template struct Sub_ { static const int value = nums1 - nums2; }; // 加减 template struct addSub { static const auto RES = IF, Sub_>::result::value; }; int main() { cout << isEven<10>::RES << endl; cout << addSub::RES << endl; } ================================================ FILE: learn_class/modern_cpp_30/compilercompute/WhileLoop.cpp ================================================ // // Created by light on 20-1-5. // #include using namespace std; // 使用template实现while循环 template struct WhileLoop; template struct WhileLoop { typedef typename WhileLoop::type type; }; template struct WhileLoop { typedef typename Body::res_type type; }; template struct While { typedef typename WhileLoop::type type; }; template using While_t = WhileLoop; namespace my { template struct integral_constant { static const T value = v; typedef T value_type; typedef integral_constant type; }; } // namespace my template struct SumLoop { // 循环的条件 static const bool cond_value = n != 0; // 循环后的结果 static const int res_value = result; // 循环时的状态 typedef my::integral_constant res_type; // 循环执行一次时的状态 typedef SumLoop next_type; }; template struct Sum { typedef SumLoop<0, n> type; }; template using Sum_t = SumLoop<0, n>; int main() { cout << While::type>::type::value << endl; cout << While_t>::type::value << endl; return 0; } ================================================ FILE: learn_class/modern_cpp_30/compilercompute/factorial.cpp ================================================ // // Created by light on 19-12-28. // #include using namespace std; template struct factorial { static_assert(n >= 0, "Arg must be non-negative"); static const int value = n * factorial::value; }; template <> struct factorial<0> { static const int value = 1; }; int main() { printf("%d\n", factorial<10>::value); return 0; } ================================================ FILE: learn_class/modern_cpp_30/compilercompute/fmap.cpp ================================================ // // Created by light on 20-1-5. // #include #include using namespace std; template