Repository: Mooophy/Cpp-Primer Branch: master Commit: 09bbce2f14a9 Files: 625 Total size: 867.6 KB Directory structure: gitextract_hm71bx_g/ ├── .gitignore ├── .tools/ │ ├── dir_handler.rb │ ├── format.rb │ ├── insert_space_in_include.rb │ ├── remove_blank_tails.rb │ ├── remover.rb │ ├── test.txt │ └── uniform_comments_mark.rb ├── LICENSE ├── README.md ├── ch01/ │ ├── README.md │ ├── ex1_1.cpp │ ├── ex1_10.cpp │ ├── ex1_11.cpp │ ├── ex1_16.cpp │ ├── ex1_20.cpp │ ├── ex1_21.cpp │ ├── ex1_22.cpp │ ├── ex1_23.cpp │ └── ex1_9.cpp ├── ch02/ │ ├── README.md │ ├── ex2_34.cpp │ ├── ex2_35.cpp │ ├── ex2_4.cpp │ ├── ex2_42.h │ ├── ex2_42_1.cpp │ ├── ex2_42_2.cpp │ └── ex2_42_3.cpp ├── ch03/ │ ├── README.md │ ├── ex3_10.cpp │ ├── ex3_14.cpp │ ├── ex3_15.cpp │ ├── ex3_16.cpp │ ├── ex3_17.cpp │ ├── ex3_18.cpp │ ├── ex3_19.cpp │ ├── ex3_1a.cpp │ ├── ex3_1b.cpp │ ├── ex3_20a.cpp │ ├── ex3_20b.cpp │ ├── ex3_21.cpp │ ├── ex3_22.cpp │ ├── ex3_23.cpp │ ├── ex3_24.cpp │ ├── ex3_25.cpp │ ├── ex3_2a.cpp │ ├── ex3_2b.cpp │ ├── ex3_31.cpp │ ├── ex3_32.cpp │ ├── ex3_35.cpp │ ├── ex3_36.cpp │ ├── ex3_39.cpp │ ├── ex3_40.cpp │ ├── ex3_41.cpp │ ├── ex3_42.cpp │ ├── ex3_43.cpp │ ├── ex3_44.cpp │ ├── ex3_45.cpp │ ├── ex3_4a.cpp │ ├── ex3_4b.cpp │ ├── ex3_5a.cpp │ ├── ex3_5b.cpp │ ├── ex3_6.cpp │ └── ex3_8.cpp ├── ch04/ │ ├── README.md │ ├── ex4_21.cpp │ ├── ex4_22.cpp │ └── ex4_28.cpp ├── ch05/ │ ├── README.md │ ├── ex5_10.cpp │ ├── ex5_11.cpp │ ├── ex5_12.cpp │ ├── ex5_14.cpp │ ├── ex5_17.cpp │ ├── ex5_19.cpp │ ├── ex5_20.cpp │ ├── ex5_21.cpp │ ├── ex5_23.cpp │ ├── ex5_24.cpp │ ├── ex5_25.cpp │ ├── ex5_5.cpp │ ├── ex5_6.cpp │ └── ex5_9.cpp ├── ch06/ │ ├── Chapter6.h │ ├── README.md │ ├── ex6_10.cpp │ ├── ex6_11.cpp │ ├── ex6_12.cpp │ ├── ex6_17.cpp │ ├── ex6_21.cpp │ ├── ex6_22.cpp │ ├── ex6_23.cpp │ ├── ex6_25_26.cpp │ ├── ex6_27.cpp │ ├── ex6_33.cpp │ ├── ex6_42.cpp │ ├── ex6_44.cpp │ ├── ex6_47.cpp │ ├── ex6_51.cpp │ ├── ex6_54_55_56.cpp │ ├── fact.cc │ └── factMain.cc ├── ch07/ │ ├── README.md │ ├── ex7_01.cpp │ ├── ex7_02.h │ ├── ex7_03.cpp │ ├── ex7_04.h │ ├── ex7_05.h │ ├── ex7_06.h │ ├── ex7_07.cpp │ ├── ex7_09.h │ ├── ex7_11.cpp │ ├── ex7_11.h │ ├── ex7_12.h │ ├── ex7_13.cpp │ ├── ex7_15.h │ ├── ex7_21.h │ ├── ex7_22.h │ ├── ex7_23.h │ ├── ex7_24.h │ ├── ex7_26.cpp │ ├── ex7_26.h │ ├── ex7_27.h │ ├── ex7_27_TEST.cpp │ ├── ex7_31.h │ ├── ex7_32.h │ ├── ex7_41.cpp │ ├── ex7_41.h │ ├── ex7_41_TEST.cpp │ ├── ex7_43.cpp │ ├── ex7_50.h │ ├── ex7_53.h │ └── ex7_57.h ├── ch08/ │ ├── README.md │ ├── ex8_02.cpp │ ├── ex8_04.cpp │ ├── ex8_05.cpp │ ├── ex8_06.cpp │ ├── ex8_07.cpp │ ├── ex8_08.cpp │ ├── ex8_09.cpp │ ├── ex8_10.cpp │ ├── ex8_11.cpp │ └── ex8_13.cpp ├── ch09/ │ ├── README.md │ ├── ex9_13.cpp │ ├── ex9_14.cpp │ ├── ex9_15.cpp │ ├── ex9_16.cpp │ ├── ex9_18.cpp │ ├── ex9_19.cpp │ ├── ex9_20.cpp │ ├── ex9_22.cpp │ ├── ex9_24.cpp │ ├── ex9_26.cpp │ ├── ex9_27.cpp │ ├── ex9_31_1.cpp │ ├── ex9_31_2.cpp │ ├── ex9_32.cpp │ ├── ex9_33.cpp │ ├── ex9_34.cpp │ ├── ex9_38.cpp │ ├── ex9_41.cpp │ ├── ex9_43.cpp │ ├── ex9_44.cpp │ ├── ex9_45.cpp │ ├── ex9_46.cpp │ ├── ex9_47_1.cpp │ ├── ex9_47_2.cpp │ ├── ex9_49.cpp │ ├── ex9_50.cpp │ ├── ex9_51.cpp │ └── ex9_52.cpp ├── ch10/ │ ├── README.md │ ├── ex10_01_02.cpp │ ├── ex10_03_04.cpp │ ├── ex10_06.cpp │ ├── ex10_07.cpp │ ├── ex10_09.cpp │ ├── ex10_11.cpp │ ├── ex10_12.cpp │ ├── ex10_13.cpp │ ├── ex10_16.cpp │ ├── ex10_17.cpp │ ├── ex10_18_19.cpp │ ├── ex10_20_21.cpp │ ├── ex10_22.cpp │ ├── ex10_24.cpp │ ├── ex10_25.cpp │ ├── ex10_27.cpp │ ├── ex10_28.cpp │ ├── ex10_29.cpp │ ├── ex10_30.cpp │ ├── ex10_31.cpp │ ├── ex10_32.cpp │ ├── ex10_33.cpp │ ├── ex10_34_35_36_37.cpp │ └── ex10_42.cpp ├── ch11/ │ ├── README.md │ ├── ex11_11.cpp │ ├── ex11_12_13.cpp │ ├── ex11_14.cpp │ ├── ex11_20.cpp │ ├── ex11_23.cpp │ ├── ex11_24_25_26.cpp │ ├── ex11_27_28_29_30.cpp │ ├── ex11_31.cpp │ ├── ex11_32.cpp │ ├── ex11_33.cpp │ ├── ex11_38.cpp │ ├── ex11_3_4.cpp │ ├── ex11_7.cpp │ ├── ex11_8.cpp │ └── ex11_9_10.cpp ├── ch12/ │ ├── README.md │ ├── ex12_02.h │ ├── ex12_02_TEST.cpp │ ├── ex12_06.cpp │ ├── ex12_07.cpp │ ├── ex12_10.cpp │ ├── ex12_11.cpp │ ├── ex12_12.cpp │ ├── ex12_13.cpp │ ├── ex12_14.cpp │ ├── ex12_15.cpp │ ├── ex12_16.cpp │ ├── ex12_17_18.cpp │ ├── ex12_19.cpp │ ├── ex12_19.h │ ├── ex12_20.cpp │ ├── ex12_22.cpp │ ├── ex12_22.h │ ├── ex12_23.cpp │ ├── ex12_24.cpp │ ├── ex12_26.cpp │ ├── ex12_27_30.cpp │ ├── ex12_27_30.h │ ├── ex12_27_30_TEST.cpp │ ├── ex12_28.cpp │ ├── ex12_32.cpp │ ├── ex12_32.h │ ├── ex12_32_TEST.cpp │ ├── ex12_33.cpp │ ├── ex12_33.h │ └── ex12_33_TEST.cpp ├── ch13/ │ ├── README.md │ ├── ex13_05.h │ ├── ex13_08.h │ ├── ex13_11.h │ ├── ex13_13.cpp │ ├── ex13_17_1.cpp │ ├── ex13_17_2.cpp │ ├── ex13_17_3.cpp │ ├── ex13_18.cpp │ ├── ex13_18.h │ ├── ex13_19.h │ ├── ex13_22.h │ ├── ex13_26.cpp │ ├── ex13_26.h │ ├── ex13_27.h │ ├── ex13_28.cpp │ ├── ex13_28.h │ ├── ex13_30.h │ ├── ex13_31.cpp │ ├── ex13_31.h │ ├── ex13_34_36_37.cpp │ ├── ex13_34_36_37.h │ ├── ex13_39.cpp │ ├── ex13_39.h │ ├── ex13_40.cpp │ ├── ex13_40.h │ ├── ex13_42.cpp │ ├── ex13_42_StrVec.cpp │ ├── ex13_42_StrVec.h │ ├── ex13_42_TextQuery.cpp │ ├── ex13_42_TextQuery.h │ ├── ex13_44_47.cpp │ ├── ex13_44_47.h │ ├── ex13_48.cpp │ ├── ex13_49_Message.cpp │ ├── ex13_49_Message.h │ ├── ex13_49_StrVec.cpp │ ├── ex13_49_StrVec.h │ ├── ex13_49_String.cpp │ ├── ex13_49_String.h │ ├── ex13_53.cpp │ ├── ex13_53.h │ ├── ex13_53_test.cpp │ └── ex13_58.cpp ├── ch14/ │ ├── README.md │ ├── ex14_02.cpp │ ├── ex14_02.h │ ├── ex14_02_TEST.cpp │ ├── ex14_05.cpp │ ├── ex14_05.h │ ├── ex14_05_TEST.cpp │ ├── ex14_07.cpp │ ├── ex14_07.h │ ├── ex14_07_TEST.cpp │ ├── ex14_13.cpp │ ├── ex14_13.h │ ├── ex14_13_TEST.cpp │ ├── ex14_15.cpp │ ├── ex14_15.h │ ├── ex14_15_TEST.cpp │ ├── ex14_16_StrBlob.cpp │ ├── ex14_16_StrBlob.h │ ├── ex14_16_StrBlobTest.cpp │ ├── ex14_16_StrVec.cpp │ ├── ex14_16_StrVec.h │ ├── ex14_16_StrVecMain.cpp │ ├── ex14_16_String.cpp │ ├── ex14_16_String.h │ ├── ex14_16_StringMain.cpp │ ├── ex14_18_StrBlob.cpp │ ├── ex14_18_StrBlob.h │ ├── ex14_18_StrBlobTest.cpp │ ├── ex14_18_StrVec.cpp │ ├── ex14_18_StrVec.h │ ├── ex14_18_StrVecMain.cpp │ ├── ex14_18_String.cpp │ ├── ex14_18_String.h │ ├── ex14_18_StringMain.cpp │ ├── ex14_22.cpp │ ├── ex14_22.h │ ├── ex14_22_TEST.cpp │ ├── ex14_23.cpp │ ├── ex14_23.h │ ├── ex14_23_TEST.cpp │ ├── ex14_24.cpp │ ├── ex14_24.h │ ├── ex14_24_TEST.cpp │ ├── ex14_26_StrBlob.cpp │ ├── ex14_26_StrBlob.h │ ├── ex14_26_StrBlobTest.cpp │ ├── ex14_26_StrVec.cpp │ ├── ex14_26_StrVec.h │ ├── ex14_26_StrVecMain.cpp │ ├── ex14_26_String.cpp │ ├── ex14_26_String.h │ ├── ex14_26_StringMain.cpp │ ├── ex14_27_28_StrBlob.cpp │ ├── ex14_27_28_StrBlob.h │ ├── ex14_27_28_StrBlobTest.cpp │ ├── ex14_30_StrBlob.cpp │ ├── ex14_30_StrBlob.h │ ├── ex14_30_StrBlobTest.cpp │ ├── ex14_32.cpp │ ├── ex14_32.h │ ├── ex14_35.cpp │ ├── ex14_36.cpp │ ├── ex14_37.cpp │ ├── ex14_38_39.cpp │ ├── ex14_40.cpp │ ├── ex14_42.cpp │ ├── ex14_43.cpp │ ├── ex14_44.cpp │ ├── ex14_45.cpp │ ├── ex14_45.h │ ├── ex14_45_TEST.cpp │ ├── ex14_49.cpp │ ├── ex14_49.h │ └── ex14_49_TEST.cpp ├── ch15/ │ ├── README.md │ ├── ex15.1.2.3/ │ │ ├── CppPrimer.pro │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.11/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.12.13.14/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.15.16.17/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── disc_quote.cpp │ │ ├── disc_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.18.19.20/ │ │ └── main.cpp │ ├── ex15.21.22/ │ │ └── main.cpp │ ├── ex15.23.cpp │ ├── ex15.24.25.cpp │ ├── ex15.26/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── disc_quote.cpp │ │ ├── disc_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.27/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── disc_quote.cpp │ │ ├── disc_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.28.29/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── disc_quote.cpp │ │ ├── disc_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.30/ │ │ ├── basket.cpp │ │ ├── basket.h │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── disc_quote.cpp │ │ ├── disc_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.31.32.33.cpp │ ├── ex15.34.35.36.38/ │ │ ├── StrBlob.h │ │ ├── andquery.cpp │ │ ├── andquery.h │ │ ├── binaryquery.cpp │ │ ├── binaryquery.h │ │ ├── main.cpp │ │ ├── notquery.cpp │ │ ├── notquery.h │ │ ├── orquery.cpp │ │ ├── orquery.h │ │ ├── query.cpp │ │ ├── query.h │ │ ├── query_base.cpp │ │ ├── query_base.h │ │ ├── queryresult.cpp │ │ ├── queryresult.h │ │ ├── textquery.cpp │ │ ├── textquery.h │ │ ├── wordquery.cpp │ │ └── wordquery.h │ ├── ex15.39.40/ │ │ ├── StrBlob.h │ │ ├── main.cpp │ │ ├── query.cpp │ │ ├── query.h │ │ ├── test.txt │ │ ├── textquery.cpp │ │ └── textquery.h │ ├── ex15.4.5.6/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.42_b/ │ │ ├── andquery.cpp │ │ ├── andquery.h │ │ ├── binaryquery.cpp │ │ ├── binaryquery.h │ │ ├── main.cpp │ │ ├── notquery.cpp │ │ ├── notquery.h │ │ ├── orquery.cpp │ │ ├── orquery.h │ │ ├── query.cpp │ │ ├── query.h │ │ ├── query_base.cpp │ │ ├── query_base.h │ │ ├── queryhistory.cpp │ │ ├── queryhistory.h │ │ ├── queryresult.cpp │ │ ├── queryresult.h │ │ ├── test.txt │ │ ├── textquery.cpp │ │ ├── textquery.h │ │ ├── wordquery.cpp │ │ └── wordquery.h │ ├── ex15.42_c/ │ │ ├── andquery.cpp │ │ ├── andquery.h │ │ ├── binaryquery.cpp │ │ ├── binaryquery.h │ │ ├── main.cpp │ │ ├── notquery.cpp │ │ ├── notquery.h │ │ ├── orquery.cpp │ │ ├── orquery.h │ │ ├── query.cpp │ │ ├── query.h │ │ ├── query_base.cpp │ │ ├── query_base.h │ │ ├── queryresult.cpp │ │ ├── queryresult.h │ │ ├── test.txt │ │ ├── textquery.cpp │ │ ├── textquery.h │ │ ├── wordquery.cpp │ │ └── wordquery.h │ ├── ex15.7/ │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ ├── ex15.8.9.10/ │ │ └── ex15.8.9.10.cpp │ └── ex15.9/ │ ├── bulk_quote.cpp │ ├── bulk_quote.h │ ├── limit_quote.cpp │ ├── limit_quote.h │ ├── main.cpp │ ├── quote.cpp │ └── quote.h ├── ch16/ │ ├── ex16.1.2.3/ │ │ └── main.cpp │ ├── ex16.12.13/ │ │ ├── Blob.h │ │ ├── blobptr.h │ │ └── main.cpp │ ├── ex16.14.15/ │ │ ├── Screen.h │ │ └── main.cpp │ ├── ex16.16/ │ │ ├── main.cpp │ │ └── vec.h │ ├── ex16.17.18/ │ │ └── main.cpp │ ├── ex16.19.20/ │ │ └── main.cpp │ ├── ex16.21.22/ │ │ ├── DebugDelete.h │ │ ├── StrBlob.h │ │ ├── main.cpp │ │ ├── wy_queryresult.cpp │ │ ├── wy_queryresult.h │ │ ├── wy_textquery.cpp │ │ └── wy_textquery.h │ ├── ex16.24/ │ │ ├── Blob.h │ │ ├── blobptr.h │ │ └── main.cpp │ ├── ex16.25.26/ │ │ └── main.cpp │ ├── ex16.28/ │ │ ├── delete.hpp │ │ ├── main.cpp │ │ ├── shared_pointer.hpp │ │ └── unique_pointer.h │ ├── ex16.29/ │ │ ├── Blob.h │ │ ├── DebugDelete.h │ │ ├── main.cpp │ │ ├── shared_pointer.h │ │ └── unique_pointer.h │ ├── ex16.32.33.34.35.36/ │ │ └── main.cpp │ ├── ex16.37.38.39/ │ │ └── main.cpp │ ├── ex16.4/ │ │ └── main.cpp │ ├── ex16.40/ │ │ └── main.cpp │ ├── ex16.41/ │ │ └── main.cpp │ ├── ex16.42.43.44.45.46/ │ │ └── main.cpp │ ├── ex16.47/ │ │ └── main.cpp │ ├── ex16.48/ │ │ └── main.cpp │ ├── ex16.49.50/ │ │ └── main.cpp │ ├── ex16.5/ │ │ └── main.cpp │ ├── ex16.51.52/ │ │ └── main.cpp │ ├── ex16.53.54.55/ │ │ └── main.cpp │ ├── ex16.56.57/ │ │ └── main.cpp │ ├── ex16.58.59/ │ │ ├── strvec.cpp │ │ ├── strvec.h │ │ └── vec.h │ ├── ex16.6/ │ │ └── main.cpp │ ├── ex16.60.61/ │ │ └── main.cpp │ ├── ex16.62/ │ │ ├── Sales_data.cc │ │ ├── Sales_data.h │ │ └── main.cpp │ ├── ex16.63.64/ │ │ └── main.cpp │ ├── ex16.65.66.67/ │ │ └── main.cpp │ ├── ex16.7.8/ │ │ └── main.cpp │ └── ex16.9.10.11/ │ └── main.cpp ├── ch17/ │ ├── 17_24.cpp │ ├── 17_25.cpp │ ├── 17_27.cpp │ ├── 17_35_36.cpp │ ├── 17_37_38/ │ │ ├── 17_37_38.cpp │ │ └── test.txt │ ├── data/ │ │ └── record.txt │ ├── ex17.3/ │ │ ├── main.cpp │ │ ├── textquery.cpp │ │ └── textquery.h │ ├── ex17_10.cpp │ ├── ex17_11_12_13.cpp │ ├── ex17_14_15_16.cpp │ ├── ex17_17_18.cpp │ ├── ex17_19_20.cpp │ ├── ex17_1_2.cpp │ ├── ex17_21.cpp │ ├── ex17_23.cpp │ ├── ex17_28_29_30.cpp │ ├── ex17_33.cpp │ ├── ex17_4_5_6_7_8.cpp │ ├── ex17_4_5_6_7_8_SalesData.cpp │ ├── ex17_4_5_6_7_8_SalesData.h │ └── ex17_9.cpp ├── ch18/ │ ├── 18_18 18_19.cpp │ ├── 18_20.cpp │ ├── README.md │ ├── ex18.1.2.3.cpp │ ├── ex18.12.13.14.cpp │ ├── ex18.15.16.17.cpp │ ├── ex18.25.cpp │ └── ex18_29.cpp ├── ch19/ │ ├── ex19_18.cpp │ ├── ex19_20.cpp │ ├── ex19_21_22_23_24_25.cpp │ └── ex19_3_4.cpp ├── data/ │ ├── book.txt │ ├── even.txt │ ├── given_to_transform.txt │ ├── input.txt │ ├── letter.txt │ ├── odd.txt │ ├── phonenumbers.txt │ ├── storyDataFile.txt │ ├── word_transformation.txt │ └── word_transformation_bad.txt └── include/ └── Sales_item.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Mac OS .DS_Store # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # ctags *tags* # vim *.swp ================================================ FILE: .tools/dir_handler.rb ================================================ class DirHandler attr_reader :paths def initialize dir, extension @paths = list_paths_recursively dir, extension end def on_each_line! @paths.each do |path| content = File.readlines path content.each { |line| yield line } write_back path, content end end private def list_paths_recursively dir, extension Dir.glob "#{ dir }/**/*.#{ extension }" end def write_back filename, content File.open(filename, 'w+'){ |file| file.puts content } end end ================================================ FILE: .tools/format.rb ================================================ require_relative 'dir_handler' class Format attr_reader :dir, :extensions def initialize dir, extensions @dir = dir @extensions = extensions @dir.freeze @extensions.freeze @handlers = extensions.collect{ |e| DirHandler.new dir, e } end # 1,2,3,4 => 1, 2, 3, 4 def for_commas each_line! do |line| begin line.gsub! /,(\S)/, ', \1' unless line.match /.*\".*,.*\".*/ or line.match /','/ rescue Exception => e puts e.message + ", ignored." end end end #{foo} => { foo } def for_curly_brackets for_pairs '{', '}' end private def each_line! @handlers.each do |h| h.on_each_line! { |line| yield line } end end def for_pairs open, close each_line! do |line| begin if line.match /#{ open + ".*" + close }/ line.gsub! /#{ open + "(\S)"}/, open + ' \1' line.gsub! /#{ "(\S)" + close }/, '\1 '+ close end rescue Exception => e puts e.message + ", ignored." end end end end format = Format.new ARGV[0], [ "cpp", "h", "hpp", "md" ] format.for_commas format.for_curly_brackets ================================================ FILE: .tools/insert_space_in_include.rb ================================================ # Yue Wang Aug.2015 # change #include to #include # to use # $ ruby insert_space_in_include.rb dir extension if ARGV.size != 2 puts "wrong argumens" exit end dir = ARGV[0] extension = ARGV[1] paths = Dir.glob "#{ dir }/**/*.#{ extension }" paths.each do |path| content = File.readlines path content.each do |line| #check if it's something like "#include" if line.include?('#') and line.split(' ').size == 1 line.sub! "#include", "#include " end end File.open(path, 'w+'){ |file| file.puts content } end ================================================ FILE: .tools/remove_blank_tails.rb ================================================ # # @Yue Wang # Aug 2015 # To use: # $ ruby remove_blank_tails.rb dir extension # class RemoveBlankTails def initialize dir, extension @paths = Dir.glob "#{ dir }/**/*.#{ extension }" end def perform @paths.each do |path| r = File.readlines(path).reverse i = r.find_index "}\n" if i != 0 and !i.nil? content = r.drop(i).reverse File.open(path, 'w+'){ |file| file.puts content } yield path,i if i != 0 end end end end if ARGV.size != 2 puts "wrong argumens" exit end rbt = RemoveBlankTails.new ARGV[0], ARGV[1] rbt.perform do |path, count| puts "[#{ path }] : deleted [#{ count }] lines " end ================================================ FILE: .tools/remover.rb ================================================ # @Yue Wang Aug 2015 # # Recursively remove lines that contain some word from files within the given directory. # To use: # $ ruby remover.rb dirname file_extension key_word # Insensitive casing by default. # class Remover attr_reader :matched, :unmatched def initialize @matched = {} @unmatched = {} end def perform_on filename @filename = filename File.readlines(@filename).each_with_index do |line, i| if yield line, i @matched[ i + 1 ] = line else @unmatched[ i + 1 ] = line end end File.open(@filename, 'w+'){ |file| file.puts @unmatched.values } end end if ARGV.size != 3 puts "wrong agruments" exit end dir = ARGV[0] extension = ARGV[1] expr = ARGV[2] allFilePaths = Dir.glob "#{ dir }/**/*.#{ extension }" pattern = Regexp.new(Regexp.escape(expr), Regexp::IGNORECASE) allFilePaths.each do |path| rm = Remover.new rm.perform_on( path ) do |line, i| begin line.match(pattern) rescue #to handl exception caused by say encoding puts "\n---------------exception happens when reading the line:" puts line puts "file: [#{ path }] line:[#{ i }]" puts "---------------just igonored\n" result = false end end if !rm.matched.empty? puts "lines deleted in file [#{ path }] :" else puts "no matched found in file [#{ path }]." end rm.matched.each do |idx, line| puts "[#{ idx }] #{ line }" end end ================================================ FILE: .tools/test.txt ================================================ _23456789 _bcdefghijk _---------- _f _lse ================================================ FILE: .tools/uniform_comments_mark.rb ================================================ # Yue Wang Aug.2015 # sub /// or //! to // # to use: # $ ruby uniform_comments_mark.rb dir extension if ARGV.size != 2 puts "wrong argumens" exit end dir = ARGV[0] extension = ARGV[1] paths = Dir.glob "#{ dir }/**/*.#{ extension }" paths.each do |path| content = File.readlines path content.each do |line| line.sub! "///", "//" line.sub! "//!", "//" end File.open(path, 'w+'){ |file| file.puts content } end ================================================ FILE: LICENSE ================================================ CC0 1.0 Universal Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. For more information, please see ================================================ FILE: README.md ================================================ ## C++ Primer 5 Answers(C++11/14) [![GitHub issues](https://img.shields.io/github/issues/Mooophy/Cpp-Primer.svg)](https://github.com/Mooophy/Cpp-Primer/issues) [![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/Mooophy/Cpp-Primer/master/LICENSE) [![](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-%E8%AE%A8%E8%AE%BA%E5%8C%BA-yellowgreen.svg)](https://github.com/ReadingLab/Discussion-for-Cpp) [![](https://img.shields.io/badge/douban-%E5%B0%8F%E7%BB%84-green.svg)](http://www.douban.com/group/532124/) ### Compilers recommended * Windows: Visual Studio 2015+ * Linux: g++ 5.0+ ``` g++ -std=c++14 some_ex.cpp -o some_ex ``` * Mac: clang++ 3.7+ ``` clang++ -std=c++1y some_ex.cpp -o some_ex ``` ### Contents - [Chapter 1. Getting Started](ch01/README.md) - Part I: The Basics - [Chapter 2. Variables and Basic Types](ch02/README.md) - [Chapter 3. Strings, Vectors, and Arrays](ch03/README.md) - [Chapter 4. Expressions](ch04/README.md) - [Chapter 5. Statements](ch05/README.md) - [Chapter 6. Functions](ch06/README.md) - [Chapter 7. Classes](ch07/README.md) - Part II: The C++ Library - [Chapter 8. The IO Library](ch08/README.md) - [Chapter 9. Sequential Containers](ch09/README.md) - [Chapter 10. Generic Algorithms](ch10/README.md) - [Chapter 11. Associative Containers](ch11/README.md) - [Chapter 12. Dynamic Memory](ch12/README.md) - Part III: Tools for Class Authors - [Chapter 13. Copy Control](ch13/README.md) - [Chapter 14. Overloaded Operations and Conversions](ch14/README.md) - [Chapter 15. Object-Oriented Programming](ch15/README.md) - [Chapter 16. Templates and Generic Programming](ch16) - Part IV: Advanced Topics - [Chapter 17. Specialized Library Facilities](ch17) - [Chapter 18. Tools for Large Programs](ch18) - [Chapter 19. Specialized Tools and Techniques](ch19) ================================================ FILE: ch01/README.md ================================================ ## Exercise 1.1 > Review the documentation for your compiler and determine what file naming convention it uses. Compile and run the main program from page 2. * [GCC and File Extensions](http://labor-liber.org/en/gnu-linux/development/index.php?diapo=extensions) * [File Types Created for Visual C++ Projects](https://msdn.microsoft.com/en-us/library/3awe4781.aspx) ## Exercise 1.2 > Exercise 1.2: Change the program to return -1. A return value of -1 is often treated as an indicator that the program failed. Recompile and rerun your program to see how your system treats a failure indicator from main. ### Windows ![windows](https://db.tt/DIJd9eZb) ### Linux ![linux](https://db.tt/lhzXhpCt) **255**? why? please look at [this](http://www.tldp.org/LDP/abs/html/exitcodes.html) ## Exercise 1.3 > Write a program to print Hello, World on the standard output. ```cpp #include int main() { std::cout << "Hello, World" << std::endl; return 0; } ``` ## Exercise 1.4 > Our program used the addition operator, +, to add two numbers. Write a program that uses the multiplication operator, *, to print the product instead. ```cpp #include int main() { std::cout << "Enter two numbers:" << std::endl; int v1 = 0, v2 = 0; std::cin >> v1 >> v2; std::cout << "The product is " << v1 * v2 << std::endl; return 0; } ``` ## Exercise 1.5 > We wrote the output in one large statement. Rewrite the program to use a separate statement to print each operand. ```cpp #include int main() { std::cout << "Enter two numbers:" << std::endl; int v1 = 0, v2 = 0; std::cin >> v1 >> v2; std::cout << "The product of "; std::cout << v1; std::cout << " and "; std::cout << v2; std::cout << " is "; std::cout << v1 * v2; std::cout << std::endl; return 0; } ``` ## Exercise 1.6 > Explain whether the following program fragment is legal. It's illegal. **[Error] expected primary-expression before '<<' token** Fixed it: remove the spare semicolons. ```cpp std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl; ``` ## Exercise 1.7 > Compile a program that has incorrectly nested comments. Example: ```cpp /* * comment pairs /* */ cannot nest. * ''cannot nest'' is considered source code, * as is the rest of the program */ int main() { return 0; } ``` Compiled result(g++): ![result](https://db.tt/CqQKu8GQ) ## Exercise 1.8 > Indicate which, if any, of the following output statements are legal: ```cpp std::cout << "/*"; std::cout << "*/"; std::cout << /* "*/" */; std::cout << /* "*/" /* "/*" */; ``` > After you’ve predicted what will happen, test your answers by compiling a program with each of these statements. Correct any errors you encounter. Compiled result(g++): ![result](https://db.tt/mrL9hDCS) Corrected? just added a quote: ```cpp std::cout << "/*"; std::cout << "*/"; std::cout << /* "*/" */"; std::cout << /* "*/" /* "/*" */; ``` Output: /**/ */ /* ## [Exercise 1.9](ex1_9.cpp) ## [Exercise 1.10](ex1_10.cpp) ## [Exercise 1.11](ex1_11.cpp) ## Exercise 1.12 > What does the following for loop do? What is the final value of sum? ```cpp int sum = 0; for (int i = -100; i <= 100; ++i) sum += i; ``` the loop sums the numbers from -100 to 100. the final value of sum is zero. ## Exercise 1.13 > Rewrite the exercises from § 1.4.1 (p. 13) using for loops. Ex1.9: ```cpp #include int main() { int sum = 0; for (int i = 50; i <= 100; ++i) sum += i; std::cout << "the sum is: " << sum << std::endl; return 0; } ``` Ex1.10: ```cpp #include int main() { for (int i = 10; i >= 0; --i) std::cout << i << std::endl; return 0; } ``` Ex1.11: ```cpp #include int main() { std::cout << "please input two integers:\n"; int small = 0, big = 0; std::cin >> small >> big; if (small > big) { int tmp = small; small = big; big = tmp; } for (int i = small; i != big; ++i) std::cout << i << std::endl; return 0; } ``` ## Exercise 1.14 > Compare and contrast the loops that used a for with those using a while. Are there advantages or disadvantages to using either form? [A similar question on Stack Overflow](http://stackoverflow.com/questions/2950931/for-vs-while-in-c-programming) ## Exercise 1.15 > Write programs that contain the common errors discussed in the box on page 16. Familiarize yourself with the messages the compiler generates. **Syntax Errors**: ```c++ int main(){ std::cout << "Hello World!" << std::endl // semicolon missed return 0; } ``` **Type errors**: ```c++ int main(){ char s = "Hello World!"; // Here char should be std::string std::cout << s << endl; return 0; } ``` **Declaration errors**: ```c++ int main(){ int k = 0; std::cout << K << std::endl; // use of undeclared identifier 'K' return 0; } ``` ## Exercise 1.16 ```cpp #include int main() { int sum = 0; for (int val; std::cin >> val; sum += val); std::cout << sum << std::endl; return 0; } ``` ## Exercise 1.17 > What happens in the program presented in this section if the input values are all equal? What if there are no duplicated values? If the input values are all equal, it will print a line which shows the count of the number you input. If there are no duplicated values, when different values input, a new line will be printed if you click `Enter`. ## Exercise 1.18 > Compile and run the program from this section giving it only equal values as input. Run it again giving it values in which no number is repeated. ![run](https://db.tt/F38zExnq) ## Exercise 1.19 > Revise the program you wrote for the exercises in § 1.4.1 (p. 13) that printed a range of numbers so that it handles input in which the first number is smaller than the second. [code](https://github.com/pezy/Cpp-Primer/blob/master/ch01/ex1_11.cpp) ## Exercise 1.20 > http://www.informit.com/title/032174113 contains a copy of Sales_item.h in the Chapter 1 code directory. Copy that file to your working directory. Use it to write a program that reads a set of book sales transactions, writing each transaction to the standard output. [Here](ex1_20.cpp) is the code. Note : C++11 flag need to enable. For GCC and Clang, this can be done with the `-std=c++11` ## Exercise 1.21 > Write a program that reads two Sales_item objects that have the same ISBN and produces their sum. The program should check whether the objects have the same ISBN. [Code](ex1_21.cpp) ## Exercise 1.22 > Write a program that reads several transactions for the same ISBN. Write the sum of all the transactions that were read. Tips: this program will appear in the section 1.6. [Here](ex1_22.cpp) is the code. ![run](https://db.tt/UlkuvpAS) ## Exercise 1.23 > Write a program that reads several transactions and counts how many transactions occur for each ISBN. Tip: please review the `1.4.4`. [Here](ex1_23.cpp) is the code. ## Exercise 1.24 > Test the previous program by giving multiple transactions representing multiple ISBNs. The records for each ISBN should be grouped together. `data/book.txt` may be used as the records. ![run](https://db.tt/EeDI7lvN) ## Exercise 1.25 > Using the Sales_item.h header from the Web site, compile and execute the bookstore program presented in this section. It is the same as Exercise 1.22. ![run](https://db.tt/C6OOPuzA) ================================================ FILE: ch01/ex1_1.cpp ================================================ int main() { return 0; } ================================================ FILE: ch01/ex1_10.cpp ================================================ // prints the numbers from ten down to zero.(use while) #include int main() { int i = 10; while (i >= 0) std::cout << i-- << " "; return 0; } ================================================ FILE: ch01/ex1_11.cpp ================================================ // Print each number in the range specified by two integers. #include using std::cout; using std::cin; void print_range(int low, int high) { if(low > high) { print_range(high, low); return; } for (int i = low; i <= high; ++i) { cout << i << " "; } } int main() { int low = 0, high = 0; cout << "Enter two integers:\n"; cin >> low >> high; print_range(low, high); return 0; } ================================================ FILE: ch01/ex1_16.cpp ================================================ #include using std::cin; using std::cout; using std::endl; int main() { int sum = 0; for (int i = 0; cin >> i; sum += i); cout << "Sum is: " << sum << endl; return 0; } ================================================ FILE: ch01/ex1_20.cpp ================================================ #include #include "include/Sales_item.h" using std::cin; using std::cout; using std::endl; int main() { for (Sales_item item; cin >> item; cout << item << endl); return 0; } ================================================ FILE: ch01/ex1_21.cpp ================================================ #include #include "include/Sales_item.h" using std::cin; using std::cout; using std::endl; using std::cerr; int main() { Sales_item item1, item2; cin >> item1 >> item2; if (item1.isbn() == item2.isbn()) cout << item1 + item2 << endl; else cerr << "Different ISBN." << endl; } ================================================ FILE: ch01/ex1_22.cpp ================================================ #include #include "include/Sales_item.h" int main() { Sales_item total; if (std::cin >> total) { Sales_item trans; while (std::cin >> trans) { if (total.isbn() == trans.isbn()) total += trans; else { std::cout << total << std::endl; total = trans; } } std::cout << total << std::endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0; } ================================================ FILE: ch01/ex1_23.cpp ================================================ #include #include "include/Sales_item.h" int main() { Sales_item currItem, valItem; if (std::cin >> currItem) { int cnt = 1; while (std::cin >> valItem) { if (valItem.isbn() == currItem.isbn()) { ++cnt; } else { std::cout << currItem << " occurs " << cnt << " times " << std::endl; currItem = valItem; cnt = 1; } } std::cout << currItem << " occurs "<< cnt << " times " << std::endl; } return 0; } ================================================ FILE: ch01/ex1_9.cpp ================================================ // sum the numbers from 50 to 100 (use while) #include auto sum(int lo, int hi) { int sum = 0; while (lo <= hi) sum += lo++; return sum; } int main() { std::cout << "Sum of 50 to 100 inclusive is: " << sum(50, 100) << std::endl; return 0; } ================================================ FILE: ch02/README.md ================================================ ### Exercise 2.1 > What are the differences between int, long, long long, and short? Between an unsigned and a signed type? Between a float and a double? C++ guarantees `short` and `int` is **at least** 16 bits, `long` **at least** 32 bits, `long long` **at least** 64 bits. The `signed` can represent positive numbers, negative numbers and zero, while `unsigned` can only represent numbers no less than zero. The C and C++ standards do not specify the representation of float, double and long double. It is possible that all three implemented as IEEE double-precision. Nevertheless, for most architectures (gcc, MSVC; x86, x64, ARM) float is indeed a IEEE **single-precision** floating point number (binary32), and double is a IEEE **double-precision** floating point number (binary64). Usage: - Use `int` for integer arithmetic. `short` is usually too small and, in practice, `long` often has the same size as `int`. If your data values are larger than the minimum guaranteed size of an `int`, then use `long long`. (In a word: short < **int** < long < long long) - Use an unsigned type when you know that the values cannot be negative. (In a word: no negative, unsigned.) - Use double for floating-point computations; float usually does not have enough precision, and the cost of double-precision calculations versus single-precision is negligible. In fact, on some machines, double-precision operations are faster than single. The precision offered by long double usually is unnecessary and often entails considerable run-time cost. (In a word: float < **double** < long double) Reference: - [What are the criteria for choosing between short / int / long data types?](https://isocpp.org/wiki/faq/newbie#choosing-int-size) - [Difference between float and double](http://stackoverflow.com/questions/2386772/difference-between-float-and-double) - Advice: Deciding which Type to Use(This book.) ## Exercise 2.2 >To calculate a mortgage payment, what types would you use for the rate, principal, and payment? Explain why you selected each type. use `double`, or also `float`. The rate most like that: 4.50 % per year. The principal most like that: $854.36 The payment most like that: $1, 142.36 Reference: - [mortgage-calculator](http://www.bankrate.com/calculators/mortgages/mortgage-calculator.aspx) - [What's in a Mortgage Payment?](https://thelawdictionary.org/mortgage-payment/) ## Exercise 2.3 > What output will the following code produce? ```cpp unsigned u = 10, u2 = 42; std::cout << u2 - u << std::endl; std::cout << u - u2 << std::endl; int i = 10, i2 = 42; std::cout << i2 - i << std::endl; std::cout << i - i2 << std::endl; std::cout << i - u << std::endl; std::cout << u - i << std::endl; ``` Output(g++ 4.8): ``` 32 4294967264 32 -32 0 0 ``` ## Exercise 2.4 > Write a program to check whether your predictions were correct. If not, study this section until you understand what the problem is. [Here](ex2_4.cpp) is the code, please test it in your computer. ## Exercise 2.5 > Determine the type of each of the following literals. Explain the differences among the literals in each of the four examples: - (a) 'a', L'a', "a", L"a" - (b) 10, 10u, 10L, 10uL, 012, 0xC - (c) 3.14, 3.14f, 3.14L - (d) 10, 10u, 10., 10e-2 (a): character literal, wide character literal, string literal, string wide character literal. (b): decimal, unsigned decimal, long decimal, unsigned long decimal, octal, hexadecimal. (c): double, float, long double. (d): decimal, unsigned decimal, double, double. ## Exercise 2.6 > What, if any, are the differences between the following definitions: ```cpp int month = 9, day = 7; int month = 09, day = 07; ``` The first line's integer is decimal. The second line: 1. `int month = 09` is invalid, cause octal don't have digit `9`. 2. `day` is octal. ## Exercise 2.7 > What values do these literals represent? What type does each have? - (a) "Who goes with F\145rgus?\012" - (b) 3.14e1L - (c) 1024f - (d) 3.14L (a): Who goes with Fergus?(new line) "string" (b): 31.4 "long double" (c): ERROR: The suffix f is valid only with floating point literals (d): 3.14 "long double" Reference: - [ASCII Table](http://www.asciitable.com/) ## Exercise 2.8 > Using escape sequences, write a program to print 2M followed by a newline. Modify the program to print 2, then a tab, then an M, followed by a newline. ```cpp #include int main() { std::cout << 2 << "\115\012"; std::cout << 2 << "\t\115\012"; return 0; } ``` ## Exercise 2.9 >Explain the following definitions. For those that are illegal, explain what’s wrong and how to correct it. - (a) std::cin >> int input_value; - (b) int i = { 3.14 }; - (c) double salary = wage = 9999.99; - (d) int i = 3.14; (a): error: expected '(' for function-style cast or type construction. ```cpp int input_value = 0; std::cin >> input_value; ``` (b):---when you compile the code without the argument "-std=c++11", you will get the warning below: warning: implicit conversion from 'double' to 'int' changes value from 3.14 to 3. ---when you compile the code using "-std=c+11", you will get a error below: error: type 'double' cannot be narrowed to 'int' in initializer list ---conclusion: Obviously, list initialization becomes strict in c++11. ```cpp double i = { 3.14 }; ``` (c): --if you declared 'wage' before, it's right. Otherwise, you'll get a error: error: use of undeclared identifier 'wage' ```cpp double wage; double salary = wage = 9999.99; ``` (d): ok: but value will be truncated. ```cpp double i = 3.14; ``` ## Exercise 2.10 >What are the initial values, if any, of each of the following variables? ```cpp std::string global_str; int global_int; int main() { int local_int; std::string local_str; } ``` `global_str` is global variable, so the value is empty string. `global_int` is global variable, so the value is zero. `local_int` is a local variable which is uninitialized, so it has a undefined value. `local_str` is also a local variable which is uninitialized, but it has a value that is defined by the class. So it is empty string. PS: please read P44 in the English version, P40 in Chinese version to get more. The note: Uninitialized objects of built-in type defined inside a function body have a undefined value. Objects of class type that we do not explicitly inititalize have a value that is defined by class. ## Exercise 2.11 > Explain whether each of the following is a declaration or a definition: - (a) extern int ix = 1024; - (b) int iy; - (c) extern int iz; (a): definition. (b): definition. (c): declaration. ## Exercise 2.12 >Which, if any, of the following names are invalid? - (a) int double = 3.14; - (b) int _; - (c) int catch-22; - (d) int 1_or_2 = 1; - (e) double Double = 3.14; `a`, `c`, `d` are invalid. ## Exercise 2.13 >What is the value of j in the following program? ```cpp int i = 42; int main() { int i = 100; int j = i; } ``` `100`, since the global `i` was hidden by the local `i`. ## Exercise 2.14 >Is the following program legal? If so, what values are printed? ```cpp int i = 100, sum = 0; for (int i = 0; i != 10; ++i) sum += i; std::cout << i << " " << sum << std::endl; ``` Legal. Output: ```100 45``` Note: Such naming is considered as bad practise. ## Exercise 2.15 >Which of the following definitions, if any, are invalid? Why? - (a) int ival = 1.01; - (b) int &rval1 = 1.01; - (c) int &rval2 = ival; - (d) int &rval3; ``` (a): valid. (b): invalid. initializer must be an object. (c): valid. (d): invalid. a reference must be initialized. ``` ## Exercise 2.16 >Which, if any, of the following assignments are invalid? If they are valid, explain what they do. int i = 0, &r1 = i; double d = 0, &r2 = d; - (a) r2 = 3.14159; - (b) r2 = r1; - (c) i = r2; - (d) r1 = d; ``` (a): valid. let d equal 3.14159. (b): valid. automatic convert will happen. (c): valid. but value will be truncated. (d): valid. but value will be truncated. ``` ## Exercise 2.17 >What does the following code print? ```cpp int i, &ri = i; i = 5; ri = 10; std::cout << i << " " << ri << std::endl; ``` `10 10` ## Exercise 2.18 >Write code to change the value of a pointer. Write code to change the value to which the pointer points. ```cpp int a = 0, b = 1; int *p1 = &a, *p2 = p1; // change the value of a pointer. p1 = &b; // change the value to which the pointer points *p2 = b; ``` ## Exercise 2.19 >Explain the key differences between pointers and references. #### definition: the pointer is "points to" any other type. the reference is "another name" of an **object**. #### key difference: 1. a reference is another name of an **already existing** object. a pointer is an object in its **own right**. 2. Once initialized, a reference remains **bound to** its initial object. There is **no way** to rebind a reference to refer to a different object. a pointer can be **assigned** and **copied**. 3. a reference always get the object to which the reference was initially bound. a single pointer can point to **several different objects** over its lifetime. 4. a reference must be initialized. a pointer need **not be** initialized at the time it is defined. ## Exercise 2.20 >What does the following program do? ```cpp int i = 42; int *p1 = &i; *p1 = *p1 * *p1; ``` `p1` pointer to `i`, `i`'s value changed to 1764(42*42) ## Exercise 2.21 >Explain each of the following definitions. Indicate whether any are illegal and, if so, why. int i = 0; - (a) double* dp = &i; - (b) int *ip = i; - (c) int *p = &i; ``` (a): illegal, cannot initialize a variable of type 'double *' with an rvalue of type 'int *' (b): illegal, cannot initialize a variable of type 'int *' with an lvalue of type 'int' (c): legal. ``` ## Exercise 2.22 Assuming p is a pointer to int, explain the following code: ```cpp if (p) // ... if (*p) // ... ``` if (p) // whether p is nullptr? if (*p) // whether the value pointed by p is zero? ## Exercise 2.23 >Given a pointer p, can you determine whether p points to a valid object? If so, how? If not, why not? No. Because more information needed to determine whether the pointer is valid or not. ## Exercise 2.24 >Why is the initialization of p legal but that of lp illegal? ```cpp int i = 42; void *p = &i; long *lp = &i; ``` Inherited from C, `void*` is a special pointer that may point to any type, hence the second line is legal. For type safety, C++ forbids implicit conversions like `long *lp = &i;`, thus such code is illegal. ## Exercise 2.25 >Determine the types and values of each of the following variables. - (a) int* ip, i, &r = i; - (b) int i, *ip = 0; - (c) int* ip, ip2; ``` (a): ip is a pointer to int, i is an int, r is a reference to int i. (b): ip is a valid, null pointer, and i is an int. (c): ip is a pointer to int, and ip2 is an int. ``` ## Exercise 2.26 >Which of the following are legal? For those that are illegal, explain why. ```cpp const int buf; // illegal, buf is uninitialized const. int cnt = 0; // legal. const int sz = cnt; // legal. ++cnt; // legal. ++sz; // illegal, attempt to write to const object(sz). ``` ## Exercise 2.27 > Which of the following initializations are legal? Explain why. ```cpp int i = -1, &r = 0; // illegal, r must refer to an object. int *const p2 = &i2; // legal. const int i = -1, &r = 0; // legal. const int *const p3 = &i2; // legal. const int *p1 = &i2; // legal const int &const r2; // illegal, r2 is a reference that cannot be const. const int i2 = i, &r = i; // legal. ``` ## Exercise 2.28 >Explain the following definitions. Identify any that are illegal. ```cpp int i, *const cp; // illegal, cp must initialize. int *p1, *const p2; // illegal, p2 must initialize. const int ic, &r = ic; // illegal, ic must initialize. const int *const p3; // illegal, p3 must initialize. const int *p; // legal. a pointer to const int. ``` ## Exercise 2.29 >Uing the variables in the previous exercise, which of the following assignments are legal? Explain why. ```cpp i = ic; // legal. p1 = p3; // illegal. p3 is a const pointer to const int. p1 = ⁣ // illegal. ic is a const int. p3 = ⁣ // illegal. p3 is a const pointer. p2 = p1; // illegal. p2 is a const pointer. ic = *p3; // illegal. ic is a const int. ``` ## Exercise 2.30 >For each of the following declarations indicate whether the object being declared has top-level or low-level const. ```cpp const int v2 = 0; int v1 = v2; int *p1 = &v1, &r1 = v1; const int *p2 = &v2, *const p3 = &i, &r2 = v2; ``` v2 is top-level const. p2 is low-level const. p3 is both low-level and top-level const. r2 is low-level const. ## Exercise 2.31 >Given the declarations in the previous exercise determine whether the following assignments are legal. Explain how the top-level or low-level const applies in each case. ```cpp r1 = v2; // legal, top-level const in v2 is ignored. p1 = p2; // illegal, p2 has a low-level const but p1 doesn't. p2 = p1; // legal, we can convert int* to const int*. p1 = p3; // illegal, p3 has a low-level const but p1 doesn't. p2 = p3; // legal, p2 has the same low-level const qualification as p3. ``` ## Exercise 2.32 >Is the following code legal or not? If not, how might you make it legal? int null = 0, *p = null; illegal. ```cpp int null = 0, *p = &null; int null = 0, *p = nullptr; ``` ## Exercise 2.33 >Using the variable definitions from this section, determine what happens in each of these assignments: ```cpp a=42; // set 42 to int a. b=42; // set 42 to int b. c=42; // set 42 to int c. d=42; // ERROR, d is an int *. correct: *d = 42; e=42; // ERROR, e is an const int *. correct: e = &c; g=42; // ERROR, g is a const int& that is bound to ci. ``` ## Exercise 2.34 >Write a program containing the variables and assignments from the previous exercise. Print the variables before and after the assignments to check whether your predictions in the previous exercise were correct. If not, study the examples until you can convince yourself you know what led you to the wrong conclusion. [Here](ex2_34.cpp) is the code. ## Exercise 2.35 >Determine the types deduced in each of the following definitions. Once you’ve figured out the types, write a program to see whether you were correct. ```cpp const int i = 42; auto j = i; const auto &k = i; auto *p = &i; const auto j2 = i, &k2 = i; ``` j is int. k is const int&. p is const int *. j2 is const int. k2 is const int&. [Here](ex2_35.cpp) is the code. ## Exercise 2.36 >In the following code, determine the type of each variable and the value each variable has when the code finishes: ```cpp int a = 3, b = 4; decltype(a) c = a; decltype((b)) d = a; ++c; ++d; ``` `c` is an int, `d` is a reference of `a`. all their value are `4`. ## Exercise 2.37 >Assignment is an example of an expression that yields a reference type. The type is a reference to the type of the left-hand operand. That is, if i is an int, then the type of the expression i = x is int&. Using that knowledge, determine the type and value of each variable in this code: ```cpp int a = 3, b = 4; decltype(a) c = a; decltype(a = b) d = a; ``` `c` is an int, `d` is a reference of int. the value: a=3, b=4, c=3, d=3 ## Exercise 2.38 >Describe the differences in type deduction between decltype and auto. Give an example of an expression where auto and decltype will deduce the same type and an example where they will deduce differing types. The way `decltype` handles top-level const and references differs **subtly** from the way `auto` does. Another important difference between `decltype` and `auto` is that the deduction done by decltype depends on the **form** of its given expression. so the key of difference is **subtly** and **form**. ```cpp int i = 0, &r = i; // same auto a = i; decltype(i) b = i; // different "c" will be int "d" will be int& auto c = r; decltype(r) d = r; ``` More? Look at [here](http://stackoverflow.com/questions/21369113/what-is-the-difference-between-auto-and-decltypeauto-when-returning-from-a-fun) and [here](http://stackoverflow.com/questions/12084040/decltype-vs-auto) ## Exercise 2.39 >Compile the following program to see what happens when you forget the semicolon after a class definition. Remember the message for future reference. ```cpp struct Foo { /* empty */ } // Note: no semicolon int main() { return 0; } ``` Error message: [Error] expected ';' after struct definition ## Exercise 2.40 >Write your own version of the Sales_data class. ```cpp struct Sale_data { std::string bookNo; std::string bookName; unsigned units_sold = 0; double revenue = 0.0; double price = 0.0; //... } ``` ## Exercise 2.41 >Use your Sales_data class to rewrite the exercises in § 1.5.1(p. 22), § 1.5.2(p. 24), and § 1.6(p. 25). For now, you should define your Sales_data class in the same file as your main function. ####1.5.1 ```cpp #include #include struct Sale_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sale_data book; double price; std::cin >> book.bookNo >> book.units_sold >> price; book.revenue = book.units_sold * price; std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price; return 0; } ``` ####1.5.2 ```cpp #include #include struct Sale_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sale_data book1, book2; double price1, price2; std::cin >> book1.bookNo >> book1.units_sold >> price1; std::cin >> book2.bookNo >> book2.units_sold >> price2; book1.revenue = book1.units_sold * price1; book2.revenue = book2.units_sold * price2; if (book1.bookNo == book2.bookNo) { unsigned totalCnt = book1.units_sold + book2.units_sold; double totalRevenue = book1.revenue + book2.revenue; std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " "; if (totalCnt != 0) std::cout << totalRevenue / totalCnt << std::endl; else std::cout << "(no sales)" << std::endl; return 0; } else { std::cerr << "Data must refer to same ISBN" << std::endl; return -1; // indicate failure } } ``` ####1.6 ```cpp #include #include struct Sale_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sale_data total; double totalPrice; if (std::cin >> total.bookNo >> total.units_sold >> totalPrice) { total.revenue = total.units_sold * totalPrice; Sale_data trans; double transPrice; while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice) { trans.revenue = trans.units_sold * transPrice; if (total.bookNo == trans.bookNo) { total.units_sold += trans.units_sold; total.revenue += trans.revenue; } else { std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " "; if (total.units_sold != 0) std::cout << total.revenue / total.units_sold << std::endl; else std::cout << "(no sales)" << std::endl; total.bookNo = trans.bookNo; total.units_sold = trans.units_sold; total.revenue = trans.revenue; } } std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " "; if (total.units_sold != 0) std::cout << total.revenue / total.units_sold << std::endl; else std::cout << "(no sales)" << std::endl; return 0; } else { std::cerr << "No data?!" << std::endl; return -1; // indicate failure } } ``` ## Exercise 2.42 >Write your own version of the Sales_data.h header and use it to rewrite the exercise from § 2.6.2(p. 76) - 1.5.1. [Code](ex2_42_1.cpp) - 1.5.2. [Code](ex2_42_2.cpp) - 1.6. [Code](ex2_42_3.cpp) ================================================ FILE: ch02/ex2_34.cpp ================================================ #include int main() { int i = 0, &r = i; auto a = r; // a is an int (r is an alias for i, which has type int) const int ci = i, &cr = ci; auto b = ci; // b is an int (top-level const in ci is dropped) auto c = cr; // c is an int (cr is an alias for ci whose const is top-level) auto d = &i; // d is an int* (& ofan int objectis int*) auto e = &ci; // e is const int*(& of a const object is low-level const) const auto f = ci; // deduced type of ci is int; f has type const int auto &g = ci; // g is a const int& that is bound to ci a = 42; b = 42; c = 42; *d = 42; e = &c; return 0; } ================================================ FILE: ch02/ex2_35.cpp ================================================ #include #include int main() { const int i = 42; auto j = i; const auto &k = i; auto *p = &i; const auto j2 = i, &k2 = i; // print i means int, and PKi means pointer to const int. std::cout << "j is " << typeid(j).name() << "\nk is " << typeid(k).name() << "\np is " << typeid(p).name() << "\nj2 is " << typeid(j2).name() << "\nk2 is " << typeid(k2).name() << std::endl; return 0; } ================================================ FILE: ch02/ex2_4.cpp ================================================ #include int main() { unsigned u = 10, u2 = 42; std::cout << u2 - u << std::endl; // 32 std::cout << u - u2 << std::endl; // 4294967264 int i = 10, i2 = 42; std::cout << i2 - i << std::endl; // 32 std::cout << i - i2 << std::endl; // -32 std::cout << i - u << std::endl; // 0 std::cout << u - i << std::endl; // 0 return 0; } ================================================ FILE: ch02/ex2_42.h ================================================ #ifndef CH02_EX2_42_H_ #define CH02_EX2_42_H_ #include #include struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; void CalcRevenue(double price); double CalcAveragePrice(); void SetData(Sales_data data); void AddData(Sales_data data); void Print(); }; void Sales_data::CalcRevenue(double price) { revenue = units_sold * price; } void Sales_data::SetData(Sales_data data) { bookNo = data.bookNo; units_sold = data.units_sold; revenue = data.revenue; } void Sales_data::AddData(Sales_data data) { if (bookNo != data.bookNo) return; units_sold += data.units_sold; revenue += data.revenue; } double Sales_data::CalcAveragePrice() { if (units_sold != 0) return revenue / units_sold; else return 0.0; } void Sales_data::Print() { std::cout << bookNo << " " << units_sold << " " << revenue << " "; double averagePrice = CalcAveragePrice(); if (averagePrice != 0.0) std::cout << averagePrice << std::endl; else std::cout << "(no sales)" << std::endl; } #endif // CH02_EX2_42_H_ ================================================ FILE: ch02/ex2_42_1.cpp ================================================ #include #include "ex2_42.h" int main() { Sales_data book; double price; std::cin >> book.bookNo >> book.units_sold >> price; book.CalcRevenue(price); book.Print(); return 0; } ================================================ FILE: ch02/ex2_42_2.cpp ================================================ #include #include "ex2_42.h" int main() { Sales_data book1, book2; double price1, price2; std::cin >> book1.bookNo >> book1.units_sold >> price1; std::cin >> book2.bookNo >> book2.units_sold >> price2; book1.CalcRevenue(price1); book2.CalcRevenue(price2); if (book1.bookNo == book2.bookNo) { book1.AddData(book2); book1.Print(); return 0; } else { std::cerr << "Data must refer to same ISBN" << std::endl; return -1; // indicate failure } } ================================================ FILE: ch02/ex2_42_3.cpp ================================================ #include #include "ex2_42.h" int main() { Sales_data total; double totalPrice; if (std::cin >> total.bookNo >> total.units_sold >> totalPrice) { total.CalcRevenue(totalPrice); Sales_data trans; double transPrice; while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice) { trans.CalcRevenue(transPrice); if (total.bookNo == trans.bookNo) { total.AddData(trans); } else { total.Print(); total.SetData(trans); } } total.Print(); return 0; } else { std::cerr << "No data?!" << std::endl; return -1; // indicate failure } } ================================================ FILE: ch03/README.md ================================================ ## Exercise 3.1 : [part1](ex3_1a.cpp) | [part2](ex3_1b.cpp) ## Exercise 3.2 : [part1](ex3_2a.cpp) | [part2](ex3_2b.cpp) ## Exercise 3.3 >Explain how whitespace characters are handled in the string input operator and in the `getline` function. * For code like `is >> s`, input is separated by whitespaces while reading into string `s`. * For code like `getline(is, s)` input is separated by newline `\n` while reading into string `s`. Other whitespaces are ignored. * For code like `getline(is, s, delim)`input is separated by `delim` while reading into string `s`. All whitespaces are ignored. ## Exercise 3.4 : [part1](ex3_4a.cpp) | [part2](ex3_4b.cpp) ## Exercise 3.5 : [part1](ex3_5a.cpp) | [part2](ex3_5b.cpp) ## [Exercise 3.6](ex3_6.cpp) ## Exercise 3.7 >What would happen if you define the loop control variable in the previous exercise as type char? Predict the results and then change your program to use a char to see if you were right. The point here is using reference to mutate a string. If changed to something like below, `c` would become a `char` rather than `char&`. In such case, `c` is a copy of each character of string `str`, thus the assignment `c = 'X'` won't mutate `str`. As a result, after this for range statement, nothing changes. ```cpp string str("a simple string"); for (char c : str) c = 'X'; ``` ## [Exercise 3.8](ex3_8.cpp) ## Exercise 3.9 >What does the following program do? Is it valid? If not, why not? ```cpp string s; cout << s[0] << endl; ``` This code was dereferencing and printing the first item stored in `s`. Since `s` is empty, such operation is invalid, a.k.a. **undefined behavior**. ## [Exercise 3.10](ex3_10.cpp) ## Exercise 3.11 >Is the following range for legal? If so, what is the type of c? ```cpp const string s = "Keep out!"; for (auto &c : s){ /*... */ } ``` Depending on the code within for loop body. For example: cout << c; // legal. c = 'X'; // illegal. The type of `c` is `const char&`. ## Exercise 3.12 >Which, if any, of the following vector definitions are in error? For those that are legal, explain what the definition does. For those that are not legal, explain why they are illegal. ```cpp vector> ivec; // legal(c++11), vectors. vector svec = ivec; // illegal, different type. vector svec(10, "null"); // legal, vector have 10 strings: "null". ``` ## Exercise 3.13 >How many elements are there in each of the following vectors? What are the values of the elements? ```cpp vector v1; // size:0, no values. vector v2(10); // size:10, value:0 vector v3(10, 42); // size:10, value:42 vector v4{ 10 }; // size:1, value:10 vector v5{ 10, 42 }; // size:2, value:10, 42 vector v6{ 10 }; // size:10, value:"" vector v7{ 10, "hi" }; // size:10, value:"hi" ``` ## [Exercise 3.14](ex3_14.cpp) ## [Exercise 3.15](ex3_15.cpp) ## [Exercise 3.16](ex3_16.cpp) ## [Exercise 3.17](ex3_17.cpp) ## [Exercise 3.18](ex3_18.cpp) ## [Exercise 3.19](ex3_19.cpp) ## Exercise 3.20 : [part1](ex3_20a.cpp) | [part2](ex3_20b.cpp) ## [Exercise 3.21](ex3_21.cpp) ## [Exercise 3.22](ex3_22.cpp) ## [Exercise 3.23](ex3_23.cpp) ## [Exercise 3.24](ex3_24.cpp) ## [Exercise 3.25](ex3_25.cpp) ## Exercise 3.26 >In the binary search program on page 112, why did we write `mid=beg+(end-beg)/2;` instead of `mid=(beg+end) /2;`? There's no operator `+` for adding two iterators. ## Exercise 3.27 >Assuming txt_size is a function that takes no arguments and returns an int value, which of the following definitions are illegal? Explain why. ```cpp unsigned buf_size = 1024; int ia[buf_size]; // illegal, The dimension value must be a constant expression. int ia[4 * 7 - 14]; // legal int ia[txt_size()]; // illegal, The dimension value must be a constant expression. char st[11] = "fundamental"; // illegal, the string's size is 12. ``` ## Exercise 3.28 >What are the values in the following arrays? ```cpp string sa[10]; //all elements are empty strings int ia[10]; //all elements are 0 int main() { string sa2[10]; //all elements are empty strings int ia2[10]; //all elements are undefined } ``` ## Exercise 3.29: >List some of the drawbacks of using an array instead of a vector. 1. Size is fixed at compiling time. 2. No API as that of vector. 3. Bug prone. ## Exercise 3.30 >Identify the indexing errors in the following code: ```cpp constexpr size_t array_size = 10; int ia[array_size]; for (size_t ix = 1; ix <= array_size; ++ix) ia[ix] = ix; ``` When `ix` equal to 10, the expression `ia[ix]` becomes a **UB**, as it is trying to dereference an element out of range. ## [Exercise 3.31](ex3_31.cpp) ## [Exercise 3.32](ex3_32.cpp) ## Exercise 3.33 >What would happen if we did not initialize the scores array in the program on page 116? If so, values of array are undefined. Like this: ![result](https://db.tt/3T4TQoo8) ## Exercise 3.34 >Given that p1 and p2 point to elements in the same array, what does the following code do? Are there values of p1 or p2 that make this code illegal? ```cpp p1 += p2 - p1; ``` * It moves `p1` with the offset `p2 - p1`. After this statement, `p1` and `p2` points to the same address. * Any legal value p1, p2 make this code legal. ## [Exercise 3.35](ex3_35.cpp) ## [Exercise 3.36](ex3_36.cpp) ## Exercise 3.37 >What does the following program do? ```cpp const char ca[] = { 'h', 'e', 'l', 'l', 'o' }; const char *cp = ca; while (*cp) { cout << *cp << endl; ++cp; } ``` This code will print all characters in `ca`, afterwards as no `\0` appended, **UB** would happen. For most cases, the while loop here won't be terminated as expected and many rubbish would be printed out. ## Exercise 3.38 >In this section, we noted that it was not only illegal but meaningless to try to add two pointers. Why would adding two pointers be meaningless? See: - [Why can't I add pointers](http://stackoverflow.com/questions/2935038/why-cant-i-add-pointers) ## [Exercise 3.39](ex3_39.cpp) ## [Exercise 3.40](ex3_40.cpp) ## [Exercise 3.41](ex3_41.cpp) ## [Exercise 3.42](ex3_42.cpp) ## [Exercise 3.43](ex3_43.cpp) ## [Exercise 3.44](ex3_44.cpp) ## [Exercise 3.45](ex3_45.cpp) ================================================ FILE: ch03/ex3_10.cpp ================================================ //reads a string of characters including punctuation and writes what was read but with the punctuation removed. #include #include using std::string; using std::cout; using std::cin; using std::endl; int main() { cout << "Enter a string of characters including punctuation." << endl; for (string s; getline(cin, s); cout << endl) for (auto i : s) if (!ispunct(i)) cout << i; return 0; } ================================================ FILE: ch03/ex3_14.cpp ================================================ // //@Author @PEZY @Yue Wang //@Date Aug. 2014 Jun.2015 //@Brief // read a sequence of ints from cin and // store those values in a vector. // #include #include int main() { std::vector vec; for (int i; std::cin >> i; vec.push_back(i)); return 0; } ================================================ FILE: ch03/ex3_15.cpp ================================================ // //@Author @PEZY @Yue Wang //@Date Aug. 2014, Jun 2015 //@Brief // read a sequence of strings from cin and // store those values in a vector. // #include #include #include int main() { std::vector vec; for (std::string buffer; std::cin >> buffer; vec.push_back(buffer)); return 0; } ================================================ FILE: ch03/ex3_16.cpp ================================================ // //@Author PEZY //@Date Aug. 2014 //@Brief // print the size and contents of the vectors from exercise 3.13. // Check whether your answers to that exercise were correct. // If not, restudy § 3.3.1 (p. 97) until you understand why you were wrong. // #include #include #include using std::vector; using std::string; using std::cout; using std::endl; int main() { vector v1; cout << "{\n \"v1\":{\"size\":\"" << v1.size() << "\",\"value\":["; for (auto i : v1) cout << i << ","; if (!v1.empty()) cout << "\b"; cout << "]}" << endl; vector v2(10); cout << " \"v2\":{\"size\":\"" << v2.size() << "\",\"value\":["; for (auto i : v2) cout << i << ","; if (!v2.empty()) cout << "\b"; cout << "]}" << endl; vector v3(10, 42); cout << " \"v3\":{\"size\":\"" << v3.size() << "\",\"value\":["; for (auto i : v3) cout << i << ","; if (!v3.empty()) cout << "\b"; cout << "]}" << endl; vector v4{ 10 }; cout << " \"v4\":{\"size\":\"" << v4.size() << "\",\"value\":["; for (auto i : v4) cout << i << ","; if (!v4.empty()) cout << "\b"; cout << "]}" << endl; vector v5{ 10, 42 }; cout << " \"v5\":{\"size\":\"" << v5.size() << "\",\"value\":["; for (auto i : v5) cout << i << ","; if (!v5.empty()) cout << "\b"; cout << "]}" << endl; vector v6{ 10 }; cout << " \"v6\":{\"size\":\"" << v6.size() << "\",\"value\":["; for (auto i : v6) if (i.empty()) cout << "(null)" << ","; else cout << i << ","; if (!v6.empty()) cout << "\b"; cout << "]}" << endl; vector v7{ 10, "hi" }; cout << " \"v7\":{\"size\":\"" << v7.size() << "\",\"value\":["; for (auto i : v7) if (i.empty()) cout << "(null)" << ","; else cout << i << ","; if (!v7.empty()) cout << "\b"; cout << "]}\n}" << endl; return 0; } ================================================ FILE: ch03/ex3_17.cpp ================================================ // //@Author @PEZY @Yue Wang //@Date Aug. 2014 Jun.2015 //@Brief // Read a sequence of words from cin and store the values a vector. // After you've read all the words, process the vector and change each word to uppercase. // Print the transformed elements, eight words to a line. // #include #include #include using std::cin; using std::cout; using std::endl; using std::vector; using std::string; int main() { vector vec; for (string word; cin >> word; vec.push_back(word)); for (auto &str : vec) for (auto &c : str) c = toupper(c); for (string::size_type i = 0; i != vec.size(); ++i) { if (i != 0 && i % 8 == 0) cout << endl; cout << vec[i] << " "; } cout << endl; return 0; } ================================================ FILE: ch03/ex3_18.cpp ================================================ #include int main() { std::vector ivec{ 42 }; return 0; } ================================================ FILE: ch03/ex3_19.cpp ================================================ // //@Author @PEZY @Yue Wang //@Date Aug. 2014 Jun. 2015 //@Brief // List three ways to define a vector and give it ten elements, // each with the value 42. // Indicate whether there is a preferred way to do so and why. #include #include using std::vector; int main() { vector ivec1(10, 42); vector ivec2{ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; vector ivec3; for (int i = 0; i != 10; ++i) ivec3.push_back(42); std::cout << "The first approach is better!" << std::endl; return 0; } ================================================ FILE: ch03/ex3_1a.cpp ================================================ // use `using` for 1.4.1 #include using std::cout; using std::endl; int main() { int sum = 0; for (int val = 1; val <= 10; ++val) sum += val; cout << "Sum of 1 to 10 inclusive is " << sum << endl; return 0; } ================================================ FILE: ch03/ex3_1b.cpp ================================================ #include #include #include "../ch02/ex2_42.h" using std::cin; using std::cout; using std::endl; using std::cerr; int main() { Sales_data data1, data2; // code to read into data1 and data2 double price = 0; // price per book, used to calculate total revenue // read the first transactions: ISBN, number of books sold, price per book cin >> data1.bookNo >> data1.units_sold >> price; // calculate total revenue from price and units_sold data1.revenue = data1.units_sold * price; // read the second transaction cin >> data2.bookNo >> data2.units_sold >> price; data2.revenue = data2.units_sold * price; // code to check whether data1 and data2 have the same ISBN // and if so print the sum of data1 and data2 if (data1.bookNo == data2.bookNo) { unsigned totalCnt = data1.units_sold + data2.units_sold; double totalRevenue = data1.revenue + data2.revenue; // print: ISBN, total sold, total revenue, average price per book cout << data1.bookNo << " " << totalCnt << " " << totalRevenue << " "; if (totalCnt != 0) cout << totalRevenue / totalCnt << endl; else cout << "(no sales)" << endl; return 0; // indicate success } else { // transactions weren't for the same ISBN cerr << "Data must refer to the same ISBN" << endl; return -1; // indicate failure } } ================================================ FILE: ch03/ex3_20a.cpp ================================================ #include #include using std::vector; using std::cout; using std::endl; using std::cin; int main() { vector ivec; for (int i; cin >> i; ivec.push_back(i)); if (ivec.empty()) { cout << "input at least one integer." << endl; return -1; } if (ivec.size() == 1) { cout << "only one integer " << ivec[0] << ", it doesn't have any adjacent elements." << endl; return -1; } for (int i = 0; i < ivec.size() - 1; ++i) cout << ivec[i] + ivec[i + 1] << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_20b.cpp ================================================ #include #include using std::vector; using std::cout; using std::endl; using std::cin; int main() { vector ivec; for (int i; cin >> i; ivec.push_back(i)); if (ivec.empty()) { cout << "input at least one integer." << endl; return -1; } // If the vector has odd size, element in the middle will add to itself. auto size = (ivec.size() + 1) / 2; for (int i = 0; i != size; ++i) cout << ivec[i] + ivec[ivec.size() - i - 1] << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_21.cpp ================================================ // //Redo the first exercise from § 3.3.3 (p. 105) using iterators. // #include #include #include #include using std::vector; using std::string; using std::cout; using std::endl; void check_and_print(const vector& vec) { cout << "size: " << vec.size() << " content: ["; for (auto it = vec.begin(); it != vec.end(); ++it) cout << *it << (it != vec.end() - 1 ? "," : ""); cout << "]\n" << endl; } void check_and_print(const vector& vec) { cout << "size: " << vec.size() << " content: ["; for (auto it = vec.begin(); it != vec.end(); ++it) cout << *it << (it != vec.end() - 1 ? "," : ""); cout << "]\n" << endl; } int main() { vector v1; vector v2(10); vector v3(10, 42); vector v4{ 10 }; vector v5{ 10, 42 }; vector v6{ 10 }; vector v7{ 10, "hi" }; check_and_print(v1); check_and_print(v2); check_and_print(v3); check_and_print(v4); check_and_print(v5); check_and_print(v6); check_and_print(v7); return 0; } ================================================ FILE: ch03/ex3_22.cpp ================================================ // //Revise the loop that printed the first paragraph in text //to instead change the elements in text that correspond //to the first paragraph to all uppercase. //After you’ve updated text, print its contents. // #include #include #include #include using std::vector; using std::string; using std::cout; using std::cin; using std::isalpha; int main() { vector text; for (string line; getline(cin, line); text.push_back(line)); for (auto& word : text) { for (auto& ch : word) if (isalpha(ch)) ch = toupper(ch); cout << word << " "; } return 0; } ================================================ FILE: ch03/ex3_23.cpp ================================================ // //Write a program to create a vector with ten int elements. //Using an iterator, assign each element a value that is twice its current value. // Test your program by printing the vector. // #include #include #include using std::vector; using std::iterator; using std::cout; int main() { vector v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; for (auto it = v.begin(); it != v.end(); ++it) *it *= 2; for (auto i : v) cout << i << " "; return 0; } ================================================ FILE: ch03/ex3_24.cpp ================================================ #include #include using std::vector; using std::cout; using std::endl; using std::cin; int main() { vector v; for (int buffer; cin >> buffer; v.push_back(buffer)); if (v.size() < 2) { cout << " please enter at least two integers"; return -1; } for (auto it = v.cbegin(); it + 1 != v.cend(); ++it) cout << *it + *(it + 1) << " "; cout << endl; for (auto lft = v.cbegin(), rht = v.cend() - 1; lft <= rht; ++lft, --rht) cout << *lft + *rht << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_25.cpp ================================================ #include #include using std::vector; using std::cout; using std::cin; using std::endl; int main() { vector scores(11, 0); for (unsigned grade; cin >> grade;/* */) if (grade <= 100) ++*(scores.begin() + grade / 10); for (auto s : scores) cout << s << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_2a.cpp ================================================ // read the standard input a line at a time. #include #include using std::string; using std::cin; using std::cout; using std::endl; using std::getline; int main() { for (string str; getline(cin, str); cout << str << endl); return 0; } ================================================ FILE: ch03/ex3_2b.cpp ================================================ // read the standard input a word at a time #include #include using std::string; using std::cin; using std::cout; using std::endl; int main() { for (string str; cin >> str; cout << str << endl); return 0; } ================================================ FILE: ch03/ex3_31.cpp ================================================ #include using std::cout; using std::endl; int main() { int arr[10]; for (auto i = 0; i < 10; ++i) arr[i] = i; for (auto i : arr) cout << i << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_32.cpp ================================================ #include #include using std::cout; using std::endl; using std::vector; int main() { // array int arr[10]; for (int i = 0; i < 10; ++i) arr[i] = i; int arr2[10]; for (int i = 0; i < 10; ++i) arr2[i] = arr[i]; // vector vector v(10); for (int i = 0; i != 10; ++i) v[i] = arr[i]; vector v2(v); for (auto i : v2) cout << i << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_35.cpp ================================================ #include using std::cout; using std::endl; int main() { const int size = 10; int arr[size]; for (auto ptr = arr; ptr != arr + size; ++ptr) *ptr = 0; for (auto i : arr) cout << i << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_36.cpp ================================================ #include #include #include using std::begin; using std::end; using std::cout; using std::endl; using std::vector; // pb point to begin of the array, pe point to end of the array. bool compare(int* const pb1, int* const pe1, int* const pb2, int* const pe2) { if ((pe1 - pb1) != (pe2 - pb2)) // have different size. return false; else { for (int* i = pb1, *j = pb2; (i != pe1) && (j != pe2); ++i, ++j) if (*i != *j) return false; } return true; } int main() { int arr1[3] = { 0, 1, 2 }; int arr2[3] = { 0, 2, 4 }; if (compare(begin(arr1), end(arr1), begin(arr2), end(arr2))) cout << "The two arrays are equal." << endl; else cout << "The two arrays are not equal." << endl; cout << "==========" << endl; vector vec1 = { 0, 1, 2 }; vector vec2 = { 0, 1, 2 }; if (vec1 == vec2) cout << "The two vectors are equal." << endl; else cout << "The two vectors are not equal." << endl; return 0; } ================================================ FILE: ch03/ex3_39.cpp ================================================ #include #include #include using std::cout; using std::endl; using std::string; int main() { // use string. string s1("Mooophy"), s2("Pezy"); if (s1 == s2) cout << "same string." << endl; else if (s1 > s2) cout << "Mooophy > Pezy" << endl; else cout << "Mooophy < Pezy" << endl; cout << "=========" << endl; // use C-Style character strings. const char* cs1 = "Wangyue"; const char* cs2 = "Pezy"; auto result = strcmp(cs1, cs2); if (result == 0) cout << "same string." << endl; else if (result < 0) cout << "Wangyue < Pezy" << endl; else cout << "Wangyue > Pezy" << endl; return 0; } ================================================ FILE: ch03/ex3_40.cpp ================================================ // more discuss: https://github.com/Mooophy/Cpp-Primer/pull/241 // @frank67 #include #include const char cstr1[]="Hello"; const char cstr2[]="world!"; int main() { constexpr size_t new_size = strlen(cstr1) + strlen(" ") + strlen(cstr2) +1; char cstr3[new_size]; strcpy(cstr3, cstr1); strcat(cstr3, " "); strcat(cstr3, cstr2); std::cout << cstr3 << std::endl; } ================================================ FILE: ch03/ex3_41.cpp ================================================ #include #include using std::vector; using std::cout; using std::endl; using std::begin; using std::end; int main() { int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; vector v(begin(arr), end(arr)); for (auto i : v) cout << i << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_42.cpp ================================================ #include #include using std::vector; using std::cout; using std::endl; using std::begin; using std::end; int main() { vector v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr[10]; for (int i = 0; i != v.size(); ++i) arr[i] = v[i]; for (auto i : arr) cout << i << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_43.cpp ================================================ #include using std::cout; using std::endl; int main() { int arr[3][4] = { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 } }; // range for for (const int(&row)[4] : arr) for (int col : row) cout << col << " "; cout << endl; // for loop for (size_t i = 0; i != 3; ++i) for (size_t j = 0; j != 4; ++j) cout << arr[i][j] << " "; cout << endl; // using pointers. for (int(*row)[4] = arr; row != arr + 3; ++row) for (int *col = *row; col != *row + 4; ++col) cout << *col << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_44.cpp ================================================ #include using std::cout; using std::endl; int main() { int ia[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; // a range for to manage the iteration // use type alias using int_array = int[4]; for (int_array& p : ia) for (int q : p) cout << q << " "; cout << endl; // ordinary for loop using subscripts for (size_t i = 0; i != 3; ++i) for (size_t j = 0; j != 4; ++j) cout << ia[i][j] << " "; cout << endl; // using pointers. // use type alias for (int_array* p = ia; p != ia + 3; ++p) for (int *q = *p; q != *p + 4; ++q) cout << *q << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_45.cpp ================================================ #include using std::cout; using std::endl; int main() { int ia[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; // a range for to manage the iteration for (auto& p : ia) for (int q : p) cout << q << " "; cout << endl; // ordinary for loop using subscripts for (size_t i = 0; i != 3; ++i) for (size_t j = 0; j != 4; ++j) cout << ia[i][j] << " "; cout << endl; // using pointers. for (auto p = ia; p != ia + 3; ++p) for (int *q = *p; q != *p + 4; ++q) cout << *q << " "; cout << endl; return 0; } ================================================ FILE: ch03/ex3_4a.cpp ================================================ // read two strings and report whether the strings are equal // If not, report which of the two is larger. #include #include using std::string; using std::cin; using std::cout; using std::endl; int main() { string str1, str2; while (cin >> str1 >> str2) { if (str1 == str2) cout << "The two strings are equal." << endl; else cout << "The larger string is " << ((str1 > str2) ? str1 : str2); } return 0; } ================================================ FILE: ch03/ex3_4b.cpp ================================================ // read two strings and report whether the strings have the same length // If not, report which is longer #include #include using std::string; using std::cin; using std::cout; using std::endl; int main() { for (string str1, str2; cin >> str1 >> str2;/* */) { if (str1.size() == str2.size()) cout << "The two strings have the same length." << endl; else cout << "The longer string is " << ((str1.size() > str2.size()) ? str1 : str2) << endl; } return 0; } ================================================ FILE: ch03/ex3_5a.cpp ================================================ //read strings from the standard input, concatenating what is read into one large string. //Print the concatenated string. #include #include using std::string; using std::cin; using std::cout; using std::endl; int main() { string concatenated; for (string buffer; cin >> buffer; concatenated += buffer); cout << "The concatenated string is " << concatenated << endl; return 0; } ================================================ FILE: ch03/ex3_5b.cpp ================================================ //separate adjacent input strings by a space. #include #include using std::string; using std::cin; using std::cout; using std::endl; int main() { string str; for (string buff; cin >> buff; str += (str.empty() ? "" : " ") + buff); cout << "The concatenated string is " << str << endl; return 0; } ================================================ FILE: ch03/ex3_6.cpp ================================================ //Use a range for to change all the characters in a string to X. #include #include using std::string; using std::cout; using std::endl; int main() { string str("a simple string"); for (auto &c : str) c = 'X'; cout << str << endl; return 0; } ================================================ FILE: ch03/ex3_8.cpp ================================================ #include #include using std::string; using std::cout; using std::endl; int main() { string str("a simple string"); // while decltype(str.size()) i = 0; while (i < str.size()) str[i++] = 'X'; cout << str << endl; // for for (i = 0; i < str.size(); str[i++] = 'Y'); cout << str << endl; // I like range for. return 0; } ================================================ FILE: ch04/README.md ================================================ ## Exercise 4.1 >What is the value returned by 5 + 10 * 20/2? 105 ## Exercise 4.2 >Using Table 4.12 (p. 166), parenthesize the following expressions to indicate the order in which the operands are grouped: ```cpp * vec.begin() //=> *(vec.begin()) * vec.begin() + 1 //=> (*(vec.begin())) + 1 ``` ## Exercise 4.3 >Order of evaluation for most of the binary operators is left undefined to give the compiler opportunities for optimization. This strategy presents a trade-off between efficient code generation and potential pitfalls in the use of the language by the programmer. Do you consider that an acceptable trade-off? Why or why not? No. IMHO, such design cannot improve performance siginificantly, but it can make bugs very tricky to handle. ## Exercise 4.4 >Parenthesize the following expression to show how it is evaluated. Test your answer by compiling the expression (without parentheses) and printing its result. ```cpp 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2 // parenthesize ((12/3)*4) + (5*15) + ((24%4)/2) // 16 + 75 + 0 = 91 // print: 91 ``` ## Exercise 4.5 >Determine the result of the following expressions. ```cpp -30 * 3 + 21 / 5 // -90+4 = -86 -30 + 3 * 21 / 5 // -30+63/5 = -30+12 = -18 30 / 3 * 21 % 5 // 10*21%5 = 210%5 = 0 -30 / 3 * 21 % 4 // -10*21%4 = -210%4 = -2 ``` ## Exercise 4.6 >Write an expression to determine whether an int value is even or odd. ```cpp i & 0x1 ``` ## Exercise 4.7 >What does overflow mean? Show three expressions that will overflow. from the book: >Some arithmetic expressions yield undefined results. Some of these undefined espressions are due to the nature of mathematics-for example, division by zero. Others are undefined due to the nature of computers-for example, due to overflow. Overflow happens when a value is computed that is outside the range of values that the type can represent. ```cpp short svalue = 32767; ++svalue; // -32768 unsigned uivalue = 0; --uivalue; // 4294967295 unsigned short usvalue = 65535; ++usvalue; // 0 ``` ## Exercise 4.8 >Explain when operands are evaluated in the logical `AND`, logical `OR`, and equality operators. from the book: > The logical `AND` and `OR` operators always evaluate their left operand before the right. Moreover, the right operand is evaluated if and only if the left operand does not determine the result. This strategy is known as **short-circuit evaluation**. - logical `AND` : the second operand is evaluated if and only if the left side is `true`. - logical `OR` : the second operand is evaluated if and only if the left side is `false` - equality operators `==` : undefined. ## Exercise 4.9 >Explain the behavior of the condition in the following if: ```cpp const char *cp = "Hello World"; if (cp && *cp) ``` cp is a pointer to `const char *`, and it's not a nullptr. true. `*cp` is a const char: 'H', and it is explicit a nonzero value. true. true && true -> true. ## Exercise 4.10 >Write the condition for a while loop that would read ints from the standard input and stop when the value read is equal to 42. ```cpp int i = 0; while(cin >> i && i != 42) ``` ## Exercise 4.11 >Write an expression that tests four values, a, b, c, and d, and ensures that a is greater than b, which is greater than c, which is greater than d. ```cpp a>b && b>c && c>d ``` ## Exercise 4.12 >Assuming `i`, `j`, and `k` are all ints, explain what `i != j < k` means. `i != j < k` is equivalent to `i != (j < k)`. ## Exercise 4.13 >What are the values of i and d after each assignment? ```cpp int i; double d; d = i = 3.5; // i = 3, d = 3.0 i = d = 3.5; // d = 3.5, i = 3 ``` ## Exercise 4.14 >Explain what happens in each of the if tests: ```cpp if (42 = i) // compile error: expression is not assignable if (i = 42) // true. ``` ## Exercise 4.15 >The following assignment is illegal. Why? How would you correct it? ```cpp double dval; int ival; int *pi; dval = ival = pi = 0; // pi is a pointer to int. // can not assign to 'int' from type 'int *' // correct it: dval = ival = 0; pi = 0; ``` ## Exercise 4.16 >Although the following are legal, they probably do not behave as the programmer expects. Why? Rewrite the expressions as you think they should be. ```cpp if (p = getPtr() != 0) if (i = 1024) // why? always true. use an assigment as a condition. // correct it if ((p=getPtr()) != 0) if (i == 1024) ``` ## Exercise 4.17 >Explain the difference between prefix and postfix increment. See: [What is the difference between ++i and i++](http://stackoverflow.com/questions/24853/what-is-the-difference-between-i-and-i) ## Exercise 4.18 >What would happen if the while loop on page 148 that prints the elements from a vector used the prefix increment operator? It will print from the second element and dereference `v.end()` at last, which is a **UB**. ## Exercise 4.19 >Given that ptr points to an int, that vec is a vector, and that ival is an int, explain the behavior of each of these expressions. Which, if any, are likely to be incorrect? Why? How might each be corrected? ```cpp ptr != 0 && *ptr++ // check ptr is not a nullptr, and then check the pointer value. ival++ && ival // check ival, and then check ival+1 whether equal zero. vec[ival++] <= vec[ival] // incorrect. It is an **undefined behavior.** // correct: vec[ival] <= vec[ival+1] ``` See [order of evaluation](http://en.cppreference.com/w/cpp/language/eval_order). ## Exercise 4.20 >Assuming that iter is a vector::iterator, indicate which, if any, of the following expressions are legal. Explain the behavior of the legal expressions and why those that aren’t legal are in error. ```cpp *iter++; // return *iter, then ++iter. (*iter)++; // illegal, *iter is a string, cannot increment the value. *iter.empty() // illegal, iter should use '->' to indicate whether empty. iter->empty(); // indicate the iter' value whether empty. ++*iter; // illegal, string have not increment. iter++->empty(); // return iter->empty(), then ++iter. ``` ## [Exercise 4.21](ex4_21.cpp) ## [Exercise 4.22](ex4_22.cpp) ## Exercise 4.23 >The following expression fails to compile due to operator precedence. Using Table 4.12 (p. 166), explain why it fails. How would you fix it? ```cpp string s = "word"; string pl = s + s[s.size() - 1] == 's' ? "" : "s" ; ``` Operator Precedence: `?:` < `+` Fix it: ```cpp string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ; ``` ## Exercise 4.24 >Our program that distinguished between high pass, pass, and fail depended on the fact that the conditional operator is right associative. Describe how that operator would be evaluated if the operator were left associative. if the operator were left associative. ```cpp finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass"; ``` would same as : ```cpp finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass"; ``` if `grade > 90`, first conditional operator's result is `high pass`. so the finalgrade is always fail. It's contradictory obviously. ## Exercise 4.25 >What is the value of ~'q' << 6 on a machine with 32-bit ints and 8 bit chars, that uses Latin-1 character set in which 'q' has the bit pattern 01110001? The final value in decimal is `-7296`. ## Exercise 4.26 >In our grading example in this section, what would happen if we used unsigned int as the type for quiz1? The C++ standard does not specify the size of integral types in bytes, but it specifies minimum ranges they must be able to hold. The minimum range of `unsigned int` is 0 to 65535. Since some implementations use only the minimum 16 bits for `unsigned int`, this could cause undefined behavior. ## Exercise 4.27 >What is the result of each of these expressions? ```cpp unsigned long ul1 = 3, ul2 = 7; ul1 & ul2 // == 3 ul1 | ul2 // == 7 ul1 && ul2 // == true ul1 || ul2 // == true ``` ## [Exercise 4.28](ex4_28.cpp) ## Exercise 4.29 >Predict the output of the following code and explain your reasoning. Now run the program. Is the output what you expected? If not, figure out why. ```cpp int x[10]; int *p = x; cout << sizeof(p)/sizeof(*p) << endl; cout << sizeof(x)/sizeof(*x) << endl; ``` * The first is 10. It returns the number of elements in x. * The second result is undefined. ----- reference: [Why the size of a pointer is 4bytes in C++](http://stackoverflow.com/a/2428809) ## Exercise 4.30 >Using Table 4.12 (p. 166), parenthesize the following expressions to match the default evaluation: ```cpp sizeof x + y // (sizeof x)+y . "sizeof" has higher precedence than binary `+`. sizeof p->mem[i] // sizeof(p->mem[i]) sizeof a < b // sizeof(a) < b sizeof f() //If `f()` returns `void`, this statement is undefined, otherwise it returns the size of return type. ``` ----- reference: [sizeof operator](http://en.cppreference.com/w/cpp/language/sizeof) ## Exercise 4.31 >The program in this section used the prefix increment and decrement operators. Explain why we used prefix and not postfix. What changes would have to be made to use the postfix versions? Rewrite the program using postfix operators. ~~postfix will copy itself as return, then increment or decrement. prefix will increment or decrement first, and return itself. so prefix is more effective in this program.(reduce one copy space.)~~ We use prefix and not postfix, just because of the `Advice: Use Postfix Operators only When Necessary` on `§4.5. Increment and Decrement Operators`. >**Advice: Use Postfix Operators only When Necessary** >Readers from a C background might be surprised that we use the prefix increment in the programs we've written. The reason is simple: The prefix version avoids unnecessary work. It increments the value and returns the incremented version.The postfix operator must store the original value so that it can return the unincremented value as its result. If we don’t need the unincremented value, there’s no need for the extra work done by the postfix operator. >For ints and pointers, the compiler can optimize away this extra work. For more complicated iterator types, this extra work potentially might be more costly. By habitually using the prefix versions, we do not have to worry about whether the performance difference matters. Moreover—and perhaps more importantly—we can express the intent of our programs more directly. So, it's just a good habits. And there are no changes if we have to be made to use the postfix versions. Rewrite: ```cpp for(vector::size_type ix = 0; ix != ivec.size(); ix++, cnt--) ivec[ix] = cnt; ``` This is not an appropriate example to discuss the difference of prefix and postfix. Look at the section `Built-in comma operator` on [this page](http://en.cppreference.com/w/cpp/language/operator_other). ----- reference: [Usage of the Built-in Comma Operator](http://stackoverflow.com/questions/22591387/usage-of-the-built-in-comma-operator) ## Exercise 4.32 >Explain the following loop. ```cpp constexpr int size = 5; int ia[size] = { 1, 2, 3, 4, 5 }; for (int *ptr = ia, ix = 0; ix != size && ptr != ia+size; ++ix, ++ptr) { /* ... */ } ``` `ptr` and `ix` have the same function. The former use a pointer, and the latter use the index of array. we use the loop to through the array.(just choose one from `ptr` and `ix`) ## Exercise 4.33 >Using Table 4.12 (p. 166) explain what the following expression does: ```cpp someValue ? ++x, ++y : --x, --y ``` Because of the most lowest precedence of the comma operator, the expression is same as: ```cpp (someValue ? ++x, ++y : --x), --y ``` If someValue is true, then `++x`, and the result is `y`, if someValue is false, then `--x`, and the result is `--y`. so it is also same as: ```cpp someValue ? (++x, y) : (--x, --y); ``` Even though the result has nothing to do with `x`, the evaluation of `someValue` does effect the operation on `x`. ## Exercise 4.34 >Given the variable definitions in this section, explain what conversions take place in the following expressions: (a) if (fval) (b) dval = fval + ival; (c) dval + ival * cval; Remember that you may need to consider the associativity of the operators. ```cpp if (fval) // fval converted to bool dval = fval + ival; // ival converted to fval, then the result of fval add ival converted to double. dval + ival * cval; // cval converted to int, then that int and ival converted to double. ``` ## Exercise 4.35 >Given the following definitions, ```cpp char cval; int ival; unsigned int ui; float fval; double dval; ``` identify the implicit type conversions, if any, taking place: ```cpp cval = 'a' + 3; // 'a' promoted to int, then the result of ('a' + 3)(int) converted to char. fval = ui - ival * 1.0; // ival converted to double , ui also converted to double. then that double converted(by truncation) to float. dval = ui * fval; // ui promoted to float. then that float converted to double. cval = ival + fval + dval; // ival converted to float, then that float and fval converted to double. At last, that double converted to char(by truncation). ``` ## Exercise 4.36 >Assuming i is an int and d is a double write the expression i *= d so that it does integral, rather than floating-point, multiplication. ```cpp i *= static_cast(d); ``` ## Exercise 4.37 >Rewrite each of the following old-style casts to use a named cast: ```cpp int i; double d; const string *ps; char *pc; void *pv; pv = (void*)ps; // pv = const_cast(ps); or pv = static_cast(const_cast(ps)); i = int(*pc); // i = static_cast(*pc); pv = &d; // pv = static_cast(&d); pc = (char*)pv; // pc = static_cast(pv); ``` ## Exercise 4.38 >Explain the following expression: ```cpp double slope = static_cast(j/i); ``` j/i is an int(by truncation), then converted to double and assigned to slope. ================================================ FILE: ch04/ex4_21.cpp ================================================ #include #include using std::cout; using std::endl; using std::vector; int main() { vector ivec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; for (auto i : ivec) { cout << ((i & 0x1) ? i * 2 : i) << " "; } cout << endl; return 0; } ================================================ FILE: ch04/ex4_22.cpp ================================================ #include using std::cout; using std::cin; using std::endl; int main() { for (unsigned g; cin >> g; ) { // conditional operators auto result = g > 90 ? "high pass" : g < 60 ? "fail" : g < 75 ? "low pass" : "pass"; cout << result << endl; // if statements if (g > 90) cout << "high pass"; else if (g < 60) cout << "fail"; else if (g < 75) cout << "low pass"; else cout << "pass"; cout << endl; } return 0; } ================================================ FILE: ch04/ex4_28.cpp ================================================ #include // high level input/output operations. int main() { // by using method below only include what is needed. using std::cout; using std::endl; // void type cout << "void: nullptr_t\t" << sizeof(std::nullptr_t) << " bytes" << endl << endl; // boolean type cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl; // character type cout << "char:\t\t" << sizeof(char) << " bytes" << endl; cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl; cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl; cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl; // integers type cout << "short:\t\t" << sizeof(short) << " bytes" << endl; cout << "int:\t\t" << sizeof(int) << " bytes" << endl; cout << "long:\t\t" << sizeof(long) << " bytes" << endl; cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl; // floating point type cout << "float:\t\t" << sizeof(float) << " bytes" << endl; cout << "double:\t\t" << sizeof(double) << " bytes" << endl; cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl; // Fixed width integers cout << "int8_t:\t\t" << sizeof(int8_t) << " bytes" << endl; cout << "uint8_t:\t" << sizeof(uint8_t) << " bytes" << endl; cout << "int16_t:\t" << sizeof(int16_t) << " bytes" << endl; cout << "uint16_t:\t" << sizeof(uint16_t) << " bytes" << endl; cout << "int32_t:\t" << sizeof(int32_t) << " bytes" << endl; cout << "uint32_t:\t" << sizeof(uint32_t) << " bytes" << endl; cout << "int64_t:\t" << sizeof(int64_t) << " bytes" << endl; cout << "uint64_t:\t" << sizeof(uint64_t) << " bytes" << endl; return 0; } ================================================ FILE: ch05/README.md ================================================ ## Exercise 5.1 >What is a null statement? When might you use a null statement? A null statement is the empty statement. like this: ```cpp ; // null statement ``` The null statement may be used as a placeholder when a statement is expected. For example: ```cpp // read until we hit end-of-file or find an input equal to sought while (cin >> s && s != sought) ; // null statement. ``` ## Exercise 5.2 >What is a block? When might you might use a block? A block is a (possibly empty) sequence of statements and declarations surrounded by a pair of curly braces.It's used when multiple statements are needed.For example: ```cpp while (val <= 10) { sum += val; ++val; } ``` ## Exercise 5.3 >Use the comma operator (§ 4.10, p. 157) to rewrite the while loop from § 1.4.1 (p. 11) so that it no longer requires a block. Explain whether this rewrite improves or diminishes the readability of this code. ```cpp #include int main() { int sum = 0, val = 1; while (val <= 10) sum += val, ++val; std::cout << "Sum of 1 to 10 inclusive is " << sum << std::endl; return 0; } ``` This rewritten version diminishes the readability. ## Exercise 5.4 >Explain each of the following examples, and correct any problems you detect. - (a) while (string::iterator iter != s.end()) { /* . . . */ } - (b) while (bool status = find(word)) { /* . . . */ } if (!status) { /* . . . */ } (a) Illegal declaration : `string::iterator iter != s.end()` ```cpp //corrrected as: std::string::iterator iter = s.begin(); while (iter != s.end()) { /* . . . */ } ``` (b) Variable `status` is only declared inside scope of while condition. ```cpp //corrrected as: bool status; while (status = find(word)) { /* ... */ } if (!status) { /* ... */ } ``` ## [Exercise 5.5](ex5_5.cpp) ## [Exercise 5.6](ex5_6.cpp) ## Exercise 5.7 >Correct the errors in each of the following code fragments: ```cpp (a) if (ival1 != ival2) ival1 = ival2 else ival1 = ival2 = 0; (b) if (ival < minval) minval = ival; occurs = 1; (c) if (int ival = get_value()) cout << "ival = " << ival << endl; if (!ival) cout << "ival = 0\n"; (d) if (ival = 0) ival = get_value(); ``` ```cpp (a) if (ival1 != ival2) ival1 = ival2; // Need to add semicolon. else ival1 = ival2 = 0; (b) if (ival < minval) // Braces needed to include both satetments in scope. { minval = ival; occurs = 1; } (c) if (int ival = get_value()) //Second if statement should be else-if. cout << "ival = " << ival << endl; else if (!ival) cout << "ival = 0\n"; (d) if (ival == 0) //Expression changed from assignment to "equal to"; ival = get_value(); ``` ## Exercise 5.8 >What is a “dangling else”? How are else clauses resolved in C++? Colloquial term used to refer to the problem of how to process nested if statements in which there are more ifs than elses. In C++, an else is always paired with the closest preceding unmatched if. ## [Exercise 5.9](ex5_9.cpp) ## [Exercise 5.10](ex5_10.cpp) ## [Exercise 5.11](ex5_11.cpp) ## [Exercise 5.12](ex5_12.cpp) ## Exercise 5.13 >Each of the programs in the highlighted text on page 184 contains a common programming error. Identify and correct each error. ```cpp (a) unsigned aCnt = 0, eCnt = 0, iouCnt = 0; char ch = next_text(); switch (ch) { case 'a': aCnt++; case 'e': eCnt++; default: iouCnt++; } (b) unsigned index = some_value(); switch (index) { case 1: int ix = get_value(); ivec[ ix ] = index; break; default: ix = ivec.size()-1; ivec[ ix ] = index; } (c) unsigned evenCnt = 0, oddCnt = 0; int digit = get_num() % 10; switch (digit) { case 1, 3, 5, 7, 9: oddcnt++; break; case 2, 4, 6, 8, 10: evencnt++; break; } (d) unsigned ival=512, jval=1024, kval=4096; unsigned bufsize; unsigned swt = get_bufCnt(); switch(swt) { case ival: bufsize = ival * sizeof(int); break; case jval: bufsize = jval * sizeof(int); break; case kval: bufsize = kval * sizeof(int); break; } ``` ```cpp (a) // Error: should have a break statement unsigned aCnt = 0, eCnt = 0, iouCnt = 0; char ch = next_text(); switch (ch) { case 'a': aCnt++; break; case 'e': eCnt++; break; default : iouCnt++; break; } (b) // Error: control bypass an explicitly initialized variable ix. unsigned index = some_value(); int ix; switch (index) { case 1: ix = get_value(); ivec[ ix ] = index; break; default: ix = static_cast(ivec.size())-1; ivec[ ix ] = index; } (c) // Error: case label syntax error unsigned evenCnt = 0, oddCnt = 0; int digit = get_num() % 10; switch (digit) { case 1: case 3: case 5: case 7: case 9: oddcnt++; // oddcnt != oddCnt break; case 2: case 4: case 6: case 8: case 0: evencnt++; // evencnt != evenCnt break; } (d) // Error: case label must be a constant expression const unsigned ival=512, jval=1024, kval=4096; unsigned bufsize; unsigned swt = get_bufCnt(); switch(swt) { case ival: bufsize = ival * sizeof(int); break; case jval: bufsize = jval * sizeof(int); break; case kval: bufsize = kval * sizeof(int); break; } ``` ## Exercise 5.14 >Write a program to read strings from standard input looking for duplicated words. The program should find places in the input where one word is followed immediately by itself. Keep track of the largest number of times a single repetition occurs and which word is repeated. Print the maximum number of duplicates, or else print a message saying that no word was repeated. For example, if the input is ```sh how now now now brown cow cow ``` the output should indicate that the word now occurred three times. - [concise solution](ex5_14.cpp) ## Exercise 5.15 >Explain each of the following loops. Correct any problems you detect. ```cpp (a) for (int ix = 0; ix != sz; ++ix) { /* ... */ } if (ix != sz) // . . . (b) int ix; for (ix != sz; ++ix) { /* ... */ } (c) for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ } ``` ```cpp (a) int ix; for (ix = 0; ix != sz; ++ix) { /* ... */ } if (ix != sz) // . . . (b) int ix; for (; ix != sz; ++ix) { /* ... */ } (c) for (int ix = 0; ix != sz; ++ix) { /*...*/ } ``` ## Exercise 5.16 >The while loop is particularly good at executing while some condition holds; for example, when we need to read values until end-of-file. The for loop is generally thought of as a **step loop**: An index steps through a range of values in a collection. Write an idiomatic use of each loop and then rewrite each using the other loop construct. If you could use only one loop, which would you choose? Why? ```cpp // while idiomatic int i; while ( cin >> i ) // ... // same as for for (int i = 0; cin >> i;) // ... // for idiomatic for (int i = 0; i != size; ++i) // ... // same as while int i = 0; while (i != size) { // ... ++i; } ``` I prefer `for` to `while` in such cases, because it's terse. More importantly, object i won't pollute the external scope after it goes out of the loop. It's a little bit easier to add new code into the external scope, since it reduces the possibility of naming conflicts .That is, a higher maintainability. Of course, this way makes the code a bit harder to read. ([@Mooophy](https://github.com/Mooophy)) ## [Exercise 5.17](ex5_17.cpp) ## Exercise 5.18 >Explain each of the following loops. Correct any problems you detect. ```cpp (a) do { // added bracket. int v1, v2; cout << "Please enter two numbers to sum:" ; if (cin >> v1 >> v2) cout << "Sum is: " << v1 + v2 << endl; }while (cin); (b) int ival; do { // . . . } while (ival = get_response()); // should not declared in this scope. (c) int ival = get_response(); do { ival = get_response(); } while (ival); // ival is not declared in this scope. ``` ## [Exercise 5.19](ex5_19.cpp) ## [Exercise 5.20](ex5_20.cpp) ## [Exercise 5.21](ex5_21.cpp) ## Exercise 5.22 >The last example in this section that jumped back to begin could be better written using a loop. Rewrite the code to eliminate the goto. ```cpp // backward jump over an initialized variable definition is okay begin: int sz = get_size(); if (sz <= 0) { goto begin; } ``` use `for` to replace `goto`: ```cpp for (int sz = get_size(); sz <=0; sz = get_size()) ; // should not remove. ``` ## [Exercise 5.23](ex5_23.cpp) ## [Exercise 5.24](ex5_24.cpp) ## [Exercise 5.25](ex5_25.cpp) ================================================ FILE: ch05/ex5_10.cpp ================================================ #include using std::cin; using std::cout; using std::endl; int main() { unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0; char ch; while (cin >> ch) switch (ch) { case 'a': case 'A': ++aCnt; break; case 'e': case 'E': ++eCnt; break; case 'i': case 'I': ++iCnt; break; case 'o': case 'O': ++oCnt; break; case 'u': case 'U': ++uCnt; break; } cout << "Number of vowel a(A): \t" << aCnt << '\n' << "Number of vowel e(E): \t" << eCnt << '\n' << "Number of vowel i(I): \t" << iCnt << '\n' << "Number of vowel o(O): \t" << oCnt << '\n' << "Number of vowel u(U): \t" << uCnt << endl; return 0; } ================================================ FILE: ch05/ex5_11.cpp ================================================ #include using std::cin; using std::cout; using std::endl; int main() { unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0; char ch; while (cin >> std::noskipws >> ch) switch (ch) { case 'a': case 'A': ++aCnt; break; case 'e': case 'E': ++eCnt; break; case 'i': case 'I': ++iCnt; break; case 'o': case 'O': ++oCnt; break; case 'u': case 'U': ++uCnt; break; case ' ': ++spaceCnt; break; case '\t': ++tabCnt; break; case '\n': ++newLineCnt; break; } cout << "Number of vowel a(A): \t" << aCnt << '\n' << "Number of vowel e(E): \t" << eCnt << '\n' << "Number of vowel i(I): \t" << iCnt << '\n' << "Number of vowel o(O): \t" << oCnt << '\n' << "Number of vowel u(U): \t" << uCnt << '\n' << "Number of space: \t" << spaceCnt << '\n' << "Number of tab char: \t" << tabCnt << '\n' << "Number of new line: \t" << newLineCnt << endl; return 0; } ================================================ FILE: ch05/ex5_12.cpp ================================================ #include using std::cin; using std::cout; using std::endl; int main() { unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0, ffCnt = 0, flCnt = 0, fiCnt = 0; char ch, prech = '\0'; while (cin >> std::noskipws >> ch) { switch (ch) { case 'a': case 'A': ++aCnt; break; case 'e': case 'E': ++eCnt; break; case 'i': if (prech == 'f') ++fiCnt; case 'I': ++iCnt; break; case 'o': case 'O': ++oCnt; break; case 'u': case 'U': ++uCnt; break; case ' ': ++spaceCnt; break; case '\t': ++tabCnt; break; case '\n': ++newLineCnt; break; case 'f': if (prech == 'f') ++ffCnt; break; case 'l': if (prech == 'f') ++flCnt; break; } prech = ch; } cout << "Number of vowel a(A): \t" << aCnt << '\n' << "Number of vowel e(E): \t" << eCnt << '\n' << "Number of vowel i(I): \t" << iCnt << '\n' << "Number of vowel o(O): \t" << oCnt << '\n' << "Number of vowel u(U): \t" << uCnt << '\n' << "Number of space: \t" << spaceCnt << '\n' << "Number of tab char: \t" << tabCnt << '\n' << "Number of new line: \t" << newLineCnt << '\n' << "Number of ff: \t" << ffCnt << '\n' << "Number of fl: \t" << flCnt << '\n' << "Number of fi: \t" << fiCnt << endl; return 0; } ================================================ FILE: ch05/ex5_14.cpp ================================================ #include #include using std::cout; using std::cin; using std::endl; using std::string; using std::pair; int main() { pair max_duplicated; int count = 0; for (string str, prestr; cin >> str; prestr = str) { if (str == prestr) ++count; else count = 0; if (count > max_duplicated.second) max_duplicated = { prestr, count }; } if (max_duplicated.first.empty()) cout << "There's no duplicated string." << endl; else cout << "the word " << max_duplicated.first << " occurred " << max_duplicated.second + 1 << " times. " << endl; return 0; } ================================================ FILE: ch05/ex5_17.cpp ================================================ #include #include using std::cout; using std::vector; bool is_prefix(vector const& lhs, vector const& rhs) { if(lhs.size() > rhs.size()) return is_prefix(rhs, lhs); for(unsigned i = 0; i != lhs.size(); ++i) if(lhs[i] != rhs[i]) return false; return true; } int main() { vector l{ 0, 1, 1, 2 }; vector r{ 0, 1, 1, 2, 3, 5, 8 }; cout << (is_prefix(r, l) ? "yes\n" : "no\n"); return 0; } ================================================ FILE: ch05/ex5_19.cpp ================================================ #include #include using std::cout; using std::cin; using std::endl; using std::string; int main() { string rsp; do { cout << "Input two strings: "; string str1, str2; cin >> str1 >> str2; cout << (str1 <= str2 ? str1 : str2) << " is less than the other. " << "\n\n" << "More? Enter yes or no: "; cin >> rsp; } while (!rsp.empty() && tolower(rsp[0]) == 'y'); return 0; } ================================================ FILE: ch05/ex5_20.cpp ================================================ #include #include using std::cout; using std::cin; using std::endl; using std::string; int main() { string read, tmp; while (cin >> read) if (read == tmp) break; else tmp = read; if (cin.eof()) cout << "no word was repeated." << endl; else cout << read << " occurs twice in succession." << endl; return 0; } ================================================ FILE: ch05/ex5_21.cpp ================================================ // @Brief Revise the program from the exercise in 5.5.1(p. 191) // so that it looks only for duplicated words that start with an uppercase letter. // @See Exercise 5.20 // @frank67 Rewrite using the 'continue' statement. See #250 #include using std::cin; using std::cout; using std::endl; #include using std::string; int main() { string curr, prev; bool no_twice = true; while (cin >> curr) { if (isupper(curr[0]) && prev == curr) { cout << curr << ": occurs twice in succession." << endl; no_twice = false; break; } prev = curr; } if (no_twice) cout << "no word was repeated." << endl; return 0; } ================================================ FILE: ch05/ex5_23.cpp ================================================ #include using std::cin; using std::cout; using std::endl; int main() { int i, j; cin >> i >> j; cout << i / j << endl; return 0; } ================================================ FILE: ch05/ex5_24.cpp ================================================ #include #include // // When a zero entered, the code below would crash with feedback as : // "Unhandled exception at 0x75834598 in just_for_cpp.exe: Microsoft C++ exception: std::runtime_error at memory location 0x0054F9F4." // // Tested on Windows 8.1 + Vs 2013 // int main(void) { int i, j; std::cin >> i >> j; if (j == 0) throw std::runtime_error("divisor is 0"); std::cout << i / j << std::endl; return 0; } ================================================ FILE: ch05/ex5_25.cpp ================================================ #include #include using std::cin; using std::cout; using std::endl; using std::runtime_error; int main(void) { for (int i, j; cout << "Input two integers:\n", cin >> i >> j; ) { try { if (j == 0) throw runtime_error("divisor is 0"); cout << i / j << endl; } catch (runtime_error err) { cout << err.what() << "\nTry again? Enter y or n" << endl; char c; cin >> c; if (!cin || c == 'n') break; } } return 0; } ================================================ FILE: ch05/ex5_5.cpp ================================================ #include #include #include using std::vector; using std::string; using std::cout; using std::endl; using std::cin; int main() { vector scores = { "F", "D", "C", "B", "A", "A++" }; for (int g; cin >> g;) { string letter; if (g < 60) { letter = scores[0]; } else { letter = scores[(g - 50) / 10]; if (g != 100) letter += g % 10 > 7 ? "+" : g % 10 < 3 ? "-" : ""; } cout << letter << endl; } return 0; } ================================================ FILE: ch05/ex5_6.cpp ================================================ #include #include #include using std::vector; using std::string; using std::cout; using std::endl; using std::cin; int main() { vector scores = { "F", "D", "C", "B", "A", "A++" }; int grade = 0; while (cin >> grade) { string lettergrade = grade < 60 ? scores[0] : scores[(grade - 50) / 10]; lettergrade += (grade == 100 || grade < 60) ? "" : (grade % 10 > 7) ? "+" : (grade % 10 < 3) ? "-" : ""; cout << lettergrade << endl; } return 0; } ================================================ FILE: ch05/ex5_9.cpp ================================================ #include using std::cout; using std::endl; using std::cin; int main() { unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0; char ch; while (cin >> ch) { if (ch == 'a') ++aCnt; else if (ch == 'e') ++eCnt; else if (ch == 'i') ++iCnt; else if (ch == 'o') ++oCnt; else if (ch == 'u') ++uCnt; } cout << "Number of vowel a: \t" << aCnt << '\n' << "Number of vowel e: \t" << eCnt << '\n' << "Number of vowel i: \t" << iCnt << '\n' << "Number of vowel o: \t" << oCnt << '\n' << "Number of vowel u: \t" << uCnt << endl; return 0; } ================================================ FILE: ch06/Chapter6.h ================================================ int fact(int val); int func(); template T abs(T i) { return i >= 0 ? i : -i; } ================================================ FILE: ch06/README.md ================================================ ## Exercise 6.1 **Parameters**: Local variable declared inside the function parameter list. they are initialized by the **arguments** provided in the each function call. **Arguments**: Values supplied in a function call that are used to initialize the function's **parameters**. ## Exercise 6.2 ```cpp (a) string f() { // return should be string, not int string s; // ... return s; } (b) void f2(int i) { /* ... */ } // function needs return type (c) int calc(int v1, int v2) { /* ... */ } // parameter list cannot use same name twice (d) double square (double x) { return x * x; } // function body needs braces ``` ## Exercise 6.3 ```cpp #include int fact(int i) { if(i<0) { runtime_error err("Input cannot be a negative number"); cout << err.what() << endl; } return i > 1 ? i * fact( i - 1 ) : 1; } int main() { std::cout << std::boolalpha << (120 == fact(5)) << std::endl; return 0; } ``` ## Exercise 6.4 ```cpp #include #include int fact(int i) { return i > 1 ? i * fact(i - 1) : 1; } void interactive_fact() { std::string const prompt = "Enter a number within [1, 13) :\n"; std::string const out_of_range = "Out of range, please try again.\n"; for (int i; std::cout << prompt, std::cin >> i; ) { if (i < 1 || i > 12) { std::cout << out_of_range; continue; } std::cout << fact(i) << std::endl; } } int main() { interactive_fact(); return 0; } ``` ## Exercise 6.5 ```cpp #include int abs(int i) { return i > 0 ? i : -i; } int main() { std::cout << abs(-5) << std::endl; return 0; } ``` ## Exercise 6.6 **local variable**: Variables defined inside a **block**; **parameter**: **Local variables** declared inside the **function parameter list** **local static variable**: **local static variable(object)** is initialized before the first time execution passes through the object’s definition.**Local statics** are not destroyed when a function ends; they are **destroyed when the program terminates.** ```cpp // example size_t count_add(int n) // n is a parameter. { static size_t ctr = 0; // ctr is a static variable. ctr += n; return ctr; } int main() { for (size_t i = 0; i != 10; ++i) // i is a local variable. cout << count_add(i) << endl; return 0; } ``` ## Exercise 6.7 ```cpp size_t generate() { static size_t ctr = 0; return ctr++; } ``` ## [Exercise 6.8](Chapter6.h) ## Exercise 6.9 [fact.cc](fact.cc) | [factMain.cc](factMain.cc) ## [Exercise 6.10](ex6_10.cpp) ## [Exercise 6.11](ex6_11.cpp) ## [Exercise 6.12](ex6_12.cpp) ## Exercise 6.13 `void f(T)` pass the argument by value. **nothing the function does to the parameter can affect the argument**. `void f(T&)` pass a reference, will be **bound to** whatever T object we pass. ## Exercise 6.14 a parameter should be a reference type: ```cpp void reset(int &i) { i = 0; } ``` a parameter should not be a reference: ```cpp void print(std::vector::iterator begin, std::vector::iterator end) { for (std::vector::iterator iter = begin; iter != end; ++iter) std::cout << *iter << std::endl; } ``` ## Exercise 6.15 >why is `s` a reference to const but `occurs` is a plain reference? Because `s` should not be changed by this function, but `occurs` result must be calculated by the function. >Why are these parameters references, but the char parameter `c` is not? Because `c` may be a temp varable, such as `find_char(s, 'a', occurs)` >What would happen if we made `s` a plain reference? What if we made `occurs` a reference to const? `s` could be changed in the function, and `occurs` would not be changed. so `occurs = 0;` is an error. ## Exercise 6.16 ```cpp bool is_empty(const string& s) { return s.empty(); } ``` Since this function doesn't change the argument, "const" shoud be added before string&s, otherwise this function is misleading and can't be used with const string or in a const function. ## [Exercise 6.17](ex6_17.cpp) Not the same. For the first one "const" was used, since no change need to do for the argument. For the second function, "const" can't be used, because the content of the agument should be changed. ## Exercise 6.18 (a) ```cpp bool compare(const matrix &m1, cosnt matrix &m2); ``` (b) ```cpp vector::iterator change_val(int num, vector::iterator &it); ``` ## Exercise 6.19 (a) illegal, only one parameter. (b) legal. (c) legal. (d) legal. ## Exercise 6.20 If we can use `const`, just use it. If we make a parameter a plain reference when it could be a reference to `const`, the reference value maybe changed. ## [Exercise 6.21](ex6_21.cpp) ## [Exercise 6.22](ex6_22.cpp) ## [Exercise 6.23](ex6_23.cpp) ## Exercise 6.24 >Arrays have two special properties that affect how we define and use functions that operate on arrays: We cannot copy an array, and when we use an array it is (usually) **converted to a pointer**. So we cannot pass an array by value, and when we pass an array to a function, we are actually passing a pointer to the array's first element. In this question, `const int ia[10]` is actually same as `const int*`, and the size of the array is **irrelevant**. we can pass `const int ia[3]` or `const int ia[255]`, there are no differences. If we want to pass an array which size is ten, we should use reference like that: ```cpp void print10(const int (&ia)[10]) { /*...*/ } ``` see more discusses at http://stackoverflow.com/questions/26530659/confused-about-array-parameters ## [Exercise 6.25 && Exercise 6.26](ex6_25_26.cpp) ## [Exercise 6.27](ex6_27.cpp) ## Exercise 6.28 The type of `elem` in the `for` loop is `const std::string&`. ## Exercise 6.29 Depends on the type of elements of `initializer_list`. When the type is [PODType](http://en.cppreference.com/w/cpp/concept/PODType), reference is unnecessary. Because `POD` is **cheap to copy**(such as `int`). Otherwise, Using reference(`const`) is the better choice. ## Exercise 6.30 Error (Clang): >Non-void function 'str_subrange' should return a value. // error #1 >Control may reach end of non-void function. // error #2 ## Exercise 6.31 when you can find the preexisting object that the reference refered. ## Exercise 6.32 legal, it gave the values (0 ~ 9) to array `ia`. ## [Exercise 6.33](ex6_33.cpp) ## Exercise 6.34 When the recursion termination condition becomes `var != 0`, two situations can happen : * case 1 : If the argument is positive, recursion stops at 0.(Note : There is one extra multiplication step though as the combined expression for factorial(5) reads 5 * 4 * 3 * 2 * 1 * 1. In terms of programming languages learning, such subtle difference probably looks quite trivial. In algorithms analysis and proof, however, this extra step may be super important.) * case 2 : if the argument is negative, recursion would never stop. As a result, a stack overflow would occur. ## Exercise 6.35 the recursive function will always use `val` as the parameter. *a recursion loop* would happen. ## Exercise 6.36 ```cpp string (&func())[10] ``` ## Exercise 6.37 ```cpp // type alias using ArrT = string[10]; ArrT& func1(); // trailing return auto func2() -> string(&)[10]; // decltype string arrS[10]; decltype(arrS)& func3(); ``` I pefer the first one. because it is more simpler to me. ## Exercise 6.38 ```cpp decltype(arrStr)& arrPtr(int i) { return (i % 2) ? odd : even; } ``` ## Exercise 6.39 (a) legal, repeated declarations(without definition) are legal in C++ (b) illegal, only the return type is different (c) legal, the parameter type is different and return type is changed ## Exercise 6.40 (a) no error (b) Missing default argument on parameter 'wd', 'bckgrnd'. ## Exercise 6.41 (a) illegal. No matching function for call to 'init'. (b) legal, and match. (c) legal, but not match. `wd` whould be setting to '*'. ## [Exercise 6.42](ex6_42.cpp) ## Exercise 6.43 Both two should put in a header. (a) is an inline function. (b) is the declaration of useful function. we always put them in the header. ## [Exercise 6.44](ex6_44.cpp) ## Exercise 6.45 For example, the function `arrPtr` in [Exercise 6.38](# Exercise-638) and `make_plural` in [Exercise 6.42](# Exercise-642) should be defined as `inline`. But the function `func` in [Exercise 6.4](# Exercise-64) shouldn't. It is not that small and it's only being called once. Hence, it will probably not expand as inline. ## Exercise 6.46 > Would it be possible to define `isShorter` as a `constexpr`? If so, do so. If not, explain why not. No. Because `std::string::size()` is not a `constexpr` function and `s1.size() == s2.size()` is not a constant expression. > **For a** non-template, non-defaulted **constexpr function** or a non-template, non-defaulted, non-inheriting constexpr constructor, **if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed;** no diagnostic required. (N3690 §7.1.5 [dcl.constexpr]/5) ## [Exercise 6.47](ex6_47.cpp) ## Exercise 6.48 This loop let user input a word all the way until the word is sought. It isn't a good use of assert. because if user begin to input a word, the `cin` would be always have content. so the `assert` would be always `true`. It is meaningless. using `assert(s == sought)` is better. ## Exercise 6.49 candidate function: >Set of functions that are considered when resolving a function call. (all the functions with the name used in the call for which a declaration is in scope at the time of the call.) viable function: >Subset of the candidate functions that could match a given call. >It have the same number of parameters as arguments to the call, and each argument type can be converted to the corresponding parameter type. ## Exercise 6.50 (a) illegal. 2.56 match the `double`, but 42 match the `int`. (b) match `void f(int)`. (c) match `void f(int, int)`. (d) match `void f(double, double = 3.14)`. ## [Exercise 6.51](ex6_51.cpp) ## Exercise 6.52 (a) Match through a promotion (b) Arithmetic type conversion ## Exercise 6.53 (a) ```cpp int calc(int&, int&); // calls lookup(int&) int calc(const int&, const int&); // calls lookup(const int&) ``` (b) ```cpp int calc(char*, char*); // calls lookup(char*) int calc(const char*, const char*); // calls lookup(const char *) ``` (c) illegal. both calls lookup(char*) ## Exercise 6.54 ```cpp int func(int a, int b); using pFunc1 = decltype(func) *; typedef decltype(func) *pFunc2; using pFunc3 = int (*)(int a, int b); using pFunc4 = int(int a, int b); typedef int(*pFunc5)(int a, int b); using pFunc6 = decltype(func); std::vector vec1; std::vector vec2; std::vector vec3; std::vector vec4; std::vector vec5; std::vector vec6; ``` ## Exercise 6.55 ```cpp int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } int multiply(int a, int b) { return a * b; } int divide(int a, int b) { return b != 0 ? a / b : 0; } vec1.push_back(add); vec1.push_back(subtract); vec1.push_back(multiply); vec1.push_back(divide); ``` ## Exercise 6.56 ```cpp std::vector vec{ add, subtract, multiply, divide }; for (auto f : vec) std::cout << f(2, 2) << std::endl; ``` ---- see @Mooophy 's [complete codes](ex6_54_55_56.cpp). ================================================ FILE: ch06/ex6_10.cpp ================================================ //@Yue wang // // Exercise 6.10: // Using pointers, write a function to swap the values of two ints. // Test the function by calling it and printing the swapped values. // #include #include #include void swap(int* lhs, int* rhs) { int tmp; tmp = *lhs; *lhs = *rhs; *rhs = tmp; } int main() { for (int lft, rht; std::cout << "Please Enter:\n", std::cin >> lft >> rht; ) { swap(&lft, &rht); std::cout << lft << " " << rht << std::endl; } return 0; } ================================================ FILE: ch06/ex6_11.cpp ================================================ #include void reset(int &i) { i = 0; } int main() { int i = 42; reset(i); std::cout << i << std::endl; return 0; } ================================================ FILE: ch06/ex6_12.cpp ================================================ //@Yue Wang // // Exercise 6.12: // Rewrite the program from exercise 6.10 in § 6.2.1 (p. 210) to use // references instead of pointers to swap the value of two ints. Which // version do you think would be easier to use and why? // The version using reference is easier. // #include #include void swap(int& lhs, int& rhs) { int temp = lhs; lhs = rhs; rhs = temp; } int main() { for (int left, right; std::cout << "Please Enter:\n", std::cin >> left >> right; ) { swap(left, right); std::cout << left << " " << right << std::endl; } return 0; } ================================================ FILE: ch06/ex6_17.cpp ================================================ #include #include using std::cout; using std::endl; using std::string; bool any_capital(string const& str) { for (auto ch : str) if (isupper(ch)) return true; return false; } void to_lowercase(string& str) { for (auto& ch : str) ch = tolower(ch); } int main() { string hello("Hello World!"); cout << any_capital(hello) << endl; to_lowercase(hello); cout << hello << endl; return 0; } ================================================ FILE: ch06/ex6_21.cpp ================================================ // @Yue Wang // // Exercise 6.21: // Write a function that takes an int and a pointer to an int and // returns the larger of the int value or the value to which the // pointer points. What type should you use for the pointer? // #include using std::cout; int larger_one(const int i, const int *const p) { return (i > *p) ? i : *p; } int main() { int i = 6; cout << larger_one(7, &i); return 0; } ================================================ FILE: ch06/ex6_22.cpp ================================================ // @Yue Wang // // Exercise 6.22: // Write a function to swap two int pointers. // #include #include void swap(int*& lft, int*& rht) { auto tmp = lft; lft = rht; rht = tmp; } int main() { int i = 42, j = 99; auto lft = &i; auto rht = &j; swap(lft, rht); std::cout << *lft << " " << *rht << std::endl; return 0; } ================================================ FILE: ch06/ex6_23.cpp ================================================ #include using std::cout; using std::endl; using std::begin; using std::end; void print(const int *pi) { if(pi) cout << *pi << endl; } void print(const char *p) { if (p) while (*p) cout << *p++; cout << endl; } void print(const int *beg, const int *end) { while (beg != end) cout << *beg++ << endl; } void print(const int ia[], size_t size) { for (size_t i = 0; i != size; ++i) { cout << ia[i] << endl; } } void print(int (&arr)[2]) { for (auto i : arr) cout << i << endl; } int main() { int i = 0, j[2] = { 0, 1 }; char ch[5] = "pezy"; print(ch); print(begin(j), end(j)); print(&i); print(j, end(j)-begin(j)); print(j); return 0; } ================================================ FILE: ch06/ex6_25_26.cpp ================================================ // @Yue Wang // // Exercise 6.25: Write a main function that takes two arguments. // Concatenate the supplied arguments and print the resulting string. // // Exercise 6.26: Write a program that accepts the options presented // in this section. Print the values of the arguments passed to main. // #include #include int main(int argc, char **argv) { std::string str; for (int i = 1; i != argc; ++i) str += std::string(argv[i]) + " "; std::cout << str << std::endl; return 0; } ================================================ FILE: ch06/ex6_27.cpp ================================================ #include #include int sum(std::initializer_list const& il) { int sum = 0; for (auto i : il) sum += i; return sum; } int main(void) { auto il = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; std::cout << sum(il) << std::endl; return 0; } ================================================ FILE: ch06/ex6_33.cpp ================================================ #include #include using std::vector; using std::cout; using Iter = vector::const_iterator; void print(Iter first, Iter last) { if (first != last) { cout << *first << " "; print(++first, last); } } int main() { vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; print(vec.cbegin(), vec.cend()); return 0; } ================================================ FILE: ch06/ex6_42.cpp ================================================ // @creator by Wang Yue // @refactor by pezy // // @date 27, July. 2015 // // @question // Give the second parameter of make_plural (§ 6.3.2, p. 224) a default // argument of 's'. Test your program by printing singular and plural versions // of the words success and failure. // #include #include using std::string; using std::cout; using std::endl; string make_plural(size_t ctr, const string& word, const string& ending = "s") { return (ctr > 1) ? word + ending : word; } int main() { cout << "singual: " << make_plural(1, "success", "es") << " " << make_plural(1, "failure") << endl; cout << "plural : " << make_plural(2, "success", "es") << " " << make_plural(2, "failure") << endl; return 0; } ================================================ FILE: ch06/ex6_44.cpp ================================================ // @Yue Wang // // Exercise 6.44: Rewrite the isShorter function from § 6.2.2 (p. 211) to be inline. // #include #include using std::string; using std::cout; using std::endl; inline bool is_shorter(const string &lft, const string &rht) // defining in the header is better. { return lft.size() < rht.size(); } int main() { cout << is_shorter("pezy", "mooophy") << endl; return 0; } ================================================ FILE: ch06/ex6_47.cpp ================================================ // // main.cpp // Test // // Created by pezy on 14/10/30. // // To turn off debugging, uncomment the following line, or compile the program with '-D NDEBUG' switch //#define NDEBUG #include #include using std::vector; using std::cout; using std::endl; void printVec(vector &vec) { #ifndef NDEBUG cout << "vector size: " << vec.size() << endl; #endif if (!vec.empty()) { auto tmp = vec.back(); vec.pop_back(); printVec(vec); cout << tmp << " "; } } int main() { vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; printVec(vec); cout << endl; return 0; } ================================================ FILE: ch06/ex6_51.cpp ================================================ #include using std::cout; using std::endl; void f() { cout << "f()" << endl; } void f(int) { cout << "f(int)" << endl; } void f(int, int) { cout << "f(int, int)" << endl; } void f(double, double) { cout << "f(double, double)" << endl; } int main() { //f(2.56, 42); // error: 'f' is ambiguous. f(42); f(42, 0); f(2.56, 3.14); return 0; } ================================================ FILE: ch06/ex6_54_55_56.cpp ================================================ // @Yue Wang // // Exercise 6.54: // Write a declaration for a function that takes two int // parameters and returns an int, and declare a vector whose // elements have this function pointer type. // // Exercise 6.55: // Write four functions that add, subtract, multiply, and divide // two int values. Store pointers to these functions in your // vector from the previous exercise. // // Exercise 6.56: // Call each element in the vector and print their result. // #include #include #include using std::vector; using std::cout; // // @brief Exercise 6.54 // @note define the function type fp // inline int f(const int, const int); typedef decltype(f) fp;//fp is just a function type not a function pointer // // @brief Exercise 6.55 // @note Store pointers to these functions in the vector // inline int NumAdd(const int n1, const int n2) { return n1 + n2; } inline int NumSub(const int n1, const int n2) { return n1 - n2; } inline int NumMul(const int n1, const int n2) { return n1 * n2; } inline int NumDiv(const int n1, const int n2) { return n1 / n2; } vector v{ NumAdd, NumSub, NumMul, NumDiv }; int main() { // // @brief Exercise 6.56 // @note Call each element in the vector and print their result. // for (auto it = v.cbegin(); it != v.cend(); ++it) cout << (*it)(2, 2) << std::endl; return 0; } ================================================ FILE: ch06/fact.cc ================================================ #include "Chapter6.h" #include int fact(int val) { if (val == 0 || val == 1) return 1; else return val * fact(val-1); } int func() { int n, ret = 1; std::cout << "input a number: "; std::cin >> n; while (n > 1) ret *= n--; return ret; } ================================================ FILE: ch06/factMain.cc ================================================ #include "Chapter6.h" #include int main() { std::cout << "5! is " << fact(5) << std::endl; std::cout << func() << std::endl; std::cout << abs(-9.78) << std::endl; } ================================================ FILE: ch07/README.md ================================================ ## [Exercise 7.1](ex7_01.cpp) ## [Exercise 7.2](ex7_02.h) ## [Exercise 7.3](ex7_03.cpp) ## [Exercise 7.4](ex7_04.h) ## [Exercise 7.5](ex7_05.h) ## [Exercise 7.6](ex7_06.h) ## [Exercise 7.7](ex7_07.cpp) ## Exercise 7.8 Define `read`'s Sales_data parameter as plain reference since it's intended to change the `revenue`'s value. Define `print`'s Sales_data parameter as a reference to const since it isn't intended to change any member's value of this object. ## [Exercise 7.9](ex7_09.h) ## Exercise 7.10 ```cpp if(read(read(cin, data1), data2)) ``` we can try to divide it like that: ``` std::istream &firstStep = read(cin, data1); sdt::istream &secondStep = read(firstStep, data2); if (secondStep) ``` the condition of the `if` statement would read two Sales_data object at one time. ## Exercise 7.11 [Header](ex7_11.h)|[CPP](ex7_11.cpp) ## [Exercise 7.12](ex7_12.h) ## [Exercise 7.13](ex7_13.cpp) ## Exercise 7.14 ```cpp Sales_data() : bookNo(""), units_sold(0) , revenue(0){ } ``` ## [Exercise 7.15](ex7_15.h) ## Exercise 7.16 There are no restrictions on how often an access specifier may appear.The specified access level remains in effect until the next access specifier or the end of the class body. The members which are accessible to all parts of the program should define after a public specifier. The members which are accessible to the member functions of the class but are not accessible to code that uses the class should define after a private specifier. ## Exercise 7.17 The only difference between using `class` and using `struct` to define a class is the default access level. (`class` : private, `struct` : public) ## Exercise 7.18 encapsulation is the separation of implementation from interface. It hides the implementation details of a type. (In C++, encapsulation is enforced by putting the implementation in the private part of a class) ----- Important advantages: - User code cannot inadvertently corrupt the state of an encapsulation object. - The implementation of an encapsulated class can change over time without requiring changes in user-level code. ## Exercise 7.19 public include: constructors, `getName()`, `getAddress()`. private include: `name`, `address`. the interface should be defined as public, the data shouldn't expose to outside of the class. ## Exercise 7.20 `friend` is a mechanism by which a class grants access to its nonpublic members. They have the same rights as members. **Pros**: - the useful functions can refer to class members in the class scope without needing to explicitly prefix them with the class name. - you can access all the nonpublic members conveniently. - sometimes, more readable to the users of class. **Cons**: - lessens encapsulation and therefore maintainability. - code verbosity, declarations inside the class, outside the class. ## [Exercise 7.21](ex7_21.h) ## [Exercise 7.22](ex7_22.h) ## [Exercise 7.23](ex7_23.h) ## [Exercise 7.24](ex7_24.h) ## Exercise 7.25 The class below can rely on it. It goes in *Section 7.1.5*: >..the synthesized versions are unlikely to work correctly for classes that allocate resources that reside outside the class objects themselves. >Moreover, the synthesized versions for copy, assignment, and destruction work correctly for classes that have **vector or string members**. Hence the class below which used only built-in type and strings can rely on the default version of copy and assignment. (by @Mooophy) ## Exercise 7.26 [Header](ex7_26.h)|[CPP](ex7_26.cpp) ## Exercise 7.27 [Class](ex7_27.h)|[Test](ex7_27_TEST.cpp) ## Exercise 7.28 The second call to `display` couldn't print `#` among the output, cause the call to `set` would change the **temporary copy**, not myScreen. ## Exercise 7.29 ```sh #with '&' XXXXXXXXXXXXXXXXXXXX#XXXX XXXXXXXXXXXXXXXXXXXX#XXXX ^^^ # without '&' XXXXXXXXXXXXXXXXXXXX#XXXX XXXXXXXXXXXXXXXXXXXXXXXXX ^^^ ``` ## Exercise 7.30 **Pros** - more explicit - less scope for misreading - can use the member function parameter which name is same as the member name. void setAddr(const std::string &addr) { this->addr = addr; } **Cons** - more to read - sometimes redundant std::string getAddr() const { return this->addr; } // unnecessary ## [Exercise 7.31](ex7_31.h) ## [Exercise 7.32](ex7_32.h) ## Exercise 7.33 [clang]error: unknown type name 'pos' fixed: ```cpp Screen::pos Screen::size() const { return height*width; } ``` ## Exercise 7.34 There is an error in dummy_fcn(pos height) ^ Unknown type name 'pos' ## Exercise 7.35 ```cpp typedef string Type; Type initVal(); // use `string` class Exercise { public: typedef double Type; Type setVal(Type); // use `double` Type initVal(); // use `double` private: int val; }; Type Exercise::setVal(Type parm) { // first is `string`, second is `double` val = parm + initVal(); // Exercise::initVal() return val; } ``` **fixed** changed ```cpp Type Exercise::setVal(Type parm) { val = parm + initVal(); return val; } ``` to ```cpp Exercise::Type Exercise::setVal(Type parm) { val = parm + initVal(); return val; } ``` and `Exercise::initVal()` should be defined. ## Exercise 7.36 >In this case, the constructor initializer makes it appear as if `base` is initialized with `i` and then `base` is used to initialize `rem`. However, `rem` is initialized first. The effect of this initializer is to initialize `rem` with the undefined value of `base`! **fixed** ```cpp struct X { X (int i, int j): base(i), rem(i % j) { } int base, rem; }; ``` ## Exercise 7.37 ```cpp Sales_data first_item(cin); // use Sales_data(std::istream &is) ; its value are up to your input. int main() { Sales_data next; // use Sales_data(std::string s = ""); bookNo = "", cnt = 0, revenue = 0.0 Sales_data last("9-999-99999-9"); // use Sales_data(std::string s = ""); bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0 } ``` ## Exercise 7.38 ```cpp Sales_data(std::istream &is = std::cin) { read(is, *this); } ``` ## Exercise 7.39 illegal. cause the call of overloaded 'Sales_data()' is **ambiguous**. ## Exercise 7.40 ```cpp #include #include class Book { public: Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate) :isbn_(isbn), name_(name), author_(author), pubdate_(pubdate) { } explicit Book(std::istream &in) { in >> isbn_ >> name_ >> author_ >> pubdate_; } private: unsigned isbn_; std::string name_; std::string author_; std::string pubdate_; }; ``` ## Exercise 7.41 [Header](ex7_41.h)|[Cpp](ex7_41.cpp)|[Test](ex7_41_TEST.cpp) ## Exercise 7.42 ```cpp #include #include class Book { public: Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate) :isbn_(isbn), name_(name), author_(author), pubdate_(pubdate) { } explicit Book(std::istream &in) { in >> isbn_ >> name_ >> author_ >> pubdate_; } private: unsigned isbn_; std::string name_; std::string author_; std::string pubdate_; }; ``` ## [Exercise 7.43](ex7_43.cpp) ## Exercise 7.44 illegal, cause there are ten elements, each would be default initialized. But no default initializer for the temporary object. ## Exercise 7.45 No problem. cause `C` have the default constructor. ## Exercise 7.46 - a) A class must provide at least one constructor. (**untrue**, "The compiler-generated constructor is known as the synthesized default constructor.") - b) A default constructor is a constructor with an empty parameter list. (**untrue**, A default constructor is a constructor that is used if no initializer is supplied.What's more, A constructor that supplies default arguments for all its parameters also defines the default constructor) - c) If there are no meaningful default values for a class, the class should not provide a default constructor. (**untrue**, the class should provide.) - d) If a class does not define a default constructor, the compiler generates one that initializes each data member to the default value of its associated type. (**untrue**, only if our class does not explicitly define **any constructors**, the compiler will implicitly define the default constructor for us.) ## Exercise 7.47 Whether the conversion of a `string` to `Sales_data` is desired **depends on how we think our users will use the conversion**. In this case, it might be okay. The `string` in null_book probably represents a nonexistent ISBN. Benefits: - prevent the use of a constructor in a context that requires an implicit conversion - we can define a constructor which is used only with the direct form of initialization Drawbacks: - meaningful only on constructors that can be called with a single argument ## Exercise 7.48 Both are nothing happened. ## Exercise 7.49 ```cpp (a) Sales_data &combine(Sales_data); // ok (b) Sales_data &combine(Sales_data&); // [Error] no matching function for call to 'Sales_data::combine(std::string&)' (`std::string&` can not convert to `Sales_data` type.) (c) Sales_data &combine(const Sales_data&) const; // The trailing const mark can't be put here, as it forbids any mutation on data members. This conflicts with combine's semantics. ``` Some detailed explanation about problem (b) :It's wrong. Because `combine`’s parameter is a non-const reference , we can't pass a temporary to that parameter. If `combine`’s parameter is a reference to const , we can pass a temporary to that parameter. Like this :`Sales_data &combine(const Sales_data&); ` Here we call the `Sales_data` `combine` member function with a string argument. This call is perfectly legal; the compiler automatically creates a `Sales_data` object from the given string. That newly generated (temporary) `Sales_data` is passed to `combine`.(Also you can read C++ Primer Page 295(English Edition)) ## [Exercise 7.50](ex7_50.h) ## Exercise 7.51 Such as a function like that: ```cpp int getSize(const std::vector&); ``` if vector has not defined its single-argument constructor as explicit. we can use the function like: ```cpp getSize(34); ``` What is this mean? It's very confused. But the `std::string` is different. In ordinary, we use `std::string` to replace `const char *`(the C language). so when we call a function like that: ```cpp void setYourName(std::string); // declaration. setYourName("pezy"); // just fine. ``` it is very natural. ## Exercise 7.52 In my opinion , the aim of the problem is Aggregate Class. Test-makers think that `Sales_data` is Aggregate Class, so `Sales_data` should have no in-class initializers if we want to initialize the data members of an aggregate class by providing a braced list of member initializers: FIXED: ```cpp struct Sales_data { std::string bookNo; unsigned units_sold; double revenue; }; ``` ## [Exercise 7.53](ex7_53.h) ## Exercise 7.54 in C++11, constexpr member functions are implicitly const, so the "set_xx" functions, which will modify data members, cannot be declared as constexpr. In C++14, this property no longer holds, so constexpr is suitable. ## Exercise 7.55 no. `std::string` is not a literal type, and it can be verified by following codes: ```cpp #include #include #include struct Data { int ival; std::string s; }; int main() { std::cout << std::boolalpha; std::cout << std::is_literal_type::value << std::endl; // output: false } ``` ## Exercise 7.56 >What is a static class member? A class member that is **associated with the class**, rather than with individual objects of the class type. >What are the advantages of static members? each object can no need to store a common data. And if the data is changed, each object can use the new value. >How do they differ from ordinary members? - a static data member can have **incomplete type**. - we can use a static member **as a default argument**. ## [Exercise 7.57](ex7_57.h) ## Exercise 7.58 ```cpp static double rate = 6.5; ^ rate should be a constant expression. static vector vec(vecSize); ^ we may not specify an in-class initializer inside parentheses. ``` Fixed: ```cpp // example.h class Example { public: static constexpr double rate = 6.5; static const int vecSize = 20; static vector vec; }; // example.C #include "example.h" constexpr double Example::rate; vector Example::vec(Example::vecSize); ``` ================================================ FILE: ch07/ex7_01.cpp ================================================ // // ex7_01.cpp // Exercise 7.1 // // Created by pezy on 14/10/30. // #include #include using std::cin; using std::cout; using std::endl; using std::string; struct Sales_data { string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sales_data total; if (cin >> total.bookNo >> total.units_sold >> total.revenue) { Sales_data trans; while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) { if (total.bookNo == trans.bookNo) { total.units_sold += trans.units_sold; total.revenue += trans.revenue; } else { cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl; total = trans; } } cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0; } ================================================ FILE: ch07/ex7_02.h ================================================ // // ex7_02.h // Exercise 7.2 // // Created by pezy on 14/11/8. // #ifndef CP5_ex7_02_h #define CP5_ex7_02_h #include struct Sales_data { std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } #endif ================================================ FILE: ch07/ex7_03.cpp ================================================ // // ex7_03.cpp // Exercise 7.03 // // Created by pezy on 14/11/8. // #include "ex7_02.h" #include using std::cin; using std::cout; using std::endl; int main() { Sales_data total; if (cin >> total.bookNo >> total.units_sold >> total.revenue) { Sales_data trans; while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) { if (total.isbn() == trans.isbn()) total.combine(trans); else { cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl; total = trans; } } cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0; } ================================================ FILE: ch07/ex7_04.h ================================================ // // ex7_04.h // Exercise 7.4 // // Created by pezy on 14/11/8. // #ifndef CP5_ex7_04_h #define CP5_ex7_04_h #include class Person { std::string name; std::string address; }; #endif ================================================ FILE: ch07/ex7_05.h ================================================ // // ex7_05.h // Exercise 7.5 // // Created by pezy on 14/11/8. // // Revised by @Yue Wang Jun 2015: // // 1. add public access modifier for the function members // 2. use trailing return type // 3. change return type from string to string const&, for better performance. // #ifndef CP5_ex7_05_h #define CP5_ex7_05_h #include class Person { std::string name; std::string address; public: auto get_name() const -> std::string const& { return name; } auto get_addr() const -> std::string const& { return address; } }; #endif ================================================ FILE: ch07/ex7_06.h ================================================ // // ex7_06.h // Exercise 7.6 // // Created by pezy on 11/8/14. // #ifndef CP5_ex7_06_h #define CP5_ex7_06_h #include #include struct Sales_data { std::string const& isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; // member functions. Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // nonmember functions std::istream &read(std::istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream &print(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } #endif ================================================ FILE: ch07/ex7_07.cpp ================================================ // // ex7_07.cpp // Exercise 7.7 // // Created by pezy on 11/8/14. // #include "ex7_06.h" int main() { Sales_data total; if (read(std::cin, total)) { Sales_data trans; while (read(std::cin, trans)) { if (total.isbn() == trans.isbn()) total.combine(trans); else { print(std::cout, total) << std::endl; total = trans; } } print(std::cout, total) << std::endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0; } ================================================ FILE: ch07/ex7_09.h ================================================ // // ex7_09.h // Exercise 7.9 // // Created by pezy on 11/8/14. // // // Revied by @Yue Wang Jun 2015 // #ifndef CP5_ex7_09_h #define CP5_ex7_09_h #include #include struct Person { std::string const& getName() const { return name; } std::string const& getAddress() const { return address; } std::string name; std::string address; }; std::istream &read(std::istream &is, Person &person) { return is >> person.name >> person.address; } std::ostream &print(std::ostream &os, const Person &person) { return os << person.name << " " << person.address; } #endif ================================================ FILE: ch07/ex7_11.cpp ================================================ // // ex7_11.cpp // Exercise 7.11 // // Created by pezy on 11/9/14. // #include "ex7_11.h" int main() { Sales_data item1; print(std::cout, item1) << std::endl; Sales_data item2("0-201-78345-X"); print(std::cout, item2) << std::endl; Sales_data item3("0-201-78345-X", 3, 20.00); print(std::cout, item3) << std::endl; Sales_data item4(std::cin); print(std::cout, item4) << std::endl; return 0; } ================================================ FILE: ch07/ex7_11.h ================================================ // // ex7_11.h // Exercise 7.11 // // Created by pezy on 11/9/14. // #ifndef CP5_ex7_11_h #define CP5_ex7_11_h #include #include struct Sales_data { Sales_data() = default; Sales_data(const std::string &s):bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is); std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; // nonmember functions std::istream &read(std::istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream &print(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } // member functions. Sales_data::Sales_data(std::istream &is) { read(is, *this); } Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } #endif ================================================ FILE: ch07/ex7_12.h ================================================ // // ex7_12.h // Exercise 7.12 // // Created by pezy on 11/9/14. // #ifndef CP5_ex7_12_h #define CP5_ex7_12_h #include #include struct Sales_data; std::istream &read(std::istream&, Sales_data&); struct Sales_data { Sales_data() = default; Sales_data(const std::string &s):bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is) { read(is, *this); } std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; // member functions. Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // nonmember functions std::istream &read(std::istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream &print(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } #endif ================================================ FILE: ch07/ex7_13.cpp ================================================ // // ex7_13.cpp // Exercise 7.13 // // Created by pezy on 11/9/14. // #include "ex7_12.h" int main() { Sales_data total(std::cin); if (!total.isbn().empty()) { std::istream &is = std::cin; while (is) { Sales_data trans(is); if (!is) break; if (total.isbn() == trans.isbn()) total.combine(trans); else { print(std::cout, total) << std::endl; total = trans; } } print(std::cout, total) << std::endl; } else { std::cerr << "No data?!" << std::endl; return -1; } return 0; } ================================================ FILE: ch07/ex7_15.h ================================================ // // ex7_15.h // Exercise 7.15 // // Created by pezy on 11/9/14. // #ifndef CP5_ex7_15_h #define CP5_ex7_15_h #include #include struct Person; std::istream &read(std::istream&, Person&); struct Person { Person() = default; Person(const std::string sname, const std::string saddr):name(sname), address(saddr){ } Person(std::istream &is){ read(is, *this); } std::string getName() const { return name; } std::string getAddress() const { return address; } std::string name; std::string address; }; std::istream &read(std::istream &is, Person &person) { is >> person.name >> person.address; return is; } std::ostream &print(std::ostream &os, const Person &person) { os << person.name << " " << person.address; return os; } #endif ================================================ FILE: ch07/ex7_21.h ================================================ // // ex7_21.h // Exercise 7.21 // // Created by pezy on 11/13/14. // #ifndef CP5_ex7_21_h #define CP5_ex7_21_h #include #include class Sales_data { friend std::istream &read(std::istream &is, Sales_data &item); friend std::ostream &print(std::ostream &os, const Sales_data &item); friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs); public: Sales_data() = default; Sales_data(const std::string &s):bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is) { read(is, *this); } std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; // member functions. Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // friend functions std::istream &read(std::istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream &print(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } #endif ================================================ FILE: ch07/ex7_22.h ================================================ // // ex7_22.h // Exercise 7.22 // // Created by pezy on 11/13/14. // #ifndef CP5_ex7_22_h #define CP5_ex7_22_h #include #include class Person { friend std::istream &read(std::istream &is, Person &person); friend std::ostream &print(std::ostream &os, const Person &person); public: Person() = default; Person(const std::string sname, const std::string saddr):name(sname), address(saddr){ } Person(std::istream &is){ read(is, *this); } std::string getName() const { return name; } std::string getAddress() const { return address; } private: std::string name; std::string address; }; std::istream &read(std::istream &is, Person &person) { is >> person.name >> person.address; return is; } std::ostream &print(std::ostream &os, const Person &person) { os << person.name << " " << person.address; return os; } #endif ================================================ FILE: ch07/ex7_23.h ================================================ // // ex7_23.h // Exercise 7.23 // // Created by pezy on 11/14/14. // #ifndef CP5_ex7_23_h #define CP5_ex7_23_h #include class Screen { public: using pos = std::string::size_type; Screen() = default; Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } char get() const { return contents[cursor]; } char get(pos r, pos c) const { return contents[r*width+c]; } private: pos cursor = 0; pos height = 0, width = 0; std::string contents; }; #endif ================================================ FILE: ch07/ex7_24.h ================================================ // // ex7_24.cpp // Exercise 7.24 // // Created by pezy on 11/14/14. // #ifndef CP5_ex7_24_h #define CP5_ex7_24_h #include class Screen { public: using pos = std::string::size_type; Screen() = default; // 1 Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2 Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3 char get() const { return contents[cursor]; } char get(pos r, pos c) const { return contents[r*width+c]; } private: pos cursor = 0; pos height = 0, width = 0; std::string contents; }; #endif ================================================ FILE: ch07/ex7_26.cpp ================================================ // // ex7_26.cpp // Exercise 7.26 // // Created by pezy on 11/9/14. // // @Brief implementation of class Sales_data // @See ex7_26.h #include "ex7_26.h" // member functions. Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // friend functions std::istream &read(std::istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream &print(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } ================================================ FILE: ch07/ex7_26.h ================================================ // // ex7_26.h // Exercise 7.26 // // Created by pezy on 11/14/14. // // @See ex7_21.h // @Add inline member function "Sales_data::avg_pric" #ifndef CP5_ex7_26_h #define CP5_ex7_26_h #include #include class Sales_data { friend std::istream &read(std::istream &is, Sales_data &item); friend std::ostream &print(std::ostream &os, const Sales_data &item); friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs); public: Sales_data() = default; Sales_data(const std::string &s):bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data(std::istream &is) { read(is, *this); } std::string isbn() const { return bookNo; }; Sales_data& combine(const Sales_data&); private: inline double avg_price() const; private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; inline double Sales_data::avg_price() const { return units_sold ? revenue/units_sold : 0; } // declarations for nonmember parts of the Sales_data interface. std::istream &read(std::istream &is, Sales_data &item); std::ostream &print(std::ostream &os, const Sales_data &item); Sales_data add(const Sales_data &lhs, const Sales_data &rhs); #endif ================================================ FILE: ch07/ex7_27.h ================================================ // // ex7_27.h // Exercise 7.27 // // Created by pezy on 11/14/14. // #ifndef CP5_ex7_27_h #define CP5_ex7_27_h #include #include class Screen { public: using pos = std::string::size_type; Screen() = default; // 1 Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2 Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3 char get() const { return contents[cursor]; } char get(pos r, pos c) const { return contents[r*width+c]; } inline Screen& move(pos r, pos c); inline Screen& set(char c); inline Screen& set(pos r, pos c, char ch); const Screen& display(std::ostream &os) const { do_display(os); return *this; } Screen& display(std::ostream &os) { do_display(os); return *this; } private: void do_display(std::ostream &os) const { os << contents; } private: pos cursor = 0; pos height = 0, width = 0; std::string contents; }; inline Screen& Screen::move(pos r, pos c) { cursor = r*width + c; return *this; } inline Screen& Screen::set(char c) { contents[cursor] = c; return *this; } inline Screen& Screen::set(pos r, pos c, char ch) { contents[r*width+c] = ch; return *this; } #endif ================================================ FILE: ch07/ex7_27_TEST.cpp ================================================ // // ex7_27_TEST.cpp // Test of Exercise 7.27 // // Created by pezy on 11/14/14. // #include "ex7_27.h" int main() { Screen myScreen(5, 5, 'X'); myScreen.move(4, 0).set('#').display(std::cout); std::cout << "\n"; myScreen.display(std::cout); std::cout << "\n"; return 0; } ================================================ FILE: ch07/ex7_31.h ================================================ // // ex7_31.h // Exercise 7.31 // // Created by pezy on 11/17/14. // #ifndef CP5_ex7_31_h #define CP5_ex7_31_h class Y; class X { Y* y = nullptr; }; class Y { X x; }; #endif ================================================ FILE: ch07/ex7_32.h ================================================ // // ex7_32.h // Exercise 7.32 // // Created by pezy on 11/18/14. // // @See ex7_27.h #ifndef CP5_ex7_32_h #define CP5_ex7_32_h #include #include #include class Screen; class Window_mgr { public: using ScreenIndex = std::vector::size_type; inline void clear(ScreenIndex); private: std::vector screens; }; class Screen { friend void Window_mgr::clear(ScreenIndex); public: using pos = std::string::size_type; Screen() = default; // 1 Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2 Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3 char get() const { return contents[cursor]; } char get(pos r, pos c) const { return contents[r*width+c]; } inline Screen& move(pos r, pos c); inline Screen& set(char c); inline Screen& set(pos r, pos c, char ch); const Screen& display(std::ostream &os) const { do_display(os); return *this; } Screen& display(std::ostream &os) { do_display(os); return *this; } private: void do_display(std::ostream &os) const { os << contents; } private: pos cursor = 0; pos height = 0, width = 0; std::string contents; }; inline void Window_mgr::clear(ScreenIndex i) { if (i >= screens.size()) return; // judge for out_of_range. Screen &s = screens[i]; s.contents = std::string(s.height * s.width, ' '); } inline Screen& Screen::move(pos r, pos c) { cursor = r*width + c; return *this; } inline Screen& Screen::set(char c) { contents[cursor] = c; return *this; } inline Screen& Screen::set(pos r, pos c, char ch) { contents[r*width+c] = ch; return *this; } #endif ================================================ FILE: ch07/ex7_41.cpp ================================================ // // ex7_41.cpp // Exercise 7.41 // // Created by pezy on 11/20/14. // // @Brief implementation of class Sales_data // @See ex7_41.h #include "ex7_41.h" // constructor Sales_data::Sales_data(std::istream &is) : Sales_data() { std::cout << "Sales_data(istream &is)" << std::endl; read(is, *this); } // member functions. Sales_data& Sales_data::combine(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // friend functions std::istream &read(std::istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } std::ostream &print(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue; return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.combine(rhs); return sum; } ================================================ FILE: ch07/ex7_41.h ================================================ // // ex7_41.h // Exercise 7.41 // // Created by pezy on 11/20/14. // // @See ex7_26.h // @Add 1. use delegating constructors // 2. add a statement to the body of each of the constructors that prints a message whether it is executed. #ifndef CP5_ex7_41_h #define CP5_ex7_41_h #include #include class Sales_data { friend std::istream &read(std::istream &is, Sales_data &item); friend std::ostream &print(std::ostream &os, const Sales_data &item); friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs); public: Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p) { std::cout << "Sales_data(const std::string&, unsigned, double)" << std::endl; } Sales_data() : Sales_data("", 0, 0.0f) { std::cout << "Sales_data()" << std::endl; } Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f) { std::cout << "Sales_data(const std::string&)" << std::endl; } Sales_data(std::istream &is); std::string isbn() const { return bookNo; } Sales_data& combine(const Sales_data&); private: inline double avg_price() const; private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; inline double Sales_data::avg_price() const { return units_sold ? revenue/units_sold : 0; } // declarations for nonmember parts of the Sales_data interface. std::istream &read(std::istream &is, Sales_data &item); std::ostream &print(std::ostream &os, const Sales_data &item); Sales_data add(const Sales_data &lhs, const Sales_data &rhs); #endif ================================================ FILE: ch07/ex7_41_TEST.cpp ================================================ // // ex7_41_TEST.cpp // Test of Exercise 7.41 // // Created by pezy on 11/14/14. // #include "ex7_41.h" using std::cout; using std::endl; int main() { cout << "1. default way: " << endl; cout << "----------------" << endl; Sales_data s1; cout << "\n2. use std::string as parameter: " << endl; cout << "----------------" << endl; Sales_data s2("CPP-Primer-5th"); cout << "\n3. complete parameters: " << endl; cout << "----------------" << endl; Sales_data s3("CPP-Primer-5th", 3, 25.8); cout << "\n4. use istream as parameter: " << endl; cout << "----------------" << endl; Sales_data s4(std::cin); return 0; } ================================================ FILE: ch07/ex7_43.cpp ================================================ // // ex7_43.cpp // Exercise 7.43 // // Created by pezy on 11/20/14. // #include class NoDefault { public: NoDefault(int i) { } }; class C { public: C() : def(0) { } // define the constructor of C. private: NoDefault def; }; int main() { C c; std::vector vec(10); return 0; } ================================================ FILE: ch07/ex7_50.h ================================================ // // ex7_50.cpp // Exercise 7.50 // // Created by pezy on 11/24/14. // // @See ex7_22.h // @Add constructors should be explicit #ifndef CP5_ex7_50_h #define CP5_ex7_50_h #include #include struct Person { friend std::istream &read(std::istream &is, Person &person); friend std::ostream &print(std::ostream &os, const Person &person); public: Person() = default; Person(const std::string sname, const std::string saddr):name(sname), address(saddr){ } explicit Person(std::istream &is){ read(is, *this); } std::string getName() const { return name; } std::string getAddress() const { return address; } private: std::string name; std::string address; }; std::istream &read(std::istream &is, Person &person) { is >> person.name >> person.address; return is; } std::ostream &print(std::ostream &os, const Person &person) { os << person.name << " " << person.address; return os; } #endif ================================================ FILE: ch07/ex7_53.h ================================================ // // ex7_53.h // Exercise 7.53 // // Created by pezy on 11/25/14. // #ifndef CP5_ex7_53_h #define CP5_ex7_53_h class Debug { public: constexpr Debug(bool b = true) : rt(b), io(b), other(b) { } constexpr Debug(bool r, bool i, bool o) : rt(r), io(i), other(o) { } constexpr bool any() { return rt || io || other; } void set_rt(bool b) { rt = b; } void set_io(bool b) { io = b; } void set_other(bool b) { other = b; } private: bool rt; // runtime error bool io; // I/O error bool other; // the others }; #endif ================================================ FILE: ch07/ex7_57.h ================================================ // // ex7_57.h // Exercise 7.57 // // Created by pezy on 11/25/14. // #ifndef CP5_ex7_57_h #define CP5_ex7_57_h #include class Account { public: void calculate() { amount += amount * interestRate; } static double rate() { return interestRate; } static void rate(double newRate) { interestRate = newRate; } private: std::string owner; double amount; static double interestRate; static constexpr double todayRate = 42.42; static double initRate() { return todayRate; } }; double Account::interestRate = initRate(); #endif ================================================ FILE: ch08/README.md ================================================ # Chapter 8. The IO Library ## Exercise 8.1: >Write a function that takes and returns an istream&. The function should read the stream until it hits end-of-file. The function should print what it reads to the standard output. Reset the stream so that it is valid before returning the stream. ```cpp std::istream& func(std::istream &is) { std::string buf; while (is >> buf) std::cout << buf << std::endl; is.clear(); return is; } ``` ## [Exercise 8.2](ex8_02.cpp) ## Exercise 8.3: >What causes the following while to terminate? ```cpp while (cin >> i) /* ... */ ``` putting `cin` in an error state cause to terminate. such as `eofbit`, `failbit` and `badbit`. ## [Exercise 8.4](ex8_04.cpp) ## [Exercise 8.5](ex8_05.cpp) ## [Exercise 8.6](ex8_06.cpp) ## [Exercise 8.7](ex8_07.cpp) ## [Exercise 8.8](ex8_08.cpp) ## [Exercise 8.9](ex8_09.cpp) ## [Exercise 8.10](ex8_10.cpp) ## [Exercise 8.11](ex8_11.cpp) ## Exercise 8.12: >Why didn’t we use in-class initializers in PersonInfo? Cause we need a aggregate class here. so it should have no in-class initializers. ## [Exercise 8.13](ex8_13.cpp) ## Exercise 8.14: >Why did we declare entry and nums as const auto &? - cause they are all class type, not the built-in type. so **reference** more effective. - output shouldn't change their values. so we added the `const`. ================================================ FILE: ch08/ex8_02.cpp ================================================ // // ex8_02.cpp // Exercise 8.2 // // Created by pezy on 11/27/14. // // @Brief Test your function by calling it, passing cin as an argument #include using std::istream; istream& func(istream &is) { std::string buf; while (is >> buf) std::cout << buf << std::endl; is.clear(); return is; } int main() { istream& is = func(std::cin); std::cout << is.rdstate() << std::endl; return 0; } ================================================ FILE: ch08/ex8_04.cpp ================================================ // // ex8_04.cpp // Exercise 8.4 // // Created by pezy on 11/9/14. // // @Brief Write a function to open a file for input and read its contents into a vector of strings, // storing each line as a separate element in the vector. #include #include #include #include using std::vector; using std::string; using std::ifstream; using std::cout; using std::endl; void ReadFileToVec(const string& fileName, vector& vec) { ifstream ifs(fileName); if (ifs) { string buf; while (std::getline(ifs, buf)) vec.push_back(buf); } } int main() { vector vec; ReadFileToVec("../data/book.txt", vec); for (const auto &str : vec) cout << str << endl; return 0; } ================================================ FILE: ch08/ex8_05.cpp ================================================ // // ex8_05.cpp // Exercise 8.5 // // Created by pezy on 11/9/14. // // @Brief Rewrite the previous program to store each word in a separate element. // @See ex8_04.cpp #include #include #include #include using std::vector; using std::string; using std::ifstream; using std::cout; using std::endl; void ReadFileToVec(const string& fileName, vector& vec) { ifstream ifs(fileName); if (ifs) { string buf; while (ifs >> buf) vec.push_back(buf); } } int main() { vector vec; ReadFileToVec("../data/book.txt", vec); for (const auto &str : vec) cout << str << endl; return 0; } ================================================ FILE: ch08/ex8_06.cpp ================================================ // // ex8_06.cpp // Exercise 8.6 // // Created by pezy on 11/27/14. // // @Brief Rewrite the bookstore program from 7.1.1 (p.256) to read its transactions from a file. // Pass the name of the file as an argument to main (6.2.5, p.218). // @See ex7_26.h (use the Sales_data) // @Run give a parameter: "../data/book.txt" // @Output 0-201-78345-X 5 110 // 0-201-78346-X 9 839.2 #include #include #include "../ch07/ex7_26.h" using std::ifstream; using std::cout; using std::endl; using std::cerr; int main(int argc, char **argv) { ifstream input(argv[1]); Sales_data total; if (read(input, total)) { Sales_data trans; while (read(input, trans)) { if (total.isbn() == trans.isbn()) total.combine(trans); else { print(cout, total) << endl; total = trans; } } print(cout, total) << endl; } else { cerr << "No data?!" << endl; } return 0; } ================================================ FILE: ch08/ex8_07.cpp ================================================ // // ex8_07.cpp // Exercise 8.7 // // Created by pezy on 11/28/14. // // @Brief Revise the bookstore program from the previous section to write its output to a file. // Pass the name of that file as a second argument to main. // @See ex8_06.cpp // @Run give a parameter: "../data/book.txt ../data/out.txt" #include #include #include "../ch07/ex7_26.h" using std::ifstream; using std::ofstream; using std::endl; using std::cerr; int main(int argc, char **argv) { ifstream input(argv[1]); ofstream output(argv[2]); Sales_data total; if (read(input, total)) { Sales_data trans; while (read(input, trans)) { if (total.isbn() == trans.isbn()) total.combine(trans); else { print(output, total) << endl; total = trans; } } print(output, total) << endl; } else { cerr << "No data?!" << endl; } return 0; } ================================================ FILE: ch08/ex8_08.cpp ================================================ // // ex8_08.cpp // Exercise 8.8 // // Created by pezy on 11/28/14. // // @Brief Revise the program from the previous exercise to append its output to its given file. // Run the program on the same output file at least twice to ensure that the data are preserved // @See ex8_07.cpp // @Run give a parameter: "../data/book.txt ../data/out.txt" #include #include #include "../ch07/ex7_26.h" using std::ifstream; using std::ofstream; using std::endl; using std::cerr; int main(int argc, char **argv) { ifstream input(argv[1]); ofstream output(argv[2], ofstream::app); Sales_data total; if (read(input, total)) { Sales_data trans; while (read(input, trans)) { if (total.isbn() == trans.isbn()) total.combine(trans); else { print(output, total) << endl; total = trans; } } print(output, total) << endl; } else { cerr << "No data?!" << endl; } return 0; } ================================================ FILE: ch08/ex8_09.cpp ================================================ // // ex8_09.cpp // Exercise 8.9 // // Created by pezy on 11/29/14. // // @Brief Use the function you wrote for the first exercise in § 8.1.2 (p.314) to print the contents of an istringstream object. // @See Exercise 8.1 #include #include using std::istream; istream& func(istream &is) { std::string buf; while (is >> buf) std::cout << buf << std::endl; is.clear(); return is; } int main() { std::istringstream iss("hello"); func(iss); return 0; } ================================================ FILE: ch08/ex8_10.cpp ================================================ // // ex8_10.cpp // Exercise 8.10 // // Created by pezy on 11/29/14. // // @Brief Write a program to store each line from a file in a vector. // Now use an istringstream to read each element from the vector a word at a time. #include #include #include #include #include using std::vector; using std::string; using std::ifstream; using std::istringstream; using std::cout; using std::endl; using std::cerr; int main() { ifstream ifs("../data/book.txt"); if (!ifs) { cerr << "No data?" << endl; return -1; } vector vecLine; string line; while (getline(ifs, line)) vecLine.push_back(line); for (auto &s : vecLine) { istringstream iss(s); string word; while (iss >> word) cout << word << endl; } return 0; } ================================================ FILE: ch08/ex8_11.cpp ================================================ // // ex8_11.cpp // Exercise 8.11 // // Created by pezy on 11/29/14. // // @Brief The program in this section defined its istringstream object inside the outer while loop. // What changes would you need to make if record were defined outside that loop? // Rewrite the program, moving the definition of record outside the while, and see whether you thought of all the changes that are needed. #include #include #include #include using std::vector; using std::string; using std::cin; using std::istringstream; struct PersonInfo { string name; vector phones; }; int main() { string line, word; vector people; istringstream record; while (getline(cin, line)) { PersonInfo info; record.clear(); record.str(line); record >> info.name; while (record >> word) info.phones.push_back(word); people.push_back(info); } for (auto &p : people) { std::cout << p.name << " "; for (auto &s : p.phones) std::cout << s << " "; std::cout << std::endl; } return 0; } ================================================ FILE: ch08/ex8_13.cpp ================================================ // // ex8_13.cpp // Exercise 8.13 // // Created by pezy on 11/29/14. // // @Brief Rewrite the phone number program from this section to read from // a named file rather than from cin. // @See ex8_11.cpp #include #include #include #include #include using std::vector; using std::string; using std::cin; using std::istringstream; using std::ostringstream; using std::ifstream; using std::cerr; using std::cout; using std::endl; using std::isdigit; struct PersonInfo { string name; vector phones; }; bool valid(const string& str) { return isdigit(str[0]); } string format(const string& str) { return str.substr(0,3) + "-" + str.substr(3,3) + "-" + str.substr(6); } int main() { ifstream ifs("../data/phonenumbers.txt"); if (!ifs) { cerr << "no phone numbers?" << endl; return -1; } string line, word; vector people; istringstream record; while (getline(ifs, line)) { PersonInfo info; record.clear(); record.str(line); record >> info.name; while (record >> word) info.phones.push_back(word); people.push_back(info); } for (const auto &entry : people) { ostringstream formatted, badNums; for (const auto &nums : entry.phones) if (!valid(nums)) badNums << " " << nums; else formatted << " " << format(nums); if (badNums.str().empty()) cout << entry.name << " " << formatted.str() << endl; else cerr << "input error: " << entry.name << " invalid number(s) " << badNums.str() << endl; } return 0; } ================================================ FILE: ch09/README.md ================================================ # Chapter 9. Sequential Containers ## Exercise 9.1: >Which is the most appropriate—a vector, a deque, or a list—for the following program tasks?Explain the rationale for your choice.If there is no reason to prefer one or another container, explain why not. >- (a) Read a fixed number of words, inserting them in the container alphabetically as they are entered. We’ll see in the next chapter that associative containers are better suited to this problem. >- (b) Read an unknown number of words. Always insert new words at the back. Remove the next value from the front. >- (c) Read an unknown number of integers from a file. Sort the numbers and then print them to standard output. - (a) Within the three options, `std::list` is the best one. To keep sorted alphabetically, each inserting into vector takes theta(n) time complexity, whereas that of list (essentially doubly linked list) takes only O(n). Hence theoretically list has better performance. - (b) `deque`. If the program needs to insert or delete elements at the front and the back, but not in the middle, use a deque - (c) `vector`, no need that insert or delete at the front or back. and If your program has lots of small elements and space overhead matters, don’t use list or forward_list. ## Exercise 9.2: >Define a list that holds elements that are deques that hold ints. ```cpp std::list> a_list_of_deque_of_ints; ``` ## Exercise 9.3: >What are the constraints on the iterators that form iterator ranges? two iterators, `begin` and `end`: - they refer to elements of the same container. - It is possible to reach `end` by repeatedly incrementing `begin`. ## Exercise 9.4: >Write a function that takes a pair of iterators to a vector and an int value. Look for that value in the range and return a bool indicating whether it was found. ```cpp bool contains(vector::const_iterator first, vector::const_iterator last, int value) { for(; first != last; ++first) if(*first == value) return true; return false; } ``` ## Exercise 9.5: >Rewrite the previous program to return an iterator to the requested element. Note that the program must handle the case where the element is not found. ```cpp auto find(vector::const_iterator first, vector::const_iterator last, int value) { for(; first != last; ++first) if(*first == value) return first; return last; } ``` ## Exercise 9.6: >What is wrong with the following program? How might you correct it? ```cpp list lst1; list::iterator iter1 = lst1.begin(), iter2 = lst1.end(); while (iter1 < iter2) ``` Fixed: ```cpp while(iter1 != iter2) ``` #### note: operator `<` is not implemented in `std::list`, because `std::list` is essetially a doubly linked list. Addresses of nodes of linked list are not necessarily continuous. ## Exercise 9.7: >What type should be used as the index into a vector of ints? vector::size_type ## Exercise 9.8: >What type should be used to read elements in a list of strings? To write them? list::const_iterator // to read list::iterator // to write ## Exercise 9.9: >What is the difference between the `begin` and `cbegin` functions? `cbegin` is a const member that returns the container’s **const_iterator** type. `begin` is nonconst and returns the container’s **iterator** type. ## Exercise 9.10: >What are the types of the following four objects? ```cpp vector v1; const vector v2; auto it1 = v1.begin(), it2 = v2.begin(); auto it3 = v1.cbegin(), it4 = v2.cbegin(); ``` ----- [@shidenggui](https://github.com/shidenggui): The question example codes have an error in `gcc 4.8`: >**error**: inconsistent deduction for ‘auto’: ‘__gnu_cxx::__normal_iterator >’ and then ‘__gnu_cxx::__normal_iterator >’ auto it1 = v1.begin(), it2 = v2.begin(); the correct codes should be: ```cpp auto it1 = v1.begin(); auto it2 = v2.begin(), it3 = v1.cbegin(), it4 = v2.cbegin(); ``` ----- `it1` is `vector::iterator` `it2`, `it3` and `it4` are `vector::const_iterator` ## Exercise 9.11: >Show an example of each of the six ways to create and initialize a vector. Explain what values each vector contains. ```cpp vector vec; // vec is empty vector vec(10); // 0 vector vec(10, 1); // 1 vector vec{ 1, 2, 3, 4, 5 }; // 1, 2, 3, 4, 5 vector vec(other_vec); // same as other_vec vector vec(other_vec.begin(), other_vec.end()); // same as other_vec ``` ## Exercise 9.12: >Explain the differences between the constructor that takes a container to copy and the constructor that takes two iterators. - The constructor that takes another container as an argument (excepting array) assumes the container type and element type of both containers are identical. It will also copy all the elements of the received container into the new one: ```cpp list numbers = { 1, 2, 3, 4, 5 }; list numbers2(numbers); // ok, numbers2 has the same elements as numbers vector numbers3(numbers); // error: no matching function for call... list numbers4(numbers); // error: no matching function for call... ``` - The constructor that takes two iterators as arguments does not require the container types to be identical. Moreover, the element types in the new and original containers can differ as long as it is possible to convert the elements we’re copying to the element type of the container we are initializing. It will also copy only the object delimited by the received iterators. ```cpp list numbers = { 1, 2, 3, 4, 5 }; list numbers2(numbers.begin(), numbers.end); // ok, numbers2 has the same elements as numbers vector numbers3(numbers.begin(), --numbers.end()); // ok, numbers3 is { 1, 2, 3, 4 } list numbers4(++numbers.beg(), --numbers.end()); // ok, numbers4 is { 2, 3, 4 } forward_list numbers5(numbers.begin(), numbers.end()); // ok, number5 is { 1, 2, 3, 4, 5 } ``` ## [Exercise 9.13](ex9_13.cpp) ## [Exercise 9.14](ex9_14.cpp) ## [Exercise 9.15](ex9_15.cpp) ## [Exercise 9.16](ex9_16.cpp) ## Exercise 9.17: >Assuming c1 and c2 are containers, what (if any) constraints does the following usage place on the types of c1 and c2? First, there must be the identical container and same type holded. Second, the type held must support relational operation. (@Mooophy) Both c1 and c2 are the containers except the unordered associative containers.(@pezy) ## [Exercise 9.18](ex9_18.cpp) ## [Exercise 9.19](ex9_19.cpp) ## [Exercise 9.20](ex9_20.cpp) ## Exercise 9.21: >Explain how the loop from page 345 that used the return from insert to add elements to a list would work if we inserted into a vector instead. It's the same. >The first call to `insert` takes the `string` we just read and puts it in front of the element denoted by `iter`. The value returned by `insert` is an iterator referring to this new element. We assign that iterator to `iter` and repeat the `while`, reading another word. As long as there are words to insert, each trip through the `while` inserts a new element ahead of `iter` and reassigns to `iter` the location of the newly inserted element. That element is the (new) first element. Thus, each iteration inserts an element ahead of the first element in the `vector`. ## Exercise 9.22: >Assuming `iv` is a `vector` of `int`s, what is wrong with the following program? How might you correct the problem(s)? ```cpp vector::iterator iter = iv.begin(), mid = iv.begin() + iv.size()/2; while (iter != mid) if (*iter == some_val) iv.insert(iter, 2 * some_val); ``` **Problems:** 1. It's a endless loop. `iter` never equal `mid`. 2. mid will be invalid after the `insert`.(see [issue 133](https://github.com/Mooophy/Cpp-Primer/issues/133)) **FIXED**: ```cpp // cause the reallocation will lead the iterators and references // after the insertion point to invalid. Thus, we need to call reserver at first. #include #include void double_and_insert(std::vector& v, int some_val) { auto mid = [&]{ return v.begin() + v.size() / 2; }; for (auto curr = v.begin(); curr != mid(); ++curr) if (*curr == some_val) ++(curr = v.insert(curr, 2 * some_val)); } int main() { std::vector v{ 1, 9, 1, 9, 9, 9, 1, 1 }; double_and_insert(v, 1); for (auto i : v) std::cout << i << std::endl; } ``` The complete test codes, check [this](ex9_22.cpp). ## Exercise 9.23: >In the first program in this section on page 346, what would the values of val, val2, val3, and val4 be if c.size() is 1? same value that equal to the first element's. ## [Exercise 9.24](ex9_24.cpp) ## Exercise 9.25: >In the program on page 349 that erased a range of elements, what happens if elem1 and elem2 are equal? What if elem2 or both elem1 and elem2 are the off-the-end iterator? if elem1 and elem2 are equal, nothing happened. if elem2 is the off-the-end iterator, it would delete from elem1 to the end. if both elem1 and elem2 are the off-the-end iterator, nothing happened too. ## [Exercise 9.26](ex9_26.cpp) ## [Exercise 9.27](ex9_27.cpp) ## Exercise 9.28: >Write a function that takes a forward_list and two additional string arguments. The function should find the first string and insert the second immediately following the first. If the first string is not found, then insert the second string at the end of the list. ```cpp void find_and_insert(forward_list &list, string const& to_find, string const& to_add) { auto prev = list.before_begin(); for (auto curr = list.begin(); curr != list.end(); prev = curr++) { if (*curr == to_find) { list.insert_after(curr, to_add); return; } } list.insert_after(prev, to_add); } ``` ## Exercise 9.29: >Given that vec holds 25 elements, what does vec.resize(100) do? What if we next wrote vec.resize(10)? ```cpp vec.resize(100); // adds 75 elements of value 0 to the back of vec vec.resize(10); // erases 90 elements from the back of vec ``` ## Exercise 9.30: >What, if any, restrictions does using the version of resize that takes a single argument place on the element type? If the container holds elements of a class type and resize adds elements we **must supply an initializer** or the element type must have a **default constructor**. ## Exercise 9.31 [use list](ex9_31_1.cpp) | [use forward_list](ex9_31_2.cpp) ## [Exercise 9.32](ex9_32.cpp) ## [Exercise 9.33](ex9_33.cpp) ## [Exercise 9.34](ex9_34.cpp) ## Exercise 9.35: >Explain the difference between a vector’s capacity and its size. The **size** of a container is the number of **elements** it already holds; The **capacity** is how many elements it can hold before more **space** must be allocated. ## Exercise 9.36: >Can a container have a capacity less than its size? cannot. ## Exercise 9.37: >Why don’t list or array have a capacity member? `list` does not hold elements contiguously. `array` has the fixed size statically. ## [Exercise 9.38](ex9_38.cpp) ## Exercise 9.39: >Explain what the following program fragment does: ```cpp vector svec; svec.reserve(1024); string word; while (cin >> word) svec.push_back(word); svec.resize(svec.size()+svec.size()/2); ``` The `while` loop will read words from `cin` and store them in out vector. Even if we initially reserved 1024 elements, if there are more words read from `cin`, our vector's capacity will be automatically increased (most implementations will double the previous capacity) to accommodate them. And now comes the catch. `resize()` is different from `reserve()`. In this case `resize()` will add another `svec.size()/2` value initialized elements to `svec`. If this exceeds `svec.capacity()` it will also automatically increase it to accommodate the new elements. ## Exercise 9.40: >If the program in the previous exercise reads 256 words, what is its likely capacity after it is resized? What if it reads 512? 1, 000? 1, 048? read | size | capacity ------ | ------ | ------ 256 | 384 | 1024 512 | 768 | 1024 1000 | 1500 | 2000(clang is 2048) 1048 | 1572 | 2048 ## [Exercise 9.41](ex9_41.cpp) ## Exercise 9.42: >Given that you want to read a character at a time into a string, and you know that you need to read at least 100 characters, how might you improve the performance of your program? Use member `reserve(120)` to allocate enough space for this string. (@Mooophy) ## [Exercise 9.43](ex9_43.cpp) ## [Exercise 9.44](ex9_44.cpp) ## [Exercise 9.45](ex9_45.cpp) ## [Exercise 9.46](ex9_46.cpp) ## Exercise 9.47 [find_first_of](ex9_47_1.cpp) | [find_first_not_of](ex9_47_2.cpp) ## Exercise 9.48: >Given the definitions of name and numbers on page 365, what does numbers.find(name) return? string::npos ## [Exercise 9.49](ex9_49.cpp) ## [Exercise 9.50](ex9_50.cpp) ## [Exercise 9.51](ex9_51.cpp) ## [Exercise 9.52](ex9_52.cpp) ================================================ FILE: ch09/ex9_13.cpp ================================================ // @author @shbling @Yue Wang // // Exercise 9.13: // How would you initialize a vector from a list? // From a vector? // Write code to check your answers. // #include #include #include #include using std::list; using std::vector; using std::cout; using std::endl; int main() { list ilst(5, 4); vector ivc(5, 5); // from list to vector vector dvc(ilst.begin(), ilst.end()); for (auto i : ilst) cout << i << " "; cout << endl; for (auto d : dvc) cout << d << " "; cout << endl; // from vector to vector vector dvc2(ivc.begin(), ivc.end()); for (auto i : ivc) cout << i << " "; cout << endl; for (auto d : dvc2) cout << d << " "; return 0; } ================================================ FILE: ch09/ex9_14.cpp ================================================ // @Yue Wang @pezy // // Exercise 9.14: // Write a program to assign the elements from a list of char* pointers // to C-style character strings // #include #include #include #include int main() { std::list l{ "Mooophy", "pezy", "Queeuqueg" }; std::vector v; v.assign(l.cbegin(), l.cend()); for (auto ptr : v) std::cout << ptr << std::endl; return 0; } ================================================ FILE: ch09/ex9_15.cpp ================================================ // // ex9_15.cpp // Exercise 9.15 // // Created by pezy on 12/2/14. // // @Brief Write a program to determine whether two vectors are equal. #include #include int main() { std::vector vec1{ 1, 2, 3, 4, 5 }; std::vector vec2{ 1, 2, 3, 4, 5 }; std::vector vec3{ 1, 2, 3, 4 }; std::cout << (vec1 == vec2 ? "true" : "false") << std::endl; std::cout << (vec1 == vec3 ? "true" : "false") << std::endl; return 0; } ================================================ FILE: ch09/ex9_16.cpp ================================================ // // ex9_16.cpp // Exercise 9.16 // // Created by pezy on 12/2/14. // // @Brief Repeat the previous program, but compare elements in a // list to a vector. // // @Refactor Yue Wang Jun 2015 // #include #include #include int main() { std::list li{ 1, 2, 3, 4, 5 }; std::vector vec2{ 1, 2, 3, 4, 5 }; std::vector vec3{ 1, 2, 3, 4 }; std::cout << (std::vector(li.begin(), li.end()) == vec2 ? "true" : "false") << std::endl; std::cout << (std::vector(li.begin(), li.end()) == vec3 ? "true" : "false") << std::endl; return 0; } ================================================ FILE: ch09/ex9_18.cpp ================================================ // // ex9_18.cpp // Exercise 9.18 // // Created by pezy on 12/3/14. // // @Brief Write a program to read a sequence of strings from the standard input into // a deque. Use iterators to write a loop to print the elements in the deque. #include #include #include using std::string; using std::deque; using std::cout; using std::cin; using std::endl; int main() { deque input; for (string str; cin >> str; input.push_back(str)); for (auto iter = input.cbegin(); iter != input.cend(); ++iter) cout << *iter << endl; return 0; } ================================================ FILE: ch09/ex9_19.cpp ================================================ // // ex9_19.cpp // Exercise 9.19 // // Created by pezy on 12/3/14. // // @Brief Rewrite the program from the previous exercise to use a list. // List the changes you needed to make. // @See ex9_18.cpp #include #include #include using std::string; using std::list; using std::cout; using std::cin; using std::endl; int main() { list input; for (string str; cin >> str; input.push_back(str)); for (auto iter = input.cbegin(); iter != input.cend(); ++iter) cout << *iter << endl; return 0; } ================================================ FILE: ch09/ex9_20.cpp ================================================ // // ex9_20.cpp // Exercise 9.20 // // Created by pezy on 12/3/14. // // @Brief Write a program to copy elements from a list into two deques. // The even-valued elements should go into one deque and the odd ones into the other. #include #include #include using std::deque; using std::list; using std::cout; using std::cin; using std::endl; int main() { list l{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; deque odd, even; for (auto i : l) (i & 0x1 ? odd : even).push_back(i); for (auto i : odd) cout << i << " "; cout << endl; for (auto i : even)cout << i << " "; cout << endl; return 0; } ================================================ FILE: ch09/ex9_22.cpp ================================================ #include #include void double_and_insert(std::vector& v, int some_val) { auto mid = [&]{ return v.begin() + v.size() / 2; }; for (auto curr = v.begin(); curr != mid(); ++curr) if (*curr == some_val) ++(curr = v.insert(curr, 2 * some_val)); } int main() { std::vector v{ 1, 9, 1, 9, 9, 9, 1, 1 }; double_and_insert(v, 1); for (auto i : v) std::cout << i << std::endl; } ================================================ FILE: ch09/ex9_24.cpp ================================================ // @Yue Wang @pezy // // Exercise 9.24: // Write a program that fetches the first element in a vector using at, // the subscript operator, front, and begin. Test your program on an empty vector. // #include #include int main() { std::vector v; std::cout << v.at(0); // terminating with uncaught exception of type std::out_of_range std::cout << v[0]; // Segmentation fault: 11 std::cout << v.front(); // Segmentation fault: 11 std::cout << *v.begin(); // Segmentation fault: 11 return 0; } ================================================ FILE: ch09/ex9_26.cpp ================================================ // // @author @huangjuncmj @Yue Wang // @date 19.11.2014 // // Exercise 9.26: // Using the following definition of ia, copy ia into a vector and into a list. // Use the single-iterator form of erase to remove the elements with odd values from your // list and the even values from your vector. // #include #include #include using std::vector; using std::list; using std::cout; using std::endl; using std::end; int main() { int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 }; // init vector vec(ia, end(ia)); list lst(vec.begin(), vec.end()); // remove odd value for(auto it = lst.begin(); it != lst.end(); ) if(*it & 0x1) it = lst.erase(it); else ++it; // remove even value for(auto it = vec.begin(); it != vec.end(); ) if(! (*it & 0x1)) it = vec.erase(it); else ++it; // print cout << "list : "; for(auto i : lst) cout << i << " "; cout << "\nvector : "; for(auto i : vec) cout << i << " "; cout << std::endl; return 0; } ================================================ FILE: ch09/ex9_27.cpp ================================================ // // ex9_27.cpp // Exercise 9.27 // // Created by pezy on 12/3/14. // // @Brief Write a program to find and remove the odd-valued // elements in a forward_list. // // @Refactored by @Yue Wang Jun 2015, Oct 2015 // #include #include using std::forward_list; using std::cout; auto remove_odds(forward_list& flist) { auto is_odd = [] (int i) { return i & 0x1; }; flist.remove_if(is_odd); } int main() { forward_list data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; remove_odds(data); for (auto i : data) cout << i << " "; return 0; } ================================================ FILE: ch09/ex9_31_1.cpp ================================================ // // ex9_31.cpp // Exercise 9.31 // // Created by pezy on 12/3/14. // // @Brief The program on page 354 to remove even-valued elements and // duplicate odd ones will not work on a list or forward_list. Why? // Revise the program so that it works on these types as well. // @list iter += 2; -> advance(iter, 2); // // Refactored by Yue Wang Oct 2015 // #include #include using std::list; using std::cout; using std::advance; auto remove_evens_and_double_odds(list& data) { for(auto cur = data.begin(); cur != data.end();) if (*cur & 0x1) cur = data.insert(cur, *cur), advance(cur, 2); else cur = data.erase(cur); } int main() { list data { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; remove_evens_and_double_odds(data); for (auto i : data) cout << i << " "; return 0; } ================================================ FILE: ch09/ex9_31_2.cpp ================================================ // // ex9_31.cpp // Exercise 9.31 // // Created by pezy on 12/3/14. // // @Brief The program on page 354 to remove even-valued elements and // duplicate odd ones will not work on a list or forward_list. Why? // Revise the program so that it works on these types as well. // // Refactored by Yue Wang Oct 2015 // #include #include using std::forward_list; using std::cout; using std::advance; auto remove_evens_and_double_odds(forward_list& data) { for(auto cur = data.begin(), prv = data.before_begin(); cur != data.end();) if (*cur & 0x1) cur = data.insert_after(prv, *cur), advance(cur, 2), advance(prv, 2); else cur = data.erase_after(prv); } int main() { forward_list data { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; remove_evens_and_double_odds(data); for (auto i : data) cout << i << " "; return 0; } ================================================ FILE: ch09/ex9_32.cpp ================================================ // // ex9_32.cpp // Exercise 9.32 // // Created by pezy on 12/3/14. // // @Brief In the program on page 354 would it be legal to write the call to insert as follows? // If not, why not? // iter = vi.insert(iter, *iter++); // @Answer the statement is illegal, because as said in Standard [5.2.2] : // "The order of evaluation of arguments is unspecified." // As a result, after entering function insert, // iter could be its original value or original value + 1 or even anything else, // depending on how compiler implemented. // @Discuss https://github.com/Mooophy/Cpp-Primer/issues/125 int main() { return 0; } ================================================ FILE: ch09/ex9_33.cpp ================================================ // // ex9_33.cpp // Exercise 9.33 // // Created by pezy on 12/3/14. // // @Brief In the final example in this section what would happen // if we did not assign the result of insert to begin? // Write a program that omits this assignment to see if your expectation was correct. // @Answer Crash, because the iterator is invalid after inserting. #include #include using std::vector; using std::cout; using std::endl; int main() { vector v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; auto begin = v.begin(); while (begin != v.end()) { ++begin; /*begin = */v.insert(begin, 42); ++begin; } for (auto i : v) cout << i << " "; return 0; } ================================================ FILE: ch09/ex9_34.cpp ================================================ // // ex9_32.cpp // Exercise 9.32 // // Created by pezy on 12/3/14. // // @Brief Assuming vi is a container of ints that includes even and odd values, // predict the behavior of the following loop. // After you've analyzed this loop, write a program to test whether your expectations were correct. // // iter = vi.begin(); // while (iter != vi.end()) // if (*iter % 2) // iter = vi.insert(iter, *iter); // ++iter; // // @Answer "infinite loop". Becasue the `++iter` is outside of the scope of the `while` loop. // Fixed as following. // // Refactored by Yue Wang Oct, 2015 // #include #include using std::cout; using std::endl; using std::vector; int main() { vector data { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; for(auto cur = data.begin(); cur != data.end(); ++cur) if(*cur & 0x1) cur = data.insert(cur, *cur), ++cur; for (auto i : data) cout << i << " "; return 0; } ================================================ FILE: ch09/ex9_38.cpp ================================================ // @Yue Wang // // Exercise 9.38: // Write a program to explore how vectors grow in the library you use. // #include #include #include int main() { std::vector v; for (std::string buffer; std::cin >> buffer; v.push_back(buffer)) std::cout << v.capacity() << std::endl; return 0; } ================================================ FILE: ch09/ex9_41.cpp ================================================ // // ex9_41.cpp // Exercise 9.41 // // Created by pezy on 12/4/14. // // @Brief Write a program that initializes a string from a vector. #include #include #include using std::vector; using std::cout; using std::endl; using std::string; int main() { vector v{ 'p', 'e', 'z', 'y' }; string str(v.cbegin(), v.cend()); cout << str << endl; return 0; } ================================================ FILE: ch09/ex9_43.cpp ================================================ // // ex9_43.cpp // Exercise 9.43 // // Created by XDXX on 4/17/2015. // // @Brief Write a function that takes three strings, s, oldVal, and newVal. // Using iterators, and the insert and erase functions replace // all instances of oldVal that appear in s by newVal. // Test your function by using it to replace common abbreviations, // such as “tho” by “though” and “thru” by “through”. // // @note This code doesn't compile on GCC. Please use Visual Studio 2015+ or Clang 3.6+ // // Refactored by Yue Wang Oct, 2015 // #include #include #include #include using std::cout; using std::endl; using std::string; auto replace_with(string &s, string const& oldVal, string const& newVal) { for (auto cur = s.begin(); cur <= s.end() - oldVal.size(); ) if (oldVal == string{ cur, cur + oldVal.size() }) cur = s.erase(cur, cur + oldVal.size()), cur = s.insert(cur, newVal.begin(), newVal.end()), cur += newVal.size(); else ++cur; } int main() { string s{ "To drive straight thru is a foolish, tho courageous act." }; replace_with(s, "tho", "though"); replace_with(s, "thru", "through"); cout << s << endl; return 0; } ================================================ FILE: ch09/ex9_44.cpp ================================================ // // ex9_44.cpp // Exercise 9.44 // // Created by XDXX on 4/17/15. // // @Brief Rewrite the previous function using an index and replace. // @See 9.43 // // Refactored by Yue Wang Oct, 2015 // #include #include using std::cout; using std::endl; using std::string; auto replace_with(string &s, string const& oldVal, string const& newVal) { for (size_t pos = 0; pos <= s.size() - oldVal.size();) if (s[pos] == oldVal[0] && s.substr(pos, oldVal.size()) == oldVal) s.replace(pos, oldVal.size(), newVal), pos += newVal.size(); else ++pos; } int main() { string str{ "To drive straight thru is a foolish, tho courageous act." }; replace_with(str, "tho", "though"); replace_with(str, "thru", "through"); cout << str << endl; return 0; } ================================================ FILE: ch09/ex9_45.cpp ================================================ // @author @TungWah @Yue Wang // @date 4 Oct, 2014. Oct, 2015. // // Exercise 9.45: // Write a funtion that takes a string representing a name and two other // strings representing a prefix, such as “Mr.” or “Ms.” and a suffix, // such as “Jr.” or “III”. Using iterators and the insert and append functions, // generate and return a new string with the suffix and prefix added to the // given name. // #include #include using std::string; using std::cin; using std::cout; using std::endl; // Exercise 9.45 auto add_pre_and_suffix(string name, string const& pre, string const& su) { name.insert(name.begin(), pre.cbegin(), pre.cend()); return name.append(su); } int main() { string name("Wang"); cout << add_pre_and_suffix(name, "Mr.", ", Jr.") << endl; return 0; } ================================================ FILE: ch09/ex9_46.cpp ================================================ // // ex9_46.cpp // Exercise 9.46 // // Created by pezy on 12/5/14. // // @Brief Rewrite the previous exercise using a position and length to manage the strings. // This time use only the insert function. // @See 9.45 // // Refactored by Yue Wang Oct, 2015 // #include #include auto add_pre_and_suffix(std::string name, std::string const& pre, std::string const& su) { name.insert(0, pre); name.insert(name.size(), su); return name; } int main() { std::string name("alan"); std::cout << add_pre_and_suffix(name, "Mr.", ",Jr."); return 0; } ================================================ FILE: ch09/ex9_47_1.cpp ================================================ // // ex9_47_1.cpp // Exercise 9.47 // // Created by pezy on 12/5/14. // // @Brief Write a program that finds each numeric character // and then each alphabetic character in the string "ab2c3d7R4E6". // Write two versions of the program. The first should use find_first_of, // and the second find_first_not_of. // @Version find_first_of #include #include using std::string; using std::cout; using std::endl; int main() { string numbers{ "123456789" }; string alphabet{ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" }; string str{ "ab2c3d7R4E6" }; cout << "numeric characters: "; for (int pos = 0; (pos = str.find_first_of(numbers, pos)) != string::npos; ++pos) cout << str[pos] << " "; cout << "\nalphabetic characters: "; for (int pos = 0; (pos = str.find_first_of(alphabet, pos)) != string::npos; ++pos) cout << str[pos] << " "; cout << endl; return 0; } ================================================ FILE: ch09/ex9_47_2.cpp ================================================ // // ex9_47_2.cpp // Exercise 9.47 // // Created by pezy on 12/5/14. // // @Brief Write a program that finds each numeric character // and then each alphabetic character in the string "ab2c3d7R4E6". // Write two versions of the program. The first should use find_first_of, // and the second find_first_not_of. // @Version find_first_not_of #include #include using std::string; using std::cout; using std::endl; int main() { string numbers{ "0123456789" }; string alphabet{ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" }; string str{ "ab2c3d7R4E6" }; cout << "numeric characters: "; for (int pos = 0; (pos = str.find_first_not_of(alphabet, pos)) != string::npos; ++pos) cout << str[pos] << " "; cout << "\nalphabetic characters: "; for (int pos = 0; (pos = str.find_first_not_of(numbers, pos)) != string::npos; ++pos) cout << str[pos] << " "; cout << endl; return 0; } ================================================ FILE: ch09/ex9_49.cpp ================================================ // // ex9_49.cpp // Exercise 9.49 // // Created by pezy on 12/5/14. // // @Brief A letter has an ascender if, as with d or f, part of the letter extends // above the middle of the line. // A letter has a descender if, as with p or g, part of the letter extends below the line. // Write a program that reads a file containing words and reports the longest word // that contains neither ascenders nor descenders. // // @Refactor @Yue Wang Jun 2015 // #include #include #include using std::string; using std::cout; using std::endl; using std::ifstream; int main() { ifstream ifs("../data/letter.txt"); if (!ifs) return -1; string longest; auto update_with = [&longest](string const& curr) { if (string::npos == curr.find_first_not_of("aceimnorsuvwxz")) longest = longest.size() < curr.size() ? curr : longest; }; for (string curr; ifs >> curr; update_with(curr)); cout << longest << endl; return 0; } ================================================ FILE: ch09/ex9_50.cpp ================================================ // @Yue Wang // // Exercise 9.50: // Write a program to process a vectors whose elements represent integral values. // Produce the sum of all the elements in that vector. // Change the program so that it sums of strings that represent floating-point values. // #include #include #include auto sum_for_int(std::vector const& v) { int sum = 0; for (auto const& s : v) sum += std::stoi(s); return sum; } auto sum_for_float(std::vector const& v) { float sum = 0.0; for (auto const& s : v) sum += std::stof(s); return sum; } int main() { std::vector v = { "1", "2", "3", "4.5" }; std::cout << sum_for_int(v) << std::endl; std::cout << sum_for_float(v) << std::endl; return 0; } ================================================ FILE: ch09/ex9_51.cpp ================================================ // @Soyn // // Exercise 9.51: // Write a class that has three unsigned members representing year, // month, and day. Write a constructor that takes a string representing // a date. Your constructor should handle a variety of date formats, // such as January 1, 1900, 1/1/1900, Jan 1, 1900, and so on. // #include #include #include /*@Soyn *Ex 9.51 *Date: 2015.6.12 */ using namespace std; class My_date{ private: unsigned year, month, day; public: My_date(const string &s){ unsigned tag; unsigned format; format = tag = 0; // 1/1/1900 if(s.find_first_of("/")!= string :: npos) { format = 0x01; } // January 1, 1900 or Jan 1, 1900 if((s.find_first_of(',') >= 4) && s.find_first_of(',')!= string :: npos){ format = 0x10; } else{ // Jan 1 1900 if(s.find_first_of(' ') >= 3 && s.find_first_of(' ')!= string :: npos){ format = 0x10; tag = 1; } } switch(format){ case 0x01: day = stoi(s.substr(0, s.find_first_of("/"))); month = stoi(s.substr(s.find_first_of("/") + 1, s.find_last_of("/")- s.find_first_of("/"))); year = stoi(s.substr(s.find_last_of("/") + 1, 4)); break; case 0x10: if( s.find("Jan") < s.size() ) month = 1; if( s.find("Feb") < s.size() ) month = 2; if( s.find("Mar") < s.size() ) month = 3; if( s.find("Apr") < s.size() ) month = 4; if( s.find("May") < s.size() ) month = 5; if( s.find("Jun") < s.size() ) month = 6; if( s.find("Jul") < s.size() ) month = 7; if( s.find("Aug") < s.size() ) month = 8; if( s.find("Sep") < s.size() ) month = 9; if( s.find("Oct") < s.size() ) month =10; if( s.find("Nov") < s.size() ) month =11; if( s.find("Dec") < s.size() ) month =12; char chr = ','; if(tag == 1){ chr = ' '; } day = stoi(s.substr(s.find_first_of("123456789"), s.find_first_of(chr) - s.find_first_of("123456789"))); year = stoi(s.substr(s.find_last_of(' ') + 1, 4)); break; } } void print(){ cout << "day:" << day << " " << "month: " << month << " " << "year: " << year; } }; int main() { My_date d("Jan 1 1900"); d.print(); return 0; } ================================================ FILE: ch09/ex9_52.cpp ================================================ // // ex9_52.cpp // Exercise 9.52 // // Created by pezy on 12/5/14. // // @Brief Use a stack to process parenthesized expressions. // When you see an open parenthesis, note that it was seen. // When you see a close parenthesis after an open parenthesis, // pop elements down to and including the open parenthesis off the stack. // push a value onto the stack to indicate that a parenthesized expression was replaced. #include #include #include using std::string; using std::cout; using std::endl; using std::stack; int main() { string expression{ "This is (pezy)." }; bool bSeen = false; stack stk; for (const auto &s : expression) { if (s == '(') { bSeen = true; continue; } else if (s == ')') bSeen = false; if (bSeen) stk.push(s); } string repstr; while (!stk.empty()) { repstr += stk.top(); stk.pop(); } expression.replace(expression.find("(")+1, repstr.size(), repstr); cout << expression << endl; return 0; } ================================================ FILE: ch10/README.md ================================================ # Chapter 10. Generic Algorithms ## [Exercise 10.1 and 10.2](ex10_01_02.cpp) ## [Exercise 10.3 and 10.4](ex10_03_04.cpp) ## Exercise 10.5: >In the call to equal on rosters, what would happen if both rosters held C-style strings, rather than library strings? For such case, std::equal is going to compare the address value rather than the string value. So the result is not the same as std::string. Try to avoid coding this way. Check [#227](https://github.com/Mooophy/Cpp-Primer/issues/227) for more detail. ## [Exercise 10.6](ex10_06.cpp) ## [Exercise 10.7](ex10_07.cpp) ## Exercise 10.8: >We said that algorithms do not change the size of the containers over which they operate. Why doesn’t the use of back_inserter invalidate this claim? Inserters like `back_inserter` is part of `` rather than ``. ## [Exercise 10.9](ex10_09.cpp) ## Exercise 10.10: >Why do you think the algorithms don’t change the size of containers? @Mooophy: The aim of this design is to separate the algorithms and the operation provided by member function. @pezy: Cause the library algorithms operate on **iterators**, **not containers**. Thus, an algorithm **cannot (directly)** add or remove elements. ## [Exercise 10.11](ex10_11.cpp) ## [Exercise 10.12](ex10_12.cpp) ## [Exercise 10.13](ex10_13.cpp) ## Exercise 10.14: >Write a lambda that takes two ints and returns their sum. ```cpp auto add = [](int lhs, int rhs){ return lhs + rhs; }; ``` ## Exercise 10.15: >Write a lambda that captures an int from its enclosing function and takes an int parameter. The lambda should return the sum of the captured int and the int parameter. ```cpp int i = 42; auto add = [i](int num){ return i + num; }; ``` ## [Exercise 10.16](ex10_16.cpp) ## [Exercise 10.17](ex10_17.cpp) ## [Exercise 10.18 and 10.19](ex10_18_19.cpp) ## [Exercise 10.20 and 10.21](ex10_20_21.cpp) ## [Exercise 10.22](ex10_22.cpp) ## Exercise 10.23: >How many arguments does bind take? Assuming the function to be bound have `n` parameters, bind take `n + 1` parameters. The additional one is for the function to be bound itself. ## [Exercise 10.24](ex10_24.cpp) ## [Exercise 10.25](ex10_25.cpp) ## Exercise 10.26: >Explain the differences among the three kinds of insert iterators. - `back_inserter` uses `push_back`. - `front_inserter` uses `push_front`. - `insert` uses `insert` >This function takes a second argument, which must be an iterator into the given container. Elements are inserted ahead of the element denoted by the given iterator. ## [Exercise 10.27](ex10_27.cpp) ## [Exercise 10.28](ex10_28.cpp) ## [Exercise 10.29](ex10_29.cpp) ## [Exercise 10.30](ex10_30.cpp) ## [Exercise 10.31](ex10_31.cpp) ## [Exercise 10.32](ex10_32.cpp) ## [Exercise 10.33](ex10_33.cpp) ## [Exercise 10.34 ~ 10.37](ex10_34_35_36_37.cpp) ## Exercise 10.38: >List the five iterator categories and the operations that each supports. - Input iterators : `==`, `!=`, `++`, `*`, `->` - Output iterators : `++`, `*` - Forward iterators : `==`, `!=`, `++`, `*`, `->` - Bidirectional iterators : `==`, `!=`, `++`, `--`, `*`, `->` - Random-access iterators : `==`, `!=`, `<`, `<=`, `>`, `>=`, `++`, `--`, `+`, `+=`, `-`, `-=`, `-`(two iterators), `*`, `->`, `iter[n]` == `* (iter + n)` ## Exercise 10.39: >What kind of iterator does a list have? What about a vector? `list` have the **Bidirectional iterators**. `vector` have the **Random-access iterators**. ## Exercise 10.40: >What kinds of iterators do you think copy requires? What about reverse or unique? - `copy` : first and second are Input iterators, last is Output iterators. - `reverse` : Bidirectional iterators. - `unique` : Forward iterators. ## Exercise 10.41: >Based only on the algorithm and argument names, describe the operation that the each of the following library algorithms performs: ```cpp replace(beg, end, old_val, new_val); // replace the old_elements in the input range as new_elements. replace_if(beg, end, pred, new_val); // replace the elements in the input range which pred is true as new_elements. replace_copy(beg, end, dest, old_val, new_val); // copy the new_elements which is old_elements in the input range into dest. replace_copy_if(beg, end, dest, pred, new_val); // copy the new_elements which pred is true in the input range into dest. ``` ## [Exercise 10.42](ex10_42.cpp) ================================================ FILE: ch10/ex10_01_02.cpp ================================================ // // @author Yue Wang // @date 01.12.2014 // // Exercise 10.1: // The algorithm header defines a function named count that, like find, // takes a pair of iterators and a value.count returns a count of how // often that value appears. // Read a sequence of ints into a vector and print the count of how many // elements have a given value. // // Exercise 10.2: // Repeat the previous program, but read values into a list of strings. // #include #include #include #include #include int main() { // 10.1 std::vector v = { 1, 2, 3, 4, 5, 6, 6, 6, 2 }; std::cout << "ex 10.01: " << std::count(v.cbegin(), v.cend(), 6) << std::endl; // 10.2 std::list l = { "aa", "aaa", "aa", "cc" }; std::cout << "ex 10.02: " << std::count(l.cbegin(), l.cend(), "aa") << std::endl; return 0; } ================================================ FILE: ch10/ex10_03_04.cpp ================================================ // // @author Yue Wang // @date 01.12.2014 // // Exercise 10.3: // Use accumulate to sum the elements in a vector. // // Exercise 10.4: // Assuming v is a vector, what, if anything, // is wrong with calling accumulate(v.cbegin(), v.cend(), 0)? // Check below. // #include #include #include #include #include int main() { // Exercise 10.3 std::vector v = { 1, 2, 3, 4 }; std::cout << "ex 10.03: " << std::accumulate(v.cbegin(), v.cend(), 0) << std::endl; // Exercise 10.4 std::vector vd = { 1.1, 0.5, 3.3 }; std::cout << "ex 10.04: " << std::accumulate(vd.cbegin(), vd.cend(), 0) << std::endl; // ^<-- note here. // @attention // // The ouput is 4 rather than 4.9 as expected. // The reason is std::accumulate is a template function. The third parameter is _Tp __init // When "0" , an integer, had been specified here, the compiler deduced _Tp as // interger.As a result , when the following statments were being excuted : // for (; __first != __last; ++__first) // __init = __init + *__first; // return __init; // all calculation would be converted to integer. return 0; } ================================================ FILE: ch10/ex10_06.cpp ================================================ // // ex10_06.cpp // Exercise 10.6 // // Created by pezy on 12/9/14. // // @Brief Using fill_n, write a program to set a sequence of int values to 0. #include #include #include using std::vector; using std::cout; using std::endl; using std::fill_n; int main() { vector vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; fill_n(vec.begin(), vec.size(), 0); for (auto i : vec) cout << i << " "; cout << endl; } ================================================ FILE: ch10/ex10_07.cpp ================================================ // // ex10_07.cpp // Exercise 10.7 // // Created by pezy on 12/9/14. // // @Brief Determine if there are any errors in the following programs and, if so, correct the error(s) #include #include #include #include using std::vector; using std::cout; using std::endl; using std::list; using std::cin; using std::fill_n; template void print(Sequence const& seq) { for (const auto& i : seq) cout << i << " "; cout << endl; } int main() { // (a) vector vec; list lst; int i; while (cin >> i) lst.push_back(i); copy(lst.cbegin(), lst.cend(), back_inserter(vec)); // Fixed: the vec.begin() was replaced by a back_inserter iterator, capable of // insert new elements automatically at the end of the container. // (b) vector v; v.reserve(10); fill_n(v.begin(), 10, 0); // ^ (b)No error, but not any sense. v.size() still equal zero. // Fixed: 1. use `v.resize(10);` // or 2. use `fill_n(std::back_inserter(v), 10, 0)` print(v); print(vec); } ================================================ FILE: ch10/ex10_09.cpp ================================================ // // @author @Yue Wang @shbling @pezy @zzzkl @Yue Wang // @date 01.12.2014 Jun 2015 // // Exercise 10.9: // Implement your own version of elimDups. Test your program by printing // the vector after you read the input, after the call to unique, and after // the call to erase. // #include #include #include #include // print containers like vector, deque, list, etc. template auto println(Sequence const& seq) -> std::ostream& { for (auto const& elem : seq) std::cout << elem << " "; return std::cout << std::endl; } auto eliminate_duplicates(std::vector &vs) -> std::vector& { std::sort(vs.begin(), vs.end()); println(vs); auto new_end = std::unique(vs.begin(), vs.end()); println(vs); vs.erase(new_end, vs.end()); return vs; } int main() { std::vector vs{ "a", "v", "a", "s", "v", "a", "a" }; println(vs); println(eliminate_duplicates(vs)); return 0; } ================================================ FILE: ch10/ex10_11.cpp ================================================ // @Alan // Exercise 10.11: // Write a program that uses stable_sort and isShorter to sort a vector passed // to your version of elimDups. // Print the vector to verify that your program is correct. // #include #include #include #include #include #include // print a container like vector, deque, list, etc. template inline std::ostream& println(Sequence const& seq) { for(auto const& elem : seq) std::cout << elem << " "; std::cout << std::endl; return std::cout; } inline bool is_shorter(std::string const& lhs, std::string const& rhs) { return lhs.size() < rhs.size(); } void elimdups(std::vector &vs) { std::sort(vs.begin(), vs.end()); auto new_end = std::unique(vs.begin(), vs.end()); vs.erase(new_end, vs.end()); } int main() { std::vector v{ "1234", "1234", "1234", "Hi", "alan", "wang" }; elimdups(v); std::stable_sort(v.begin(), v.end(), is_shorter); std::cout << "ex10.11 :\n"; println(v); return 0; } ================================================ FILE: ch10/ex10_12.cpp ================================================ // @Yue Wang // Exercise 10.12: // Write a function named compareIsbn that compares the isbn() members of two Sales_data objects. // Use that function to sort a vector that holds Sales_data objects. // #include #include #include #include #include "../ch07/ex7_26.h" // Sales_data class. inline bool compareIsbn(const Sales_data &sd1, const Sales_data &sd2) { return sd1.isbn().size() < sd2.isbn().size(); } int main() { Sales_data d1("aa"), d2("aaaa"), d3("aaa"), d4("z"), d5("aaaaz"); std::vector v{ d1, d2, d3, d4, d5 }; // @note the elements the iterators pointing to // must match the parameters of the predicate. std::sort(v.begin(), v.end(), compareIsbn); for(const auto &element : v) std::cout << element.isbn() << " "; std::cout << std::endl; return 0; } ================================================ FILE: ch10/ex10_13.cpp ================================================ // @Yue Wang // // Exercise 10.13: // The library defines an algorithm named partition that takes a predicate // and partitions the container so that values for which the predicate is // true appear in the first part and those for which the predicate is false // appear in the second part. The algorithm returns an iterator just past // the last element for which the predicate returned true. Write a function // that takes a string and returns a bool indicating whether the string has // five characters or more. Use that function to partition words. Print the // elements that have five or more characters. // #include #include #include #include bool predicate(const std::string &s) { return s.size() >= 5; } int main() { auto v = std::vector{ "a", "as", "aasss", "aaaaassaa", "aaaaaabba", "aaa" }; auto pivot = std::partition(v.begin(), v.end(), predicate); for (auto it = v.cbegin(); it != pivot; ++it) std::cout << *it << " "; std::cout << std::endl; return 0; } ================================================ FILE: ch10/ex10_16.cpp ================================================ // // @author @Yue Wang @pezy // @date 02.12.2014 // // Exercise 10.16: // Write your own version of the biggies function using lambdas. // #include #include #include #include // from ex 10.9 void elimdups(std::vector &vs) { std::sort(vs.begin(), vs.end()); auto new_end = std::unique(vs.begin(), vs.end()); vs.erase(new_end, vs.end()); } void biggies(std::vector &vs, std::size_t sz) { using std::string; elimdups(vs); // sort by size, but maintain alphabetical order for same size. std::stable_sort(vs.begin(), vs.end(), [](string const& lhs, string const& rhs){ return lhs.size() < rhs.size(); }); // get an iterator to the first one whose size() is >= sz auto wc = std::find_if(vs.begin(), vs.end(), [sz](string const& s){ return s.size() >= sz; }); // print the biggies std::for_each(wc, vs.end(), [](const string &s){ std::cout << s << " "; }); } int main() { // ex10.16 std::vector v { "1234","1234","1234","hi~", "alan", "alan", "cp" }; std::cout << "ex10.16: "; biggies(v, 3); std::cout << std::endl; return 0; } ================================================ FILE: ch10/ex10_17.cpp ================================================ // @pezy // // Exercise 10.17 // Rewrite exercise 10.12 from 10.3.1 (p. 387) // to use a lambda in the call to sort instead of the compareIsbn function. // // @See 7.26, 10.12 #include #include #include #include #include "../ch07/ex7_26.h" // Sales_data class. int main() { Sales_data d1("aa"), d2("aaaa"), d3("aaa"), d4("z"), d5("aaaaz"); std::vector v{ d1, d2, d3, d4, d5 }; std::sort(v.begin(), v.end(), [](const Sales_data &sd1, const Sales_data &sd2){ return sd1.isbn().size() < sd2.isbn().size(); }); for(const auto &element : v) std::cout << element.isbn() << " "; std::cout << std::endl; return 0; } ================================================ FILE: ch10/ex10_18_19.cpp ================================================ // // @author @Yue Wang @pezy // @date 12.10.2014 // // Exercise 10.18: // Rewrite biggies to use partition instead of find_if. // // Exercise 10.19: // Rewrite the previous exercise to use stable_partition, which like // stable_sort maintains the original element order in the paritioned // sequence. // #include #include #include #include // from ex 10.9 void elimdups(std::vector &vs) { std::sort(vs.begin(), vs.end()); auto new_end = std::unique(vs.begin(), vs.end()); vs.erase(new_end, vs.end()); } // ex10.18 void biggies_partition(std::vector &vs, std::size_t sz) { elimdups(vs); auto pivot = partition(vs.begin(), vs.end(), [sz](const std::string &s){ return s.size() >= sz;} ); for(auto it = vs.cbegin(); it != pivot; ++it) std::cout << *it << " "; } // ex10.19 void biggies_stable_partition(std::vector &vs, std::size_t sz) { elimdups(vs); auto pivot = stable_partition(vs.begin(), vs.end(), [sz](const std::string& s){ return s.size() >= sz; }); for(auto it = vs.cbegin(); it != pivot; ++it) std::cout << *it << " "; } int main() { // ex10.18 std::vector v{ "the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle" }; std::cout << "ex10.18: "; std::vector v1(v); biggies_partition(v1, 4); std::cout << std::endl; // ex10.19 std::cout << "ex10.19: "; std::vector v2(v); biggies_stable_partition(v2, 4); std::cout << std::endl; return 0; } ================================================ FILE: ch10/ex10_20_21.cpp ================================================ // // @author @Yue Wang @shbling @pezy @zzzkl @agyild // @date 04.01.2018 // // Exercise 10.20: // The library defines an algorithm named count_if. Like find_if, this function takes // a pair of iterators denoting an input range and a predicate that it applies to each // element in the given range. count_if returns a count of how often the predicate is // true. Use count_if to rewrite the portion of our program that counted how many words // are greater than length 6. // // Exercise 10.21: // Write a lambda that captures a local int variable and decrements that variable until // it reaches 0. Once the variable is 0 additional calls should no longer decrement the // variable. The lambda should return a bool that indicates whether the captured variable is 0. // #include #include #include #include using std::vector; using std::count_if; using std::string; // Exercise 10.20 std::size_t bigerThan6(vector const& v) { return count_if(v.cbegin(), v.cend(), [](string const& s){ return s.size() > 6; }); } int main() { // ex10.20 vector v{ "alan","moophy","1234567","1234567","1234567","1234567" }; std::cout << "ex10.20: " << bigerThan6(v) << std::endl; // ex10.21 int i = 7; auto check_and_decrement = [&i]() { return i > 0 ? !--i : !i; }; std::cout << "ex10.21: "; while(!check_and_decrement()) std::cout << i << " "; std::cout << i << std::endl; return 0; } ================================================ FILE: ch10/ex10_22.cpp ================================================ // // ex10_22.cpp // Exercise 10.22 // // Created by pezy on 12/11/14. // Updated by pezy on 10/12/15. // // @Brief Rewrite the program to count words of size 6 or less using // functions in place of the lambdas. #include #include #include #include #include using std::string; using namespace std::placeholders; bool isLesserThanOrEqualTo6(const string &s, string::size_type sz) { return s.size() <= sz; } int main() { std::vector authors{ "Mooophy", "pezy", "Queequeg90", "shbling", "evan617" }; std::cout << count_if(authors.cbegin(), authors.cend(), bind(isLesserThanOrEqualTo6, _1, 6)); } ================================================ FILE: ch10/ex10_24.cpp ================================================ // // @author @Yue Wang @shbling @Soyn @Yue Wang // // Exercise 10.24: // Use bind and check_size to find the first element in a vector of ints that has a value greater // than the length of a specified string value. // // Discussion over this exercise on StackOverflow // http://stackoverflow.com/questions/20539406/what-type-does-stdfind-if-not-return // #include #include #include #include using std::cout; using std::endl; using std::string; using std::vector; using std::find_if; using std::bind; using std::size_t; auto check_size(string const& str, size_t sz) { return str.size() < sz; } int main() { vector vec{ 0, 1, 2, 3, 4, 5, 6, 7 }; string str("123456"); auto result = find_if(vec.begin(), vec.end(), bind(check_size, str, std::placeholders::_1)); if (result != vec.end()) cout << *result << endl; else cout << "Not found" << endl; return 0; } ================================================ FILE: ch10/ex10_25.cpp ================================================ // // ex10_25.cpp // Exercise 10.25 // // Created by pezy on 12/11/14. // // @Brief In the exercises for 10.3.2 (p.392) you wrote a version of biggies that uses partition. // Rewrite that function to use check_size and bind. #include #include #include #include #include using std::string; using std::vector; using namespace std::placeholders; void elimdups(vector &vs) { std::sort(vs.begin(), vs.end()); vs.erase(unique(vs.begin(), vs.end()), vs.end()); } bool check_size(const string &s, string::size_type sz) { return s.size() >= sz; } void biggies(vector &words, vector::size_type sz) { elimdups(words); auto iter = std::stable_partition(words.begin(), words.end(), bind(check_size, _1, sz)); for_each(words.begin(), iter, [](const string &s){ std::cout << s << " "; }); } int main() { std::vector v{ "the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle" }; biggies(v, 4); } ================================================ FILE: ch10/ex10_27.cpp ================================================ // // ex10_27.cpp // Exercise 10.27 // // Created by pezy on 12/13/14. // // In addition to unique, the library defines function named unique_copy that // takes a third iterator denoting a destination into which to copy the unique elements. // Write a program that uses unique_copy to copy the unique elements from // a vector into an initially empty list. #include #include #include #include #include int main() { std::vector vec{ 1, 1, 3, 3, 5, 5, 7, 7, 9 }; std::list lst; std::unique_copy(vec.begin(), vec.end(), back_inserter(lst)); for (auto i : lst) std::cout << i << " "; std::cout << std::endl; } ================================================ FILE: ch10/ex10_28.cpp ================================================ // // ex10_28.cpp // Exercise 10.28 // // Created by pezy on 12/13/14. // // Copy a vector that holds the values from 1 to 9 inclusive, into three other containers. // Use an inserter, a back_inserter, and a front_inserter, respectivly to add elements to these containers. // Predict how the output sequence varies by the kind of inserter and verify your predictions // by running your programs. #include #include #include #include #include using std::list; using std::copy; using std::cout; using std::endl; template void print(Sequence const& seq) { for (const auto& i : seq) std::cout << i << " "; std::cout << std::endl; } int main() { std::vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // uses inserter list lst1; copy(vec.cbegin(), vec.cend(), inserter(lst1, lst1.begin())); print(lst1); // uses back_inserter list lit2; copy(vec.cbegin(), vec.cend(), back_inserter(lit2)); print(lit2); // uses front_inserter list lst3; copy(vec.cbegin(), vec.cend(), front_inserter(lst3)); print(lst3); } ================================================ FILE: ch10/ex10_29.cpp ================================================ // // ex10_29.cpp // Exercise 10.29 // // Created by pezy on 12/13/14. // // Write a program using stream iterators to read a text file into a vector of strings. #include #include #include #include #include using std::string; int main() { std::ifstream ifs("../data/book.txt"); std::istream_iterator in(ifs), eof; std::vector vec; std::copy(in, eof, back_inserter(vec)); // output std::copy(vec.cbegin(), vec.cend(), std::ostream_iterator(std::cout, "\n")); } ================================================ FILE: ch10/ex10_30.cpp ================================================ // // ex10_30.cpp // Exercise 10.30 // // Created by pezy on 12/13/14. // // Use stream iterators, sort, and copy to read a sequence of integers from the standard input, // sort them, and then write them back to the standard output. #include #include #include #include int main() { std::vector vec(std::istream_iterator(std::cin), std::istream_iterator()); std::sort(vec.begin(), vec.end()); std::copy(vec.cbegin(), vec.cend(), std::ostream_iterator(std::cout, " ")); } ================================================ FILE: ch10/ex10_31.cpp ================================================ // // ex10_31.cpp // Exercise 10.31 // // Created by pezy on 12/13/14. // // Update the program from the previous exercise so that it prints only the unique elements. // Your program should use unqiue_copy #include #include #include #include int main() { std::istream_iterator in_iter(std::cin), eof; std::vector vec; while (in_iter != eof) vec.push_back(*in_iter++); std::sort(vec.begin(), vec.end()); std::unique_copy(vec.cbegin(), vec.cend(), std::ostream_iterator(std::cout, " ")); } ================================================ FILE: ch10/ex10_32.cpp ================================================ // // ex10_32.cpp // Exercise 10.32 // // Created by pezy on 12/13/14. // // Rewrite the bookstore problem from 1.6 (p. 24) using a vector to hold the transactions // and various algorithms to do the processing. // Use sort with your compareIsbn function from 10.3.1 (p. 387) to arrange the transactions in order, // and then use find and accumulate to do the sum. #include #include #include #include #include #include "../include/Sales_item.h" bool compareIsbn(const Sales_item &item1, const Sales_item &item2) { return item1.isbn() < item2.isbn(); } int main() { std::istream_iterator in_iter(std::cin), in_eof; std::vector vec; while (in_iter != in_eof) vec.push_back(*in_iter++); sort(vec.begin(), vec.end(), compareIsbn); for (auto beg = vec.cbegin(), end = beg; beg != vec.cend(); beg = end) { end = find_if(beg, vec.cend(), [beg](const Sales_item &item){ return item.isbn() != beg->isbn(); }); std::cout << std::accumulate(beg, end, Sales_item(beg->isbn())) << std::endl; } } ================================================ FILE: ch10/ex10_33.cpp ================================================ // // ex10_33.cpp // Exercise 10.33 // // Created by pezy on 12/13/14. // // Write a program that takes the names of an input file and two output files. // The input file should hold integers. Using an istream_iterator read the input file. // Using ostream_iterators, write the odd numbers into the first output file. // Each value should be followed by a space.Write the even numbers into the second file. // Each of these values should be placed on a separate line. // // Run: ./a.out "../data/input.txt" "../data/odd.txt" "../data/even.txt" #include #include #include int main(int argc, char **argv) { if (argc != 4) return -1; std::ifstream ifs(argv[1]); std::ofstream ofs_odd(argv[2]), ofs_even(argv[3]); std::istream_iterator in(ifs), in_eof; std::ostream_iterator out_odd(ofs_odd, " "), out_even(ofs_even, "\n"); std::for_each(in, in_eof, [&out_odd, &out_even](const int i){ *(i & 0x1 ? out_odd : out_even)++ = i; }); return 0; } ================================================ FILE: ch10/ex10_34_35_36_37.cpp ================================================ // @Alan @pezy // // Exercise 10.34: // Use reverse_iterators to print a vector in reverse order. // // Exercise 10.35: // Now print the elements in reverse order using ordinary iterators. // // Exercise 10.36: // Use find to find the last element in a list of ints with value 0. // // Exercise 10.37: // Given a vector that has ten elements, copy the elements from positions // 3 through 7 in reverse order to a list. // #include #include #include #include #include int main() { std::vector vec = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 10.34 for (auto rit = vec.crbegin(); rit != vec.crend(); ++rit) std::cout << *rit << " "; std::cout << std::endl; // 10.35 for (auto it = std::prev(vec.cend()); true; --it) { std::cout << *it << " "; if (it == vec.cbegin()) break; } std::cout << std::endl; // 10.36 std::list lst = { 1, 2, 3, 4, 0, 5, 6 }; auto found_0 = std::find(lst.crbegin(), lst.crend(), 0); std::cout << std::distance(found_0, lst.crend()) << std::endl; // 10.37 std::list ret_lst(8 - 3); std::copy(vec.cbegin() + 3, vec.cbegin() + 8, ret_lst.rbegin()); // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 // ^ ^ // rend rbegin // @note: std::copy copies the range [first, last) into result. // hence, the arguments here denote: // [7 6 5 4 3 2) // ^ this one is specified but not included. for (auto i : ret_lst) std::cout << i << " "; std::cout << std::endl; } ================================================ FILE: ch10/ex10_42.cpp ================================================ // @Alan @pezy // // Exercise 10.42: // Reimplement the program that eliminated duplicate words that // we wrote in § 10.2.3 (p. 383) to use a list instead of a vector. // #include #include #include using std::string; using std::list; void elimDups(list &words) { words.sort(); words.unique(); } int main() { list l = { "aa", "aa", "aa", "aa", "aasss", "aa" }; elimDups(l); for (const auto& e : l) std::cout << e << " "; std::cout << std::endl; } ================================================ FILE: ch11/README.md ================================================ # Chapter 11. Associative Containers ## Exercise 11.1: >Describe the differences between a map and a vector. `map` is an associative container whereas `vector` is a sequence container ## Exercise 11.2: >Give an example of when each of list, vector, deque, map, and set might be most useful. - list : anytime when a doubly-linked list is required. - vector : anytime when a dynamic array is required. - deque : [An answer from SO](http://stackoverflow.com/questions/3880254/why-do-we-need-deque-data-structures-in-the-real-world). - map : dictionary. - set : when to keep elements sorted and unique. ## [Exercise 11.3 and 11.4](ex11_3_4.cpp) ## Exercise 11.5: >Explain the difference between a map and a set. When might you use one or the other? [A nice answer on SO](http://stackoverflow.com/questions/16286714/advantages-of-stdset-vs-vectors-or-maps) ## Exercise 11.6: >Explain the difference between a set and a list. When might you use one or the other? [list vs set](http://stackoverflow.com/questions/2302681/c-stl-list-vs-set) ## [Exercise 11.7](ex11_7.cpp) ## [Exercise 11.8](ex11_8.cpp) ## [Exercise 11.9 and 11.10](ex11_9_10.cpp) ## [Exercise 11.11](ex11_11.cpp) ## [Exercise 11.12 and 11.13](ex11_12_13.cpp) ## [Exercise 11.14](ex11_14.cpp) ## Exercise 11.15: >What are the mapped_type, key_type, and value_type of a map from int to vector< int >? - mapped_type : vector< int > - key_type : int - value_type : std::pair< const int, vector > ## Exercise 11.16: >Using a map iterator write an expression that assigns a value to an element. ```cpp std::map map; map[25] = "Alan"; std::map::iterator it = map.begin(); it->second = "Wang"; ``` ## Exercise 11.17: >Assuming c is a multiset of strings and v is a vector of strings, explain the following calls. Indicate whether each call is legal: ```cpp copy(v.begin(), v.end(), inserter(c, c.end())); // legal copy(v.begin(), v.end(), back_inserter(c)); // illegal, no `push_back` in `set`. copy(c.begin(), c.end(), inserter(v, v.end())); // legal. copy(c.begin(), c.end(), back_inserter(v)); // legal. ``` ## Exercise 11.18: >Write the type of map_it from the loop on page 430 without using auto or decltype. ```cpp std::map::const_iterator; ``` ## Exercise 11.19: >Define a variable that you initialize by calling begin() on the multiset named bookstore from 11.2.2 (p. 425). Write the variable’s type without using auto or decltype. ```cpp using compareType = bool (*)(const Sales_data &lhs, const Sales_data &rhs); std::multiset bookstore(compareIsbn); std::multiset::iterator c_it = bookstore.begin(); ``` ## [Exercise 11.20](ex11_20.cpp) ## Exercise 11.21: >Assuming word_count is a map from string to size_t and word is a string, explain the following loop: ```cpp while (cin >> word) ++word_count.insert({ word, 0 }).first->second; ``` This code can be explained like this pseudocode: ```python while reading into word if word_count has key word: word_count[word] += 1 else: word_count[word] = 0 word_count[word] += 1 ``` ## Exercise 11.22: >Given a `map>`, write the types used as an argument and as the return value for the version of insert that inserts one element. ```cpp std::pair> // argument std::pair>::iterator, bool> // return ``` ## [Exercise 11.23](ex11_23.cpp) ## [Exercise 11.24 ~ 11.26](ex11_24_25_26.cpp) ## [Exercise 11.27 ~ 11.30](ex11_27_28_29_30.cpp) ## [Exercise 11.31](ex11_31.cpp) ## [Exercise 11.32](ex11_32.cpp) ## [Exercise 11.33](ex11_33.cpp) ## Exercise 11.34: >What would happen if we used the subscript operator instead of find in the transform function? Say the code has been changed like below: ```cpp const string& transform(const string &s, const map &m) { return m[s]; } ``` The above code won't compile because the subscript operator might insert an element (when the element with the key s is not found), and we may use subscript only on a map that is not const. ## Exercise 11.35: >In buildMap, what effect, if any, would there be from rewriting `trans_map[key] = value.substr(1);` as `trans_map.insert({ key, value.substr(1) })`? - use subscript operator: if a word does appear multiple times, our loops will put the **last** corresponding phrase into trans_map - use `insert`: if a word does appear multiple times, our loops will put the **first** corresponding phrase into trans_map ## Exercise 11.36: >Our program does no checking on the validity of either input file. In particular, it assumes that the rules in the transformation file are all sensible. What would happen if a line in that file has a key, one space, and then the end of the line? Predict the behavior and then check it against your version of the program. If so, a key-value pair will be `{key, " "}`(" ".size() !> 1), which cannot be added into the map. As a result, the key would not be replaced with any string. ## Exercise 11.37: >What are the advantages of an unordered container as compared to the ordered version of that container? What are the advantages of the ordered version? [A summary](http://www.cs.fsu.edu/~lacher/courses/COP4531/fall13/lectures/containers2/slide04.html) ## [Exercise 11.38](ex11_38.cpp) ================================================ FILE: ch11/ex11_11.cpp ================================================ // // ex11_11.cpp // Exercise 11.11 // // Created by pezy on 12/15/14. // Refactored by Yue Wang Oct,2015 // // Redefine bookstore without using decltype. // #include "../ch07/ex7_26.h" #include auto less(Sales_data const& lhs, Sales_data const& rhs) { return lhs.isbn() < rhs.isbn(); } int main() { using Less = bool (*)(Sales_data const&, Sales_data const&); std::multiset bookstore(less); return 0; } ================================================ FILE: ch11/ex11_12_13.cpp ================================================ // // ex11_12_13.cpp // Exercise 11.12 11.13 // // Created by pezy on 12/15/14. // // Write a program to read a sequence of strings and ints, // storing each into a pair. Store the pairs in a vector. // // There are at least three ways to create the pairs in the program for the previous exercise. // Write three versions of that program, creating the pairs in each way. // Explain which form you think is easiest to write and understand, and why. #include #include #include #include int main() { std::vector> vec; std::string str; int i; while (std::cin >> str >> i) vec.push_back(std::pair(str, i)); //vec.push_back(std::make_pair(str, i)); //vec.push_back({ str, i }); //vec.emplace_back(str, i); //!! easiest way. for (const auto &p : vec) std::cout << p.first << ":" << p.second << std::endl; } ================================================ FILE: ch11/ex11_14.cpp ================================================ // // @Yue Wang // // Exercise 11.14: // Extend the map of children to their family name that you wrote for the // exercises in § 11.2.1 (p. 424) by having the vector store a pair that // holds a child’s name and birthday. // // Exercise 11.7: // Define a map for which the key is the family’s last name and // the value is a vector of the children’s names. Write code to // add new families and to add new children to an existing family. // #include #include #include #include using std::ostream; using std::cout; using std::cin; using std::endl; using std::string; using std::make_pair; using std::pair; using std::vector; using std::map; class Families { public: using Child = pair; using Children = vector; using Data = map; auto add(string const& last_name, string const& first_name, string birthday) { auto child = make_pair(first_name, birthday); _data[last_name].push_back(child); } auto print() const { for (auto const& pair : _data) { cout << pair.first << ":\n" ; for (auto const& child : pair.second) cout << child.first << " " << child.second << endl; cout << endl; } } private: Data _data; }; int main() { Families families; auto msg = "Please enter last name, first name and birthday:\n"; for (string l, f, b; cout << msg, cin >> l >> f >> b; families.add(l, f, b)); families.print(); return 0; } ================================================ FILE: ch11/ex11_20.cpp ================================================ // @Yue Wang @pezy // // Exercise 11.20: // Rewrite the word-counting program from § 11.1 (p. 421) to use insert instead // of subscripting. Which program do you think is easier to write and read? // Explain your reasoning. // #include #include #include using std::string; using std::map; using std::cin; using std::cout; int main() { map counts; for(string word; cin >> word;) { auto result = counts.insert({ word, 1 }); if(!result.second) ++result.first->second; } for(auto const& count : counts) cout << count.first << " " << count.second << ((count.second > 1) ? " times\n" : " time\n"); } ================================================ FILE: ch11/ex11_23.cpp ================================================ // // ex11_23.cpp // Exercise 11.23 // // Created by pezy on 12/16/14. // // Rewrite the map that stored vectors of children’s names with a key that is the family last name for the exercises // in 11.2.1 (p. 424) to use a multimap. #include #include #include using std::string; using std::multimap; using std::cin; using std::endl; int main() { multimap families; for (string lname, cname; cin >> cname >> lname; families.emplace(lname, cname)); for (auto const& family : families) std::cout << family.second << " " << family.first << endl; } ================================================ FILE: ch11/ex11_24_25_26.cpp ================================================ // @Yue Wang // // Exercise 11.24: // What does the following program do? // map m; // m[0] = 1; // add a key-value pair { 0, 1 } into the map. // // Exercise 11.25: // Contrast the following program with the one in the previous exercise // vector v; // v[0] = 1; // UB, since it's trying to dereference an item out of range. // // Exercise 11.26: // What type can be used to subscript a map? What type does the subscript // operator return? Give a concrete example—that is, define a map and then // write the types that can be used to subscript the map and the type that // would be returned from the subscript operator. // #include #include #include #include int main() { // ex11.26 std::map m = { { 1,"ss" },{ 2,"sz" } }; using KeyType = std::map::key_type; std::cout << "type to subscript: " << typeid(KeyType).name() << std::endl; std::cout << "returned from the subscript operator: " << typeid(decltype(m[1])).name() << std::endl; return 0; } ================================================ FILE: ch11/ex11_27_28_29_30.cpp ================================================ // // @Yue Wang // // Exercise 11.27: // What kinds of problems would you use count to solve? // When might you use find instead? // I would use count to deal with multimap or multi multiset. // As for the associative container that have unique key, I would use find instead of count. // // Exercise 11.28: // Define and initialize a variable to hold the result of // calling find on a map from string to vector of int. // // Exercise 11.29: // What do upper_bound, lower_bound, and equal_range return // when you pass them a key that is not in the container? // If the element is not in the multimap, then lower_bound // and upper_bound will return equal iterators; both will // refer to the point at which the key can be inserted without // disrupting the order. // // If no matching element is found, then both the first and // second iterators refer to the position where this key can // be inserted. // // Exercise 11.30: // Explain the meaning of the operand pos.first->second used // in the output expression of the final program in this section. // cout << pos.first->second << endl; // ^^^^^^^^^^^^^^^^^ // pos a pair // pos.first the iterator refering to the first element with the matching key // pos.first->second the value part of the key-value of the first element with the matching key #include #include #include #include #include int main() { std::map> m{ { "Alan",{ 1,2,3,4,5, } },{ "John",{ 1,5,6,7,8 } } }; // ex11.28 std::map>::iterator it = m.find("Alan"); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return 0; } ================================================ FILE: ch11/ex11_31.cpp ================================================ // // ex11_31.cpp // Exercise 11.31 // // Created by pezy on 12/17/14. // // Write a program that defines a multimap of authors and their works. // Use **find** to find **an element** in the multimap and erase that element. // Be sure your program works correctly if the element you look for is not in the map. #include #include #include using std::string; int main() { std::multimap authors{ { "alan", "DMA" }, { "pezy", "LeetCode" }, { "alan", "CLRS" }, { "wang", "FTP" }, { "pezy", "CP5" }, { "wang", "CPP-Concurrency" } }; // want to delete an element that author is [Alan], work is [112]. string author = "pezy"; string work = "CP5"; auto found = authors.find(author); auto count = authors.count(author); while (count) { if (found->second == work) { authors.erase(found); break; } ++found; --count; } for (const auto &author : authors) std::cout << author.first << " " << author.second << std::endl; } ================================================ FILE: ch11/ex11_32.cpp ================================================ // // ex11_32.cpp // Exercise 11.32 // // Created by pezy on 12/17/14. // // Using the multimap from the previous exercise, write a program to print the list of **authors and their works** alphabetically. #include #include #include #include using std::string; int main() { std::multimap authors{ { "alan", "DMA" }, { "pezy", "LeetCode" }, { "alan", "CLRS" }, { "wang", "FTP" }, { "pezy", "CP5" }, { "wang", "CPP-Concurrency" } }; std::map> order_authors; for (const auto &author : authors) order_authors[author.first].insert(author.second); for (const auto &author : order_authors) { std::cout << author.first << ": "; for (const auto &work : author.second) std::cout << work << " "; std::cout << std::endl; } } ================================================ FILE: ch11/ex11_33.cpp ================================================ // // ex11_33.cpp // Exercise 11.33 // // Created by pezy on 12/18/14. // // Implement your own version of the word-transformation program. #include #include #include #include #include using std::string; using std::ifstream; std::map buildMap(ifstream &map_file) { std::map trans_map; for (string key, value; map_file >> key && getline(map_file, value); ) if (value.size() > 1) trans_map[key] = value.substr(1).substr(0, value.find_last_not_of(' ')); return trans_map; } const string & transform(const string &s, const std::map &m) { auto map_it = m.find(s); return map_it == m.cend() ? s : map_it->second; } void word_transform(ifstream &map, ifstream &input) { auto trans_map = buildMap(map); for (string text; getline(input, text); ) { std::istringstream iss(text); for (string word; iss >> word; ) std::cout << transform(word, trans_map) << " "; std::cout << std::endl; } } int main() { ifstream ifs_map("../data/word_transformation_bad.txt"), ifs_content("../data/given_to_transform.txt"); if (ifs_map && ifs_content) word_transform(ifs_map, ifs_content); else std::cerr << "can't find the documents." << std::endl; } ================================================ FILE: ch11/ex11_38.cpp ================================================ // // ex11_38.cpp // Exercise 11.38 // // Created by pezy on 12/18/14. // // Rewrite the word-counting (11.1, p. 421) and word-transformation (11.3.6, p. 440) programs to use an unordered_map. #include #include #include #include #include #include using std::string; void wordCounting() { std::unordered_map word_count; for (string word; std::cin >> word; ++word_count[word]); for (const auto &w : word_count) std::cout << w.first << " occurs " << w.second << (w.second > 1 ? "times" : "time") << std::endl; } void wordTransformation() { std::ifstream ifs_map("../data/word_transformation.txt"), ifs_content("../data/given_to_transform.txt"); if (!ifs_map || !ifs_content) { std::cerr << "can't find the documents." << std::endl; return; } std::unordered_map trans_map; for (string key, value; ifs_map >> key && getline(ifs_map, value); ) if (value.size() > 1) trans_map[key] = value.substr(1).substr(0, value.find_last_not_of(' ')); for (string text, word; getline(ifs_content, text); std::cout << std::endl) for (std::istringstream iss(text); iss >> word; ) { auto map_it = trans_map.find(word); std::cout << (map_it == trans_map.cend() ? word : map_it->second) << " "; } } int main() { //wordCounting(); wordTransformation(); } ================================================ FILE: ch11/ex11_3_4.cpp ================================================ // @Yue Wang Aug, 2015 // // Exercise 11.3: // Write your own version of the word-counting program. // // Exercise 11.4: // Extend your program to ignore case and punctuation. // For example, “example.” “example, ” and “Example” should // all increment the same counter. // #include #include #include #include #include using std::string; using std::cin; using std::cout; using std::remove_if; using Map = std::map; //for ex11.3 auto count() { Map counts; for (string w; cin >> w; ++counts[w]); return counts; } //for ex11.4 auto strip(string& str) -> string const& { for (auto& ch : str) ch = tolower(ch); str.erase(remove_if(str.begin(), str.end(), ispunct), str.end()); return str; } //for ex11.4 auto strip_and_count() { Map counts; for (string w; cin >> w; ++counts[strip(w)]); return counts; } auto print(Map const& m) { for (auto const& kv : m) cout << kv.first << " : " << kv.second << "\n"; } int main() { cout << "[ex11.3] Enter a few words please:\n"; print(count()); cin.clear(); cout << "[ex11.4] Enter a few words please:\n"; print(strip_and_count()); return 0; } ================================================ FILE: ch11/ex11_7.cpp ================================================ // Yue Wang Oct, 2015 // // Exercise 11.7: // Define a map for which the key is the family’s last name and // the value is a vector of the children’s names. Write code to // add new families and to add new children to an existing family. // #include #include #include #include #include using std::string; using std::vector; using std::map; using std::cin; using std::cout; using Families = map>; auto make_families() { Families families; for (string ln; cout << "Last name:\n", cin >> ln && ln != "@q";) for (string cn; cout << "|-Children's names:\n", cin >> cn && cn != "@q";) families[ln].push_back(cn); return families; } auto print(Families const& families) { for (auto const& family : families) { cout << family.first << ":\n"; for (auto const& child : family.second) cout << child << " "; cout << "\n"; } } int main() { print(make_families()); return 0; } ================================================ FILE: ch11/ex11_8.cpp ================================================ // @Yue Wang Sep, 2015 // // Exercise 11.8: // Write a program that stores the excluded words in a vector // instead of in a set. What are the advantages to using a set? // copied from the post on stack overflow: // 1.No matter what elements you add or remove (unless you add // a duplicate, which is not allowed in a set), it will always // be ordered. // 2.A vector has exactly and only the ordering you explicitly // give it. Items in a vector are where you put them. If you put // them in out of order, then they're out of order; you now need // to sort the container to put them back in order. // 3.However, if you are constantly inserting and removing items // from the container, vector will run into many issues. // 4.The time it takes to insert an item into a vector is proportional // to the number of items already in the vector. The time it takes // to insert an item into a set is proportional to the log of the // number of items. If the number of items is large, that's a huge // difference. Log(100, 000) is 17; that's a major speed improvement. // The same goes for removal. // // http://stackoverflow.com/questions/8686725/what-is-the-difference-between-stdset-and-stdvector // #include #include #include #include int main() { std::vector exclude = { "aa", "bb", "cc", "dd", "ee", "ff" }; for (std::string word; std::cout << "Enter plz:\n", std::cin >> word; ) { auto is_excluded = std::binary_search(exclude.cbegin(), exclude.cend(), word); auto reply = is_excluded ? "excluded" : "not excluded"; std::cout << reply << std::endl; } return 0; } ================================================ FILE: ch11/ex11_9_10.cpp ================================================ // @Alan // // Exercise 11.9: // Define a map that associates words with a list of // line numbers on which the word might occur. // // Exercise 11.10: // Could we define a map from vector::iterator // to int? What about from list::iterator to int? // In each case, if not, why not? // vector::iterator to int is ok , because < is defined // list::iterator to int is not ok, as no < is defined. #include #include #include #include #include #include int main() { // ex 11.9 std::map> m; // ex 11.10 // can be declared. std::map::iterator, int> mv; std::map::iterator, int> ml; std::vector vi; mv.insert(std::pair::iterator, int>(vi.begin(), 0)); // but when using this one the compiler complained that // error: no match for 'operator<' in '__x < __y' std::list li; ml.insert(std::pair::iterator, int>(li.begin(), 0)); return 0; } ================================================ FILE: ch12/README.md ================================================ # Chapter 12. Dynamic Memory ## Exercise 12.1: >How many elements do b1 and b2 have at the end of this code? ```cpp StrBlob b1; { StrBlob b2 = { "a", "an", "the" }; b1 = b2; b2.push_back("about"); } ``` At the end of this code, * `b1` holds 4 elements; * `b2` has been destroyed automatically. So it's meaningless to say how many elements in `b2`. ## Exercise 12.2 [StrBlob](ex12_02.h) | [TEST](ex12_02_TEST.cpp) ## Exercise 12.3: >Does this class need const versions of push_back and pop_back? If so, add them. If not, why aren’t they needed? You can certainly do this if you want to, but there doesn't seem to be any logical reason. The compiler doesn't complain because this doesn't modify data (which is a pointer) but rather the thing data points to, which is perfectly legal to do with a const pointer. by David Schwartz. ----- Discussion over this exercise on [Stack Overflow](http://stackoverflow.com/questions/20725190/operating-on-dynamic-memory-is-it-meaningful-to-overload-a-const-memeber-functi) Discussion over this exercise more on [douban](http://www.douban.com/group/topic/61573279/)(chinese) ## Exercise 12.4: >In our check function we didn’t check whether i was greater than zero. Why is it okay to omit that check? Because the type of `i` is `std::vector::size_type` which is an `unsigned`.When any argument less than 0 is passed in, it will convert to a number greater than 0. In short `std::vector::size_type` will ensure it is a positive number or 0. ## Exercise 12.5: >We did not make the constructor that takes an initializer_list explicit (7.5.4, p. 296). Discuss the pros and cons of this design choice. [@Mooophy](https://github.com/Mooophy): keyword `explicit` prevents automatic conversion from an `initializer_list` to `StrBlob`. This design choice would easy to use but hard to debug. [@pezy](https://github.com/pezy): **Pros** - The compiler will not use this constructor **in an automatic conversion**. - We can realize clearly which class we have used. **Cons** - We always uses the constructor to construct **a temporary StrBlob object**. - cannot use the copy form of initialization with an explicit constructor. not easy to use. ## [Exercise 12.6](ex12_06.cpp) ## [Exercise 12.7](ex12_07.cpp) ## Exercise 12.8: >Explain what if anything is wrong with the following function. ```cpp bool b() { int* p = new int; // ... return p; } ``` The p will convert to a bool , which means that the dynamic memory allocated has no chance to be freed. As a result, memory leakage will occur. ## Exercise 12.9: >Explain what happens in the following code: ```cpp int *q = new int(42), *r = new int(100); r = q; auto q2 = make_shared(42), r2 = make_shared(100); r2 = q2; ``` - to `q` and `r`: Memory leakage happens. Because after `r = q` was executed, no pointer points to the int `r` had pointed to. It implies that no chance to free the memory for it. - to `q2` and `r2`: It's safe. Because after 'r2 = q2', the reference count belongs to r2 reduce to 0 and the reference count belongs to q2 increase to 2, then the memory allocated by r2 will be released automatically. ## [Exercise 12.10](ex12_10.cpp) ## [Exercise 12.11](ex12_11.cpp) ## [Exercise 12.12](ex12_12.cpp) ## [Exercise 12.13](ex12_13.cpp) ## [Exercise 12.14](ex12_14.cpp) ## [Exercise 12.15](ex12_15.cpp) ## [Exercise 12.16](ex12_16.cpp) ## [Exercise 12.17 and 12.18](ex12_17_18.cpp) ## Exercise 12.19 [Header](ex12_19.h)|[Implementation](ex12_19.cpp) ## [Exercise 12.20](ex12_20.cpp) ## Exercise 12.21: >We could have written StrBlobPtr’s deref member as follows: ```cpp std::string& deref() const { return (*check(curr, "dereference past end"))[curr]; } ``` Which version do you think is better and why? The original one is better, because it's more readable. ## Exercise 12.22 [Header](ex12_22.h)|[Implementation](ex12_22.cpp) ## [Exercise 12.23](ex12_23.cpp) ## [Exercise 12.24](ex12_24.cpp) ## Exercise 12.25: >Given the following new expression, how would you delete pa? ```cpp int *pa = new int[10]; ``` ```cpp delete [] pa; ``` ## [Exercise 12.26](ex12_26.cpp) ## Exercise 12.27 [Header](ex12_27_30.h)|[Implementation](ex12_27_30.cpp)|[Test](ex12_27_30_TEST.cpp) ## [Exercise 12.28](ex12_28.cpp) ## Exercise 12.29: >We could have written the loop to manage the interaction with the user as a do while (5.4.4, p. 189) loop. Rewrite the loop to use a do while. Explain which version you prefer and why. ```cpp do { std::cout << "enter word to look for, or q to quit: "; string s; if (!(std::cin >> s) || s == "q") break; print(std::cout, tq.query(s)) << std::endl; } while ( true ); ``` I prefer `do while`, because it looks clearer. ## Exercise 12.30 [Header](ex12_27_30.h)|[Implementation](ex12_27_30.cpp)|[Test](ex12_27_30_TEST.cpp) ## Exercise 12.31: >What difference(s) would it make if we used a vector instead of a set to hold the line numbers? Which approach is better? Why? `vector` doesn't guarantee that elements being held are unique, so `set` is a better choice for this case. ## Exercise 12.32 [Header](ex12_32.h)|[Implementation](ex12_32.cpp)|[Test](ex12_32_TEST.cpp) ## Exercise 12.33 [Header](ex12_33.h)|[Implementation](ex12_33.cpp)|[Test](ex12_33_TEST.cpp) ================================================ FILE: ch12/ex12_02.h ================================================ // // ex12_02.cpp // Exercise 12.2 // // Created by pezy on 12/22/14. // // Write your own version of the StrBlob class including the const versions of front and back. #include #include #include #include #include using std::vector; using std::string; class StrBlob { public: using size_type = vector::size_type; StrBlob():data(std::make_shared>()) { } StrBlob(std::initializer_list il):data(std::make_shared>(il)) { } size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } std::string& front() { check(0, "front on empty StrBlob"); return data->front(); } std::string& back() { check(0, "back on empty StrBlob"); return data->back(); } const std::string& front() const { check(0, "front on empty StrBlob"); return data->front(); } const std::string& back() const { check(0, "back on empty StrBlob"); return data->back(); } private: void check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } private: std::shared_ptr> data; }; ================================================ FILE: ch12/ex12_02_TEST.cpp ================================================ #include "ex12_02.h" #include int main() { const StrBlob csb{ "hello", "world", "pezy" }; StrBlob sb{ "hello", "world", "Mooophy" }; std::cout << csb.front() << " " << csb.back() << std::endl; sb.back() = "pezy"; std::cout << sb.front() << " " << sb.back() << std::endl; } ================================================ FILE: ch12/ex12_06.cpp ================================================ /*************************************************************************** * @file The code is for the exercises in C++ Primmer 5th Edition * @author Yue Wang * @date 22 DEC 2013 * Jun 2015 * Oct 2015 * @remark ***************************************************************************/ // // Exercise 12.6: // Write a function that returns a dynamically allocated vector of ints. // Pass that vector to another function that reads the standard input to // give values to the elements. Pass the vector to another function to print // the values that were read. // Remember to delete the vector at the appropriate time. // #include #include using Ptr = std::vector*; auto make_dynamically() { return new std::vector < int > { }; } auto populate(Ptr vec) { for (int i; std::cout << "Pls Enter:\n", std::cin >> i; vec->push_back(i)); return vec; } auto print(Ptr vec) -> std::ostream& { for (auto i : *vec) std::cout << i << " "; return std::cout; } int main() { auto vec = populate(make_dynamically()); print(vec) << std::endl; delete vec; return 0; } ================================================ FILE: ch12/ex12_07.cpp ================================================ /*************************************************************************** * @file The code is for the exercises in C++ Primmer 5th Edition * @author Yue Wang * @date 22 DEC 2013 * Oct 2015 * @remark ***************************************************************************/ // // Exercise 12.6: // Write a function that returns a dynamically allocated vector of ints. // Pass that vector to another function that reads the standard input to // give values to the elements. Pass the vector to another function to print // the values that were read. // Remember to delete the vector at the appropriate time. // // Exercise 12.7: // Redo the previous exercise, this time using shared_ptr. // #include #include #include using Sptr = std::shared_ptr < std::vector > ; auto make_with_shared_ptr() { return std::make_shared>(); } auto populate(Sptr vec) { for (int i; std::cout << "Pls Enter:\n", std::cin >> i; vec->push_back(i)); return vec; } auto print(Sptr vec) -> std::ostream& { for (auto i : *vec) std::cout << i << " "; return std::cout; } int main() { auto vec = populate(make_with_shared_ptr()); print(vec) << std::endl; return 0; } ================================================ FILE: ch12/ex12_10.cpp ================================================ /*************************************************************************** * @file The code is for the exercises in C++ Primmer 5th Edition * @author Yue Wang * @date 23 DEC 2013 * @remark ***************************************************************************/ // // Exercise 12.10: // Explain whether the following call to the process function defined on page // 464 is correct. If not, how would you correct the call? // correct. #include #include void process(std::shared_ptr ptr) { std::cout << "inside the process function:" << ptr.use_count() << "\n"; } int main() { std::shared_ptr p(new int(42)); process(std::shared_ptr(p)); /** * codes below shows how the reference count change. */ std::cout << p.use_count() << "\n"; auto q = p; std::cout << p.use_count() << "\n"; std::cout << "the int p now points to is:" << *p << "\n"; return 0; } ================================================ FILE: ch12/ex12_11.cpp ================================================ /*************************************************************************** * @file The code is for the exercises in C++ Primmer 5th Edition * @author Alan.W * @date 23 DEC 2013 * @remark ***************************************************************************/ // // Exercise 12.11: // What would happen if we called process as follows? // An error was generated at run time : double free or corruption. // See the comments below. #include #include #include #include void process(std::shared_ptr ptr) { std::cout << "inside the process function:" << ptr.use_count() << "\n"; } int main() { std::shared_ptr p(new int(42)); /** * @brief std::shared_ptr(p.get()) construct a temporary shared_ptr and copy it * to the parameter.However it is not a copy of p. As a result, at end of this * main function p will free the memory that has been freed inside process (). * That's why "double freed or corruption" was generated. */ process(std::shared_ptr(p.get())); return 0; } ================================================ FILE: ch12/ex12_12.cpp ================================================ /*************************************************************************** * @file The code is for the exercises in C++ Primmer 5th Edition * @author Alan.W * @date 23 DEC 2013 * @remark ***************************************************************************/ // // Exercise 12.12: // Using the declarations of p and sp explain each of the following calls to // process. If the call is legal, explain what it does. If the call is illegal, // explain why: // See comments below. // #include #include #include #include void process(std::shared_ptr ptr) { std::cout << "inside the process function:" << ptr.use_count() << "\n"; } int main() { auto p = new int(); auto sp = std::make_shared(); /** @brief * legal. Just copy sp which is a shared_ptr to process(). */ //process(sp); /** @brief * illegale.plain pointer can not convert to smart pointer implicitly. */ //process(new int()); /** @brief * illegale.plain pointer can not convert to smart pointer implicitly. */ //process(p); /** @brief * Legal. But it's a bad practice to do so. * Because using smart pointer together with raw pointer could potentially cause problems. * For example double free as shown in #145. * * Check issue #145 for detail, thx @endyul for reporting */ //process(std::shared_ptr(p)); return 0; } ================================================ FILE: ch12/ex12_13.cpp ================================================ // @Yue Wang // // ex12.13 What happens if we excute the following code? // // generate a runtime error : double free // #include #include int main() { { auto sp = std::make_shared(); auto p = sp.get(); delete p; } return 0; } ================================================ FILE: ch12/ex12_14.cpp ================================================ // // ex12_14.cpp // Exercise 12.14 // // Created by pezy on 12/22/14. // // Write your own version of a function that uses a shared_ptr to manage a connection. #include #include #include struct connection { std::string ip; int port; connection(std::string ip_, int port_):ip(ip_), port(port_){ } }; struct destination { std::string ip; int port; destination(std::string ip_, int port_):ip(ip_), port(port_){ } }; connection connect(destination* pDest) { std::shared_ptr pConn(new connection(pDest->ip, pDest->port)); std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl; return *pConn; } void disconnect(connection pConn) { std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl; } void end_connection(connection *pConn) { disconnect(*pConn); } void f(destination &d) { connection conn = connect(&d); std::shared_ptr p(&conn, end_connection); std::cout << "connecting now(" << p.use_count() << ")" << std::endl; } int main() { destination dest("202.118.176.67", 3316); f(dest); } ================================================ FILE: ch12/ex12_15.cpp ================================================ // // ex12_15.cpp // Exercise 12.15 // // Created by pezy on 12/22/14. // // Rewrite the first exercise to use a lambda (10.3.2, p.388) in place of the end_connection function. #include #include #include struct connection { std::string ip; int port; connection(std::string ip_, int port_):ip(ip_), port(port_){ } }; struct destination { std::string ip; int port; destination(std::string ip_, int port_):ip(ip_), port(port_){ } }; connection connect(destination* pDest) { std::shared_ptr pConn(new connection(pDest->ip, pDest->port)); std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl; return *pConn; } void disconnect(connection pConn) { std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl; } void f(destination &d) { connection conn = connect(&d); std::shared_ptr p(&conn, [](connection *p){ disconnect(*p); }); std::cout << "connecting now(" << p.use_count() << ")" << std::endl; } int main() { destination dest("202.118.176.67", 3316); f(dest); } ================================================ FILE: ch12/ex12_16.cpp ================================================ // // ex12_15.cpp // Exercise 12.15 // // Created by pezy on 12/22/14. // // Compilers don’t always give easy-to-understand error messages if we attempt to // copy or assign a unique_ptr. Write a program that contains these errors to see // how your compiler diagnoses them. #include #include #include using std::string; using std::unique_ptr; int main() { unique_ptr p1(new string("pezy")); // unique_ptr p2(p1); // copy // ^ // Error: Call to implicitly-deleted copy constructor of 'unique_ptr' // // unique_ptr p3 = p1; // assign // ^ // Error: Call to implicitly-deleted copy constructor of 'unique_ptr' std::cout << *p1 << std::endl; p1.reset(nullptr); } ================================================ FILE: ch12/ex12_17_18.cpp ================================================ /*************************************************************************** * @file The code is for the exercises in C++ Primmer 5th Edition * @author Alan.W * @date 24 DEC 2013 * @remark ***************************************************************************/ // // Exercise 12.17: // Which of the following unique_ptr declarations are illegal or likely to // result in subsequent program error? Explain what the problem is with each // one. // // Exercise 12.18: // Why doesn’t shared_ptr have a release member? // Because other shared_ptr that points the same object can still delete this // object.Thus, it's meaningless to provide this member // more detail can be found a thread on Stack Overflow: // http://stackoverflow.com/questions/1525764/how-to-release-pointer-from-boostshared-ptr #include #include #include #include int main() { int ix = 1024, *pi = &ix, *pi2 = new int(2048); typedef std::unique_ptr IntP; /** * @brief error: invalid conversion from 'int' to 'std::unique_ptr::pointer { aka int* }' [-fpermissive] */ //IntP p0(ix); /** * @brief The code below can compile, but will cause error at run time. * The reason is that when the unique_ptr p1 is out of scope, delete will be called * to free th object. But the object is not allocate using new.Thus, an error * would be thrown by operating system. * @badcode */ //IntP p1(pi); /** * @brief This code can compile, but cause a dangling pointer at run time. * The reason is that the unique_ptr will free the object the raw pointer * is pointing to. * @badcode */ //{ IntP p2(pi2); } /** * @brief When the unique_ptr goes out of scope, it will call delete to free an * obeject not allocated using new. * @badcode */ //IntP p3(&ix); /** * @brief Recommended. */ //IntP p4(new int(2048)); /** * @brief error: double free or corruption at run time * two unique_ptr are pointing to the same object. Thus, when both are out of * scope, Operating system will throw double free or corruption. * @badcode */ //IntP p2(new int(555)); //IntP p5(p2.get()); return 0; } ================================================ FILE: ch12/ex12_19.cpp ================================================ // // ex12_19.cpp // Exercise 12.19 // // Created by pezy on 12/26/14. // // Define your own version of StrBlobPtr and // update your StrBlob class with the appropriate friend declaration and begin and end members. // // @See ex12_19.h #include "ex12_19.h" StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); } ================================================ FILE: ch12/ex12_19.h ================================================ // // ex12_19.h // Exercise 12.19 // // Created by pezy on 12/26/14. // // Define your own version of StrBlobPtr and // update your StrBlob class with the appropriate friend declaration and begin and end members. // // @See ex12_02.h #ifndef CP5_ex12_19_h #define CP5_ex12_19_h #include #include #include #include #include using std::vector; using std::string; class StrBlobPtr; class StrBlob { public: using size_type = vector::size_type; friend class StrBlobPtr; StrBlobPtr begin(); StrBlobPtr end(); StrBlob():data(std::make_shared>()) { } StrBlob(std::initializer_list il):data(std::make_shared>(il)) { } size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } std::string& front() { check(0, "front on empty StrBlob"); return data->front(); } std::string& back() { check(0, "back on empty StrBlob"); return data->back(); } const std::string& front() const { check(0, "front on empty StrBlob"); return data->front(); } const std::string& back() const { check(0, "back on empty StrBlob"); return data->back(); } private: void check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } private: std::shared_ptr> data; }; class StrBlobPtr { public: StrBlobPtr():curr(0) { } StrBlobPtr(StrBlob &a, size_t sz = 0):wptr(a.data), curr(sz) { } bool operator!=(const StrBlobPtr& p) { return p.curr != curr; } string& deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } StrBlobPtr& incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } private: std::shared_ptr> check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } std::weak_ptr> wptr; size_t curr; }; #endif ================================================ FILE: ch12/ex12_20.cpp ================================================ // // ex12_20.cpp // Exercise 12.20 // // Created by pezy on 12/26/14. // // Write a program that reads an input file a line at a time into // a StrBlob and uses a StrBlobPtr to print each element in that StrBlob. #include "ex12_19.h" #include #include int main() { std::ifstream ifs("../data/book.txt"); StrBlob blob; for (std::string str; std::getline(ifs, str); ) blob.push_back(str); for (StrBlobPtr pbeg(blob.begin()), pend(blob.end()); pbeg != pend; pbeg.incr()) std::cout << pbeg.deref() << std::endl; } ================================================ FILE: ch12/ex12_22.cpp ================================================ // // ex12_22.cpp // CP5 // // Created by pezy on 1/2/15. // #include "ex12_22.h" ConstStrBlobPtr StrBlob::begin() const // should add const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::end() const // should add const { return ConstStrBlobPtr(*this, data->size()); } ================================================ FILE: ch12/ex12_22.h ================================================ // // ex12_22.cpp // Exercise 12.22 // // Created by pezy on 12/28/14. // // What changes would need to be made to StrBlobPtr to create a class // that can be used with a const StrBlob? // Define a class named ConstStrBlobPtr that can point to a const StrBlob. // // @See ex12_19.h #ifndef CP5_ex12_22_h #define CP5_ex12_22_h #include #include #include #include #include using std::vector; using std::string; class ConstStrBlobPtr; class StrBlob { public: using size_type = vector::size_type; friend class ConstStrBlobPtr; ConstStrBlobPtr begin() const; // should add const ConstStrBlobPtr end() const; // should add const StrBlob():data(std::make_shared>()) { } StrBlob(std::initializer_list il):data(std::make_shared>(il)) { } size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } std::string& front() { check(0, "front on empty StrBlob"); return data->front(); } std::string& back() { check(0, "back on empty StrBlob"); return data->back(); } const std::string& front() const { check(0, "front on empty StrBlob"); return data->front(); } const std::string& back() const { check(0, "back on empty StrBlob"); return data->back(); } private: void check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } private: std::shared_ptr> data; }; class ConstStrBlobPtr { public: ConstStrBlobPtr():curr(0) { } ConstStrBlobPtr(const StrBlob &a, size_t sz = 0):wptr(a.data), curr(sz) { } // should add const bool operator!=(ConstStrBlobPtr& p) { return p.curr != curr; } const string& deref() const { // return value should add const auto p = check(curr, "dereference past end"); return (*p)[curr]; } ConstStrBlobPtr& incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } private: std::shared_ptr> check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } std::weak_ptr> wptr; size_t curr; }; #endif ================================================ FILE: ch12/ex12_23.cpp ================================================ // // ex12_23.cpp // Exercise 12.23 // // Created by pezy on 12/30/14. // // Write a program to concatenate two string literals, putting the result in a dynamically allocated array of char. // Write a program to concatenate two library strings that have the same value as the literals used in the first program. #include #include #include int main() { // dynamically allocated array of char char *concatenate_string = new char[strlen("hello " "world") + 1](); strcat(concatenate_string, "hello "); strcat(concatenate_string, "world"); std::cout << concatenate_string << std::endl; delete [] concatenate_string; // std::string std::string str1{ "hello " }, str2{ "world" }; std::cout << str1 + str2 << std::endl; } ================================================ FILE: ch12/ex12_24.cpp ================================================ // // ex12_24.cpp // Exercise 12.24 // // Created by pezy on 12/30/14. // // Write a program that reads a string from the standard input into a dynamically allocated character array. // Describe how your program handles varying size inputs. // Test your program by giving it a string of data that is longer than the array size you've allocated. #include int main() { // need to tell the size. std::cout << "How long do you want the string? "; int size{ 0 }; std::cin >> size; char *input = new char[size+1](); std::cin.ignore(); std::cout << "input the string: "; std::cin.get(input, size+1); std::cout << input; delete [] input; // Test: if longer than the array size, we will lost the characters which are out of range. } ================================================ FILE: ch12/ex12_26.cpp ================================================ // // ex12_26.cpp // Exercise 12.26 // // Created by pezy on 12/30/14. // // Rewrite the program on page 481 using an allocator. #include #include #include void input_reverse_output_string(int n) { std::allocator alloc; auto const p = alloc.allocate(n); std::string s; auto q = p; while (q != p + n && std::cin >> s) alloc.construct(q++, s); while (q != p) { std::cout << *--q << " "; alloc.destroy(q); } alloc.deallocate(p, n); } int main() { input_reverse_output_string(5); } ================================================ FILE: ch12/ex12_27_30.cpp ================================================ // // ex12_27.cpp // Exercise 12.27 // // Created by pezy on 12/31/14. // // The TextQuery and QueryResult classes use only capabilities that we have already covered. // Without looking ahead, write your own versions of these classes. #include "ex12_27_30.h" #include #include TextQuery::TextQuery(std::ifstream &ifs) : input(new vector) { LineNo lineNo{ 0 }; for (string line; std::getline(ifs, line); ++lineNo) { input->push_back(line); std::istringstream line_stream(line); for (string text, word; line_stream >> text; word.clear()) { // avoid read a word followed by punctuation(such as: word, ) std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct); // use reference avoid count of shared_ptr add. auto &nos = result[word]; if (!nos) nos.reset(new std::set); nos->insert(lineNo); } } } QueryResult TextQuery::query(const string& str) const { // use static just allocate once. static shared_ptr> nodata(new std::set); auto found = result.find(str); if (found == result.end()) return QueryResult(str, nodata, input); else return QueryResult(str, found->second, input); } std::ostream& print(std::ostream &out, const QueryResult& qr) { out << qr.word << " occurs " << qr.nos->size() << (qr.nos->size() > 1 ? " times" : " time") << std::endl; for (auto i : *qr.nos) out << "\t(line " << i+1 << ") " << qr.input->at(i) << std::endl; return out; } ================================================ FILE: ch12/ex12_27_30.h ================================================ // // ex12_27.h // Exercise 12.27 // // Created by pezy on 12/31/14. // // The TextQuery and QueryResult classes use only capabilities that we have already covered. // Without looking ahead, write your own versions of these classes. #ifndef CP5_ex12_27_h #define CP5_ex12_27_h #include using std::string; #include using std::vector; #include using std::shared_ptr; #include #include #include #include class QueryResult; class TextQuery { public: using LineNo = vector::size_type; TextQuery(std::ifstream &); QueryResult query(const string&) const; private: shared_ptr> input; std::map>> result; }; class QueryResult { public: friend std::ostream& print(std::ostream &, const QueryResult&); public: QueryResult(const string &s, shared_ptr> set, shared_ptr> v) : word(s), nos(set), input(v) { } private: string word; shared_ptr> nos; shared_ptr> input; }; std::ostream& print(std::ostream &, const QueryResult&); #endif ================================================ FILE: ch12/ex12_27_30_TEST.cpp ================================================ // // ex12_27_TEST.cpp // Exercise 12.27 // // Created by pezy on 12/31/14. // // The TextQuery and QueryResult classes use only capabilities that we have already covered. // Without looking ahead, write your own versions of these classes. #include "ex12_27_30.h" #include void runQueries(std::ifstream &infile) { TextQuery tq(infile); while (true) { std::cout << "enter word to look for, or q to quit: "; string s; if (!(std::cin >> s) || s == "q") break; print(std::cout, tq.query(s)) << std::endl; } } int main() { std::ifstream file("../data/storyDataFile.txt"); runQueries(file); } ================================================ FILE: ch12/ex12_28.cpp ================================================ // // ex12_28.cpp // Exercise 12.28 // // Created by pezy on 1/1/15. // // Write a program to implement text queries without defining classes to manage the data. // Your program should take a file and interact with a user to query for words in that file. // Use vector, map, and set containers to hold the data for the file and // to generate the results for the queries. #include using std::string; #include using std::vector; #include using std::shared_ptr; #include #include #include #include #include #include int main() { std::ifstream file("../data/letter.txt"); vector input; std::map> dictionary; decltype(input.size()) lineNo{ 0 }; for (string line; std::getline(file, line); ++lineNo) { input.push_back(line); std::istringstream line_stream(line); for (string text, word; line_stream >> text; word.clear()) { std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct); dictionary[word].insert(lineNo); } } while (true) { std::cout << "enter word to look for, or q to quit: "; string s; if (!(std::cin >> s) || s == "q") break; auto found = dictionary.find(s); if (found != dictionary.end()) { std::cout << s << " occurs " << found->second.size() << (found->second.size() > 1 ? " times" : " time") << std::endl; for (auto i : found->second) std::cout << "\t(line " << i+1 << ") " << input.at(i) << std::endl; } else std::cout << s << " occurs 0 time" << std::endl; } } ================================================ FILE: ch12/ex12_32.cpp ================================================ // // ex12_32.cpp // Exercise 12.32 // // Created by pezy on 1/1/15. // // Rewrite the TextQuery and QueryResult classes to use a StrBlob // instead of a vector to hold the input file. #include "ex12_32.h" #include #include TextQuery::TextQuery(std::ifstream &ifs) { StrBlob::size_type lineNo{ 0 }; for (string line; std::getline(ifs, line); ++lineNo) { file.push_back(line); std::istringstream line_stream(line); for (string text, word; line_stream >> text; word.clear()) { // avoid read a word followed by punctuation(such as: word, ) std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct); // use reference avoid count of shared_ptr add. auto &nos = result[word]; if (!nos) nos.reset(new std::set); nos->insert(lineNo); } } } QueryResult TextQuery::query(const string& str) const { // use static just allocate once. static shared_ptr> nodate(new std::set); auto found = result.find(str); if (found == result.end()) return QueryResult(str, nodate, file); else return QueryResult(str, found->second, file); } std::ostream& print(std::ostream &out, const QueryResult& qr) { out << qr.word << " occurs " << qr.nos->size() << (qr.nos->size() > 1 ? " times" : " time") << std::endl; for (auto i : *qr.nos) { ConstStrBlobPtr p(qr.file, i); out << "\t(line " << i+1 << ") " << p.deref() << std::endl; } return out; } ================================================ FILE: ch12/ex12_32.h ================================================ // // ex12_32.h // Exercise 12.32 // // Created by pezy on 1/1/15. // // Rewrite the TextQuery and QueryResult classes to use a StrBlob // instead of a vector to hold the input file. #ifndef CP5_ex12_32_h #define CP5_ex12_32_h #include "ex12_22.h" using std::shared_ptr; #include #include #include #include class QueryResult; class TextQuery { public: TextQuery(std::ifstream &); QueryResult query(const string&) const; private: StrBlob file; std::map>> result; }; class QueryResult { public: friend std::ostream& print(std::ostream &, const QueryResult&); public: QueryResult(const string &s, shared_ptr> set, const StrBlob& f) : word(s), nos(set), file(f) { } private: string word; shared_ptr> nos; StrBlob file; }; std::ostream& print(std::ostream &, const QueryResult&); #endif ================================================ FILE: ch12/ex12_32_TEST.cpp ================================================ // // ex12_27_TEST.cpp // Exercise 12.27 // // Created by pezy on 12/31/14. // // The TextQuery and QueryResult classes use only capabilities that we have already covered. // Without looking ahead, write your own versions of these classes. #include "ex12_32.h" #include void runQueries(std::ifstream &infile) { TextQuery tq(infile); while (true) { std::cout << "enter word to look for, or q to quit: "; string s; if (!(std::cin >> s) || s == "q") break; print(std::cout, tq.query(s)) << std::endl; } } int main() { std::ifstream file("../data/storyDataFile.txt"); runQueries(file); } ================================================ FILE: ch12/ex12_33.cpp ================================================ // // ex12_33.cpp // Exercise 12.33 // // Created by pezy on 1/1/15. // // Rewrite the TextQuery and QueryResult classes to use a StrBlob // instead of a vector to hold the input file. #include "ex12_33.h" #include #include TextQuery::TextQuery(std::ifstream &ifs) { StrBlob::size_type lineNo{ 0 }; for (string line; std::getline(ifs, line); ++lineNo) { file.push_back(line); std::istringstream line_stream(line); for (string text, word; line_stream >> text; word.clear()) { // avoid read a word followed by punctuation(such as: word, ) std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct); // use reference avoid count of shared_ptr add. auto &nos = result[word]; if (!nos) nos.reset(new std::set); nos->insert(lineNo); } } } QueryResult TextQuery::query(const string& str) const { // use static just allocate once. static shared_ptr> nodate(new std::set); auto found = result.find(str); if (found == result.end()) return QueryResult(str, nodate, file); else return QueryResult(str, found->second, file); } std::ostream& print(std::ostream &out, const QueryResult& qr) { out << qr.word << " occurs " << qr.nos->size() << (qr.nos->size() > 1 ? " times" : " time") << std::endl; for (auto it = qr.begin(); it != qr.end(); ++it) { ConstStrBlobPtr p(*qr.get_file(), *it); out << "\t(line " << *it + 1 << ") " << p.deref() << std::endl; } return out; } ================================================ FILE: ch12/ex12_33.h ================================================ // // ex12_33.h // Exercise 12.33 // // Created by pezy on 1/1/15. // // In Chapter 15 we’ll extend our query system and will need some additional members // in the QueryResult class. // Add members named [begin] and [end] that return iterators into the set of line numbers // returned by a given query, and a member named [get_file] that // returns a shared_ptr to the file in the QueryResult object. #ifndef CP5_ex12_33_h #define CP5_ex12_33_h #include "ex12_22.h" using std::shared_ptr; #include #include #include #include class QueryResult; class TextQuery { public: TextQuery(std::ifstream &); QueryResult query(const string&) const; private: StrBlob file; std::map>> result; }; class QueryResult { public: using ResultIter = std::set::iterator; friend std::ostream& print(std::ostream &, const QueryResult&); public: QueryResult(const string &s, shared_ptr> set, const StrBlob& f) : word(s), nos(set), file(f) { } ResultIter begin() const { return nos->begin(); } ResultIter end() const { return nos->end(); } shared_ptr get_file() const { return std::make_shared(file); } private: string word; shared_ptr> nos; StrBlob file; }; std::ostream& print(std::ostream &, const QueryResult&); #endif ================================================ FILE: ch12/ex12_33_TEST.cpp ================================================ // // ex12_27_TEST.cpp // Exercise 12.27 // // Created by pezy on 12/31/14. // // The TextQuery and QueryResult classes use only capabilities that we have already covered. // Without looking ahead, write your own versions of these classes. #include "ex12_33.h" #include void runQueries(std::ifstream &infile) { TextQuery tq(infile); while (true) { std::cout << "enter word to look for, or q to quit: "; string s; if (!(std::cin >> s) || s == "q") break; print(std::cout, tq.query(s)) << std::endl; } } int main() { std::ifstream file("../data/storyDataFile.txt"); runQueries(file); } ================================================ FILE: ch13/README.md ================================================ # Chapter 13. Copy Control ## Exercise 13.1: >What is a copy constructor? When is it used? A copy constructor is a constructor which first parameter is a **reference** to the class type and any additional parameters have **default values**. When copy initialization happens and that copy initialization requires either the copy constructor or the move constructor. - Define variables using an `=` - Pass an object as an argument to a parameter of nonreference type - Return an object from a function that has a nonreference return type - Brace initialize the elements in an array or the members of an aggregate class - Some class types also use copy initialization for the objects they allocate. ## Exercise 13.2: > Explain why the following declaration is illegal: ```cpp Sales_data::Sales_data(Sales_data rhs); ``` If declaration like that, the call would never succeed to call the copy constructor, `Sales_data rhs` is an argument to a parameter, thus, we'd need to use the copy constructor to copy the argument, but to copy the argument, we'd need to call the copy constructor, and so on indefinitely. ## Exercise 13.3: >What happens when we copy a `StrBlob`? What about `StrBlobPtrs`? ```cpp // added a public member function to StrBlob and StrBlobPrts long count() { return data.use_count(); // and wptr.use_count(); } // test codes in main() StrBlob str({ "hello", "world" }); std::cout << "before: " << str.count() << std::endl; // 1 StrBlob str_cp(str); std::cout << "after: " << str.count() << std::endl; // 2 ConstStrBlobPtr p(str); std::cout << "before: " << p.count() << std::endl; // 2 ConstStrBlobPtr p_cp(p); std::cout << "after: " << p.count() << std::endl; // 2 ``` when we copy a `StrBlob`, the `shared_ptr` member's use_count add one. when we copy a `StrBlobPrts`, the `weak_ptr` member's use_count isn't changed.(cause the count belongs to `shared_ptr`) ## Exercise 13.4: >Assuming Point is a class type with a public copy constructor, identify each use of the copy constructor in this program fragment: ```cpp Point global; Point foo_bar(Point arg) // 1 { Point local = arg, *heap = new Point(global); // 2, 3 *heap = local; Point pa[ 4 ] = { local, *heap }; // 4, 5 return *heap; // 6 } ``` ## [Exercise 13.5](ex13_05.h) ## Exercise 13.6: >What is a copy-assignment operator? When is this operator used? What does the synthesized copy-assignment operator do? When is it synthesized? The copy-assignment operator is function named `operator=` and takes an argument of the same type as the class. This operator is used when assignment occurred. The synthesized copy-assignment operator assigns each nonstatic member of the right-hand object to corresponding member of the left-hand object using the copy-assignment operator for the type of that member. It is synthesized when the class does not define its own. ## Exercise 13.7: >What happens when we assign one StrBlob to another? What about StrBlobPtrs? In both cases, shallow copy will happen. All pointers point to the same address. The `use_count` changed the same as 13.3. ## [Exercise 13.8](ex13_08.h) ## Exercise 13.9: >What is a destructor? What does the synthesized destructor do? When is a destructor synthesized? The destructor is a member function with the name of the class prefixed by a tilde(~). As with the copy constructor and the copy-assignment operator, for some classes, the synthesized destructor is defined to disallow objects of the type from being destoryed. Otherwise, the synthesized destructor has an empty function body. The compiler defines a synthesized destructor for any class that does not define its own destructor. ## Exercise 13.10: >What happens when a StrBlob object is destroyed? What about a StrBlobPtr? When a `StrBlob` object destroyed, the `use_count` of the dynamic object will decrement. It will be freed if no `shared_ptr` to that dynamic object. When a `StrBlobPter` object is destroyed the object dynamically allocated will not be freed. ## [Exercise 13.11](ex13_11.h) ## Exercise 13.12: >How many destructor calls occur in the following code fragment? ```cpp bool fcn(const Sales_data *trans, Sales_data accum) { Sales_data item1(*trans), item2(accum); return item1.isbn() != item2.isbn(); } ``` 3 times. There are `accum`, `item1` and `item2`. ## [Exercise 13.13](ex13_13.cpp) ## Exercise 13.14: >Assume that `numbered` is a class with a default constructor that generates a unique serial number for each object, which is stored in a data member named `mysn`. Assuming numbered uses the synthesized copy-control members and given the following function: ```cpp void f (numbered s) { cout << s.mysn << endl; } ``` what output does the following code produce? ```cpp numbered a, b = a, c = b; f(a); f(b); f(c); ``` Three identical numbers. ## Exercise 13.15: >Assume `numbered` has a copy constructor that generates a new serial number. Does that change the output of the calls in the previous exercise? If so, why? What output gets generated? Yes, it does. Because, as described, the newly defined copy constructor can handle such situations as expected.Thus, the output will be three different numbers. ## Exercise 13.16: >What if the parameter in f were const numbered&? Does that change the output? If so, why? What output gets generated? Yes, the output will change. Because no copy operation happens within function `f`. Thus, the three Output are the same. ## Exercise 13.17 > Write versions of numbered and f corresponding to the previous three exercises and check whether you correctly predicted the output. [For 13.14](ex13_17_1.cpp) | [For 13.15](ex13_17_2.cpp) | [For 13.16](ex13_17_3.cpp) ## Exercise 13.18 [.h](ex13_18.h) | [.cpp](ex13_18.cpp) ## [Exercise 13.19](ex13_19.h) ## Exercise 13.20: >Explain what happens when we copy, assign, or destroy objects of our TextQuery and QueryResult classes from § 12.3 (p. 484). The member (smart pointer and container) will be copied. ## Exercise 13.21: >Do you think the TextQuery and QueryResult classes need to define their own versions of the copy-control members? If so, why? If not, why not? Implement whichever copy-control operations you think these classes require. As synthesized version meet all requirements for this case, no custom version control memebers need to define. Check [#304](https://github.com/Mooophy/Cpp-Primer/issues/304#issuecomment-124081395) for detail. ## [Exercise 13.22](ex13_22.h) ## Exercise 13.23: >Compare the copy-control members that you wrote for the solutions to the previous section’s exercises to the code presented here. Be sure you understand the differences, if any, between your code and ours. Check 13.22. ## Exercise 13.24: >What would happen if the version of `HasPtr` in this section didn’t define a destructor? What if `HasPtr` didn’t define the copy constructor? If `HasPtr` didn't define a destructor, a memory leak would occur, compiler synthesized destructor does not manage dynamic memory. If `HasPtr` didn't define the copy constructor, we would get pointer-like copy behaviour. The ps pointer would be copied to the left hand side, but ps in the lhs and the rhs would still point to the same string on the heap. ## Exercise 13.25: >Assume we want to define a version of `StrBlob` that acts like a value. Also assume that we want to continue to use a shared_ptr so that our `StrBlobPtr` class can still use a weak_ptr to the vector. Your revised class will need a copy constructor and copy-assignment operator but will not need a destructor. Explain what the copy constructor and copy-assignment operators must do. Explain why the class does not need a destructor. Copy constructor and copy-assignment operator should dynamically allocate memory for its own , rather than share the object with the right hand operand. `StrBlob` is using smart pointers which can be managed with synthesized destructor, If an object of `StrBlob` is out of scope, the destructor for std::shared_ptr will be called automatically to free the memory dynamically allocated when the `use_count` goes to 0. ## Exercise 13.26 [hpp](ex13_26.h) | [cpp](ex13_26.cpp) ## [Exercise 13.27](ex13_27.h) ## Exercise 13.28 [hpp](ex13_28.h) | [cpp](ex13_28.cpp) ## Exercise 13.29: > Explain why the calls to swap inside swap(HasPtr&, HasPtr&) do not cause a recursion loop. `swap(lhs.ps, rhs.ps);` feed the version : `swap(std::string*, std::string*)` and `swap(lhs.i, rhs.i);` feed the version : `swap(int, int)`. Both them can't call `swap(HasPtr&, HasPtr&)`. Thus, the calls don't cause a recursion loop. ## [Exercise 13.30](ex13_30.h) ## Exercise 13.31 [hpp](ex13_31.h) | [cpp](ex13_31.cpp) ## Exercise 13.32: >Would the pointerlike version of `HasPtr` benefit from defining a swap function? If so, what is the benefit? If not, why not? @Mooophy: Essentially, the specific avoiding memory allocation is the reason why it improve performance. As for the pointerlike version, no dynamic memory allocation anyway. Thus, a specific version for it will not improve the performance. ## Exercise 13.33: >Why is the parameter to the `save` and `remove` members of Message a Folder&? Why didn’t we define that parameter as `Folder`? Or `const Folder&`? Because these operations must also update the given `Folder`. Updating a `Folder` is a job that the `Folder` class controls through its `addMsg` and `remMsg` members, which will add or remove a pointer to a given `Message`, respectively. ## Exercise 13.34 [hpp](ex13_34_36_37.h) | [cpp](ex13_34_36_37.cpp) ## Exercise 13.35: >What would happen if `Message` used the synthesized versions of the copy-control members? some existing `Folders` will out of sync with the `Message` after assignment. ## Exercise 13.36 [hpp](ex13_34_36_37.h) | [cpp](ex13_34_36_37.cpp) ## Exercise 13.37 [hpp](ex13_34_36_37.h) | [cpp](ex13_34_36_37.cpp) ## Exercise 13.38: >We did not use copy and swap to define the Message assignment operator. Why do you suppose this is so? @Mooophy The copy and swap is an elegant way when working with dynamicly allocated memory. In the Message class , nothing is allocated dynamically. Thus using this idiom makes no sense and will make it more complicated to implement due to the pointers that point back. @pezy In this case, `swap` function is special. It will be clear two `Message`'s folders , then swap members, and added themselves to each folders. But, `Message` assignment operator just clear itself, and copy the members, and added itself to each folders. The `rhs` don't need to clear and add to folders. So, if using copy and swap to define, it will be very inefficiency. ## Exercise 13.39 [hpp](ex13_39.h) | [cpp](ex13_39.cpp) ## Exercise 13.40 [hpp](ex13_40.h) | [cpp](ex13_40.cpp) ## Exercise 13.41: >Why did we use postfix increment in the call to construct inside push_back? What would happen if it used the prefix increment? |a|b|c|d|f|..............| ^ ^ ^ elements first_free cap // if use alloc.construct(first_free++, "g"); |a|b|c|d|f|g|.............| ^ ^ ^ elements first_free cap // if use alloc.construct(++first_free, "g"); |a|b|c|d|f|.|g|............| ^ ^ ^ ^ elements | first_free cap | "unconstructed" ## Exercise 13.42: >Test your StrVec class by using it in place of the vector in your TextQuery and QueryResult classes (12.3, p. 484). - StrVec : [hpp](ex13_42_StrVec.h) | [cpp](ex13_42_StrVec.cpp) - TextQuery and QueryResult : [hpp](ex13_42_TextQuery.h) | [cpp](ex13_42_TextQuery.cpp) - Text : [ex13_42.cpp](ex13_42.cpp) ## Exercise 13.43: >Rewrite the free member to use `for_each` and a lambda (10.3.2, p. 388) in place of the for loop to destroy the elements. Which implementation do you prefer, and why? **Rewrite** ```cpp for_each(elements, first_free, [this](std::string &rhs){ alloc.destroy(&rhs); }); ``` @Mooophy: The new version is better. Compared to the old one, it doesn't need to worry about the order and decrement.So more straightforward and handy. The only thing to do for using this approach is to add "&" to build the pointers to string pointers. ## Exercise 13.44: >Write a class named String that is a simplified version of the library string class. Your class should have at least a default constructor and a constructor that takes a pointer to a C-style string. Use an allocator to allocate memory that your String class uses. [hpp](ex13_44_47.h) | [cpp](ex13_44_47.cpp) | [Test](ex13_48.cpp) more information to see [A trivial String class that designed for write-on-paper in an interview](https://github.com/chenshuo/recipes/blob/fcf9486f5155117fb8c36b6b0944c5486c71c421/string/StringTrivial.h) ## Exercise 13.45: >Distinguish between an rvalue reference and an lvalue reference. Definition: - lvalue reference: reference that can bind to **an lvalue**. (Regular reference) - rvalue reference: reference **to an object that is about to be destroyed**. We can bind an rvalue reference to expression that require conversion, to literals, or to expressions that return an rvalue, but we cannot directly bind an rvalue reference to an lvalue. ```cpp int i = 42; int &r = i; // lvalue reference int &&rr = i; // rvalue reference (Error: i is a lvalue) int &r2 = i*42; // lvalue reference (Error: i*42 is a rvalue) const int &r3 = i*42; // reference to const (bind to a rvalue) int &&rr2 = i*42; // rvalue reference ``` - lvalue : functions that return lvalue references, assignment, subscript, dereference, and prefix increment/decrement operator. - rvalue / const reference : functions that return a nonreferences, arithmetic, relational bitwise, postfix increment/decrement operators. ## Exercise 13.46: >Which kind of reference can be bound to the following initializers? ```cpp int f(); vector vi(100); int&& r1 = f(); int& r2 = vi[0]; int& r3 = r1; int&& r4 = vi[0] * f(); ``` ## Exercise 13.47 [hpp](ex13_44_47.h) | [cpp](ex13_44_47.cpp) ## [Exercise 13.48](ex13_48.cpp) ## Exercise 13.49: >Add a move constructor and move-assignment operator to your StrVec, String, and Message classes. - StrVec: [hpp](ex13_49_StrVec.h) | [cpp](ex13_49_StrVec.cpp) - String: [hpp](ex13_49_String.h) | [cpp](ex13_49_String.cpp) - Message:[hpp](ex13_49_Message.h) | [cpp](ex13_49_Message.cpp) ## Exercise 13.50: > Put print statements in the move operations in your String class and rerun the program from exercise 13.48 in 13.6.1 (p. 534) that used a vector to see when the copies are avoided. ```cpp String baz() { String ret("world"); return ret; // first avoided } String s5 = baz(); // second avoided ``` ## Exercise 13.51: >Although `unique_ptrs` cannot be copied, in 12.1.5 (p. 471) we wrote a clone function that returned a unique_ptr by value. Explain why that function is legal and how it works. For such case, move semantics is expected rather than copy operation.That's why a `unique_ptr` may be returned from a function by value. Reference: [StackOverflow - returning unique pointers from functions] (http://stackoverflow.com/questions/4316727)
## Exercise 13.52: >Explain in detail what happens in the assignments of the `HasPtr` objects on page 541. In particular, describe step by step what happens to values of `hp`, `hp2`, and of the `rhs` parameter in the `HasPtr` assignment operator. `rhs` parameter is nonreference, which means the parameter is **copy initialized**. Depending on the type of the argument, copy initialization uses either the *copy constructor* or the *move constructor*. **lvalues are copied and rvalues are moved.** Thus, in `hp = hp2;`, `hp2` is an lvalue, copy constructor used to copy `hp2`. In `hp = std::move(hp2);`, move constructor moves `hp2`. ## Exercise 13.53: >As a matter of low-level efficiency, the `HasPtr` assignment operator is not ideal. Explain why. Implement a copy-assignment and move-assignment operator for `HasPtr` and compare the operations executed in your new move-assignment operator versus the copy-and-swap version. nothing to say, just see the versus codes: [hpp](ex13_53.h) | [cpp](ex13_53.cpp) | [Test](ex13_53_test.cpp) see more information at [this question && answer](http://stackoverflow.com/questions/21010371/why-is-it-not-efficient-to-use-a-single-assignment-operator-handling-both-copy-a). ## Exercise 13.54: >What would happen if we defined a `HasPtr` move-assignment operator but did not change the copy-and-swap operator? Write code to test your answer. ```sh error: ambiguous overload for 'operator=' (operand types are 'HasPtr' and 'std::remove_reference::type { aka HasPtr }') hp1 = std::move(*pH); ^ ``` ## Exercise 13.55: >Add an rvalue reference version of `push_back` to your `StrBlob`. ```cpp void push_back(string &&s) { data->push_back(std::move(s)); } ``` ## Exercise 13.56: >What would happen if we defined sorted as: ```cpp Foo Foo::sorted() const & { Foo ret(*this); return ret.sorted(); } ``` recursion and stack overflow. @miaojiuchen: Because the local variable `ret` here is an Lvalue, so when we call `ret.sorted()`, we are actually not calling the member function `Foo Foo::sorted() &&` as expected, but `Foo Foo::sorted() const &` instead. As a result, the code will be trapped into a recursion and causes a deadly stack overflow. ## Exercise 13.57: >What if we defined sorted as: ```cpp Foo Foo::sorted() const & { return Foo(*this).sorted(); } ``` ok, it will call the move version. ## Exercise 13.58: >Write versions of class Foo with print statements in their sorted functions to test your answers to the previous two exercises. [Exercise 13.58](ex13_58.cpp) ================================================ FILE: ch13/ex13_05.h ================================================ // // ex13_05.h // CP5 // // Created by pezy on 1/5/15. // // Given the following sketch of a class, write a copy constructor that copies all the members. // Your constructor should dynamically allocate a new string and copy the object to which ps points, // rather than copying ps itself. #ifndef CP5_ex13_05_h #define CP5_ex13_05_h #include class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { } private: std::string *ps; int i; }; #endif ================================================ FILE: ch13/ex13_08.h ================================================ // // ex13_08.h // CP5 // // Created by pezy on 1/12/15. // // Write the assignment operator for the HasPtr class from exercise 13.5 in 13.1.1 (p. 499). // As with the copy constructor, your assignment operator should copy the object to which ps points. // // See ex13_05.h #ifndef CP5_ex13_08_h #define CP5_ex13_08_h #include class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { } HasPtr& operator=(const HasPtr &rhs_hp) { if(this != &rhs_hp){ std::string *temp_ps = new std::string(*rhs_hp.ps); delete ps; ps = temp_ps; i = rhs_hp.i; } return *this; } private: std::string *ps; int i; }; #endif ================================================ FILE: ch13/ex13_11.h ================================================ // // ex13_11.h // CP5 // // Created by pezy on 1/13/15. // // Add a destructor to your HasPtr class from the previous exercises. // // See ex13_08.h #ifndef CP5_ex13_11_h #define CP5_ex13_11_h #include class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { } HasPtr& operator=(const HasPtr &hp) { std::string *new_ps = new std::string(*hp.ps); delete ps; ps = new_ps; i = hp.i; return *this; } ~HasPtr() { delete ps; } private: std::string *ps; int i; }; #endif ================================================ FILE: ch13/ex13_13.cpp ================================================ // // ex13_13.cpp // CP5 // // Created by pezy on 1/13/15. // // A good way to understand copy-control members and constructors is to define a simple class with these members in which each member prints its name: // struct X { // X() { std::cout << "X()" << std::endl; } // X(const X&) { std::cout << "X(const X&)" << std::endl; } // }; // Add the copy-assignment operator and destructor to X and write a program using X objects in various ways: // Pass them as nonreference and reference parameters; // dynamically allocate them; // put them in containers; and so forth. // Study the output until you are certain you understand when and why each copy-control member is used. // As you read the output, remember that the compiler can omit calls to the copy constructor. #include #include #include struct X { X() { std::cout << "X()" << std::endl; } X(const X&) { std::cout << "X(const X&)" << std::endl; } X& operator=(const X&) { std::cout << "X& operator=(const X&)" << std::endl; return *this; } ~X() { std::cout << "~X()" << std::endl; } }; void f(const X &rx, X x) { std::vector vec; vec.reserve(2); vec.push_back(rx); vec.push_back(x); } int main() { X *px = new X; f(*px, *px); delete px; return 0; } ================================================ FILE: ch13/ex13_17_1.cpp ================================================ // // ex13_17.cpp // Exercise 13.17 // // Created by pezy on 1/15/15. // Refactoed by acgtyrant Aug 2015 // // Write versions of numbered and f corresponding to the previous three exercises // and check whether you correctly predicted the output. // // For 13.14 #include class numbered { public: numbered() { mysn = unique++; } int mysn; static int unique; }; int numbered::unique = 10; void f(numbered s) { std::cout << s.mysn << std::endl; } int main() { numbered a, b = a, c = b; f(a); f(b); f(c); } ================================================ FILE: ch13/ex13_17_2.cpp ================================================ // // ex13_17.cpp // Exercise 13.17 // // Created by pezy on 1/15/15. // Refactoed by acgtyrant Aug 2015 // // Write versions of numbered and f corresponding to the previous three exercises // and check whether you correctly predicted the output. // // For 13.15 #include class numbered { public: numbered() { mysn = unique++; } numbered(const numbered& n) { mysn = unique++; } int mysn; static int unique; }; int numbered::unique = 10; void f(numbered s) { std::cout << s.mysn << std::endl; } int main() { numbered a, b = a, c = b; f(a); f(b); f(c); } ================================================ FILE: ch13/ex13_17_3.cpp ================================================ // // ex13_17.cpp // Exercise 13.17 // // Created by pezy on 1/15/15. // Refactoed by acgtyrant Aug 2015 // // Write versions of numbered and f corresponding to the previous three exercises // and check whether you correctly predicted the output. // // For 13.16 #include class numbered { public: numbered() { mysn = unique++; } numbered(const numbered& n) { mysn = unique++; } int mysn; static int unique; }; int numbered::unique = 10; void f(const numbered& s) { std::cout << s.mysn << std::endl; } int main() { numbered a, b = a, c = b; f(a); f(b); f(c); } ================================================ FILE: ch13/ex13_18.cpp ================================================ // // ex13_18.cpp // Exercise 13.18 // // Created by pezy on 1/15/15. // // Define an Employee class that contains an employee name and a unique employee identifier. // Give the class a default constructor and a constructor that // takes a string representing the employee’s name. // Each constructor should generate a unique ID by incrementing a static data member. // #include "ex13_18.h" int Employee::s_increment = 0; Employee::Employee() { id_ = s_increment++; } Employee::Employee(const string &name) { id_ = s_increment++; name_ = name; } int main() { return 0; } ================================================ FILE: ch13/ex13_18.h ================================================ // // ex13_18.h // Exercise 13.18 // // Created by pezy on 1/15/15. // // Define an Employee class that contains an employee name and a unique employee identifier. // Give the class a default constructor and a constructor that // takes a string representing the employee’s name. // Each constructor should generate a unique ID by incrementing a static data member. // #ifndef CP5_ex13_18_h #define CP5_ex13_18_h #include using std::string; class Employee { public: Employee(); Employee(const string &name); const int id() const { return id_; } private: string name_; int id_; static int s_increment; }; #endif ================================================ FILE: ch13/ex13_19.h ================================================ // // ex13_19.h // Exercise 13.19 // // Created by pezy on 1/15/15. // // Does your Employee class need to define its own versions of the copy-control members? // If so, why? If not, why not? // Implement whatever copy-control members you think Employee needs. // // Answer: No, cause there really is no sensible meaning. employee can't copy in real world. #ifndef CP5_ex13_19_h #define CP5_ex13_19_h #include using std::string; class Employee { public: Employee(); Employee(const string &name); Employee(const Employee&) = delete; Employee& operator=(const Employee&) = delete; const int id() const { return id_; } private: string name_; int id_; static int s_increment; }; #endif ================================================ FILE: ch13/ex13_22.h ================================================ // // ex13_22.h // Exercise 13.22 // // Created by pezy on 1/13/15. // // Assume that we want HasPtr to behave like a value. // That is, each object should have its own copy of the string to which the objects point. // We'll show the definitions of the copy-control members in the next section. // However, you already know everything you need to know to implement these members. // Write the HasPtr copy constructor and copyassignment operator before reading on. // // See ex13_11.h #ifndef CP5_ex13_11_h #define CP5_ex13_11_h #include class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { } HasPtr& operator=(const HasPtr &hp) { auto new_p = new std::string(*hp.ps); delete ps; ps = new_p; i = hp.i; return *this; } ~HasPtr() { delete ps; } private: std::string *ps; int i; }; #endif ================================================ FILE: ch13/ex13_26.cpp ================================================ // // ex13_26.cpp // Exercise 13.26 // // Created by pezy on 1/19/15. // // Write your own version of the StrBlob class described in the previous exercise. // // @See ex12_22 and ex13_25 #include "ex13_26.h" ConstStrBlobPtr StrBlob::begin() const // should add const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::end() const // should add const { return ConstStrBlobPtr(*this, data->size()); } StrBlob& StrBlob::operator=(const StrBlob& sb) { data = std::make_shared>(*sb.data); return *this; } int main() { return 0; } ================================================ FILE: ch13/ex13_26.h ================================================ // // ex13_26.cpp // Exercise 13.26 // // Created by pezy on 1/19/15. // // Write your own version of the StrBlob class described in the previous exercise. // // @See ex12_22 and ex13_25 #ifndef CP5_ex13_26_h #define CP5_ex13_26_h #include #include #include #include #include using std::vector; using std::string; class ConstStrBlobPtr; class StrBlob { public: using size_type = vector::size_type; friend class ConstStrBlobPtr; ConstStrBlobPtr begin() const; ConstStrBlobPtr end() const; StrBlob():data(std::make_shared>()) { } StrBlob(std::initializer_list il):data(std::make_shared>(il)) { } // copy constructor StrBlob(const StrBlob& sb) : data(std::make_shared>(*sb.data)) { } // copyassignment operators StrBlob& operator=(const StrBlob& sb); size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } string& front() { check(0, "front on empty StrBlob"); return data->front(); } string& back() { check(0, "back on empty StrBlob"); return data->back(); } const string& front() const { check(0, "front on empty StrBlob"); return data->front(); } const string& back() const { check(0, "back on empty StrBlob"); return data->back(); } private: void check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } private: std::shared_ptr> data; }; class ConstStrBlobPtr { public: ConstStrBlobPtr():curr(0) { } ConstStrBlobPtr(const StrBlob &a, size_t sz = 0):wptr(a.data), curr(sz) { } // should add const bool operator!=(ConstStrBlobPtr& p) { return p.curr != curr; } const string& deref() const { // return value should add const auto p = check(curr, "dereference past end"); return (*p)[curr]; } ConstStrBlobPtr& incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } private: std::shared_ptr> check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } std::weak_ptr> wptr; size_t curr; }; #endif ================================================ FILE: ch13/ex13_27.h ================================================ // // ex13_27.h // Exercise 13.27 // // Created by pezy on 1/20/15. // // Define your own reference-counted version of HasPtr. #ifndef CP5_ex13_27_h #define CP5_ex13_27_h #include class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0), use(new size_t(1)) { } HasPtr(const HasPtr &hp) : ps(hp.ps), i(hp.i), use(hp.use) { ++*use; } HasPtr& operator=(const HasPtr &rhs) { ++*rhs.use; if (--*use == 0) { delete ps; delete use; } ps = rhs.ps; i = rhs.i; use = rhs.use; return *this; } ~HasPtr() { if (--*use == 0) { delete ps; delete use; } } private: std::string *ps; int i; size_t *use; }; #endif ================================================ FILE: ch13/ex13_28.cpp ================================================ // // ex13_28.cpp // Exercise 13.28 // // Created by pezy on 1/20/15. // // Given the following classes, implement a default constructor and the necessary copy-control members. #include "ex13_28.h" TreeNode& TreeNode::operator=(const TreeNode &rhs) { ++*rhs.count; if (--*count == 0) { delete left; delete right; delete count; } value = rhs.value; left = rhs.left; right = rhs.right; count = rhs.count; return *this; } BinStrTree& BinStrTree::operator=(const BinStrTree &bst) { TreeNode *new_root = new TreeNode(*bst.root); delete root; root = new_root; return *this; } int main() { return 0; } ================================================ FILE: ch13/ex13_28.h ================================================ // // ex13_28.h // Exercise 13.28 // // Created by pezy on 1/20/15. // // Given the following classes, implement a default constructor and the necessary copy-control members. #ifndef CP5_ex13_28_h #define CP5_ex13_28_h #include using std::string; class TreeNode { public: TreeNode() : value(string()), count(new int(1)), left(nullptr), right(nullptr) { } TreeNode(const TreeNode &rhs) : value(rhs.value), count(rhs.count), left(rhs.left), right(rhs.right) { ++*count; } TreeNode& operator=(const TreeNode &rhs); ~TreeNode() { if (--*count == 0) { delete left; delete right; delete count; } } private: std::string value; int *count; TreeNode *left; TreeNode *right; }; class BinStrTree { public: BinStrTree() : root(new TreeNode()) { } BinStrTree(const BinStrTree &bst) : root(new TreeNode(*bst.root)) { } BinStrTree& operator=(const BinStrTree &bst); ~BinStrTree() { delete root; } private: TreeNode *root; }; #endif ================================================ FILE: ch13/ex13_30.h ================================================ // // ex13_30.h // Exercise 13.30 // // Created by pezy on 1/23/15. // // Write and test a swap function for your valuelike version of HasPtr. // Give your swap a print statement that notes when it is executed. // // See ex13_22.h #ifndef CP5_ex13_11_h #define CP5_ex13_11_h #include #include class HasPtr { public: friend void swap(HasPtr&, HasPtr&); HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { } HasPtr& operator=(const HasPtr &hp) { auto new_p = new std::string(*hp.ps); delete ps; ps = new_p; i = hp.i; return *this; } ~HasPtr() { delete ps; } void show() { std::cout << *ps << std::endl; } private: std::string *ps; int i; }; inline void swap(HasPtr& lhs, HasPtr& rhs) { using std::swap; swap(lhs.ps, rhs.ps); swap(lhs.i, rhs.i); std::cout << "call swap(HasPtr& lhs, HasPtr& rhs)" << std::endl; } #endif ================================================ FILE: ch13/ex13_31.cpp ================================================ // // ex13_31 // Exercise 13.31 // Created by Soyn on 21/7/15. // Add the vector, and sort it. // // Refactored by Yue Wang Jul 2015 // #include #include #include "ex13_31.h" int main(void) { HasPtr s{ "s" }, a{ "a" }, c{ "c" }; std::vector vec{ s, a, c }; std::sort(vec.begin(), vec.end()); for (auto const& elem : vec) elem.show(); return 0; } ================================================ FILE: ch13/ex13_31.h ================================================ // // ex13_31.h // Exercise 13.31 // // Created by pezy on 1/23/15. // // Give your class a < operator and define a vector of HasPtrs. // Give that vector some elements and then sort the vector. // Note when swap is called. // // Refactored by Yue Wang Jul 2015 // #ifndef CP5_ex13_11_h #define CP5_ex13_11_h #include #include class HasPtr { public: friend void swap(HasPtr&, HasPtr&); friend bool operator<(const HasPtr &lhs, const HasPtr &rhs); HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { } HasPtr& operator=(HasPtr tmp) { this->swap(tmp); return *this; } ~HasPtr() { delete ps; } void swap(HasPtr &rhs) { using std::swap; swap(ps, rhs.ps); swap(i, rhs.i); std::cout << "call swap(HasPtr &rhs)" << std::endl; } void show() const { std::cout << *ps << std::endl; } private: std::string *ps; int i; }; void swap(HasPtr& lhs, HasPtr& rhs) { lhs.swap(rhs); } bool operator<(const HasPtr &lhs, const HasPtr &rhs) { return *lhs.ps < *rhs.ps; } #endif ================================================ FILE: ch13/ex13_34_36_37.cpp ================================================ #include "ex13_34_36_37.h" #include void swap(Message &lhs, Message &rhs) { using std::swap; lhs.remove_from_Folders(); // Use existing member function to avoid duplicate code. rhs.remove_from_Folders(); // Use existing member function to avoid duplicate code. swap(lhs.folders, rhs.folders); swap(lhs.contents, rhs.contents); lhs.add_to_Folders(lhs); // Use existing member function to avoid duplicate code. rhs.add_to_Folders(rhs); // Use existing member function to avoid duplicate code. } // Message Implementation void Message::save(Folder &f) { addFldr(&f); // Use existing member function to avoid duplicate code. f.addMsg(this); } void Message::remove(Folder &f) { remFldr(&f); // Use existing member function to avoid duplicate code. f.remMsg(this); } void Message::add_to_Folders(const Message &m) { for (auto f : m.folders) f->addMsg(this); } Message::Message(const Message &m) : contents(m.contents), folders(m.folders) { add_to_Folders(m); } void Message::remove_from_Folders() { for (auto f : folders) f->remMsg(this); // The book added one line here: folders.clear(); but I think it is redundant and more importantly, it will cause a bug: // - In Message::operator=, in the case of self-assignment, it first calls remove_from_Folders() and its folders.clear() // clears the data member of lhs(rhs), and there is no way we can assign it back to lhs. // Refer to: http://stackoverflow.com/questions/29308115/protection-again-self-assignment // - Why is it redundant? As its analogous function Message::add_to_Folders(), Message::remove_from_Folders() should ONLY // take care of the bookkeeping in Folders but not touch the Message's own data members - makes it much clearer and easier // to use. As you can see in the 2 places where we call Message::remove_from_Folders(): in Message::operator=, folders.clear() // introduces a bug as illustrated above; in the destructor ~Message(), the member "folders" will be destroyed anyways, why do // we need to clear it first? } Message::~Message() { remove_from_Folders(); } Message &Message::operator=(const Message &rhs) { remove_from_Folders(); contents = rhs.contents; folders = rhs.folders; add_to_Folders(rhs); return *this; } void Message::print_debug() { std::cout << contents << std::endl; } // Folder Implementation void swap(Folder &lhs, Folder &rhs) { using std::swap; lhs.remove_from_Message(); rhs.remove_from_Message(); swap(lhs.msgs, rhs.msgs); lhs.add_to_Message(lhs); rhs.add_to_Message(rhs); } void Folder::add_to_Message(const Folder &f) { for (auto m : f.msgs) m->addFldr(this); } Folder::Folder(const Folder &f) : msgs(f.msgs) { add_to_Message(f); } void Folder::remove_from_Message() { for (auto m : msgs) m->remFldr(this); } Folder::~Folder() { remove_from_Message(); } Folder &Folder::operator=(const Folder &rhs) { remove_from_Message(); msgs = rhs.msgs; add_to_Message(rhs); return *this; } void Folder::print_debug() { for (auto m : msgs) std::cout << m->contents << " "; std::cout << std::endl; } int main() { return 0; } ================================================ FILE: ch13/ex13_34_36_37.h ================================================ // // ex13_34_36_37.h // Exercise 13.34 13.36 13.37 // // Created by pezy on 1/26/15. // // 34: Write the Message class as described in this section. // // 36: Design and implement the corresponding Folder class. That class should hold a set that points to the Messages in that Folder. // // 37: Add members to the Message class to insert or remove a given Folder* into folders. // These members are analogous to Folder’s addMsg and remMsg operations. #ifndef CP5_ex13_34_36_37_h #define CP5_ex13_34_36_37_h #include #include class Folder; class Message { friend void swap(Message &, Message &); friend class Folder; public: explicit Message(const std::string &str = ""):contents(str) { } Message(const Message&); Message& operator=(const Message&); ~Message(); void save(Folder&); void remove(Folder&); void print_debug(); private: std::string contents; std::set folders; void add_to_Folders(const Message&); void remove_from_Folders(); void addFldr(Folder *f) { folders.insert(f); } void remFldr(Folder *f) { folders.erase(f); } }; void swap(Message&, Message&); class Folder { friend void swap(Folder &, Folder &); friend class Message; public: Folder() = default; Folder(const Folder &); Folder& operator=(const Folder &); ~Folder(); void print_debug(); private: std::set msgs; void add_to_Message(const Folder&); void remove_from_Message(); void addMsg(Message *m) { msgs.insert(m); } void remMsg(Message *m) { msgs.erase(m); } }; void swap(Folder &, Folder &); #endif // MESSAGE ================================================ FILE: ch13/ex13_39.cpp ================================================ // // ex13_39.cpp // Exercise 13.39 // // Created by pezy on 2/3/15. // // Write your own version of StrVec, including versions of // reserve, capacity (9.4, p. 356), and resize (9.3.5, p. 352). // #include "ex13_39.h" void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for (auto p = first_free; p != elements;) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } StrVec::StrVec(const StrVec &rhs) { auto newdata = alloc_n_copy(rhs.begin(), rhs.end()); elements = newdata.first; first_free = cap = newdata.second; } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } int main() { return 0; } ================================================ FILE: ch13/ex13_39.h ================================================ // // ex13_39.h // Exercise 13.39 // // Created by pezy on 2/3/15. // // Write your own version of StrVec, including versions of // reserve, capacity (9.4, p. 356), and resize (9.3.5, p. 352). // #ifndef CP5_EX_13_39_H_ #define CP5_EX_13_39_H_ #include #include class StrVec { public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(const StrVec&); StrVec& operator=(const StrVec&); ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; #endif ================================================ FILE: ch13/ex13_40.cpp ================================================ // // ex13_40.cpp // Exercise 13.40 // // Created by pezy on 2/3/15. // // Add a constructor that takes an initializer_list to your StrVec class. // #include "ex13_40.h" void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for (auto p = first_free; p != elements;) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } void StrVec::range_initialize(const std::string *first, const std::string *last) { auto newdata = alloc_n_copy(first, last); elements = newdata.first; first_free = cap = newdata.second; } StrVec::StrVec(const StrVec &rhs) { range_initialize(rhs.begin(), rhs.end()); } StrVec::StrVec(std::initializer_list il) { range_initialize(il.begin(), il.end()); } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } int main() { return 0; } ================================================ FILE: ch13/ex13_40.h ================================================ // // ex13_40.h // Exercise 13.40 // // Created by pezy on 2/3/15. // // Add a constructor that takes an initializer_list to your StrVec class. // #ifndef CP5_EX_13_40_H_ #define CP5_EX_13_40_H_ #include #include #include class StrVec { public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(const StrVec&); StrVec(std::initializer_list); StrVec& operator=(const StrVec&); ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; #endif ================================================ FILE: ch13/ex13_42.cpp ================================================ #include "ex13_42_TextQuery.h" #include void runQueries(std::ifstream &infile) { TextQuery tq(infile); while (true) { std::cout << "enter word to look for, or q to quit: "; std::string s; if (!(std::cin >> s) || s == "q") break; print(std::cout, tq.query(s)) << std::endl; } } int main() { std::ifstream file("../data/storyDataFile.txt"); runQueries(file); } ================================================ FILE: ch13/ex13_42_StrVec.cpp ================================================ #include "ex13_42_StrVec.h" std::allocator StrVec::alloc; void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for (auto p = first_free; p != elements;) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } void StrVec::range_initialize(const std::string *first, const std::string *last) { auto newdata = alloc_n_copy(first, last); elements = newdata.first; first_free = cap = newdata.second; } StrVec::StrVec(const StrVec &rhs) { range_initialize(rhs.begin(), rhs.end()); } StrVec::StrVec(std::initializer_list il) { range_initialize(il.begin(), il.end()); } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } ================================================ FILE: ch13/ex13_42_StrVec.h ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include class StrVec { public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(const StrVec&); StrVec(std::initializer_list); StrVec& operator=(const StrVec&); ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; #endif ================================================ FILE: ch13/ex13_42_TextQuery.cpp ================================================ #include "ex13_42_TextQuery.h" #include #include using std::string; TextQuery::TextQuery(std::ifstream &ifs) : input(new StrVec) { size_t lineNo = 0; for (string line; std::getline(ifs, line); ++lineNo) { input->push_back(line); std::istringstream line_stream(line); for (string text, word; line_stream >> text; word.clear()) { // avoid read a word followed by punctuation(such as: word, ) std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct); // use reference avoid count of shared_ptr add. auto &nos = result[word]; if (!nos) nos.reset(new std::set); nos->insert(lineNo); } } } QueryResult TextQuery::query(const string& str) const { // use static just allocate once. static std::shared_ptr> nodate(new std::set); auto found = result.find(str); if (found == result.end()) return QueryResult(str, nodate, input); else return QueryResult(str, found->second, input); } std::ostream& print(std::ostream &out, const QueryResult& qr) { out << qr.word << " occurs " << qr.nos->size() << (qr.nos->size() > 1 ? " times" : " time") << std::endl; for (auto i : *qr.nos) out << "\t(line " << i+1 << ") " << qr.input->at(i) << std::endl; return out; } ================================================ FILE: ch13/ex13_42_TextQuery.h ================================================ #ifndef CP5_TEXTQUERY_H_ #define CP5_TEXTQUERY_H_ #include #include #include #include #include #include #include "ex13_42_StrVec.h" class QueryResult; class TextQuery { public: TextQuery(std::ifstream &); QueryResult query(const std::string&) const; private: std::shared_ptr input; std::map>> result; }; class QueryResult { public: friend std::ostream& print(std::ostream &, const QueryResult&); public: QueryResult(const std::string &s, std::shared_ptr> set, std::shared_ptr v) : word(s), nos(set), input(v) { } private: std::string word; std::shared_ptr> nos; std::shared_ptr input; }; std::ostream& print(std::ostream &, const QueryResult&); #endif ================================================ FILE: ch13/ex13_44_47.cpp ================================================ #include "ex13_44_47.h" #include #include std::pair String::alloc_n_copy(const char *b, const char *e) { auto str = alloc.allocate(e - b); return{ str, std::uninitialized_copy(b, e, str) }; } void String::range_initializer(const char *first, const char *last) { auto newstr = alloc_n_copy(first, last); elements = newstr.first; end = newstr.second; } String::String(const char *s) { char *sl = const_cast(s); while (*sl) ++sl; range_initializer(s, ++sl); } String::String(const String& rhs) { range_initializer(rhs.elements, rhs.end); std::cout << "copy constructor" << std::endl; } void String::free() { if (elements) { std::for_each(elements, end, [this](char &c){ alloc.destroy(&c); }); alloc.deallocate(elements, end - elements); } } String::~String() { free(); } String& String::operator = (const String &rhs) { auto newstr = alloc_n_copy(rhs.elements, rhs.end); free(); elements = newstr.first; end = newstr.second; std::cout << "copy-assignment" << std::endl; return *this; } ================================================ FILE: ch13/ex13_44_47.h ================================================ #ifndef CP5_STRING_H__ #define CP5_STRING_H__ #include class String { public: String() : String("") { } String(const char *); String(const String&); String& operator=(const String&); ~String(); const char *c_str() const { return elements; } size_t size() const { return end - elements; } size_t length() const { return end - elements - 1; } private: std::pair alloc_n_copy(const char*, const char*); void range_initializer(const char*, const char*); void free(); private: char *elements; char *end; std::allocator alloc; }; #endif ================================================ FILE: ch13/ex13_48.cpp ================================================ #include "ex13_44_47.h" #include #include // Test reference to http://coolshell.cn/articles/10478.html void foo(String x) { std::cout << x.c_str() << std::endl; } void bar(const String& x) { std::cout << x.c_str() << std::endl; } String baz() { String ret("world"); return ret; } int main() { char text[] = "world"; String s0; String s1("hello"); String s2(s0); String s3 = s1; String s4(text); s2 = s1; foo(s1); bar(s1); foo("temporary"); bar("temporary"); String s5 = baz(); std::vector svec; svec.reserve(8); svec.push_back(s0); svec.push_back(s1); svec.push_back(s2); svec.push_back(s3); svec.push_back(s4); svec.push_back(s5); svec.push_back(baz()); svec.push_back("good job"); for (const auto &s : svec) { std::cout << s.c_str() << std::endl; } } ================================================ FILE: ch13/ex13_49_Message.cpp ================================================ #include "ex13_49_Message.h" #include void swap(Message &lhs, Message &rhs) { using std::swap; for (auto f : lhs.folders) f->remMsg(&lhs); std::cout << "Remove message from folders" << std::endl; // debug for (auto f : rhs.folders) f->remMsg(&rhs); std::cout << "Remove message from folders" << std::endl; // debug swap(lhs.folders, rhs.folders); swap(lhs.contents, rhs.contents); std::cout << "Message members swaped" << std::endl; // debug for (auto f : lhs.folders) f->addMsg(&lhs); std::cout << "Added message to folders" << std::endl; // debug for (auto f : rhs.folders) f->addMsg(&rhs); std::cout << "Added message to folders" << std::endl; // debug } Message::Message(const Message &m) : contents(m.contents), folders(m.folders) { add_to_Folders(m); } Message::~Message() { remove_from_Folders(); } void Message::save(Folder &f) { folders.insert(&f); f.addMsg(this); } void Message::remove(Folder &f) { folders.erase(&f); f.remMsg(this); } void Message::add_to_Folders(const Message &m) { for (auto f : m.folders) f->addMsg(this); std::cout << "Added message to folders" << std::endl; // debug } void Message::remove_from_Folders() { for (auto f : folders) f->remMsg(this); std::cout << "Remove message from folders" << std::endl; // debug } Message& Message::operator=(const Message &rhs) { remove_from_Folders(); contents = rhs.contents; folders = rhs.folders; std::cout << "Message members assgined" << std::endl; // debug add_to_Folders(rhs); return *this; } void Message::print_debug() { std::cout << contents << ": "; for (auto f : folders) std::cout << f->fldr() << " "; std::cout << std::endl; } void Message::move_Folders(Message *m) { folders = std::move(m->folders); for (auto f : folders) { f->remMsg(m); f->addMsg(this); } m->folders.clear(); } Message::Message(Message &&m) : contents(std::move(m.contents)) { move_Folders(&m); } Message& Message::operator= (Message &&rhs) { if (this != &rhs) { remove_from_Folders(); contents = std::move(rhs.contents); move_Folders(&rhs); } std::cout << "Message members moved" << std::endl; // debug return *this; } // Folder Implementation void swap(Folder &lhs, Folder &rhs) { using std::swap; for (auto m : lhs.msgs) m->remFldr(&lhs); std::cout << "clear folder" << std::endl; // debug for (auto m : rhs.msgs) m->remFldr(&rhs); std::cout << "clear folder" << std::endl; // debug swap(lhs.name, rhs.name); swap(lhs.msgs, rhs.msgs); std::cout << "Folder members swaped" << std::endl; // debug for (auto m : lhs.msgs) m->addFldr(&lhs); std::cout << "Added messages to folder" << std::endl; // debug for (auto m : rhs.msgs) m->addFldr(&rhs); std::cout << "Added messages to folder" << std::endl; // debug } void Folder::add_to_Message(const Folder &f) { for (auto m : f.msgs) m->addFldr(this); std::cout << "Added messages to folder" << std::endl; // debug } Folder::Folder(const Folder &f) : name(f.name), msgs(f.msgs) { add_to_Message(f); } void Folder::remove_from_Message() { for (auto m : msgs) m->remFldr(this); std::cout << "clear folder" << std::endl; // debug } Folder::~Folder() { remove_from_Message(); } Folder& Folder::operator =(const Folder &rhs) { remove_from_Message(); name = rhs.name; msgs = rhs.msgs; std::cout << "Folder members assigned" << std::endl; // debug add_to_Message(rhs); return *this; } void Folder::print_debug() { std::cout << name << ": "; for (auto m : msgs) std::cout << m->msg() << " "; std::cout << std::endl; } void Folder::move_Messages(Folder *f) { msgs = std::move(f->msgs); for (auto m : msgs) { m->remFldr(f); m->addFldr(this); } f->msgs.clear(); } Folder::Folder(Folder&& f) : name(std::move(f.name)) { move_Messages(&f); } Folder& Folder::operator=(Folder &&rhs) { if (this != &rhs) { remove_from_Message(); name = std::move(rhs.name); move_Messages(&rhs); } std::cout << "Message members moved" << std::endl; // debug return *this; } ================================================ FILE: ch13/ex13_49_Message.h ================================================ #ifndef CP5_MESSAGE_H_ #define CP5_MESSAGE_H_ #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class Folder; class Message { friend void swap(Message &, Message &); friend void swap(Folder &, Folder &); friend class Folder; public: explicit Message(const std::string &str = "") : contents(str) { } Message(const Message&); Message& operator=(const Message&); Message(Message &&m); // need to update the Folders Message& operator=(Message&&); // need to update the Folders ~Message(); void save(Folder&); void remove(Folder&); const std::string& msg() const { return contents; } void print_debug(); private: void add_to_Folders(const Message&); void remove_from_Folders(); void addFldr(Folder *f) { folders.insert(f); } void remFldr(Folder *f) { folders.erase(f); } void move_Folders(Message*); // define function to do the common work private: std::string contents; std::set folders; }; void swap(Message&, Message&); class Folder { friend void swap(Message&, Message&); friend void swap(Folder &, Folder &); friend class Message; public: explicit Folder(const std::string &str = "") :name(str) { } Folder(const Folder &); Folder& operator=(const Folder &); Folder(Folder &&f); // need to update the Messages Folder& operator=(Folder &&); // need to update the Messages ~Folder(); const std::string& fldr() const { return name; } void print_debug(); private: std::string name; std::set msgs; void add_to_Message(const Folder&); void remove_from_Message(); void addMsg(Message *m) { msgs.insert(m); } void remMsg(Message *m) { msgs.erase(m); } void move_Messages(Folder*); // define function to do the common work }; void swap(Folder &, Folder &); #endif ================================================ FILE: ch13/ex13_49_StrVec.cpp ================================================ #include "ex13_49_StrVec.h" #include // for_each void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for_each(elements, first_free, [this](std::string &rhs){ alloc.destroy(&rhs); }); alloc.deallocate(elements, cap - elements); } } void StrVec::range_initialize(const std::string *first, const std::string *last) { auto newdata = alloc_n_copy(first, last); elements = newdata.first; first_free = cap = newdata.second; } StrVec::StrVec(const StrVec &rhs) { range_initialize(rhs.begin(), rhs.end()); } StrVec::StrVec(std::initializer_list il) { range_initialize(il.begin(), il.end()); } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } StrVec::StrVec(StrVec &&s) NOEXCEPT : elements(s.elements), first_free(s.first_free), cap(s.cap) { // leave s in a state in which it is safe to run the destructor. s.elements = s.first_free = s.cap = nullptr; } StrVec& StrVec::operator = (StrVec &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; } ================================================ FILE: ch13/ex13_49_StrVec.h ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrVec { public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list); StrVec(const StrVec&); StrVec& operator=(const StrVec&); StrVec(StrVec&&) NOEXCEPT; StrVec& operator=(StrVec&&)NOEXCEPT; ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; static std::allocator alloc; }; #endif ================================================ FILE: ch13/ex13_49_String.cpp ================================================ #include "ex13_49_String.h" #include std::pair String::alloc_n_copy(const char *b, const char *e) { auto str = alloc.allocate(e - b); return{ str, std::uninitialized_copy(b, e, str) }; } void String::range_initializer(const char *first, const char *last) { auto newstr = alloc_n_copy(first, last); elements = newstr.first; end = newstr.second; } String::String(const char *s) { char *sl = const_cast(s); while (*sl) ++sl; range_initializer(s, ++sl); } String::String(const String& rhs) { range_initializer(rhs.elements, rhs.end); } void String::free() { if (elements) { std::for_each(elements, end, [this](char &c){ alloc.destroy(&c); }); alloc.deallocate(elements, end - elements); } } String::~String() { free(); } String& String::operator = (const String &rhs) { auto newstr = alloc_n_copy(rhs.elements, rhs.end); free(); elements = newstr.first; end = newstr.second; return *this; } String::String(String &&s) NOEXCEPT : elements(s.elements), end(s.end) { s.elements = s.end = nullptr; } String& String::operator = (String &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; end = rhs.end; rhs.elements = rhs.end = nullptr; } return *this; } ================================================ FILE: ch13/ex13_49_String.h ================================================ #ifndef CP5_STRING_H__ #define CP5_STRING_H__ #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class String { public: String() : String("") { } String(const char *); String(const String&); String& operator=(const String&); String(String &&) NOEXCEPT; String& operator=(String&&)NOEXCEPT; ~String(); const char *c_str() const { return elements; } size_t size() const { return end - elements; } size_t length() const { return end - elements - 1; } private: std::pair alloc_n_copy(const char*, const char*); void range_initializer(const char*, const char*); void free(); private: char *elements; char *end; std::allocator alloc; }; #endif ================================================ FILE: ch13/ex13_53.cpp ================================================ #include "ex13_53.h" #include inline void swap(HasPtr &lhs, HasPtr &rhs) { using std::swap; swap(lhs.ps, rhs.ps); swap(lhs.i, rhs.i); std::cout << "call swap" << std::endl; } HasPtr::HasPtr(const std::string &s) : ps(new std::string(s)), i(0) { std::cout << "call constructor" << std::endl; } HasPtr::HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { std::cout << "call copy constructor" << std::endl; } HasPtr::HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) { p.ps = 0; std::cout << "call move constructor" << std::endl; } HasPtr& HasPtr::operator=(HasPtr rhs) { swap(*this, rhs); return *this; } HasPtr::~HasPtr() { std::cout << "call destructor" << std::endl; delete ps; } int main() { return 0; } ================================================ FILE: ch13/ex13_53.h ================================================ #ifndef CP5_ex13_53_h #define CP5_ex13_53_h #include class HasPtr { public: friend void swap(HasPtr&, HasPtr&); HasPtr(const std::string &s = std::string()); HasPtr(const HasPtr &hp); HasPtr(HasPtr &&p) noexcept; HasPtr& operator=(HasPtr rhs); //HasPtr& operator=(const HasPtr &rhs); //HasPtr& operator=(HasPtr &&rhs) noexcept; ~HasPtr(); private: std::string *ps; int i; }; #endif // CP5_ex13_53_h ================================================ FILE: ch13/ex13_53_test.cpp ================================================ #include "ex13_53.h" int main() { HasPtr hp1("hello"), hp2("World"), *pH = new HasPtr("World"); hp1 = hp2; hp1 = std::move(*pH); } ================================================ FILE: ch13/ex13_58.cpp ================================================ #include #include #include using std::vector; using std::sort; class Foo { public: Foo sorted() &&; Foo sorted() const &; private: vector data; }; Foo Foo::sorted() && { sort(data.begin(), data.end()); std::cout << "&&" << std::endl; // debug return *this; } Foo Foo::sorted() const & { // Foo ret(*this); // sort(ret.data.begin(), ret.data.end()); // return ret; std::cout << "const &" << std::endl; // debug // Foo ret(*this); // ret.sorted(); // Exercise 13.56 // return ret; return Foo(*this).sorted(); // Exercise 13.57 } int main() { Foo().sorted(); // call "&&" Foo f; f.sorted(); // call "const &" } ================================================ FILE: ch14/README.md ================================================ # Chapter 14. Overloaded Operations and Conversions ## Exercise 14.1: >In what ways does an overloaded operator differ from a built-in operator? In what ways are overloaded operators the same as the built-in operators? **Differ** 1. We can call an overloaded operator function directly. 2. An overloaded operator function must either be a member of a class or have at least one parameter of class type. 3. A few operators guarantee the order in which operands are evaluated. These overloaded versions of these operators do not preserve order of evaluation and/or short-circuit evaluation, it is usually a bad idea to overload them. > In particular, the operand-evaluation guarantees of the logical `AND`, logical `OR`, and comma operators are not preserved, Moreover, overloaded versions of `&&` or `||` operators do not preserve short-circuit evaluation properties of the built-in operators. Both operands are always evaluated. **Same** - An overloaded operator has the same precedence and associativity as the corresponding built-in operator. ## Exercise 14.2: >Write declarations for the overloaded input, output, addition, and compound-assignment operators for `Sales_data`. [hpp](ex14_02.h) | [cpp](ex14_02.cpp) ## Exercise 14.3: >Both `string` and `vector` define an overloaded == that can be used to compare objects of those types. Assuming `svec1` and `svec2 `are `vectors` that hold `strings`, identify which version of == is applied in each of the following expressions: - (a) `"cobble" == "stone"` - (b) `svec1[0] == svec2[0]` - (c) `svec1 == svec2` - (d) `"svec1[0] == "stone"` (a) neither. (b) `string` (c) `vector` (d) `string` ----- **Reference** - [Why does the following not invoke the overloaded operator== (const String &, const String &)? “cobble” == “stone”](http://stackoverflow.com/questions/2690737/why-does-the-following-not-invoke-the-overloaded-operator-const-string-con) ## Exercise 14.4: >Explain how to decide whether the following should be class members: - (a) % - (b) %= - (c) ++ - (d) -> - (e) << - (f) && - (g) == - (h) () (a) symmetric operator. Hence, non-member (b) changing state of objects. Hence, member (c) changing state of objects. Hence, member (d) = () [] -> must be member (e) non-member (f) symetric , non-member (g) symetric , non-member (h) = () [] -> must be member ## Exercise 14.5: >In exercise 7.40 from 7.5.1 (p. 291) you wrote a sketch of one of the following classes. Decide what, if any, overloaded operators your class should provide. Such as `Book` [hpp](ex14_05.h) | [cpp](ex14_05.cpp) | [test](ex14_05_TEST.cpp) ## Exercise 14.6: >Define an output operator for your `Sales_data` class. see [Exercise 14.2](#exercise-142). ## Exercise 14.7: >Define an output operator for you `String` class you wrote for the exercises in 13.5 (p. 531). [hpp](ex14_07.h) | [cpp](ex14_07.cpp) | [Test](ex14_07_TEST.cpp) ## Exercise 14.8: >Define an output operator for the class you chose in exercise 7.40 from 7.5.1 (p. 291). see [Exercise 14.5](#exercise-145) ## Exercise 14.9: >Define an input operator for your Sales_data class. see [Exercise 14.2](#exercise-142). ## Exercise 14.10: >Describe the behaviour of the Sales_data input operator if given the following input: - (a) 0-201-99999-9 10 24.95 - (b) 10 24.95 0-210-99999-9 - (a) correct format. - (b) ilegal input. But `.95` will be converted to a float stored in this object. As a result, the data inside will be a wrong one. Output: `10 24 22.8 0.95` check [Test](ex14_02_TEST.cpp) ## Exercise 14.11: >What, if anything, is wrong with the following Sales_data input operator? What would happen if we gave this operator the data in the previous exercise? ```cpp istream& operator>>(istream& in, Sales_data& s) { double price; in >> s.bookNo >> s.units_sold >> price; s.revenue = s.units_sold * price; return in; } ``` no input check. nothing happend. ## Exercise 14.12: >Define an input operator for the class you used in exercise 7.40 from 7.5.1 (p. 291). Be sure the operator handles input errors. see [Exercise 14.5](#exercise-145) ## Exercise 14.13: >Which other arithmetic operators (Table 4.1 (p. 139)), if any, do you think Sales_data ought to support? Define any you think the class should include. Substraction, in order to be able to revert a previously addition. [hpp](ex14_13.h) | [cpp](ex14_13.cpp) | [Test](ex14_13_TEST.cpp) ## Exercise 14.14: >Why do you think it is more efficient to define `operator+` to call `operator+=` rather than the other way around? Discussing on [SO](http://stackoverflow.com/questions/21071167/why-is-it-more-efficient-to-define-operator-to-call-operator-rather-than-the). ## Exercise 14.15: >Should the class you chose for exercise 7.40 from 7.5.1 (p. 291) define any of the arithmetic operators? If so, implement them. If not, explain why not. [hpp](ex14_15.h) | [cpp](ex14_15.cpp) | [Test](ex14_15_TEST.cpp) ## Exercise 14.16: >Define equality and inequality operators for your `StrBlob` (12.1.1, p. 456), `StrBlobPtr` (12.1.6, p. 474), `StrVec` (13.5, p.526), and `String` (13.5, p. 531) classes. - `StrBlob` & `StrBlobPtr`: [hpp](ex14_16_StrBlob.h) | [cpp](ex14_16_StrBlob.cpp) | [Test](ex14_16_StrBlobTest.cpp) - `StrVec`: [hpp](ex14_16_StrVec.h) | [cpp](ex14_16_StrVec.cpp) | [Test](ex14_16_StrVecMain.cpp) - `String`: [hpp](ex14_16_String.h) | [cpp](ex14_16_String.cpp) | [Test](ex14_16_StringMain.cpp) ## Exercise 14.17: >Should the class you chose for exercise 7.40 from 7.5.1(p. 291) define the equality operators? If so, implement them. If not, explain why not. yes.see [Exercise 14.15](#exercise-1415) ## Exercise 14.18: >Define relational operators for your `StrBlob`, `StrBlobPtr`, `StrVec`, and `String` classes. - `StrBlob` & `StrBlobPtr`: [hpp](ex14_18_StrBlob.h) | [cpp](ex14_18_StrBlob.cpp) | [Test](ex14_18_StrBlobTest.cpp) - `StrVec`: [hpp](ex14_18_StrVec.h) | [cpp](ex14_18_StrVec.cpp) | [Test](ex14_18_StrVecMain.cpp) - `String`: [hpp](ex14_18_String.h) | [cpp](ex14_18_String.cpp) | [Test](ex14_18_StringMain.cpp) ## Exercise 14.19: >Should the class you chose for exercise 7.40 from 7.5.1 (p. 291) define the relational operators? If so, implement them. If not, explain why not. yes.see [Exercise 14.15](#exercise-1415) ## Exercise 14.20: >Define the addition and compound-assignment operators for your `Sales_data` class. see [Exercise 14.2](#exercise-142). ## Exercise 14.21: >Write the `Sales_data` operators so that `+` does the actual addition and `+=` calls `+`. Discuss the disadvantages of this approach compared to the way these operators were defined in 14.3 (p. 560) and 14.4 (p.564). ```cpp Sales_data& Sales_data::operator+=(const Sales_data &rhs) { Sales_data old_data = *this; *this = old_data + rhs; return *this; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum; sum.units_sold = lhs.units_sold + rhs.units_sold; sum.revenue = lhs.revenue + rhs.revenue; return sum; } ``` **Disadvantages**: Both `+` and `+=`, uses an temporary object of `Sales_data`. But it is no need for that. ## Exercise 14.22: >Define a version of the assignment operator that can assign a `string` representing an ISBN to a `Sales_data`. [hpp](ex14_22.h) | [cpp](ex14_22.cpp) | [Test](ex14_22_TEST.cpp) ## Exercise 14.23: >Define an initializer_list assignment operator for your version of the `StrVec` class. [hpp](ex14_23.h) | [cpp](ex14_23.cpp) | [Test](ex14_23_TEST.cpp) ## Exercise 14.24: >Decide whether the class you used in exercise 7.40 from 7.5.1 (p. 291) needs a copy- and move-assignment operator. If so, define those operators. [hpp](ex14_24.h) | [cpp](ex14_24.cpp) | [Test](ex14_24_TEST.cpp) ## Exercise 14.25: >Implement any other assignment operators your class should define. Explain which types should be used as operands and why. see [Exercise 14.24](#exercise-1424) ## Exercise 14.26: >Define subscript operators for your `StrVec`, `String`, `StrBlob`, and `StrBlobPtr` classes. - `StrBlob` & `StrBlobPtr`: [hpp](ex14_26_StrBlob.h) | [cpp](ex14_26_StrBlob.cpp) | [Test](ex14_26_StrBlobTest.cpp) - `StrVec`: [hpp](ex14_26_StrVec.h) | [cpp](ex14_26_StrVec.cpp) | [Test](ex14_26_StrVecMain.cpp) - `String`: [hpp](ex14_26_String.h) | [cpp](ex14_26_String.cpp) | [Test](ex14_26_StringMain.cpp) ## Exercise 14.27: > Add increment and decrement operators to your `StrBlobPtr` class. [hpp](ex14_27_28_StrBlob.h) | [cpp](ex14_27_28_StrBlob.cpp) | [Test](ex14_27_28_StrBlobTest.cpp) ## Exercise 14.28: > Define addition and subtraction for `StrBlobPtr` so that these operators implement pointer arithmetic (3.5.3, p. 119). see [Exercise 14.27](#exercise-1427) ## Exercise 14.29: > We did not define a `const` version of the increment and decrement operators. Why not? Because `++` and `--` change the state of the object. Hence , it's meaningless to do so. ## Exercise 14.30: > Add dereference and arrow operators to your `StrBlobPtr` class and to the `ConstStrBlobPtr` class that you defined in exercise 12.22 from 12.1.6 (p. 476). Note that the operators in `constStrBlobPtr` must return `const` references because the `data` member in `constStrBlobPtr` points to a `const vector`. [hpp](ex14_30_StrBlob.h) | [cpp](ex14_30_StrBlob.cpp) | [Test](ex14_30_StrBlobTest.cpp) ## Exercise 14.31: > Our StrBlobPtr class does not define the copy constructor, assignment operator, or a destructor. Why is that okay? Applying the Rule of 3/5: There is no dynamic allocation to deal with, so the synthesized destructor is enough. Moreover, no unique is needed. Hence, the synthesized ones can handle all the corresponding operations. ## Exercise 14.32: > Define a class that holds a pointer to a `StrBlobPtr`. Define the overloaded arrow operator for that class. [hpp](ex14_32.h) | [cpp](ex14_32.cpp) ## Exercise 14.33: > How many operands may an overloaded function-call operator take? An overloaded operator function has the same number of parameters as the operator has operands. Hence the maximum value should be around 256. ([question on SO](http://stackoverflow.com/questions/21211889/how-many-operands-may-an-overloaded-function-call-operator-take)) ## Exercise 14.34: > Define a function-object class to perform an if-then-else operation: The call operator for this class should take three parameters. It should test its first parameter and if that test succeeds, it should return its second parameter; otherwise, it should return its third parameter. ```cpp struct Test { int operator()(bool b, int iA, int iB) { return b ? iA : iB; } }; ``` ## Exercise 14.35: > Write a class like `PrintString` that reads a line of input from an `istream` and returns a `string` representing what was read. If the read fails, return the empty `string`. [Test](ex14_35.cpp) ## Exercise 14.36: > Use the class from the previous exercise to read the standard input, storing each line as an element in a vector. [Test](ex14_36.cpp) ## Exercise 14.37: > Write a class that tests whether two values are equal. Use that object and the library algorithms to write a program to replace all instances of a given value in a sequence. [Test](ex14_37.cpp) ## Exercise 14.38: > Write a class that tests whether the length of a given `string` matches a given bound. Use that object to write a program to report how many words in an input file are of sizes 1 through 10 inclusive. [BoundTest](ex14_38_39.cpp) ## Exercise 14.39: > Revise the previous program to report the count of words that are sizes 1 through 9 and 10 or more. see [Exercise 14.38](#exercise-1438) ## Exercise 14.40: > Rewrite the `biggies` function from 10.3.2 (p. 391) to use function-object classes in place of lambdas. [Test](ex14_40.cpp) ## Exercise 14.41: > Why do you suppose the new standard added lambdas? Explain when you would use a lambda and when you would write a class instead. IMO, lambda is quite handy to use. Lambda can be used when the functor is not used frequently nor complicated, whereas functor is supposed to call more times than lambda or quite complicated to implement as a lambda. ## Exercise 14.42: > Using library function objects and adaptors, define an expression to - (a) Count the number of values that are greater than 1024 - (b) Find the first string that is not equal to `pooh` - (c) Multiply all values by 2 ```cpp std::count_if(ivec.cbegin(), ivec.cend(), std::bind(std::greater(), _1, 1024)); std::find_if(svec.cbegin(), svec.cend(), std::bind(std::not_equal_to(), _1, "pooh")); std::transform(ivec.begin(), ivec.end(), ivec.begin(), std::bind(std::multiplies(), _1, 2)); ``` [Test](ex14_42.cpp) ## Exercise 14.43: > Using library function objects, determine whether a given `int` value is divisible by any element in a container of `int`s. [ex14_43.cpp](ex14_43.cpp) ## Exercise 14.44: > Write your own version of a simple desk calculator that can handle binary operations. [ex14_44.cpp](ex14_44.cpp) ## Exercise 14.45: > Write conversion operators to convert a `Sales_data` to `string` and to `double`. What values do you think these operators should return? [hpp](ex14_45.h) | [cpp](ex14_45.cpp) | [Test](ex14_45_TEST.cpp) ## Exercise 14.46: > Explain whether defining these Sales_data conversion operators is a good idea and whether they should be explicit. It's a bad idea to do so, because these conversion is misleading.`explicit` should be added to prevent implicit conversion. ## Exercise 14.47: > Explain the difference between these two conversion operators: ```cpp struct Integral { operator const int(); // meaningless, it will be ignored by compiler. operator int() const; // promising that this operator will not change the state of the obj }; ``` ## Exercise 14.48: > Determine whether the class you used in exercise 7.40 from 7.5.1 (p. 291) should have a conversion to `bool`. If so, explain why, and explain whether the operator should be `explicit`. If not, explain why not. A conversion to bool can be useful for the class Date. But it must be an explicit one to prevent any automatic conversion. ## Exercise 14.49: > Regardless of whether it is a good idea to do so, define a conversion to bool for the class from the previous exercise. [hpp](ex14_49.h) | [cpp](ex14_49.cpp) | [Test](ex14_49_TEST.cpp) ## Exercise 14.50: > Show the possible class-type conversion sequences for the initializations of ex1 and ex2. Explain whether the initializations are legal or not. ```cpp struct LongDouble { LongDouble(double = 0.0); operator double(); operator float(); }; LongDouble ldObj; int ex1 = ldObj; // error ambiguous: double or float? float ex2 = ldObj; // legal ``` ## Exercise 14.51: > Show the conversion sequences (if any) needed to call each version of `calc` and explain why the best viable function is selected. ```cpp void calc(int); void calc(LongDouble); double dval; calc(dval); // which calc? ``` best viable function: `void calc(int)`. cause class-type conversion is the lowest ranked. review the order: 1. exact match 2. const conversion 3. promotion 4. arithmetic or pointer conversion 5. class-type conversion ## Exercise 14.52: > Which `operator+`, if any, is selected for each of the addition expressions? List the candidate functions, the viable functions, and the type conversions on the arguments for each viable function: ```cpp struct LongDouble { // member operator+ for illustration purposes; + is usually a nonmember LongDouble operator+(const SmallInt&); // 1 // other members as in 14.9.2 (p. 587) }; LongDouble operator+(LongDouble&, double); // 2 SmallInt si; LongDouble ld; ld = si + ld; ld = ld + si; ``` `ld = si + ld;` is ambiguous. `ld = ld + si` can use both 1 and 2, but 1 is more exactly. (in the 2, SmallInt need to convert to `double`) ## Exercise 14.53: > Given the definition of SmallInt on page 588, determine whether the following addition expression is legal. If so, what addition operator is used? If not, how might you change the code to make it legal? ```cpp SmallInt s1; double d = s1 + 3.14; ``` ambiguous. **Fixed**: ```cpp SmallInt s1; double d = s1 + SmallInt(3.14); ``` ================================================ FILE: ch14/ex14_02.cpp ================================================ // // ex14_02.cpp // Exercise 14.2 // // Created by pezy on 3/9/15. // // @Brief implementation of class Sales_data // @See ex14_02.h #include "ex14_02.h" Sales_data::Sales_data(std::istream &is) : Sales_data() { is >> *this; } Sales_data& Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& operator>>(std::istream &is, Sales_data &item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; if (is) item.revenue = price * item.units_sold; else item = Sales_data(); return is; } std::ostream& operator<<(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum += rhs; return sum; } ================================================ FILE: ch14/ex14_02.h ================================================ // // ex14_02.h // Exercise 14.2 // // Created by pezy on 3/9/15. // // @See ex7_41.h // @Add overloaded input, output, addition, and compound-assignment operators #ifndef CP5_CH14_EX14_02_H #define CP5_CH14_EX14_02_H #include #include class Sales_data { friend std::istream& operator>>(std::istream&, Sales_data&); // input friend std::ostream& operator<<(std::ostream&, const Sales_data&); // output friend Sales_data operator+(const Sales_data&, const Sales_data&); // addition public: Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data() : Sales_data("", 0, 0.0f){ } Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f){ } Sales_data(std::istream &is); Sales_data& operator+=(const Sales_data&); // compound-assignment std::string isbn() const { return bookNo; } private: inline double avg_price() const; std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; std::istream& operator>>(std::istream&, Sales_data&); std::ostream& operator<<(std::ostream&, const Sales_data&); Sales_data operator+(const Sales_data&, const Sales_data&); inline double Sales_data::avg_price() const { return units_sold ? revenue/units_sold : 0; } #endif // CP5_CH14_EX14_02_H ================================================ FILE: ch14/ex14_02_TEST.cpp ================================================ #include "ex14_02.h" int main() { Sales_data cp5; std::cin >> cp5; std::cout << cp5 << std::endl; } ================================================ FILE: ch14/ex14_05.cpp ================================================ #include "ex14_05.h" std::istream& operator>>(std::istream &in, Book &book) { in >> book.no_ >> book.name_ >> book.author_ >> book.pubdate_; return in; } std::ostream& operator<<(std::ostream &out, const Book &book) { out << book.no_ << " " << book.name_ << " " << book.author_ << " " << book.pubdate_; return out; } bool operator==(const Book &lhs, const Book &rhs) { return lhs.no_ == rhs.no_; } bool operator!=(const Book &lhs, const Book &rhs) { return !(lhs == rhs); } ================================================ FILE: ch14/ex14_05.h ================================================ #ifndef CP5_CH14_EX14_05_H #define CP5_CH14_EX14_05_H #include #include class Book { friend std::istream& operator>>(std::istream&, Book&); friend std::ostream& operator<<(std::ostream&, const Book&); friend bool operator==(const Book&, const Book&); friend bool operator!=(const Book&, const Book&); public: Book() = default; Book(unsigned no, std::string name, std::string author, std::string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { } Book(std::istream &in) { in >> *this; } private: unsigned no_; std::string name_; std::string author_; std::string pubdate_; }; std::istream& operator>>(std::istream&, Book&); std::ostream& operator<<(std::ostream&, const Book&); bool operator==(const Book&, const Book&); bool operator!=(const Book&, const Book&); #endif // CP5_CH14_EX14_05_H ================================================ FILE: ch14/ex14_05_TEST.cpp ================================================ #include "ex14_05.h" int main() { Book book1(123, "CP5", "Lippman", "2012"); Book book2(123, "CP5", "Lippman", "2012"); if (book1 == book2) std::cout << book1 << std::endl; } ================================================ FILE: ch14/ex14_07.cpp ================================================ #include "ex14_07.h" #include #include std::pair String::alloc_n_copy(const char *b, const char *e) { auto str = alloc.allocate(e - b); return{ str, std::uninitialized_copy(b, e, str) }; } void String::range_initializer(const char *first, const char *last) { auto newstr = alloc_n_copy(first, last); elements = newstr.first; end = newstr.second; } String::String(const char *s) { char *sl = const_cast(s); while (*sl) ++sl; range_initializer(s, ++sl); } String::String(const String& rhs) { range_initializer(rhs.elements, rhs.end); std::cout << "copy constructor" << std::endl; } void String::free() { if (elements) { std::for_each(elements, end, [this](char &c){ alloc.destroy(&c); }); alloc.deallocate(elements, end - elements); } } String::~String() { free(); } String& String::operator = (const String &rhs) { auto newstr = alloc_n_copy(rhs.elements, rhs.end); free(); elements = newstr.first; end = newstr.second; std::cout << "copy-assignment" << std::endl; return *this; } std::ostream& operator<<(std::ostream &os, const String &s) { char *c = const_cast(s.c_str()); while (*c) os << *c++; return os; } ================================================ FILE: ch14/ex14_07.h ================================================ #ifndef CP5_CH14_EX07_H_ #define CP5_CH14_EX07_H_ #include #include class String { friend std::ostream& operator<<(std::ostream&, const String&); public: String() : String("") { } String(const char *); String(const String&); String& operator=(const String&); ~String(); const char *c_str() const { return elements; } size_t size() const { return end - elements; } size_t length() const { return end - elements - 1; } private: std::pair alloc_n_copy(const char*, const char*); void range_initializer(const char*, const char*); void free(); private: char *elements; char *end; std::allocator alloc; }; std::ostream& operator<<(std::ostream&, const String&); #endif //CP5_CH14_EX07_H_ ================================================ FILE: ch14/ex14_07_TEST.cpp ================================================ #include "ex14_07.h" int main() { String str("Hello World"); std::cout << str << std::endl; } ================================================ FILE: ch14/ex14_13.cpp ================================================ // // ex14_13.cpp // Exercise 14.13 // // Created by pezy on 3/9/15. // Substraction and compound-substraction operations added by hoeselm on 4/10/16. // // @Brief implementation of class Sales_data // @See ex14_13.h #include "ex14_13.h" Sales_data::Sales_data(std::istream &is) : Sales_data() { is >> *this; } Sales_data& Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_data& Sales_data::operator-=(const Sales_data &rhs) { units_sold -= rhs.units_sold; revenue -= rhs.revenue; return *this; } std::istream& operator>>(std::istream &is, Sales_data &item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; if (is) item.revenue = price * item.units_sold; else item = Sales_data(); return is; } std::ostream& operator<<(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum += rhs; return sum; } Sales_data operator-(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum -= rhs; return sum; } ================================================ FILE: ch14/ex14_13.h ================================================ // // ex14_13.h // Exercise 14.13 // // Created by pezy on 3/9/15. // Substraction and compound-substraction operations added by hoeselm on 4/10/16. // // @See ex7_41.h // @Add overloaded input, output, addition, and compound-assignment operators #ifndef CP5_CH14_EX14_13_H #define CP5_CH14_EX14_13_H #include #include class Sales_data { friend std::istream& operator>>(std::istream&, Sales_data&); // input friend std::ostream& operator<<(std::ostream&, const Sales_data&); // output friend Sales_data operator+(const Sales_data&, const Sales_data&); // addition friend Sales_data operator-(const Sales_data&, const Sales_data&); // substraction public: Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data() : Sales_data("", 0, 0.0f){ } Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f){ } Sales_data(std::istream &is); Sales_data& operator+=(const Sales_data&); // compound-assignment Sales_data& operator-=(const Sales_data&); // compound-substraction std::string isbn() const { return bookNo; } private: inline double avg_price() const; std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; std::istream& operator>>(std::istream&, Sales_data&); std::ostream& operator<<(std::ostream&, const Sales_data&); Sales_data operator+(const Sales_data&, const Sales_data&); Sales_data operator-(const Sales_data&, const Sales_data&); inline double Sales_data::avg_price() const { return units_sold ? revenue/units_sold : 0; } #endif // CP5_CH14_EX14_13_H ================================================ FILE: ch14/ex14_13_TEST.cpp ================================================ #include "ex14_13.h" int main() { Sales_data s1("book1", 150, 10); Sales_data s2("book1", 200, 20); std::cout << s1 << std::endl; // Assignment s1 = s1 + s2; std::cout << s1 << std::endl; // Compound assignment s1 += s2; std::cout << s1 << std::endl; // Compound substraction s1 -= s2; std::cout << s1 << std::endl; // Substraction s1 = s1 - s2; std::cout << s1 << std::endl; } ================================================ FILE: ch14/ex14_15.cpp ================================================ #include "ex14_15.h" std::istream& operator>>(std::istream &in, Book &book) { in >> book.no_ >> book.name_ >> book.author_ >> book.pubdate_ >> book.number_; return in; } std::ostream& operator<<(std::ostream &out, const Book &book) { out << book.no_ << " " << book.name_ << " " << book.author_ << " " << book.pubdate_ << " " << book.number_ << std::endl; return out; } bool operator==(const Book &lhs, const Book &rhs) { return lhs.no_ == rhs.no_; } bool operator!=(const Book &lhs, const Book &rhs) { return !(lhs == rhs); } bool operator<(const Book &lhs, const Book &rhs) { return lhs.no_ < rhs.no_; } bool operator>(const Book &lhs, const Book &rhs) { return rhs < lhs; } Book& Book::operator+=(const Book &rhs) { if (rhs == *this) this->number_ += rhs.number_; return *this; } Book operator+(const Book &lhs, const Book &rhs) { Book book = lhs; book += rhs; return book; } ================================================ FILE: ch14/ex14_15.h ================================================ #ifndef CP5_CH14_EX14_15_H_ #define CP5_CH14_EX14_15_H_ #include #include class Book { friend std::istream& operator>>(std::istream&, Book&); friend std::ostream& operator<<(std::ostream&, const Book&); friend bool operator==(const Book&, const Book&); friend bool operator!=(const Book&, const Book&); friend bool operator<(const Book&, const Book&); friend bool operator>(const Book&, const Book&); friend Book operator+(const Book&, const Book&); public: Book() = default; Book(unsigned no, std::string name, std::string author, std::string pubdate, unsigned number):no_(no), name_(name), author_(author), pubdate_(pubdate), number_(number) { } Book(std::istream &in) { in >> *this; } Book& operator+=(const Book&); private: unsigned no_; std::string name_; std::string author_; std::string pubdate_; unsigned number_; }; std::istream& operator>>(std::istream&, Book&); std::ostream& operator<<(std::ostream&, const Book&); bool operator==(const Book&, const Book&); bool operator!=(const Book&, const Book&); bool operator<(const Book&, const Book&); bool operator>(const Book&, const Book&); Book operator+(const Book&, const Book&); #endif // CP5_CH14_EX14_15_H_ ================================================ FILE: ch14/ex14_15_TEST.cpp ================================================ #include "ex14_15.h" int main() { Book cp5_1(12345, "CP5", "Lippmen", "2012", 2); Book cp5_2(12345, "CP5", "Lippmen", "2012", 4); std::cout << cp5_1 + cp5_2 << std::endl; } ================================================ FILE: ch14/ex14_16_StrBlob.cpp ================================================ #include "ex14_16_StrBlob.h" //================================================================== // // operators // //================================================================== bool operator==(const StrBlob &lhs, const StrBlob &rhs) { return *lhs.data == *rhs.data; } bool operator!=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs == rhs); } bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !(lhs == rhs); } bool operator==(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return !(lhs == rhs); } //================================================================== // // copy assignment operator and move assignment operator. // //================================================================== StrBlob& StrBlob::operator=(const StrBlob &lhs) { data = make_shared>(*lhs.data); return *this; } StrBlob& StrBlob::operator=(StrBlob &&rhs) NOEXCEPT { if (this != &rhs) { data = std::move(rhs.data); rhs.data = nullptr; } return *this; } //================================================================== // // members // //================================================================== StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); } ConstStrBlobPtr StrBlob::cbegin() const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::cend() const { return ConstStrBlobPtr(*this, data->size()); } ================================================ FILE: ch14/ex14_16_StrBlob.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code StrBlob, StrBlobPtr, ConstStrBlobPtr If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_STRBLOB_H_ #define CP5_STRBLOB_H_ #include using std::vector; #include using std::string; #include using std::initializer_list; #include using std::make_shared; using std::shared_ptr; #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrBlobPtr; class ConstStrBlobPtr; //================================================================================= // // StrBlob - custom vector // //================================================================================= class StrBlob { using size_type = vector::size_type; friend class ConstStrBlobPtr; friend class StrBlobPtr; friend bool operator==(const StrBlob&, const StrBlob&); friend bool operator!=(const StrBlob&, const StrBlob&); public: StrBlob() : data(make_shared>()) { } StrBlob(initializer_list il) : data(make_shared>(il)) { } StrBlob(const StrBlob &sb) : data(make_shared>(*sb.data)) { } StrBlob& operator=(const StrBlob&); StrBlob(StrBlob &&rhs) NOEXCEPT : data(std::move(rhs.data)) { } StrBlob& operator=(StrBlob &&)NOEXCEPT; StrBlobPtr begin(); StrBlobPtr end(); ConstStrBlobPtr cbegin() const; ConstStrBlobPtr cend() const; size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void push_back(string &&s) { data->push_back(std::move(s)); } void pop_back(); string& front(); string& back(); const string& front() const; const string& back() const; private: void check(size_type, const string&) const; shared_ptr> data; }; bool operator==(const StrBlob&, const StrBlob&); bool operator!=(const StrBlob&, const StrBlob&); inline void StrBlob::pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } inline string& StrBlob::front() { check(0, "front on empty StrBlob"); return data->front(); } inline string& StrBlob::back() { check(0, "back on empty StrBlob"); return data->back(); } inline const string& StrBlob::front() const { check(0, "front on empty StrBlob"); return data->front(); } inline const string& StrBlob::back() const { check(0, "back on empty StrBlob"); return data->back(); } inline void StrBlob::check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } //================================================================================= // // StrBlobPtr - custom iterator of StrBlob // //================================================================================= class StrBlobPtr { friend bool operator==(const StrBlobPtr&, const StrBlobPtr&); friend bool operator!=(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr() : curr(0) { } StrBlobPtr(StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } string& deref() const; StrBlobPtr& incr(); private: shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const StrBlobPtr&, const StrBlobPtr&); bool operator!=(const StrBlobPtr&, const StrBlobPtr&); inline string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline StrBlobPtr& StrBlobPtr::incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline shared_ptr> StrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } //================================================================================= // // ConstStrBlobPtr - custom const_iterator of StrBlob // //================================================================================= class ConstStrBlobPtr { friend bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); public: ConstStrBlobPtr() : curr(0) { } ConstStrBlobPtr(const StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } const string& deref() const; ConstStrBlobPtr& incr(); private: std::shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; inline const string& ConstStrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline ConstStrBlobPtr& ConstStrBlobPtr::incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline std::shared_ptr> ConstStrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); #endif //CP5_STRBLOB_H_ ================================================ FILE: ch14/ex14_16_StrBlobTest.cpp ================================================ #include "ex14_16_StrBlob.h" #include int main() { StrBlob sb{ "Hello", "World", "Pezy" }; for (ConstStrBlobPtr iter = sb.cbegin(); iter != sb.cend(); iter.incr()) { std::cout << iter.deref() << " "; } std::cout << std::endl; } ================================================ FILE: ch14/ex14_16_StrVec.cpp ================================================ // // ex13_39.cpp // Exercise 13.39 // // Created by pezy on 2/3/15. // // Write your own version of StrVec, including versions of // reserve, capacity (9.4, p. 356), and resize (9.3.5, p. 352). // #include "ex13_39.h" void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for (auto p = first_free; p != elements;) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } StrVec::StrVec(const StrVec &rhs) { auto newdata = alloc_n_copy(rhs.begin(), rhs.end()); elements = newdata.first; first_free = cap = newdata.second; } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } int main() { return 0; } ================================================ FILE: ch14/ex14_16_StrVec.h ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrVec { friend bool operator==(const StrVec&, const StrVec&); friend bool operator!=(const StrVec&, const StrVec&); public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list); StrVec(const StrVec&); StrVec& operator=(const StrVec&); StrVec(StrVec&&) NOEXCEPT; StrVec& operator=(StrVec&&)NOEXCEPT; ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; bool operator==(const StrVec&, const StrVec&); bool operator!=(const StrVec&, const StrVec&); #endif ================================================ FILE: ch14/ex14_16_StrVecMain.cpp ================================================ #include "ex14_16_StrVec.h" #include #include int main() { StrVec vec; vec.reserve(6); std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl; vec.reserve(4); std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl; vec.push_back("hello"); vec.push_back("world"); vec.resize(4); for (auto i = vec.begin(); i != vec.end(); ++i) std::cout << *i << std::endl; std::cout << "-EOF-" << std::endl; vec.resize(1); for (auto i = vec.begin(); i != vec.end(); ++i) std::cout << *i << std::endl; std::cout << "-EOF-" << std::endl; StrVec vec_list{ "hello", "world", "pezy" }; for (auto i = vec_list.begin(); i != vec_list.end(); ++i) std::cout << *i << " "; std::cout << std::endl; // Test operator== const StrVec const_vec_list{ "hello", "world", "pezy" }; if (vec_list == const_vec_list) for (const auto &str : const_vec_list) std::cout << str << " "; std::cout << std::endl; } ================================================ FILE: ch14/ex14_16_String.cpp ================================================ #include "ex14_16_String.h" #include //=========================================================================== // // operator - friend // //=========================================================================== std::ostream& operator<<(std::ostream &os, const String &lhs) { os << lhs.c_str(); return os; } std::istream& operator>>(std::istream &is, String &rhs) { for (char c; (c = is.get()) != '\n';) { rhs.push_back(c); } return is; } bool operator==(const String &lhs, const String &rhs) { return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } bool operator!=(const String &lhs, const String &rhs) { return !(lhs == rhs); } //=========================================================================== // // Constructors // //=========================================================================== String::String(const char *s) { char *sl = const_cast(s); while (*sl) ++sl; range_initializer(s, ++sl); } //=========================================================================== // // Big 5 // //=========================================================================== String::String(const String& rhs) { range_initializer(rhs.elements, rhs.first_free); } String& String::operator = (const String &rhs) { auto newstr = alloc_n_copy(rhs.elements, rhs.first_free); free(); elements = newstr.first; first_free = cap = newstr.second; last_elem = first_free - 1; return *this; } String::String(String &&s) NOEXCEPT : elements(s.elements), last_elem(s.last_elem), first_free(s.first_free), cap(s.cap) { s.elements = s.last_elem = s.first_free = s.cap = nullptr; } String& String::operator = (String &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; last_elem = rhs.last_elem; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.last_elem = rhs.first_free = rhs.cap = nullptr; } return *this; } String::~String() { free(); } //=========================================================================== // // members // //=========================================================================== void String::push_back(const char c) { chk_n_alloc(); *last_elem = c; last_elem = first_free; alloc.construct(first_free++, '\0'); } void String::reallocate() { // \0 | - // ^ ^ // elements first_free // last_elem cap auto newcapacity = size() ? 2 * (size() + 1) : 2; alloc_n_move(newcapacity); } void String::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size() + 1; ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; last_elem = dest - 1; first_free = dest; cap = elements + new_cap; } void String::free() { if (elements) { std::for_each(elements, first_free, [this](char &c){ alloc.destroy(&c); }); alloc.deallocate(elements, cap - elements); } } std::pair String::alloc_n_copy(const char *b, const char *e) { auto str = alloc.allocate(e - b); return{ str, std::uninitialized_copy(b, e, str) }; } void String::range_initializer(const char *first, const char *last) { auto newstr = alloc_n_copy(first, last); elements = newstr.first; first_free = cap = newstr.second; last_elem = first_free - 1; } void String::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void String::resize(size_t count, char c) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) { *last_elem++ = c; alloc.construct(first_free++, '\0'); } } else if (count < size()) { while (last_elem != elements + count) { --last_elem; alloc.destroy(--first_free); } *last_elem = '\0'; } } void String::resize(size_t count) { resize(count, ' '); } ================================================ FILE: ch14/ex14_16_String.h ================================================ #ifndef CP5_STRING_H__ #define CP5_STRING_H__ #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class String { friend std::ostream& operator<<(std::ostream&, const String&); friend std::istream& operator>>(std::istream&, String&); friend bool operator==(const String&, const String&); friend bool operator!=(const String&, const String&); public: String() : String("") { } String(const char *); String(const String&); String& operator=(const String&); String(String &&) NOEXCEPT; String& operator=(String&&)NOEXCEPT; ~String(); void push_back(const char); char* begin() const { return elements; } char* end() const { return last_elem; } const char *c_str() const { return elements; } size_t size() const { return last_elem - elements; } size_t length() const { return size(); } size_t capacity() const { return cap - elements; } void reserve(size_t); void resize(size_t); void resize(size_t, char); private: std::pair alloc_n_copy(const char*, const char*); void range_initializer(const char*, const char*); void free(); void reallocate(); void alloc_n_move(size_t new_cap); void chk_n_alloc() { if (first_free == cap) reallocate(); } private: char *elements; char *last_elem; char *first_free; char *cap; std::allocator alloc; }; std::ostream& operator<<(std::ostream&, const String&); std::istream& operator>>(std::istream&, String&); bool operator==(const String&, const String&); bool operator!=(const String&, const String&); #endif ================================================ FILE: ch14/ex14_16_StringMain.cpp ================================================ #include "ex14_16_String.h" #include #include #include #include #include void foo(String x) { std::cout << x << std::endl; } void bar(const String& x) { std::cout << x.c_str() << std::endl; } String baz() { String ret("world"); return ret; } int main() { char text[] = "world"; String s0; String s1("hello"); String s2(std::move(s0)); String s3 = s1; String s4(text); s2 = s1; if (s2 == s1) std::cout << "s2 == s1" << std::endl; foo(s1); bar(s1); foo("temporary"); bar("temporary"); String s5 = baz(); std::vector svec; //svec.push_back(s0); svec.push_back(s1); svec.push_back(s2); svec.push_back(s3); svec.push_back(s4); svec.push_back(baz()); svec.push_back("good job"); for (const auto &s : svec) { std::cout << s << std::endl; } std::cout << "Input a string: "; String s6; std::cin >> s6; std::cout << s6 << std::endl; } ================================================ FILE: ch14/ex14_18_StrBlob.cpp ================================================ #include "ex14_18_StrBlob.h" #include //================================================================== // // StrBlob - operators // //================================================================== bool operator==(const StrBlob &lhs, const StrBlob &rhs) { return *lhs.data == *rhs.data; } bool operator!=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs == rhs); } bool operator< (const StrBlob &lhs, const StrBlob &rhs) { return std::lexicographical_compare(lhs.data->begin(), lhs.data->end(), rhs.data->begin(), rhs.data->end()); } bool operator> (const StrBlob &lhs, const StrBlob &rhs) { return rhs < lhs; } bool operator<=(const StrBlob &lhs, const StrBlob &rhs) { return !(rhs < lhs); } bool operator>=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs < rhs); } //================================================================ // // StrBlobPtr - operators // //================================================================ bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr < y.curr; } bool operator>(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr > y.curr; } bool operator<=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr <= y.curr; } bool operator>=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr >= y.curr; } //================================================================ // // ConstStrBlobPtr - operators // //================================================================ bool operator==(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr < rhs.curr; } bool operator>(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr > rhs.curr; } bool operator<=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr <= rhs.curr; } bool operator>=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr >= rhs.curr; } //================================================================== // // copy assignment operator and move assignment operator. // //================================================================== StrBlob& StrBlob::operator=(const StrBlob &lhs) { data = make_shared>(*lhs.data); return *this; } StrBlob& StrBlob::operator=(StrBlob &&rhs) NOEXCEPT { if (this != &rhs) { data = std::move(rhs.data); rhs.data = nullptr; } return *this; } //================================================================== // // members // //================================================================== StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); } ConstStrBlobPtr StrBlob::cbegin() const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::cend() const { return ConstStrBlobPtr(*this, data->size()); } ================================================ FILE: ch14/ex14_18_StrBlob.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code StrBlob, StrBlobPtr, ConstStrBlobPtr If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_STRBLOB_H_ #define CP5_STRBLOB_H_ #include using std::vector; #include using std::string; #include using std::initializer_list; #include using std::make_shared; using std::shared_ptr; #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrBlobPtr; class ConstStrBlobPtr; //================================================================================= // // StrBlob - custom vector // //================================================================================= class StrBlob { using size_type = vector::size_type; friend class ConstStrBlobPtr; friend class StrBlobPtr; friend bool operator==(const StrBlob&, const StrBlob&); friend bool operator!=(const StrBlob&, const StrBlob&); friend bool operator< (const StrBlob&, const StrBlob&); friend bool operator> (const StrBlob&, const StrBlob&); friend bool operator<=(const StrBlob&, const StrBlob&); friend bool operator>=(const StrBlob&, const StrBlob&); public: StrBlob() : data(make_shared>()) { } StrBlob(initializer_list il) : data(make_shared>(il)) { } StrBlob(const StrBlob &sb) : data(make_shared>(*sb.data)) { } StrBlob& operator=(const StrBlob&); StrBlob(StrBlob &&rhs) NOEXCEPT : data(std::move(rhs.data)) { } StrBlob& operator=(StrBlob &&)NOEXCEPT; StrBlobPtr begin(); StrBlobPtr end(); ConstStrBlobPtr cbegin() const; ConstStrBlobPtr cend() const; size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void push_back(string &&s) { data->push_back(std::move(s)); } void pop_back(); string& front(); string& back(); const string& front() const; const string& back() const; private: void check(size_type, const string&) const; shared_ptr> data; }; bool operator==(const StrBlob&, const StrBlob&); bool operator!=(const StrBlob&, const StrBlob&); bool operator< (const StrBlob&, const StrBlob&); bool operator> (const StrBlob&, const StrBlob&); bool operator<=(const StrBlob&, const StrBlob&); bool operator>=(const StrBlob&, const StrBlob&); inline void StrBlob::pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } inline string& StrBlob::front() { check(0, "front on empty StrBlob"); return data->front(); } inline string& StrBlob::back() { check(0, "back on empty StrBlob"); return data->back(); } inline const string& StrBlob::front() const { check(0, "front on empty StrBlob"); return data->front(); } inline const string& StrBlob::back() const { check(0, "back on empty StrBlob"); return data->back(); } inline void StrBlob::check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } //================================================================================= // // StrBlobPtr - custom iterator of StrBlob // //================================================================================= class StrBlobPtr { friend bool operator==(const StrBlobPtr&, const StrBlobPtr&); friend bool operator!=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator< (const StrBlobPtr&, const StrBlobPtr&); friend bool operator> (const StrBlobPtr&, const StrBlobPtr&); friend bool operator<=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator>=(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr() : curr(0) { } StrBlobPtr(StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } string& deref() const; StrBlobPtr& incr(); private: shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const StrBlobPtr&, const StrBlobPtr&); bool operator!=(const StrBlobPtr&, const StrBlobPtr&); bool operator< (const StrBlobPtr&, const StrBlobPtr&); bool operator> (const StrBlobPtr&, const StrBlobPtr&); bool operator<=(const StrBlobPtr&, const StrBlobPtr&); bool operator>=(const StrBlobPtr&, const StrBlobPtr&); inline string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline StrBlobPtr& StrBlobPtr::incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline shared_ptr> StrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } //================================================================================= // // ConstStrBlobPtr - custom const_iterator of StrBlob // //================================================================================= class ConstStrBlobPtr { friend bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); public: ConstStrBlobPtr() : curr(0) { } ConstStrBlobPtr(const StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } const string& deref() const; ConstStrBlobPtr& incr(); private: std::shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); inline const string& ConstStrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline ConstStrBlobPtr& ConstStrBlobPtr::incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline std::shared_ptr> ConstStrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } #endif //CP5_STRBLOB_H_ ================================================ FILE: ch14/ex14_18_StrBlobTest.cpp ================================================ #include "ex14_18_StrBlob.h" #include int main() { StrBlob sb1{ "a", "b", "c" }; StrBlob sb2{ "a", "b", "b" }; if (sb1 > sb2) { for (ConstStrBlobPtr iter = sb1.cbegin(); iter < sb1.cend(); iter.incr()) std::cout << iter.deref() << " "; std::cout << std::endl; } } ================================================ FILE: ch14/ex14_18_StrVec.cpp ================================================ #include "ex14_18_StrVec.h" #include // for_each, equal void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for_each(elements, first_free, [this](std::string &rhs){ alloc.destroy(&rhs); }); alloc.deallocate(elements, cap - elements); } } void StrVec::range_initialize(const std::string *first, const std::string *last) { auto newdata = alloc_n_copy(first, last); elements = newdata.first; first_free = cap = newdata.second; } StrVec::StrVec(const StrVec &rhs) { range_initialize(rhs.begin(), rhs.end()); } StrVec::StrVec(std::initializer_list il) { range_initialize(il.begin(), il.end()); } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } StrVec::StrVec(StrVec &&s) NOEXCEPT : elements(s.elements), first_free(s.first_free), cap(s.cap) { // leave s in a state in which it is safe to run the destructor. s.elements = s.first_free = s.cap = nullptr; } StrVec& StrVec::operator = (StrVec &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; } bool operator==(const StrVec &lhs, const StrVec &rhs) { return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } bool operator!=(const StrVec &lhs, const StrVec &rhs) { return !(lhs == rhs); } bool operator<(const StrVec &lhs, const StrVec &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } bool operator>(const StrVec &lhs, const StrVec &rhs) { return rhs < lhs; } bool operator<=(const StrVec &lhs, const StrVec &rhs) { return !(rhs < lhs); } bool operator>=(const StrVec &lhs, const StrVec &rhs) { return !(lhs < rhs); } ================================================ FILE: ch14/ex14_18_StrVec.h ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrVec { friend bool operator==(const StrVec&, const StrVec&); friend bool operator!=(const StrVec&, const StrVec&); friend bool operator< (const StrVec&, const StrVec&); friend bool operator> (const StrVec&, const StrVec&); friend bool operator<=(const StrVec&, const StrVec&); friend bool operator>=(const StrVec&, const StrVec&); public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list); StrVec(const StrVec&); StrVec& operator=(const StrVec&); StrVec(StrVec&&) NOEXCEPT; StrVec& operator=(StrVec&&)NOEXCEPT; ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; bool operator==(const StrVec&, const StrVec&); bool operator!=(const StrVec&, const StrVec&); bool operator< (const StrVec&, const StrVec&); bool operator> (const StrVec&, const StrVec&); bool operator<=(const StrVec&, const StrVec&); bool operator>=(const StrVec&, const StrVec&); #endif ================================================ FILE: ch14/ex14_18_StrVecMain.cpp ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrVec { friend bool operator==(const StrVec&, const StrVec&); friend bool operator!=(const StrVec&, const StrVec&); friend bool operator< (const StrVec&, const StrVec&); friend bool operator> (const StrVec&, const StrVec&); friend bool operator<=(const StrVec&, const StrVec&); friend bool operator>=(const StrVec&, const StrVec&); public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list); StrVec(const StrVec&); StrVec& operator=(const StrVec&); StrVec(StrVec&&) NOEXCEPT; StrVec& operator=(StrVec&&)NOEXCEPT; ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; bool operator==(const StrVec&, const StrVec&); bool operator!=(const StrVec&, const StrVec&); bool operator< (const StrVec&, const StrVec&); bool operator> (const StrVec&, const StrVec&); bool operator<=(const StrVec&, const StrVec&); bool operator>=(const StrVec&, const StrVec&); #endif ================================================ FILE: ch14/ex14_18_String.cpp ================================================ #include "ex14_18_String.h" #include //=========================================================================== // // operator - friend // //=========================================================================== std::ostream& operator<<(std::ostream &os, const String &lhs) { os << lhs.c_str(); return os; } std::istream& operator>>(std::istream &is, String &rhs) { for (char c; (c = is.get()) != '\n';) { rhs.push_back(c); } return is; } bool operator==(const String &lhs, const String &rhs) { return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } bool operator!=(const String &lhs, const String &rhs) { return !(lhs == rhs); } bool operator<(const String &lhs, const String &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } bool operator>(const String &lhs, const String &rhs) { return rhs < lhs; } bool operator<=(const String &lhs, const String &rhs) { return !(rhs < lhs); } bool operator>=(const String &lhs, const String &rhs) { return !(lhs < rhs); } //=========================================================================== // // Constructors // //=========================================================================== String::String(const char *s) { char *sl = const_cast(s); while (*sl) ++sl; range_initializer(s, ++sl); } //=========================================================================== // // Big 5 // //=========================================================================== String::String(const String& rhs) { range_initializer(rhs.elements, rhs.first_free); } String& String::operator = (const String &rhs) { auto newstr = alloc_n_copy(rhs.elements, rhs.first_free); free(); elements = newstr.first; first_free = cap = newstr.second; last_elem = first_free - 1; return *this; } String::String(String &&s) NOEXCEPT : elements(s.elements), last_elem(s.last_elem), first_free(s.first_free), cap(s.cap) { s.elements = s.last_elem = s.first_free = s.cap = nullptr; } String& String::operator = (String &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; last_elem = rhs.last_elem; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.last_elem = rhs.first_free = rhs.cap = nullptr; } return *this; } String::~String() { free(); } //=========================================================================== // // members // //=========================================================================== void String::push_back(const char c) { chk_n_alloc(); *last_elem = c; last_elem = first_free; alloc.construct(first_free++, '\0'); } void String::reallocate() { // \0 | - // ^ ^ // elements first_free // last_elem cap auto newcapacity = size() ? 2 * (size() + 1) : 2; alloc_n_move(newcapacity); } void String::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size() + 1; ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; last_elem = dest - 1; first_free = dest; cap = elements + new_cap; } void String::free() { if (elements) { std::for_each(elements, first_free, [this](char &c){ alloc.destroy(&c); }); alloc.deallocate(elements, cap - elements); } } std::pair String::alloc_n_copy(const char *b, const char *e) { auto str = alloc.allocate(e - b); return{ str, std::uninitialized_copy(b, e, str) }; } void String::range_initializer(const char *first, const char *last) { auto newstr = alloc_n_copy(first, last); elements = newstr.first; first_free = cap = newstr.second; last_elem = first_free - 1; } void String::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void String::resize(size_t count, char c) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) { *last_elem++ = c; alloc.construct(first_free++, '\0'); } } else if (count < size()) { while (last_elem != elements + count) { --last_elem; alloc.destroy(--first_free); } *last_elem = '\0'; } } void String::resize(size_t count) { resize(count, ' '); } ================================================ FILE: ch14/ex14_18_String.h ================================================ #ifndef CP5_STRING_H__ #define CP5_STRING_H__ #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif //=================================================================================== // // |s|t|r|i|n|g|\0|-------------------| // ^ ^ ^ first_free ^ // elements last_elem cap // //=================================================================================== class String { friend std::ostream& operator<<(std::ostream&, const String&); friend std::istream& operator>>(std::istream&, String&); friend bool operator==(const String&, const String&); friend bool operator!=(const String&, const String&); friend bool operator< (const String&, const String&); friend bool operator> (const String&, const String&); friend bool operator<=(const String&, const String&); friend bool operator>=(const String&, const String&); public: String() : String("") { } String(const char *); String(const String&); String& operator=(const String&); String(String &&) NOEXCEPT; String& operator=(String&&)NOEXCEPT; ~String(); void push_back(const char); char* begin() const { return elements; } char* end() const { return last_elem; } const char *c_str() const { return elements; } size_t size() const { return last_elem - elements; } size_t length() const { return size(); } size_t capacity() const { return cap - elements; } void reserve(size_t); void resize(size_t); void resize(size_t, char); private: std::pair alloc_n_copy(const char*, const char*); void range_initializer(const char*, const char*); void free(); void reallocate(); void alloc_n_move(size_t new_cap); void chk_n_alloc() { if (first_free == cap) reallocate(); } private: char *elements; char *last_elem; char *first_free; char *cap; std::allocator alloc; }; std::ostream& operator<<(std::ostream&, const String&); std::istream& operator>>(std::istream&, String&); bool operator==(const String&, const String&); bool operator!=(const String&, const String&); bool operator< (const String&, const String&); bool operator> (const String&, const String&); bool operator<=(const String&, const String&); bool operator>=(const String&, const String&); #endif ================================================ FILE: ch14/ex14_18_StringMain.cpp ================================================ #include "ex14_18_String.h" #include #include #include #include #include void foo(String x) { std::cout << x << std::endl; } void bar(const String& x) { std::cout << x.c_str() << std::endl; } String baz() { String ret("world"); return ret; } int main() { char text[] = "world"; String s0; String s1("hello"); String s2(std::move(s0)); String s3 = s1; String s4(text); s2 = s1; if (s2 == s1) std::cout << "s2 == s1" << std::endl; foo(s1); bar(s1); foo("temporary"); bar("temporary"); String s5 = baz(); std::vector svec; //svec.push_back(s0); svec.push_back(s1); svec.push_back(s2); svec.push_back(s3); svec.push_back(s4); svec.push_back(baz()); svec.push_back("good job"); for (const auto &s : svec) { std::cout << s << std::endl; } std::cout << "Input a string: "; String s6; std::cin >> s6; std::cout << s6 << std::endl; if (s6 > s1) std::cout << "s6 > s1" << std::endl; } ================================================ FILE: ch14/ex14_22.cpp ================================================ #include "ex14_22.h" Sales_data::Sales_data(std::istream &is) : Sales_data() { is >> *this; } Sales_data& Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& operator>>(std::istream &is, Sales_data &item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; if (is) item.revenue = price * item.units_sold; else item = Sales_data(); return is; } std::ostream& operator<<(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum += rhs; return sum; } Sales_data& Sales_data::operator=(const std::string &isbn) { *this = Sales_data(isbn); return *this; } ================================================ FILE: ch14/ex14_22.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code Sales_data If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_ex14_22_h #define CP5_ex14_22_h #include #include class Sales_data { friend std::istream& operator>>(std::istream&, Sales_data&); friend std::ostream& operator<<(std::ostream&, const Sales_data&); friend Sales_data operator+(const Sales_data&, const Sales_data&); public: Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data() : Sales_data("", 0, 0.0f){ } Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f){ } Sales_data(std::istream &is); Sales_data& operator=(const std::string&); Sales_data& operator+=(const Sales_data&); std::string isbn() const { return bookNo; } private: inline double avg_price() const; std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; std::istream& operator>>(std::istream&, Sales_data&); std::ostream& operator<<(std::ostream&, const Sales_data&); Sales_data operator+(const Sales_data&, const Sales_data&); inline double Sales_data::avg_price() const { return units_sold ? revenue/units_sold : 0; } #endif ================================================ FILE: ch14/ex14_22_TEST.cpp ================================================ #include "ex14_22.h" int main() { std::string strCp5("C++ Primer 5th"); Sales_data cp5; cp5 = strCp5; std::cout << cp5 << std::endl; } ================================================ FILE: ch14/ex14_23.cpp ================================================ #include "ex14_23.h" #include // for_each, equal void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e - b); return{ data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for_each(elements, first_free, [this](std::string &rhs){ alloc.destroy(&rhs); }); alloc.deallocate(elements, cap - elements); } } void StrVec::range_initialize(const std::string *first, const std::string *last) { auto newdata = alloc_n_copy(first, last); elements = newdata.first; first_free = cap = newdata.second; } StrVec::StrVec(const StrVec &rhs) { range_initialize(rhs.begin(), rhs.end()); } StrVec::StrVec(std::initializer_list il) { range_initialize(il.begin(), il.end()); } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } StrVec::StrVec(StrVec &&s) NOEXCEPT : elements(s.elements), first_free(s.first_free), cap(s.cap) { // leave s in a state in which it is safe to run the destructor. s.elements = s.first_free = s.cap = nullptr; } StrVec& StrVec::operator = (StrVec &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; } bool operator==(const StrVec &lhs, const StrVec &rhs) { return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } bool operator!=(const StrVec &lhs, const StrVec &rhs) { return !(lhs == rhs); } bool operator<(const StrVec &lhs, const StrVec &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } bool operator>(const StrVec &lhs, const StrVec &rhs) { return rhs < lhs; } bool operator<=(const StrVec &lhs, const StrVec &rhs) { return !(rhs < lhs); } bool operator>=(const StrVec &lhs, const StrVec &rhs) { return !(lhs < rhs); } StrVec& StrVec::operator=(std::initializer_list il) { auto data = alloc_n_copy(il.begin(), il.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } ================================================ FILE: ch14/ex14_23.h ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrVec { friend bool operator==(const StrVec&, const StrVec&); friend bool operator!=(const StrVec&, const StrVec&); friend bool operator< (const StrVec&, const StrVec&); friend bool operator> (const StrVec&, const StrVec&); friend bool operator<=(const StrVec&, const StrVec&); friend bool operator>=(const StrVec&, const StrVec&); public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list); StrVec(const StrVec&); StrVec& operator=(const StrVec&); StrVec(StrVec&&) NOEXCEPT; StrVec& operator=(StrVec&&)NOEXCEPT; ~StrVec(); StrVec& operator=(std::initializer_list); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; bool operator==(const StrVec&, const StrVec&); bool operator!=(const StrVec&, const StrVec&); bool operator< (const StrVec&, const StrVec&); bool operator> (const StrVec&, const StrVec&); bool operator<=(const StrVec&, const StrVec&); bool operator>=(const StrVec&, const StrVec&); #endif ================================================ FILE: ch14/ex14_23_TEST.cpp ================================================ #include "ex14_23.h" #include #include int main() { StrVec vec; vec.reserve(6); std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl; vec.reserve(4); std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl; vec.push_back("hello"); vec.push_back("world"); vec.resize(4); for (auto i = vec.begin(); i != vec.end(); ++i) std::cout << *i << std::endl; std::cout << "-EOF-" << std::endl; vec.resize(1); for (auto i = vec.begin(); i != vec.end(); ++i) std::cout << *i << std::endl; std::cout << "-EOF-" << std::endl; StrVec vec_list{ "hello", "world", "pezy" }; for (auto i = vec_list.begin(); i != vec_list.end(); ++i) std::cout << *i << " "; std::cout << std::endl; // Test operator== const StrVec const_vec_list = { "hello", "world", "pezy" }; if (vec_list == const_vec_list) for (const auto &str : const_vec_list) std::cout << str << " "; std::cout << std::endl; // Test operator< const StrVec const_vec_list_small = { "hello", "pezy", "ok" }; std::cout << (const_vec_list_small < const_vec_list) << std::endl; } ================================================ FILE: ch14/ex14_24.cpp ================================================ #include "ex14_24.h" #include // constructor taking Size as days // the argument must be within (0, 2^32) Date::Date(Size days) { // calculate the year Size y400 = days/YtoD_400; Size y100 = (days - y400*YtoD_400)/YtoD_100; Size y4 = (days - y400*YtoD_400 - y100*YtoD_100)/YtoD_4; Size y = (days - y400*YtoD_400 - y100*YtoD_100 - y4*YtoD_4)/365; Size d = days - y400*YtoD_400 - y100*YtoD_100 - y4*YtoD_4 - y*365; this->year = y400*400 + y100*100 + y4*4 + y; // check if leap and choose the months vector accordingly std::vectorcurrYear = isLeapYear(this->year) ? monthsVec_l : monthsVec_n; // calculate day and month using find_if + lambda Size D_accumu = 0, M_accumu = 0; // @bug fixed: the variabbles above hade been declared inside the find_if as static // which caused the bug. It works fine now after being move outside. std::find_if(currYear.cbegin(), currYear.cend(), [&](Size m){ D_accumu += m; M_accumu ++; if(d < D_accumu) { this->month = M_accumu; this->day = d + m - D_accumu ; return true; } else return false; }); } // construcotr taking iostream Date::Date(std::istream &is, std::ostream &os) { is >> day >> month >> year; if(is) { if(check(*this)) return; else { os << "Invalid input! Object is default initialized."; *this = Date(); } } else { os << "Invalid input! Object is default initialized."; *this = Date(); } } // copy constructor Date::Date(const Date &d) : day(d.day), month(d.month), year(d.year) { } // move constructor Date::Date(Date&& d) NOEXCEPT : day(d.day), month(d.month), year(d.year) { std::cout << "copy moving"; } // copy operator= Date &Date::operator= (const Date &d) { this->day = d.day; this->month = d.month; this->year = d.year; return *this; } // move operator= Date &Date::operator =(Date&& rhs) NOEXCEPT { if(this != &rhs) { this->day = rhs.day; this->month = rhs.month; this->year = rhs.year; } std::cout << "moving ="; return *this; } // conver to days Date::Size Date::toDays() const { Size result = this->day; // check if leap and choose the months vector accordingly std::vectorcurrYear = isLeapYear(this->year) ? monthsVec_l : monthsVec_n; // calculate result + days by months for(auto it = currYear.cbegin(); it != currYear.cbegin() + this->month -1; ++it) result += *it; // calculate result + days by years result += (this->year/400) * YtoD_400; result += (this->year%400/100) * YtoD_100; result += (this->year%100/4) * YtoD_4; result += (this->year%4) * YtoD_1; return result; } // member operators: += -= Date &Date::operator +=(Date::Size offset) { *this = Date(this->toDays() + offset); return *this; } Date &Date::operator -=(Date::Size offset) { if(this->toDays() > offset) *this = Date(this->toDays() - offset); else *this = Date(); return *this; } // non-member operators: << >> - == != < <= > >= std::ostream& operator <<(std::ostream& os, const Date& d) { os << d.day << " " << d.month << " " << d.year; return os; } std::istream& operator >>(std::istream& is, Date& d) { if(is) { Date input = Date(is, std::cout); if(check(input)) d = input; } return is; } int operator -(const Date &lhs, const Date &rhs) { return lhs.toDays() - rhs.toDays(); } bool operator ==(const Date &lhs, const Date &rhs) { return (lhs.day == rhs.day ) && (lhs.month == rhs.month) && (lhs.year == rhs.year ) ; } bool operator !=(const Date &lhs, const Date &rhs) { return !(lhs == rhs); } bool operator < (const Date &lhs, const Date &rhs) { return lhs.toDays() < rhs.toDays(); } bool operator <=(const Date &lhs, const Date &rhs) { return (lhs < rhs) || (lhs == rhs); } bool operator >(const Date &lhs, const Date &rhs) { return !(lhs <= rhs); } bool operator >=(const Date &lhs, const Date &rhs) { return !(lhs < rhs); } Date operator - (const Date &lhs, Date::Size rhs) { // ^^^ rhs must not be larger than 2^32-1 // copy lhs Date result(lhs); result -= rhs; return result; } Date operator + (const Date &lhs, Date::Size rhs) { // ^^^ rhs must not be larger than 2^32-1 // copy lhs Date result(lhs); result += rhs; return result; } ================================================ FILE: ch14/ex14_24.h ================================================ /*************************************************************************** * @file date.h * @author Alan.W * @date 15-17 JAN 2014 * @remark ***************************************************************************/ // // Exercise 7.40: // Choose one of the following abstractions (or an abstraction of your own choosing). // Determine what data are needed in the class. Provide an appropriate set of constructors. // Explain your decisions. // // Exercise 14.5: // In exercise 7.40 from § 7.5.1 (p. 291) you wrote a sketch of one of the // following classes. Decide what, if any, overloaded operators your class // should provide. // - = < > <= >= ++ -- << >> == != += -= // Exercise 14.8: // Define an output operator for the class you chose in exercise 7.40 from // § 7.5.1 (p. 291). // // Exercise 14.12: // Define an input operator for the class you used in exercise 7.40 from // § 7.5.1 (p. 291). Be sure the operator handles input errors. // // Exercise 14.15: // Should the class you chose for exercise 7.40 from § 7.5.1 (p. 291) // define any of the arithmetic operators? If so, implement them. // If not, explain why not. // // arithmetic operators : all non-members // + : Date + Size // - : Date - Size // - : Date - Date // // Exercise 14.17: // Should the class you chose for exercise 7.40 from § 7.5.1 (p. 291) define // the equality operators? If so, implement them. If not, explain why not. // // Exercise 14.19: // Should the class you chose for exercise 7.40 from § 7.5.1 (p. 291) define // the relational operators? If so, implement them. If not, explain why not. // // Exercise 14.25: // Implement any other assignment operators your class should define. // Explain which types should be used as operands and why. // #ifndef DATE_H #define DATE_H #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif #include #include class Date { friend bool operator ==(const Date& lhs, const Date& rhs); friend bool operator < (const Date &lhs, const Date &rhs); friend bool check(const Date &d); friend std::ostream& operator <<(std::ostream& os, const Date& d); public: typedef std::size_t Size; // default constructor Date() = default; // constructor taking Size as days explicit Date(Size days); // constructor taking three Size Date(Size d, Size m, Size y) : day(d), month(m), year(y) { } // constructor taking iostream Date(std::istream &is, std::ostream &os); // copy constructor Date(const Date& d); // move constructor Date(Date&& d) NOEXCEPT; // copy operator= Date& operator= (const Date& d); // move operator= Date& operator= (Date&& rhs) NOEXCEPT; // destructor -- in this case, user-defined destructor is not nessary. ~Date(){ std::cout << "destroying\n"; } // members Size toDays() const; //not implemented yet. Date& operator +=(Size offset); Date& operator -=(Size offset); private: Size day = 1; Size month = 1; Size year = 0; }; static const Date::Size YtoD_400 = 146097; //365*400 + 400/4 -3 == 146097 static const Date::Size YtoD_100 = 36524; //365*100 + 100/4 -1 == 36524 static const Date::Size YtoD_4 = 1461; //365*4 + 1 == 1461 static const Date::Size YtoD_1 = 365; //365 // normal year static const std::vector monthsVec_n = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // leap year static const std::vector monthsVec_l = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // non-member operators: << >> - == != < <= > >= // std::ostream& operator <<(std::ostream& os, const Date& d); std::istream& operator >>(std::istream& is, Date& d); int operator - (const Date& lhs, const Date& rhs); bool operator ==(const Date& lhs, const Date& rhs); bool operator !=(const Date& lhs, const Date& rhs); bool operator < (const Date& lhs, const Date& rhs); bool operator <=(const Date& lhs, const Date& rhs); bool operator >(const Date& lhs, const Date& rhs); bool operator >=(const Date& lhs, const Date& rhs); Date operator - (const Date& lhs, Date::Size rhs); Date operator +(const Date& lhs, Date::Size rhs); // utillities: bool check(const Date &d); inline bool isLeapYear(Date::Size y); // check if the date object passed in is valid inline bool check(const Date &d) { if(d.month==0 || d.month >12) return false; else { // month == 1 3 5 7 8 10 12 if(d.month==1 || d.month==3 || d.month==5 || d.month==7 || d.month==8 || d.month==10|| d.month==12) { if(d.day==0 || d.day > 31) return false; else return true; } else { // month == 4 6 9 11 if(d.month==4 || d.month==6 || d.month==9 || d.month==11) { if(d.day==0 || d.day > 30) return false; else return true; } else { // month == 2 if(isLeapYear(d.year)) { if(d.day==0 || d.day >29) return false; else return true; } else { if(d.day==0 || d.day >28) return false; else return true; } } } } } inline bool isLeapYear(Date::Size y) { if (!(y%400)) { return true; } else { if(!(y%100)) { return false; } else return !(y%4); } } #endif // DATE_H ================================================ FILE: ch14/ex14_24_TEST.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 14 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 14.20: // Define the addition and compound-assignment operators for your Sales_data class. // // Exercise 14.22: // Define a version of the assignment operator that can assign a string representing // an ISBN to a Sales_data. // // Exercise 14.23: // Define an initializer_list assignment operator for your version of the StrVec // class. // // Exercise 14.24: // Decide whether the class you used in exercise 7.40 from § 7.5.1 (p. 291) needs a // copy- and move-assignment operator. If so, define those operators. // #include "ex14_24.h" #include int main() { Date lhs(9999999), rhs(1); std::cout << (lhs -= 12000) <<"\n"; return 0; } ================================================ FILE: ch14/ex14_26_StrBlob.cpp ================================================ #include "ex14_26_StrBlob.h" #include //================================================================== // // StrBlob - operators // //================================================================== bool operator==(const StrBlob &lhs, const StrBlob &rhs) { return *lhs.data == *rhs.data; } bool operator!=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs == rhs); } bool operator< (const StrBlob &lhs, const StrBlob &rhs) { return std::lexicographical_compare(lhs.data->begin(), lhs.data->end(), rhs.data->begin(), rhs.data->end()); } bool operator> (const StrBlob &lhs, const StrBlob &rhs) { return rhs < lhs; } bool operator<=(const StrBlob &lhs, const StrBlob &rhs) { return !(rhs < lhs); } bool operator>=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs < rhs); } //================================================================ // // StrBlobPtr - operators // //================================================================ bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr < y.curr; } bool operator>(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr > y.curr; } bool operator<=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr <= y.curr; } bool operator>=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr >= y.curr; } //================================================================ // // ConstStrBlobPtr - operators // //================================================================ bool operator==(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr < rhs.curr; } bool operator>(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr > rhs.curr; } bool operator<=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr <= rhs.curr; } bool operator>=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr >= rhs.curr; } //================================================================== // // copy assignment operator and move assignment operator. // //================================================================== StrBlob& StrBlob::operator=(const StrBlob &lhs) { data = make_shared>(*lhs.data); return *this; } StrBlob& StrBlob::operator=(StrBlob &&rhs) NOEXCEPT { if (this != &rhs) { data = std::move(rhs.data); rhs.data = nullptr; } return *this; } //================================================================== // // members // //================================================================== StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); } ConstStrBlobPtr StrBlob::cbegin() const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::cend() const { return ConstStrBlobPtr(*this, data->size()); } ================================================ FILE: ch14/ex14_26_StrBlob.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code StrBlob, StrBlobPtr, ConstStrBlobPtr If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_STRBLOB_H_ #define CP5_STRBLOB_H_ #include using std::vector; #include using std::string; #include using std::initializer_list; #include using std::make_shared; using std::shared_ptr; #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrBlobPtr; class ConstStrBlobPtr; //================================================================================= // // StrBlob - custom vector // //================================================================================= class StrBlob { using size_type = vector::size_type; friend class ConstStrBlobPtr; friend class StrBlobPtr; friend bool operator==(const StrBlob&, const StrBlob&); friend bool operator!=(const StrBlob&, const StrBlob&); friend bool operator< (const StrBlob&, const StrBlob&); friend bool operator> (const StrBlob&, const StrBlob&); friend bool operator<=(const StrBlob&, const StrBlob&); friend bool operator>=(const StrBlob&, const StrBlob&); public: StrBlob() : data(make_shared>()) { } StrBlob(initializer_list il) : data(make_shared>(il)) { } StrBlob(const StrBlob &sb) : data(make_shared>(*sb.data)) { } StrBlob& operator=(const StrBlob&); StrBlob(StrBlob &&rhs) NOEXCEPT : data(std::move(rhs.data)) { } StrBlob& operator=(StrBlob &&)NOEXCEPT; StrBlobPtr begin(); StrBlobPtr end(); ConstStrBlobPtr cbegin() const; ConstStrBlobPtr cend() const; string& operator[](size_t n); const string& operator[](size_t n) const; size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void push_back(string &&s) { data->push_back(std::move(s)); } void pop_back(); string& front(); string& back(); const string& front() const; const string& back() const; private: void check(size_type, const string&) const; shared_ptr> data; }; bool operator==(const StrBlob&, const StrBlob&); bool operator!=(const StrBlob&, const StrBlob&); bool operator< (const StrBlob&, const StrBlob&); bool operator> (const StrBlob&, const StrBlob&); bool operator<=(const StrBlob&, const StrBlob&); bool operator>=(const StrBlob&, const StrBlob&); inline void StrBlob::pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } inline string& StrBlob::front() { check(0, "front on empty StrBlob"); return data->front(); } inline string& StrBlob::back() { check(0, "back on empty StrBlob"); return data->back(); } inline const string& StrBlob::front() const { check(0, "front on empty StrBlob"); return data->front(); } inline const string& StrBlob::back() const { check(0, "back on empty StrBlob"); return data->back(); } inline void StrBlob::check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } inline string& StrBlob::operator[](size_t n) { check(n, "out of range"); return data->at(n); } inline const string& StrBlob::operator[](size_t n) const { check(n, "out of range"); return data->at(n); } //================================================================================= // // StrBlobPtr - custom iterator of StrBlob // //================================================================================= class StrBlobPtr { friend bool operator==(const StrBlobPtr&, const StrBlobPtr&); friend bool operator!=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator< (const StrBlobPtr&, const StrBlobPtr&); friend bool operator> (const StrBlobPtr&, const StrBlobPtr&); friend bool operator<=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator>=(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr() : curr(0) { } StrBlobPtr(StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } string& deref() const; StrBlobPtr& incr(); string& operator[](size_t n); const string& operator[](size_t n) const; private: shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const StrBlobPtr&, const StrBlobPtr&); bool operator!=(const StrBlobPtr&, const StrBlobPtr&); bool operator< (const StrBlobPtr&, const StrBlobPtr&); bool operator> (const StrBlobPtr&, const StrBlobPtr&); bool operator<=(const StrBlobPtr&, const StrBlobPtr&); bool operator>=(const StrBlobPtr&, const StrBlobPtr&); inline string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline StrBlobPtr& StrBlobPtr::incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline shared_ptr> StrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } inline string& StrBlobPtr::operator[](size_t n) { auto p = check(n, "dereference out of range."); return (*p)[n]; } inline const string& StrBlobPtr::operator[](size_t n) const { auto p = check(n, "dereference out of range."); return (*p)[n]; } //================================================================================= // // ConstStrBlobPtr - custom const_iterator of StrBlob // //================================================================================= class ConstStrBlobPtr { friend bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); public: ConstStrBlobPtr() : curr(0) { } ConstStrBlobPtr(const StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } const string& deref() const; ConstStrBlobPtr& incr(); const string& operator[](size_t n) const; private: std::shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); inline const string& ConstStrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline ConstStrBlobPtr& ConstStrBlobPtr::incr() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline std::shared_ptr> ConstStrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } inline const string& ConstStrBlobPtr::operator[](size_t n) const { auto p = check(n, "dereference out of range."); return (*p)[n]; } #endif //CP5_STRBLOB_H_ ================================================ FILE: ch14/ex14_26_StrBlobTest.cpp ================================================ #include "ex14_26_StrBlob.h" #include int main() { StrBlob sb1{ "a", "b", "c" }; StrBlob sb2 = sb1; sb2[2] = "b"; if (sb1 > sb2) { for (ConstStrBlobPtr iter = sb1.cbegin(); iter < sb1.cend(); iter.incr()) std::cout << iter.deref() << " "; std::cout << std::endl; } StrBlobPtr iter(sb2); std::cout << iter[2] << std::endl; } ================================================ FILE: ch14/ex14_26_StrVec.cpp ================================================ #include "ex14_26_StrVec.h" #include // for_each, equal void StrVec::push_back(const std::string &s) { chk_n_alloc(); alloc.construct(first_free++, s); } std::pair StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e-b); return { data, std::uninitialized_copy(b, e, data) }; } void StrVec::free() { if (elements) { for_each(elements, first_free, [this](std::string &rhs){ alloc.destroy(&rhs); }); alloc.deallocate(elements, cap - elements); } } void StrVec::range_initialize(const std::string *first, const std::string *last) { auto newdata = alloc_n_copy(first, last); elements = newdata.first; first_free = cap = newdata.second; } StrVec::StrVec(const StrVec &rhs) { range_initialize(rhs.begin(), rhs.end()); } StrVec::StrVec(std::initializer_list il) { range_initialize(il.begin(), il.end()); } StrVec::~StrVec() { free(); } StrVec& StrVec::operator = (const StrVec &rhs) { auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } void StrVec::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; first_free = dest; cap = elements + new_cap; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; alloc_n_move(newcapacity); } void StrVec::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void StrVec::resize(size_t count) { resize(count, std::string()); } void StrVec::resize(size_t count, const std::string &s) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) alloc.construct(first_free++, s); } else if (count < size()) { while (first_free != elements + count) alloc.destroy(--first_free); } } StrVec::StrVec(StrVec &&s) NOEXCEPT : elements(s.elements), first_free(s.first_free), cap(s.cap) { // leave s in a state in which it is safe to run the destructor. s.elements = s.first_free = s.cap = nullptr; } StrVec& StrVec::operator = (StrVec &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; } bool operator==(const StrVec &lhs, const StrVec &rhs) { return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } bool operator!=(const StrVec &lhs, const StrVec &rhs) { return !(lhs == rhs); } bool operator<(const StrVec &lhs, const StrVec &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } bool operator>(const StrVec &lhs, const StrVec &rhs) { return rhs < lhs; } bool operator<=(const StrVec &lhs, const StrVec &rhs) { return !(rhs < lhs); } bool operator>=(const StrVec &lhs, const StrVec &rhs) { return !(lhs < rhs); } ================================================ FILE: ch14/ex14_26_StrVec.h ================================================ #ifndef CP5_STRVEC_H_ #define CP5_STRVEC_H_ #include #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrVec { friend bool operator==(const StrVec&, const StrVec&); friend bool operator!=(const StrVec&, const StrVec&); friend bool operator< (const StrVec&, const StrVec&); friend bool operator> (const StrVec&, const StrVec&); friend bool operator<=(const StrVec&, const StrVec&); friend bool operator>=(const StrVec&, const StrVec&); public: StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list); StrVec(const StrVec&); StrVec& operator=(const StrVec&); StrVec(StrVec&&) NOEXCEPT; StrVec& operator=(StrVec&&)NOEXCEPT; ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string *begin() const { return elements; } std::string *end() const { return first_free; } std::string& at(size_t pos) { return *(elements + pos); } const std::string& at(size_t pos) const { return *(elements + pos); } std::string& operator[](size_t n) { return elements[n]; } const std::string& operator[](size_t n) const { return elements[n]; } void reserve(size_t new_cap); void resize(size_t count); void resize(size_t count, const std::string&); private: std::pair alloc_n_copy(const std::string*, const std::string*); void free(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void reallocate(); void alloc_n_move(size_t new_cap); void range_initialize(const std::string*, const std::string*); private: std::string *elements; std::string *first_free; std::string *cap; std::allocator alloc; }; bool operator==(const StrVec&, const StrVec&); bool operator!=(const StrVec&, const StrVec&); bool operator< (const StrVec&, const StrVec&); bool operator> (const StrVec&, const StrVec&); bool operator<=(const StrVec&, const StrVec&); bool operator>=(const StrVec&, const StrVec&); #endif ================================================ FILE: ch14/ex14_26_StrVecMain.cpp ================================================ #include "ex14_26_StrVec.h" #include #include int main() { StrVec vec; vec.reserve(6); std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl; vec.reserve(4); std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl; vec.push_back("hello"); vec.push_back("world"); vec.resize(4); for (auto i = vec.begin(); i != vec.end(); ++i) std::cout << *i << std::endl; std::cout << "-EOF-" << std::endl; vec.resize(1); for (auto i = vec.begin(); i != vec.end(); ++i) std::cout << *i << std::endl; std::cout << "-EOF-" << std::endl; StrVec vec_list{ "hello", "world", "pezy" }; for (auto i = vec_list.begin(); i != vec_list.end(); ++i) std::cout << *i << " "; std::cout << std::endl; // Test operator== const StrVec const_vec_list{ "hello", "world", "pezy" }; if (vec_list == const_vec_list) for (const auto &str : const_vec_list) std::cout << str << " "; std::cout << std::endl; // Test operator< const StrVec const_vec_list_small{ "hello", "pezy", "ok" }; std::cout << (const_vec_list_small < const_vec_list) << std::endl; // Test [] std::cout << const_vec_list_small[1] << std::endl; } ================================================ FILE: ch14/ex14_26_String.cpp ================================================ #include "ex14_26_String.h" #include //=========================================================================== // // operator - friend // //=========================================================================== std::ostream& operator<<(std::ostream &os, const String &lhs) { os << lhs.c_str(); return os; } std::istream& operator>>(std::istream &is, String &rhs) { for (char c; (c = is.get()) != '\n';) { rhs.push_back(c); } return is; } bool operator==(const String &lhs, const String &rhs) { return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } bool operator!=(const String &lhs, const String &rhs) { return !(lhs == rhs); } bool operator<(const String &lhs, const String &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } bool operator>(const String &lhs, const String &rhs) { return rhs < lhs; } bool operator<=(const String &lhs, const String &rhs) { return !(rhs < lhs); } bool operator>=(const String &lhs, const String &rhs) { return !(lhs < rhs); } //=========================================================================== // // Constructors // //=========================================================================== String::String(const char *s) { char *sl = const_cast(s); while (*sl) ++sl; range_initializer(s, ++sl); } //=========================================================================== // // Big 5 // //=========================================================================== String::String(const String& rhs) { range_initializer(rhs.elements, rhs.first_free); } String& String::operator = (const String &rhs) { auto newstr = alloc_n_copy(rhs.elements, rhs.first_free); free(); elements = newstr.first; first_free = cap = newstr.second; last_elem = first_free - 1; return *this; } String::String(String &&s) NOEXCEPT : elements(s.elements), last_elem(s.last_elem), first_free(s.first_free), cap(s.cap) { s.elements = s.last_elem = s.first_free = s.cap = nullptr; } String& String::operator = (String &&rhs) NOEXCEPT { if (this != &rhs) { free(); elements = rhs.elements; last_elem = rhs.last_elem; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.last_elem = rhs.first_free = rhs.cap = nullptr; } return *this; } String::~String() { free(); } //=========================================================================== // // members // //=========================================================================== void String::push_back(const char c) { chk_n_alloc(); *last_elem = c; last_elem = first_free; alloc.construct(first_free++, '\0'); } void String::reallocate() { // \0 | - // ^ ^ // elements first_free // last_elem cap auto newcapacity = size() ? 2 * (size() + 1) : 2; alloc_n_move(newcapacity); } void String::alloc_n_move(size_t new_cap) { auto newdata = alloc.allocate(new_cap); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size() + 1; ++i) alloc.construct(dest++, std::move(*elem++)); free(); elements = newdata; last_elem = dest - 1; first_free = dest; cap = elements + new_cap; } void String::free() { if (elements) { std::for_each(elements, first_free, [this](char &c){ alloc.destroy(&c); }); alloc.deallocate(elements, cap - elements); } } std::pair String::alloc_n_copy(const char *b, const char *e) { auto str = alloc.allocate(e - b); return{ str, std::uninitialized_copy(b, e, str) }; } void String::range_initializer(const char *first, const char *last) { auto newstr = alloc_n_copy(first, last); elements = newstr.first; first_free = cap = newstr.second; last_elem = first_free - 1; } void String::reserve(size_t new_cap) { if (new_cap <= capacity()) return; alloc_n_move(new_cap); } void String::resize(size_t count, char c) { if (count > size()) { if (count > capacity()) reserve(count * 2); for (size_t i = size(); i != count; ++i) { *last_elem++ = c; alloc.construct(first_free++, '\0'); } } else if (count < size()) { while (last_elem != elements + count) { --last_elem; alloc.destroy(--first_free); } *last_elem = '\0'; } } void String::resize(size_t count) { resize(count, ' '); } ================================================ FILE: ch14/ex14_26_String.h ================================================ #ifndef CP5_STRING_H__ #define CP5_STRING_H__ #include #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif //=================================================================================== // // |s|t|r|i|n|g|\0|-------------------| // ^ ^ ^ first_free ^ // elements last_elem cap // //=================================================================================== class String { friend std::ostream& operator<<(std::ostream&, const String&); friend std::istream& operator>>(std::istream&, String&); friend bool operator==(const String&, const String&); friend bool operator!=(const String&, const String&); friend bool operator< (const String&, const String&); friend bool operator> (const String&, const String&); friend bool operator<=(const String&, const String&); friend bool operator>=(const String&, const String&); public: String() : String("") { } String(const char *); String(const String&); String& operator=(const String&); String(String &&) NOEXCEPT; String& operator=(String&&)NOEXCEPT; ~String(); void push_back(const char); char* begin() const { return elements; } char* end() const { return last_elem; } char& operator[](size_t n) { return elements[n]; } const char& operator[](size_t n) const { return elements[n]; } const char *c_str() const { return elements; } size_t size() const { return last_elem - elements; } size_t length() const { return size(); } size_t capacity() const { return cap - elements; } void reserve(size_t); void resize(size_t); void resize(size_t, char); private: std::pair alloc_n_copy(const char*, const char*); void range_initializer(const char*, const char*); void free(); void reallocate(); void alloc_n_move(size_t new_cap); void chk_n_alloc() { if (first_free == cap) reallocate(); } private: char *elements; char *last_elem; char *first_free; char *cap; std::allocator alloc; }; std::ostream& operator<<(std::ostream&, const String&); std::istream& operator>>(std::istream&, String&); bool operator==(const String&, const String&); bool operator!=(const String&, const String&); bool operator< (const String&, const String&); bool operator> (const String&, const String&); bool operator<=(const String&, const String&); bool operator>=(const String&, const String&); #endif ================================================ FILE: ch14/ex14_26_StringMain.cpp ================================================ #include "ex14_26_String.h" #include #include #include #include #include void foo(String x) { std::cout << x << std::endl; } void bar(const String& x) { std::cout << x.c_str() << std::endl; } String baz() { String ret("world"); return ret; } int main() { char text[] = "world"; String s0; String s1("hello"); String s2(std::move(s0)); String s3 = s1; String s4(text); s2 = s1; if (s2 == s1) std::cout << "s2 == s1" << std::endl; foo(s1); bar(s1); foo("temporary"); bar("temporary"); String s5 = baz(); std::vector svec; //svec.push_back(s0); svec.push_back(s1); svec.push_back(s2); svec.push_back(s3); svec.push_back(s4); svec.push_back(baz()); svec.push_back("good job"); for (const auto &s : svec) { std::cout << s << std::endl; } std::cout << "Input a string: "; String s6; std::cin >> s6; std::cout << s6 << std::endl; if (s6 > s1) std::cout << "s6 > s1" << std::endl; std::cout << s5[2] << std::endl; } ================================================ FILE: ch14/ex14_27_28_StrBlob.cpp ================================================ #include "ex14_27_28_StrBlob.h" #include //================================================================== // // StrBlob - operators // //================================================================== bool operator==(const StrBlob &lhs, const StrBlob &rhs) { return *lhs.data == *rhs.data; } bool operator!=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs == rhs); } bool operator< (const StrBlob &lhs, const StrBlob &rhs) { return std::lexicographical_compare(lhs.data->begin(), lhs.data->end(), rhs.data->begin(), rhs.data->end()); } bool operator> (const StrBlob &lhs, const StrBlob &rhs) { return rhs < lhs; } bool operator<=(const StrBlob &lhs, const StrBlob &rhs) { return !(rhs < lhs); } bool operator>=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs < rhs); } //================================================================ // // StrBlobPtr - operators // //================================================================ bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr < y.curr; } bool operator>(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr > y.curr; } bool operator<=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr <= y.curr; } bool operator>=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr >= y.curr; } //================================================================ // // ConstStrBlobPtr - operators // //================================================================ bool operator==(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr < rhs.curr; } bool operator>(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr > rhs.curr; } bool operator<=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr <= rhs.curr; } bool operator>=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr >= rhs.curr; } //================================================================== // // copy assignment operator and move assignment operator. // //================================================================== StrBlob& StrBlob::operator=(const StrBlob &lhs) { data = make_shared>(*lhs.data); return *this; } StrBlob& StrBlob::operator=(StrBlob &&rhs) NOEXCEPT { if (this != &rhs) { data = std::move(rhs.data); rhs.data = nullptr; } return *this; } //================================================================== // // members // //================================================================== StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); } ConstStrBlobPtr StrBlob::cbegin() const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::cend() const { return ConstStrBlobPtr(*this, data->size()); } ================================================ FILE: ch14/ex14_27_28_StrBlob.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code StrBlob, StrBlobPtr, ConstStrBlobPtr If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_STRBLOB_H_ #define CP5_STRBLOB_H_ #include using std::vector; #include using std::string; #include using std::initializer_list; #include using std::make_shared; using std::shared_ptr; #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrBlobPtr; class ConstStrBlobPtr; //================================================================================= // // StrBlob - custom vector // //================================================================================= class StrBlob { using size_type = vector::size_type; friend class ConstStrBlobPtr; friend class StrBlobPtr; friend bool operator==(const StrBlob&, const StrBlob&); friend bool operator!=(const StrBlob&, const StrBlob&); friend bool operator< (const StrBlob&, const StrBlob&); friend bool operator> (const StrBlob&, const StrBlob&); friend bool operator<=(const StrBlob&, const StrBlob&); friend bool operator>=(const StrBlob&, const StrBlob&); public: StrBlob() : data(make_shared>()) { } StrBlob(initializer_list il) : data(make_shared>(il)) { } StrBlob(const StrBlob &sb) : data(make_shared>(*sb.data)) { } StrBlob& operator=(const StrBlob&); StrBlob(StrBlob &&rhs) NOEXCEPT : data(std::move(rhs.data)) { } StrBlob& operator=(StrBlob &&)NOEXCEPT; StrBlobPtr begin(); StrBlobPtr end(); ConstStrBlobPtr cbegin() const; ConstStrBlobPtr cend() const; string& operator[](size_t n); const string& operator[](size_t n) const; size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void push_back(string &&s) { data->push_back(std::move(s)); } void pop_back(); string& front(); string& back(); const string& front() const; const string& back() const; private: void check(size_type, const string&) const; shared_ptr> data; }; bool operator==(const StrBlob&, const StrBlob&); bool operator!=(const StrBlob&, const StrBlob&); bool operator< (const StrBlob&, const StrBlob&); bool operator> (const StrBlob&, const StrBlob&); bool operator<=(const StrBlob&, const StrBlob&); bool operator>=(const StrBlob&, const StrBlob&); inline void StrBlob::pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } inline string& StrBlob::front() { check(0, "front on empty StrBlob"); return data->front(); } inline string& StrBlob::back() { check(0, "back on empty StrBlob"); return data->back(); } inline const string& StrBlob::front() const { check(0, "front on empty StrBlob"); return data->front(); } inline const string& StrBlob::back() const { check(0, "back on empty StrBlob"); return data->back(); } inline void StrBlob::check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } inline string& StrBlob::operator[](size_t n) { check(n, "out of range"); return data->at(n); } inline const string& StrBlob::operator[](size_t n) const { check(n, "out of range"); return data->at(n); } //================================================================================= // // StrBlobPtr - custom iterator of StrBlob // //================================================================================= class StrBlobPtr { friend bool operator==(const StrBlobPtr&, const StrBlobPtr&); friend bool operator!=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator< (const StrBlobPtr&, const StrBlobPtr&); friend bool operator> (const StrBlobPtr&, const StrBlobPtr&); friend bool operator<=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator>=(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr() : curr(0) { } StrBlobPtr(StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } string& deref() const; StrBlobPtr& operator++(); StrBlobPtr& operator--(); StrBlobPtr operator++(int); StrBlobPtr operator--(int); StrBlobPtr& operator+=(size_t); StrBlobPtr& operator-=(size_t); StrBlobPtr operator+(size_t) const; StrBlobPtr operator-(size_t) const; string& operator[](size_t n); const string& operator[](size_t n) const; private: shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const StrBlobPtr&, const StrBlobPtr&); bool operator!=(const StrBlobPtr&, const StrBlobPtr&); bool operator< (const StrBlobPtr&, const StrBlobPtr&); bool operator> (const StrBlobPtr&, const StrBlobPtr&); bool operator<=(const StrBlobPtr&, const StrBlobPtr&); bool operator>=(const StrBlobPtr&, const StrBlobPtr&); inline string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline StrBlobPtr& StrBlobPtr::operator++() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline StrBlobPtr& StrBlobPtr::operator--() { check(--curr, "decrement past begin of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator++(int) { StrBlobPtr ret = *this; ++*this; return ret; } inline StrBlobPtr StrBlobPtr::operator--(int) { StrBlobPtr ret = *this; --*this; return ret; } inline StrBlobPtr& StrBlobPtr::operator+=(size_t n) { curr += n; check(curr, "increment past end of StrBlobPtr"); return *this; } inline StrBlobPtr& StrBlobPtr::operator-=(size_t n) { curr -= n; check(curr, "increment past end of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator+(size_t n) const { StrBlobPtr ret = *this; ret += n; return ret; } inline StrBlobPtr StrBlobPtr::operator-(size_t n) const { StrBlobPtr ret = *this; ret -= n; return ret; } inline shared_ptr> StrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } inline string& StrBlobPtr::operator[](size_t n) { auto p = check(n, "dereference out of range."); return (*p)[n]; } inline const string& StrBlobPtr::operator[](size_t n) const { auto p = check(n, "dereference out of range."); return (*p)[n]; } //================================================================================= // // ConstStrBlobPtr - custom const_iterator of StrBlob // //================================================================================= class ConstStrBlobPtr { friend bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); public: ConstStrBlobPtr() : curr(0) { } ConstStrBlobPtr(const StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } const string& deref() const; ConstStrBlobPtr& operator++(); ConstStrBlobPtr& operator--(); ConstStrBlobPtr operator++(int); ConstStrBlobPtr operator--(int); ConstStrBlobPtr& operator+=(size_t); ConstStrBlobPtr& operator-=(size_t); ConstStrBlobPtr operator+(size_t) const; ConstStrBlobPtr operator-(size_t) const; const string& operator[](size_t n) const; private: std::shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); inline const string& ConstStrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator++() { check(curr, "increment past end of ConstStrBlobPtr"); ++curr; return *this; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator--() { --curr; check(-1, "decrement past begin of ConstStrBlobPtr"); return *this; } inline ConstStrBlobPtr ConstStrBlobPtr::operator++(int) { ConstStrBlobPtr ret = *this; ++*this; return ret; } inline ConstStrBlobPtr ConstStrBlobPtr::operator--(int) { ConstStrBlobPtr ret = *this; --*this; return ret; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator+=(size_t n) { curr += n; check(curr, "increment past end of ConstStrBlobPtr"); return *this; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator-=(size_t n) { curr -= n; check(curr, "increment past end of ConstStrBlobPtr"); return *this; } inline ConstStrBlobPtr ConstStrBlobPtr::operator+(size_t n) const { ConstStrBlobPtr ret = *this; ret += n; return ret; } inline ConstStrBlobPtr ConstStrBlobPtr::operator-(size_t n) const { ConstStrBlobPtr ret = *this; ret -= n; return ret; } inline std::shared_ptr> ConstStrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } inline const string& ConstStrBlobPtr::operator[](size_t n) const { auto p = check(n, "dereference out of range."); return (*p)[n]; } #endif //CP5_STRBLOB_H_ ================================================ FILE: ch14/ex14_27_28_StrBlobTest.cpp ================================================ #include "ex14_27_28_StrBlob.h" #include int main() { StrBlob sb1{ "a", "b", "c" }; StrBlob sb2 = sb1; sb2[2] = "b"; if (sb1 > sb2) { for (StrBlobPtr iter = sb1.begin(); iter < sb1.end(); ++iter) std::cout << iter.deref() << " "; std::cout << std::endl; } ConstStrBlobPtr iter(sb2); std::cout << (iter + 2).deref() << std::endl; } ================================================ FILE: ch14/ex14_30_StrBlob.cpp ================================================ #include "ex14_30_StrBlob.h" #include //================================================================== // // StrBlob - operators // //================================================================== bool operator==(const StrBlob &lhs, const StrBlob &rhs) { return *lhs.data == *rhs.data; } bool operator!=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs == rhs); } bool operator< (const StrBlob &lhs, const StrBlob &rhs) { return std::lexicographical_compare(lhs.data->begin(), lhs.data->end(), rhs.data->begin(), rhs.data->end()); } bool operator> (const StrBlob &lhs, const StrBlob &rhs) { return rhs < lhs; } bool operator<=(const StrBlob &lhs, const StrBlob &rhs) { return !(rhs < lhs); } bool operator>=(const StrBlob &lhs, const StrBlob &rhs) { return !(lhs < rhs); } //================================================================ // // StrBlobPtr - operators // //================================================================ bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr < y.curr; } bool operator>(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr > y.curr; } bool operator<=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr <= y.curr; } bool operator>=(const StrBlobPtr &x, const StrBlobPtr &y) { return x.curr >= y.curr; } //================================================================ // // ConstStrBlobPtr - operators // //================================================================ bool operator==(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr == rhs.curr; } bool operator!=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return !(lhs == rhs); } bool operator< (const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr < rhs.curr; } bool operator>(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr > rhs.curr; } bool operator<=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr <= rhs.curr; } bool operator>=(const ConstStrBlobPtr &lhs, const ConstStrBlobPtr &rhs) { return lhs.curr >= rhs.curr; } //================================================================== // // copy assignment operator and move assignment operator. // //================================================================== StrBlob& StrBlob::operator=(const StrBlob &lhs) { data = make_shared>(*lhs.data); return *this; } StrBlob& StrBlob::operator=(StrBlob &&rhs) NOEXCEPT { if (this != &rhs) { data = std::move(rhs.data); rhs.data = nullptr; } return *this; } //================================================================== // // members // //================================================================== StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); } ConstStrBlobPtr StrBlob::cbegin() const { return ConstStrBlobPtr(*this); } ConstStrBlobPtr StrBlob::cend() const { return ConstStrBlobPtr(*this, data->size()); } ================================================ FILE: ch14/ex14_30_StrBlob.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code StrBlob, StrBlobPtr, ConstStrBlobPtr If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_STRBLOB_H_ #define CP5_STRBLOB_H_ #include using std::vector; #include using std::string; #include using std::initializer_list; #include using std::make_shared; using std::shared_ptr; #include #ifndef _MSC_VER #define NOEXCEPT noexcept #else #define NOEXCEPT #endif class StrBlobPtr; class ConstStrBlobPtr; //================================================================================= // // StrBlob - custom vector // //================================================================================= class StrBlob { using size_type = vector::size_type; friend class ConstStrBlobPtr; friend class StrBlobPtr; friend bool operator==(const StrBlob&, const StrBlob&); friend bool operator!=(const StrBlob&, const StrBlob&); friend bool operator< (const StrBlob&, const StrBlob&); friend bool operator> (const StrBlob&, const StrBlob&); friend bool operator<=(const StrBlob&, const StrBlob&); friend bool operator>=(const StrBlob&, const StrBlob&); public: StrBlob() : data(make_shared>()) { } StrBlob(initializer_list il) : data(make_shared>(il)) { } StrBlob(const StrBlob &sb) : data(make_shared>(*sb.data)) { } StrBlob& operator=(const StrBlob&); StrBlob(StrBlob &&rhs) NOEXCEPT : data(std::move(rhs.data)) { } StrBlob& operator=(StrBlob &&)NOEXCEPT; StrBlobPtr begin(); StrBlobPtr end(); ConstStrBlobPtr cbegin() const; ConstStrBlobPtr cend() const; string& operator[](size_t n); const string& operator[](size_t n) const; size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &t) { data->push_back(t); } void push_back(string &&s) { data->push_back(std::move(s)); } void pop_back(); string& front(); string& back(); const string& front() const; const string& back() const; private: void check(size_type, const string&) const; shared_ptr> data; }; bool operator==(const StrBlob&, const StrBlob&); bool operator!=(const StrBlob&, const StrBlob&); bool operator< (const StrBlob&, const StrBlob&); bool operator> (const StrBlob&, const StrBlob&); bool operator<=(const StrBlob&, const StrBlob&); bool operator>=(const StrBlob&, const StrBlob&); inline void StrBlob::pop_back() { check(0, "pop_back on empty StrBlob"); data->pop_back(); } inline string& StrBlob::front() { check(0, "front on empty StrBlob"); return data->front(); } inline string& StrBlob::back() { check(0, "back on empty StrBlob"); return data->back(); } inline const string& StrBlob::front() const { check(0, "front on empty StrBlob"); return data->front(); } inline const string& StrBlob::back() const { check(0, "back on empty StrBlob"); return data->back(); } inline void StrBlob::check(size_type i, const string &msg) const { if (i >= data->size()) throw std::out_of_range(msg); } inline string& StrBlob::operator[](size_t n) { check(n, "out of range"); return data->at(n); } inline const string& StrBlob::operator[](size_t n) const { check(n, "out of range"); return data->at(n); } //================================================================================= // // StrBlobPtr - custom iterator of StrBlob // //================================================================================= class StrBlobPtr { friend bool operator==(const StrBlobPtr&, const StrBlobPtr&); friend bool operator!=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator< (const StrBlobPtr&, const StrBlobPtr&); friend bool operator> (const StrBlobPtr&, const StrBlobPtr&); friend bool operator<=(const StrBlobPtr&, const StrBlobPtr&); friend bool operator>=(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr() : curr(0) { } StrBlobPtr(StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } string& deref() const; StrBlobPtr& operator++(); StrBlobPtr& operator--(); StrBlobPtr operator++(int); StrBlobPtr operator--(int); StrBlobPtr& operator+=(size_t); StrBlobPtr& operator-=(size_t); StrBlobPtr operator+(size_t) const; StrBlobPtr operator-(size_t) const; string& operator[](size_t n); const string& operator[](size_t n) const; private: shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const StrBlobPtr&, const StrBlobPtr&); bool operator!=(const StrBlobPtr&, const StrBlobPtr&); bool operator< (const StrBlobPtr&, const StrBlobPtr&); bool operator> (const StrBlobPtr&, const StrBlobPtr&); bool operator<=(const StrBlobPtr&, const StrBlobPtr&); bool operator>=(const StrBlobPtr&, const StrBlobPtr&); inline string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline StrBlobPtr& StrBlobPtr::operator++() { check(curr, "increment past end of StrBlobPtr"); ++curr; return *this; } inline StrBlobPtr& StrBlobPtr::operator--() { --curr; check(-1, "decrement past begin of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator++(int) { StrBlobPtr ret = *this; ++*this; return ret; } inline StrBlobPtr StrBlobPtr::operator--(int) { StrBlobPtr ret = *this; --*this; return ret; } inline StrBlobPtr& StrBlobPtr::operator+=(size_t n) { curr += n; check(curr, "increment past end of StrBlobPtr"); return *this; } inline StrBlobPtr& StrBlobPtr::operator-=(size_t n) { curr -= n; check(curr, "increment past end of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator+(size_t n) const { StrBlobPtr ret = *this; ret += n; return ret; } inline StrBlobPtr StrBlobPtr::operator-(size_t n) const { StrBlobPtr ret = *this; ret -= n; return ret; } inline shared_ptr> StrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } inline string& StrBlobPtr::operator[](size_t n) { auto p = check(n, "dereference out of range."); return (*p)[n]; } inline const string& StrBlobPtr::operator[](size_t n) const { auto p = check(n, "dereference out of range."); return (*p)[n]; } //================================================================================= // // ConstStrBlobPtr - custom const_iterator of StrBlob // //================================================================================= class ConstStrBlobPtr { friend bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); friend bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); public: ConstStrBlobPtr() : curr(0) { } ConstStrBlobPtr(const StrBlob &s, size_t sz = 0) : wptr(s.data), curr(sz) { } const string& operator*() const; const string* operator->() const; ConstStrBlobPtr& operator++(); ConstStrBlobPtr& operator--(); ConstStrBlobPtr operator++(int); ConstStrBlobPtr operator--(int); ConstStrBlobPtr& operator+=(size_t); ConstStrBlobPtr& operator-=(size_t); ConstStrBlobPtr operator+(size_t) const; ConstStrBlobPtr operator-(size_t) const; const string& operator[](size_t n) const; private: std::shared_ptr> check(size_t, const string&) const; std::weak_ptr> wptr; size_t curr; }; bool operator==(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator!=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator< (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator> (const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator<=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); bool operator>=(const ConstStrBlobPtr&, const ConstStrBlobPtr&); inline const string& ConstStrBlobPtr::operator*() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline const string* ConstStrBlobPtr::operator->() const { return &this->operator*(); } inline ConstStrBlobPtr& ConstStrBlobPtr::operator++() { check(curr, "increment past end of ConstStrBlobPtr"); ++curr; return *this; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator--() { --curr; check(-1, "decrement past begin of ConstStrBlobPtr"); return *this; } inline ConstStrBlobPtr ConstStrBlobPtr::operator++(int) { ConstStrBlobPtr ret = *this; ++*this; return ret; } inline ConstStrBlobPtr ConstStrBlobPtr::operator--(int) { ConstStrBlobPtr ret = *this; --*this; return ret; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator+=(size_t n) { curr += n; check(curr, "increment past end of ConstStrBlobPtr"); return *this; } inline ConstStrBlobPtr& ConstStrBlobPtr::operator-=(size_t n) { curr -= n; check(curr, "increment past end of ConstStrBlobPtr"); return *this; } inline ConstStrBlobPtr ConstStrBlobPtr::operator+(size_t n) const { ConstStrBlobPtr ret = *this; ret += n; return ret; } inline ConstStrBlobPtr ConstStrBlobPtr::operator-(size_t n) const { ConstStrBlobPtr ret = *this; ret -= n; return ret; } inline std::shared_ptr> ConstStrBlobPtr::check(size_t i, const string &msg) const { auto ret = wptr.lock(); if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; } inline const string& ConstStrBlobPtr::operator[](size_t n) const { auto p = check(n, "dereference out of range."); return (*p)[n]; } #endif //CP5_STRBLOB_H_ ================================================ FILE: ch14/ex14_30_StrBlobTest.cpp ================================================ #include "ex14_30_StrBlob.h" #include int main() { StrBlob sb1{ "a", "b", "c" }; StrBlob sb2 = sb1; sb2[2] = "b"; if (sb1 > sb2) { for (ConstStrBlobPtr iter = sb1.cbegin(); iter != sb1.cend(); ++iter) std::cout << *iter << " "; std::cout << std::endl; } ConstStrBlobPtr iter(sb2); std::cout << iter->size() << std::endl; } ================================================ FILE: ch14/ex14_32.cpp ================================================ #include "ex14_32.h" #include "ex14_30_StrBlob.h" #include StrBlobPtr& StrBlobPtr_pointer::operator *() const { return *pointer; } StrBlobPtr* StrBlobPtr_pointer::operator ->() const { return pointer; } int main() { StrBlob sb{ "hello", "world" }; StrBlobPtr iter = sb.begin(); StrBlobPtr_pointer p(&iter); std::cout << p->deref() << std::endl; } ================================================ FILE: ch14/ex14_32.h ================================================ /*************************************************************************** * @file StrBlobPtr_pointer.h * @author Alan.W * @date 19 JAN 2014 * @remark a class that holds a pointer to a StrBlobPtr. ***************************************************************************/ #ifndef STRBLOBPTR_POINTER_H #define STRBLOBPTR_POINTER_H class StrBlobPtr; /** * @brief a class that holds a pointer to a StrBlobPtr. */ class StrBlobPtr_pointer { public: StrBlobPtr_pointer() = default; StrBlobPtr_pointer(StrBlobPtr* p) : pointer(p) { } StrBlobPtr& operator *() const; StrBlobPtr* operator->() const; private: StrBlobPtr* pointer = nullptr; }; #endif // STRBLOBPTR_POINTER_H ================================================ FILE: ch14/ex14_35.cpp ================================================ #include #include class GetInput { public: GetInput(std::istream &i = std::cin) : is(i) { } std::string operator()() const { std::string str; std::getline(is, str); return is ? str : std::string(); } private: std::istream &is; }; int main() { GetInput getInput; std::cout << getInput() << std::endl; } ================================================ FILE: ch14/ex14_36.cpp ================================================ #include #include #include class GetInput { public: GetInput(std::istream &i = std::cin) : is(i) { } std::string operator()() const { std::string str; std::getline(is, str); return is ? str : std::string(); } private: std::istream &is; }; int main() { GetInput getInput; std::vector vec; for ( std::string tmp; !( tmp = getInput() ).empty(); ) vec.push_back( tmp ); for (const auto &str : vec) std::cout << str << " "; std::cout << std::endl; } ================================================ FILE: ch14/ex14_37.cpp ================================================ #include #include #include class IsEqual { int value; public: IsEqual(int v) : value(v) { } bool operator()(int elem) { return elem == value; } }; int main() { std::vector vec = { 3, 2, 1, 4, 3, 7, 8, 6 }; std::replace_if(vec.begin(), vec.end(), IsEqual(3), 5); for (int i : vec) std::cout << i << " "; std::cout << std::endl; } ================================================ FILE: ch14/ex14_38_39.cpp ================================================ // // Exercise 14.38: // Write a class that tests whether the length of a given string matches a // given bound. Use that object to write a program to report how many words // in an input file are of sizes 1 through 10 inclusive. // // Exercise 14.39: // Revise the previous program to report the count of words that are sizes // 1 through 9 and 10 or more. // #include #include #include #include #include struct IsInRange { IsInRange(std::size_t lower, std::size_t upper) :_lower(lower), _upper(upper) { } bool operator()(std::string const& str) const { return str.size() >= _lower && str.size() <= _upper; } std::size_t lower_limit() const { return _lower; } std::size_t upper_limit() const { return _upper; } private: std::size_t _lower; std::size_t _upper; }; int main() { //create predicates with various upper limits. std::size_t lower = 1; auto uppers = { 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u }; std::vector predicates; for (auto upper : uppers) predicates.push_back(IsInRange{ lower, upper }); //create count_table to store counts std::map count_table; for (auto upper : uppers) count_table[upper] = 0; //read file and count std::ifstream fin("../data/storyDataFile.txt"); for (std::string word; fin >> word; /* */) for (auto is_size_in_range : predicates) if (is_size_in_range(word)) ++count_table[is_size_in_range.upper_limit()]; //print for (auto pair : count_table) std::cout << "count in range [1, " << pair.first << "] : " << pair.second << std::endl; return 0; } ================================================ FILE: ch14/ex14_40.cpp ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code Rewrite the `biggies` function to use function-object classes in place of lambdas If you have questions, try to connect with me: pezy ================================================================================= */ #include using std::vector; #include using std::string; #include using std::cout; using std::endl; #include using std::sort; using std::stable_sort; using std::for_each; class ShorterString { public: bool operator()(string const& s1, string const& s2) const { return s1.size() < s2.size(); } }; class BiggerEqual { size_t sz_; public: BiggerEqual(size_t sz) : sz_(sz) { } bool operator()(string const& s) { return s.size() >= sz_; } }; class Print { public: void operator()(string const& s) { cout << s << " "; } }; string make_plural(size_t ctr, string const& word, string const& ending) { return (ctr > 1) ? word + ending : word; } void elimDups(vector &words) { sort(words.begin(), words.end()); auto end_unique = unique(words.begin(), words.end()); words.erase(end_unique, words.end()); } void biggies( vector &words, vector::size_type sz ) { elimDups(words); stable_sort(words.begin(), words.end(), ShorterString()); auto wc = find_if(words.begin(), words.end(), BiggerEqual(sz)); auto count = words.end() - wc; cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl; for_each(wc, words.end(), Print()); cout << endl; } int main() { vector vec{ "fox", "jumps", "over", "quick", "red", "red", "slow", "the", "turtle" }; biggies(vec, 4); } ================================================ FILE: ch14/ex14_42.cpp ================================================ #include #include #include #include #include int main() { using std::placeholders::_1; std::vector ivec { 1, 111, 1111, 11111 }; int count = std::count_if (ivec.cbegin(), ivec.cend(), std::bind(std::greater(), _1, 1024)); std::cout << count << std::endl; std::vector svec { "pooh", "pooh", "pezy", "pooh" }; auto found = std::find_if (svec.cbegin(), svec.cend(), std::bind(std::not_equal_to(), _1, "pooh")); std::cout << *found << std::endl; std::transform(ivec.begin(), ivec.end(), ivec.begin(), std::bind(std::multiplies(), _1, 2)); for (int i : ivec) std::cout << i << " "; std::cout << std::endl; } ================================================ FILE: ch14/ex14_43.cpp ================================================ /*************************************************************************** * @file main.cpp * @author XDXX, Yue Wang * @date 5/24/2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 14.43: // Using library function objects, determine whether a given int value is // divisible by any element in a container of ints. // #include #include #include #include int main() { auto data = { 2, 3, 4, 5 }; int input; std::cin >> input; std::modulus mod; auto predicator = [&](int i){ return 0 == mod(input, i); }; auto is_divisible = std::any_of(data.begin(), data.end(), predicator); std::cout << (is_divisible ? "Yes!" : "No!") << std::endl; return 0; } ================================================ FILE: ch14/ex14_44.cpp ================================================ /*************************************************************************** * @file ex14_44.cpp * @author Yue Wang * @date 21 Jan 2014 * 17 Jun 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 14.44: // Write your own version of a simple desk calculator that can handle binary operations. // #include #include #include #include int add(int i, int j){ return i + j; } auto mod = [](int i, int j){ return i % j; }; struct Div{ int operator ()(int i, int j) const { return i / j; } }; auto binops = std::map> { { "+", add }, // function pointer { "-", std::minus() }, // library functor { "/", Div() }, // user-defined functor { "*", [](int i, int j) { return i*j; } }, // unnamed lambda { "%", mod } // named lambda object }; int main() { while ( std::cout << "Pls enter as: num operator num :\n", true ) { int lhs, rhs; std::string op; std::cin >> lhs >> op >> rhs; std::cout << binops[op](lhs, rhs) << std::endl; } return 0; } ================================================ FILE: ch14/ex14_45.cpp ================================================ #include "ex14_45.h" Sales_data::Sales_data(std::istream &is) : Sales_data() { is >> *this; } Sales_data& Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } std::istream& operator>>(std::istream &is, Sales_data &item) { double price = 0.0; is >> item.bookNo >> item.units_sold >> price; if (is) item.revenue = price * item.units_sold; else item = Sales_data(); return is; } std::ostream& operator<<(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum += rhs; return sum; } Sales_data& Sales_data::operator=(const std::string &isbn) { *this = Sales_data(isbn); return *this; } ================================================ FILE: ch14/ex14_45.h ================================================ /* ================================================================================= C++ Primer 5th Exercise Answer Source Code Sales_data If you have questions, try to connect with me: pezy ================================================================================= */ #ifndef CP5_ex14_45_h #define CP5_ex14_45_h #include #include class Sales_data { friend std::istream& operator>>(std::istream&, Sales_data&); friend std::ostream& operator<<(std::ostream&, const Sales_data&); friend Sales_data operator+(const Sales_data&, const Sales_data&); public: Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ } Sales_data() : Sales_data("", 0, 0.0f){ } Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f){ } Sales_data(std::istream &is); Sales_data& operator=(const std::string&); Sales_data& operator+=(const Sales_data&); explicit operator std::string() const { return bookNo; } explicit operator double() const { return avg_price(); } std::string isbn() const { return bookNo; } private: inline double avg_price() const; std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; std::istream& operator>>(std::istream&, Sales_data&); std::ostream& operator<<(std::ostream&, const Sales_data&); Sales_data operator+(const Sales_data&, const Sales_data&); inline double Sales_data::avg_price() const { return units_sold ? revenue/units_sold : 0; } #endif ================================================ FILE: ch14/ex14_45_TEST.cpp ================================================ #include "ex14_45.h" int main() { Sales_data cp5("C++ Primer 5th", 4, 106.5); std::cout << cp5 << std::endl; std::cout << static_cast(cp5) << std::endl; std::cout << static_cast(cp5) << std::endl; } ================================================ FILE: ch14/ex14_49.cpp ================================================ #include "ex14_49.h" #include // constructor taking Size as days // the argument must be within (0, 2^32) Date::Date(Size days) { // calculate the year Size y400 = days/YtoD_400; Size y100 = (days - y400*YtoD_400)/YtoD_100; Size y4 = (days - y400*YtoD_400 - y100*YtoD_100)/YtoD_4; Size y = (days - y400*YtoD_400 - y100*YtoD_100 - y4*YtoD_4)/365; Size d = days - y400*YtoD_400 - y100*YtoD_100 - y4*YtoD_4 - y*365; this->year = y400*400 + y100*100 + y4*4 + y; // check if leap and choose the months vector accordingly std::vectorcurrYear = isLeapYear(this->year) ? monthsVec_l : monthsVec_n; // calculate day and month using find_if + lambda Size D_accumu = 0, M_accumu = 0; // @bug fixed: the variabbles above hade been declared inside the find_if as static // which caused the bug. It works fine now after being move outside. std::find_if(currYear.cbegin(), currYear.cend(), [&](Size m){ D_accumu += m; M_accumu ++; if(d < D_accumu) { this->month = M_accumu; this->day = d + m - D_accumu ; return true; } else return false; }); } // construcotr taking iostream Date::Date(std::istream &is, std::ostream &os) { is >> day >> month >> year; if(is) { if(check(*this)) return; else { os << "Invalid input! Object is default initialized."; *this = Date(); } } else { os << "Invalid input! Object is default initialized."; *this = Date(); } } // copy constructor Date::Date(const Date &d) : day(d.day), month(d.month), year(d.year) { } // move constructor Date::Date(Date&& d) noexcept : day(d.day), month(d.month), year(d.year) { std::cout << "copy moving"; } // copy operator= Date &Date::operator= (const Date &d) { this->day = d.day; this->month = d.month; this->year = d.year; return *this; } // move operator= Date &Date::operator =(Date&& rhs) noexcept { if(this != &rhs) { this->day = rhs.day; this->month = rhs.month; this->year = rhs.year; } std::cout << "moving ="; return *this; } // conver to days Date::Size Date::toDays() const { Size result = this->day; // check if leap and choose the months vector accordingly std::vectorcurrYear = isLeapYear(this->year) ? monthsVec_l : monthsVec_n; // calculate result + days by months for(auto it = currYear.cbegin(); it != currYear.cbegin() + this->month -1; ++it) result += *it; // calculate result + days by years result += (this->year/400) * YtoD_400; result += (this->year%400/100) * YtoD_100; result += (this->year%100/4) * YtoD_4; result += (this->year%4) * YtoD_1; return result; } // member operators: += -= Date &Date::operator +=(Date::Size offset) { *this = Date(this->toDays() + offset); return *this; } Date &Date::operator -=(Date::Size offset) { if(this->toDays() > offset) *this = Date(this->toDays() - offset); else *this = Date(); return *this; } // non-member operators: << >> - == != < <= > >= std::ostream& operator <<(std::ostream& os, const Date& d) { os << d.day << " " << d.month << " " << d.year; return os; } std::istream& operator >>(std::istream& is, Date& d) { if(is) { Date input = Date(is, std::cout); if(check(input)) d = input; } return is; } int operator -(const Date &lhs, const Date &rhs) { return lhs.toDays() - rhs.toDays(); } bool operator ==(const Date &lhs, const Date &rhs) { return (lhs.day == rhs.day ) && (lhs.month == rhs.month) && (lhs.year == rhs.year ) ; } bool operator !=(const Date &lhs, const Date &rhs) { return !(lhs == rhs); } bool operator < (const Date &lhs, const Date &rhs) { return lhs.toDays() < rhs.toDays(); } bool operator <=(const Date &lhs, const Date &rhs) { return (lhs < rhs) || (lhs == rhs); } bool operator >(const Date &lhs, const Date &rhs) { return !(lhs <= rhs); } bool operator >=(const Date &lhs, const Date &rhs) { return !(lhs < rhs); } Date operator - (const Date &lhs, Date::Size rhs) { // ^^^ rhs must not be larger than 2^32-1 // copy lhs Date result(lhs); result -= rhs; return result; } Date operator + (const Date &lhs, Date::Size rhs) { // ^^^ rhs must not be larger than 2^32-1 // copy lhs Date result(lhs); result += rhs; return result; } ================================================ FILE: ch14/ex14_49.h ================================================ /*************************************************************************** * @file date.h * @author Alan.W * @date 15-17 JAN 2014 * @remark ***************************************************************************/ // // Exercise 7.40: // Choose one of the following abstractions (or an abstraction of your own choosing). // Determine what data are needed in the class. Provide an appropriate set of constructors. // Explain your decisions. // // Exercise 14.5: // In exercise 7.40 from § 7.5.1 (p. 291) you wrote a sketch of one of the // following classes. Decide what, if any, overloaded operators your class // should provide. // - = < > <= >= ++ -- << >> == != += -= // Exercise 14.8: // Define an output operator for the class you chose in exercise 7.40 from // § 7.5.1 (p. 291). // // Exercise 14.12: // Define an input operator for the class you used in exercise 7.40 from // § 7.5.1 (p. 291). Be sure the operator handles input errors. // // Exercise 14.15: // Should the class you chose for exercise 7.40 from § 7.5.1 (p. 291) // define any of the arithmetic operators? If so, implement them. // If not, explain why not. // // arithmetic operators : all non-members // + : Date + Size // - : Date - Size // - : Date - Date // // Exercise 14.17: // Should the class you chose for exercise 7.40 from § 7.5.1 (p. 291) define // the equality operators? If so, implement them. If not, explain why not. // // Exercise 14.19: // Should the class you chose for exercise 7.40 from § 7.5.1 (p. 291) define // the relational operators? If so, implement them. If not, explain why not. // // Exercise 14.25: // Implement any other assignment operators your class should define. // Explain which types should be used as operands and why. // // Exercise 14.49: // Regardless of whether it is a good idea to do so, define a conversion to bool // for the class from the previous exercise. // #ifndef DATE_H #define DATE_H #include #include class Date { friend bool operator ==(const Date& lhs, const Date& rhs); friend bool operator < (const Date &lhs, const Date &rhs); friend bool check(const Date &d); friend std::ostream& operator <<(std::ostream& os, const Date& d); public: typedef std::size_t Size; // default constructor Date() = default; // constructor taking Size as days explicit Date(Size days); // constructor taking three Size Date(Size d, Size m, Size y) : day(d), month(m), year(y) { } // constructor taking iostream Date(std::istream &is, std::ostream &os); // copy constructor Date(const Date& d); // move constructor Date(Date&& d) noexcept; // copy operator= Date& operator= (const Date& d); // move operator= Date& operator= (Date&& rhs) noexcept; // destructor -- in this case, user-defined destructor is not nessary. ~Date(){ std::cout << "destroying\n"; } // members Size toDays() const; //not implemented yet. Date& operator +=(Size offset); Date& operator -=(Size offset); explicit operator bool() { return (year<4000) ? true : false; } private: Size day = 1; Size month = 1; Size year = 0; }; static const Date::Size YtoD_400 = 146097; //365*400 + 400/4 -3 == 146097 static const Date::Size YtoD_100 = 36524; //365*100 + 100/4 -1 == 36524 static const Date::Size YtoD_4 = 1461; //365*4 + 1 == 1461 static const Date::Size YtoD_1 = 365; //365 // normal year static const std::vector monthsVec_n = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // leap year static const std::vector monthsVec_l = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // non-member operators: << >> - == != < <= > >= // std::ostream& operator <<(std::ostream& os, const Date& d); std::istream& operator >>(std::istream& is, Date& d); int operator - (const Date& lhs, const Date& rhs); bool operator ==(const Date& lhs, const Date& rhs); bool operator !=(const Date& lhs, const Date& rhs); bool operator < (const Date& lhs, const Date& rhs); bool operator <=(const Date& lhs, const Date& rhs); bool operator >(const Date& lhs, const Date& rhs); bool operator >=(const Date& lhs, const Date& rhs); Date operator - (const Date& lhs, Date::Size rhs); Date operator +(const Date& lhs, Date::Size rhs); // utillities: bool check(const Date &d); inline bool isLeapYear(Date::Size y); // check if the date object passed in is valid inline bool check(const Date &d) { if(d.month==0 || d.month >12) return false; else { // month == 1 3 5 7 8 10 12 if(d.month==1 || d.month==3 || d.month==5 || d.month==7 || d.month==8 || d.month==10|| d.month==12) { if(d.day==0 || d.day > 31) return false; else return true; } else { // month == 4 6 9 11 if(d.month==4 || d.month==6 || d.month==9 || d.month==11) { if(d.day==0 || d.day > 30) return false; else return true; } else { // month == 2 if(isLeapYear(d.year)) { if(d.day==0 || d.day >29) return false; else return true; } else { if(d.day==0 || d.day >28) return false; else return true; } } } } } inline bool isLeapYear(Date::Size y) { if (!(y%400)) { return true; } else { if(!(y%100)) { return false; } else return !(y%4); } } #endif // DATE_H ================================================ FILE: ch14/ex14_49_TEST.cpp ================================================ #include "ex14_49.h" int main() { Date date(12, 4, 2015); if (static_cast(date)) std::cout << date << std::endl; } ================================================ FILE: ch15/README.md ================================================ # Chapter 15. Object-Oriented Programming ## Exercise 15.1: > What is a virtual member? A virtual member in a base class expects its derived class define its own version. In particular base classes ordinarily should define a virtual destructor, even if it does no work. ## Exercise 15.2: > How does the protected access specifier differ from private? * **private member**: base class itself and friend can access * **protected members**: base class itself, friend and derived classes can access ## Exercise 15.3: > Define your own versions of the `Quote` class and the `print_total` function. [Quote](ex15.1.2.3/quote.h) | [main.cpp](ex15.1.2.3/main.cpp) ## Exercise 15.4: > Which of the following declarations, if any, are incorrect? Explain why. > class Base { ... }; > (a) class Derived : public Derived { ... }; > (b) class Derived : private Base { ... }; > (c) class Derived : public Base; * (a): **incorrect**, derive from itself. * (b): **incorrect**, this is a definition not a declaration. * (c): **incorrect**, A derived class is declared like any other class. The declaration contains the class name but does not include its derivation list. ## Exercise 15.5: > Define your own version of the `Bulk_quote` class. [Bulk_quote](ex15.4.5.6/bulk_quote.h) ## Exercise 15.6: > Test your `print_total` function from the exercises in § 15.2.1 (p. 595) by passing both `Quote` and `Bulk_quote` objects o that function. [main](ex15.4.5.6/main.cpp) ## [Exercise 15.7](ex15.7/main.cpp) ## Exercise 15.8 > Define static type and dynamic type. The static type of an expression is always known at compile time. The dynamic type is the type of the object in memory that the variable or expression represents. The dynamic type may not be known until run time. ## Exercise 15.9: > When is it possible for an expression’s static type to differ from its dynamic type? Give three examples in which the static and dynamic type differ. The static type of a pointer or reference to a base class may differ from its dynamic type. Anything like this can be an example. [Exercise 15.9](ex15.9/main.cpp) ## Exercise 15.10: > Recalling the discussion from §8.1 (p. 311), explain how the program on page 317 that passed an `ifstream` to the `Sales_data` read function works. The function takes a `std::istream` from which `std::ifstream` is derived. Hence the `ifstream` object "is a" i`stream` , which is why it works. ## Exercise 15.11: > Add a virtual debug function to your `Quote` class hierarchy that displays the data members of the respective classes. ```cpp void Quote::debug() const { std::cout << "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " "; } ``` [test](ex15.11/main.cpp) ## Exercise 15.12: > Is it ever useful to declare a member function as both override and final? Why or why not? Sure. override means overriding the same name virtual function in base class. final means preventing any overriding this virtual function by any derived classes that are more lower at the hierarchy. ## [Exercise 15.13](ex15.12.13.14/main.cpp) ## [Exercise 15.14](ex15.12.13.14/main.cpp) ## Exercise 15.15: > Define your own versions of `Disc_quote` and `Bulk_quote`. [Disc_quote](ex15.15.16.17/disc_quote.h) | [Bulk_quote](ex15.15.16.17/bulk_quote.h) ## Exercise 15.16: > Rewrite the class representing a limited discount strategy, which you wrote for the exercises in § 15.2.2 (p. 601), to inherit from Disc_quote. [Limit_quote](ex15.15.16.17/limit_quote.h) ## Exercise 15.17: > Try to define an object of type Disc_quote and see what errors you get from the compiler. `error: cannot declare variable 'd' to be of abstract type 'Disc_quote': Disc_quote d;` `note: because the following virtual functions are pure within 'Disc_quote': class Disc_quote : public Quote` `note: virtual double Disc_quote::net_price(std::size_t) const: virtual double net_price(std::size_t n) const override = 0;` ## Exercise 15.18: > Given the classes from page 612 and page 613, and assuming each object has the type specified in the comments, determine which of these assignments are legal. Explain why those that are illegal aren’t allowed: > Base \*p = &d1; // d1 has type Pub_Derv > p = &d2; // d2 has type Priv_Derv > p = &d3; // d3 has type Prot_Derv > p = &dd1; // dd1 has type Derived_from_Public > p = &dd2; // dd2 has type Derived_from_Private > p = &dd3; // dd3 has type Derived_from_Protected * Base \*p = &d1; **legal** * p = &d2; **illegal** * p = &d3; **illegal** * p = &dd1; **legal** * p = &dd2; **illegal** * p = &dd3; **illegal** User code may use the derived-to-base conversion only if D inherits publicly from B. User code may not use the conversion if D inherits from B using either protected or private. ## Exercise 15.19: > Assume that each of the classes from page 612 and page 613 has a member function of the form: > `void memfcn(Base &b) { b = *this; }` > For each class, determine whether this function would be legal. Member functions and friends of D can use the conversion to B regardless of how D inherits from B. The derived-to-base conversion to a direct base class is always accessible to members and friends of a derived class. Hence, the 3 below are all legal: * Pub_Derv * Priv_Derv * Prot_Derv Member functions and friends of classes derived from D may use the derived-to-base conversion if D inherits from B using either public or protected. Such code may not use the conversion if D inherits privately from B.Hence: * Derived_from_Public **legal** * Derived_from_Private **illegal** * Derived_from_Protected **legal** ## [Exercise 15.20](ex15.18.19.20/main.cpp) > Choose one of the following general abstractions containing a family of types (or choose one of your own). Organize the types into an inheritance hierarchy: > (a) Graphical file formats (such as gif, tiff, jpeg, bmp) > (b) Geometric primitives (such as box, circle, sphere, cone) > (c) C++ language types (such as class, function, member function) [Here is a example of 2D shape](ex15.21.22/main.cpp) ## [Exercise 15.21](ex15.21.22/main.cpp) ## Exercise 15.23: > Assuming class D1 on page 620 had intended to override its inherited `fcn` function, how would you fix that class? Assuming you fixed the class so that `fcn` matched the definition in Base, how would the calls in that section be resolved? remove the parameter int. [main](ex15.23.cpp) | [Disscussion on SO](http://stackoverflow.com/questions/21320779/trying-to-understand-dynamic-binding-and-virtual-functions) ## Exercise 15.24: > What kinds of classes need a virtual destructor? What operations must a virtual destructor perform? Generally, a base class should define a virtual destructor. The destructor needs to be virtual to allow objects in the inheritance hierarchy to be dynamically allocated and destroyed. ## Exercise 15.25: > Why did we define a default constructor for Disc_quote? What effect, if any, would removing that constructor have on the behavior of Bulk_quote? Without it, when building the statement below, the compiler would complain that: > note: 'Bulk_quote::Bulk_quote()' is implicitly deleted because the default definition would be ill-formed. The reason is that a constructor taking 4 parameters has been defined, which prevented the compiler generate synthesized version default constructor. As a result, the default constructor of any class derived from it has been defined as deleted. Thus the default constructor must be defined explicitly so that the derived classes can call it when executing its default constructor. ## Exercise 15.26: > Define the `Quote` and `Bulk_quote` copy-control members to do the same job as the synthesized versions. Give them and the other constructors print statements that identify which function is running. Write programs using these classes and predict what objects will be created and destroyed. >Compare your predictions with the output and continue experimenting until your predictions are reliably correct. [Quote](ex15.26/quote.h) | [Bulk_quote](ex15.26/bulk_quote.h) ## Exercise 15.27: > Redefine your `Bulk_quote` class to inherit its constructors. **rules:** 1. only inherit from the direct base class. 2. default, copy and move constructors can not inherit. 3. any data members of its own are default initialized. 4. the rest details are in the section section 15.7.4. [Bulk_quote](ex15.27/bulk_quote.h) ## [Exercise 15.28](ex15.28.29/main.cpp) ## Exercise 15.29: > Repeat your program, but this time store `shared_ptrs` to objects of type `Quote`. Explain any discrepancy in the sum generated by the this version and the previous program. If there is no discrepancy, explain why there isn’t one. Since the vector from the previous exercise holds objects, there's no polymorphism happened while calling the virtual function net_price. Essentially, the objects held in it are the Quote subjects of the Bulk_quote objects being pushed back, Thus, the virtual net_price functions called are Quote::net_price. As a result, no discount was applied. The outcome was 9090. The objects held for this exercise are smart pointers to the Quote objects.In this case, polymorphism happened as expected.The actual virtual functions being called are Bulk_quote::net_price that ensure discount is applied.Thus, the outcome is 6363. It can be found that 30% discount has been applied to the price calculation. ## Exercise 15.30: > Write your own version of the `Basket` class and use it to compute prices for the same transactions as you used in the previous exercises. [Basket h](ex15.30/basket.h) | [Basket cpp](ex15.30/basket.cpp) | [main](ex15.30/main.cpp) ## Exercise 15.31: > Given that s1, s2, s3, and s4 are all strings, determine what objects are created in the following expressions: > (a) `Query(s1) | Query(s2) & ~ Query(s3);` > (b) `Query(s1) | (Query(s2) & ~ Query(s3));` > (c) `(Query(s1) & (Query(s2)) | (Query(s3) & Query(s4)));` * (a) `OrQuery, AndQuery, NotQuery, WordQuery` * (b) the same as the previous one * (c) `OrQuery, AndQuery, WordQuery` ## Exercise 15.32: > What happens when an object of type Query is copied, moved, assigned, and destroyed? * **copy:** While being copied, the synthesized copy constructor is called. It copies the data member into the new object. Since in this case, the data member is a shared pointer, while copying, the corresponding shared pointer points to the same address and the use count from the both shared pointer becomes 2. * **move:** while being moved, the synthesized move constructor is called. It moves the data member into the new object. In this case, the shared pointer from the newly created object will point to the address to which the original shared pointer pointed. After the move operation, the use count of the shared pointer in the new object is 1, whereas the pointer from the original object becomes `nullptr`. * **copy assignment:** The synthesized copy assignment will be called. The outcome of this operation is identical with the copy operation. * **move assignment:** The synthesized move assignment will be called. The rest is the same as the move operation. * **destroy:** The synthesized destructor will be called. It will call the destructor of `shared_ptr` which decrements the use count. If the count becomes zero, the destructor from shared_ptr will delete the resources it point to. ## Exercise 15.33: > What about objects of type `Query_base`? Managed by the synthesized version. Since Query_base a abstract class, the object of this type is essentially a subobject of its derived class. ## Exercise 15.34: > For the expression built in Figure 15.3 (p. 638): > (a) List the constructors executed in processing that expression. > (b) List the calls to rep that are made from `cout << q`. > (c) List the calls to `eval` made from `q.eval()`. * **a:** Query q = Query("fiery") & Query("bird") | Query("wind"); 1. `Query::Query(const std::string& s)` where s == "fiery","bird" and "wind" 2. `WordQuery::WordQuery(const std::string& s)` where s == "fiery","bird" and "wind" 3. `AndQuery::AndQuery(const Query& left, const Query& right);` 4. `BinaryQuery(const Query&l, const Query& r, std::string s);` 5. `Query::Query(std::shared_ptr query)` 2times 6. `OrQuery::OrQuery(const Query& left, const Query& right);` 7. `BinaryQuery(const Query&l, const Query& r, std::string s);` 8. `Query::Query(std::shared_ptr query)` 2times * **b:** 1. `query.rep()` inside the operator <<(). 2. `q->rep()` inside the member function rep(). 3. `OrQuery::rep()` which is inherited from `BinaryQuery`. 4. `Query::rep()` for `lhs` and `rhs`: for `rhs` which is a `WordQuery` : `WordQuery::rep()` where `query_word("wind")` is returned.For `lhs` which is an `AndQuery`. 5. `AndQuery::rep()` which is inherited from `BinaryQuery`. 6. `BinaryQuer::rep()`: for `rhs: WordQuery::rep()` where query_word("fiery") is returned. For `lhs: WordQuery::rep()` where query_word("bird" ) is returned. * **c:** 1. `q.eval()` 2. `q->rep()`: where q is a pointer to `OrQuary`. 3. `QueryResult eval(const TextQuery& )const override`: is called but this one has not been defined yet. ## Exercise 15.35: > Implement the `Query` and `Query_base classes`, including a definition of rep but omitting the definition of `eval`. [Query](ex15.34.35.36.38/query.h) | [Query_base](ex15.34.35.36.38/query_base.h) ## Exercise 15.36: > Put print statements in the constructors and rep members and run your code to check your answers to (a) and (b) from the first exercise. ```cpp Query q = Query("fiery") & Query("bird") | Query("wind"); WordQuery::WordQuery(wind) Query::Query(const std::string& s) where s=wind WordQuery::WordQuery(bird) Query::Query(const std::string& s) where s=bird WordQuery::WordQuery(fiery) Query::Query(const std::string& s) where s=fiery BinaryQuery::BinaryQuery() where s=& AndQuery::AndQuery() Query::Query(std::shared_ptr query) BinaryQuery::BinaryQuery() where s=| OrQuery::OrQuery Query::Query(std::shared_ptr query) Press to close this window... ``` ```cpp std::cout << q < to close this window... ``` ## Exercise 15.37: ## Exercise 15.38: > Are the following declarations legal? If not, why not? If so, explain what the declarations mean. > BinaryQuery a = Query("fiery") & Query("bird"); >AndQuery b = Query("fiery") & Query("bird"); > OrQuery c = Query("fiery") & Query("bird"); 1. Illegal. Because `BinaryQuery` is an abstract class. 2. Illegal. Because operator & returns a `Query` which can not convert to an `AndQuery` object. 3. Illegal. Because operator & returns a `Query` which can not convert to an `OrQuery` object. ## Exercise 15.39: > Implement the `Query` and `Query_base` classes. Test your application by evaluating and printing a query such as the one in Figure 15.3 (p. 638). [Query](ex15.39.40/query.h) | [Query_base](ex15.34.35.36.38/query_base.h) | [main](ex15.39.40/main.cpp) ## Exercise 15.40: > In the `OrQuery` eval function what would happen if its `rhs` member returned an empty set? What if its `lhs` member did so? What if both `rhs` and `lhs` returned empty sets? Nothing special will happen. The codes as following: ```cpp std::shared_ptr> ret_lines = std::make_shared>(left.begin(), left.end()); ``` Since `std::make_shared` will allocate dynamically a new `std::set`, nothing will be added into this `std::set` if any set is empty.The codes in main function proves this. ## Exercise 15.41: ## Exercise 15.42: > Design and implement one of the following enhancements: > **(a)** Print words only once per sentence rather than once per line. > **(b)** Introduce a history system in which the user can refer to a previous query by number, possibly adding to it or combining it with another. > **(c)** Allow the user to limit the results so that only matches in a given range of lines are displayed. Here are solutions for [(b)](ex15.42_b/main.cpp) and [(c)](ex15.42_c/main.cpp). ================================================ FILE: ch15/ex15.1.2.3/CppPrimer.pro ================================================ TEMPLATE = app CONFIG += console CONFIG -= app_bundle CONFIG -= qt CONFIG += c++11 SOURCES += main.cpp \ quote.cpp HEADERS += \ quote.h ================================================ FILE: ch15/ex15.1.2.3/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 21 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.1: // What is a virtual member? // A virtual member in a base class expects its derived class define its own version. // In particular base classes ordinarily should define a virtual destructor, even if // it does no work. // // Exercise 15.2: // How does the protected access specifier differ from private? // private members : base class itself and friend can access // protected members : base class itself, friend and derived classes can access // Exercise 15.3: // Define your own versions of the Quote class and the print_total function. // // #include #include #include #include #include "quote.h" double print_total (std::ostream& os, const Quote& item, size_t n); int main() { return 0; } double print_total(std::ostream &os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN:" << item.isbn() << "# sold: " << n << " total due: " << ret << std::endl; return ret; } ================================================ FILE: ch15/ex15.1.2.3/quote.cpp ================================================ #include "quote.h" ================================================ FILE: ch15/ex15.1.2.3/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch15/ex15.11/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= min_qty ? 1 - discount : 1); } void Bulk_quote::debug() const { Quote::debug(); std::cout << "min_qty= " << this->min_qty << " " << "discount= " << this->discount<< " "; } ================================================ FILE: ch15/ex15.11/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "quote.h" class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Quote(b, p), min_qty(q), discount(disc) { } double net_price(std::size_t n) const override; void debug() const override; private: std::size_t min_qty = 0; double discount = 0.0; }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.11/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { Quote::debug(); std::cout << "max_qty= " << this->max_qty << " " << "discount= " << this->discount<< " "; } ================================================ FILE: ch15/ex15.11/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "quote.h" class Limit_quote : public Quote { public: Limit_quote(); Limit_quote(const std::string& b, double p, std::size_t max, double disc): Quote(b, p), max_qty(max), discount(disc) { } double net_price(std::size_t n) const override { return n * price * (n < max_qty ? 1 - discount : 1 ); } void debug() const override; private: std::size_t max_qty = 0; double discount = 0.0; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.11/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 22 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.11: // Add a virtual debug function to your Quote class hierarchy that displays // the data members of the respective classes. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" void print_debug(const Quote& q); double print_total (std::ostream& os, const Quote& item, size_t n); int main() { Quote q("aaa", 10.60); Bulk_quote bq("bbb", 111, 10, 0.3); Limit_quote lq("ccc", 222, 10, 0.3); /** @note Not dynamic binding! * The codes below are not dynamic binding. The compiler has known what the * r refering to at compile time. As a result, the virtual function debug of * the subobject is called. */ Quote& r = q; r.debug(); std::cout << "\n"; r = bq; r.debug(); std::cout << "\n"; r = lq; r.debug(); std::cout << "\n"; std::cout << "====================\n"; /** @note dynamic binding! * The below will happen dynamic binding. The reason might be that while print_debug * compiling the compiler compile it independly from the rest codes. The compiler has * no idea what the paramter q refering to. Thus compiler will leave the decision to * run time. That is, dynamic binding. * * Once dynamic binding happens, the corresponding vertual function in derived class will * be called rather than that of the subobject inside the derived object. * * Anyway, the reference is essentially an address being passed. * */ print_debug(q); std::cout << "\n"; print_debug(lq); std::cout << "\n"; print_debug(bq); std::cout << "\n"; return 0; } double print_total(std::ostream &os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN:" << item.isbn() << "# sold: " << n << " total due: " << ret << std::endl; return ret; } void print_debug(const Quote &q) { q.debug(); } ================================================ FILE: ch15/ex15.11/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout << "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " "; } ================================================ FILE: ch15/ex15.11/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch15/ex15.12.13.14/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= min_qty ? 1 - discount : 1); } void Bulk_quote::debug() const { std::cout << "data members of this class:\n" << "min_qty= " << this->min_qty << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.12.13.14/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include “quote.h” class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Quote(b, p), min_qty(q), discount(disc) { } double net_price(std::size_t n) const override; void debug() const override; private: std::size_t min_qty = 0; double discount = 0.0; }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.12.13.14/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { std::cout << "data members of this class:\n" << "max_qty= " << this->max_qty << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.12.13.14/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "quote.h" class Limit_quote : public Quote { public: Limit_quote(); Limit_quote(const std::string& b, double p, std::size_t max, double disc): Quote(b, p), max_qty(max), discount(disc) { } double net_price(std::size_t n) const override { return n * price * (n < max_qty ? 1 - discount : 1 ); } void debug() const override; private: std::size_t max_qty = 0; double discount = 0.0; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.12.13.14/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 22 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.12: // Is it ever useful to declare a member function as both override and final? // Why or why not? // Sure. override means overriding the same name virtual function in base class. // final means preventing any overriding this virtual function by any derived classes // that are more lower at the hierarchy . // // Exercise 15.13: // Given the following classes, explain each print function: // If there is a problem in this code, how would you fix it? // // Exercise 15.14: // Given the classes from the previous exercise and the following objects, // determine which function is called at run time: // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" class base { public: std::string name() { return basename; } virtual void print(std::ostream &os) { os << basename; } // ~~~~~^^^^^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // The print here just output the basename of the base. private: std::string basename = "base\n"; }; class derived : public base { public: void print(std::ostream &os) override { base::print(os); os << " derived\n " << i; } // ^^^^^ ^^^^^^^^ ^^^^^^ -- added to fix this problem // this print wanted to call the print from the base class. // however, the class scope base:: was omitted.As a result // it will cause an infinit recursion. // btw, we can add a keyword `override` to show this function // overrides a virtual function from the base class, although // it is not neccessary, but for security, the more, the better. private: int i; }; void print_debug(const Quote& q); double print_total (std::ostream& os, const Quote& item, size_t n); int main() { // ex15.14 base bobj; base *bp1 = &bobj; base &br1 = bobj; derived dobj; base *bp2 = &dobj; base &br2 = dobj; // a. this is an object, so compile time. //bobj.print(std::cout); // b. this is an object, so compile time. //dobj.print(std::cout); // c. function name is not virtual , so no dynamic // binding happens.so compile time //std::cout << bp1->name(); // d. function name is not virtual , so no dynamic // binding happens.so compile time //std::cout << bp2->name(); // e. run time //br1.print(std::cout); // f. run time br2.print(std::cout); return 0; } double print_total(std::ostream &os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN:" << item.isbn() << "# sold: " << n << " total due: " << ret << std::endl; return ret; } void print_debug(const Quote &q) { q.debug(); } ================================================ FILE: ch15/ex15.12.13.14/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout << "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " \n"; } ================================================ FILE: ch15/ex15.12.13.14/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch15/ex15.15.16.17/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= quantity ? 1 - discount : 1); } void Bulk_quote::debug() const { Quote::debug(); std::cout //<< "data members of this class:\n" << "min_qty= " << quantity << " " << "discount= " << discount<< " "; } ================================================ FILE: ch15/ex15.15.16.17/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "disc_quote.h" class Bulk_quote : public Disc_quote { public: Bulk_quote() = default; Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Disc_quote(b, p, q, disc) { } double net_price(std::size_t n) const override; void debug() const override; }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.15.16.17/disc_quote.cpp ================================================ #include "disc_quote.h" ================================================ FILE: ch15/ex15.15.16.17/disc_quote.h ================================================ #ifndef DISC_QUOTE_H #define DISC_QUOTE_H #include "quote.h" class Disc_quote : public Quote { public: Disc_quote(); Disc_quote(const std::string& b, double p, std::size_t q, double d) : Quote(b, p), quantity(q), discount(d) { } virtual double net_price(std::size_t n) const override = 0; protected: std::size_t quantity; double discount; }; #endif // DISC_QUOTE_H ================================================ FILE: ch15/ex15.15.16.17/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { Quote::debug(); std::cout //<< "data members of this class:\n" << "max_qty= " << quantity << " " << "discount= " << discount<< " "; } ================================================ FILE: ch15/ex15.15.16.17/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "disc_quote.h" class Limit_quote : public Disc_quote { public: Limit_quote() = default; Limit_quote(const std::string& b, double p, std::size_t max, double disc): Disc_quote(b, p, max, disc) { } double net_price(std::size_t n) const override { return n * price * (n < quantity ? 1 - discount : 1 ); } void debug() const override; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.15.16.17/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 23 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.15: // Define your own versions of Disc_quote and Bulk_quote. // // Exercise 15.16: // Rewrite the class representing a limited discount strategy, which you wrote // for the exercises in § 15.2.2 (p. 601), to inherit from Disc_quote. // // Exercise 15.17: // Try to define an object of type Disc_quote and see what errors you get from // the compiler. // error: cannot declare variable 'd' to be of abstract type 'Disc_quote' // Disc_quote d; // ^ // note: because the following virtual functions are pure within 'Disc_quote': // class Disc_quote : public Quote // ^ // note: virtual double Disc_quote::net_price(std::size_t) const // virtual double net_price(std::size_t n) const override = 0; // ^ #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" int main() { /* error C2259 : 'Disc_quote' : cannot instantiate abstract class 1> due to following members : 1> 'double Disc_quote::net_price(size_t) const' : is abstract */ Disc_quote d; return 0; } ================================================ FILE: ch15/ex15.15.16.17/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout //<< "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " "; } ================================================ FILE: ch15/ex15.15.16.17/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch15/ex15.18.19.20/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 23 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.18: // Given the classes from page 612 and page 613, and assuming each object // has the type specified in the comments, determine which of these assignments // are legal. Explain why those that are illegal aren’t allowed: // // Base *p = &d1; // d1 has type Pub_Derv -- legal --right // p = &d2; // d2 has type Priv_Derv -- illegal --right // p = &d3; // d3 has type Prot_Derv -- illegal --right // // p = &dd1; // dd1 has type Derived_from_Public -- legal --right // p = &dd2; // dd2 has type Derived_from_Private -- illegal --right // p = &dd3; // dd3 has type Derived_from_Protected -- illegal --right // // User code may use the derived-to-base conversion only if D inherits // publicly from B. User code may not use the conversion if D inherits // from B using either protected or private. // // Exercise 15.19: // Assume that each of the classes from page 612 and page 613 has a member // function of the form: // // void memfcn(Base &b) { b = *this; } // // For each class, determine whether this function would be legal. // // Member functions and friends of D can use the conversion to B regardless // of how D inherits from B. The derived-to-base conversion to a direct base // class is always accessible to members and friends of a derived class. // Hence, the 3 below are all legal: // // Pub_Derv -- legal --right // Priv_Derv -- legal --right // Prot_Derv -- legal --right // // Member functions and friends of classes derived from D may use the // derived-to-base conversion if D inherits from B using either public or // protected. Such code may not use the conversion if D inherits privately // from B.Hence: // Derived_from_Public -- legal --right // Derived_from_Private -- illegal --right // Derived_from_Protected -- legal --right // // Exercise 15.20: // Write code to test your answers to the previous two exercises. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" class Base { public: void pub_mem(); // public member protected: int prot_mem; // protected member private: char priv_mem; // private member }; struct Pub_Derv : public Base { void memfcn(Base &b) { b = *this; } }; struct Priv_Derv : private Base { void memfcn(Base &b) { b = *this; } }; struct Prot_Derv : protected Base { void memfcn(Base &b) { b = *this; } }; struct Derived_from_Public : public Pub_Derv { void memfcn(Base &b) { b = *this; } }; struct Derived_from_Private : public Priv_Derv { //void memfcn(Base &b) { b = *this; } }; struct Derived_from_Protected : public Prot_Derv { void memfcn(Base &b) { b = *this; } }; int main() { Pub_Derv d1; Base *p = &d1; Priv_Derv d2; //p = &d2; Prot_Derv d3; //p = &d3; Derived_from_Public dd1; p = &dd1; Derived_from_Private dd2; //p =& dd2; Derived_from_Protected dd3; //p = &dd3; return 0; } ================================================ FILE: ch15/ex15.21.22/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 23 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.21: // Choose one of the following general abstractions containing a family of // types (or choose one of your own). Organize the types into an inheritance // hierarchy: // (a) Graphical file formats (such as gif, tiff, jpeg, bmp) // (b) Geometric primitives (such as box, circle, sphere, cone) // (c) C++ language types (such as class, function, member function) // // Exercise 15.22: // For the class you chose in the previous exercise, identify some of the // likely virtual functions as well as public and protected members. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" // just for 2D shape class Shape { public: typedef std::pair Coordinate; Shape() = default; Shape(const std::string& n) : name(n) { } virtual double area() const = 0; virtual double perimeter() const = 0; virtual ~Shape() = default; private: std::string name; }; class Rectangle : public Shape { public: Rectangle() = default; Rectangle(const std::string& n, const Coordinate& a, const Coordinate& b, const Coordinate& c, const Coordinate& d) : Shape(n), a(a), b(b), c(c), d(d) { } ~Rectangle() = default; protected: Coordinate a; Coordinate b; Coordinate c; Coordinate d; }; class Square : public Rectangle { public: Square() = default; Square(const std::string& n, const Coordinate& a, const Coordinate& b, const Coordinate& c, const Coordinate& d) : Rectangle(n, a, b, c, d) { } ~Square() = default; }; int main() { return 0; } ================================================ FILE: ch15/ex15.23.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 24 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.23: // Assuming class D1 on page 620 had intended to override its inherited fcn // function, how would you fix that class? Assuming you fixed the class so // that fcn matched the definition in Base, how would the calls in that section // be resolved? // remove the parameter int. // Disscussion on SO : // http://stackoverflow.com/questions/21320779/trying-to-understand-dynamic-binding-and-virtual-functions #include #include class Base { public: virtual int fcn(){ std::cout << "Base::fcn()\n"; return 0; } }; class D1 : public Base { public: int fcn() override { std::cout << "D1::fcn()\n";return 0; } // ^^^--fixed to override the inherited version virtual void f2() { std::cout << "D1::f2()\n"; } }; class D2 : public D1 { public: int fcn(int); int fcn() override { std::cout << "D2::fcn()\n";return 0; } void f2() override { std::cout << "D2::f2()\n"; } }; int main() { Base bobj; D1 d1obj; D2 d2obj; Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj; //bp1->fcn(); // virtual call, will call Base::fcn at run time //bp2->fcn(); // virtual call, will call D1::fcn at run time //bp3->fcn(); // virtual call, will call D2::fcn at run time D1 *d1p = &d1obj; D2 *d2p = &d2obj; //bp2->f2(); //^^^^^^^^^^^^ // @note You are calling virtual member functions via a pointer // to Base. That means that you can only call methods that exist // in the Base class. You cannot simply add methods to a type // dynamically. //d1p->f2(); // virtual call, will call D1::f2() at run time //d2p->f2(); // virtual call, will call D2::f2() at run time return 0; } ================================================ FILE: ch15/ex15.24.25.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 24 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.24: // What kinds of classes need a virtual destructor? What operations must a // virtual destructor perform? // Generally, a base class should define a virtual destructor. // The destructor needs to be virtual to allow objects in the inheritance // hierarchy to be dynamically allocated and destroyed. // // Exercise 15.25: // Why did we define a default constructor for Disc_quote? What effect, if // any, would removing that constructor have on the behavior of Bulk_quote? // // Without it, when building the statement below, the compiler would complain // that: // // note: 'Bulk_quote::Bulk_quote()' is implicitly deleted because the default // definition would be ill-formed. // // The reason is that a constructor taking 4 parameters has been defined, which // prevented the compiler generate synthesized version default constructor. As a // result, the default constructor of any class derived from it has been defined // as deleted. Thus the default constructor must be defined explicitly so that // the derived classes can call it when executing its default constructor. // int main(){ return 0; } ================================================ FILE: ch15/ex15.26/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= quantity ? 1 - discount : 1); } void Bulk_quote::debug() const { std::cout //<< "data members of this class:\n" << "min_qty= " << quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.26/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "disc_quote.h" class Bulk_quote : public Disc_quote { public: Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; } Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; } // copy constructor Bulk_quote(const Bulk_quote& bq) : Disc_quote(bq) { std::cout << "Bulk_quote : copy constructor\n"; } // move constructor //page 535, " In a constructor, noexcept appears between the parameter list and the : that begins the constructor initializer list" Bulk_quote(Bulk_quote&& bq) noexcept : Disc_quote(std::move(bq)) { std::cout << "Bulk_quote : move constructor\n"; } // copy =() Bulk_quote& operator =(const Bulk_quote& rhs) { Disc_quote::operator =(rhs); std::cout << "Bulk_quote : copy =()\n"; return *this; } // move =() Bulk_quote& operator =(Bulk_quote&& rhs) noexcept { Disc_quote::operator =(std::move(rhs)); std::cout << "Bulk_quote : move =()\n"; return *this; } double net_price(std::size_t n) const override; void debug() const override; ~Bulk_quote() override { std::cout << "destructing Bulk_quote\n"; } }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.26/disc_quote.cpp ================================================ #include "disc_quote.h" ================================================ FILE: ch15/ex15.26/disc_quote.h ================================================ #ifndef DISC_QUOTE_H #define DISC_QUOTE_H #include "quote.h" class Disc_quote : public Quote { friend bool operator !=(const Disc_quote& lhs, const Disc_quote& rhs); public: Disc_quote() { std::cout << "default constructing Disc_quote\n"; } Disc_quote(const std::string& b, double p, std::size_t q, double d) : Quote(b, p), quantity(q), discount(d) { std::cout << "Disc_quote : constructor taking 4 parameters.\n"; } // copy constructor Disc_quote(const Disc_quote& dq) : Quote(dq), quantity(dq.quantity), discount(dq.discount) { std::cout << "Disc_quote : copy constructor.\n"; } // move constructor Disc_quote(Disc_quote&& dq) noexcept : Quote(std::move(dq)), quantity(std::move(dq.quantity)), discount(std::move(dq.discount)) { std::cout << "Disc_quote : move constructor.\n"; } // copy =() Disc_quote& operator =(const Disc_quote& rhs) { Quote::operator =(rhs); this->quantity = rhs.quantity; this->discount = rhs.discount; std::cout << "Disc_quote : copy =()\n"; return *this; } // move =() Disc_quote& operator =(Disc_quote&& rhs) noexcept { if (*this != rhs) { Quote::operator =(std::move(rhs)); this->quantity = std::move(rhs.quantity); this->discount = std::move(rhs.discount); } std::cout << "Disc_quote : move =()\n"; return *this; } virtual double net_price(std::size_t n) const override = 0; ~Disc_quote() { std::cout << "destructing Dis_quote\n"; } protected: std::size_t quantity = 3; double discount = 0.0; }; bool inline operator !=(const Disc_quote& lhs, const Disc_quote& rhs) { return Quote(lhs) != Quote(rhs) && lhs.quantity != rhs.quantity && lhs.discount != rhs.discount; } #endif // DISC_QUOTE_H ================================================ FILE: ch15/ex15.26/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { std::cout //<< "data members of this class:\n" << "max_qty= " << this->quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.26/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "disc_quote.h" class Limit_quote : public Disc_quote { public: Limit_quote() = default; Limit_quote(const std::string& b, double p, std::size_t max, double disc): Disc_quote(b, p, max, disc) { } double net_price(std::size_t n) const override { return n * price * (n < quantity ? 1 - discount : 1 ); } void debug() const override; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.26/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 27 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.26: // Define the Quote and Bulk_quote copy-control members to do the same job // as the synthesized versions. Give them and the other constructors print // statements that identify which function is running. Write programs using // these classes and predict what objects will be created and destroyed. // Compare your predictions with the output and continue experimenting // until your predictions are reliably correct. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" int main() { Bulk_quote bq1; Bulk_quote bq2("ss", 2.05, 12, 0.3); bq2 = std::move(bq2); return 0; } ================================================ FILE: ch15/ex15.26/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout //<< "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " \n"; } ================================================ FILE: ch15/ex15.26/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { friend bool operator !=(const Quote& lhs, const Quote& rhs); public: Quote() { std::cout << "default constructing Quote\n"; } Quote(const std::string &b, double p) : bookNo(b), price(p) { std::cout << "Quote : constructor taking 2 parameters\n"; } // copy constructor Quote(const Quote& q) : bookNo(q.bookNo), price(q.price) { std::cout << "Quote: copy constructing\n"; } // move constructor Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price)) { std::cout << "Quote: move constructing\n"; } // copy = Quote& operator =(const Quote& rhs) { if(*this != rhs) { bookNo = rhs.bookNo; price = rhs.price; } std::cout << "Quote: copy =() \n"; return *this; } // move = Quote& operator =(Quote&& rhs) noexcept { if(*this != rhs) { bookNo = std::move(rhs.bookNo); price = std::move(rhs.price); } std::cout << "Quote: move =!!!!!!!!! \n"; return *this; } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() { std::cout << "destructing Quote\n"; } private: std::string bookNo; protected: double price = 10.0; }; bool inline operator !=(const Quote& lhs, const Quote& rhs) { return lhs.bookNo != rhs.bookNo && lhs.price != rhs.price; } #endif // QUOTE_H ================================================ FILE: ch15/ex15.27/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= quantity ? 1 - discount : 1); } void Bulk_quote::debug() const { std::cout //<< "data members of this class:\n" << "min_qty= " << quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.27/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "disc_quote.h" class Bulk_quote : public Disc_quote { public: Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; } // changed the below to the inherited constructor for ex15.27. // rules: 1. only inherit from the direct base class. // 2. default, copy and move constructors can not inherit. // 3. any data members of its own are default initialized. // 4. the rest details are in the section section 15.7.4. /* Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; } */ using Disc_quote::Disc_quote; // copy constructor Bulk_quote(const Bulk_quote& bq) : Disc_quote(bq) { std::cout << "Bulk_quote : copy constructor\n"; } // move constructor Bulk_quote(Bulk_quote&& bq) : Disc_quote(std::move(bq)) { std::cout << "Bulk_quote : move constructor\n"; } // copy =() Bulk_quote& operator =(const Bulk_quote& rhs) { Disc_quote::operator =(rhs); std::cout << "Bulk_quote : copy =()\n"; return *this; } // move =() Bulk_quote& operator =(Bulk_quote&& rhs) { Disc_quote::operator =(std::move(rhs)); std::cout << "Bulk_quote : move =()\n"; return *this; } double net_price(std::size_t n) const override; void debug() const override; ~Bulk_quote() override { std::cout << "destructing Bulk_quote\n"; } }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.27/disc_quote.cpp ================================================ #include "disc_quote.h" ================================================ FILE: ch15/ex15.27/disc_quote.h ================================================ #ifndef DISC_QUOTE_H #define DISC_QUOTE_H #include "quote.h" class Disc_quote : public Quote { friend bool operator !=(const Disc_quote& lhs, const Disc_quote& rhs); public: Disc_quote() { std::cout << "default constructing Disc_quote\n"; } Disc_quote(const std::string& b, double p, std::size_t q, double d) : Quote(b, p), quantity(q), discount(d) { std::cout << "Disc_quote : constructor taking 4 parameters.\n"; } // copy constructor Disc_quote(const Disc_quote& dq) : Quote(dq), quantity(dq.quantity), discount(dq.discount) { std::cout << "Disc_quote : copy constructor.\n"; } // move constructor Disc_quote(Disc_quote&& dq) noexcept : Quote(std::move(dq)), quantity(std::move(dq.quantity)), discount(std::move(dq.discount)) { std::cout << "Disc_quote : move constructor.\n"; } // copy =() Disc_quote& operator =(const Disc_quote& rhs) { Quote::operator =(rhs); this->quantity = rhs.quantity; this->discount = rhs.discount; std::cout << "Disc_quote : copy =()\n"; return *this; } // move =() Disc_quote& operator =(Disc_quote&& rhs) noexcept { if (*this != rhs) { Quote::operator =(std::move(rhs)); this->quantity = std::move(rhs.quantity); this->discount = std::move(rhs.discount); } std::cout << "Disc_quote : move =()\n"; return *this; } virtual double net_price(std::size_t n) const override = 0; ~Disc_quote() { std::cout << "destructing Dis_quote\n"; } protected: std::size_t quantity = 3; double discount = 0.0; }; bool inline operator !=(const Disc_quote& lhs, const Disc_quote& rhs) { return Quote(lhs) != Quote(rhs) && lhs.quantity != rhs.quantity && lhs.discount != rhs.discount; } #endif // DISC_QUOTE_H ================================================ FILE: ch15/ex15.27/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { std::cout //<< "data members of this class:\n" << "max_qty= " << this->quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.27/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "disc_quote.h" class Limit_quote : public Disc_quote { public: Limit_quote() = default; Limit_quote(const std::string& b, double p, std::size_t max, double disc): Disc_quote(b, p, max, disc) { } double net_price(std::size_t n) const override { return n * price * (n < quantity ? 1 - discount : 1 ); } void debug() const override; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.27/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 28 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.27: // Redefine your Bulk_quote class to inherit its constructors. // rules: 1. only inherit from the direct base class. // 2. default, copy and move constructors can not inherit. // 3. any data members of its own are default initialized. // 4. the rest details are in the section section 15.7.4. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" int main() { Bulk_quote bq("sss", 20.0, 2, 0.3); return 0; } ================================================ FILE: ch15/ex15.27/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout //<< "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " \n"; } ================================================ FILE: ch15/ex15.27/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { friend bool operator !=(const Quote& lhs, const Quote& rhs); public: Quote() { std::cout << "default constructing Quote\n"; } Quote(const std::string &b, double p) : bookNo(b), price(p) { std::cout << "Quote : constructor taking 2 parameters\n"; } // copy constructor Quote(const Quote& q) : bookNo(q.bookNo), price(q.price) { std::cout << "Quote: copy constructing\n"; } // move constructor Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price)) { std::cout << "Quote: move constructing\n"; } // copy = Quote& operator =(const Quote& rhs) { if(*this != rhs) { bookNo = rhs.bookNo; price = rhs.price; } std::cout << "Quote: copy =() \n"; return *this; } // move = Quote& operator =(Quote&& rhs) noexcept { if(*this != rhs) { bookNo = std::move(rhs.bookNo); price = std::move(rhs.price); } std::cout << "Quote: move =!!!!!!!!! \n"; return *this; } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() { std::cout << "destructing Quote\n"; } private: std::string bookNo; protected: double price = 10.0; }; bool inline operator !=(const Quote& lhs, const Quote& rhs) { return lhs.bookNo != rhs.bookNo && lhs.price != rhs.price; } #endif // QUOTE_H ================================================ FILE: ch15/ex15.28.29/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= quantity ? 1 - discount : 1); } void Bulk_quote::debug() const { std::cout //<< "data members of this class:\n" << "min_qty= " << quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.28.29/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "disc_quote.h" class Bulk_quote : public Disc_quote { public: Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; } // changed the below to the inherited constructor for ex15.27. // rules: 1. only inherit from the direct base class. // 2. default, copy and move constructors can not inherit. // 3. any data members of its own are default initialized. // 4. the rest details are in the section section 15.7.4. /* Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; } */ using Disc_quote::Disc_quote; // copy constructor Bulk_quote(const Bulk_quote& bq) : Disc_quote(bq) { std::cout << "Bulk_quote : copy constructor\n"; } // move constructor Bulk_quote(Bulk_quote&& bq) : Disc_quote(std::move(bq)) { std::cout << "Bulk_quote : move constructor\n"; } // copy =() Bulk_quote& operator =(const Bulk_quote& rhs) { Disc_quote::operator =(rhs); std::cout << "Bulk_quote : copy =()\n"; return *this; } // move =() Bulk_quote& operator =(Bulk_quote&& rhs) { Disc_quote::operator =(std::move(rhs)); std::cout << "Bulk_quote : move =()\n"; return *this; } double net_price(std::size_t n) const override; void debug() const override; ~Bulk_quote() override { std::cout << "destructing Bulk_quote\n"; } }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.28.29/disc_quote.cpp ================================================ #include "disc_quote.h" ================================================ FILE: ch15/ex15.28.29/disc_quote.h ================================================ #ifndef DISC_QUOTE_H #define DISC_QUOTE_H #include "quote.h" class Disc_quote : public Quote { friend bool operator !=(const Disc_quote& lhs, const Disc_quote& rhs); public: Disc_quote() { std::cout << "default constructing Disc_quote\n"; } Disc_quote(const std::string& b, double p, std::size_t q, double d) : Quote(b, p), quantity(q), discount(d) { std::cout << "Disc_quote : constructor taking 4 parameters.\n"; } // copy constructor Disc_quote(const Disc_quote& dq) : Quote(dq), quantity(dq.quantity), discount(dq.discount) { std::cout << "Disc_quote : copy constructor.\n"; } // move constructor Disc_quote(Disc_quote&& dq) noexcept : Quote(std::move(dq)), quantity(std::move(dq.quantity)), discount(std::move(dq.discount)) { std::cout << "Disc_quote : move constructor.\n"; } // copy =() Disc_quote& operator =(const Disc_quote& rhs) { Quote::operator =(rhs); this->quantity = rhs.quantity; this->discount = rhs.discount; std::cout << "Disc_quote : copy =()\n"; return *this; } // move =() Disc_quote& operator =(Disc_quote&& rhs) noexcept { if (*this != rhs) { Quote::operator =(std::move(rhs)); this->quantity = std::move(rhs.quantity); this->discount = std::move(rhs.discount); } std::cout << "Disc_quote : move =()\n"; return *this; } virtual double net_price(std::size_t n) const override = 0; ~Disc_quote() { std::cout << "destructing Dis_quote\n"; } protected: std::size_t quantity = 3; double discount = 0.0; }; bool inline operator !=(const Disc_quote& lhs, const Disc_quote& rhs) { return Quote(lhs) != Quote(rhs) && lhs.quantity != rhs.quantity && lhs.discount != rhs.discount; } #endif // DISC_QUOTE_H ================================================ FILE: ch15/ex15.28.29/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { std::cout //<< "data members of this class:\n" << "max_qty= " << this->quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.28.29/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "disc_quote.h" class Limit_quote : public Disc_quote { public: Limit_quote() = default; Limit_quote(const std::string& b, double p, std::size_t max, double disc): Disc_quote(b, p, max, disc) { } double net_price(std::size_t n) const override { return n * price * (n < quantity ? 1 - discount : 1 ); } void debug() const override; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.28.29/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 28 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // Exercise 15.28: // Define a vector to hold Quote objects but put Bulk_quote objects into that // vector. Compute the total net_price of all the elements in the vector. // // Exercise 15.29: // Repeat your program, but this time store shared_ptrs to objects of type // Quote. Explain any discrepancy in the sum generated by the this version and // the previous program. // // Since the vector from the previous exercise holds objects, there's no polymorphism // happened while calling the virtual function net_price. Essentially, the objects // held in it are the Quote subjects of the Bulk_quote objects being pushed back, // Thus, the virtual net_price functions called are Quote::net_price. As a result, // no discount was applied. The outcome was 9090 // // The objects held for this exercise are smart pointers to the Quote objects.In this // case, polymorphism happened as expected.The actual virtual functions being called // are Bulk_quote::net_price that ensure discount is applied.Thus, the outcome is 6363. // It can be found that 30% discount has been applied to the price calculation. // // // If there is no discrepancy, explain why there isn’t one. // #include #include #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" int main() { /** * @brief ex15.28 outcome == 9090 */ std::vector v; for(unsigned i =1; i != 10; ++i) v.push_back(Bulk_quote("sss", i * 10.1, 10, 0.3)); double total = 0; for (const auto& b : v) { total += b.net_price(20); } std::cout << total << std::endl; std::cout << "======================\n\n"; /** * @brief ex15.29 outccome == 6363 */ std::vector> pv; for(unsigned i =1; i != 10; ++i) pv.push_back(std::make_shared(Bulk_quote("sss", i * 10.1, 10, 0.3))); double total_p = 0; for (auto p : pv) { total_p += p->net_price(20); } std::cout << total_p << std::endl; return 0; } ================================================ FILE: ch15/ex15.28.29/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout //<< "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " \n"; } ================================================ FILE: ch15/ex15.28.29/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { friend bool operator !=(const Quote& lhs, const Quote& rhs); public: Quote() { std::cout << "default constructing Quote\n"; } Quote(const std::string &b, double p) : bookNo(b), price(p) { std::cout << "Quote : constructor taking 2 parameters\n"; } // copy constructor Quote(const Quote& q) : bookNo(q.bookNo), price(q.price) { std::cout << "Quote: copy constructing\n"; } // move constructor Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price)) { std::cout << "Quote: move constructing\n"; } // copy = Quote& operator =(const Quote& rhs) { if(*this != rhs) { bookNo = rhs.bookNo; price = rhs.price; } std::cout << "Quote: copy =() \n"; return *this; } // move = Quote& operator =(Quote&& rhs) noexcept { if(*this != rhs) { bookNo = std::move(rhs.bookNo); price = std::move(rhs.price); } std::cout << "Quote: move =!!!!!!!!! \n"; return *this; } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() { std::cout << "destructing Quote\n"; } private: std::string bookNo; protected: double price = 10.0; }; bool inline operator !=(const Quote& lhs, const Quote& rhs) { return lhs.bookNo != rhs.bookNo && lhs.price != rhs.price; } #endif // QUOTE_H ================================================ FILE: ch15/ex15.30/basket.cpp ================================================ #include "basket.h" double Basket::total_receipt(std::ostream &os) const { double sum = 0.0; for(auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter)) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ // @note this increment moves iter to the first element with key // greater than *iter. { sum += print_total(os, **iter, items.count(*iter)); } // ^^^^^^^^^^^^^ using count to fetch // the number of the same book. os << "Total Sale: " << sum << std::endl; return sum; } ================================================ FILE: ch15/ex15.30/basket.h ================================================ #ifndef BASKET_H #define BASKET_H #include "quote.h" #include #include // a basket of objects from Quote hierachy, using smart pointers. class Basket { public: // copy verison void add_item(const Quote& sale) { items.insert(std::shared_ptr(sale.clone())); } // move version void add_item(Quote&& sale) { items.insert(std::shared_ptr(std::move(sale).clone())); } double total_receipt(std::ostream& os) const; private: // function to compare needed by the multiset member static bool compare(const std::shared_ptr& lhs, const std::shared_ptr& rhs) { return lhs->isbn() < rhs->isbn(); } // hold multiple quotes, ordered by the compare member std::multiset, decltype(compare)*> items{ compare }; }; #endif // BASKET_H ================================================ FILE: ch15/ex15.30/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= quantity ? 1 - discount : 1); } void Bulk_quote::debug() const { std::cout //<< "data members of this class:\n" << "min_qty= " << quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.30/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "disc_quote.h" class Bulk_quote : public Disc_quote { public: Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; } // changed the below to the inherited constructor for ex15.27. // rules: 1. only inherit from the direct base class. // 2. default, copy and move constructors can not inherit. // 3. any data members of its own are default initialized. // 4. the rest details are in the section section 15.7.4. /* Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; } */ using Disc_quote::Disc_quote; // copy constructor Bulk_quote(const Bulk_quote& bq) : Disc_quote(bq) { std::cout << "Bulk_quote : copy constructor\n"; } // move constructor Bulk_quote(Bulk_quote&& bq) : Disc_quote(std::move(bq)) { std::cout << "Bulk_quote : move constructor\n"; } // copy =() Bulk_quote& operator =(const Bulk_quote& rhs) { Disc_quote::operator =(rhs); std::cout << "Bulk_quote : copy =()\n"; return *this; } // move =() Bulk_quote& operator =(Bulk_quote&& rhs) { Disc_quote::operator =(std::move(rhs)); std::cout << "Bulk_quote : move =()\n"; return *this; } // clone self virtual Bulk_quote* clone() const & { return new Bulk_quote(*this); } virtual Bulk_quote* clone() && { return new Bulk_quote(std::move(*this)); } double net_price(std::size_t n) const override; void debug() const override; ~Bulk_quote() override { std::cout << "destructing Bulk_quote\n"; } }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.30/disc_quote.cpp ================================================ #include "disc_quote.h" ================================================ FILE: ch15/ex15.30/disc_quote.h ================================================ #ifndef DISC_QUOTE_H #define DISC_QUOTE_H #include "quote.h" class Disc_quote : public Quote { friend bool operator !=(const Disc_quote& lhs, const Disc_quote& rhs); public: Disc_quote() { std::cout << "default constructing Disc_quote\n"; } Disc_quote(const std::string& b, double p, std::size_t q, double d) : Quote(b, p), quantity(q), discount(d) { std::cout << "Disc_quote : constructor taking 4 parameters.\n"; } // copy constructor Disc_quote(const Disc_quote& dq) : Quote(dq), quantity(dq.quantity), discount(dq.discount) { std::cout << "Disc_quote : copy constructor.\n"; } // move constructor Disc_quote(Disc_quote&& dq) noexcept : Quote(std::move(dq)), quantity(std::move(dq.quantity)), discount(std::move(dq.discount)) { std::cout << "Disc_quote : move constructor.\n"; } // copy =() Disc_quote& operator =(const Disc_quote& rhs) { Quote::operator =(rhs); this->quantity = rhs.quantity; this->discount = rhs.discount; std::cout << "Disc_quote : copy =()\n"; return *this; } // move =() Disc_quote& operator =(Disc_quote&& rhs) noexcept { if (*this != rhs) { Quote::operator =(std::move(rhs)); this->quantity = std::move(rhs.quantity); this->discount = std::move(rhs.discount); } std::cout << "Disc_quote : move =()\n"; return *this; } virtual double net_price(std::size_t n) const override = 0; ~Disc_quote() { std::cout << "destructing Dis_quote\n"; } protected: std::size_t quantity = 3; double discount = 0.0; }; bool inline operator !=(const Disc_quote& lhs, const Disc_quote& rhs) { return Quote(lhs) != Quote(rhs) && lhs.quantity != rhs.quantity && lhs.discount != rhs.discount; } #endif // DISC_QUOTE_H ================================================ FILE: ch15/ex15.30/limit_quote.cpp ================================================ #include "limit_quote.h" void Limit_quote::debug() const { std::cout //<< "data members of this class:\n" << "max_qty= " << this->quantity << " " << "discount= " << this->discount<< " \n"; } ================================================ FILE: ch15/ex15.30/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "disc_quote.h" class Limit_quote : public Disc_quote { public: Limit_quote() = default; Limit_quote(const std::string& b, double p, std::size_t max, double disc): Disc_quote(b, p, max, disc) { } double net_price(std::size_t n) const override { return n * price * (n < quantity ? 1 - discount : 1 ); } void debug() const override; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.30/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 28 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.30: // Write your own version of the Basket class and use it to compute prices // for the same transactions as you used in the previous exercises. // #include #include #include #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" #include "disc_quote.h" #include "basket.h" int main() { Basket basket; for (unsigned i = 0; i != 10; ++i) basket.add_item(Bulk_quote("Bible", 20.6, 20, 0.3)); for (unsigned i = 0; i != 10; ++i) basket.add_item(Bulk_quote("C++Primer", 30.9, 5, 0.4)); for (unsigned i = 0; i != 10; ++i) basket.add_item(Quote("CLRS", 40.1)); std::ofstream log("log.txt", std::ios_base::app|std::ios_base::out); basket.total_receipt(log); return 0; } ================================================ FILE: ch15/ex15.30/quote.cpp ================================================ #include "quote.h" void Quote::debug() const { std::cout //<< "data members of this class:\n" << "bookNo= " <bookNo << " " << "price= " <price<< " \n"; } // non-member double print_total(std::ostream &os, const Quote &item, size_t n) { // depending on the type of the object bound to the item parameter // calls either Quote::net_price or Bulk_quote::net_price double ret = item.net_price(n); os << "ISBN: " << item.isbn() // calls Quote::isbn << " # sold: " << n << " total due: " << ret << std::endl; return ret; } ================================================ FILE: ch15/ex15.30/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { friend bool operator !=(const Quote& lhs, const Quote& rhs); public: Quote() { std::cout << "default constructing Quote\n"; } Quote(const std::string &b, double p) : bookNo(b), price(p) { std::cout << "Quote : constructor taking 2 parameters\n"; } // copy constructor Quote(const Quote& q) : bookNo(q.bookNo), price(q.price) { std::cout << "Quote: copy constructing\n"; } // move constructor Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price)) { std::cout << "Quote: move constructing\n"; } // copy = Quote& operator =(const Quote& rhs) { if(*this != rhs) { bookNo = rhs.bookNo; price = rhs.price; } std::cout << "Quote: copy =() \n"; return *this; } // move = Quote& operator =(Quote&& rhs) noexcept { if(*this != rhs) { bookNo = std::move(rhs.bookNo); price = std::move(rhs.price); } std::cout << "Quote: move =!!!!!!!!! \n"; return *this; } // clone self virtual Quote* clone() const & { return new Quote(*this); } virtual Quote* clone() && { return new Quote(std::move(*this)); } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual void debug() const; virtual ~Quote() { std::cout << "destructing Quote\n"; } private: std::string bookNo; protected: double price = 10.0; }; bool inline operator !=(const Quote& lhs, const Quote& rhs) { return lhs.bookNo != rhs.bookNo && lhs.price != rhs.price; } // non-member double print_total(std::ostream &os, const Quote &item, size_t n); #endif // QUOTE_H ================================================ FILE: ch15/ex15.31.32.33.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 28 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.31: // Given that s1, s2, s3, and s4 are all strings, determine what objects are created in the following expressions: // (a) Query(s1) | Query(s2) & ~ Query(s3); // OrQuery, AndQuery, NotQuery, WordQuery // (b) Query(s1) | (Query(s2) & ~ Query(s3)); // the same as the previous one // (c) (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4))); // OrQuery, AndQuery, WordQuery // Exercise 15.32: // What happens when an object of type Query is copied, moved, assigned, and destroyed? // copy: // While being copied, the synthesized copy constructor is called. It copies the data member // into the new object. Since in this case, the data member is a shared pointer, while // copying, the corresponding shared pointer points to the same address and // the use count from the both shared pointer becomes 2. // // move: // while being moved, the synthesized move constructor is called. It moves the data member // into the new object. In this case, the shared pointer from the newly created // object will point to the address to which the original shared pointer pointed . // After the move operation, the use count of the shared pointer in the new object is 1, // whereas the pointer from the original object becomes nullptr. // // copy assignment: // The synthesized copy assignment will be called. The outcome of this // operation is identical with the copy operation. // // move assignment: // The synthesized move assignment will be called. The rest is the same as // the move operation. // // destroy: // The synthesized destructor will be called. It will call the destructor of // shared_ptr which decrements the use count. If the count becomes zero, the destructor // from shared_ptr will delete the resources it point to. // // Exercise 15.33: // What about objects of type Query_base? // Managed by the synthesized version. Since Query_base a abstract class, the object of // this type is essentially a subobject of its derived class. // #include #include #include #include #include #include "queryresult.h" #include "textquery.h" #include "query_base.h" #include "query.h" // this class is a simulation for class Query used to answer ex15.32 class Foo { public: Foo(const std::vector& t) : text(new std::vector(t)) { } private: std::shared_ptr> text; }; int main() { std::vector v = { "alan","alan","alan","alan","alan" }; Foo foo(v); Foo bar = std::move(foo); return 0; } ================================================ FILE: ch15/ex15.34.35.36.38/StrBlob.h ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced. Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ #ifndef STRBLOB_H #define STRBLOB_H #include #include #include #include #include // forward declaration needed for friend declaration in StrBlob class StrBlobPtr; class StrBlob { friend class StrBlobPtr; public: typedef std::vector::size_type size_type; // constructors StrBlob() : data(std::make_shared>()) { } StrBlob(std::initializer_list il); // size operations size_type size() const { return data->size(); } bool empty() const { return data->empty(); } // add and remove elements void push_back(const std::string &t) { data->push_back(t); } void pop_back(); // element access std::string& front(); std::string& back(); // interface to StrBlobPtr StrBlobPtr begin(); // can't be defined until StrBlobPtr is StrBlobPtr end(); private: std::shared_ptr> data; // throws msg if data[i] isn't valid void check(size_type i, const std::string &msg) const; }; // constructor inline StrBlob::StrBlob(std::initializer_list il): data(std::make_shared>(il)) { } // StrBlobPtr throws an exception on attempts to access a nonexistent element class StrBlobPtr { friend bool eq(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr(): curr(0) { } StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { } // newly overloaded why? StrBlobPtr(const StrBlob &a, const size_t sz = 0) : wptr(a.data), curr(sz) { } std::string& deref() const; StrBlobPtr& incr(); // prefix version StrBlobPtr& decr(); // prefix version private: // check returns a shared_ptr to the vector if the check succeeds std::shared_ptr> check(std::size_t, const std::string&) const; // store a weak_ptr, which means the underlying vector might be destroyed std::weak_ptr> wptr; std::size_t curr; // current position within the array }; inline std::string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; // (*p) is the vector to which this object points } inline std::shared_ptr> StrBlobPtr::check(std::size_t i, const std::string &msg) const { auto ret = wptr.lock(); // is the vector still around? if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; // otherwise, return a shared_ptr to the vector } // prefix: return a reference to the incremented object inline StrBlobPtr& StrBlobPtr::incr() { // if curr already points past the end of the container, can't increment it check(curr, "increment past end of StrBlobPtr"); ++curr; // advance the current state return *this; } inline StrBlobPtr& StrBlobPtr::decr() { // if curr is zero, decrementing it will yield an invalid subscript --curr; // move the current state back one element} check(-1, "decrement past begin of StrBlobPtr"); return *this; } // begin and end members for StrBlob inline StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } inline StrBlobPtr StrBlob::end() { auto ret = StrBlobPtr(*this, data->size()); return ret; } // named equality operators for StrBlobPtr inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { auto l = lhs.wptr.lock(), r = rhs.wptr.lock(); // if the underlying vector is the same if (l == r) // then they're equal if they're both null or // if they point to the same element return (!r || lhs.curr == rhs.curr); else return false; // if they point to difference vectors, they're not equal } inline bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !eq(lhs, rhs); } #endif ================================================ FILE: ch15/ex15.34.35.36.38/andquery.cpp ================================================ #include "andquery.h" ================================================ FILE: ch15/ex15.34.35.36.38/andquery.h ================================================ #ifndef ANDQUERY_H #define ANDQUERY_H #include "binaryquery.h" class AndQuery : public BinaryQuery { friend Query operator&(const Query&, const Query&); AndQuery(const Query& left, const Query& right): BinaryQuery(left, right, "&") { std::cout << "AndQuery::AndQuery()\n"; } // @note: inherits rep and define eval QueryResult eval(const TextQuery &) const override { // this is just a placeholder rather than the real definition } }; inline Query operator& (const Query& lhs, const Query& rhs) { return std::shared_ptr(new AndQuery(lhs, rhs)); } #endif // ANDQUERY_H ================================================ FILE: ch15/ex15.34.35.36.38/binaryquery.cpp ================================================ #include "binaryquery.h" ================================================ FILE: ch15/ex15.34.35.36.38/binaryquery.h ================================================ #ifndef BINARYQUERY_H #define BINARYQUERY_H #include "query_base.h" #include "query.h" /** * @brief The BinaryQuery class *An abstract class holds data needed by the query types that operate on two operands */ class BinaryQuery : public Query_base { protected: BinaryQuery(const Query&l, const Query& r, std::string s): lhs(l), rhs(r), opSym(s) { std::cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n"; } // @note: abstract class: BinaryQuery doesn't define eval std::string rep() const override { std::cout << "BinaryQuery::rep()\n"; return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } Query lhs, rhs; std::string opSym; }; #endif // BINARYQUERY_H ================================================ FILE: ch15/ex15.34.35.36.38/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 31 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.34: For the expression built in Figure 15.3 (p. 638): // (a) List the constructors executed in processing that expression. // Query q = Query("fiery") & Query("bird") | Query("wind"); // 1: Query::Query(const std::string& s) where s == "fiery","bird" and "wind" // 2: WordQuery::WordQuery(const std::string& s) where s == "fiery","bird" and "wind" // 3: AndQuery::AndQuery(const Query& left, const Query& right); // 4: BinaryQuery(const Query&l, const Query& r, std::string s); // 5: Query::Query(std::shared_ptr query) 2times // 6: OrQuery::OrQuery(const Query& left, const Query& right); // 7: BinaryQuery(const Query&l, const Query& r, std::string s); // 8: Query::Query(std::shared_ptr query) 2times // // (b) List the calls to rep that are made from cout << q. // query.rep() inside the operator <<() // q->rep() insede the member function rep() // OrQuery::rep() which is inherited from BinaryQuery // BinaryQuery::rep() // Query::rep() for lhs and rhs // for rhs which is a WordQuery : WordQuery::rep() where query_word("wind") is returned. // for lhs which is an AndQuery: // AndQuery::rep() --which is inherited from BinaryQuery // BinaryQuer::rep() // for rhs: WordQuery::rep() where query_word("fiery") is returned // for lhs: WordQuery::rep() where query_word("bird" ) is returned // // (c) List the calls to eval made from q.eval(). // q.eval() // q->rep() where q is a pointer to OrQuary // QueryResult eval(const TextQuery& )const override -- is called but this one has not been defined yet. // // Exercise 15.35: // Implement the Query and Query_base classes, including a definition of // rep but omitting the definition of eval. // // Exercise 15.36: // Put print statements in the constructors and rep members and run your code // to check your answers to (a) and (b) from the first exercise. // // Query q = Query("fiery") & Query("bird") | Query("wind"); // // WordQuery::WordQuery(wind) // Query::Query(const std::string& s) where s=wind // WordQuery::WordQuery(bird) // Query::Query(const std::string& s) where s=bird // WordQuery::WordQuery(fiery) // Query::Query(const std::string& s) where s=fiery // BinaryQuery::BinaryQuery() where s=& // AndQuery::AndQuery() // Query::Query(std::shared_ptr query) // BinaryQuery::BinaryQuery() where s=| // OrQuery::OrQuery // Query::Query(std::shared_ptr query) // Press to close this window... // // // // std::cout << q < to close this window... // // Exercise 15.37: // What changes would your classes need if the derived classes had // members of type shared_ptr rather than of type Query? // not done // // Exercise 15.38: // Are the following declarations legal? // If not, why not? If so, explain what the declarations mean. // BinaryQuery a = Query("fiery") & Query("bird"); // Illegal. Because BinaryQuery is an abstract class. // AndQuery b = Query("fiery") & Query("bird"); // Illegal. Because operator & returns a Query which can not convert to an AndQuery object. // OrQuery c = Query("fiery") & Query("bird"); // Illegal. Because operator & returns a Query which can not convert to an OrQuery object. // #include #include #include #include #include #include "queryresult.h" #include "textquery.h" #include "query_base.h" #include "query.h" #include "andquery.h" #include "orquery.h" int main() { return 0; } ================================================ FILE: ch15/ex15.34.35.36.38/notquery.cpp ================================================ #include "notquery.h" ================================================ FILE: ch15/ex15.34.35.36.38/notquery.h ================================================ #ifndef NOTQUERY_H #define NOTQUERY_H #include "query_base.h" #include "query.h" /** * @brief The NotQuery class * *The ~ operator generates a NotQuery, which holds a Query, *which it negates. */ class NotQuery : public Query_base { friend Query operator~(const Query& operand); NotQuery(const Query& q): query(q) { std::cout << "NotQuery::NotQuery()\n"; } // virtuals: std::string rep() const override { std::cout << "NotQuery::rep()\n"; return "~(" + query.rep() + ")"; } QueryResult eval(const TextQuery &) const override; Query query; }; inline Query operator~(const Query& operand) { return std::shared_ptr(new NotQuery(operand)); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // note : There is an imlplicit conversion here. // The Query constructor that takes shared_ptr is not // "explicit", thus the compiler allows this conversion. } #endif // NOTQUERY_H ================================================ FILE: ch15/ex15.34.35.36.38/orquery.cpp ================================================ #include "orquery.h" ================================================ FILE: ch15/ex15.34.35.36.38/orquery.h ================================================ #ifndef ORQUERY_H #define ORQUERY_H #include "binaryquery.h" class OrQuery :public BinaryQuery { friend Query operator|(const Query&, const Query&); OrQuery(const Query& left, const Query& right): BinaryQuery(left, right, "|") { std::cout << "OrQuery::OrQuery\n"; } QueryResult eval(const TextQuery& )const override { //place holder } }; inline Query operator|(const Query &lhs, const Query& rhs) { return std::shared_ptr(new OrQuery(lhs, rhs)); } #endif // ORQUERY_H ================================================ FILE: ch15/ex15.34.35.36.38/query.cpp ================================================ #include "query.h" ================================================ FILE: ch15/ex15.34.35.36.38/query.h ================================================ #ifndef QUERY_H #define QUERY_H #include #include #include #include "query_base.h" #include "queryresult.h" #include "textquery.h" #include "wordquery.h" /** * @brief interface class to manage the Query_base inheritance hierachy */ class Query { friend Query operator~(const Query&); friend Query operator|(const Query&, const Query&); friend Query operator&(const Query&, const Query&); public: // build a new WordQuery Query(const std::string& s) : q(new WordQuery(s)) { std::cout << "Query::Query(const std::string& s) where s="+s+"\n"; } // interface functions: call the corresponding Query_base operatopns QueryResult eval(const TextQuery& t) const { return q->eval(t); } std::string rep() const { std::cout << "Query::rep() \n"; return q->rep(); } private: // constructor only for friends Query(std::shared_ptr query) : q(query) { std::cout << "Query::Query(std::shared_ptr query)\n"; } std::shared_ptr q; }; inline std::ostream& operator << (std::ostream& os, const Query& query) { // make a virtual call through its Query_base pointer to rep(); return os << query.rep(); } #endif // QUERY_H ================================================ FILE: ch15/ex15.34.35.36.38/query_base.cpp ================================================ #include "query_base.h" ================================================ FILE: ch15/ex15.34.35.36.38/query_base.h ================================================ #ifndef QUERY_BASE_H #define QUERY_BASE_H #include "textquery.h" #include "queryresult.h" /** * @brief abstract class acts as a base class for all concrete query types * all members are private. */ class Query_base { friend class Query; protected: using line_no = TextQuery::line_no; // used in the eval function virtual ~Query_base() = default; private: // returns QueryResult that matches this query virtual QueryResult eval(const TextQuery&) const = 0; // a string representation of this query virtual std::string rep() const = 0; }; #endif // QUERY_BASE_H ================================================ FILE: ch15/ex15.34.35.36.38/queryresult.cpp ================================================ /*************************************************************************** * @file queryresult.cpp * @author Alan.W * @date 30 DEC 2013 * @remark using class StrBlob ***************************************************************************/ #include "queryresult.h" /** * @brief print the result to the output stream specified. * @note class QueryResult's friend */ std::ostream &print(std::ostream &os, const QueryResult &qr) { os << qr.sought << " occurs " << qr.sp_lines->size() << " " << "times" << "\n"; // print each line in which the word appears for ( auto &index : *qr.sp_lines) { os << "\t(line " << index + 1 << ") "; const StrBlobPtr wp(qr.file, index); os << wp.deref() << "\n"; } return os; } ================================================ FILE: ch15/ex15.34.35.36.38/queryresult.h ================================================ /*************************************************************************** * @file queryresult.h * @author Alan.W * @date 30 DEC 2013 * @remark using class StrBlob ***************************************************************************/ // // Exercise 12.33: // In Chapter 15 we’ll extend our query system and will need some additional // members in the QueryResult class. // // Add members named begin and end that // return iterators into the set of line numbers returned by a given query, // and a member named get_file that returns a shared_ptr to the file in the // QueryResult object. // #ifndef QUERYRESULT_H #define QUERYRESULT_H #include #include #include #include #include #include "textquery.h" /** * @brief Query Result */ class QueryResult { friend std::ostream& print(std::ostream&, const QueryResult&); public: // constructor QueryResult(std::string s, std::shared_ptr> sp_l, StrBlob f) : sought(s), sp_lines(sp_l), file(f) { } // added for ex12.33 // ? Think about wether the "const"s here are expected. const StrBlob& get_file() const{ return file; } std::set::iterator begin() { return sp_lines->begin(); } std::set::iterator end() { return sp_lines->end(); } private: // three data members std::string sought; std::shared_ptr> sp_lines; StrBlob file; }; /** * @brief print the result to the output stream specified. */ std::ostream& print(std::ostream&, const QueryResult &); #endif // QUERYRESULT_H ================================================ FILE: ch15/ex15.34.35.36.38/textquery.cpp ================================================ /*************************************************************************** * @file textquery.cpp * @author Alan.W * @date 30 DEC 2013 * @remark The TextQuery class using StrBlob ***************************************************************************/ // // Exercise 12.32: // Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a // vector to hold the input file. // Relevant post on Stack Overflow: // http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m // #include "textquery.h" #include "queryresult.h" #include #include #include /** * @brief constructor using StrBlob. */ TextQuery::TextQuery(std::ifstream &fin) : file(StrBlob()), wordMap(std::map>>()) { std::string line; // each line while(std::getline(fin, line)) { file.push_back(line); int n = file.size() - 1; // the current line number // each word std::stringstream lineSteam(line); std::string word; while(lineSteam >> word) { std::shared_ptr>& sp_lines = wordMap[word]; // if null if(!sp_lines) { sp_lines.reset(new std::set); } sp_lines->insert(n); } } } /** * @brief do a query opertion and return QueryResult object. */ QueryResult TextQuery::query(const std::string &sought) const { // dynamicaly allocated set used for the word does not appear. static std::shared_ptr> noData(new std::set); // fetch the iterator to the matching element in the map. //std::map>>::const_iterator auto iter = wordMap.find(sought); if(iter == wordMap.end()) return QueryResult(sought, noData, file); else return QueryResult(sought, iter->second, file); } ================================================ FILE: ch15/ex15.34.35.36.38/textquery.h ================================================ /*************************************************************************** * @file textquery.h * @author Alan.W * @date 30 DEC 2013 * @remark The TextQuery class using StrBlob ***************************************************************************/ // // Exercise 12.32: // Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a // vector to hold the input file. // Relevant post on Stack Overflow: // http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m // #ifndef TEXTQUERY_H #define TEXTQUERY_H #include "StrBlob.h" #include #include #include #include #include class QueryResult; /** * @brief The TextQuery class using StrBlob */ class TextQuery { public: typedef StrBlob::size_type line_no; // constructor TextQuery(std::ifstream& fin); // query operation QueryResult query(const std::string&) const; private: // data members StrBlob file; std::map>> wordMap; }; #endif // TEXTQUERY_H ================================================ FILE: ch15/ex15.34.35.36.38/wordquery.cpp ================================================ #include "wordquery.h" ================================================ FILE: ch15/ex15.34.35.36.38/wordquery.h ================================================ #ifndef WORDQUERY_H #define WORDQUERY_H #include "query_base.h" /** * @brief The WordQuery class *The only class that actually performs a query on the given TextQuery object. *No public members defined in this class. All operation are through the friend *class Query. */ class WordQuery : public Query_base { // class Query uses the WordQuery constructor friend class Query; WordQuery(const std::string& s): query_word(s) { std::cout << "WordQuery::WordQuery(" + s + ")\n"; } // virtuals: QueryResult eval(const TextQuery& t) const override { return t.query(query_word); } std::string rep() const override { std::cout << "WodQuery::rep()\n"; return query_word; } std::string query_word; }; #endif // WORDQUERY_H ================================================ FILE: ch15/ex15.39.40/StrBlob.h ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced. Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ #ifndef STRBLOB_H #define STRBLOB_H #include #include #include #include #include // forward declaration needed for friend declaration in StrBlob class StrBlobPtr; class StrBlob { friend class StrBlobPtr; public: typedef std::vector::size_type size_type; // constructors StrBlob() : data(std::make_shared>()) { } StrBlob(std::initializer_list il); // size operations size_type size() const { return data->size(); } bool empty() const { return data->empty(); } // add and remove elements void push_back(const std::string &t) { data->push_back(t); } void pop_back(); // element access std::string& front(); std::string& back(); // interface to StrBlobPtr StrBlobPtr begin(); // can't be defined until StrBlobPtr is StrBlobPtr end(); private: std::shared_ptr> data; // throws msg if data[i] isn't valid void check(size_type i, const std::string &msg) const; }; // constructor inline StrBlob::StrBlob(std::initializer_list il): data(std::make_shared>(il)) { } // StrBlobPtr throws an exception on attempts to access a nonexistent element class StrBlobPtr { friend bool eq(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr(): curr(0) { } StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { } // newly overloaded why? StrBlobPtr(const StrBlob &a, const size_t sz = 0) : wptr(a.data), curr(sz) { } std::string& deref() const; StrBlobPtr& incr(); // prefix version StrBlobPtr& decr(); // prefix version private: // check returns a shared_ptr to the vector if the check succeeds std::shared_ptr> check(std::size_t, const std::string&) const; // store a weak_ptr, which means the underlying vector might be destroyed std::weak_ptr> wptr; std::size_t curr; // current position within the array }; inline std::string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; // (*p) is the vector to which this object points } inline std::shared_ptr> StrBlobPtr::check(std::size_t i, const std::string &msg) const { auto ret = wptr.lock(); // is the vector still around? if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; // otherwise, return a shared_ptr to the vector } // prefix: return a reference to the incremented object inline StrBlobPtr& StrBlobPtr::incr() { // if curr already points past the end of the container, can't increment it check(curr, "increment past end of StrBlobPtr"); ++curr; // advance the current state return *this; } inline StrBlobPtr& StrBlobPtr::decr() { // if curr is zero, decrementing it will yield an invalid subscript --curr; // move the current state back one element} check(-1, "decrement past begin of StrBlobPtr"); return *this; } // begin and end members for StrBlob inline StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } inline StrBlobPtr StrBlob::end() { auto ret = StrBlobPtr(*this, data->size()); return ret; } // named equality operators for StrBlobPtr inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { auto l = lhs.wptr.lock(), r = rhs.wptr.lock(); // if the underlying vector is the same if (l == r) // then they're equal if they're both null or // if they point to the same element return (!r || lhs.curr == rhs.curr); else return false; // if they point to difference vectors, they're not equal } inline bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !eq(lhs, rhs); } #endif ================================================ FILE: ch15/ex15.39.40/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 01 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.39: // Implement the Query and Query_base classes. Test your application by // evaluating and printing a query such as the one in Figure 15.3 (p. 638). // // Exercise 15.40: // In the OrQuery eval function what would happen if its rhs member returned // an empty set? What if its lhs member did so? What if both rhs and lhs // returned empty sets? // Nothing special will happen. The codes as following: // std::shared_ptr> ret_lines = // std::make_shared>(left.begin(), left.end()); // ^^^^^^^^^^^^^^^^ // Since std::make_shared will allocate dynamically a new std::set, nothing will // be added into this std::set if any set is empty.The codes in main function // proves this. // #include #include #include #include #include #include "textquery.h" #include "query.h" int main() { std::ifstream file("test.txt"); TextQuery tQuery(file); Query q = Query("fieryzzz") | Query("wind"); std::cout << q.eval(tQuery); return 0; } ================================================ FILE: ch15/ex15.39.40/query.cpp ================================================ #include #include "query.h" /** * @brief AndQuery::eval * @return the intersection of its operands' result sets */ QueryResult AndQuery::eval(const TextQuery &text) const { // virtual calls through the Query operands to get result sets for the operands QueryResult left = lhs.eval(text), right = rhs.eval(text); // set to hold the intersection of the left and right std::shared_ptr> ret_lines = std::make_shared>(); // writes the intersection of two ranges to a destination iterator std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), std::inserter(*ret_lines, ret_lines->begin())); return QueryResult(rep(), ret_lines, left.get_file()); } /** * @brief OrQuery::eval * @return the union of its operands' result sets */ QueryResult OrQuery::eval(const TextQuery &text) const { QueryResult right = rhs.eval(text), left= lhs.eval(text); // copy the left-hand operand into the result set std::shared_ptr> ret_lines = std::make_shared>(left.begin(), left.end()); // inert lines from the right-hand operand ret_lines->insert(right.begin(), right.end()); return QueryResult(rep(), ret_lines, left.get_file()); } /** * @brief NotQuery::eval * @return the lines not in its operand's result set */ QueryResult NotQuery::eval(const TextQuery &text) const { // virtual call to eval through the Query operand QueryResult result = query.eval(text); // start out with an empty result set std::shared_ptr> ret_lines = std::make_shared>(); std::set::iterator begin = result.begin(), end = result.end(); StrBlob::size_type sz = result.get_file().size(); for(std::size_t n = 0; n != sz; ++n) { if(begin == end || *begin != n) ret_lines->insert(n); else if (begin != end) ++begin; } return QueryResult(rep(), ret_lines, result.get_file()); } ================================================ FILE: ch15/ex15.39.40/query.h ================================================ #ifndef QUERY_H #define QUERY_H #include #include #include #include "textquery.h" /** * @brief abstract class acts as a base class for all concrete query types * all members are private. */ class Query_base { friend class Query; protected: using line_no = TextQuery::line_no; // used in the eval function virtual ~Query_base() = default; private: // returns QueryResult that matches this query virtual QueryResult eval(const TextQuery&) const = 0; // a string representation of this query virtual std::string rep() const = 0; }; /** * @brief The WordQuery class *The only class that actually performs a query on the given TextQuery object. *No public members defined in this class. All operation are through the friend *class Query. */ class WordQuery : public Query_base { // class Query uses the WordQuery constructor friend class Query; WordQuery(const std::string& s): query_word(s) { std::cout << "WordQuery::WordQuery(" + s + ")\n"; } // virtuals: QueryResult eval(const TextQuery& t) const override { return t.query(query_word); } std::string rep() const override { std::cout << "WodQuery::rep()\n"; return query_word; } std::string query_word; }; /** * @brief interface class to manage the Query_base inheritance hierachy */ class Query { friend Query operator~(const Query&); friend Query operator|(const Query&, const Query&); friend Query operator&(const Query&, const Query&); public: // build a new WordQuery Query(const std::string& s) : q(new WordQuery(s)) { std::cout << "Query::Query(const std::string& s) where s="+s+"\n"; } // interface functions: call the corresponding Query_base operatopns QueryResult eval(const TextQuery& t) const { return q->eval(t); } std::string rep() const { std::cout << "Query::rep() \n"; return q->rep(); } private: // constructor only for friends Query(std::shared_ptr query) : q(query) { std::cout << "Query::Query(std::shared_ptr query)\n"; } std::shared_ptr q; }; inline std::ostream& operator << (std::ostream& os, const Query& query) { // make a virtual call through its Query_base pointer to rep(); return os << query.rep(); } /** * @brief The BinaryQuery class *An abstract class holds data needed by the query types that operate on two operands */ class BinaryQuery : public Query_base { protected: BinaryQuery(const Query&l, const Query& r, std::string s): lhs(l), rhs(r), opSym(s) { std::cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n"; } // @note: abstract class: BinaryQuery doesn't define eval std::string rep() const override { std::cout << "BinaryQuery::rep()\n"; return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } Query lhs, rhs; std::string opSym; }; /** * @brief The OrQuery class * *The & operator generates a OrQuery, which held by a Query, */ class OrQuery :public BinaryQuery { friend Query operator|(const Query&, const Query&); OrQuery(const Query& left, const Query& right): BinaryQuery(left, right, "|") { std::cout << "OrQuery::OrQuery\n"; } QueryResult eval(const TextQuery& )const override; }; inline Query operator|(const Query &lhs, const Query& rhs) { return std::shared_ptr(new OrQuery(lhs, rhs)); } /** * @brief The AndQuery class * *The & operator generates a AndQuery, which held by a Query, */ class AndQuery : public BinaryQuery { friend Query operator&(const Query&, const Query&); AndQuery(const Query& left, const Query& right): BinaryQuery(left, right, "&") { std::cout << "AndQuery::AndQuery()\n"; } // @note: inherits rep and define eval QueryResult eval(const TextQuery &) const override; }; inline Query operator& (const Query& lhs, const Query& rhs) { return std::shared_ptr(new AndQuery(lhs, rhs)); } /** * @brief The NotQuery class * *The ~ operator generates a NotQuery, which held by a Query, *which it negates. */ class NotQuery : public Query_base { friend Query operator~(const Query& operand); NotQuery(const Query& q): query(q) { std::cout << "NotQuery::NotQuery()\n"; } // virtuals: std::string rep() const override { std::cout << "NotQuery::rep()\n"; return "~(" + query.rep() + ")"; } QueryResult eval(const TextQuery &) const override; Query query; }; inline Query operator~(const Query& operand) { return std::shared_ptr(new NotQuery(operand)); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // note : There is an imlplicit conversion here. // The Query constructor that takes shared_ptr is not // "explicit", thus the compiler allows this conversion. } #endif // QUERY_H ================================================ FILE: ch15/ex15.39.40/test.txt ================================================ Alice Emma has long flowing red hair. Her daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. "Daddy, shush, there is no such thing," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there?" ================================================ FILE: ch15/ex15.39.40/textquery.cpp ================================================ /*************************************************************************** * @file textquery.cpp * @author Alan.W * @date 30 DEC 2013 * @remark The TextQuery class using StrBlob ***************************************************************************/ // // Exercise 12.32: // Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a // vector to hold the input file. // Relevant post on Stack Overflow: // http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m // #include "textquery.h" #include #include #include /** * @brief constructor using StrBlob. */ TextQuery::TextQuery(std::ifstream &fin) : file(StrBlob()), wordMap(std::map>>()) { std::string line; // each line while(std::getline(fin, line)) { file.push_back(line); int n = file.size() - 1; // the current line number // each word std::stringstream lineSteam(line); std::string word; while(lineSteam >> word) { std::shared_ptr>& sp_lines = wordMap[word]; // if null if(!sp_lines) { sp_lines.reset(new std::set); } sp_lines->insert(n); } } } /** * @brief do a query opertion and return QueryResult object. */ QueryResult TextQuery::query(const std::string &sought) const { // dynamicaly allocated set used for the word does not appear. static std::shared_ptr> noData(new std::set); // fetch the iterator to the matching element in the map. //std::map>>::const_iterator auto iter = wordMap.find(sought); if(iter == wordMap.end()) return QueryResult(sought, noData, file); else return QueryResult(sought, iter->second, file); } /** * @brief print the result to the output stream specified. * @note class QueryResult's friend */ std::ostream& operator <<(std::ostream &os, const QueryResult &qr) { os << qr.sought << " occurs " << qr.sp_lines->size() << " " << "times" << "\n"; // print each line in which the word appears for ( auto &index : *qr.sp_lines) { os << "\t(line " << index + 1 << ") "; const StrBlobPtr wp(qr.file, index); os << wp.deref() << "\n"; } return os; } ================================================ FILE: ch15/ex15.39.40/textquery.h ================================================ /*************************************************************************** * @file textquery.h * @author Alan.W * @date 30 DEC 2013 * @remark The TextQuery class using StrBlob ***************************************************************************/ // // Exercise 12.32: // Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a // vector to hold the input file. // Relevant post on Stack Overflow: // http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m // #ifndef TEXTQUERY_H #define TEXTQUERY_H #include "StrBlob.h" #include #include #include #include #include class QueryResult; /** * @brief The TextQuery class using StrBlob */ class TextQuery { public: typedef StrBlob::size_type line_no; // constructor TextQuery(std::ifstream& fin); // query operation QueryResult query(const std::string&) const; private: // data members StrBlob file; std::map>> wordMap; }; /** * @brief Query Result */ class QueryResult { friend std::ostream& operator<<(std::ostream&, const QueryResult&); public: // constructor QueryResult(std::string s, std::shared_ptr> sp_l, StrBlob f) : sought(s), sp_lines(sp_l), file(f) { } // added for ex12.33 // ? Think about wether the "const"s here are expected. const StrBlob& get_file() const{ return file; } std::set::iterator begin() { return sp_lines->begin(); } std::set::iterator end() { return sp_lines->end(); } private: // three data members std::string sought; std::shared_ptr> sp_lines; StrBlob file; }; /** * @brief print the result to the output stream specified. */ std::ostream& print(std::ostream&, const QueryResult &); #endif // TEXTQUERY_H ================================================ FILE: ch15/ex15.4.5.6/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= min_qty ? 1 - discount : 1); } ================================================ FILE: ch15/ex15.4.5.6/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Quote(b, p), min_qty(q), discount(disc) { } double net_price(std::size_t n) const override; private: std::size_t min_qty = 0; double discount = 0.0; }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.4.5.6/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 21 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.4: // Which of the following declarations, if any, are incorrect? Explain why. // class Base { ... }; // (a) class Derived : public Derived { ... }; //incorrect, deirve from itself // (b) class Derived : private Base { ... }; //incorrect, this is a definition not a declaration // (c) class Derived : public Base; //incorrect, A derived class is declared like any other class. The declaration //contains the class name but does not include its derivation list. //@ reported by lafener, check #154 for detail. // Exercise 15.5: // Define your own version of the Bulk_quote class. // // Exercise 15.6: // Test your print_total function from the exercises in § 15.2.1 (p. 595) // by passing both Quote and Bulk_quote objects o that function. // #include #include #include "quote.h" #include "bulk_quote.h" double print_total (std::ostream& os, const Quote& item, size_t n); int main() { // ex15.6 Quote q("textbook", 10.60); Bulk_quote bq("textbook", 10.60, 10, 0.3); print_total(std::cout, q, 12); print_total(std::cout, bq, 12); return 0; } double print_total(std::ostream &os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN:" << item.isbn() << "# sold: " << n << " total due: " << ret << std::endl; return ret; } ================================================ FILE: ch15/ex15.4.5.6/quote.cpp ================================================ #include "quote.h" ================================================ FILE: ch15/ex15.4.5.6/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch15/ex15.42_b/andquery.cpp ================================================ #include "andquery.h" #include using std::set_intersection; #include using std::inserter; #include using std::make_shared; #include using std::set; #include "queryresult.h" #include "textquery.h" #include "query.h" QueryResult AndQuery::eval(const TextQuery& text) const { auto left = lhs.eval(text); auto right = rhs.eval(text); auto ret_lines = make_shared>(); set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(*ret_lines, ret_lines->begin())); return QueryResult(rep(), ret_lines, left.get_file()); } ================================================ FILE: ch15/ex15.42_b/andquery.h ================================================ #ifndef _ANDQUERY_H #define _ANDQUERY_H #include using std::shared_ptr; #include "query.h" #include "binaryquery.h" class QueryResult; class TextQuery; class AndQuery :public BinaryQuery { friend Query operator&(const Query&, const Query&); AndQuery(const Query &left, const Query &right) :BinaryQuery(left, right, "&"){ } QueryResult eval(const TextQuery&) const; }; inline Query operator&(const Query &lhs, const Query &rhs) { return shared_ptr(new AndQuery(lhs, rhs)); } #endif ================================================ FILE: ch15/ex15.42_b/binaryquery.cpp ================================================ #include "binaryquery.h" ================================================ FILE: ch15/ex15.42_b/binaryquery.h ================================================ #ifndef _BINARYQUERY_H #define _BINARYQUERY_H #include using std::string; #include "query.h" #include "query_base.h" class BinaryQuery :public Query_base { protected: BinaryQuery(const Query &l, const Query &r, string s) :lhs(l), rhs(r), opSym(s){ } string rep() const { return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } Query lhs, rhs; string opSym; }; #endif ================================================ FILE: ch15/ex15.42_b/main.cpp ================================================ #include using std::cout; using std::cin; using std::endl; #include using std::ifstream; #include using std::string; #include using std::vector; #include "queryhistory.h" #include "queryresult.h" #include "textquery.h" #include "query.h" #include "andquery.h" #include "orquery.h" #include "notquery.h" int main() { ifstream fin("test.txt"); TextQuery text(fin); QueryHistory history; Query q0("Alice"); Query q1("hair"); Query q2("Daddy"); history.add_query(q0); history.add_query(q1); history[0] = history[0] | q2; auto result = history[0].eval(text); print(cout, result); return 0; } ================================================ FILE: ch15/ex15.42_b/notquery.cpp ================================================ #include "notquery.h" #include using std::make_shared; #include using std::set; #include "queryresult.h" #include "textquery.h" #include "query.h" QueryResult NotQuery::eval(const TextQuery &text) const { auto result = query.eval(text); auto ret_lines = make_shared>(); auto beg = result.begin(); auto end = result.end(); auto sz = result.get_file()->size(); for (size_t n = 0; n != sz; ++n) { if (beg == end || *beg != n) ret_lines->insert(n); else if (beg != end) ++beg; } return QueryResult(rep(), ret_lines, result.get_file()); } ================================================ FILE: ch15/ex15.42_b/notquery.h ================================================ #ifndef _NOTQUERY_H #define _NOTQUERY_H #include using std::shared_ptr; #include using std::string; #include "query.h" #include "query_base.h" class QueryResult; class TextQuery; class NotQuery :public Query_base { friend Query operator~(const Query&); //call Query's default copy constructor. NotQuery(const Query &q) :query(q){ } string rep() const{ return "~(" + query.rep() + ")"; } QueryResult eval(const TextQuery&) const; Query query; }; inline Query operator~(const Query &operand) { return shared_ptr(new NotQuery(operand)); } #endif ================================================ FILE: ch15/ex15.42_b/orquery.cpp ================================================ #include "orquery.h" #include using std::make_shared; #include using std::set; #include "queryresult.h" #include "textquery.h" #include "query.h" QueryResult OrQuery::eval(const TextQuery &text) const { auto right = rhs.eval(text); auto left = lhs.eval(text); auto ret_lines = make_shared>(left.begin(), left.end()); ret_lines->insert(right.begin(), right.end()); return QueryResult(rep(), ret_lines, left.get_file()); } ================================================ FILE: ch15/ex15.42_b/orquery.h ================================================ #ifndef _ORQUERY_H #define _ORQUERY_H #include using std::shared_ptr; #include "query.h" #include "binaryquery.h" class QueryResult; class TextQuery; class OrQuery :public BinaryQuery { friend Query operator|(const Query&, const Query&); OrQuery(const Query &left, const Query &right) :BinaryQuery(left, right, "|"){ } QueryResult eval(const TextQuery&) const; }; inline Query operator|(const Query &lhs, const Query &rhs) { return shared_ptr(new OrQuery(lhs, rhs)); } #endif ================================================ FILE: ch15/ex15.42_b/query.cpp ================================================ #include "query.h" #include using std::ostream; ostream& operator<<(ostream &os, const Query &query) { return os << query.rep(); } ================================================ FILE: ch15/ex15.42_b/query.h ================================================ #ifndef _QUERY_H #define _QUERY_H #include using std::ostream; #include using std::shared_ptr; #include using std::string; #include "query_base.h" #include "queryresult.h" #include "wordquery.h" class TextQuery; class Query { friend Query operator~(const Query&); friend Query operator|(const Query&, const Query&); friend Query operator&(const Query&, const Query&); public: Query(const string&); //call QueryResult's default copy constructor. QueryResult eval(const TextQuery &t) const { return q->eval(t); } string rep() const { return q->rep(); } private: Query(shared_ptr query) :q(query){ } shared_ptr q; }; ostream & operator<<(ostream &os, const Query &query); inline Query::Query(const string &s) :q(new WordQuery(s)){ } #endif ================================================ FILE: ch15/ex15.42_b/query_base.cpp ================================================ #include "query_base.h" ================================================ FILE: ch15/ex15.42_b/query_base.h ================================================ #ifndef _QUERY_BASE_H #define _QUERY_BASE_H #include using std::string; #include "textquery.h" class Query_base { friend class Query; protected: using line_no = TextQuery::line_no; virtual ~Query_base() = default; private: virtual QueryResult eval(const TextQuery&) const = 0; virtual string rep() const = 0; }; #endif ================================================ FILE: ch15/ex15.42_b/queryhistory.cpp ================================================ #include "queryhistory.h" #include using std::shared_ptr; using std::make_shared; size_t QueryHistory::add_query(const Query &query) { shared_ptr p = make_shared(query); query_vec.push_back(p); return query_vec.size() - 1; } ================================================ FILE: ch15/ex15.42_b/queryhistory.h ================================================ #ifndef _QUERYHISTORY_H #define _QUERYHISTORY_H #include using std::shared_ptr; #include using std::vector; #include "query.h" class QueryHistory { public: Query& operator[](size_t n) { return *(query_vec[n]); } //return the assigned number of the new query size_t add_query(const Query&); private: vector> query_vec; }; #endif ================================================ FILE: ch15/ex15.42_b/queryresult.cpp ================================================ #include "queryresult.h" #include using std::ostream; ostream& print(ostream &os, const QueryResult &qr) { os <<"The result of your query "<< qr.sought <<" is: \n"; for (const auto &index: *qr.lines) os << "\t(line " << index + 1 << ")" << *(qr.file->begin() + index) << "\n"; return os; } ================================================ FILE: ch15/ex15.42_b/queryresult.h ================================================ #ifndef _QUERYRESULT_H #define _QUERYRESULT_H #include using std::ostream; #include using std::shared_ptr; #include using std::set; #include using std::vector; #include using std::string; #include "textquery.h" class QueryResult { friend ostream& print(ostream&, const QueryResult&); public: QueryResult(string s, shared_ptr> l, shared_ptr> f) : sought(s), lines(l), file(f){ } set::iterator begin(){ return lines->begin(); } set::iterator end(){ return lines->end(); } shared_ptr> get_file(){ return file; } private: string sought; shared_ptr> lines; shared_ptr> file; }; ostream& print(ostream&, const QueryResult&); #endif ================================================ FILE: ch15/ex15.42_b/test.txt ================================================ Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. "Daddy, shush, there is no such thing," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there?" ================================================ FILE: ch15/ex15.42_b/textquery.cpp ================================================ #include "textquery.h" #include using std::ifstream; #include using std::istringstream; #include using std::shared_ptr; using std::make_shared; #include using std::vector; #include using std::string; #include using std::map; #include using std::set; #include "queryresult.h" TextQuery::TextQuery(ifstream& is) :file(new vector) { using std::getline; string text; while(getline(is, text)) { file->push_back(text); int n = file->size() - 1; istringstream line(text); string word; while(line >> word) { auto p = handlePunct(word); for (auto w : *p) { auto &lines = wm[w]; if (!lines) lines.reset(new set); lines->insert(n); } } } } QueryResult TextQuery::query(const string &sought) const { static shared_ptr> nodata(new set); // fetch the iterator to the matching element in the map. //std::map>>::const_iterator auto loc = wm.find(sought); if(loc == wm.end()) return QueryResult(sought, nodata, file); else return QueryResult(sought, loc->second, file); } shared_ptr> TextQuery::handlePunct(const string &s) { shared_ptr> p = make_shared>(); size_t first = 0, index = 0; while(index != s.size()) { if (ispunct(s[index])) { string word = s.substr(first, index - first); if (!word.empty()) p->push_back(word); p->push_back(s.substr(index, 1)); ++index; first = index; } else ++index; } string trail = s.substr(first); if (!trail.empty()) p->push_back(trail); return p; } ================================================ FILE: ch15/ex15.42_b/textquery.h ================================================ #ifndef _TEXTQUERY_H #define _TEXTQUERY_H #include using std::ifstream; #include using std::shared_ptr; #include using std::vector; #include using std::string; #include using std::map; #include using std::set; class QueryResult; class TextQuery { public: typedef vector::size_type line_no; TextQuery(ifstream&); QueryResult query(const string&) const; private: shared_ptr> file; map>> wm; shared_ptr> handlePunct(const string&); }; #endif ================================================ FILE: ch15/ex15.42_b/wordquery.cpp ================================================ #include "wordquery.h" ================================================ FILE: ch15/ex15.42_b/wordquery.h ================================================ #ifndef _WORDQUERY_H #define _WORDQUERY_H #include using std::string; #include "query_base.h" #include "queryresult.h" #include "textquery.h" class WordQuery:public Query_base { friend class Query; WordQuery(const string &s) :query_word(s){ } QueryResult eval(const TextQuery &t) const{ return t.query(query_word); } string rep() const { return query_word; } string query_word; }; #endif ================================================ FILE: ch15/ex15.42_c/andquery.cpp ================================================ #include "andquery.h" #include using std::set_intersection; #include using std::inserter; #include using std::make_shared; #include using std::set; #include "queryresult.h" #include "textquery.h" #include "query.h" QueryResult AndQuery::eval(const TextQuery& text) const { auto left = lhs.eval(text); auto right = rhs.eval(text); auto ret_lines = make_shared>(); set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(*ret_lines, ret_lines->begin())); return QueryResult(rep(), ret_lines, left.get_file()); } ================================================ FILE: ch15/ex15.42_c/andquery.h ================================================ #ifndef _ANDQUERY_H #define _ANDQUERY_H #include using std::shared_ptr; #include "query.h" #include "binaryquery.h" class QueryResult; class TextQuery; class AndQuery :public BinaryQuery { friend Query operator&(const Query&, const Query&); AndQuery(const Query &left, const Query &right) :BinaryQuery(left, right, "&"){ } QueryResult eval(const TextQuery&) const; }; inline Query operator&(const Query &lhs, const Query &rhs) { return shared_ptr(new AndQuery(lhs, rhs)); } #endif ================================================ FILE: ch15/ex15.42_c/binaryquery.cpp ================================================ #include "binaryquery.h" ================================================ FILE: ch15/ex15.42_c/binaryquery.h ================================================ #ifndef _BINARYQUERY_H #define _BINARYQUERY_H #include using std::string; #include "query.h" #include "query_base.h" class BinaryQuery :public Query_base { protected: BinaryQuery(const Query &l, const Query &r, string s) :lhs(l), rhs(r), opSym(s){ } string rep() const { return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } Query lhs, rhs; string opSym; }; #endif ================================================ FILE: ch15/ex15.42_c/main.cpp ================================================ #include using std::cout; using std::cin; using std::endl; #include using std::ifstream; #include using std::string; #include using std::vector; #include "queryresult.h" #include "textquery.h" #include "query.h" #include "andquery.h" #include "orquery.h" #include "notquery.h" int main() { ifstream fin("test.txt"); TextQuery text(fin); auto q = ~Query("Alice"); auto result = q.eval(text); print(cout, result); cout << endl; print(cout, result, -3, 5); cout << endl; print(cout, result, 3, 5); cout << endl; print(cout, result, 3, 20); cout << endl; return 0; } ================================================ FILE: ch15/ex15.42_c/notquery.cpp ================================================ #include "notquery.h" #include using std::make_shared; #include using std::set; #include "queryresult.h" #include "textquery.h" #include "query.h" QueryResult NotQuery::eval(const TextQuery &text) const { auto result = query.eval(text); auto ret_lines = make_shared>(); auto beg = result.begin(); auto end = result.end(); auto sz = result.get_file()->size(); for (size_t n = 0; n != sz; ++n) { if (beg == end || *beg != n) ret_lines->insert(n); else if (beg != end) ++beg; } return QueryResult(rep(), ret_lines, result.get_file()); } ================================================ FILE: ch15/ex15.42_c/notquery.h ================================================ #ifndef _NOTQUERY_H #define _NOTQUERY_H #include using std::shared_ptr; #include using std::string; #include "query.h" #include "query_base.h" class QueryResult; class TextQuery; class NotQuery :public Query_base { friend Query operator~(const Query&); //call Query's default copy constructor. NotQuery(const Query &q) :query(q){ } string rep() const{ return "~(" + query.rep() + ")"; } QueryResult eval(const TextQuery&) const; Query query; }; inline Query operator~(const Query &operand) { return shared_ptr(new NotQuery(operand)); } #endif ================================================ FILE: ch15/ex15.42_c/orquery.cpp ================================================ #include "orquery.h" #include using std::make_shared; #include using std::set; #include "queryresult.h" #include "textquery.h" #include "query.h" QueryResult OrQuery::eval(const TextQuery &text) const { auto right = rhs.eval(text); auto left = lhs.eval(text); auto ret_lines = make_shared>(left.begin(), left.end()); ret_lines->insert(right.begin(), right.end()); return QueryResult(rep(), ret_lines, left.get_file()); } ================================================ FILE: ch15/ex15.42_c/orquery.h ================================================ #ifndef _ORQUERY_H #define _ORQUERY_H #include using std::shared_ptr; #include "query.h" #include "binaryquery.h" class QueryResult; class TextQuery; class OrQuery :public BinaryQuery { friend Query operator|(const Query&, const Query&); OrQuery(const Query &left, const Query &right) :BinaryQuery(left, right, "|"){ } QueryResult eval(const TextQuery&) const; }; inline Query operator|(const Query &lhs, const Query &rhs) { return shared_ptr(new OrQuery(lhs, rhs)); } #endif ================================================ FILE: ch15/ex15.42_c/query.cpp ================================================ #include "query.h" #include using std::ostream; ostream& operator<<(ostream &os, const Query &query) { return os << query.rep(); } ================================================ FILE: ch15/ex15.42_c/query.h ================================================ #ifndef _QUERY_H #define _QUERY_H #include using std::ostream; #include using std::shared_ptr; #include using std::string; #include "query_base.h" #include "queryresult.h" #include "wordquery.h" class TextQuery; class Query { friend Query operator~(const Query&); friend Query operator|(const Query&, const Query&); friend Query operator&(const Query&, const Query&); public: Query(const string&); //call QueryResult's default copy constructor. QueryResult eval(const TextQuery &t) const { return q->eval(t); } string rep() const { return q->rep(); } private: Query(shared_ptr query) :q(query){ } shared_ptr q; }; ostream & operator<<(ostream &os, const Query &query); inline Query::Query(const string &s) :q(new WordQuery(s)){ } #endif ================================================ FILE: ch15/ex15.42_c/query_base.cpp ================================================ #include "query_base.h" ================================================ FILE: ch15/ex15.42_c/query_base.h ================================================ #ifndef _QUERY_BASE_H #define _QUERY_BASE_H #include using std::string; #include "textquery.h" class Query_base { friend class Query; protected: using line_no = TextQuery::line_no; virtual ~Query_base() = default; private: virtual QueryResult eval(const TextQuery&) const = 0; virtual string rep() const = 0; }; #endif ================================================ FILE: ch15/ex15.42_c/queryresult.cpp ================================================ #include "queryresult.h" #include using std::ostream; ostream& print(ostream &os, const QueryResult &qr) { os <<"The result of your query "<< qr.sought <<" is: \n"; for (const auto &index: *qr.lines) os << "\t(line " << index + 1 << ")" << *(qr.file->begin() + index) << "\n"; return os; } /* Head is the first line of the range. Trail is the last line of the range. */ ostream& print(ostream& os, const QueryResult &qr, size_t head, size_t trail) { if (head > trail) { os << "illegal range!\n"; return os; } else { os << "The result of your query " << qr.sought << " is: \n"; for (const auto &index : *qr.lines) { if (index + 1 >= head&&index + 1 <= trail) { os << "\t(line " << index + 1 << ")" << *(qr.file->begin() + index) << "\n"; } } return os; } } ================================================ FILE: ch15/ex15.42_c/queryresult.h ================================================ #ifndef _QUERYRESULT_H #define _QUERYRESULT_H #include using std::ostream; #include using std::shared_ptr; #include using std::set; #include using std::vector; #include using std::string; #include "textquery.h" class QueryResult { friend ostream& print(ostream&, const QueryResult&); friend ostream& print(ostream&, const QueryResult&, size_t, size_t); public: QueryResult(string s, shared_ptr> l, shared_ptr> f) : sought(s), lines(l), file(f){ } set::iterator begin(){ return lines->begin(); } set::iterator end(){ return lines->end(); } shared_ptr> get_file(){ return file; } private: string sought; shared_ptr> lines; shared_ptr> file; TextQuery::line_no line_no() const { return lines->size(); } }; ostream& print(ostream&, const QueryResult&); ostream& print(ostream&, const QueryResult&, size_t, size_t); #endif ================================================ FILE: ch15/ex15.42_c/test.txt ================================================ Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. "Daddy, shush, there is no such thing," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there?" ================================================ FILE: ch15/ex15.42_c/textquery.cpp ================================================ #include "textquery.h" #include using std::ifstream; #include using std::istringstream; #include using std::shared_ptr; using std::make_shared; #include using std::vector; #include using std::string; #include using std::map; #include using std::set; #include "queryresult.h" TextQuery::TextQuery(ifstream& is) :file(new vector) { using std::getline; string text; while(getline(is, text)) { file->push_back(text); int n = file->size() - 1; istringstream line(text); string word; while(line >> word) { auto p = handlePunct(word); for (auto w : *p) { auto &lines = wm[w]; if (!lines) lines.reset(new set); lines->insert(n); } } } } QueryResult TextQuery::query(const string &sought) const { static shared_ptr> nodata(new set); // fetch the iterator to the matching element in the map. //std::map>>::const_iterator auto loc = wm.find(sought); if(loc == wm.end()) return QueryResult(sought, nodata, file); else return QueryResult(sought, loc->second, file); } shared_ptr> TextQuery::handlePunct(const string &s) { shared_ptr> p = make_shared>(); size_t first = 0, index = 0; while(index != s.size()) { if (ispunct(s[index])) { string word = s.substr(first, index - first); if (!word.empty()) p->push_back(word); p->push_back(s.substr(index, 1)); ++index; first = index; } else ++index; } string trail = s.substr(first); if (!trail.empty()) p->push_back(trail); return p; } ================================================ FILE: ch15/ex15.42_c/textquery.h ================================================ #ifndef _TEXTQUERY_H #define _TEXTQUERY_H #include using std::ifstream; #include using std::shared_ptr; #include using std::vector; #include using std::string; #include using std::map; #include using std::set; class QueryResult; class TextQuery { public: typedef vector::size_type line_no; TextQuery(ifstream&); QueryResult query(const string&) const; private: shared_ptr> file; map>> wm; shared_ptr> handlePunct(const string&); }; #endif ================================================ FILE: ch15/ex15.42_c/wordquery.cpp ================================================ #include "wordquery.h" ================================================ FILE: ch15/ex15.42_c/wordquery.h ================================================ #ifndef _WORDQUERY_H #define _WORDQUERY_H #include using std::string; #include "query_base.h" #include "queryresult.h" #include "textquery.h" class WordQuery:public Query_base { friend class Query; WordQuery(const string &s) :query_word(s){ } QueryResult eval(const TextQuery &t) const{ return t.query(query_word); } string rep() const { return query_word; } string query_word; }; #endif ================================================ FILE: ch15/ex15.7/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { return n * price * ( n >= min_qty ? 1 - discount : 1); } ================================================ FILE: ch15/ex15.7/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Quote(b, p), min_qty(q), discount(disc) { } double net_price(std::size_t n) const override; private: std::size_t min_qty = 0; double discount = 0.0; }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.7/limit_quote.cpp ================================================ #include "limit_quote.h" double Limit_quote::net_price(std::size_t n) const { if (n > max_qty) return max_qty * price * discount + (n - max_qty) * price; else return n * discount *price; } ================================================ FILE: ch15/ex15.7/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "quote.h" class Limit_quote : public Quote { public: Limit_quote(); Limit_quote(const std::string& b, double p, std::size_t max, double disc): Quote(b, p), max_qty(max), discount(disc) { } double net_price(std::size_t n) const override; private: std::size_t max_qty = 0; double discount = 0.0; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.7/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 21 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.7: // Define a class that implements a limited discount strategy, which applies // a discount to books purchased up to a given limit. If the number of copies // exceeds that limit, the normal price applies to those purchased beyond the // limit. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" double print_total (std::ostream& os, const Quote& item, size_t n); int main() { // ex15.6 Quote q("textbook", 10.60); Bulk_quote bq("textbook", 10.60, 10, 0.3); Limit_quote lq("Bible", 10.60, 10 , 0.3); print_total(std::cout, q, 5); print_total(std::cout, bq, 5); print_total(std::cout , lq, 5); return 0; } double print_total(std::ostream &os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN:" << item.isbn() << "# sold: " << n << " total due: " << ret << std::endl; return ret; } ================================================ FILE: ch15/ex15.7/quote.cpp ================================================ #include "quote.h" ================================================ FILE: ch15/ex15.7/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { return n * price; } virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch15/ex15.8.9.10/ex15.8.9.10.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 21 Jan 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.8: // Define static type and dynamic type. // The static type of an expression is always known at compile time. // The dynamic type is the type of the object in memory that the variable or // expression represents. The dynamic type may not be known until run time. // // Exercise 15.9: // When is it possible for an expression’s static type to differ from its // dynamic type? Give three examples in which the static and dynamic type differ. // The static type of a pointer or reference to a base class may differ from // its dynamic type. Anying like this can be an example // // Exercise 15.10: // Recalling the discussion from §8.1 (p. 311), explain how the program on // page 317 that passed an ifstream to the Sales_data read function works. // the function takes a std::istream from which std::ifstream is derived. // Hence the ifstream object "is a" istream , which is why it works. // #include #include #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" double print_total (std::ostream& os, const Quote& item, size_t n); int main() { return 0; } double print_total(std::ostream &os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN:" << item.isbn() << "# sold: " << n << " total due: " << ret << std::endl; return ret; } ================================================ FILE: ch15/ex15.9/bulk_quote.cpp ================================================ #include "bulk_quote.h" double Bulk_quote::net_price(std::size_t n) const { std::cout << "Bulk_Quote net_price method got called" << std::endl; return n * price * ( n >= min_qty ? 1 - discount : 1); } ================================================ FILE: ch15/ex15.9/bulk_quote.h ================================================ #ifndef BULK_QUOTE_H #define BULK_QUOTE_H #include "quote.h" class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Quote(b, p), min_qty(q), discount(disc) { } double net_price(std::size_t n) const override; private: std::size_t min_qty = 0; double discount = 0.0; }; #endif // BULK_QUOTE_H ================================================ FILE: ch15/ex15.9/limit_quote.cpp ================================================ #include "limit_quote.h" double Limit_quote::net_price(std::size_t n) const { if (n > max_qty) return max_qty * price * discount + (n - max_qty) * price; else return n * discount *price; } ================================================ FILE: ch15/ex15.9/limit_quote.h ================================================ #ifndef LIMIT_QUOTE_H #define LIMIT_QUOTE_H #include "quote.h" class Limit_quote : public Quote { public: Limit_quote(); Limit_quote(const std::string& b, double p, std::size_t max, double disc): Quote(b, p), max_qty(max), discount(disc) { } double net_price(std::size_t n) const override; private: std::size_t max_qty = 0; double discount = 0.0; }; #endif // LIMIT_QUOTE_H ================================================ FILE: ch15/ex15.9/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Hoesel Markus * @date 11 Apr 2016 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 15.9: // When is it possible for an expression’s static type to differ from its // dynamic type? Give three examples in which the static and dynamic type differ. // #include #include #include "quote.h" #include "bulk_quote.h" #include "limit_quote.h" int main() { Bulk_quote bulk_quote("bulk_quote_1", 10.10, 10, 0.5); // The pointer is of static type Quote, but it's dynamic type is Bulk Quote // Because of polymorphism the Bulk Quote implementation of the net_price() // method gets called. Quote *quote_pointer = &bulk_quote; quote_pointer->net_price(5); // The reference is of static type Quote, but it's dynamic type is Bulk Quote // Like with the pointer, the Bulk Quote implementation of the net_price() // method gets called. Quote "e_reference = bulk_quote; quote_reference.net_price(5); // The static type of this variable is Quote. Here the Bulk Quote object // gets upcasted. The Quote part of bulk_quote gets copied into quote, but // the rest is not handled. Because of the cast the Quote implementation of // the net_price() method gets called. Quote quote = bulk_quote; quote.net_price(5); return 0; } ================================================ FILE: ch15/ex15.9/quote.cpp ================================================ #include "quote.h" ================================================ FILE: ch15/ex15.9/quote.h ================================================ #ifndef QUOTE_H #define QUOTE_H #include #include class Quote { public: Quote() = default; Quote(const std::string &b, double p) : bookNo(b), price(p) { } std::string isbn() const { return bookNo; } virtual double net_price(std::size_t n) const { std::cout << "Quote net_price method got called" << std::endl; return n * price; } virtual ~Quote() = default; private: std::string bookNo; protected: double price = 0.0; }; #endif // QUOTE_H ================================================ FILE: ch16/ex16.1.2.3/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.1: // Define instantiation. // When the compiler instantiates a template, it creates a new “instance” of // the template using the actual template argument(s) in place of the // corresponding template parameter(s). // // Exercise 16.2: // Write and test your own versions of the compare functions. // // Exercise 16.3: // Call your compare function on two Sales_data objects to see how your compiler // handles errors during instantiation. // error: no match for 'operator<' (operand types are 'const Sales_data' and // 'const Sales_data') // // // #include using std::cout; using std::endl; #include using std::vector; class Sales_data{ }; template int compare(const T& lhs, const T& rhs) { if(lhs < rhs) return -1; if(rhs < lhs) return 1; return 0; } int main() { // Test compare function cout << compare(1, 0) << endl; vector vec1{ 1, 2, 3 }, vec2{ 4, 5, 6 }; cout << compare(vec1, vec2) << endl; return 0; } ================================================ FILE: ch16/ex16.12.13/Blob.h ================================================ #ifndef BLOB_H #define BLOB_H #include #include template class Blob { public: typedef T value_type; typedef typename std::vector::size_type size_type; // constructors Blob(); Blob(std::initializer_list il); // number of elements in the Blob size_type size() const { return data->size(); } bool empty() const{ return data->empty(); } void push_back(const T& t) { data->push_back(t); } void push_back(T&& t) { data->push_back(std::move(t)); } void pop_back(); // element access T& back(); T& operator[](size_type i); const T& back() const; const T& operator [](size_type i) const; private: std::shared_ptr> data; // throw msg if data[i] isn't valid void check(size_type i, const std::string &msg) const; }; // constructors template Blob::Blob() : data(std::make_shared>()) { } template Blob::Blob(std::initializer_list il): data(std::make_shared>(il)){ } template void Blob::check(size_type i, const std::string &msg) const { if(i >= data->size()) throw std::out_of_range(msg); } template T& Blob::back() { check(0, "back on empty Blob"); return data->back(); } template const T& Blob::back() const { check(0, "back on empty Blob"); return data->back(); } template T& Blob::operator [](size_type i) { // if i is too big, check function will throw, preventing access to a nonexistent element check(i, "subscript out of range"); return (*data)[i]; } template const T& Blob::operator [](size_type i) const { // if i is too big, check function will throw, preventing access to a nonexistent element check(i, "subscript out of range"); return (*data)[i]; } template void Blob::pop_back() { check(0, "pop_back on empty Blob"); data->pop_back(); } #endif // BLOB_H ================================================ FILE: ch16/ex16.12.13/blobptr.h ================================================ #ifndef BLOBPTR_H #define BLOBPTR_H #include "Blob.h" #include #include template class BlobPtr; template bool operator ==(const BlobPtr& lhs, const BlobPtr& rhs); template bool operator < (const BlobPtr& lhs, const BlobPtr& rhs); template class BlobPtr { friend bool operator == (const BlobPtr& lhs, const BlobPtr& rhs); friend bool operator < (const BlobPtr& lhs, const BlobPtr& rhs); public: BlobPtr() : curr(0) { } BlobPtr(Blob& a, std::size_t sz = 0) : wptr(a.data), curr(sz) { } T& operator*() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } // prefix BlobPtr& operator++(); BlobPtr& operator--(); // postfix BlobPtr operator ++(int); BlobPtr operator --(int); private: // returns a shared_ptr to the vector if the check succeeds std::shared_ptr> check(std::size_t, const std::string&) const; std::weak_ptr> wptr; std::size_t curr; }; // prefix ++ template BlobPtr& BlobPtr::operator ++() { // if curr already points past the end of the container, can't increment it check(curr, "increment past end of StrBlob"); ++curr; return *this; } // prefix -- template BlobPtr& BlobPtr::operator --() { -- curr; check(curr, "decrement past begin of BlobPtr"); return *this; } // postfix ++ template BlobPtr BlobPtr::operator ++(int) { BlobPtr ret = *this; ++*this; return ret; } // postfix -- template BlobPtr BlobPtr::operator --(int) { BlobPtr ret = *this; --*this; return ret; } template bool operator==(const BlobPtr &lhs, const BlobPtr &rhs) { if (lhs.wptr.lock() != rhs.wptr.lock()) { throw runtime_error("ptrs to different Blobs!"); } return lhs.i == rhs.i; } template bool operator< (const BlobPtr &lhs, const BlobPtr &rhs) { if (lhs.wptr.lock() != rhs.wptr.lock()) { throw runtime_error("ptrs to different Blobs!"); } return lhs.i < rhs.i; } #endif // BLOBPTR_H ================================================ FILE: ch16/ex16.12.13/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.12: // Write your own version of the Blob and BlobPtr templates. including the // various const members that were not shown in the text. // // Exercise 16.13: // Explain which kind of friendship you chose for the equality and relational // operators for BlobPtr. // As shown in the class body. // #include #include #include #include #include "Blob.h" #include "blobptr.h" int main() { return 0; } ================================================ FILE: ch16/ex16.14.15/Screen.h ================================================ // // Exercise 16.14: // Write a Screen class template that uses nontype parameters to define the // height and width of the Screen. // #ifndef SCREEN_H #define SCREEN_H #include #include template class Screen { public: typedef std::string::size_type pos; Screen() = default; // needed because Screen has another constructor // cursor initialized to 0 by its in-class initializer Screen(char c):contents(H * W, c) { } char get() const // get the character at the cursor { return contents[cursor]; } // implicitly inline Screen &move(pos r, pos c); // can be made inline later friend std::ostream & operator<< ( std::ostream &os , const Screen & c ) { unsigned int i, j; for( i=0 ;i> ( std::istream &is , Screen & c ) { char a; is>>a; std::string temp(H*W, a); c.contents = temp; return is; } private: pos cursor = 0; pos height = H, width = W; std::string contents; }; template inline Screen& Screen::move(pos r, pos c) { pos row = r * width; cursor = row + c; return *this; } #endif // SCREEN_H ================================================ FILE: ch16/ex16.14.15/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.14: // Write a Screen class template that uses nontype parameters to define the // height and width of the Screen. // // Exercise 16.15: // Implement input and output operators for your Screen template. // Which, if any, friends are necessary in class Screen to make the input and // output operators work? Explain why each friend declaration, if any, was // needed. // According to the chapter of 14.2.1, the class of << and << should be a friend of this class. // #include "Screen.h" #include int main() { Screen<5, 5> scr('c'); Screen<5, 5> scr2; // output src to the screen std::cout<>scr2; // test input std::cout< #include int main() { Vec v = { 1, 2, 3, 4, 5 }; Vec v2; v2 = v; std::cout << v2.capacity() << "\n"; v2.push_back(99); v2.resize(2, 2); for (auto t : v2) std::cout << t << " "; std::cout << v2.capacity() << "\n"; return 0; } ================================================ FILE: ch16/ex16.16/vec.h ================================================ /*************************************************************************** * @file vec.h * @author Alan.W * @date 02 FEB 2014 * @remark ***************************************************************************/ #ifndef VEC_H #define VEC_H #include /** * @brief a vector like class */ template class Vec { public: Vec():element(nullptr), first_free(nullptr), cap(nullptr){ } Vec(std::initializer_list l); Vec(const Vec& v); Vec& operator =(const Vec& rhs); ~Vec(); // memmbers void push_back(const T& t); std::size_t size() const { return first_free - element; } std::size_t capacity()const { return cap - element; } T* begin() const { return element; } T* end() const { return first_free; } void reserve(std::size_t n); void resize(std::size_t n); void resize(std::size_t n, const T& t); private: // data members T* element; T* first_free; T* cap; std::allocator alloc; // utillities void reallocate(); void chk_n_alloc() { if(size()==capacity()) reallocate(); } void free(); void wy_alloc_n_move(std::size_t n); std::pair alloc_n_copy(T* b, T* e); }; // copy constructor template Vec::Vec(const Vec &v) { /** * @brief newData is a pair of pointers pointing to newly allocated and copied * from range : [b, e) */ std::pair newData = alloc_n_copy(v.begin(), v.end()); element = newData.first; first_free = cap = newData.second; } // constructor that takes initializer_list template Vec::Vec(std::initializer_list l) { // allocate memory as large as l.size() T* const newData = alloc.allocate(l.size()); // copy elements from l to the address allocated T* p = newData; for(const auto &t : l) alloc.construct(p++, t); // build data structure element = newData; first_free = cap = element + l.size(); } // operator = template Vec& Vec::operator =(const Vec& rhs) { // allocate and copy first to protect against self_assignment std::pair newData = alloc_n_copy(rhs.begin(), rhs.end()); // destroy and deallocate free(); // update data structure element = newData.first; first_free = cap = newData.second; return *this; } // destructor template Vec::~Vec() { free(); } /** * @brief allocate new memeory if nessary and push back the new T * @param t new T */ template void Vec::push_back(const T &t) { chk_n_alloc(); alloc.construct(first_free++, t); } /** * @brief preallocate enough memory for specified number of elements * @param n number of elements required */ template void Vec::reserve(std::size_t n) { // if n too small, just return without doing anything if(n <= capacity()) return; // allocate new memory and move data from old address to the new one wy_alloc_n_move(n); } /** * @brief Resizes to the specified number of elements. * @param n Number of elements the %vector should contain. * * This function will resize it to the specified * number of elements. If the number is smaller than the * current size it is truncated, otherwise * default constructed elements are appended. */ template void Vec::resize(std::size_t n) { resize(n, T()); } /** * @brief Resizes it to the specified number of elements. * @param __new_size Number of elements it should contain. * @param __x Data with which new elements should be populated. * * This function will resize it to the specified * number of elements. If the number is smaller than the * current size the it is truncated, otherwise * the it is extended and new elements are populated with * given data. */ template void Vec::resize(std::size_t n, const T &t) { if(n < size()) { // destroy the range [element+n, first_free) using destructor for(auto p = element + n; p != first_free; ) alloc.destroy(p++); // update first_free to point to the new address first_free = element + n; } else if(n > size()) { for (auto i = size(); i != n; ++i) push_back(t); } } /** * @brief allocate new space for the given range and copy them into it * @param b * @param e * @return a pair of pointers pointing to [first element , one past the last) in the new space */ template std::pair Vec::alloc_n_copy(T *b, T *e) { // calculate the size needed and allocate space accordingly T* data = alloc.allocate(e-b); return { data, std::uninitialized_copy(b, e, data) }; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // which copies the range[first, last) to the space to which // the starting address data is pointing. // This function returns a pointer to one past the last element } /** * @brief destroy the elements and deallocate the space previously allocated. */ template void Vec::free() { // if not nullptr if(element) { // destroy it in reverse order. for(auto p = first_free; p != element; ) alloc.destroy(--p); alloc.deallocate(element, capacity()); } } /** * @brief allocate memory for spicified number of elements * @param n * @note it's user's responsibility to ensure that @param n is greater than * the current capacity. */ template void Vec::wy_alloc_n_move(std::size_t n) { // allocate as required. std::size_t newCapacity = n; T* newData = alloc.allocate(newCapacity); // move the data from old place to the new one T* dest = newData; T* old = element; for(std::size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*old++)); free(); // update data structure element = newData; first_free = dest; cap = element + newCapacity; } /** * @brief Double the capacity and using std::move move the original data to the newly * allocated memory */ template void Vec::reallocate() { // calculate the new capacity required std::size_t newCapacity = size() ? 2 * size() : 1; // allocate and move old data to the new space wy_alloc_n_move(newCapacity); } #endif // VEC_H ================================================ FILE: ch16/ex16.17.18/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.17: // What, if any, are the differences between a type parameter that is declared // as a typename and one that is declared as a class? When must typename be used? // // There is no difference. typename and class are interchangeable in the // declaration of a type template parameter. // You do, however, have to use class (and not typename) when declaring a // template template parameter. // // When we want to inform the compiler that a name represents a type, we must use // the keyword typename, not class // // Exercise 16.18: // Explain each of the following function template declarations and identify // whether any are illegal. Correct each error that you find. // template void f1(T, U, V); // ^^^^^^^^ added to fix the error template T f2(T&); // ^^ template inline T foo(T, unsigned int*); // ^^^^^^ inline must be after template template T f4(T, T); // ^ -- return type must be provided typedef char Ctype; // ^^^^^ //the template declatation below hides this typedef template Ctype f5(Ctype a); int main() { return 0; } ================================================ FILE: ch16/ex16.19.20/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 03 Feb 2014 * Aug 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.19: // Write a function that takes a reference to a container and prints the // elements in that container. Use the container’s size_type and size members // to control the loop that prints the elements. // // Exercise 16.20: // Rewrite the function from the previous exercise to use iterators returned // from begin and end to control the loop. // #include #include #include // ex16.19 template std::ostream& print(Container const& container, std::ostream& os) { for(typename Container::size_type i = 0; i != container.size(); ++ i) os << container[i] << " "; return os; } // ex16.20 template std::ostream& print2(Container const& container, std::ostream &os) { for(auto curr = container.cbegin(); curr != container.cend(); ++curr) os << *curr << " "; return os; } int main() { std::vector v = { 1, 23, 6, 4, 5, 7, 4 }; std::list l = { "ss", "sszz", "saaas", "s333s", "ss2"," sss" }; print2(v, std::cout) << std::endl; print2(l, std::cout) << std::endl; return 0; } ================================================ FILE: ch16/ex16.21.22/DebugDelete.h ================================================ #ifndef DEBUGDELETE_H #define DEBUGDELETE_H #include class DebugDelete { public: DebugDelete(std::ostream& s = std::cerr) : os(s) { } template void operator() (T* p) const { os << "deleting unique_ptr" << std::endl; delete p; } private: std::ostream& os; }; #endif // DEBUGDELETE_H ================================================ FILE: ch16/ex16.21.22/StrBlob.h ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced. Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ #ifndef STRBLOB_H #define STRBLOB_H #include #include #include #include #include // forward declaration needed for friend declaration in StrBlob class StrBlobPtr; class StrBlob { friend class StrBlobPtr; public: typedef std::vector::size_type size_type; // constructors StrBlob() : data(std::make_shared>()) { } StrBlob(std::initializer_list il); // size operations size_type size() const { return data->size(); } bool empty() const { return data->empty(); } // add and remove elements void push_back(const std::string &t) { data->push_back(t); } void pop_back(); // element access std::string& front(); std::string& back(); // interface to StrBlobPtr StrBlobPtr begin(); // can't be defined until StrBlobPtr is StrBlobPtr end(); private: std::shared_ptr> data; // throws msg if data[i] isn't valid void check(size_type i, const std::string &msg) const; }; // constructor inline StrBlob::StrBlob(std::initializer_list il): data(std::make_shared>(il)) { } // StrBlobPtr throws an exception on attempts to access a nonexistent element class StrBlobPtr { friend bool eq(const StrBlobPtr&, const StrBlobPtr&); public: StrBlobPtr(): curr(0) { } StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { } // newly overloaded why? StrBlobPtr(const StrBlob &a, const size_t sz = 0) : wptr(a.data), curr(sz) { } std::string& deref() const; StrBlobPtr& incr(); // prefix version StrBlobPtr& decr(); // prefix version private: // check returns a shared_ptr to the vector if the check succeeds std::shared_ptr> check(std::size_t, const std::string&) const; // store a weak_ptr, which means the underlying vector might be destroyed std::weak_ptr> wptr; std::size_t curr; // current position within the array }; inline std::string& StrBlobPtr::deref() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; // (*p) is the vector to which this object points } inline std::shared_ptr> StrBlobPtr::check(std::size_t i, const std::string &msg) const { auto ret = wptr.lock(); // is the vector still around? if (!ret) throw std::runtime_error("unbound StrBlobPtr"); if (i >= ret->size()) throw std::out_of_range(msg); return ret; // otherwise, return a shared_ptr to the vector } // prefix: return a reference to the incremented object inline StrBlobPtr& StrBlobPtr::incr() { // if curr already points past the end of the container, can't increment it check(curr, "increment past end of StrBlobPtr"); ++curr; // advance the current state return *this; } inline StrBlobPtr& StrBlobPtr::decr() { // if curr is zero, decrementing it will yield an invalid subscript --curr; // move the current state back one element} check(-1, "decrement past begin of StrBlobPtr"); return *this; } // begin and end members for StrBlob inline StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); } inline StrBlobPtr StrBlob::end() { auto ret = StrBlobPtr(*this, data->size()); return ret; } // named equality operators for StrBlobPtr inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { auto l = lhs.wptr.lock(), r = rhs.wptr.lock(); // if the underlying vector is the same if (l == r) // then they're equal if they're both null or // if they point to the same element return (!r || lhs.curr == rhs.curr); else return false; // if they point to difference vectors, they're not equal } inline bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !eq(lhs, rhs); } #endif ================================================ FILE: ch16/ex16.21.22/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 03 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.21: // Write your own version of DebugDelete. // // Exercise 16.22: // Revise your TextQuery programs from § 12.3 (p. 484) so that the shared_ptr // members use a DebugDelete as their deleter (§ 12.1.4, p. 468). // #include #include #include #include "DebugDelete.h" #include #include "wy_queryresult.h" #include "wy_textquery.h" int main() { return 0; } ================================================ FILE: ch16/ex16.21.22/wy_queryresult.cpp ================================================ /*************************************************************************** * @file wy_queryresult.cpp * @author Alan.W * @date 27 DEC 2013 * @remark ***************************************************************************/ // // Exercise 12.27: // The TextQuery and QueryResult classes use only capabilities that we have // already covered. Without looking ahead, write your own versions of these // classes. // #include "wy_queryresult.h" // copy constructor inline wy_queryResult::wy_queryResult(const wy_queryResult &qr): counter(qr.getCounter()), queryWord(qr.getQueryWord()), sp_file(qr.getSp_file()), sp_Qmap(qr.getSp_Qmap()) { } // constructor wy_queryResult::wy_queryResult(std::size_t c, const std::string &str, const wy_textQuery::sp_file_Tp &sp_f, const wy_textQuery::sp_Qmap_Tp &sp_m) : counter(c), queryWord(str), sp_file(sp_f), sp_Qmap(sp_m) { } // a non-member function printing the result of a query. void print(std::ostream &os, const wy_queryResult &qr) { // fetch the word being queried. const std::string queryWord = qr.getQueryWord(); // print the word and occurrence times os << "The word [" < class wy_queryResult { public: // default constructor wy_queryResult() = default; // copy constructor wy_queryResult(const wy_queryResult &qr); wy_queryResult(std::size_t c, const std::string &str, const wy_textQuery::sp_file_Tp &sp_f, const wy_textQuery::sp_Qmap_Tp &sp_m); std::size_t getCounter() const { return counter; } std::string getQueryWord() const { return queryWord; } wy_textQuery::sp_file_Tp getSp_file() const { return sp_file; } wy_textQuery::sp_Qmap_Tp getSp_Qmap() const { return sp_Qmap; } private: // number of occurrence std::size_t counter = 0; // the word being searched std::string queryWord = ""; // smart pointer to a vector to be storing a file. wy_textQuery::sp_file_Tp sp_file = nullptr; // smart pointer to a map to be storing results of querries. wy_textQuery::sp_Qmap_Tp sp_Qmap = nullptr; }; // print the result of searching void print(std::ostream &os, const wy_queryResult &qr); #endif // WY_QUERYRESULT_H ================================================ FILE: ch16/ex16.21.22/wy_textquery.cpp ================================================ /*************************************************************************** * @file wy_textquery.cpp * @author Alan.W * @date 27 DEC 2013 * @remark 03 FEB 2014 deleter added ***************************************************************************/ // // Exercise 12.27: // The TextQuery and QueryResult classes use only capabilities that we have // already covered. Without looking ahead, write your own versions of these // classes. // #include "wy_textquery.h" #include "wy_queryresult.h" #include "DebugDelete.h" #include #include /** * Constructor * read each line into the dynamicly allocated vector. */ wy_textQuery::wy_textQuery(std::ifstream &fin) : // @oldcode // custom deleter may not use with std::make_shared, as it has an internal // deleter which may not be replaced. As a result, keyword new is the only // option to use with DebugDelete. /* sp_fileData(std::make_shared>() ), sp_queryMap(std::make_shared>>() ) */ sp_fileData(new std::vector(), DebugDelete() ), sp_queryMap(new std::map>(), DebugDelete()) { std::string line; while(std::getline(fin, line)) sp_fileData->push_back(line); } /** * @brief return a wy_queryResult object for the search result. * @param qWord */ wy_queryResult wy_textQuery::query(const std::string &qWord) const { // storing the amount of occurrence std::size_t counter = 0; // loop through each line for(std::size_t i=0; i != sp_fileData->size(); ++i) { // break into each word std::stringstream lineStream((*sp_fileData)[i]); std::string word; while(lineStream >> word) { if(!word.compare(qWord)) { ++counter; // add the index of the line into the result map (*sp_queryMap)[qWord].insert(i); } } } // creare a object holding the result . wy_queryResult qResult(counter, qWord, sp_fileData, sp_queryMap); return qResult; } ================================================ FILE: ch16/ex16.21.22/wy_textquery.h ================================================ /*************************************************************************** * @file wy_textquery.h * @author Alan.W * @date 27 DEC 2013 * @remark ***************************************************************************/ // // Exercise 12.27: // The TextQuery and QueryResult classes use only capabilities that we have // already covered. Without looking ahead, write your own versions of these // classes. // #ifndef WY_TEXTQUERY_H #define WY_TEXTQUERY_H #include #include #include #include #include #include #include class wy_queryResult; class wy_textQuery { public: typedef std::shared_ptr> sp_file_Tp; typedef std::shared_ptr>> sp_Qmap_Tp; // constructors wy_textQuery() = default; wy_textQuery(std::ifstream &fin); // query operation wy_queryResult query(const std::string &qWord) const; private: // smart pointer to a vector to be storing a file. sp_file_Tp sp_fileData = nullptr; // smart pointer to a map to be storing results of querries. sp_Qmap_Tp sp_queryMap = nullptr; }; #endif // WY_TEXTQUERY_H ================================================ FILE: ch16/ex16.24/Blob.h ================================================ /*************************************************************************** * @file Blob.h * @author Alan.W * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note 03 Feb 2014 Add a constructor that takes two iterators to Blob . ***************************************************************************/ #ifndef BLOB_H #define BLOB_H #include #include template class Blob { public: typedef T value_type; typedef typename std::vector::size_type size_type; // constructors Blob(); Blob(std::initializer_list il); // constructor taking two iterators template Blob(It b, It e); // number of elements in the Blob size_type size() const { return data->size(); } bool empty() const{ return data->empty(); } void push_back(const T& t) { data->push_back(t); } void push_back(T&& t) { data->push_back(std::move(t)); } void pop_back(); // element access T& back(); T& operator[](size_type i); const T& back() const; const T& operator [](size_type i) const; private: std::shared_ptr> data; // throw msg if data[i] isn't valid void check(size_type i, const std::string &msg) const; }; // default constructor template Blob::Blob() : data(std::make_shared>()) { } // constructor taking initializer_list template Blob::Blob(std::initializer_list il): data(std::make_shared>(il)){ } // constructor taking two iterators template //for class template //for this member Blob::Blob(It b, It e) : data(std::make_shared>(b, e)) { } template void Blob::check(size_type i, const std::string &msg) const { if(i >= data->size()) throw std::out_of_range(msg); } template T& Blob::back() { check(0, "back on empty Blob"); return data->back(); } template const T& Blob::back() const { check(0, "back on empty Blob"); return data->back(); } template T& Blob::operator [](size_type i) { // if i is too big, check function will throw, preventing access to a nonexistent element check(i, "subscript out of range"); return (*data)[i]; } template const T& Blob::operator [](size_type i) const { // if i is too big, check function will throw, preventing access to a nonexistent element check(i, "subscript out of range"); return (*data)[i]; } template void Blob::pop_back() { check(0, "pop_back on empty Blob"); data->pop_back(); } #endif // BLOB_H ================================================ FILE: ch16/ex16.24/blobptr.h ================================================ #ifndef BLOBPTR_H #define BLOBPTR_H #include "Blob.h" #include #include template class BlobPtr; template bool operator ==(const BlobPtr& lhs, const BlobPtr& rhs); template bool operator < (const BlobPtr& lhs, const BlobPtr& rhs); template class BlobPtr { friend bool operator == (const BlobPtr& lhs, const BlobPtr& rhs); friend bool operator < (const BlobPtr& lhs, const BlobPtr& rhs); public: BlobPtr() : curr(0) { } BlobPtr(Blob& a, std::size_t sz = 0) : wptr(a.data), curr(sz) { } T& operator*() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } // prefix BlobPtr& operator++(); BlobPtr& operator--(); // postfix BlobPtr operator ++(int); BlobPtr operator --(int); private: // returns a shared_ptr to the vector if the check succeeds std::shared_ptr> check(std::size_t, const std::string&) const; std::weak_ptr> wptr; std::size_t curr; }; // prefix ++ template BlobPtr& BlobPtr::operator ++() { // if curr already points past the end of the container, can't increment it check(curr, "increment past end of StrBlob"); ++curr; return *this; } // prefix -- template BlobPtr& BlobPtr::operator --() { -- curr; check(curr, "decrement past begin of BlobPtr"); return *this; } // postfix ++ template BlobPtr BlobPtr::operator ++(int) { BlobPtr ret = *this; ++*this; return ret; } // postfix -- template BlobPtr BlobPtr::operator --(int) { BlobPtr ret = *this; --*this; return ret; } #endif // BLOBPTR_H ================================================ FILE: ch16/ex16.24/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 03 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.24: // Add a constructor that takes two iterators to your Blob template. // #include #include #include #include #include int main() { std::vector v = { 1, 2, 3, 4, 5 }; Blob b(v.begin(), v.end()); return 0; } ================================================ FILE: ch16/ex16.25.26/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 03 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.25: // Explain the meaning of these declarations: // /** * @brief instantiation declaration. The definition of it is somewhere else */ //extern template class vector; /** * @brief instantiation definition. The compiler will generate codes for it. */ //template class vector; // Exercise 16.26: // Assuming NoDefault is a class that does not have a default constructor, // can we explicitly instantiate vector? If not, why not? // http://stackoverflow.com/questions/21525169/while-explicitly-instantiating-vectorsometype-what-is-the-sometype-default-co // int main() { return 0; } ================================================ FILE: ch16/ex16.28/delete.hpp ================================================ /*************************************************************************** * @file delete.hpp * @author Yue Wang * @date 04 Feb 2014 * Jul 2015 * Oct 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ #pragma once namespace cp5 { struct Delete { template auto operator() (T* p) const { delete p; } }; } ================================================ FILE: ch16/ex16.28/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 04 Feb 2014 * Jul 2015 * Oct 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.28 Write your own versions of shared_ptr and unique_ptr // #include #include #include "shared_pointer.hpp" int main() { auto foo = cp5::SharedPointer{ new int(42) }; auto bar(foo) ; std::cout << *foo << std::endl; std::cout << foo.use_count() << std::endl; auto string_ptr = cp5::SharedPointer{ new std::string{ "Yue" } }; std::cout << *string_ptr << std::endl; std::cout << string_ptr->size() << std::endl; return 0; } ================================================ FILE: ch16/ex16.28/shared_pointer.hpp ================================================ /*************************************************************************** * @file shared_pointer.hpp * @author Yue Wang * @date 04 Feb 2014 * Jul 2015 * Oct 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ #pragma once #include #include "delete.hpp" namespace cp5 { template class SharedPointer; template auto swap(SharedPointer& lhs, SharedPointer& rhs) { using std::swap; swap(lhs.ptr, rhs.ptr); swap(lhs.ref_count, rhs.ref_count); swap(lhs.deleter, rhs.deleter); } template class SharedPointer { public: // // Default Ctor // SharedPointer() : ptr{ nullptr }, ref_count{ new std::size_t(1) }, deleter{ cp5::Delete{} } { } // // Ctor that takes raw pointer // explicit SharedPointer(T* raw_ptr) : ptr{ raw_ptr }, ref_count{ new std::size_t(1) }, deleter{ cp5::Delete{} } { } // // Copy Ctor // SharedPointer(SharedPointer const& other) : ptr{ other.ptr }, ref_count{ other.ref_count }, deleter{ other.deleter } { ++*ref_count; } // // Move Ctor // SharedPointer(SharedPointer && other) noexcept : ptr{ other.ptr }, ref_count{ other.ref_count }, deleter{ std::move(other.deleter) } { other.ptr = nullptr; other.ref_count = nullptr; } // // Copy assignment // SharedPointer& operator=(SharedPointer const& rhs) { //increment first to ensure safty for self-assignment ++*rhs.ref_count; decrement_and_destroy(); ptr = rhs.ptr, ref_count = rhs.ref_count, deleter = rhs.deleter; return *this; } // // Move assignment // SharedPointer& operator=(SharedPointer && rhs) noexcept { cp5::swap(*this, rhs); rhs.decrement_and_destroy(); return *this; } // // Conversion operator // operator bool() const { return ptr ? true : false; } // // Dereference // T& operator* () const { return *ptr; } // // Arrow // T* operator->() const { return &*ptr; } // // Use count // auto use_count() const { return *ref_count; } // // Get underlying pointer // auto get() const { return ptr; } // // Check if the unique user // auto unique() const { return 1 == *refCount; } // // Swap // auto swap(SharedPointer& rhs) { ::swap(*this, rhs); } // // Free the object pointed to, if unique // auto reset() { decrement_and_destroy(); } // // Reset with the new raw pointer // auto reset(T* pointer) { if (ptr != pointer) { decrement_and_destroy(); ptr = pointer; ref_count = new std::size_t(1); } } // // Reset with raw pointer and deleter // auto reset(T *pointer, const std::function& d) { reset(pointer); deleter = d; } // // Dtor // ~SharedPointer() { decrement_and_destroy(); } private: T* ptr; std::size_t* ref_count; std::function deleter; auto decrement_and_destroy() { if (ptr && 0 == --*ref_count) delete ref_count, deleter(ptr); else if (!ptr) delete ref_count; ref_count = nullptr; ptr = nullptr; } }; }//namespace ================================================ FILE: ch16/ex16.28/unique_pointer.h ================================================ #ifndef UNIQUE_POINTER_H #define UNIQUE_POINTER_H #include // forward declarations for friendship template class unique_pointer; template void swap(unique_pointer& lhs, unique_pointer& rhs); /** * @brief std::unique_ptr like class template. */ template class unique_pointer { friend void swap(unique_pointer& lhs, unique_pointer& rhs); public: // preventing copy and assignment unique_pointer(const unique_pointer&) = delete; unique_pointer& operator = (const unique_pointer&) = delete; // default constructor and one taking T* unique_pointer() = default; explicit unique_pointer(T* up): ptr(up) { } // move constructor unique_pointer(unique_pointer&& up) noexcept : ptr(up.ptr) { up.ptr = nullptr; } // move assignment unique_pointer& operator =(unique_pointer&& rhs) noexcept; // std::nullptr_t assignment unique_pointer& operator =(std::nullptr_t n) noexcept; // operator overloaded : * -> bool T& operator *() const { return *ptr; } T* operator ->() const { return & this->operator *(); } operator bool() const { return ptr ? true : false; } // return the underlying pointer T* get() const noexcept { return ptr; } // swap member using swap friend void swap(unique_pointer &rhs) { ::swap(*this, rhs); } // free and make it point to nullptr or to p's pointee. void reset() noexcept { deleter(ptr); ptr = nullptr; } void reset(T* p) noexcept { deleter(ptr); ptr = p; } // return ptr and make ptr point to nullptr. T* release(); ~unique_pointer() { deleter(ptr); } private: T* ptr = nullptr; D deleter = D(); }; // swap template inline void swap(unique_pointer& lhs, unique_pointer& rhs) { using std::swap; swap(lhs.ptr, rhs.ptr); swap(lhs.deleter, rhs.deleter); } // move assignment template inline unique_pointer& unique_pointer::operator =(unique_pointer&& rhs) noexcept { // prevent self-assignment if(this->ptr != rhs.ptr) { deleter(ptr); ptr = nullptr; ::swap(*this, rhs); } return *this; } // std::nullptr_t assignment template inline unique_pointer& unique_pointer::operator =(std::nullptr_t n) noexcept { if(n == nullptr) { deleter(ptr); ptr = nullptr; } return *this; } // relinquish contrul by returnning ptr and making ptr point to nullptr. template inline T* unique_pointer::release() { T* ret = ptr; ptr = nullptr; return ret; } #endif // UNIQUE_POINTER_H ================================================ FILE: ch16/ex16.29/Blob.h ================================================ /*************************************************************************** * @file Blob.h * @author Alan.W * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note 03 Feb 2014 Add a constructor that takes two iterators to Blob . * 06 Feb 2014 revise to use custome shared_ptr ***************************************************************************/ #ifndef BLOB_H #define BLOB_H #include #include #include "shared_pointer.h" /** * Blob * manage a vector through a shared_pointer. */ template class Blob { public: typedef T value_type; typedef typename std::vector::size_type size_type; // constructors Blob(); Blob(std::initializer_list il); // constructor taking two iterators template Blob(It b, It e); // number of elements in the Blob size_type size() const { return data->size(); } bool empty() const{ return data->empty(); } void push_back(const T& t) { data->push_back(t); } void push_back(T&& t) { data->push_back(std::move(t)); } void pop_back(); // element access T& back(); T& operator[](size_type i); const T& back() const; const T& operator [](size_type i) const; private: /** * @oldcode updated Feb 06, 2014 @Alan */ //std::shared_ptr> data; shared_pointer> data; // throw msg if data[i] isn't valid void check(size_type i, const std::string &msg) const; }; // default constructor template Blob::Blob() : data(std::make_shared>()) { } // constructor taking initializer_list template Blob::Blob(std::initializer_list il): data(std::make_shared>(il)){ } // constructor taking two iterators template //for class template //for this member Blob::Blob(It b, It e) : data(std::make_shared>(b, e)) { } template void Blob::check(size_type i, const std::string &msg) const { if(i >= data->size()) throw std::out_of_range(msg); } template T& Blob::back() { check(0, "back on empty Blob"); return data->back(); } template const T& Blob::back() const { check(0, "back on empty Blob"); return data->back(); } template T& Blob::operator [](size_type i) { // if i is too big, check function will throw, preventing access to a nonexistent element check(i, "subscript out of range"); return (*data)[i]; } template const T& Blob::operator [](size_type i) const { // if i is too big, check function will throw, preventing access to a nonexistent element check(i, "subscript out of range"); return (*data)[i]; } template void Blob::pop_back() { check(0, "pop_back on empty Blob"); data->pop_back(); } #endif // BLOB_H ================================================ FILE: ch16/ex16.29/DebugDelete.h ================================================ /*************************************************************************** * @file DebugDelete.h * @author Alan.W * @date 04 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ #ifndef DEBUGDELETE_H #define DEBUGDELETE_H #include /** * @brief The DebugDelete class is a deleter functor using delete */ class DebugDelete { public: DebugDelete(std::ostream& s = std::cerr) : os(s) { } template void operator() (T* p) const { os << "deleting ptr" << std::endl; delete p; } private: std::ostream& os; }; #endif // DEBUGDELETE_H ================================================ FILE: ch16/ex16.29/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 04 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.29: // Revise your Blob class to use your version of shared_ptr rather than the // library version. // #include #include #include #include #include "DebugDelete.h" #include "shared_pointer.h" #include "unique_pointer.h" #include "Blob.h" int main() { Blob b; b.push_back("sss"); b[0] = "zzzz"; std::cout << b[0] << "\n"; } ================================================ FILE: ch16/ex16.29/shared_pointer.h ================================================ /*************************************************************************** * @file shared_pointer.h * @author Alan.W * @date 04 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note 06 Feb 2014 ctor taking std::shared_ptr&& added ***************************************************************************/ #ifndef SHARED_POINTER_H #define SHARED_POINTER_H #include "DebugDelete.h" #include #include // forward declarations needed for friendship template class shared_pointer; template void swap(shared_pointer& lhs, shared_pointer& rhs); /** * @brief shared_ptr like template class * @note don't mix this one with std::shared_ptr. * DebugDelete is the default deleter which can be replaced at run time */ template class shared_pointer { friend void ::swap(shared_pointer& lhs, shared_pointer& rhs); // ^^^ -- don't forget this. public: // default constructor shared_pointer() = default; // constructor taking raw pointer. // set the refCount as 1 explicit shared_pointer(T* up, std::function d = DebugDelete()) : ptr(up), refCount(new std::size_t(1)), deleter(d) { } // ctor taking std::shared_ptr&& i.e. rvalue reference explicit shared_pointer(std::shared_ptr&& sp, std::function d = DebugDelete()); // copy constructor. // increment useCount for each Copy shared_pointer(const shared_pointer& sp): ptr(sp.ptr), refCount(sp.refCount), deleter(sp.deleter) { ++*refCount; } // move constructor shared_pointer(shared_pointer&& sp) noexcept; // copy assignment shared_pointer& operator =(const shared_pointer& rhs); // move assignment shared_pointer& operator =(shared_pointer&& rhs) noexcept; // conversion operator operator bool() const { return ptr ? true : false; } // dereference * arrow -> T& operator* () const { return *ptr; } T* operator->() const { return & this->operator *(); } // return useCount std::size_t use_count() const { return *refCount; } // get the underlying pointer T* get() const noexcept { return ptr; } // check if the unique user bool unique() const noexcept { return *refCount == 1; } // swap member void swap( shared_pointer& rhs) { ::swap(*this, rhs); } // if unique user, free the object pointed to void reset() noexcept { decrement_n_destroy(); } // make prt point where p pointing and create a new coount for it void reset(T* p); // reset to point where p is pointing and change deleter to d. void reset(T *p, const std::function& d) { reset(p); deleter = d; } ~shared_pointer() { decrement_n_destroy(); } private: T* ptr = nullptr; std::size_t* refCount = new std::size_t(0); // any functor lambda or function pointer that matched this // signature can replace the default one at run time. std::function deleter{ DebugDelete() }; // ^~~~~~~~~~~~~~^ // here: = doesn't work, another way is initializing it in constructor. // utilities void decrement_n_destroy(); }; /** * @brief swap and big 5 */ template inline void swap(shared_pointer& lhs, shared_pointer& rhs) { using std::swap; swap(lhs.ptr, rhs.ptr); swap(lhs.refCount, rhs.refCount); swap(lhs.deleter, rhs.deleter); } /** * @brief ctor taking std::shared_ptr&& i.e. rvalue reference * an interface between shared_pointer and std::shared_ptr. * only rvalue AND unique ownership can use this ctor. */ template inline shared_pointer::shared_pointer(std::shared_ptr&& sp, std::function d) { if(sp.unique()) *this = shared_pointer(new T(*sp), d); else throw std::runtime_error ("only unique and rvalue reference can transfer ownership--@Alan\n"); } // move constructor template inline shared_pointer::shared_pointer(shared_pointer&& sp) noexcept: ptr(sp.ptr), refCount(sp.refCount), deleter(std::move(sp.deleter)) { sp.ptr = nullptr; sp.refCount = nullptr; } // copy assignment template inline shared_pointer& shared_pointer::operator =(const shared_pointer& rhs) { // increment rhs.refCount first to ensure safty when self-assignment ++*rhs.refCount; // for lhs: decrement_n_destroy(); // copy datastructure from rhs to this. ptr = rhs.ptr; refCount = rhs.refCount; deleter = rhs.deleter; return *this; } // move assignment template inline shared_pointer& shared_pointer::operator =(shared_pointer&& rhs) noexcept { // for lhs decrement_n_destroy(); // swap two sides ::swap(*this, rhs); std::cout << "shared_pointer::move=\n"; return *this; } // make prt point where p pointing and create a new coount for it template inline void shared_pointer::reset(T* p) { if(ptr != p) { decrement_n_destroy(); ptr = p; refCount = new std::size_t(1); } } /** *@brief operators: << **/ template inline std::ostream& operator <<(std::ostream& os, shared_pointer p) { os << p.get(); return os; } // utilities for decrement and delete using deleter. template inline void shared_pointer::decrement_n_destroy() { if(ptr) { if (--*refCount == 0) { delete refCount; deleter(ptr); } refCount = nullptr; ptr = nullptr; } } #endif // SHARED_POINTER_H ================================================ FILE: ch16/ex16.29/unique_pointer.h ================================================ #ifndef UNIQUE_POINTER_H #define UNIQUE_POINTER_H #include "DebugDelete.h" // forward declarations for friendship template class unique_pointer; template void swap(unique_pointer& lhs, unique_pointer& rhs); /** * @brief std::unique_ptr like class template. */ template class unique_pointer { friend void swap(unique_pointer& lhs, unique_pointer& rhs); public: // preventing copy and assignment unique_pointer(const unique_pointer&) = delete; unique_pointer& operator = (const unique_pointer&) = delete; // default constructor and one taking T* unique_pointer() = default; explicit unique_pointer(T* up): ptr(up) { } // move constructor unique_pointer(unique_pointer&& up) noexcept : ptr(up.ptr) { up.ptr = nullptr; } // move assignment unique_pointer& operator =(unique_pointer&& rhs) noexcept; // std::nullptr_t assignment unique_pointer& operator =(std::nullptr_t n) noexcept; // operator overloaded : * -> bool T& operator *() const { return *ptr; } T* operator ->() const { return & this->operator *(); } operator bool() const { return ptr ? true : false; } // return the underlying pointer T* get() const noexcept { return ptr; } // swap member using swap friend void swap(unique_pointer &rhs) { ::swap(*this, rhs); } // free and make it point to nullptr or to p's pointee. void reset() noexcept { deleter(ptr); ptr = nullptr; } void reset(T* p) noexcept { deleter(ptr); ptr = p; } // return ptr and make ptr point to nullptr. T* release(); ~unique_pointer() { deleter(ptr); } private: T* ptr = nullptr; D deleter = D(); }; // swap template inline void swap(unique_pointer& lhs, unique_pointer& rhs) { using std::swap; swap(lhs.ptr, rhs.ptr); swap(lhs.deleter, rhs.deleter); } // move assignment template inline unique_pointer& unique_pointer::operator =(unique_pointer&& rhs) noexcept { // prevent self-assignment if(this->ptr != rhs.ptr) { deleter(ptr); ptr = nullptr; ::swap(*this, rhs); } return *this; } // std::nullptr_t assignment template inline unique_pointer& unique_pointer::operator =(std::nullptr_t n) noexcept { if(n == nullptr) { deleter(ptr); ptr = nullptr; } return *this; } // relinquish contrul by returnning ptr and making ptr point to nullptr. template inline T* unique_pointer::release() { T* ret = ptr; ptr = nullptr; return ret; } #endif // UNIQUE_POINTER_H ================================================ FILE: ch16/ex16.32.33.34.35.36/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang, Mugurell * @date 04 Feb 2014 * Aug, 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.32: // What happens during template argument deduction? // The process of determining the template arguments from the function arguments // is known as template argument deduction. During template argument deduction, // the compiler uses types of the arguments in the call to find the template // arguments that generate a version of the function that best matches the given // call. // // Exercise 16.33: // Name two type conversions allowed on function arguments involved in template // argument deduction. // • const conversions: A function parameter that is a reference (or pointer) // to a const can be passed a reference (or pointer) to a nonconst object // (§ 4.11.2, p. 162). // // • Array- or function-to-pointer conversions: If the function parameter is // not a reference type, then the normal pointer conversion will be applied // to arguments of array or function type. An array argument will be converted // to a pointer to its first element. Similarly, a function argument will be // converted to a pointer to the function’s type (§ 4.11.2, p. 161). // // Exercise 16.34: // Given only the following code, explain whether each of these calls is legal. // If so, what is the type of T? If not, why not? // template int compare(const T&, const T&); // compare("hi", "world"); // It didn't complie, as two types are different, the first type being char[3] , second char[6] // compare("bye", "dad"); // the type should be pointer to char i.e. char* // // Exercise 16.35: // Which, if any, of the following calls are errors? If the call is legal, what // is the type of T? If the call is not legal, what is the problem? // template T calc(T, int); // template T fcn(T, T); // double d; float f; char c; // (a) calc(c, 'c'); -- legal, T is a char // (b) calc(d, f); -- legal, T is a double // (c) fcn(c, 'c'); -- legal, T is a char // (d) fcn(d, f); -- illegal, arguments d and f are double and float repectively // // Exercise 16.36: // What happens in the following calls: // template f1(T, T); // template (int*, int*) // (b) f2(p1, p2); // f2(int*, int*) // (c) f1(cp1, cp2); // f1(int const*, int const*) // (d) f2(cp1, cp2); // f2(int const*, int const*) // (e) f1(p1, cp1); // deduced conflicting types for parameter 'T' // (f) f2(p1, cp1); // f2(int*, int const*) // int main() { return 0; } ================================================ FILE: ch16/ex16.37.38.39/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.37: // The library max function has two function parameters and returns the // larger of its arguments. This function has one template type parameter. // Could you call max passing it an int and a double? If so, how? If not, // why not? // Yes, just offer an explicit template argument, like: // int a = 1; // double b = 2; // std::max(a, b); // // Exercise 16.38: // When we call make_shared (§ 12.1.1, p. 451), we have to provide an // explicit template argument. Explain why that argument is needed and // how it is used. // // without specified type given, make_shared has no possibility to // to determine how big the size it should allocate, which is the reason. // // Depending on the type specified, make_shared allocates proper size of memory // space and returns a proper type of shared_ptr pointing to it. // // Exercise 16.39: // Use an explicit template argument to make it sensible to pass two string // literals to the original version of compare from § 16.1.1 (p. 652). // #include template int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } int main() { std::cout << compare("sss","aaa") << "\n"; } ================================================ FILE: ch16/ex16.4/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 02 Feb 2014 * 18 Jun 2015 * Nov 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.4: // Write a template that acts like the library find algorithm. The function // will need two template type parameters, one to represent the function’s // iterator parameters and the other for the type of the value. Use your // function to find a given value in a vector and in a list. // #include #include #include #include namespace ch16 { template auto find(Iterator first, Iterator last, Value const& value) { for (; first != last && *first != value; ++first); return first; } } int main() { std::vector v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; auto is_in_vector = v.cend() != ch16::find(v.cbegin(), v.cend(), 5); std::cout << (is_in_vector ? "found\n" : "not found\n"); std::list l = { "aa", "bb", "cc", "dd", "ee", "ff", "gg" }; auto is_in_list = l.cend() != ch16::find(l.cbegin(), l.cend(), "zz"); std::cout << (is_in_list ? "found\n" : "not found\n"); return 0; } ================================================ FILE: ch16/ex16.40/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.40: // Is the following function legal? If not, why not? If it is legal, what, // if any, are the restrictions on the argument type(s) that can be passed, // and what is the return type? // legal. // As shown below, only type that support this + 0 operation can be passed. // the return type depends on the what type the operator + returns. In the case // below, the return type is Bar. // #include #include #include class Bar{ }; Bar operator +(Bar lhs, int) { return lhs; } template auto fcn3(It beg, It end) -> decltype(*beg + 0) { return *beg; // return a copy of an element from the range } int main() { std::vector v; v.push_back(Bar()); Bar b = fcn3(v.begin(), v.end()); ; ; } ================================================ FILE: ch16/ex16.41/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.41: // Write a version of sum with a return type that is guaranteed to be large // enough to hold the result of the addition. // #include #include #include template auto sum(T lhs, T rhs) -> decltype( lhs + rhs) { return lhs + rhs; } int main() { auto s= sum(123456789123456789123456789123456789123456789, 123456789123456789123456789123456789123456789) ; ; ; } ================================================ FILE: ch16/ex16.42.43.44.45.46/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.42: // Determine the type of T and of val in each of the following calls: // template void g(T&& val); // int i = 0; const int ci = i; // (a) g(i); // since i is lvalue, T is deduced as int&, val is int& && collapsing to int& // (b) g(ci); // since ci is lvaue, T is deduced as const int&, val is const int& && collapsing to const int& // (c) g(i * ci); // since i * ci is rvalue, T is deduced as int, val is int&& && colapsing to int&& // // Exercise 16.43: // Using the function defined in the previous exercise, what would the template // parameter of g be if we called g(i = ci)? // (i = ci) returns lvalue refering to the object i. // Hence T is deduced as int& val is int& && . // any change on val changes the object i. // // Exercise 16.44: // Using the same three calls as in the first exercise, determine the types for T // if g’s function parameter is declared as T (not T&&). // ^ // g(i); -- T is deduced as int // g(ci); -- T is deduced as int, const is ignored. // g(i * ci); -- T is deduced as int, (i * ci) returns rvalue which is copied to // T // What if g’s function parameter is const T&? // ^^^^^^^^ // g(i) -- T is deduced as int , val : const int& // g(ci) -- T is deduced as int , val : const int& // g(i * ci) -- T is deduced as int , val : const int&(see example on page 687) // // Exercise 16.45: // Given the following template, explain what happens if we call g on a literal value // such as 42. What if we call g on a variable of type int? // template void g(T&& val) { vector v; } // // Discussion on SO: // http://stackoverflow.com/questions/21624016/when-a-lvalue-is-passed-to-t-what-will-happen // // relevant section from textbook: // When we pass an lvalue (e.g., i) to a function parameter that is an rvalue reference to a // template type parameter (e.g, T&&), the compiler deduces the template type parameter as the // argument’s lvalue reference type. So, when we call f3(i), the compiler deduces the type of // T as int&, not int. // -- P.688 // // In this case, when calling on a literal value, say 42. int&& && will collapse to int&&. At last // T is deduced as int. Hence std::vector is instantiated as std::vector which is legal. // // When calling on a variable int. T will be deduced as int&. int & && will collapse to int&. // std::vector is not legal. The reason why int& can't be element of a vector can be found at: // http://stackoverflow.com/questions/922360/why-cant-i-make-a-vector-of-references // // Exercise 16.46: // Explain this loop from StrVec::reallocate in § 13.5 (p. 530): // // for (size_t i = 0; i != size(); ++i) // alloc.construct(dest++, std::move(*elem++)); // // In each iteration, the dereference operator * returns a lvalue which is changed to rvalue by // std::move , becasue the member function construct takes rvalue reference rather than lvalue // reference. // #include #include #include template void g(T&& val) { std::vector v; } int main() { std::allocator a; a.construct } ================================================ FILE: ch16/ex16.47/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.47: // Write your own version of the flip function // and test it by calling functions that have lvalue and rvalue reference parameters. // #include #include void func_lvalue(std::string& lhs, std::string& rhs) { lhs = "Wang\n"; rhs = "Alan\n"; } void func_rvalue(int&& lhs, int&& rhs) { // allocate enough space std::allocator alloc; int* data(alloc.allocate(3)); // move into the spaced newly allocated alloc.construct(data , lhs); alloc.construct(data +1 , 0); alloc.construct(data +2 , rhs); // print for (auto p = data; p != data + 3; ++p) std::cout << *p << "\n"; // destroy and deallocation for (auto p = data +3; p != data; ) alloc.destroy(--p); alloc.deallocate(data, 3); } template void flip(F f, T1&& t1, T2&& t2) { f(std::forward(t2), std::forward(t1)); } int main() { // test for lvalue reference /* std::string s1, s2; flip(func_lvalue, s1, s2); std::cout << s1 << s2; */ // test for rvalue reference flip(func_rvalue, 99, 88); } ================================================ FILE: ch16/ex16.48/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.48: // Write your own versions of the debug_rep functions. // #include #include #include // always declare first: template std::string debug_rep(const T& t); template std::string debug_rep(T* p); std::string debug_rep(const std::string &s); std::string debug_rep(char* p); std::string debug_rep(const char *p); // print any type we don't otherwise. template std::string debug_rep(const T& t) { std::ostringstream ret; ret << t; return ret.str(); } // print pointers as their pointer value, followed by the object to which the pointer points template std::string debug_rep(T* p) { std::ostringstream ret; ret << "pointer: " << p; if(p) ret << " " << debug_rep(*p); else ret << " null pointer"; return ret.str(); } // non-template version std::string debug_rep(const std::string &s) { return '"' + s + '"'; } // convert the character pointers to string and call the string version of debug_rep std::string debug_rep(char *p) { return debug_rep(std::string(p)); } std::string debug_rep(const char *p) { return debug_rep(std::string(p)); } int main() { } ================================================ FILE: ch16/ex16.49.50/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 07 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.49: // Explain what happens in each of the following calls: // // Exercise 16.50: // Define the functions from the previous exercise so that they print an // identifying message. Run the code from that exercise. If the calls behave // differently from what you expected, make sure you understand why. // #include #include #include template void f(T) { std::cout << "f(T)\n"; } template void f(const T*) { std::cout << "f(const T*)\n"; } template void g(T) { std::cout << "template void g(T)\n"; } template void g(T*) { std::cout << "template void g(T*)\n"; } int main() { int i = 42, *p = &i; const int ci = 0, *p2 = &ci; //g(42); //template void g(T ); --is called //g(p); //template void g(T*); --is called //g(ci); //template void g(T) --is called //g(p2); //template void g(T*) --is called //f(42); //f(T) //f(p); //f(T) //f(ci); //f(T) f(p2); //f(const T*) } ================================================ FILE: ch16/ex16.5/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 02 Feb 2014 * 13 Oct 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.5: // Write a template version of the print function from § 6.2.4 (p. 217) that // takes a reference to an array and can handle arrays of any size and any // element type. // #include #include template void print(Arr const& arr) { for (auto const& elem : arr) std::cout << elem << std::endl; } int main() { std::string s[] = { "ssss", "aaa", "ssssss" }; char c[] = { 'a', 'b', 'c', 'd' }; int i[] = { 1 }; print(i); print(c); print(s); return 0; } ================================================ FILE: ch16/ex16.51.52/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 12 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.51: // Determine what sizeof...(Args) and sizeof...(rest) return for each call to foo in this section. // // Exercise 16.52: // Write a program to check your answer to the previous question. // #include template void foo(T t, Args ...args) { std::cout << sizeof...(Args) << std::endl; std::cout << sizeof...(args) << std::endl; } int main() { foo(1, 2); foo(1, 23, 4, 5, 6); } ================================================ FILE: ch16/ex16.53.54.55/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 16 Feb 2014 Aug, 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.53: // Write your own version of the print functions and test them by printing // one, two, and five arguments, each of which should have different types. // // Exercise 16.54: // What happens if we call print on a type that doesn’t have an << operator? // It didn't compile. // // Exercise 16.55: // Explain how the variadic version of print would execute if we declared // the nonvariadic version of print after the definition of the variadic // version. // error: no matching function for call to 'print(std::ostream&)' // #include // trivial case template std::ostream& print(std::ostream& os, Printable const& printable) { return os << printable; } // recursion template std::ostream& print(std::ostream& os, Printable const& printable, Args const&... rest) { return print(os << printable << ", ", rest...); } int main() { print(std::cout, 1) << std::endl; print(std::cout, 1, 2) << std::endl; print(std::cout, 1, 2, 3, 4, "sss", 42.4242) << std::endl; return 0; } ================================================ FILE: ch16/ex16.56.57/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 16 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.56: // Write and test a variadic version of errorMsg. // // Exercise 16.57: // Compare your variadic version of errorMsg to the error_msg function in // § 6.2.6 (p. 220). What are the advantages and disadvantages of each // approach? // The error_msg takes initializer_list as the argument. So only the elements // stored in it must be the same or at least convertible. In contrast, the variadic // version provides better flexibility. // #include #include #include // always declare first: template std::string debug_rep(const T& t); template std::string debug_rep(T* p); std::string debug_rep(const std::string &s); std::string debug_rep(char* p); std::string debug_rep(const char *p); // print any type we don't otherwise. template std::string debug_rep(const T& t) { std::ostringstream ret; ret << t; return ret.str(); } // print pointers as their pointer value, followed by the object to which the pointer points template std::string debug_rep(T* p) { std::ostringstream ret; ret << "pointer: " << p; if (p) ret << " " << debug_rep(*p); else ret << " null pointer"; return ret.str(); } // non-template version std::string debug_rep(const std::string &s) { return '"' + s + '"'; } // convert the character pointers to string and call the string version of debug_rep std::string debug_rep(char *p) { return debug_rep(std::string(p)); } std::string debug_rep(const char *p) { return debug_rep(std::string(p)); } // function to end the recursion and print the last element // this function must be declared before the variadic version of // print is defined template std::ostream& print(std::ostream& os, const T& t) { return os << t; // ^ // note: no seperator after the last element in the pack } // this version of print will be called for all but the last element in the pack template std::ostream& print(std::ostream &os, const T &t, const Args&... rest) { // print the first argument os << t << ","; // recursive call; print the other arguments return print(os, rest...); } // call debug_rep on each argument in the call to print template std::ostream& errorMsg(std::ostream& os, const Args... rest) { return print(os, debug_rep(rest)...); } int main() { errorMsg(std::cout, 1, 2, 3, 4, 9.0f, "sss", "alan"); return 0; } ================================================ FILE: ch16/ex16.58.59/strvec.cpp ================================================ /*************************************************************************** * @file strvec.cpp * @author Alan.W * @date 05 JAN 2014 * @remark 16 Feb 2014 : emplace_back added ***************************************************************************/ #include "strvec.h" #include // copy constructor StrVec::StrVec(const StrVec &s) { /** * @brief newData is a pair of pointers pointing to newly allocated and copied * range : [b, e) */ std::pair newData = alloc_n_copy(s.begin(), s.end()); element = newData.first; first_free = cap = newData.second; } /** * @brief constructor taking initializer_list * for ex 13.40 * @param l */ StrVec::StrVec(std::initializer_list l) { // allocate memory as large as l.size() std::string * const newData = alloc.allocate(l.size()); // copy elements from l to the address allocated auto p = newData; for(const auto &s : l) alloc.construct(p++, s); // build the data structure element = newData; first_free = cap = element + l.size(); } // operator = StrVec& StrVec::operator =(const StrVec& rhs) { // allocate and copy first to protect against self-assignment std::pair newData = alloc_n_copy(rhs.begin(), rhs.end()); // destroy and deallocate free(); element = newData.first; first_free = cap = newData.second; return *this; } // destructor StrVec::~StrVec() { free(); } /** * @brief allocate new room if nessary and push back the new string * @param s new string */ void StrVec::push_back(const std::string& s) { chk_n_alloc(); alloc.construct(first_free++, s); } /** * @brief preallocate enough memory for specified number of elements * @param n number of elements required * @note this function is implemented refering to StrVec::reallocate(). */ void StrVec::reserve(std::size_t n) { // if the n is too small, just ignore it. if(n <= capacity()) return; // allocate and move old ones into the new address. wy_alloc_n_move(n); } /** * @brief Resizes to the specified number of elements. * @param n Number of elements the %vector should contain. * * This function will resize it to the specified * number of elements. If the number is smaller than the * current size it is truncated, otherwise * default constructed elements are appended. */ void StrVec::resize(std::size_t n) { resize(n, std::string()); } /** * @brief Resizes it to the specified number of elements. * @param __new_size Number of elements it should contain. * @param __x Data with which new elements should be populated. * * This function will resize it to the specified * number of elements. If the number is smaller than the * current size the it is truncated, otherwise * the it is extended and new elements are populated with * given data. */ void StrVec::resize(std::size_t n, const std::string &s) { if(n < size()) { // destroy the range : [element+n, first_free) using destructor for(auto p = element + n; p != first_free; /* empty */) alloc.destroy(p++); // move frist_free point to the new address element + n first_free = element + n; } else if( n > size() ) { for(auto i = size(); i != n; ++i) push_back(std::string(s)); } } /** * @brief Double the capacity and using std::move move the original strings to the newly * allocated memory */ void StrVec::reallocate() { // calculate the new capacity required. std::size_t newCapacity = size() ? 2 * size() : 1; // allocate and move old ones into the new address. wy_alloc_n_move(newCapacity); } /** * @brief allocate new space for the given range and copy them into it * @param b * @param e * @return a pair of pointers pointing to [first element , one past the last) in the new space */ std::pair StrVec::alloc_n_copy(std::string *b, std::string *e) { // calculate the size needed and allocate space accordingly std::string* data = alloc.allocate(e - b); return { data, std::uninitialized_copy(b, e, data) }; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // which copies the range [first, last) into the space of which // the starting address p_data is pointing to. // This function returns a pointer pointing to one past the last element. } /** * @brief destroy the elements and deallocate the space previously allocated. */ void StrVec::free() { if(element) // if not nullptr { // destory it in reverse order. for(auto p = first_free; p != element; /* empty */) alloc.destroy(--p); alloc.deallocate(element, capacity()); } } /** * @brief allocate memory for spicified number of elements * @param n * @note it's user's responsibility to ensure that @param n is greater than * the current capacity. */ void StrVec::wy_alloc_n_move(std::size_t n) { std::size_t newCapacity = n; std::string* newData = alloc.allocate(newCapacity); std::string* dest = newData; std::string* elem = element; // move the old to newly allocated space. for(std::size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); // update data structure element = newData; first_free = dest; cap = element + newCapacity; } ================================================ FILE: ch16/ex16.58.59/strvec.h ================================================ /*************************************************************************** * @file strvec.h * @author Alan.W * @date 05 JAN 2014 * @remark 16 Feb 2014 : emplace_back added ***************************************************************************/ #ifndef STRVEC_H #define STRVEC_H #include class StrVec { public: // Big 3/5. StrVec(): element(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(std::initializer_list l); StrVec(const StrVec& s); StrVec& operator =(const StrVec& rhs); ~StrVec(); // public members void push_back(const std::string &s); // a variadic member template using its argumenst to construct // an element directly in space managed by the constainer template void emplace_back(Args&&...); std::size_t size() const { return first_free - element; } std::size_t capacity() const { return cap - element; } std::string* begin() const { return element; } std::string* end() const { return first_free; } // preallocate enough memory for specified number of elements void reserve(std::size_t n); // resize as required. void resize(std::size_t n); void resize(std::size_t n, const std::string& s); private: // data members std::string* element; // pointer to the first element std::string* first_free; // pointer to the first free element std::string* cap; // pointer to one past the end std::allocator alloc; // utilities for Big 3/5 void reallocate(); void chk_n_alloc() { if (size() == capacity()) reallocate(); } void free(); // utilities added // used in reallocate() reserve() and resize(). void wy_alloc_n_move(std::size_t n); std::pair alloc_n_copy (std::string* b, std::string* e); }; // call the constructors of the type to construct this element // and push it back. template inline void StrVec::emplace_back(Args&&... args) { // reallocate if necessary chk_n_alloc(); alloc.construct(first_free++, std::forward(args)...); } #endif // STRVEC_H ================================================ FILE: ch16/ex16.58.59/vec.h ================================================ /*************************************************************************** * @file vec.h * @author Alan.W * @date 02 FEB 2014 * @remark 16 Feb 2014 : emplace_back added ***************************************************************************/ #ifndef VEC_H #define VEC_H #include /** * @brief a vector like class */ template class Vec { public: Vec():element(nullptr), first_free(nullptr), cap(nullptr){ } Vec(std::initializer_list l); Vec(const Vec& v); Vec& operator =(const Vec& rhs); ~Vec(); // memmbers void push_back(const T& t); template void emplace_back(Args&&...); std::size_t size() const { return first_free - element; } std::size_t capacity()const { return cap - element; } T* begin() const { return element; } T* end() const { return first_free; } void reserve(std::size_t n); void resize(std::size_t n); void resize(std::size_t n, const T& t); private: // data members T* element; T* first_free; T* cap; std::allocator alloc; // utillities void reallocate(); void chk_n_alloc() { if(size()==capacity()) reallocate(); } void free(); void wy_alloc_n_move(std::size_t n); std::pair alloc_n_copy(T* b, T* e); }; // copy constructor template Vec::Vec(const Vec &v) { /** * @brief newData is a pair of pointers pointing to newly allocated and copied * from range : [b, e) */ std::pair newData = alloc_n_copy(v.begin(), v.end()); element = newData.first; first_free = cap = newData.second; } // constructor that takes initializer_list template Vec::Vec(std::initializer_list l) { // allocate memory as large as l.size() T* const newData = alloc.allocate(l.size()); // copy elements from l to the address allocated T* p = newData; for(const auto &t : l) alloc.construct(p++, t); // build data structure element = newData; first_free = cap = element + l.size(); } // operator = template Vec& Vec::operator =(const Vec& rhs) { // allocate and copy first to protect against self_assignment std::pair newData = alloc_n_copy(rhs.begin(), rhs.end()); // destroy and deallocate free(); // update data structure element = newData.first; first_free = cap = newData.second; return *this; } // destructor template Vec::~Vec() { free(); } /** * @brief allocate new memeory if nessary and push back the new T * @param t new T */ template void Vec::push_back(const T &t) { chk_n_alloc(); alloc.construct(first_free++, t); } template //for the class template template //for the member template inline void Vec::emplace_back(Args&&...args) { chk_n_alloc(); alloc.construct(first_free++, std::forward(args)...); } /** * @brief preallocate enough memory for specified number of elements * @param n number of elements required */ template void Vec::reserve(std::size_t n) { // if n too small, just return without doing anything if(n <= capacity()) return; // allocate new memory and move data from old address to the new one wy_alloc_n_move(n); } /** * @brief Resizes to the specified number of elements. * @param n Number of elements the %vector should contain. * * This function will resize it to the specified * number of elements. If the number is smaller than the * current size it is truncated, otherwise * default constructed elements are appended. */ template void Vec::resize(std::size_t n) { resize(n, T()); } /** * @brief Resizes it to the specified number of elements. * @param __new_size Number of elements it should contain. * @param __x Data with which new elements should be populated. * * This function will resize it to the specified * number of elements. If the number is smaller than the * current size the it is truncated, otherwise * the it is extended and new elements are populated with * given data. */ template void Vec::resize(std::size_t n, const T &t) { if(n < size()) { // destroy the range [element+n, first_free) using destructor for(auto p = element + n; p != first_free; ) alloc.destroy(p++); // update first_free to point to the new address first_free = element + n; } else if(n > size()) { for (auto i = size(); i != n; ++i) push_back(t); } } /** * @brief allocate new space for the given range and copy them into it * @param b * @param e * @return a pair of pointers pointing to [first element , one past the last) in the new space */ template std::pair Vec::alloc_n_copy(T *b, T *e) { // calculate the size needed and allocate space accordingly T* data = alloc.allocate(e-b); return { data, std::uninitialized_copy(b, e, data) }; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // which copies the range[first, last) to the space to which // the starting address data is pointing. // This function returns a pointer to one past the last element } /** * @brief destroy the elements and deallocate the space previously allocated. */ template void Vec::free() { // if not nullptr if(element) { // destroy it in reverse order. for(auto p = first_free; p != element; ) alloc.destroy(--p); alloc.deallocate(element, capacity()); } } /** * @brief allocate memory for spicified number of elements * @param n * @note it's user's responsibility to ensure that @param n is greater than * the current capacity. */ template void Vec::wy_alloc_n_move(std::size_t n) { // allocate as required. std::size_t newCapacity = n; T* newData = alloc.allocate(newCapacity); // move the data from old place to the new one T* dest = newData; T* old = element; for(std::size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*old++)); free(); // update data structure element = newData; first_free = dest; cap = element + newCapacity; } /** * @brief Double the capacity and using std::move move the original data to the newly * allocated memory */ template void Vec::reallocate() { // calculate the new capacity required std::size_t newCapacity = size() ? 2 * size() : 1; // allocate and move old data to the new space wy_alloc_n_move(newCapacity); } #endif // VEC_H ================================================ FILE: ch16/ex16.6/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.6: // How do you think the library begin and end functions that take an array // argument work? // std::begin is a template function that takes a reference to an array.It // returns this reference as the iterator pointing to the first element in // this array. // // std::end is a template function that takes a reference to an array and // capture the size. It return this reference plus the size as the iterator pointing // to one past last element // // Define your own versions of these functions. // #include #include #include #include // the same as std::begin template T* begin_def(T(&arr)[size]) { return arr; } // the same as std::end template T* end_def(T (&arr)[size]) ^^//We usually don't use a function name which is the same as the function of standard libary ^^ //This should not be const { return arr + size; } int main() { std::string s[] = { "sssss","ss","ss","ssssszzzz" }; std::cout << *(begin_def(s)+1) << std::endl; std::cout << *(end_def(s) - 1) << std::endl; return 0; } ================================================ FILE: ch16/ex16.60.61/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 26 Feb 2014 * Aug 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.60: // Explain how make_shared (§ 12.1.1, p. 451) works. // // make_shared shoudl be a variadic template function that forwards all arguments to // underlying constructors that allocate and initializes an object in dynamic memory and // , at last, build a shared_ptr by wrapping the raw pointer. // // Exercise 16.61: // Define your own version of make_shared. // #include #include #include namespace ch16 //to differ from std::make_shared { template auto make_shared(Args&&... args) -> std::shared_ptr { return std::shared_ptr(new T(std::forward(args)...)); } } struct Foo { explicit Foo(int b) : bar(b){ } int bar; }; int main() { auto num = ch16::make_shared(42); std::cout << *num << std::endl; auto str = ch16::make_shared(10, 'c'); std::cout << *str << std::endl; auto foo = ch16::make_shared(99); std::cout << foo->bar << std::endl; return 0; } ================================================ FILE: ch16/ex16.62/Sales_data.cc ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced.Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ #include "Sales_data.h" #include using std::istream; using std::ostream; Sales_data::Sales_data(istream &is) { is >> *this; // read a transaction from is into this object } double Sales_data::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } // member binary operator: left-hand operand is bound to the implicit this pointer // assumes that both objects refer to the same book Sales_data& Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // assumes that both objects refer to the same book Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; // copy data members from lhs into sum sum += rhs; // add rhs into sum return sum; } istream &operator>>(istream &is, Sales_data &item) { double price; // no need to initialize; we'll read into price before we use it is >> item.bookNo >> item.units_sold >> price; if (is) // check that the inputs succeeded item.revenue = item.units_sold * price; else item = Sales_data(); // input failed: give the object the default state return is; } ostream &operator<<(ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } // operators replace these original named functions istream &read(istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } ostream &print(ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; // copy data members from lhs into sum sum += rhs; // add rhs into sum return sum; } ================================================ FILE: ch16/ex16.62/Sales_data.h ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced.Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ #ifndef SALES_DATA_H #define SALES_DATA_H #include #include // unchanged from ch14 except for added friend declaration for hash. class Sales_data { friend std::hash; friend std::ostream &operator<< (std::ostream&, const Sales_data&); friend std::istream &operator>>(std::istream&, Sales_data&); friend bool operator==(const Sales_data &, const Sales_data &); friend std::ostream &print(std::ostream&, const Sales_data&); friend std::istream &read(std::istream&, Sales_data&); public: // constructors Sales_data() = default; Sales_data(const std::string &s): bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p*n) { } Sales_data(std::istream &); std::string isbn() const { return bookNo; } Sales_data& operator+=(const Sales_data&); private: double avg_price() const; std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; // non-member Sales_data operations inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs) { return lhs.isbn() < rhs.isbn(); } inline bool operator==(const Sales_data &lhs, const Sales_data &rhs) { return lhs.isbn() == rhs.isbn() && lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue; } inline bool operator!=(const Sales_data &lhs, const Sales_data &rhs) { return !(lhs == rhs); } // old versions Sales_data add(const Sales_data&, const Sales_data&); std::ostream &print(std::ostream&, const Sales_data&); std::istream &read(std::istream&, Sales_data&); // new operator functions Sales_data operator+(const Sales_data&, const Sales_data&); std::ostream &operator<<(std::ostream&, const Sales_data&); std::istream &operator>>(std::istream&, Sales_data&); // specialize std::hash // note : template specialization should be put in the header! namespace std { template<> struct hash { typedef size_t result_type; typedef Sales_data argument_type; size_t operator()(const Sales_data& s) const { return hash()(s.bookNo) ^ hash()(s.units_sold) ^ hash()(s.revenue); } }; } //std #endif ================================================ FILE: ch16/ex16.62/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 2 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.62: // Define your own version of hash and define an unordered_multiset of Sales_data // objects. Put several transactions into the container and print its contents. // // keep all template codes in the header. // #include #include #include #include "Sales_data.h" int main() { // test for ex16.62 std::unordered_multiset mset; Sales_data sd("Bible", 10, 0.98); mset.emplace(sd); mset.emplace("C++ Primer", 5, 9.99); for(const auto &item : mset) std::cout << "the hash code of " << item.isbn() <<":\n0x" << std::hex << std::hash()(item) << "\n"; return 0; } ================================================ FILE: ch16/ex16.63.64/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 3 Mar 2014 * Aug, 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.63: // Define a function template to count the number of occurrences of a given // value in a vector. // Test your program by passing it a vector of doubles, a vector of ints, // and a vector of strings. // // Exercise 16.64: // Write a specialized version of the template from the previous exercise to // handle vector and a program that uses this specialization. // #include #include #include // template template std::size_t count(std::vector const& vec, T value) { auto count = 0u; for(auto const& elem : vec) if(value == elem) ++count; return count; } // template specialization template<> std::size_t count (std::vector const& vec, const char* value) { auto count = 0u; for(auto const& elem : vec) if(0 == strcmp(value, elem)) ++count; return count; } int main() { // for ex16.63 std::vector vd = { 1.1, 1.1, 2.3, 4 }; std::cout << count(vd, 1.1) << std::endl; // for ex16.64 std::vector vcc = { "alan", "alan", "alan", "alan", "moophy" }; std::cout << count(vcc, "alan") << std::endl; return 0; } ================================================ FILE: ch16/ex16.65.66.67/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 3 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.65: // In § 16.3 (p. 698) we defined overloaded two versions of debug_rep one // had a const char* and the other a char* parameter. Rewrite these functions // as specializations. // // Exercise 16.66: // What are the advantages and disadvantages of overloading // these debug_rep functions as compared to defining specializations? // // Overloading changes the function match. // // Exercise 16.67: // Would defining these specializations affect function matching for debug_rep? // If so, how? If not, why not? // // Won't change. // Specializations instantiate a template; they do not overload it. As a result, // specializations do not affect function matching. // #include #include #include #include // template template std::string debug_rep(T* t); // template specialization T=const char* , char* respectively. template<> std::string debug_rep(const char* str); template<> std::string debug_rep( char *str); int main() { char p[] = "alan"; std::cout << debug_rep(p) << "\n"; return 0; } template std::string debug_rep(T* t) { std::ostringstream ret; ret << t; return ret.str(); } // template specialization // T = const char* template<> std::string debug_rep(const char* str) { std::string ret(str); return str; } // template specialization // T = char* template<> std::string debug_rep( char *str) { std::string ret(str); return ret; } ================================================ FILE: ch16/ex16.7.8/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.7: // Write a constexpr template that returns the size of a given array. // // Exercise 16.8: // In the “Key Concept” box on page 108, we noted that as a matter of habit // C++ programmers prefer using != to using <. Explain the rationale for // this habit. // // The reason is that more class defines "!=" rather than "<". Doing so can // reduce the number of requirement of the class used with a template class. // #include #include #include #include template constexpr unsigned getSize(const T(&)[size]) { return size; } int main() { std::string s[] = { "sss" }; std::cout << getSize(s) << std::endl; char c[] = "s"; std::cout << getSize(c) << std::endl; // the output is 2, as '\0' is added at the end of the array return 0; } ================================================ FILE: ch16/ex16.9.10.11/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 02 Feb 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 16.9: // What is a function template? What is a class template? // A function template is a formula from which we can generate type-specific // versions of that function. // // A class template is a blueprint for generating classes. Class templates // differ from function templates in that the compiler cannot deduce the // template parameter type(s) for a class template. Instead, as we’ve seen // many times, to use a class template we must supply additional information // inside angle brackets following the template’s name (§ 3.3, p. 97). // // Exercise 16.10: // What happens when a class template is instantiated? // The compiler uses these template arguments to instantiate a specific // class from the template. // // Exercise 16.11: // The following definition of List is incorrect. How would you fix it? // as commented below. // template class ListItem; template class List { public: List(); List(const List &); List& operator=(const List &); ~List(); void insert(ListItem *ptr, elemType value); // ^^^^^^^^^^ -- template is not a type, the type must be provided private: ListItem *front, *end; // ^^^^^^^^ -- template is not a type, the type must be provided }; int main() { return 0; } ================================================ FILE: ch17/17_24.cpp ================================================ //exercise 17.24 //Write your own version of the program to reformat phone numbers. #include #include #include using namespace std; string pattern = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})"; string format = "$2.$5.$7"; regex r(pattern); string s; int main() { while(getline(cin,s)) { cout< #include #include using namespace std; string pattern = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})"; string fmt = "$2.$5.$7"; regex r(pattern); string s; int main() { while(getline(cin,s)) { smatch result; regex_search(s,result,r); if(!result.empty()) { cout< #include #include using namespace std; string pattern = "(\\d{5})([.- ])?(\\d{4})"; string fmt = "$1-$3"; regex r(pattern); string s; int main() { while(getline(cin,s)) { smatch result; regex_search(s,result, r); if(!result.empty()) { cout< #include #include using namespace std; int main() { cout <<"default format: " << 100 * sqrt(2.0) << '\n' << "scientific: " << scientific << 100 * sqrt(2.0) << '\n' << "fixed decimal: " << fixed << 100 * sqrt(2.0) << '\n' << "hexidecimal: " << uppercase << hexfloat << 100 * sqrt(2.0) << '\n' << "use defaults: " << defaultfloat << 100 * sqrt(2.0) << "\n\n"; } //17.36 //Modify the program from the previous exercise to print the various floating-point values so that they line up in a column. #include #include #include using namespace std; int main() { cout < #include #include using namespace std; //int main () { // ifstream myfile("F:\\Git\\Cpp-Primer\\ch17\\17_37_38\\test.txt"); // if (myfile) cout << 1 << endl; // char sink [250]; // // while(myfile.getline(sink,250)) // { // cout << sink << endl; // } // return 0; //} //17.38 //Extend your program from the previous exercise to print each word you read onto its own line. //#include //#include //#include // //using namespace std; // //int main () { // ifstream myfile ("F:\\Git\\Cpp-Primer\\ch17\\17_37_38\\test.txt"); // char sink [250]; // // while(myfile.getline(sink,250,' ')) // { // cout << sink << endl; // } // return 0; //} int main() { std::cout << "Standard Output!\n"; std::cerr << "Standard Error!\n"; std::clog << "Standard Log??\n"; } ================================================ FILE: ch17/17_37_38/test.txt ================================================ I'm expressin' with my full capabilities, And now I'm livin' in correctional facilities, Cause some don't agree with how I do this. I get straight, meditate like a Buddhist I'm droppin' flava, my behaviour is heriditery, But my technique is very necessary. Blame it on Ice Cube... Because he says it gets funky When you got a subject and a predacit. Add it on a dope beat And that'll make you think. Some suckaz just tickle me pink To my stomache. 'Cause they don't flow like this one. You know what? I won't hesitate to dis one Or two before I'm through. So don't try to sing this! Some drop science While I'm droppin' English. Even if Yella Makes it a-capella I still express, yo, I don't smoke weed or a sess. Cause its known to give a brother brain damage. And brain damage on the mic don't manage Nuthin' But makin' a sucker and you equal. Don't be another sequel... ================================================ FILE: ch17/data/record.txt ================================================ green (9998886666 9996668888 morgan 2015552368 8625550123 drew 9735550130 lee 6095550132 2015550175 8005550000 ================================================ FILE: ch17/ex17.3/main.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 3 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.3: // Rewrite the TextQuery programs from § 12.3 (p. 484) to use a tuple instead // of the QueryResult class. Explain which design you think is better and why. // // The orignal way is more formal.The second way is quick to implement , but hard to // refactor.So the second way is better for testing. // #include #include "textquery.h" int main() { return 0; } ================================================ FILE: ch17/ex17.3/textquery.cpp ================================================ /*************************************************************************** * @file textqueryr.cpp * @author Alan.W * @date 29 DEC 2013 * @remark ***************************************************************************/ #include "textquery.h" #include "queryresult.h" #include #include #include // Constructor TextQuery::TextQuery(std::ifstream & is) : file(new std::vector) { // each line std::string line; while(std::getline(is, line)) { file->push_back(line); // current line index int index = file->size() - 1; // for each word std::stringstream lineSteam(line); std::string word; while(lineSteam >> word) { // fetch the smart pointer which is null when the word first time seen std::shared_ptr>& sp_lineIndex = wm[word]; // if null, allcate a new set to contain line indices if(!sp_lineIndex) sp_lineIndex.reset(new std::set); // insert sp_lineIndex->insert(index); } } } /** * @brief do a query opertion and return QueryResult object. */ QueryResult TextQuery::query(const std::string &sought) const { // dynamicaly allocated set used for the word does not appear. static std::shared_ptr> noData(new std::set); // fetch the iterator to the matching element in the map. //std::map>>::const_iterator auto iter = wm.find(sought); if(iter == wm.end()) return QueryResult(sought, noData, file); else return QueryResult(sought, iter->second, file); } /** * @brief do a query opertion and return tuple. */ result_tuple TextQuery::query_return_tuple(const std::string &sought) { // dynamicaly allocated set used for the word does not appear. static std::shared_ptr> noData(new std::set); // fetch the iterator to the matching element in the map. auto iter = wm.find(sought); if(iter == wm.end()) return result_tuple(sought, noData, file); else return result_tuple(sought, iter->second, file); } ================================================ FILE: ch17/ex17.3/textquery.h ================================================ /*************************************************************************** * @file textqueryr.h * @author Alan.W * @date 29 DEC 2013 * @remark ***************************************************************************/ #ifndef TEXTQUERY_H #define TEXTQUERY_H #include #include #include #include #include #include class QueryResult; class TextQuery { public: typedef std::vector::size_type index_Tp; typedef std::tuple >, std::shared_ptr>> result_tuple; // constructor TextQuery(std::ifstream&); // query operation returns QueryResult QueryResult query(const std::string&) const; // query operation returns tuple result_tuple query_return_tuple(const std::string& sought); private: std::shared_ptr> file; std::map>> wm; }; #endif // TEXTQUERY_H ================================================ FILE: ch17/ex17_10.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 6 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.10: // Using the sequence 1, 2, 3, 5, 8, 13, 21, initialize a bitset that has a // 1 bit in each position corresponding to a number in this sequence. Default // initialize another bitset and write a small program to turn on each of the // appropriate bits. // #include #include #include int main() { std::vector v = { 1, 2, 3, 5, 8, 13, 21 }; std::bitset<32> bset; for (auto i : v) bset.set(i); std::bitset<32> bset2; for (unsigned i = 0; i != 32; ++i) bset2[i] = bset[i]; std::cout < #include #include #include #include //class Quiz template class Quiz { public: //constructors Quiz() = default; Quiz(std::string& s) :bitquiz(s){ } //generate grade template friend std::size_t grade(Quiz const&, Quiz const&); //print template friend std::ostream& operator<<(std::ostream&, Quiz const&); //update bitset void update(std::pair); private: std::bitset bitquiz; }; #endif template void Quiz::update(std::pair pair) { bitquiz.set(pair.first, pair.second); } template std::ostream& operator<<(std::ostream& os, Quiz const& quiz) { os << quiz.bitquiz; return os; } template std::size_t grade(Quiz const& corAns, Quiz const& stuAns) { auto result = stuAns.bitquiz ^ corAns.bitquiz; result.flip(); return result.count(); } int main() { //Ex17_11 std::string s = "1010101"; Quiz<10> quiz(s); std::cout << quiz << std::endl; //EX17_12 quiz.update(std::make_pair(1, true)); std::cout << quiz << std::endl; //Ex17_13 std::string answer = "10011"; std::string stu_answer = "11001"; Quiz<5> ans(answer), stu_ans(stu_answer); std::cout << grade(ans, stu_ans) << std::endl; return 0; } ================================================ FILE: ch17/ex17_14_15_16.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Queequeg * @date 19 Nov 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note There are some bugs in gcc(include the latest version 4.9.2) * to handle regular expression.To compile this program, please * turn to other compilers such as msvs2013 and clang. ***************************************************************************/ // // Exercise 17.14 // Write several regular expressions designed to trigger various errors. // Run your program to see what output your compiler generates for each error. // Exercise 17.15 // Write a program using the pattern that finds word that violate the // "i before e except after c" rule. Have your program prompt the user to // supply a word and indicate whether the word is okay or not. Test your // program with words that do and do not violate the rule. // Exercise 17.16 // What would happen if your regex object in the previous program were // initialized with "[^c]ei"? Test your program using that pattern to see // whether your expectations were correct. #include using std::cout; using std::cin; using std::endl; #include using std::string; #include using std::regex; using std::regex_error; int main() { // for ex17.14 // error_brack try{ regex r("[[:alnum:]+\\.(cpp|cxx|cc)$", regex::icase); } catch(regex_error e) { cout << e.what() << " code: " << e.code() << endl; } // for ex17.15 regex r("[[:alpha:]]*[^c]ei[[:alpha:]]*", regex::icase); string s; cout << "Please input a word! Input 'q' to quit!" << endl; while(cin >> s && s != "q") { if(std::regex_match(s, r)) cout << "Input word " << s << " is okay!" << endl; else cout << "Input word " << s << " is not okay!" <> s && s != "q") { if(std::regex_match(s, r)) cout << "Input word " << s << " is okay!" << endl; else cout << "Input word " << s << " is not okay!" < using std::cout; using std::cin; using std::endl; #include using std::string; #include using std::regex; using std::sregex_iterator; int main() { string s; cout << "Please input a sequence of words:" << endl; getline(cin, s); cout << endl; cout << "Word(s) that violiate the \"ei\" grammar rule:" << endl; string pattern("[^c]ei"); pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*"; regex r(pattern, regex::icase); for (sregex_iterator it(s.begin(), s.end(), r), end_it; it != end_it; ++it) cout << it->str() << endl; return 0; } ================================================ FILE: ch17/ex17_19_20.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Queequeg * @date 26 Nov 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note There are some bugs in gcc(include the latest version 4.9.2) * to handle regular expression.To compile this program, please * turn to other compilers such as msvs2013 and clang. ***************************************************************************/ // // Exercise 17.19 // Why is it okay to call m[4].str() without first checking whether m[4] // was matched? // We expect that the two delimiters in m[4] and m[6] are the same. // If m[4](or m[6]) is mismatched, m[4].str()(or m[6].str() respectively) // returns an empty string which can also be compared with the other // delimiter. // // Exercise 17.20 // Write your own version of the program to validate phone numbers. #include using std::cout; using std::cin; using std::endl; #include using std::string; #include using std::regex; using std::sregex_iterator; using std::smatch; bool valid(const smatch& m); int main() { string phone = "(\\()?(\\d{ 3 })(\\))?([-. ])?(\\d{ 3 })([-. ]?)(\\d{ 4 })"; regex r(phone); smatch m; string s; bool valid_record; // read each record from the input file while (getline(cin, s)) { valid_record = false; // for each matching phone number for (sregex_iterator it(s.begin(), s.end(), r), end_it; it != end_it; ++it) { valid_record = true; // check whether the number's formatting is valid if (valid(*it)) cout << "valid phone number: " << it->str() << endl; else cout << "invalid phone number: " << it->str() << endl; } if (!valid_record) cout << "invalid record!" << endl; } return 0; } bool valid(const smatch& m) { // if there is an open parenthesis before the area code if (m[1].matched) // the area code must be followed by a close parenthesis // and followed immediately by the rest of the number or a space return m[3].matched && (m[4].matched == 0 || m[4].str() == " "); else // then there can't be a close after the area code // the delimiters between the other two components must match return !m[3].matched && m[4].str() == m[6].str(); } ================================================ FILE: ch17/ex17_1_2.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Yue Wang * @date 3 Mar 2014 Jun 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.1: // Define a tuple that holds three int values and initialize the members to 10, 20, and 30. // // Exercise 17.2: // Define a tuple that holds a string, a vector, and a pair. // #include #include #include int main() { auto three_ints = std::make_tuple(10, 20, 30); using SomeTuple = std::tuple < std::string, std::vector, std::pair > ; SomeTuple some_tuple; return 0; } ================================================ FILE: ch17/ex17_21.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Queequeg * @date 26 Nov 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.21 // Rewrite your phone number program from 8.3.2 (p. 323) to use the // valid function defined in this section. #include using std::cerr; using std::cout; using std::cin; using std::endl; using std::istream; using std::ostream; #include using std::ifstream; using std::ofstream; #include using std::istringstream; using std::ostringstream; #include using std::string; #include using std::vector; #include using std::regex; using std::sregex_iterator; using std::smatch; struct PersonInfo { string name; vector phones; }; bool valid(const smatch& m); bool read_record(istream& is, vector& people); void format_record(ostream& os, const vector& people); // fake function that makes the program compile string format(const string &num) { return num; } int main() { vector people; string filename; cout << "Please input a record file name: "; cin >> filename; cout << endl; ifstream fin(filename); if (read_record(fin, people)) { ofstream fout("data\\result.txt", ofstream::trunc); format_record(fout, people); } else { cout << "Fail to open file " << filename << endl; } return 0; } bool valid(const smatch& m) { // if there is an open parenthesis before the area code if (m[1].matched) // the area code must be followed by a close parenthesis // and followed immediately by the rest of the number or a space return m[3].matched && (m[4].matched == 0 || m[4].str() == " "); else // then there can't be a close after the area code // the delimiters between the other two components must match return !m[3].matched && m[4].str() == m[6].str(); } bool read_record(istream& is, vector& people) { if (is) { string line, word; // will hold a line and word from input, respectively // read the input a line at a time until cin hits end-of-file (or another error) while (getline(is, line)) { PersonInfo info; // create an object to hold this record's data istringstream record(line); // bind record to the line we just read record >> info.name; // read the name while (record >> word) // read the phone numbers info.phones.push_back(word); // and store them people.push_back(info); // append this record to people } return true; } else return false; } void format_record(ostream& os, const vector& people) { string phone = "(\\()?(\\d{ 3 })(\\))?([-. ])?(\\d{ 3 })([-. ]?)(\\d{ 4 })"; regex r(phone); smatch m; for (const auto &entry : people) { // for each entry in people ostringstream formatted, badNums; // objects created on each loop for (const auto &nums : entry.phones) { for (sregex_iterator it(nums.begin(), nums.end(), r), end_it; it != end_it; ++it) { // for each number // check whether the number's formatting is valid if (!valid(*it)) // string in badNums badNums << " " << nums; else // "writes" to formatted's string formatted << " " << format(nums); } } if (badNums.str().empty()) // there were no bad numbers os << entry.name << " " // print the name << formatted.str() << endl; // and reformatted numbers else // otherwise, print the name and bad numbers cerr << "input error: " << entry.name << " invalid number(s) " << badNums.str() << endl; } } ================================================ FILE: ch17/ex17_23.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Queequeg * @date 26 Nov 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.23 #include using std::cout; using std::cin; using std::endl; #include using std::string; #include using std::regex; using std::sregex_iterator; using std::smatch; bool valid(const smatch& m); int main() { string zipcode = "(\\d{5})([-])?(\\d{4})?\\b"; regex r(zipcode); smatch m; string s; while (getline(cin, s)) { //! for each matching zipcode number for (sregex_iterator it(s.begin(), s.end(), r), end_it; it != end_it; ++it) { //! check whether the number's formatting is valid if (valid(*it)) cout << "valid zipcode number: " << it->str() << endl; else cout << "invalid zipcode number: " << s << endl; } } return 0; } bool valid(const smatch& m) { if ((m[2].matched)&&(!m[3].matched)) return false; else return true; } ================================================ FILE: ch17/ex17_28_29_30.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 7 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.28: // Write a function that generates and returns a uniformly distributed random // unsigned int each time it is called. // // Exercise 17.29: // Allow the user to supply a seed as an optional argument to the function you // wrote in the previous exercise. // // Exercise 17.30: // Revise your function again this time to take a minimum and maximum value for // the numbers that the function should return. // #include #include #include // default version unsigned random_gen(); // with seed spicified unsigned random_gen(unsigned seed); // with seed and range spicified unsigned random_gen(unsigned seed, unsigned min, unsigned max); int main() { std::string temp; while(std::cin >> temp) std::cout << std::hex << random_gen(19, 1, 10) << std::endl; return 0; } unsigned random_gen() { static std::default_random_engine e; static std::uniform_int_distribution ud; return ud(e); } unsigned random_gen(unsigned seed) { static std::default_random_engine e(seed); static std::uniform_int_distribution ud; return ud(e); } unsigned random_gen(unsigned seed, unsigned min, unsigned max) { static std::default_random_engine e(seed); static std::uniform_int_distribution ud(min, max); return ud(e); } ================================================ FILE: ch17/ex17_33.cpp ================================================ /*************************************************************************** * @file main.cpp * @author qwert2603 * @date 13 Apr 2015 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ #include using std::cout; using std::endl; #include using std::ifstream; #include using std::string; #include using std::vector; #include using std::default_random_engine; using std::uniform_int_distribution; #include using std::time; #include using std::sort; using std::find_if; #include using std::pair; int main() { typedef pair ps; ifstream i("d.txt"); vector dict; string str1, str2; // read wirds from dictionary while (i >> str1 >> str2) { dict.emplace_back(str1, str2); } i.close(); // sort words in vector sort(dict.begin(), dict.end(), [](const ps &_ps1, const ps &_ps2){ return _ps1.first < _ps2.first; }); i.open("i.txt"); default_random_engine e((unsigned int)(time(0))); // read words from text while (i >> str1) { // find word in dictionary vector::const_iterator it = find_if(dict.cbegin(), dict.cend(), [&str1](const ps &_ps){ return _ps.first == str1; }); // if word doesn't exist in dictionary if (it == dict.cend()) { // write it itself cout << str1 << ' '; } else { // get random meaning of word uniform_int_distribution u (0, find_if(dict.cbegin(), dict.cend(), [&str1](const ps &_ps){ return _ps.first > str1; }) - it - 1); // write random meaning cout << (it + u(e))->second << ' '; } } return 0; } ================================================ FILE: ch17/ex17_4_5_6_7_8.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 3 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.4: // Write and test your own version of the findBook function. // // Exercise 17.5: // Rewrite findBook to return a pair that holds an index and a pair of iterators. // // Exercise 17.6: // Rewrite findBook so that it does not use tuple or pair. // // Exercise 17.7: // Explain which version of findBook you prefer and why. // // The version using tuple is prefered.It's more flexible, campared to other versions. // // Exercise 17.8: // What would happen if we passed Sales_data() as the third parameter to accumulate // in the last code example in this section? // // If so, the output should be 0, as the Sales_data is default constructed. // #include #include #include #include #include #include #include #include "ex17_4_5_6_7_8_SalesData.h" // for ex17.4 // maches has 3 members: // an index of a store and iterators into that store's vector typedef std::tuple::size_type, std::vector::const_iterator, std::vector::const_iterator> matches; // for ex17.5 // return a pair that holds an index and a pair of iterators. typedef std::pair::size_type, std::pair::const_iterator, std::vector::const_iterator>> matches_pair; // for ex17.6 // return a struct that holds an index of a store and iterators into that store's vector struct matches_struct { std::vector::size_type st; std::vector::const_iterator first; std::vector::const_iterator last; matches_struct(std::vector::size_type s, std::vector::const_iterator f, std::vector::const_iterator l) : st(s), first(f), last(l) { } } ; // for ex17.4 // return a vector with an entry for each store that sold the given book. std::vector findBook(const std::vector>& files, const std::string& book); // print the result using the given iostream void reportResults(std::istream& in, std::ostream os, const std::vector>& files); // for ex17.5 // return a vector with an entry for each store that sold the given book. std::vector findBook_pair(const std::vector > &files, const std::string &book); // for ex17.6 // return a vector with an entry for each store that sold the given book. std::vector findBook_struct(const std::vector > &files, const std::string &book); int main() { return 0; } // for ex17.4 // return a vector with an entry for each store that sold the given book. std::vector findBook(const std::vector>& files, const std::string& book) { std::vector ret; // for each strore find the range of matching books, if any for (auto it = files.cbegin(); it != files.cend(); ++it) { // find the range of Sales_data tat have the same ISBN auto found = std::equal_range(it->cbegin(), it->cend(), book, compareIsbn); if(found.first != found.second) ret.push_back(std::make_tuple(it - files.cbegin(), found.first, found.second)); } return ret; } // for ex17.4 // print the result using the given iostream void reportResults(std::istream& in, std::ostream os, const std::vector>& files) { std::string s; while(in >> s) { auto trans = findBook(files, s); if(trans.empty()){ std::cout << s << "not found in any stores" << std::endl; continue; } for(const auto& store :trans) os << "store " << std::get<0>(store) << " sales: " << std::accumulate(std::get<1>(store), std::get<2>(store), Sales_data(s)) << std::endl; } } // for ex17.5 // return a vector with an entry for each store that sold the given book std::vector findBook_pair(const std::vector > &files, const std::string &book) { std::vector ret; for(auto it = files.cbegin(); it != files.cend(); ++it) { auto found = std::equal_range(it->cbegin(), it->cend(), book, compareIsbn); if(found.first != found.second) ret.push_back(std::make_pair(it - files.cbegin(), std::make_pair(found.first, found.second))); } return ret; } // for ex17.6 // return a vector with an entry for each store that sold the given book. std::vector findBook_struct(const std::vector > &files, const std::string &book) { std::vector ret; for(auto it = files.cbegin(); it != files.cend(); ++it) { auto found = std::equal_range(it->cbegin(), it->cend(), book, compareIsbn); if(found.first != found.second) ret.push_back(matches_struct(it - files.cbegin(), found.first, found.second)); } return ret; } ================================================ FILE: ch17/ex17_4_5_6_7_8_SalesData.cpp ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced.Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ #include using std::istream; using std::ostream; #include "ex17_4_5_6_7_8_SalesData.h" Sales_data::Sales_data(std::istream &is) { // read will read a transaction from is into this object read(is, *this); } double Sales_data::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } // add the value of the given Sales_data into this object Sales_data& Sales_data::combine(const Sales_data &rhs) { units_sold += rhs.units_sold; // add the members of rhs into revenue += rhs.revenue; // the members of ``this'' object return *this; // return the object on which the function was called } // = Sales_data Sales_data &Sales_data::operator =(const Sales_data &rhs) { this->bookNo = rhs.bookNo; this->revenue = rhs.revenue; this->units_sold = rhs.units_sold; return *this; } // =string Sales_data &Sales_data::operator =(const std::string &rhs) { *this= Sales_data(rhs); return *this; } // += Sales_data &Sales_data::operator +=(const Sales_data &rhs) { this->revenue += rhs.revenue; this->units_sold += rhs.units_sold; return *this; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; // copy data members from lhs into sum sum.combine(rhs); // add data members from rhs into sum return sum; } // transactions contain ISBN, number of copies sold, and sales price istream& read(istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } ostream& print(ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } // added 10.Jan 2014 std::ostream & operator <<(std::ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } // added 12.Jan 2014 std::istream& operator >>(std::istream &is, Sales_data &s) { double price; // read input is >> s.bookNo >> s.units_sold >> price; // if successful, write into the object, give the object default state otherwise. if(is) s.revenue = s.units_sold * price; else s = Sales_data(); return is; } ================================================ FILE: ch17/ex17_4_5_6_7_8_SalesData.h ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced.Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ // // Exercise 14.45: // Write conversion operators to convert a Sales_data to string and to double. // What values do you think these operators should return? // #ifndef SALES_DATA_H #define SALES_DATA_H #include #include class Sales_data { // friends friend Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs); friend std::ostream& operator << (std::ostream& os, const Sales_data& s); friend std::istream& operator >> (std::istream& is, Sales_data& s); friend Sales_data add(const Sales_data&, const Sales_data&); friend std::ostream &print(std::ostream&, const Sales_data&); friend std::istream &read(std::istream&, Sales_data&); public: // constructors Sales_data() = default; Sales_data(const std::string &s): bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p*n) { } Sales_data(const Sales_data &s ): bookNo(s.bookNo), units_sold(s.units_sold), revenue(s.revenue) { } Sales_data(Sales_data&& s): bookNo(s.bookNo), units_sold(s.units_sold), revenue(s.revenue) { } ~Sales_data(){ } Sales_data(std::istream &); std::string isbn() const { return bookNo; } Sales_data& combine(const Sales_data&); // assignments Sales_data& operator =(const Sales_data& rhs); Sales_data& operator =(const std::string& rhs); Sales_data& operator +=(const Sales_data& rhs); // conversion explicit operator std::string () const { return bookNo; } explicit operator double () const { return revenue; } double avg_price() const; private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; // overloaded operators added 10.Jan.2014 for ex14.2 inline Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs) { Sales_data sum = lhs; sum += rhs; return sum; } std::ostream& operator << (std::ostream& os, const Sales_data& item); std::istream& operator >> (std::istream& is, Sales_data& s); // nonmember Sales_data interface functions Sales_data add(const Sales_data&, const Sales_data&); std::ostream &print(std::ostream&, const Sales_data&); std::istream &read(std::istream&, Sales_data&); // used in future chapters inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs) { return lhs.isbn() < rhs.isbn(); } #endif ================================================ FILE: ch17/ex17_9.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 3 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 17.9: Explain the bit pattern each of the following bitset objects contains: // (a) bitset<64> bitvec(32); // 0000000000000000000000000000000000000000000000000000000000100000 // // ^ // (b) bitset<32> bv(1010101); // 00000000000011110110100110110101 // // note that the argument here is decemal // (c) string bstr; cin >> bstr; bitset<8>bv(bstr); // depends on what has been input. // #include #include //#include "Sales_data.h" int main() { std::bitset<32> bv(1010101); std::cout << bv << std::endl; } ================================================ FILE: ch18/18_18 18_19.cpp ================================================ //@Squawk09 //18.18 //Given the following typical definition of swap 13.3 (p.157) determine which version of swap is used //if mem1 is a string. What if mem1 is an int? //Explain how name lookup works in both cases. void swap(T v1,Tv2) { using std::swap; swap(vq.mem1,v2.mem1); //swap remaining members of type T } //Swap is a template function defined by the standard library. //By stating we are using std::swap all the following uses of swap in the scope of the function will //look for the matching template for its argument types in the standard library. //If mem1 is a string the program will use the standard library function that has string arguments. //If mem1 is int, it will use the standard library template version with int arguments. //18.19 //What if the call to swap was std::swap(v1.mem1,v2.mem1); //The function would use the matching std version of swap for that specific call. //It would not necessarily use the matching standard library version of swap for the following calls. ================================================ FILE: ch18/18_20.cpp ================================================ //@Squawk09 //Exercise 18.20 //In the following code, determine which function, if any, matches the call to compute, List the candidate and viable functions. //What type conversions, if any, are applied to the argument to match the parameter in each viable function. //void compute(int) first, no type conversion //void const void works. //void compute() doesn't work //double double works converted to double //char char works #include namespace primerLib { void compute(); //Error, does not work. Too many argument in the call to match. void compute(const void *) { }; //Works! Converts argument to a constant void pointer. } void compute(int) { std::cout << "compute(int)" << std::endl; };//Works! Most closely matches the argument parameters so it is selected first. void compute(double, double =3.4);//Works! Converts argument to double. void compute(char* x,char* =0);//Works! converts to a character pointer. void f() { using primerLib::compute; compute(0); } int main() { f(); return 0; } //What would happen if using declaration were located in main before the call to compute? //Answer the same questions as before. //The compiler will match to void compute( const void *) in the primerLib namespace. //void compute() will still not work because of too many arguments. ================================================ FILE: ch18/README.md ================================================ ## Exercise 18.1 > What is the type of the exception object in the following throws? > > **(a)**`range_error r("error");` > `throw r`; > **(b)**`exception *p = &r;` > `throw *P;` >What would happen if the `throw` in **(b)** were written as `throw p`? The type of the exception object in (a) is range_error which is used to report range errors in internal computations. The type of the exception object in (b) is exception. If the "throw" in (b) were written as "throw p", there will be a runtime error. ## Exercise 18.2 > Explain what happens if an exception occurs at the indicated point: ```cpp void exercise(int *b, int *e) { vector v(b, e); int *p = new int[v.size()]; ifstream in("ints"); // exception occurs here } ``` The space "p" points will not be free. There will be a memory leak. ## Exercise 18.3 ## Exercise 18.21 >Explain the following declarations. Identify any that are in error and explain why they are incorrect: >**(a)** ```cpp class CAD{}; class Vehicle{}; class CADVehicle : public CAD, Vehicle{}; ``` CAD Vehicle publicly inherits from CAD and privaetely inherits from Vehicle. CADVehicle gets all the public and private methods that Vehicle has but cant be cast to a Vehicle argument. It is an "inaccessible" base. for example ```cpp CadVehicle example; void foo(Vehicle){/*do something*/}; foo(CADVehicle);//will not work, will work if Vehicle were public ``` >**(b)** ```cpp class DBList: public List,public List {/*do something*/}; ``` Error because you are trying to derive from the same base class twice. If two different libraries or header files define the same named class,you need to specify with a scope resolution operator, i.e. headerfile_name::List. >**(c)** ```cpp class iostream : public istream, public ostream{/*do something*/}; ``` Ok. ## Exercise 18.22 >Given the following class hierarchy, in which each class defines a default constructor. >What is the order of constructor execution for the following definition. ```cpp #include class A {}; class B : public A{}; class C : public B{}; class X {}; class Y {}; class Z : public X, public Y {}; class MI : public C, public Z {}; class D : public X, public C{}; MI mi; int main() { *pd = new D; X *px = pd; B *pb = pd; A *pa =pd; C *pc = pd; return 0; } ``` The order in which base classes are constructed depends on the order in which they appear in the class derivation list. construction order is as follows: A, B, C, X, Y, Z, MI. ## Exercise 18.23 >Using the hierarchy in exercise 18.22 along with class D defined below, and assuming each class defines a default constructor, which,if any, of the following conversion are not permitted? ```cpp class D : public c{ ... }; D *pd = new D; ``` All of the conversions are permitted. ## Exercise 18.24 >On page 807 we presented a series of calls made through a Bear pointer that pointed to a Panda object. >Explain each call assuming we used a ZooAnimal pointer pointing to a Panda Object instead. ```cpp ZooAnimal *pb = new Panda ("ying_yang"); pb->print();//Ok, part of ZooAnimal interface pb->cuddle();//Error, not part of interface pb->highlight();//Error, not part of interface delete pb;//Ok, part of interface ``` ## Exercise 18.25 >Assume we have two base classes, Base1 and Base 2, each of which >define a virtual member named print and a virtual destructor. From these base >classes we derive the following classes, each of which redefines the print function. [cpp](./ex18.25.cpp "Exercise 18.25") ## Exercise 18.26 >Given the hierarchy in the box on page 810, why is the following call to print an error? >Revise MI to allow this call to print to compile and execute correctly. ```cpp #include #include struct Base1{ void print(int) const{ std::cout<<"Base1 Print Used"<){}; void print(int x){ Base1::print(x); } protected: int *ival; std::vector dvec; }; using namespace std; int main() { MI mi; mi.print(42); return 0; } ``` There is no matching version of print in MI that matches an integer argument. If we just remove the print function in MI there is an ambiguity between the Derived and Base2 versions of print; therefore, we should overload the MI version of print() to take an int argument. ## Exercise 18.27 >Given the hierarchy in the box on page 810, why is the following call to print an error? >Revise MI to allow this call to print to compile and execute correctly. ```cpp #include #include struct Base1{ void print(int) const{ std::cout<<"Base1 Print Used"<){}; void print(int x){ Base1::print(x); } int ival; double dval; void foo(double cval) { int dval; dval = Base1::dval+Derived::dval;//(c) Base2::fval=dvec.back()-1;//(d) Derived::sval[0]= Base1::cval;//(e) std::cout< dvec={9,8,7}; }; int main() { MI mi; return 0; } ``` (a) Everything that is a property of the classes that MI derives from is visible except those that are private. (b) Yes any names in the base classes that repeat and are not private can be accessed in foo by adding a scope operator. (c) see above (d) see above (e) see above ## Exercise 18.28 >Given the following class hierarchy, which inherited members can be accessed without qualification, from within the vmi class? >which require qualification? Explain your reasoning. ```cpp struct Base{ void bar(int); //Accessed without qualification, not defined with int arg anywhere protected: int ival;//Need qualification, VMI will use Derived2::ival by default }; struct Derived1 : virtual public Base{ void bar(char);//Accessed with no qualification, VMI derives from Derived1 which derives from Base. void foo(char);//Need qualification, can convert arg between two foos. protected: char cval;//need to qualify ambiguous with other cval. }; struct Derived2 : virtual public Base{ void foo(int);//Need qualification, can convert arg between two foos. protected: int ival;//Accessed with no qualification. char cval;//need to qualify ambiguous with other cval. }; class VMI : public Derived1, public Derived2 { }; ``` ================================================ FILE: ch18/ex18.1.2.3.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Alan.W * @date 11 Mar 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 18.1: // What is the type of the exception object in the following throws? // (a) range_error r("error"); // throw r; // (b) exception *p = &r; // throw *p; // the static, compile-time type of that expression determines the type of // the exception object. // so the object thrown the second time is std::exception type not std::range_error. // What would happen if the throw in (b) were written as throw p? // terminate called after throwing an instance of 'std::exception*' // // Exercise 18.2: Explain what happens if an exception occurs at the indicated point: /* void exercise(int *b, int *e) { vector v(b, e); // the object v will be destroyed by its destructor. int *p = new int[v.size()]; // the dynamically allocated int array will be no way deallocated. ifstream in("ints"); // the object in will be destroyed. // exception occurs here } */ // Exercise 18.3: // There are two ways to make the previous code work correctly // if an exception is thrown. Describe them and implement them. // The first approach is to mange it using a class. The second one is using smart pointer. // #include #include #include #include #include /** * @brief The intArray class manage a dynamicly allocated int array. * @note for ex18.3 approach 1. */ struct intArray { intArray() : p(nullptr) { } explicit intArray(std::size_t s): p(new int[s]) { } ~intArray() { delete[] p; } // data meber int *p; }; void exercise(int *b, int *e) { std::vector v(b, e); // the object v will be destroyed by its destructor. // @oldcode: //int *p = new int[v.size()]; // the dynamically allocated int array will be no way deallocated. // approach 1: intArray p(v.size()); // approach 2: //std::shared_ptr p(new int[v.size()], [](int *p) { delete[] p; }); // delete array using lambda std::ifstream in("ints"); // the object in will be destroyed. // exception occurs here } int main() { // for ex18.1 /* std::range_error r("error"); //throw r; std::exception *p = &r; throw p; */ return 0; } ================================================ FILE: ch18/ex18.12.13.14.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Queequeg * @date 20 Nov 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 18.12 // Organize the programs you have written to answer the questions in each // chapter into their own namespaces. That is, namespace chapter15 would // contain code for the Query programs and chapter10 would contain the // TextQuery code. Using this structure, compile the Query code examples. // Exercise 18.13 // When might you see use an unnamed namespace? // http://stackoverflow.com/questions/154469/unnamed-anonymous-namespaces-vs-static-functions // http://stackoverflow.com/questions/5312213/uses-of-unnamed-namespace-in-c // Exercise 18.14 // Suppose we have the following declaration of the operator* that is a // a member of the nested namespace mathLib::MatrixLib. // How would you declare this operator in global scope? // mathLib::MatrixLib::matrix mathLib::MatrixLib::operator* // (const mathLib::MatrixLib::matrix&, const mathLib::MatrixLib::matrix&); // or :(provided by @lafener in #173) //mathLib::MatrixLib::matrix mathLib::MatrixLib::operator* (const matrix&, const matrix&); int main() { return 0; } ================================================ FILE: ch18/ex18.15.16.17.cpp ================================================ /*************************************************************************** * @file main.cpp * @author Queequeg * @date 20 Nov 2014 * @remark This code is for the exercises from C++ Primer 5th Edition * @note ***************************************************************************/ // // Exercise 18.15 // Explain the differences between using declarations and directives. // This difference in scope between a using declaration and a using directive // stems directly from how these two facilities work. In the case of a using // declaration, we are simply making name directly accessible in the local // scope. In contrast, a using directive makes the entire contents of a // namespace available In general, a namespace might include definitions that // cannot appear in a local scope. As a consequence, a using directive is // treated as if it appeared in the nearest enclosing namespace scope. // Exercise 18.16 // Explain the following code assuming using declarations for all the // members of namespace Exercise are located at the location labeled // position 1. What if they appear at position 2 instead? Now answer the // same question but replace the using declarations with a using directive // for namespace Exercise. // Exercise 18.17 // Write code to test your answers to the previous question. #include using std::cout; using std::endl; // using declarations for all the members of namespace Exercise // are located at the location labeled position 1. namespace Test0 { namespace Exercise { int ivar = 0; double dvar = 0; const int limit = 1000; } int ivar = 0; // using Exercise::ivar; // error C2874: using-declaration causes a multiple declaration of 'Test0::Exercise::ivar' // So we delete it to make the program compile. using Exercise::dvar; using Exercise::limit; void manip() { double dvar = 3.1416; cout << "********** Before call Test0::manip **********" << endl; cout << "Exercise::ivar " << Exercise::ivar << endl << "Exercise::dvar " << Exercise::dvar << endl << "Exercise::limit " << Exercise::limit < struct Base1 { /* ... */ virtual void print(){std::cout << "Print from Base1" << std::endl;} virtual ~Base1(){std::cout << "Base1" << std::endl;} }; struct Base2 { /* ... */ virtual void print(){std::cout << "Print from Base2" << std::endl;} virtual ~Base2(){std::cout << "Base2" << std::endl;} }; struct D1 : public Base1 { /* ... */ void print() override {std::cout << "Print from D1" << std::endl;} ~D1() override {std::cout << "D1" << std::endl;} }; struct D2 : public Base2 { /* ... */ void print() override {std::cout << "Print from D2" << std::endl;} ~D2() override {std::cout << "D2" < print(); std::cout << "pd1 print..........." << std::endl; pd1 -> print(); std::cout << "pd2 print..........." << std::endl; pd2 -> print(); std::cout << "delete pb2..........." << std::endl; delete pb2; std::cout << "delete pd1..........." << std::endl; delete pd1; std::cout << "delete pd2..........." << std::endl; delete pd2; } ================================================ FILE: ch18/ex18_29.cpp ================================================ /* Exercise 18.29: Given the following class hierarchy: class Class { ... }; class Base : public Class { ... }; class D1 : virtual public Base { ... }; class D2 : virtual public Base { ... }; class MI : public D1, public D2 { ... }; class Final : public MI, public Class { ... }; (a) In what order are constructors and destructors run on a Final object? (b) A Final object has how many Base parts? How many Class parts? (c) Which of the following assignments is a compile-time error? Base *pb; Class *pc; MI *pmi; D2 *pd2; (a) pb = new Class; (b) pc = new Final; (c) pmi = pb; (d) pd2 = pmi; Solution: (a) Constructors run order: Class Base D1 D2 MI Class Final. Destructors run order: Final Class MI D2 D1 Base Class. Class parts are constructed from left to right and base class to derived class. (b) 1 Base part and 2 Class parts. Because ‘Base’ is a virtual base class of ‘D1’ and ‘D2’. There is only 1 Base part. However, ‘Class’ is a normal base class of ‘Final’ and ‘Base’. So there is 2 Class part. (c) error. Can't convert a pointer of base class to a pointer of derived class implicitly. error. ‘Class’ is an ambiguous base of ‘Final’. error. Can't convert a pointer of base class to a pointer of derived class implicitly. pass. A pointer of derived class can be cast to a pointer of base class. */ #include using namespace std; class Class { public: Class() { cout << "Class() called" << endl; } ~Class() { cout << "~Class() called" << endl; } }; class Base : public Class { public: Base() { cout << "Base() called" << endl; } ~Base() { cout << "~Base() called" << endl; } }; class D1 : virtual public Base { public: D1() { cout << "D1() called" << endl; } ~D1() { cout << "~D1() called" << endl; } }; class D2 : virtual public Base { public: D2() { cout << "D2() called" << endl; } ~D2() { cout << "~D2() called" << endl; } }; class MI : public D1, public D2 { public: MI() { cout << "MI() called" << endl; } ~MI() { cout << "~MI() called" << endl; } }; class Final : public MI, public Class { public: Final() { cout << "Final() called" << endl; } ~Final() { cout << "~Final() called" << endl; } }; int main(int argc, char const *argv[]) { Final final; Base *pb; Class *pc; MI *pmi; D2 *pd2; // pb = new Class; // pc = new Final; // pmi = pb; pd2 = pmi; return 0; } ================================================ FILE: ch19/ex19_18.cpp ================================================ #include #include #include #include #include int main(int argc, char *argv[]) { std::vector< std::string > v; std::string a; while (std::getline(std::cin,a)) { v.push_back(a); } auto m = std::count_if(v.begin(),v.end(),std::mem_fn(&std::string::empty)); std::cout << "Using mem_fn,the number of empty lines:" << m << std::endl; auto n = std::count_if(v.begin(),v.end(),std::bind(&std::string::empty,std::placeholders::_1)); std::cout << "Using bind,the number of empty lines:" << n << std::endl; auto q = std::count_if(v.begin(),v.end(),[](std::string &tem){ return tem.empty(); }); std::cout << "Using lamba,the number of empty lines:" << q << std::endl; return 0; } ================================================ FILE: ch19/ex19_20.cpp ================================================ #include #include #include #include #include #include #include #include class TextQuery { public: class QueryResult; using line_no = std::vector::size_type; TextQuery(std::ifstream&); TextQuery::QueryResult query(const std::string&) const; private: std::shared_ptr > file; std::map > > wm; }; class TextQuery::QueryResult{ friend std::ostream& print(std::ostream&, const QueryResult&); public: QueryResult(std::string s, std::shared_ptr > p, std::shared_ptr > f):sought(s),lines(p),file(f){}; private: std::string sought; //query word std::shared_ptr > lines; //lines the word show std::shared_ptr > file; //files show the word; }; TextQuery::TextQuery(std::ifstream &is) : file(new std::vector ){ std::string text; while (getline(is, text)) { file->push_back(text); int n = file->size() - 1; std::istringstream line(text); std::string word; while (line >> word) { auto &lines = wm[word]; if (!lines) { lines.reset(new std::set); } lines->insert(n); } } } TextQuery::QueryResult TextQuery::query(const std::string &sought) const{ static std::shared_ptr > nodata(new std::set); auto loc = wm.find(sought); if (loc == wm.end()) { return TextQuery::QueryResult(sought, nodata, file); } else { return TextQuery::QueryResult(sought,loc->second,file); } } std::ostream &print (std::ostream & os, const TextQuery::QueryResult & qr) { os << qr.sought << " occurls " << qr.lines->size() << " time(s)" << std::endl; for (auto i : *qr.lines) { os << "\t(line " << i+1 << ") " << *(qr.file->begin() + i) << std::endl; } return os; } int main(int argc, char *argv[]) { std::ifstream file; file.open("ex19_18.cpp"); TextQuery si(file); auto res = si.query("std"); print(std::cout, res); return 0; } ================================================ FILE: ch19/ex19_21_22_23_24_25.cpp ================================================ #include #include using std::string; class Sales_data{ public: Sales_data() = default; ~Sales_data() = default; Sales_data(int i):a(i){} Sales_data(const Sales_data &rhs):a(rhs.a){} Sales_data& operator=(const Sales_data&rhs){ a = rhs.a; return *this; } private: int a = 0; }; class Token{ public: Token():tok(INT),ival(0){} Token(const Token&t): tok(t.tok){copyUnion(t);} ~Token(){ if(tok == STR) sval.~string(); if(tok == SAL) item.~Sales_data(); } Token& operator=(Token &&); Token(Token&&); Token& operator=(const Token&); Token& operator=(const string&); Token& operator=(const int&); Token& operator=(const char&); Token& operator=(const Sales_data&); private: enum { INT, CHAR, STR, SAL} tok; union{ char cval; int ival; std::string sval; Sales_data item; }; void copyUnion(const Token&); //move edition void copyUnion(Token&&); }; void Token::copyUnion(Token&& t){ switch (t.tok) { case INT : ival = t.ival; break; case CHAR : cval = t.cval; break; case STR : std::move(t.sval);break; case SAL : std::move(t.item); break; } } void Token::copyUnion(const Token &t){ switch (t.tok) { case INT : ival = t.ival; break; case CHAR : cval = t.cval; break; case STR : new(&sval) string(t.sval);break; case SAL : new(&item) Sales_data(t.item); break; } } Token& Token::operator=(const Token&t){ if(tok == STR && t.tok != STR) sval.~string(); if(tok == SAL && t.tok != SAL) item.~Sales_data(); if(tok == STR && t.tok == STR) sval = t.sval; if(tok == SAL && t.tok == SAL) item = t.item; else copyUnion(t); tok = t.tok; return *this; } //move constructor Token& Token::operator=(Token&& t){ if(this != &t){ this->~Token(); copyUnion(t); tok = std::move(t.tok); } return *this; } Token::Token(Token &&t){ copyUnion(t); tok = std::move(t.tok); } Token& Token::operator=(const Sales_data& rhs){ if(tok == STR) sval.~string(); if(tok == SAL) item = rhs; else new(&item) Sales_data(rhs); tok = SAL; return *this; } Token& Token::operator=(const int& i){ if(tok == STR) sval.~string(); if(tok == SAL) item.~Sales_data(); ival = i; tok = INT; return *this; } int main(int argc, char const *argv[]) { Token s; Sales_data sal(5); s = sal; int i = 5; std::cout << i << std::endl; return 0; } ================================================ FILE: ch19/ex19_3_4.cpp ================================================ /* Exercises Section 19.2.1 Exercise 19.3: Given the following class hierarchy in which each class defines a public default constructor and virtual destructor: class A { . . . }; class B : public A { . . . }; class C : public B { . . . }; class D : public B, public A { . . . }; which, if any, of the following dynamic_casts fail? (a) A *pa = new C; B *pb = dynamic_cast< B* >(pa); (b) B *pb = new B; C *pc = dynamic_cast< C* >(pb); (c) A *pa = new D; B *pb = dynamic_cast< B* >(pa); Solution: (a) Succeed. The type of ‘pa’ (class type 'C') is publicly derived from the target type 'B'. (b) Fail. The type of ‘pb’ (class type 'B') is a public base class of the target type 'C'. ‘pc’ will equal to nullptr. (c) Fail. ‘A’ is an ambiguous base of ‘D’. Converting a pointer of 'D' to a pointer of 'A' is not allowed. Exercise 19.4: Using the classes defined in the first exercise, rewrite the following code to convert the expression *pa to the type C&: if (C *pc = dynamic_cast< C* >(pa)) // use C's members } else { // use A's members } Solution: try { C &rc = dynamic_cast(ra); // use C's members } catch (bad_cast) { // use A's members } */ #include #include using namespace std; class A { public: virtual ~A() {} }; class B : public A { public: virtual ~B() {} }; class C : public B { public: virtual ~C() {} }; class D : public B, public A { public: virtual ~D() {} }; int main(int argc, char const *argv[]) { /* Exercise 19.3 */ A *pa = new C; B *pb = dynamic_cast< B* >(pa); if (pb) cout << "19.3 (a) succeed!" << endl; else cout << "19.3 (a) fail!" << endl; pb = new B; C *pc = dynamic_cast< C* >(pb); if (pc) cout << "19.3 (b) succeed!" << endl; else cout << "19.3 (b) fail!" << endl; /* pa = new D; pb = dynamic_cast< B* >(pa); */ /* Exercise 19.4 */ C c; B b; A &ra1 = c, &ra2 = b; try { /* succeed */ C &rc1 = dynamic_cast(ra1); cout << "19.4 succeed!" << endl; /* fail */ C &rc2 = dynamic_cast(ra2); } catch (bad_cast) { cout << "19.4 failed!" << endl; } return 0; } ================================================ FILE: data/book.txt ================================================ 0-201-78345-X 3 20 0-201-78345-X 2 25 0-201-78346-X 1 100 0-201-78346-X 2 99.9 0-201-78346-X 6 89.9 ================================================ FILE: data/even.txt ================================================ 2 4 6 8 ================================================ FILE: data/given_to_transform.txt ================================================ where r u y dont u send me a pic k thk l8r ================================================ FILE: data/input.txt ================================================ 1 2 3 4 5 6 7 8 9 ================================================ FILE: data/letter.txt ================================================ In typography, an ascender is the portion of a minuscule letter in a Latin-derived alphabet that extends above the mean line of a font. That is, the part of a lower-case letter that is taller than the font's x-height. Ascenders, together with descenders, increase the recognizability of words. For this reason, British road signs no longer use all capital letters.[1] Studies made at the start of the construction of the British motorway network concluded that words with mixed-case letters were much easier to read than "all-caps" and a special font was designed for motorway signs. These then became universal across the U.K. See Road signs in the United Kingdom. ================================================ FILE: data/odd.txt ================================================ 1 3 5 7 9 ================================================ FILE: data/phonenumbers.txt ================================================ morgan 2015552368 8625550123 drew 9735550130 lee 6095550132 2015550175 8005550000 ================================================ FILE: data/storyDataFile.txt ================================================ Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. "Daddy, shush, there is no such thing," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there?" ================================================ FILE: data/word_transformation.txt ================================================ brb be right back k okay? y why r are u you pic picture thk thanks! l8r later ================================================ FILE: data/word_transformation_bad.txt ================================================ l8r ================================================ FILE: include/Sales_item.h ================================================ /* * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * * * * "The authors and publisher have taken care in the preparation of this book, * but make no expressed or implied warranty of any kind and assume no * responsibility for errors or omissions. No liability is assumed for * incidental or consequential damages in connection with or arising out of the * use of the information or programs contained herein." * * Permission is granted for this code to be used for educational purposes in * association with the book, given proper citation if and when posted or * reproduced.Any commercial use of this code requires the explicit written * permission of the publisher, Addison-Wesley Professional, a division of * Pearson Education, Inc. Send your request for permission, stating clearly * what code you would like to use, and in what specific way, to the following * address: * * Pearson Education, Inc. * Rights and Permissions Department * One Lake Street * Upper Saddle River, NJ 07458 * Fax: (201) 236-3290 */ /* This file defines the Sales_item class used in chapter 1. * The code used in this file will be explained in * Chapter 7 (Classes) and Chapter 14 (Overloaded Operators) * Readers shouldn't try to understand the code in this file * until they have read those chapters. */ #ifndef SALESITEM_H // we're here only if SALESITEM_H has not yet been defined #define SALESITEM_H // Definition of Sales_item class and related functions goes here #include #include class Sales_item { // these declarations are explained section 7.2.1, p. 270 // and in chapter 14, pages 557, 558, 561 friend std::istream& operator>>(std::istream&, Sales_item&); friend std::ostream& operator<<(std::ostream&, const Sales_item&); friend bool operator<(const Sales_item&, const Sales_item&); friend bool operator==(const Sales_item&, const Sales_item&); public: // constructors are explained in section 7.1.4, pages 262 - 265 // default constructor needed to initialize members of built-in type Sales_item() = default; Sales_item(const std::string &book): bookNo(book) { } Sales_item(std::istream &is) { is >> *this; } public: // operations on Sales_item objects // member binary operator: left-hand operand bound to implicit this pointer Sales_item& operator+=(const Sales_item&); // operations on Sales_item objects std::string isbn() const { return bookNo; } double avg_price() const; // private members as before private: std::string bookNo; // implicitly initialized to the empty string unsigned units_sold = 0; // explicitly initialized double revenue = 0.0; }; // used in chapter 10 inline bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs) { return lhs.isbn() == rhs.isbn(); } // nonmember binary operator: must declare a parameter for each operand Sales_item operator+(const Sales_item&, const Sales_item&); inline bool operator==(const Sales_item &lhs, const Sales_item &rhs) { // must be made a friend of Sales_item return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.isbn() == rhs.isbn(); } inline bool operator!=(const Sales_item &lhs, const Sales_item &rhs) { return !(lhs == rhs); // != defined in terms of operator== } // assumes that both objects refer to the same ISBN Sales_item& Sales_item::operator+=(const Sales_item& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // assumes that both objects refer to the same ISBN Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs) { Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return ret += rhs; // add in the contents of (|rhs|) return ret; // return (|ret|) by value } std::istream& operator>>(std::istream& in, Sales_item& s) { double price; in >> s.bookNo >> s.units_sold >> price; // check that the inputs succeeded if (in) s.revenue = s.units_sold * price; else s = Sales_item(); // input failed: reset object to default state return in; } std::ostream& operator<<(std::ostream& out, const Sales_item& s) { out << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return out; } double Sales_item::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } #endif