‍ď˜ďťŽď˜ďťŽďť? ﺎďş&#x; ﺊďş?ﺸﺎ ďť ďťŁďşŞďşďşłďťŞâ€Ź
C++ ‍ Ůˆ ŘŻŮˆ ن‏ blog version
version: 1.35 March 3, 2009
http://www.irancplusplus.blogspot.com/
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ﺳﺎلﻫﺎﺳﺖ ﻛﻪ ) C++ﺑﺨﻮاﻧﻴﺪ (C plus plusﻳﻜﻲ از زﺑﺎنﻫﺎي ﻣﺤﺒﻮب ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ و ﺷﺎﻳﺪ ﭘﺮ ﻃﺮﻓﺪارﺗﺮﻳﻦ زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﺳﺖ C++ .ﮔﺴﺘﺮﺷﻲ از زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ Cاﺳﺖ .ﻳﻌﻨﻲ ﺗﻘﺮﻳﺒﺎ ﻫﺮ ﺑﺮﻧﺎﻣﻪ Cﻳﻚ ﺑﺮﻧﺎﻣﻪي C++اﺳﺖ .ﺷﺎﻳﺪ اﺳﻢ C#را ﻫﻢ ،ﻛﻪ C Sharpﺗﻠﻔﻆ ﻣﻲﺷﻮد ،ﺷﻨﻴﺪه ﺑﺎﺷﻴﺪ C# .ﮔﺴﺘﺮش C++ﻧﻴﺴﺖ وﻟﻲ ﺑﻪ C++ﺷﺒﺎﻫﺖ دارد. در اﻳﻦ ﻧﻮﺷﺘﻪ ﻣﻲﺧﻮاﻫﻢ ﺳﺎﺧﺘﺎر زﺑﺎن C++را ﺑﻪ ﺷﻜﻞ ﻓﺸﺮده ﺑﺮاي ﺷﻤﺎ ﺑﮕﻮﻳﻢ .ﻫﺪف اﺻﻠﻲ ﻣﻦ ﻳﺎد دادن ﺗﻮاﺑﻊ ﻣﻮﺟﻮد در header fileﻫﺎ ﻧﻴﺴﺖ .درﺑﺎرهي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﺑﺮاي Windowsﻫﻢ ﭼﻴﺰي زﻳﺎدي ﻧﻤﻲﮔﻮﻳﻢ وﻟﻲ ﭼﻴﺰﻫﺎﻳﻲ ﻛﻪ اﻳﻦ ﺟﺎ ﻣﻲﮔﻮﻳﻢ ﺑﺮاي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ Windowsﻻزم ﻫﺴﺘﻨﺪ .ﻫﻤﻴﻦ ﻃﻮر ﻗﺼﺪ ﻧﺪارم ﺑﻪ ﻃﻮر ﺳﻴﺴﺘﻤﺎﺗﻴﻚ ﺑﻪ روشﻫﺎ و ﺷﮕﺮدﻫﺎي ﻣﺨﺘﻠﻒ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﺑﭙﺮدازم .ﺷﺎﻳﺪ ﺑﻌﺪﻫﺎ در ﻳﻚ ﻧﻮﺷﺘﻪ دﻳﮕﺮ اﻳﻦ روشﻫﺎ را ﺑﮕﻮﻳﻢ .وﻟﻲ اﮔﺮ واﻗﻌﺎ در ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﺳﺘﻌﺪاد داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﺧﻴﻠﻲ از اﻳﻦ روشﻫﺎ را ﺧﻮدﺗﺎن ﭘﻴﺪا ﻣﻲﻛﻨﻴﺪ اﮔﺮﭼﻪ داﻧﺴﺘﻦ آنﻫﺎ ﺑﺎﻋﺚ ﺻﺮﻓﻪ ﺟﻮﻳﻲ در وﻗﺖ ﻣﻲﺷﻮد. ﺑﺮاي ﻳﺎدﮔﻴﺮي C++ﺑﻬﺘﺮ اﺳﺖ Cرا ﻳﺎد ﻧﮕﺮﻓﺘﻪ ﺑﺎﺷﻴﺪ .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻗﺮار ﻧﻴﺴﺖ ﻣﻦ اول ﺷﻤﺎ را ﺑﺎ Cو ﺑﻌﺪ ﺑﺎ C++آﺷﻨﺎ ﻛﻨﻢ .ﺑﻪ ﻗﻮل ﺑﻌﻀﻲ ﻣﻴﻮه ﻓﺮوشﻫﺎ »ﺳﻮاﻳﻲ ﻧﻴﺲ ﻫﻤﻪ درﻫﻤﻪ!«
.
The ANSI Standardﻳﻚ ﺟﻮر اﺳﺘﺎﻧﺪارد ﺑﺮاي C++دارد .اﻳﻦ اﺳﺘﺎﻧﺪارد ﻛﻤﻚ ﻣﻲﻛﻨﺪ ﻛﻪ ﻳﻚ ﺑﺮﻧﺎﻣﻪي ﺧﺎص در compilerﻫﺎي ﻣﺨﺘﻠﻒ ﻗﺎﺑﻞ اﺟﺮا ﺑﺎﺷﺪ .ﻗﻮل ﻧﻤﻲدﻫﻢ ﻫﻤﻪ ﺑﺮﻧﺎﻣﻪﻫﺎي اﻳﻦ ﻧﻮﺷﺘﻪ اﺳﺘﺎﻧﺪارد ﺑﺎﺷﻨﺪ وﻟﻲ ﻫﻤﻪي ﺑﺮﻧﺎﻣﻪﻫﺎ را در Borland Developer Studio 2006 آزﻣﺎﻳﺶ ﻛﺮدهام ﺗﺎ ﻣﺸﻜﻠﻲ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻨﺪ .ﺑﻴﺶ ﺗﺮ آنﻫﺎ در Microsoft Visual C++ 2005 Express Edition ﻫﻢ آزﻣﺎﻳﺶ ﺷﺪهاﻧﺪ )و اﮔﺮ ﺑﺎ آن آزﻣﺎﻳﺶ ﻧﻜﺮده ﺑﺎﺷﻢ ﮔﻔﺘﻪام(. ﭼﻮن ﻧﻮﺷﺘﻪﻫﺎي ﺧﺸﻚ وﻋﻠﻤﻲ و ﺑﻲ روح را دوﺳﺖ ﻧﺪارم ﻫﺮ ﺟﺎ ﻛﻪ ﭼﻴﺰي ﺑﺮاي ﺧﻨﺪﻳﺪن ﭘﻴﺪا ﺷﺪه ﺑﻪ ﻧﻮﺷﺘﻪ اﺿﺎﻓﻪ ﻛﺮدهام .ﺣﺘﻲ ﻗﺼﺪ داﺷﺘﻢ ﻧﻮﺷﺘﻪ را ﺑﻪ زﺑﺎن ﻋﺎﻣﻴﺎﻧﻪ ﺑﻨﻮﻳﺴﻢ ﻛﻪ ﺑﻪ ﺗﻮﺻﻴﻪي ﻳﻜﻲ از دوﺳﺘﺎن ﻣﻨﺼﺮف ﺷﺪم وﻟﻲ ﻃﻮري ﻧﻮﺷﺘﻪام ﻛﻪ ﻣﻲﺷﻮد آن را ﻋﺎﻣﻴﺎﻧﻪ ﺧﻮاﻧﺪ .اﻣﻴﺪوارم ﻛﻪ ﺷﻮﺧﻲﻫﺎي )ﮔﺎﻫﻲ ﺑﻲ ﻣﺰهي( ﻣﻦ ﺑﺎﻋﺚ آزردﮔﻲ ﺷﻤﺎ ﻧﺸﻮد .اﻳﻦ ﻛﻪ ﻣﻲﺑﻴﻨﻴﺪ ﻓﻌﻞﻫﺎ را ﻣﻔﺮد ﺑﻪ ﻛﺎر ﺑﺮدهام )ﻣﺜﻼ ﺑﻪ ﺟﺎي »ﺑﮕﻮﻳﻴﻢ«» ،ﺑﮕﻮﻳﻢ« را ﺑﻪ ﻛﺎر ﺑﺮدهام( ﺑﻪ ﭘﻴﺮوي از Jesse Libertyﻧﻮﻳﺴﻨﺪهي ﭘﺮ ﻓﺮوش ﺗﺮﻳﻦ ﻛﺘﺎبﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﺳﺖ .ﻓﻜﺮ ﻣﻲﻛﻨﻢ ﻓﻌﻞﻫﺎي ﻣﻔﺮد ﺧﺸﻜﻲ ﻣﺘﻦ را ﻛﻢ ﻣﻲﻛﻨﻨﺪ
.
ﭼﻮن ﺑﺮاي ﻣﻦ ﺗﺼﻮر ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﺑﺪون ﻛﻤﻚ ﮔﺮﻓﺘﻦ از ِ helpﻳﻚ compilerﻏﻴﺮ ﻣﻤﻜﻦ اﺳﺖ و ِ help compilerﻫﺎ ﺑﻪ زﺑﺎن اﻧﮕﻠﻴﺴﻲ ﻧﻮﺷﺘﻪ ﺷﺪه اﺳﺖ ،ﺑﻬﺘﺮ اﺳﺖ اﺻﻄﻼﺣﺎت ﺗﺨﺼﺼﻲ ﺗﺎ ﺣﺪ اﻣﻜﺎن ﺑﻪ ﻓﺎرﺳﻲ ﺗﺮﺟﻤﻪ
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻧﺸﻮﻧﺪ .ﺿﻤﻦ اﻳﻦ ﻛﻪ ﻣﻌﻤﻮﻻ ﺑﺮاي ﻳﻚ واژه در ﻛﺘﺎبﻫﺎي ﻣﺨﺘﻠﻒ ﺗﺮﺟﻤﻪﻫﺎي ﻣﺘﻔﺎوﺗﻲ ﮔﻔﺘﻪ ﻣﻲﺷﻮد ﻛﻪ اﻳﻦ ﺧﻴﻠﻲ ﺧﻮب ﻧﻴﺴﺖ .ﺑﺮاي ﻫﻤﻴﻦ اﺻﻼ ﺳﻌﻲ ﻧﻜﺮدهام ﺑﺮاي واژﮔﺎن آﺷﻨﺎي اﻧﮕﻠﻴﺴﻲ ﻣﻌﺎدلﻫﺎي ﺑﻴﮕﺎﻧﻪي ﻓﺎرﺳﻲ ﭘﻴﺪا ﻛﻨﻢ . اﮔﺮ درﺑﺎرهي اﻳﻦ ﻧﻮﺷﺘﻪ ﭘﻴﺸﻨﻬﺎد ﻳﺎ اﻧﺘﻘﺎدي دارﻳﺪ ﻟﻄﻔﺎ ﺑﺮاي ﻣﻦ اﻳﻤﻴﻞ ﻛﻨﻴﺪ: CppBuilder2006@msn.com ﺣﺘﻤﺎ ﭘﻴﺸﻨﻬﺎدﻫﺎي ﺷﻤﺎ را در وﻳﺮاﻳﺶﻫﺎي ﺑﻌﺪي ﺑﻪ ﻛﺎر ﻣﻲﮔﻴﺮم. در ﻣﻮرد copyrightو ﺣﻖ ﻣﺆﻟﻒ ﻫﻢ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ اﻳﻦ ﻧﻮﺷﺘﻪ ﺑﺮاي اﺳﺘﻔﺎده ﻋﻤﻮم ﻣﺮدم اﺳﺖ و ﻫﺮ ﮔﻮﻧﻪ اﺳﺘﻔﺎده از آن )از ﻧﻈﺮ ﻣﻦ( آزاد اﺳﺖ. اﻳﻦ وﻳﺮاﻳﺶ دوم »ﺑﺎ C++آﺷﻨﺎ ﺷﻮﻳﻢ« اﺳﺖ .ﻓﺎﻳﻞ pdfو wordآن را ﻫﻤﺮاه ﺑﺎ ﺗﻮﺿﻴﺤﺎﺗﻲ درﺑﺎرهي وﻳﺮاﻳﺶ دوم در ﭘﺴﺖ ﺷﻤﺎرهي 13وﺑﻼگ ﻣﻦ ﻳﻌﻨﻲ http://www.irancplusplus.blogspot.com/ ﭘﻴﺪا ﻣﻲﻛﻨﻴﺪ .ﻋﻼوه ﺑﺮ اﻳﻤﻴﻞ ﺑﺎﻻ ﻣﻲﺗﻮاﻧﻴﺪ ﻧﻈﺮﻫﺎي ﺧﻮدﺗﺎن را در ﻗﺴﻤﺖ ﻧﻈﺮ وﺑﻼگ ﻗﺮار ﺑﺪﻫﻴﺪ. در اﻳﻦ وﻳﺮاﻳﺶ ﺑﻪ ﺣﺠﻢ ﻛﺘﺎب ﺣﺪود 20در ﺻﺪ اﺿﺎﻓﻪ ﺷﺪه .اﺷﺘﺒﺎﻫﺎت ﺗﺎﻳﭙﻲ در اﻳﻦ وﻳﺮاﻳﺶ ﻫﻢ ﭘﻴﺪا ﻣﻲﺷﻮد ﺑﻪ ﺧﺼﻮص در ﺑﺨﺶﻫﺎي اﺿﺎﻓﻪ ﺷﺪه .ﺑﻌﻀﻲ از اﺷﺘﺒﺎﻫﺎت ﺗﺎﻳﭙﻲ وﻳﺮاﻳﺶ ﻗﺒﻞ درﺳﺖ ﺷﺪهاﻧﺪ .در اﻳﻦ وﻳﺮاﻳﺶ از ﻓﻮﻧﺖ ” “2 Compsetاﺳﺘﻔﺎده ﺷﺪه ﻛﻪ ﺟﺰء ﻓﻮﻧﺖﻫﺎي ﻣﻌﻤﻮل در وﻳﻨﺪوز و wordﻧﻴﺴﺖ. در ﭘﺎﻳﺎن از ﻣﺴﺌﻮﻻن http://forum.p30world.comﺗﺸﻜﺮ ﻣﻲﻛﻨﻢ ﻛﻪ اﻳﻦ ﻧﻮﺷﺘﻪ را در وﺑﺴﺎﻳﺖ ﺧﻮدﺷﺎن uploadﻛﺮدهاﻧﺪ.
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻓﻬﺮﺳﺖ ان اول عه او در ار از ر ! ه #$ار"ه د& %ر if ' ار و (% * )ه ار* /ط -را %و ,ر $ن * ) commentه ر %ه ا& 0%د" از ,را ا& 0%د" از new ا ر" $ه و ,را ه ا! اد 1 23 ا! اد ا! 4ر * /ع unsigned )(getch 5ار (%ه و * )ه (%ه ! 6 ! ه ++و -- عه 6و& ; * 6 9 : ز 6 /و < ا 6 >= * @ * &? دوم ا اع د& %ره در C++ ! ه =6 B د& %ر if … else for C while C do … while C Dه د& %ر switch د& %ر goto ه sizeofو typeidو typedef
7 7 8 15 16 19 20 22 23 28 32 33 41 43 45 50 50 52 58 59 60 61 62 64 66 68 69 70 70 75 84 87 94 95 97 101 106 108
www.pupuol.com
â&#x20AC;Ťď&#x2DC;ď&#x2DC;ďť? ﺎďş&#x;ďť&#x160; ﺊďş?ﺸďŽ&#x2022;ďş&#x17D; ďť ďťŁďşŞďşďşłďťŞâ&#x20AC;Ź 114 115 116 120 127 129 131 133 136 139 140 141 141 142 146 155 156 158 161 161 164 170 178 179 180 193 201 201 204 207 210 212 215 220 232 237 240 242 256 263 265 274 280
www.pupuol.com
default-int 6$E â&#x20AC;ŤŮ&#x2C6;â&#x20AC;Ź LValue â&#x20AC;Ť Ů&#x2C6;â&#x20AC;ŹRValue prototype 6%4$â&#x20AC;Ť* )Ů&#x2021; Ů&#x2C6; * )Ů&#x2021; زâ&#x20AC;Ź pointer â&#x20AC;ŤŘąŘ§ Ů&#x2021;â&#x20AC;Ź, â&#x20AC;Ť * )Ů&#x2021; Ů&#x2C6;â&#x20AC;Ź â&#x20AC;Ť > ' Ř&#x;â&#x20AC;Ź#include default % â&#x20AC;Ť عاâ&#x20AC;Źâ&#x20AC;Ť ŘŻŮ&#x2020;â&#x20AC;Źoverload namespace â&#x20AC;Ť Ů&#x2C6;â&#x20AC;Źusing throw Ř&#x152;try Ř&#x152; catch ) * @ â&#x20AC;Ť ŘŻŘąŮ&#x2C6;Ů&#x2020;â&#x20AC;Źstatic register auto inline enum $ => â&#x20AC;ŤŘąŘ§ Ů&#x2021;â&#x20AC;Ź, void* â&#x20AC;Ť Řšâ&#x20AC;Ź$ "â&#x20AC;ŤŘ§ Řąâ&#x20AC;Ź * => ?& â&#x20AC;Ť* Ů&#x2021;â&#x20AC;Ź â&#x20AC;Ť & Ů&#x2026;â&#x20AC;Ź â&#x20AC;ŤŘłŮ&#x2021;â&#x20AC;ŹI constructor â&#x20AC;Ť Ů&#x2C6;â&#x20AC;Źdestructor copy constructor explicit this â&#x20AC;ŤŮ&#x2021;â&#x20AC;ŹOperator new ! conversion operator operator = private constructor â&#x20AC;ŤŮ&#x2021;â&#x20AC;Źmember â&#x20AC;Ť اع اŮ&#x2C6; دادŮ&#x2020;â&#x20AC;Ź method â&#x20AC;Ť Ů&#x2021;â&#x20AC;Źinline â&#x20AC;ŤŘłâ&#x20AC;ŹI â&#x20AC;Ť Ů&#x2C6;â&#x20AC;Ź6 ! â&#x20AC;Ť( Ů&#x2021;â&#x20AC;Ź% Ř&#x152;typedef static J!â&#x20AC;ŤŘ§â&#x20AC;Ź Template â&#x20AC;ŤŮ&#x2021;â&#x20AC;Ź friend member â&#x20AC;Ť Řš != اŮ&#x2020;â&#x20AC;Ź const â&#x20AC;ŤŮ&#x2021;â&#x20AC;Źmethod Inheritance virtual â&#x20AC;ŤŮ&#x2021;â&#x20AC;Źmethod virtual destructor dynamic_cast virtual inheritance abstract â&#x20AC;ŤŘłŮ&#x2021;â&#x20AC;ŹI
â&#x20AC;Ťď&#x2DC;ď&#x2DC;ďť? ﺎďş&#x;ďť&#x160; ﺊďş?ﺸďŽ&#x2022;ďş&#x17D; ďť ďťŁďşŞďşďşłďťŞâ&#x20AC;Ź 284 287 291 294 296 296 308 308 311 315 317 328 329 331 336 341 343 346 350 353 357 366 369 370 375 376 377 391
compiler < = â&#x20AC;ŤŘłŮ&#x2021; ŘŻŘąâ&#x20AC;ŹI â&#x20AC;Ť ŘŻ" ازâ&#x20AC;Ź0%&â&#x20AC;ŤŘ§â&#x20AC;Ź â&#x20AC;ŤŮ&#x2021;â&#x20AC;Źunion * => ?& â&#x20AC;Ť* Ů&#x2021;â&#x20AC;Ź â&#x20AC;Ť ŘąŮ&#x2026;â&#x20AC;ŹK> preprocessor â&#x20AC;Ť ŘąŮ&#x2021;â&#x20AC;Ź%&â&#x20AC;ŤŘŻâ&#x20AC;Ź precompiled headers " 9 :* L - â&#x20AC;Ť Ů&#x2C6;Ů&#x2021; ازâ&#x20AC;Ź => â&#x20AC;Ť ŘŻŘąâ&#x20AC;Ź%
extern linkage => â&#x20AC;Ť Ů&#x2C6; ŘŻŘąâ&#x20AC;Ź#define â&#x20AC;Ť ا! ادâ&#x20AC;ŹL 9 %? â&#x20AC;Ť Ů&#x2021;â&#x20AC;ŹM â&#x20AC;Ť Řą Ů&#x2021;â&#x20AC;Ź 6% â&#x20AC;Ť! Ů&#x2021;â&#x20AC;Ź calling convention stdio.h stdlib.h main() ) * â&#x20AC;Ť Ů&#x2020;Ů&#x2021;â&#x20AC;Ź$â&#x20AC;ŤŘąâ&#x20AC;Ź, stdarg.h Map string string.h vector fstream.h math.h windows.h â&#x20AC;Ť اعâ&#x20AC;ŹK â&#x20AC;Ť< اâ&#x20AC;Ź
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اول در اﻳﻦ ﻓﺼﻞ ﺑﺎ ﻫﻢ در C++دوري ﻣﻲزﻧﻴﻢ و ﺑﺎ ﺑﺨﺶﻫﺎي ﻣﺨﺘﻠﻔﺶ آﺷﻨﺎ ﻣﻲﺷﻮﻳﻢ.
عه
ﻧﻮع ﻳﻚ ﻣﺘﻐﻴﺮ ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎي ﺣﺎﻓﻈﻪي آن را ﻧﺸﺎن ﻣﻲدﻫﺪ .اﻟﺒﺘﻪ ﺗﻨﻬﺎ ﻛﺎر ﻧﻮع ،اﻳﻦ ﻧﻴﺴﺖ .وﻟﻲ ﻓﻌﻼ ﻫﻤﻴﻦ ﺗﺼﻮر از آن ﻛﺎﻓﻲ اﺳﺖ .ﺑﻌﻀﻲ از ﻧﻮعﻫﺎي ﻣﻮﺟﻮد در C++اﻳﻦﻫﺎ ﻫﺴﺘﻨﺪ: ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ )ﻋﺪد ﻏﻴﺮ اﻋﺸﺎري ﻛﻪ ﻣﻲﺗﻮاﻧﺪ ﻣﻨﻔﻲ ﻫﻢ ﺑﺎﺷﺪ(
4ﺑﺎﻳﺖ
int
ﻳﻚ ﻛﺎراﻛﺘﺮ )ﻳﻚ ﺣﺮف در زﺑﺎن اﻧﮕﻠﻴﺴﻲ(
1ﺑﺎﻳﺖ
char
ﻳﻚ ﮔﺰاره )ﻋﺒﺎرﺗﻲ ﻛﻪ ﻳﺎ درﺳﺖ ﻳﺎ ﻏﻠﻂ اﺳﺖ(
1ﺑﺎﻳﺖ
bool
ﻳﻚ رﺷﺘﻪ )دﺳﺘﻪاي از ﻛﺎراﻛﺘﺮﻫﺎ ﻛﻪ ﻳﻚ ﺟﻤﻠﻪ و ...ﺗﺸﻜﻴﻞ ﻣﻲدﻫﻨﺪ(
4ﺑﺎﻳﺖ
*char
ﻳﻚ ﻋﺪد اﻋﺸﺎري
8ﺑﺎﻳﺖ
double
)compilerﻫﺎي ﺧﻴﻠﻲ ﻗﺪﻳﻤﻲ boolرا اﺻﻼ ﻧﻤﻲ ﺷﻨﺎﺳﻨﺪ(. ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻣﺘﻐﻴﺮي داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﻋﺪد ﺻﺤﻴﺢ )ﻳﻌﻨﻲ ﻋﺪد ﻏﻴﺮ اﻋﺸﺎري( در ﺧﻮد ﺟﺎ ﺑﺪﻫﺪ ،ﻣﻲ- ﻧﻮﻳﺴﻴﻢ: ;int a
ﺣﺎل aﻣﻲﺗﻮاﻧﺪ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ را در ﺧﻮدش ﺟﺎ ﺑﺪﻫﺪ .ﺑﺮاي ﻗﺮار دادن ﻋﺪد 2در اﻳﻦ ﻣﺘﻐﻴﺮ ﻣﻲﺷﻮد ﻧﻮﺷﺖ ;a = 2
" "hello, byeﻳﻚ رﺷﺘﻪ اﺳﺖ .ﺑﺮاي ﻗﺮار دادن اﻳﻦ رﺷﺘﻪ در ﻳﻚ ﻣﺘﻐﻴﺮ ﻣﻲﻧﻮﻳﺴﻴﻢ: ;"char* c = "hello, bye
' 'gﻳﻚ ﻛﺎراﻛﺘﺮ اﺳﺖ و ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;'char ch = 'g
ﺑﻪ ﻗﺮار دادن ﻣﻘﺪار )ﻣﺜﻼ ﻋﺪد ﻳﺎ رﺷﺘﻪ( در ﻳﻚ ﻣﺘﻐﻴﺮ assignmentﻳﺎ assignﻛﺮدن ﻣﻲﮔﻮﻳﻴﻢ. ﻳﻌﻨﻲ در ﺑﺎﻻ ﺑﺎ ﻧﻮﺷﺘﻦ ;a = 2
7
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻋﺪد 2را ﺑﻪ assign ،aﻛﺮد هاﻳﻢ. ﻣﻲﺷﻮد دو ﻳﺎ ﭼﻨﺪ ﺑﺎر ﻣﻘﺪارﻫﺎي ﻣﺨﺘﻠﻒ را ﺑﻪ ﻳﻚ ﻣﺘﻐﻴﺮ assignﻛﺮد: ;double r = 2.3 ;r = 5 ;r = 7.91
ﭘﺲ از اﻧﺠﺎم اﻳﻦ دﺳﺘﻮرات ﻣﻘﺪار 7.91 ،rاﺳﺖ )ﺣﺘﻤﺎ ﻣﻲداﻧﻴﺪ ﻛﻪ ﻧﻘﻄﻪ در 7.91ﺑﻪ ﻣﻌﻨﻲ ﻣﻤﻴﺰ اﺳﺖ
(.
او
اول ﺑﺎﻳﺪ compilerﺧﻮدﺗﺎن را ﺑﺎز ﻛﻨﻴﺪ و ﻳﻚ projectاز ﻧﻮع consoleاﻳﺠﺎد ﻛﻨﻴﺪ .ﺑﮕﺬارﻳﺪ ﻧﺤﻮهي اﻳﺠﺎد projectرا در دو compilerﺧﻮدم ﺑﮕﻮﻳﻢ: ) :BDS 2006 (C++Builderاول از ﻃﺮﻳﻖ ﻣﻨﻮﻫﺎ ،ﻣﺴﻴﺮ زﻳﺮ را ﻃﻲ ﻛﻨﻴﺪ: …File | New | Other ﭘﻨﺠﺮهاي ﺑﺎ ﻧﺎم New Itemsﺑﺎز ﻣﻲﺷﻮد:
8
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
از ﻗﺴﻤﺖ Console Application ،C++Builder Projectsرا اﻧﺘﺨﺎب ﻛﻨﻴﺪ .ﭘﻨﺠﺮهاي دﻳﮕﺮ ﺑﺎ ﻧﺎم New Console Applicationﺑﺎز ﻣﻲﺷﻮد .ﺗﻨﻬﺎ Console Applicationو C++را ﺗﻴﻚ ﺑﺰﻧﻴﺪ:
و دﻛﻤﻪي OKرا ﻓﺸﺎر ﺑﺪﻫﻴﺪ .در ﻓﺎﻳﻞ .cppﻣﻲﺗﻮاﻧﻴﺪ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﺗﺎن را ﺑﻨﻮﻳﺴﻴﺪ.
:Visual C++ 2005 Express Editionاول از ﻃﺮﻳﻖ ﻣﻨﻮ ﻣﺴﻴﺮ زﻳﺮ را ﻃﻲ ﻛﻨﻴﺪ: 9
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ File | New | Project ﭘﻨﺠﺮهاي ﺑﺎ ﻧﺎم New Projectﺑﺎز ﻣﻲﺷﻮد .در ﺑﺨﺶ Project Typesروي Visual C++ﻛﻠﻴﻚ ﻛﻨﻴﺪ و Win32 Console Applicationرا اﻧﺘﺨﺎب ﻛﻨﻴﺪ .در ﻧﻮار ِ Nameﭘﺎﻳﻴﻦ ،ﻳﻚ اﺳﻢ ﺑﺮاي آن ﻣﺸﺨﺺ ﻛﻨﻴﺪ )ﺑﻬﺘﺮ اﺳﺖ اﺳﻢ consoleرا اﻧﺘﺨﺎب ﻛﻨﻴﺪ ﺗﺎ در اداﻣﻪ اﻳﻦ ﻧﻮﺷﺘﻪ راﺣﺖ ﺗﺮ ﺑﺎﺷﻴﺪ(:
دﮔﻤﻪي OKرا ﻓﺸﺎر ﺑﺪﻫﻴﺪ .ﭘﻨﺠﺮهاي ﺑﺎ ﻧﺎم Win32 Application Wizardﺑﺎز ﻣﻲﺷﻮد:
10
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
دﮔﻤﻪي Nextرا ﻓﺸﺎر ﺑﺪﻫﻴﺪ و Empty Projectرا ﺗﻴﻚ ﺑﺰﻧﻴﺪ:
11
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ و در آﺧﺮ Finishرا ﻓﺸﺎر ﺑﺪﻫﻴﺪ .در ﻗﺴﻤﺖ Solution Explorerروي ﻓﻮﻟﺪر Source Filesﻛﻠﻴﻚ ﻛﻨﻴﺪ و دﮔﻤﻪي Deleteرا روي ﺻﻔﺤﻪ ﻛﻠﻴﺪ ﻓﺸﺎر ﺑﺪﻫﻴﺪ ﺗﺎ ﺣﺬف ﺑﺸﻮد .ﻫﻤﻴﻦ ﻛﺎر را ﺑﺎ دو ﺗﺎ ﻓﻮﻟﺪر دﻳﮕﺮ ﻫﻢ اﻧﺠﺎم ﺑﺪﻫﻴﺪ. ﺣﺎﻻ روي ،consoleراﺳﺖ ﻛﻠﻴﻚ ﻛﻨﻴﺪ .ﻳﻚ ﻣﻨﻮ ﺑﺎز ﻣﻲﺷﻮد .از اﻳﻦ ﻣﻨﻮ ،اﻳﻦ ﻣﺴﻴﺮ را دﻧﺒﺎل ﻛﻨﻴﺪ: Add | New Item… | Visual C++ | C++ File (.cpp) | Name | Add در ﻓﺎﻳﻞ .cppﻣﻲﺗﻮاﻧﻴﺪ ﺑﺮﻧﺎﻣﻪي ﺧﻮد را ﺑﻨﻮﻳﺴﻴﺪ. ﻣﺮﺳﻮم اﺳﺖ ﻛﻪ اوﻟﻴﻦ ﺑﺮﻧﺎﻣﻪ ،ﺑﺮﻧﺎﻣﻪاي ﺑﺎﺷﺪ ﻛﻪ ﭘﻴﺎم "!"Hello World
را ﺑﺮ ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ ﭼﺎپ ﻣﻲﻛﻨﺪ ﻛﻪ ﻧﻮﻋﻲ ﺧﻮﺷﺎﻣﺪ ﮔﻮﻳﻲ ﺑﻪ دﻧﻴﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﺳﺖ» .ﭼﺎپ ﻛﺮدن« در اﻳﻦ ﺟﺎ ﻫﻴﭻ رﺑﻄﻲ ﺑﻪ » printﮔﺮﻓﺘﻦ روي ﻛﺎﻏـﺬ« ﻧﺪارد .اﻳﻦ اوﻟﻴﻦ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;"!cout<< "Hello World ;)(getch }
اﻳﻦ ﺑﺮﻧﺎﻣﻪ را در ﻓﺎﻳﻞ .cppﻛﻪ در Projectﺷﻤﺎ ﻗﺮار دارد ﻛﭙﻲ ﻛﻨﻴﺪ و ﻛﻠﻴﺪ Runرا در compilerﺧﻮد ﭘﻴﺪا ﻛﻨﻴﺪ و ﻓﺸﺎر ﺑﺪﻫﻴﺪ .ﺧﺮوﺟﻲ زﻳﺮ را درﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﺪ: Output: !Hello World
ﺣﺘﻤﺎ از دﻳﺪن اﻳﻦ ﻫﻤﻪ ﻛﻠﻤﺎت ﻋﺠﻴﺐ و ﻏﺮﻳﺐ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺷﻮﻛﻪ ﺷﺪهاﻳﺪ .اﺻﻼ ﻧﺘﺮﺳﻴﺪ ﺗﻨﻬﺎ ﻳﻚ ﺧﻂ از اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻬﻢ اﺳﺖ .ﺑﻘﻴﻪي ﺧﻂﻫﺎ در ﻫﻤﻪي ﺑﺮﻧﺎﻣﻪﻫﺎ ﻣﺸﺘﺮك ﻫﺴﺘﻨﺪ و ﻣﻲﺗﻮاﻧﻴﺪ آنﻫﺎ را ﻧﺎدﻳﺪه ﺑﮕﻴﺮﻳﺪ .ﺧﻂ ﻣﻬﻢ ﺑﺮﻧﺎﻣﻪ اﻳﻦ اﺳﺖ: ;"!cout<< "Hello World
ﻗﺒﻼ ﮔﻔﺘﻢ "! "Hello Worldﻳﻚ رﺷﺘﻪ اﺳﺖ .ﺑﺮاي ﭼﺎپ ﻛﺮدن ﻳﻚ رﺷﺘﻪ )ﻛﻪ رﺑﻄﻲ ﺑﻪ printﮔﺮﻓﺘﻦ روي ﻛﺎﻏـﺬ ﻧﺪارد( از ) coutﺑﺨﻮاﻧﻴﺪ (See Outاﺳﺘﻔﺎده ﻣﻲﺷﻮد .اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ: ;"char* c = "This is a test
12
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cﻳﻚ رﺷﺘﻪ اﺳﺖ و ﻣﻲﺷﻮد آن را ﺑﺎ coutروي ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ ﭼﺎپ ﻛﺮد .ﺑﺮاي ﭼﺎپ ﻣﻲﻧﻮﻳﺴﻢ: ;cout<< c
ﻳﻌﻨﻲ ﻗﺒﻞ از آن << coutﻣﻲﮔﺬارم .آﺧﺮ ﻫﻤﻪي دﺳﺘﻮرات در ««; ،C++ﻗﺮار ﻣﻲﮔﻴﺮد )ﺑﻪ ﺟﺰ ﻣﻮارد ﺧﺎﺻﻲ ﻛﻪ اﻳﻦ ﺟﺎ ﺟﺎي ﮔﻔﺘﻦ آنﻫﺎ ﻧﻴﺴﺖ( .اﺳﻢ ﻋﻼﻣﺖ »;« semicolon ،اﺳﺖ. ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻗﺒﻼ اﺷﺎره ﻛﺮدم ﺑﺨﺶ ﻣﺸﺘﺮك ﻫﻤﻪي ﺑﺮﻧﺎﻣﻪﻫﺎ اﻳﻦ اﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std )(int main { // your code ;)(getch }
اﮔﺮ compilerﺷﻤﺎ ﺑﻴﺶ از ﺣﺪ ﺟﺪﻳﺪ ﺑﺎﺷﺪ ﻣﻤﻜﻦ اﺳﺖ ﻣﺠﺒﻮر ﺷﻮﻳﺪ ﺑﻪ ﺟﺎي ;)( getchﺑﻨﻮﻳﺴﻴﺪ ;)( ._getchدر ﺿﻤﻦ ﺗﻐﻴﻴﺮ زﻳﺮ در compilerﻫﺎي ﻗﺪﻳﻤﻲ ﻻزم ،در BDS 2006اﻣﻜﺎن ﭘﺬﻳﺮ و در compilerﻫﺎي ﺟﺪﻳﺪ Microsoftﻏﻴﺮ ﻣﻤﻜﻦ اﺳﺖ: >#include <conio.h >#include <iostream.h )(int main { // your code ;)(getch }
دﺳﺘﻮرات ﺑﺮﻧﺎﻣﻪ در ﻗﺴﻤﺘﻲ ﻛﻪ ﺑﺎ // your codeﻣﺸﺨﺺ ﻛﺮدهام ﻗﺮار ﻣﻲﮔﻴﺮﻧﺪ .ﺑﻨﺎﺑﺮاﻳﻦ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;"char* c = "This is a test ;cout<<c ;)(getch }
Output: This is a test
13
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﺎ coutﻣﻲﺷﻮد ﻫﺮ ﭼﻴﺰ دﻳﮕﺮي را ﻫﻢ ﭼﺎپ ﻛﺮد .ﻣﺜﻼ ﻳﻚ ﻋﺪد ﻳﺎ ﻳﻚ ﻛﺎراﻛﺘﺮ )ﻛﺎراﻛﺘﺮ ﻳﻚ ﺣﺮف در اﻟﻔﺒﺎي اﻧﮕﻠﻴﺴﻲ اﺳﺖ( .ﺑﺮﻧﺎﻣﻪي زﻳﺮ دو ﺗﺎ ﻋﺪد را ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻣﻲﻛﻨﺪ و ﺣﺎﺻﻞ را ﭼﺎپ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a ;int b ;int c ;a = 2 ;b = 3 ;c = a + b ;cout<< c ;)(getch }
Output: 5
اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﻣﻲﺷﻮد ﺑﻪ ﺷﻜﻞ ﺳﺎده ﺗﺮي ﻫﻢ ﻧﻮﺷﺖ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 2, b = 3, c = a + b ;cout<< c ;)(getch }
Output: 5
در واﻗﻊ ﺑﻪ ﺟﺎي ;int a ;a = 2
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;int a = 2
و ﺑﻪ ﺟﺎي ;int a = 2 ;int b = 3
ﻣﻲﺷﻮد ﻧﻮﺷﺖ:
14
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int a = 2, b = 3
و در آﺧﺮ ﺑﻪ ﺟﺎي ;int a = 2, b = 3 ;int c = a + b
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;int a = 2, b = 3, c = a + b
و ﺑﻪ اﻳﻦ ﺷﻜﻞ از ﺗﻜﺮار intﺟﻠﻮﮔﻴﺮي ﻣﻲﺷﻮد .ﻣﻲﺷﻮد اﻳﻦ ﻗﺪر ﻣﺨﺘﺼﺮﻧﻮﻳﺴﻲ ﻛﺮد وﻟﻲ اﺻﻼ ﺧﻮب ﻧﻴﺴﺖ ﭼﻮ ن زﻳﺒﺎﻳﻲ و ﺳﺎدﮔﻲ ﻣﺘﻦ از دﺳﺖ ﻣﻲرود.
در ار از ر
ﺣﺘﻤﺎ ﻣﻲﭘﺮﺳﻴﺪ ﻛﺎرﺑﺮ دﻳﮕﺮ ﻛﻴﺴﺖ؟ ﻛﺎرﺑﺮ ﻓﻌﻼ ﺧﻮد ﺷﻤﺎ ﻫﺴﺘﻴﺪ .وﻗﺘﻲ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﺗﺎن را اﺟﺮا ﻣﻲﻛﻨﻴﺪ و ﺑﺮﻧﺎﻣﻪ در ﺣﺎل اﺟﺮاﺳﺖ ﺷﻤﺎ ﻣﻮﻗﺘﺎ از ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺑﻪ ﻛﺎرﺑﺮ ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮﻳﺪ .ﺑﻌﺪ ﺑﺎ ﻓﺸﺎر ﻳﻚ ﻛﻠﻴﺪ از keyboardاﺟﺮاي
ﺑﺮﻧﺎﻣﻪ ﺗﻤﺎم ﻣﻲﺷﻮد و ﺷﻤﺎ دوﺑﺎره ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻣﻲﺷﻮﻳﺪ و روزﮔﺎر وﺻﻞ ﺧﻮﻳﺶ ﺑﺎز ﻣﻲﺟﻮﻳﻴﺪ
.
ﺑﺮاي اﻳﻦ ﻛﻪ ﻛﺎرﺑﺮ ﺑﺘﻮاﻧﺪ ﺑﻪ ﻳﻚ ﻣﺘﻐﻴﺮ ﻣﻘﺪار ﺑﺪﻫﺪ ﺑﺎﻳﺪ از >> cinاﺳﺘﻔﺎده ﻛﻨﻴﻢ cin .ﺑﺮ ﻋﻜﺲ coutﻋﻤﻞ ﻣﻲﻛﻨﺪ cout .ﻣﻘﺪار ﻳﻚ ﻣﺘﻐﻴﺮ را در ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ ﻧﺸﺎن ﻣﻲدﻫﺪ و cinﻳﻚ ﻣﻘﺪار را از ﺻﻔﺤﻪ ﻧﻤﺎﻳﺶ ﻣﻲ- ﮔﻴﺮد و در ﻣﺘﻐﻴﺮ ﻗﺮار ﻣﻲدﻫﺪ. ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻣﻘﺪاري را از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد ،اﻳﻦ ﻣﻘﺪار را ﺑﻪ ﺗﻮان 2ﻣﻲرﺳﺎﻧﺪ و ﭼﺎپ ﻣﻲﻛﻨﺪ .ﻓﻘﻂ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ در C++ﺑﺮاي ﺿﺮب ﻛﺮدن دو ﻋﺪد از * اﺳﺘﻔﺎده ﻣﻲﺷﻮد ﻧﻪ از . >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a ;cin>> a ;cout<< a*a ;)(getch }
Output: 5
15
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 25
وﻗﺘﻲ ﻣﻦ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻛﺮدم اول ﺑﺮﻧﺎﻣﻪ ﻣﻨﺘﻈﺮ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ ﻣﺎﻧﺪ .ﻣﻦ ﻋﺪد 5را وارد ﻛﺮدم و ﻛﻠﻴﺪ Enterرا ﻓﺸﺎر دادم .ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻣﻲﺑﻴﻨﻴﺪ ﻋﺪد 25ﭼﺎپ ﺷﺪ. ﺷﺎﻳﺪ ﺑﺎ دﻳﺪن ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ اﻳﻦ ﺳﻮال ﺑﺮاﻳﺘﺎن ﻣﻄﺮح ﺷﺪه ﺑﺎﺷﺪ ﻛﻪ ﭼﺮا از ﺗﻮان اﺳﺘﻔﺎده ﻧﻜﺮدﻳﻢ؟ ﺧﻴﻠﻲ واﺿﺢ اﺳﺖ؛ ﭼﻮن در C++ﺑﺮ ﻋﻜﺲ Basicﻋﻤﻠﮕﺮ ﺗﻮان ﻧﺪارﻳﻢ )در C++ﻋﻤﻠﮕﺮ »^« ﻫﺴﺖ اﻣﺎ ﻣﻌﻨﻲ دﻳﮕﺮي دارد و ﺑﺮاي ﺗﻮان از آن اﺳﺘﻔﺎده ﻧﻤﻲﺷﻮد( .اﻟﺒﺘﻪ در آﻳﻨﺪه ﻣﺘﻮﺟﻪ ﺧﻮاﻫﻴﺪ ﺷﺪ ﻛﻪ C++آن ﻗﺪر اﻧﻌﻄﺎف دارد ﻛﻪ ﺧﻮد ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻣﻲﺗﻮاﻧﺪ ﻗﺎﺑﻠﻴﺖ »ﺑﻪ ﺗﻮان رﺳﺎﻧﺪن« را ﺑﻪ ﺑﺮﻧﺎﻣﻪي ﺧﻮدش اﺿﺎﻓﻪ ﻛﻨﺪ.
از cinﻣﻲﺷﻮد ﺑﺮاي ﮔﺮﻓﺘﻦ ﻫﺮ ﻧﻮع ﻣﻘﺪاري ﻣﺜﻞ ﻋﺪد ﺻﺤﻴﺢ ،ﻋﺪد اﻋﺸﺎري ،ﻛﺎراﻛﺘﺮ و رﺷﺘﻪ اﺳﺘﻔﺎده ﻛﺮد .اﻣﺎ ﻓﻌﻼ ﺑﺮاي ﮔﺮﻓﺘﻦ رﺷﺘﻪ از آن اﺳﺘﻔﺎده ﻧﻜﻨﻴﺪ ﭼﻮن در ﻣﻮرد رﺷﺘﻪ ،ﭼﻴﺰﻫﺎﻳﻲ ﻫﺴﺖ ﻛﻪ ﻫﻨﻮز ﻧﮕﻔﺘﻪام. ه
ﻗﺒﻼ ﺑﺎ ﻋﻤﻠﮕﺮﻫﺎﻳﻲ ﻣﺜﻞ ﺟﻤﻊ و ﺿﺮب آﺷﻨﺎ ﺷﺪﻳﺪ .در C++ﻋﻤﻠﮕﺮﻫﺎي زﻳﺎدي ﻫﺴﺖ ﺑﻌﻀﻲ از آنﻫﺎ را در ﺟﺪول زﻳﺮ ﻣﻲﺑﻴﻨﻴﺪ: ﻣﺜﺎل
a + b
ﺗﻮﺿﻴﺤﺎت ﺟﻤﻊ
+
a – b
ﺗﻔﺮﻳﻖ
-
a * b
ﺿﺮب
*
a / b
ﺗﻘﺴﻴﻢ
/
++aﻳﺎ a++اﺿﺎﻓﻪ ﻛﺮدن ﻳﻚ واﺣﺪ ﻛﻢ ﻛﺮدن ﻳﻚ واﺣﺪ –aﻳﺎ a-- a % b ﺑﺎﻗﻴﻤﺎﻧﺪهي ﺗﻘﺴﻴﻢ دو ﻋﺪد
ﻋﻤﻠﮕﺮ
++ -%
ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ را از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد ﻳﻜﻲ ﺑﻪ آن اﺿﺎﻓﻪ ﻣﻲﻛﻨﺪ ﺣﺎﺻﻞ را ﺑﻪ ﺗﻮان 2ﻣﻲرﺳﺎﻧﺪ و از ﺣﺎﺻﻞ ﻳﻜﻲ ﻛﻢ ﻣﻲﻛﻨﺪ و ﻧﺘﻴﺠﻪ را ﭼﺎپ ﻣﻲﻛﻨﺪ .ﺑﻪ ﻃﻮر ﺳﺎده ﺗﺮ ﺑﺮﻧﺎﻣﻪ ،را از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد و را ﭼﺎپ ﻣﻲﻛﻨﺪ.
16
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
>#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a ;cin>> a ;a++ ;int b = a*a ;b-- ;cout<< b ;)(getch }
Output: 5 35
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ دو ﺗﺎ ﻣﺘﻐﻴﺮ ﺻﺤﻴﺢ aو bﺗﻌﺮﻳﻒ ﺷﺪهاﻧﺪ .ﻣﻲﺷﻮد آنﻫﺎ را ﺑﻪ ﺗﻨﻬﺎ ﻳﻚ ﻣﺘﻐﻴﺮ ﺻﺤﻴﺢ ﺗﺒﺪﻳﻞ ﻛﺮد. ﺑﺮاي اﻳﻦ ﻛﺎر ﺑﺎﻳﺪ ﺗﻮﺟﻪ ﻛﻨﻴﻢ ﻛﻪ در ) assignmentﻳﻌﻨﻲ ﻫﻤﺎن ﺗﺴﺎوي( اول ﻃﺮف راﺳﺖ ﺗﺴﺎوي ﺣﺴﺎب ﻣﻲ- ﺷﻮد و ﺑﻌﺪ ﻣﻘﺪار ﺣﺴﺎب ﺷﺪه در ﺳﻤﺖ ﭼﭗ ﺗﺴﺎوي ﻛﭙﻲ ﻣﻲﺷﻮد .ﻣﺜﻼ در ;b = a*a
اول a*aﺣﺴﺎب ﻣﻲﺷﻮد و اﻳﻦ ﻣﻘﺪار ﺣﺴﺎب ﺷﺪه در bﻗﺮار ﻣﻲﮔﻴﺮد .ﺣﺎﻻ اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ: ;a = a*a
ﺑﺎز ﻫﻢ ﻫﻤﻴﻦ اﺗﻔﺎق ﻣﻲاﻓﺘﺪ ﻳﻌﻨﻲ ﻣﺜﻼ اﮔﺮ ﻣﻘﺪار aﻋﺪد 5ﺑﺎﺷﺪ ﺑﻌﺪ از اﺟﺮاي اﻳﻦ دﺳﺘﻮر ﻣﻘﺪار aﺑﻪ 25ﺗﻐﻴﻴﺮ ﻣﻲ- ﻛﻨﺪ .اﻳﻦ ﺟﺎﺳﺖ ﻛﻪ ﺳﺮ و ﺻﺪاي رﻳﺎﺿﻴﺪاﻧﺎن ﻣﺤﺘﺮم ) ﻛﻪ اﻟﺒﺘﻪ ﻣﻦ ﺧﻮدم را از آنﻫﺎ ﺟﺪا ﻧﻤﻲداﻧﻢ( ﺑﻠﻨﺪ ﻣﻲﺷﻮد ﻛﻪ ﭼﻨﻴﻦ ﭼﻴﺰي اﻣﻜﺎن ﻧﺪارد .اﻟﺒﺘﻪ آنﻫﺎ ﺣﻖ دارﻧﺪ ﭼﻮن وﻗﺘﻲ در رﻳﺎﺿﻲ ﻣﺘﻐﻴﺮي ﻣﺜﻞ ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد دﻳﮕﺮ اﺟﺎزه- ي ﺗﻐﻴﻴﺮ ﻧﺪارد )ﻣﺘﻐﻴﺮي ﻛﻪ ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﻪ!
( .ﻣﺘﻐﻴﺮ ﻣﻲﮔﻮﻳﻨﺪ ﭼﻮن ﻣﻘﺪارش ﻣﻌﻠﻮم ﻧﻴﺴﺖ .رﻳﺎﺿﻴﺪاﻧﺎن
ﺣﺪاﻛﺜﺮ ﻛﺎري ﻛﻪ ﻣﻲﺗﻮاﻧﻨﺪ ﺑﺮاي ﻣﻘﺪار دﻫﻲ اﻧﺠﺎم دﻫﻨﺪ اﻳﻦ اﺳﺖ ﻛﻪ ﻓﺮض ﻛﻨﻨﺪ ﺑﺮاﺑﺮ ﺑﺎ 1ﻳﺎ 5ﻳﺎ ﭼﻴﺰ دﻳﮕﺮي اﺳﺖ .در C++ﻧﺎم ﻳﻚ ﻣﺘﻐﻴﺮ )ﻣﺜﻞ (aﻧﺸﺎن دﻫﻨﺪه ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪي RAMاﺳﺖ )ﺑﻪ زﺑﺎن ﺷﻨﺎﺳﺎن ﻋﺰﻳﺰ )و ﻛﻤﻲ اﻳﺮادﮔﻴﺮ( ﻣﻲﮔﻢ ﻛﻪ درﺳﺘﻪ ﻛﻪ ﺗﺮﻛﻴﺐ »ﺣﺎﻓﻈﻪي ) «RAMﺗﻘﺮﻳﺒﺎ( ﻏﻠﻄﻪ و ﭘﺴﻨﺪﻳﺪه ﺗﺮ آن ﺑﻮد ﻛﻪ ﻓﻘﻂ ﺑﻨﻮﻳﺴﻢ » «RAMوﻟﻲ ﺧﺐ اﻧﻘﺪر اﻳﺮاد ﻧﮕﻴﺮﻳﺪ دﻳﮕﻪ
17
www.pupuol.com
(
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﺘﻐﻴﺮﻫﺎي ،C++ﺑﺮﻋﻜﺲ ﻣﺘﻐﻴﺮﻫﺎي رﻳﺎﺿﻲ ،ﻣﻘﺪار ﻧﻴﺴﺘﻨﺪ ﺑﻠﻜﻪ ﻣﻜﺎنﻫﺎﻳﻲ از ﺣﺎﻓﻈﻪ ﻫﺴﺘﻨﺪ .ﺑﻨﺎﺑﺮاﻳﻦ ﻣﻘﺪار آنﻫﺎ ﻣﻲﺗﻮاﻧﺪ در ﻃﻮل ﺑﺮﻧﺎﻣﻪ ﺗﻐﻴﻴﺮ ﻛﻨﺪ .ﻗﺒﻼ ﺗﻌﺮﻳﻒ ) assignmentﻫﻤﺎن ﺗﺴﺎوي( را دﻳﺪﻳﺪ assignment .در ﺣﻘﻴﻘﺖ ﻣﻘﺪار ﻣﺘﻐﻴﺮ را ﻋﻮض ﻣﻲﻛﻨﺪ .ﻣﺜﻼ a=2ﻣﻘﺪار ﻗﺴﻤﺘﻲ از )ﺣﺎﻓﻈﻪي( RAMرا ﻛﻪ ﺑﺎ aﻣﺸﺨﺺ ﺷﺪه 2 ﻣﻲﮔﺬارد .ﺣﺎل ﻣﻲﺗﻮاﻧﻴﻢ ﺑﺮﻧﺎﻣﻪي ﻗﺒﻠﻲ را ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a ;cin>> a ;a++ ;a = a*a ;--a ;cout<< a ;)(getch }
Output: 5 35
اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﻓﻘﻂ ﺑﺮاي آﻣﻮزش اﻳﻦ ﻃﻮر ﻧﻮﺷﺘﻪام .اﮔﺮ اﻳﻦ ﻫـﺪف را ﻧﺪاﺷﺘﻢ آن را ﺧﻴﻠﻲ راﺣﺖ ﺑﻪ ﺻﻮرت زﻳﺮ ﻣﻲﻧﻮﺷﺘﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a ;cin>> a ;cout<< (a+1)*(a+1)-1 ;)(getch }
Output: 5 35
18
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ! ار ه
در ﺟﺪول ﺻﻔﺤﻪ 3ﻧﻮﻋﻲ ﺑﺎ ﻧﺎم boolﻣﻌﺮﻓﻲ ﺷﺪ .در آن ﺟﺎ ﮔﻔﺘﻢ ﻫﺮ ﻣﺘﻐﻴﺮ ﺑﺎ اﻳﻦ ﻧﻮع ﻳﻚ ﮔﺰاره اﺳﺖ و ﮔﺰاره ﻋﺒﺎرﺗﻲ اﺳﺖ ﻛﻪ ﻳﺎ درﺳﺖ اﺳﺖ ﻳﺎ ﻏﻠﻂ .ﻗﺒﻞ از اﻳﻦ ﻛﻪ ﺑﻴﺶﺗﺮ ﺗﻮﺿﻴﺢ ﺑﺪﻫﻢ ﺑﮕﺬارﻳﺪ ﻋﻤﻠﮕﺮﻫﺎي ﻣﻨﻄﻘﻲ را ﻣﻌﺮﻓﻲ ﻛﻨﻢ: ﻣﺜﺎل
ﺗﻮﺿﻴﺤﺎت
ﻋﻤﻠﮕﺮ
a < b
آﻳﺎ ﻛﻮﭼﻚ ﺗﺮ اﺳﺖ؟
<
6 > 2
آﻳﺎ ﺑﺰرگ ﺗﺮ اﺳﺖ؟
>
5 <= 5آﻳﺎ ﻛﻮﭼﻚ ﺗﺮ ﻳﺎ ﻣﺴﺎوي اﺳﺖ؟
=<
a >= b
آﻳﺎ ﺑﺰرگ ﺗﺮ ﻳﺎ ﻣﺴﺎوي اﺳﺖ؟
=>
a == 2
آﻳﺎ ﻣﺴﺎوي اﺳﺖ؟
==
1 != 2
آﻳﺎ ﻣﺴﺎوي ﻧﻴﺴﺖ؟
=!
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ >= و <= )ﻛﻪ در آنﻫﺎ ﺗﺴﺎوي ،ﺳﻤﺖ ﭼﭗ اﺳﺖ( در C++ﻋﻤﻠﮕﺮ ﻧﻴﺴﺘﻨﺪ .در ﺿﻤﻦ ﺑﻴﻦ < ﻳﺎ > و = ﻧﺒﺎﻳﺪ ﻫﻴﭻ ﻓﺎﺻﻠﻪاي ﺑﺎﺷﺪ .ﻳﻌﻨﻲ اﮔﺮ ﺑﻨﻮﻳﺴﻴﺪ = 5
) errorﺑﻪ ﻣﻌﻨﻲ ﺧﻄﺎﺳﺖ
< compiler ،5ﻳﻚ errorﺻﺎدر ﻣﻲﻛﻨﺪ.
در دن ه وج و ت از "!ر%
(.
ﺣﺎﻻ ﻣﻲﺗﻮاﻧﻴﻢ ﻳﻚ ﻣﺘﻐﻴﺮ از ﻧﻮع boolﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ و ﺑﻪ آن ﻣﻘﺪار ﺑﺪﻫﻴﻢ: ;bool b ;)b = (2 < 3
ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع boolﺗﻨﻬﺎ ﻣﻲﺗﻮاﻧﺪ دو ﻣﻘﺪار داﺷﺘﻪ ﺑﺎﺷﺪ true :ﻳﺎ false. trueﺑﻪ ﻣﻌﻨﻲ »درﺳﺖ« و falseﺑﻪ ﻣﻌﻨﻲ »ﻏﻠﻂ« اﺳﺖ .ﻣﺜﻼ ﻣﻲداﻧﻴﻢ ﻛﻪ ) (2<3ﺑﺮاﺑﺮ ﺑﺎ trueو ) (6==5ﺑﺮاﺑﺮ ﺑﺎ falseاﺳﺖ. اﮔﺮ ﻳﻚ ﻣﻘﺪار ﺑﺎ ﻧﻮع boolرا ﺑﺎ coutﭼﺎپ ﻛﻨﻴﺪ 0ﻳﺎ 1ﭼﺎپ ﻣﻲﺷﻮد 0 .ﺑﺮاي falseو 1ﺑﺮاي .trueﻣﺜﻼ >#include <conio.h >#include <iostream ;using namespace std )(int main { ;)bool b = (2 == 2 ;cout<< b ;)(getch
19
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output: 1
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ b==2ﻳﻌﻨﻲ »ﻣﻘﺪار bﺑﺮاﺑﺮ ﺑﺎ 2اﺳﺖ« وﻟﻲ b = 2ﻳﻌﻨﻲ »ﻣﻘﺪار bرا ﺑﻪ 2ﺗﺒﺪﻳﻞ ﻛﻦ« .اﮔﺮ ﻫﻨﻮز ﻣﻨﻈﻮر ﻣﻦ را از ﮔﺰاره ﻣﺘﻮﺟﻪ ﻧﺸﺪهاﻳﺪ ﺣﻖ دارﻳﺪ .اﻳﻦ ﺑﻪ اﻳﻦ ﺧﺎﻃﺮ اﺳﺖ ﻛﻪ ﻫﻨﻮز دﺳﺘﻮر ifرا ﻣﻌﺮﻓﻲ ﻧﻜﺮدهام.
د "#ر if
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﻢ ﻛﻪ ﻋﺪدﻫﺎي زوج و ﻓﺮد را ﺗﺸﺨﻴﺺ دﻫﺪ ﺑﺎﻳﺪ از ifاﺳﺘﻔﺎده ﻛﻨﻴﻢ .ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻳﻚ intرا )ﻳﻌﻨﻲ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ را( از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد ،اﮔﺮ زوج ﺑﻮد ﻋـﺒﺎرت " "it is evenو اﮔﺮ ﻓﺮد ﺑﻮد ﻋـﺒﺎرت " "it is oddرا ﭼﺎپ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a ;cin>> a )if(a%2 == 0 ;"cout<< "it is even )if(a%2 == 1 ;"cout<< "it is odd ;)(getch }
Output: 7 it is odd
اﻳﻦ ﺑﺮﻧﺎﻣﻪ اول ﺣﺎﻓﻈﻪاي ﺑﻪ اﻧﺪارهي 4ﺑﺎﻳﺖ ﺑﺮاي ﻳﻚ intﺑﺎ اﺳﻢ aﺗﺨﺼﻴﺺ ﻣﻲدﻫﺪ .ﺑﻌﺪ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد و آن را در aﻗﺮار ﻣﻲدﻫﺪ .اﮔﺮ ﺑﺎﻗﻲ ﻣﺎﻧﺪهي aﺑﺮ 2ﺻﻔﺮ ﺑﺎﺷﺪ )ﻳﻌﻨﻲ اﮔﺮ زوج ﺑﺎﺷﺪ( ،ﭘﻴﺎم it
is evenﭼﺎپ ﻣﻲﺷﻮد .اﮔﺮ ﺑﺎﻗﻲ ﻣﺎﻧﺪهي aﺑﺮ 2ﻳﻚ ﺑﺎﺷﺪ ،ﭘﻴﺎم it is oddﭼﺎپ ﻣﻲﺷﻮد .ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﭼﻮن ﺑﺎﻗﻲ ﻣﺎﻧﺪهي 7ﺑﺮ 2ﻳﻚ اﺳﺖ )ﻳﻌﻨﻲ ﭼﻮن 7ﻓﺮد اﺳﺖ( ﭘﻴﺎم it is oddﭼﺎپ ﺷﺪه اﺳﺖ .ﻣﻲﺷﻮد ﮔﻔﺖ ifﺳﺎﺧﺘﺎر زﻳﺮ را دارد:
20
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )67ارif(3 ;د89:ر
ﭘﺮاﻧﺘﺰﻫﺎي ﺑﻌﺪ از ifﺿﺮوري ﻫﺴﺘﻨﺪ .اﮔﺮ ﻣﻘﺪار 23ار ،/ﺑﺮاﺑﺮ ﺑﺎ trueﺑﺎﺷﺪ )ﻳﻌﻨﻲ اﮔﺮ درﺳﺖ ﺑﺎﺷﺪ(، د456ر اﺟﺮا ﻣﻲﺷﻮد وﮔﺮﻧﻪ د456ر ﺑﺪون اﺟﺮا ﻧﺎدﻳﺪه ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد .ﻣﺜﻼ )if(3 > 4 ;"cout<< "hello
ﻫﻴﭻ ﻛﺎري اﻧﺠﺎم ﻧﻤﻲدﻫﺪ ﭼﻮن 3 > 4ﻧﺎدرﺳﺖ اﺳﺖ .ﺧﻮد )if(3 > 4 ;"cout<< "hello
ﻳﻚ دﺳﺘﻮر اﺳﺖ .ﭘﺲ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;"cout<< "hello
)if(a == 4 )if(3 > 4
ﻫﻴﭻ ﻻزم ﻧﻴﺴﺖ اﻳﻦ دﺳﺘﻮرات در دو ﻳﺎ ﭼﻨﺪ ﺧﻂ ﻧﻮﺷﺘﻪ ﺑﺸﻮﻧﺪ .در C++ﺣﺘﻲ ﻣﻲﺷﻮد ﻳﻚ ﺑﺮﻧﺎﻣﻪي ﺧﻴﻠﻲ ﻃﻮﻻﻧﻲ را در دو ﻳﺎ ﺳﻪ ﺧﻂ ﻧﻮﺷﺖ .اﻣﺎ واﺿﺢ اﺳﺖ ﻛﻪ اﻳﻦ ﻛﺎر اﺻﻼ ﻣﻨﺎﺳﺐ ﻧﻴﺴﺖ .در ﺿﻤﻦ ﮔﺬاﺷﺘﻦ =) spaceﺟﺎي ﺧﺎﻟﻲ( و tabﺑﻪ ﻫﺮ ﺗﻌﺪاد ﻣﺠﺎز اﺳﺖ و اﺿﺎﻓﻲﻫﺎ ﻧﺎدﻳﺪه ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮﻧﺪ .ﻣﻦ در ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪﻫﺎ از ﻳﻚ اﺳﺘﺎﻧﺪارد ﭘﻴﺮوي ﻣﻲﻛﻨﻢ و ﺷﻤﺎ ﻫﻢ از ﻫﻤﻴﻦ اﺑﺘﺪا ﺑﺎﻳﺪ ﭼﻨﻴﻦ اﺳﺘﺎﻧﺪاردي را رﻋﺎﻳﺖ ﻛﻨﻴﺪ وﮔﺮﻧﻪ ﻫﺮﮔﺰ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺣﺮﻓﻪاي ﻧﻤﻲﺷﻮﻳﺪ .ﺑﺎ ﺗﺎﻛﻴﺪ ﻓﺮاوان ﻣﻲﮔﻮﻳﻢ ﻛﻪ ﺣﺘﻤﺎ ﺑﻪ زﻳﺒﺎﻳﻲ و اﺳﺘﺎﻧﺪارد ﺑﻮدن ﻧﺤﻮهي ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ و ﺗﻨﻈﻴﻢ ﺧﻄﻮط ﺗﻮﺟﻪ ﻛﻨﻴﺪ .ﺷﺎﻳﺪ اﻳﻦ اﺻﻼ ﺑﺮاﻳﺘﺎن ﻣﻬﻢ ﺑﻪ ﻧﻈﺮ ﻧﺮﺳﺪ وﻟﻲ واﻗﻌﺎ ﻣﻬﻢ اﺳﺖ. ﻫﺮ دو ﺻﻮرت ﻧﻮﺷﺘﻦ ﻛﻪ در زﻳﺮ ﻧﺸﺎن داده ﺷﺪه ﻏﻴﺮ ﻋﺎدي و ﻏﻴﺮ اﺳﺘﺎﻧﺪارد اﺳﺖ وﻟﻲ از ﻧﻈﺮ compilerﻣﺠﺎز اﺳﺖ و ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺑﺪون errorاﺟﺮا ﺧﻮاﻫﺪ ﺷﺪ: if ( == a 2 ) cout << ""hello ; ;"if(a==2)cout<<"hello
)1
)2
ﺷﻜﻞ اﺳﺘﺎﻧﺪارد اﻳﻦ اﺳﺖ: )if(a==2 ;"cout<<"hello
trueﻳﻚ ﮔﺰارهي درﺳﺖ اﺳﺖ و falseﻳﻚ ﮔﺰارهي ﻏﻠﻂ اﺳﺖ .ﺑﻨﺎﺑﺮاﻳﻦ
21
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )if(true ;"cout<<"hello
و )if(1 == 1 ;"cout<<"hello
و )
(1 == 1) == true ;"cout<<"hello
(if
و ;"cout<<"hello
ﻫﻤﻪ ﻳﻚ ﺟﻮر ﻋﻤﻞ ﻣﻲﻛﻨﻨﺪ .ﻧﻮﺷﺘﻦ )if(false ;"cout<<"hello
ﻫﻢ ﺑﺎ ﻧﻨﻮﺷﺘﻦ آن ﻳﻜﻲ اﺳﺖ. ﻣﻤﻜﻦ اﺳﺖ ﻫﻨﻮز ﻗﺎدر ﻧﺒﺎﺷﻴﺪ ﻣﻌﻨﻲ ﮔﺰاره را ﺧﻴﻠﻲ ﺧﻮب ﺑﻴﺎن ﻛﻨﻴﺪ وﻟﻲ ﺣﺎﻻ دﻳﮕﺮ ﺑﺎﻳﺪ ﺑﺘﻮاﻧﻴﺪ از ifاﺳﺘﻔﺎده ﻛﻨﻴﺪ .ﻫﻨﻮز ﭼﻴﺰﻫﺎﻳﻲ ﻫﺴﺖ ﻛﻪ ﻧﮕﻔﺘﻪام
.
$ار و " %
ﺣﺎل ﻓﺮض ﻛﻨﻴﺪ aو bدو ﻣﺘﻐﻴﺮ از ﻧﻮع intﻫﺴﺘﻨﺪ a==b .ﻳﻌﻨﻲ ﻣﺤﺘﻮﻳﺎت دو ﻣﺤﻞ از ﺣﺎﻓﻈﻪ ﻛﻪ ﺑﺎ aو bﺑﻪ آنﻫﺎ اﺷﺎره ﻣﻲﺷﻮد ﻣﺴﺎوي ﻫﺴﺘﻨﺪ ﻣﺜﻼ ﺑﻪ ﺷﻜﻞ زﻳﺮ ﻫﺴﺘﻨﺪ: a: 0 1 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 0 0 1 1 b: 0 1 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 0 0 1 1
ﺣﺘﻤﺎ ﻣﻲداﻧﻴﺪ ﻛﻪ ﻫﻤﻪ ﭼﻴﺰ ﺣﺘﻲ اﻋﺪاد در ﻛﺎﻣﭙﻴﻮﺗﺮ ﺑﻪ ﺻﻔﺮ و ﻳﻚ ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮﻧﺪ و ﺑﻌﺪ ذﺧﻴﺮه ﻣﻲﺷﻮﻧﺪ .در واﻗﻊ 01101111000011100001111111010011ﻫﻤﺎن ﻋﺪد 1863196627اﺳﺖ) .ﺑﺮاي اﻳﻦ ﻛﻪ ﭼﺸﻤﺎﺗﻮن ﺑﻪ ﺧﺎﻃﺮ ﺧﻮاﻧﺪن اﻳﻦ ﻧﻮﺷﺘﻪ ﺧﻴﻠﻲ درد ﻧﮕﻴﺮه
)و ﻧﻪ ﺑﺮاي اﻳﻦ ﻛﻪ ﺧﻮدم از ﺷﺮ ﺗﺎﻳﭗ ﻛﺮدن راﺣﺖ
ﺑﺸﻢ( ،ﻧﺤﻮهي ﺗﺒﺪﻳﻞ 01101111000011100001111111010011رو ﺑﻪ 1863196627ﻧﻤﻲﮔﻢ .ﺑﻪ ﻫﺮ ﺣﺎل ﻫﺮ ﺟﺎ ﻧﻴﺎز ﺑﻪ ﺗﺒﺪﻳﻞ داﺷﺘﻴﺪ از ﻣﺎﺷﻴﻦ ﺣﺴﺎب وﻳﻨﺪوز ﻛﻤﻚ ﺑﮕﻴﺮﻳﺪ(.
22
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﭘﺲ a==bﻳﻌﻨﻲ »ﻣﻘﺪار aو bﻳﻜﻲ اﺳﺖ« .در ﺿﻤﻦ a==2ﻳﻌﻨﻲ »ﻣﻘﺪار aﻣﺴﺎوي ﺑﺎ 2اﺳﺖ« و »3==2
ﻳﻌﻨﻲ 2ﺑﺮاﺑﺮ اﺳﺖ ﺑﺎ .«3 ﻣﻨﻈﻮر ﻣﻦ از ﻣﻄﺎﻟﺐ اﻳﻦ ﺑﺨﺶ اﻳﻦ اﺳﺖ ﻛﻪ ﻣﺘﻐﻴﺮ aﺑﺎ ﻣﻘﺪارش ﻣﺘﻔﺎوت اﺳﺖ اﻣﺎ وﻗﺘﻲ ﺑﺨﻮاﻫﻴﻢ از ﻣﻘﺪار a
اﺳﺘﻔﺎده ﻛﻨﻴﻢ ﺧﻮد aرا ﻣﻲﻧﻮﻳﺴﻢ .در a ،a=1ﺑﻪ ﻋﻨﻮان ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪ اﺳﺖ وﻟﻲ در a ،a==1ﺑﻪ ﻋﻨﻮان ﻣﻘﺪار ﺑﻪ ﻛﺎر رﻓﺘﻪ اﺳﺖ.
' &ه
ﺣﺘﻤﺎ در رﻳﺎﺿﻲ دورهي دﺑﻴﺮﺳﺘﺎن ﺑﺎ ﻣﻔﻬﻮم ﺗﺎﺑﻊ آﺷﻨﺎ ﺷﺪهاﻳﺪ )اﮔﺮ ﻫﻨﻮز ﺑﻪ دور هي دﺑﻴﺮﺳﺘﺎن ﻧﺮﺳﻴﺪهاﻳﺪ ﻳﺎ ﺑﻪ ﻫﺮ دﻟﻴﻞ دﻳﮕﺮ ﺑﺎ ﻣﻔﻬﻮم ﺗﺎﺑﻊ آﺷﻨﺎ ﻧﻴﺴﺘﻴﺪ .اﺑﺘﺪا اﻳﻦ ﺑﺨﺶ را ﺑﺨﻮاﻧﻴﺪ و اﮔﺮ ﻣﺘﻮﺟﻪ ﻧﺸﺪﻳﺪ ﺑﻪ ﭘﺎﻳﮕﺎه ﻛﺘﺎبﻫﺎي درﺳﻲ اﻳﺮان )اﺣﺘﻤﺎﻻ ﺑﺎ آدرس (www.chap.sch.irﻣﺮاﺟﻌﻪ ﻛﻨﻴﺪ و ﻛﺘﺎب رﻳﺎﺿﻲ ﺳﺎل دوم رﺷﺘﻪي رﻳﺎﺿﻲ ﻓﻴﺰﻳﻚ را downloadو ﻣﻄﺎﻟﻌﻪ ﺑﻜﻨﻴﺪ .ﺑﻪ ﻃﻮر ﺳﺎده ﺗﺎﺑﻊ ﻳﻌﻨﻲ ﻓﺮﻣﻮل(. ﺗﺎﺑﻊ
را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ .ﺑﺮاي ﻣﺤﺎﺳﺒﻪي ﺑﮕﺬارﻳﻢ ﺟﺎي ﺑﻪ ﺑﺎﻻ ﻓﺮﻣﻮل در اﺳﺖ ﻛﺎﻓﻲ :
ﭘﺲ ﺑﺮاﺑﺮ ﺑﺎ اﺳﺖ .ﺑﻪ ) argument ،2ﻳﺎ آرﮔﻮﻣﺎن( ﻣﻲﮔﻮﻳﻨﺪ .در C++ﻫﻢ ﻣﻲﺷﻮد ﺗﺎﺑﻊﻫﺎﻳﻲ ﻣﺜﻞ اﻳﻦ ﻧﻮﺷﺖ .ﻫﻤﻴﻦ ﺗﺎﺑﻊ در C++ﺑﻪ ﺷﻜﻞ زﻳﺮ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد: )double f(double x { ;return x*x - x + 1 }
اوﻟﻴﻦ doubleﻧﻮع ﺧﺮوﺟﻲ ﺗﺎﺑﻊ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ .ﻳﻌﻨﻲ ) f(-1) ،f(2) ،f(1.1و ...ﻫﻤﻪ ﻣﻘﺪارﻫﺎﻳﻲ ﺑﺎ ﻧﻮع doubleﻳﻌﻨﻲ ﻋﺪد اﻋﺸﺎري ﻫﺴﺘﻨﺪ .دوﻣﻴﻦ doubleﻣﻲﮔﻮﻳﺪ ﻛﻪ ورودي ﺗﺎﺑﻊ ﻳﺎ ﻫﻤﺎن argumentﻛﻪ در ﺑﺎﻻ ﻣﻌﺮﻓﻲ ﻛﺮدم ﺑﺎﻳﺪ ﺑﺎ ﻧﻮع doubleﺑﺎﺷﺪ .ﻣﺜﻼ ﻣﻲﺗﻮان ﻧﻮﺷﺖ ) f(2.32اﻣﺎ ﻧﻤﻲﺗﻮان ﻧﻮﺷﺖ )" f("helloزﻳﺮا " "helloﻳﻚ رﺷﺘﻪ اﺳﺖ ﻧﻪ ﻳﻚ ﻋﺪد .ﻛﻠﻤﻪي ﻛﻠﻴﺪي returnﻣﻘﺪاري را ﻛﻪ )) f(xﺑﻪ ازاي xداده ﺷﺪه( ﺑﺎﻳﺪ داﺷﺘﻪ ﺑﺎﺷﺪ ﻣﻌﻴﻦ ﻣﻲﻛﻨﺪ.
23
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻋﺪهاي از ﻛﻠﻤﺎت در C++ﻛﻠﻴﺪي ﻫﺴﺘﻨﺪ .در اﻏﻠﺐ compilerﻫﺎي ﻣﻌﺘﺒﺮ وﻗﺘﻲ اﻳﻦ ﻛﻠﻤﺎت ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮﻧﺪ رﻧﮓ آنﻫﺎ ﻋﻮض ﻣﻲﺷﻮد .ﻣﻦ compilerﺧﻮدم را ﻃﻮري ﺗﻨﻈﻴﻢ ﻛﺮدهام ﻛﻪ اﻳﻦ ﻛﻠﻤﺎت ﺳﻔﻴﺪ رﻧﮓ ﺷﻮﻧﺪ .ﺑﻪ ﻋﻜﺲ زﻳﺮ ﻛﻪ ﻣﻦ از compilerم ﮔﺮﻓﺘﻪام ﻧﮕﺎه ﻛﻨﻴﺪ:
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻛﻠﻤﻪﻫﺎي ﻛﻠﻴﺪي returnو doubleﺑﻪ رﻧﮓ ﺳﻔﻴﺪ ﻫﺴﺘﻨﺪ .ﻋـﻠﺖ ِ اﻳﻦ ﻛﻪ رﻧﮓ زﻣﻴﻨﻪ را آﺑﻲ ﺗﻴﺮه ﮔﺮﻓﺘﻪام اﻳﻦ اﺳﺖ ﻛﻪ ﭼﺸﻢ ﻛﻢ ﺗﺮ اذﻳﺖ ﺑﺸﻮد ) .ﺑﺒﻴﻨﻴﺪ اوﻻ ﻣﻦ ﭼﺸﻢ ﭘﺰﺷﻚ ﻧﻴﺴﺘﻢ ﻛﻪ ﺑﺪوﻧﻢ ﺑﺎ ﭼﻪ رﻧﮓ زﻣﻴﻨﻪاي ﭼﺸﻢ ﻛﻢ ﺗﺮ آﺳﻴﺐ ﻣﻲﺑﻴﻨﻪ و ﺛﺎﻧﻴﺎ اﮔﺮ ﺷﻤﺎم ﻣﻲﺧﻮاﻳﺪ از compilerﺧﻮد ﻋﻜﺲ ﺑﮕﻴﺮﻳﺪ ﻛﻠﻴﺪﻫﺎي Alt + Print_Screenرا ﻓﺸﺎر ﺑﺪﻳﺪ و ﻋﻜﺲ رو در ﻧﺮم اﻓﺰار Paintوﻳﻨﺪوز pasteﻛﻨﻴﺪ .ﻛﻠﻴﺪ Print_Screenﻫﻢ ﻳﻜﻲ از ﺳﻪ ﺗﺎ ﻛﻠﻴﺪ ﭘﻴﺶ ﻫﻢ در ﮔﻮﺷﻪي ﺑﺎﻻ و راﺳﺖ keyboardده .در ﺿﻤﻦ »ﻛﻠﻤﻪ- ي ﻛﻠﻴﺪي« ﺗﺮﺟﻤﻪي keywordده
(.
ﺑﺮﻧﺎﻣﻪي زﻳﺮ از ﺗﺎﺑﻊ ﻣﻌﺮﻓﻲ ﺷﺪه در ﺑﺎﻻ اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )double f(double x { ;return x*x - x + 1 } )(int main { ;)cout<< f(2 ;)(getch }
Output: 3
24
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﻪ ﺑﺎ ﻣﻘﺪاري ﻛﻪ از راه رﻳﺎﺿﻲ ﺑﻪ دﺳﺖ آورده ﺑﻮدﻳﻢ ﻳﻜﻲ اﺳﺖ. ﺗﺎﺑﻊﻫﺎي C++ﺑﺮ ﻋﻜﺲ ﺗﻮاﺑﻊ رﻳﺎﺿﻲ ﺧﻴﻠﻲ ﺑﻲ دﺳﺖ و ﭘﺎ ﻧﻴﺴﺘﻨﺪ .ﻣﺜﻼ در ﻣﺜﺎل ﺑﺎﻻ ﻣﻲﺷﻮد ﺧﻮد ﺗﺎﺑﻊ fرا وادار ﺑﻪ ﭼﺎپ ﻣﻘﺪارش ﻛﺮد: >#include <conio.h >#include <iostream ;using namespace std )double f(double x { ;int a = x*x - x + 1 ;cout<< a ;return a } )(int main { ;)f(2 ;)(getch }
Output: 3
ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻣﻲﺑﻴﻨﻴﺪ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻧﻴﺎزي ﺑﻪ ﺧﺮوﺟﻲ ﺗﺎﺑﻊ ﻧﺪارﻳﻢ .ﭘﺲ ﺑﻬﺘﺮ اﺳﺖ ﺗﺎﺑﻊ را ﻃﻮري ﺗﻌﺮﻳﻒ ﻛﻨﻢ ﻛﻪ ﺧﺮوﺟﻲ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ: >#include <conio.h >#include <iostream ;using namespace std )void f(double x { ;int a = x*x - x + 1 ;cout<< a ;return } )(int main { ;)f(2 ;)(getch }
Output: 3
voidﻳﻌﻨﻲ اﻳﻦ ﻛﻪ ﺗﺎﺑﻊ ﺧﺮوﺟﻲ ﻧﺪارد.
25
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻫﻤﺎن ﻃﻮر ﻛﻪ در رﻳﺎﺿﻴﺎت ﺗﺎﺑﻊﻫﺎي ﭼﻨﺪ ﻣﺘﻐﻴﺮه دارﻳﻢ ،در C++ﻫﻢ ﺗﺎﺑﻊﻫﺎ ﻣﻲﺗﻮاﻧﻨﺪ ﭼﻨﺪ ﭘﺎراﻣﺘﺮ داﺷﺘﻪ ﺑﺎﺷﻨﺪ. ﻣﺜﻼ )int f(int a, char* c { ;cout<< c ;return a*a }
دو ﭘﺎراﻣﺘﺮ دارد .ﭘﺎراﻣﺘﺮ اول ﻳﻚ intاﺳﺖ و ﭘﺎراﻣﺘﺮ دوم ﻳﻚ رﺷﺘﻪ اﺳﺖ .اﻳﻦ ﺗﺎﺑﻊ ﺑﻌﺪ از ﻓﺮاﺧﻮاﻧﻲ ،رﺷﺘﻪ را ﭼﺎپ ﻣﻲﻛﻨﺪ و ﻣﺮﺑﻊ aرا ﺑﻪ ﻋﻨﻮان ﺧﺮوﺟﻲ ﺑﺮ ﻣﻲﮔﺮداﻧﺪ )ﻣﺮﺑﻊ ﻫﻢ ﻛﻪ ﻳﻌﻨﻲ ﺗﻮان دوم ﻳﺎ ﻣﺠﺬور ﻳﺎ ﻫﻤﺎن squareﺧﻮدﻣﺎن
(.
ﺑﻪ ﻣﺜﺎل زﻳﺮ ﻛﻪ ﻓﻬﻢ آن ﻛﻤﻲ ﺳﺨﺖ اﺳﺖ ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )int f(int a, char* c { ;cout<< c << a ;return a*a } )(int main { ;)" cout<< endl << f(2,"an integer ;)(getch }
Output: an integer 2 4 ﻧﻜﺎت زﻳﺎدي در اﻳﻦ ﻣﺜﺎل وﺟﻮد دارد .اوﻟﻴﻦ و ﻣﻬﻢ ﺗﺮﻳﻦ آنﻫﺎ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺮاي ﭼﺎپ دو ﻣﻘﺪار ﺑﺎ coutﻻزم
ﻧﻴﺴﺖ دو ﺑﺎر از coutاﺳﺘﻔﺎده ﺷﻮد و ﺑﻪ ﺟﺎي ;cout<< c ;cout<< a
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;cout<< c << a
ﻧﻜﺘﻪي ﺑﻌﺪي آن اﺳﺖ ﻛﻪ ﺑﺮاي اﻳﻦ ﻛﻪ ﭼﺎپ ﺑﻘﻴﻪي ﻣﻘﺪارﻫﺎ ﺑﻪ ﺧﻂ ﺑﻌﺪي ﻣﻨﺘﻘﻞ ﺷﻮد ﻣﻲﻧﻮﻳﺴﻢ: ;cout<< endl
در ﻋﻮض ﻣﻲﺗﻮاﻧﻴﺪ ﺑﻨﻮﻳﺴﻴﺪ: ;"cout<< "\n
26
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻳﺎ ;'cout<< '\n
در ﺿﻤﻦ ﻫﻤﺎن ﻃﻮر ﻛﻪ ﺑﻪ ﺟﺎي ;"cout<< "hello"<< "bye
ﻣﻲﺷﻮد ﻧﻮﺷﺖ ;"cout<< "hellobye
ﺑﻪ ﺟﺎي ;"cout<< "hello" << "\n
ﻫﻢ ﻣﻲﺷﻮد ﻧﻮﺷﺖ ;"cout<< "hello\n
ﻧﻜﺘﻪي ﺑﻌﺪي ﺗﺮﺗﻴﺐ اﺟﺮاﺳﺖ .اول اﺟﺮاﻛﻨﻨﺪهي ﺧﻄﻮط ) ﻛﻪ ﺑﻪ آن ِ controlﺑﺮﻧﺎﻣﻪ ﻣﻲﮔﻮﻳﻨﺪ( ﺑﻪ ﺳﺮاغ coutي ﻣﻲرود ﻛﻪ ﺑﻌﺪ از mainﻗﺮار دارد .ﺑﺮاي اﻳﻦ ﻛﻪ coutاﺟﺮا ﺑﺸﻮد ﺑﺎﻳﺪ ﻣﻘﺪار ﭘﺎراﻣﺘﺮﻫﺎي آن ﻣﺸﺨﺺ ﺑﺎﺷﻨﺪ .ﭘﺲ ﺑﺎﻳﺪ ﻣﻘﺪار )" f(2,"an integerﻣﺸﺨﺺ ﺑﺎﺷﺪ .ﺑﺮاي ﻣﺸﺨﺺ ﺷﺪن اﻳﻦ ﻣﻘﺪارِ control ، ﺑﺮﻧﺎﻣﻪ وارد ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ ﻣﻲﺷﻮد .در اﻳﻦ ﺣﺎﻟﺖ ﻣﻘﺪار ﭘﺎراﻣﺘﺮ aﺑﺮاﺑﺮ 2اﺳﺖ و cﻫﻢ ﺑﻪ رﺷﺘﻪي
"an
" integerاﺷﺎره دارد )ﺑﻌﺪﻫﺎ ﻣﺘﻮﺟﻪ ﻣﻲﺷﻮﻳﺪ ﻛﻪ ﭼﺮا ﻣﻲﮔﻮﻳﻢ »اﺷﺎره دارد«( .دﺳﺘﻮر ِ coutدرون ﺗﺎﺑﻊ اﺟﺮا ﻣﻲﺷﻮد و ﻋﺒﺎرت an integer 2 ﭼﺎپ ﻣﻲﺷﻮد .ﺳﭙﺲ 4ﺑﺮﮔﺮداﻧﺪه ﻣﻲﺷﻮد .و اﻳﻦ 4ﻫﻤﺎن ﻣﻘﺪار )" f(2,"an integerاﺳﺖ ﻛﻪ ﺑﻪ دﻧﺒﺎل آن ﺑﻮدﻳﻢ .ﺣﺎل ،coutاﺑﺘﺪا endlرا ﭼﺎپ ﻣﻲﻛﻨﺪ ﻛﻪ ﻧﺘﻴﺠﻪ- ي آن رﻓﺘﻦ ﺑﻪ ﺧﻂ ﺑﻌﺪي اﺳﺖ و ﺳﭙﺲ 4را ﭼﺎپ ﻣﻲﻛﻨﺪ. ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻓﻬﻤﻴﺪم ﻛﻪ ﻣﻘﺪارﻫﺎﻳﻲ ﻛﻪ ﺑﺎ coutﭼﺎپ ﻣﻲﺷﻮﻧﺪ ﺑﺎﻳﺪ از راﺳﺖ ﺑﻪ ﭼﭗ ﻣﺸﺨﺺ ﺷﻮﻧﺪ وﻟﻲ ﺗﺮﺗﻴﺐ ﭼﺎپ آنﻫﺎ از ﭼﭗ ﺑﻪ راﺳﺖ اﺳﺖ .ﻣﺜﻼ در ;)cout<< f(1) << g(2
اول ﺑﺎﻳﺪ ﻣﻘﺪار ) g(2ﻣﺸﺨﺺ ﺷﻮد )ﻛﻪ ﺑﺮاي ﻣﺸﺨﺺ ﺷﺪن آن ﺗﺎﺑﻊ gاﺟﺮا ﻣﻲﺷﻮد( و ﺳﭙﺲ ﺑﺎﻳﺪ )f(1
ﻣﺸﺨﺺ ﺷﻮد .وﻟﻲ اول ) f(1ﭼﺎپ ﻣﻲﺷﻮد و ﺳﭙﺲ ).g(2 ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﺜﺎل ﺑﺎﻻ ﺑﺮاﻳﺘﺎن ﺑﻬﺘﺮ ﺟﺎ ﺑﻴﻔﺘﺪ ﻣﺜﺎل زﻳﺮ را ﻣﻲآورم ﻛﻪ در ﺿﻤﻦ ﺗﺎﺑﻊﻫﺎﻳﻲ ﺑﺪون ﭘﺎراﻣﺘﺮ دارد: >#include <conio.h >#include <iostream ;using namespace std )(int f { ;cout<< "function f" << endl ;return 1 } )(int g
27
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { ;cout<< "function g" << endl ;return 2 } )(int main { ;)(cout<< "hello\n" << f()<< endl << g ;)(getch }
Output: function g function f hello 1 2
)ﻳﻌﻨﻲ ﺗﺤﻠﻴﻞ آن ﺑﻪ ﻋﻬﺪهي ﺧﻮدﺗﺎن .ﻓﻘﻂ ﺑﻪ ﻋﻨﻮان راﻫﻨﻤﺎﻳﻲ ﺑﮕﻮﻳﻢ ﻛﻪ از ﻣﻨﻮي Runﻳﺎ ﻣﻨﻮي Debug ﮔﺰﻳﻨﻪي Trace Intoﻳﺎ Step Intoرا اﻧﺘﺨﺎب ﻛﻨﻴﺪ ﺗﺎ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺧﻂ ﺑﻪ ﺧﻂ اﺟﺮا ﺑﺸﻮد و ﺑﺮاي رﻓﺘﻦ ﺑﻪ ﺧﻂ ﺑﻌﺪي ﻣﻨﺘﻈﺮ اﺟﺎزهي ﺷﻤﺎ ﺑﻤﺎﻧﺪ .ﺑﻪ اﻳﻦ ﻧﻮع از اﺟﺮاي ﺑﺮﻧﺎﻣﻪ و رﻓﻊ اﺷﻜﺎﻻت آن debugﻛﺮدن ﻣﻲﮔﻮﻳﻴﻢ. ﻓﻘﻂ ﻳﺎدﺗﺎن ﺑﺎﺷﺪ ﻛﻪ وﻗﺘﻲ ِ controlﺑﺮﻧﺎﻣﻪ از ﻓﺎﻳﻞ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺧﺎرج ﺷﺪ آن ﻗﺪر ﮔﺰﻳﻨﻪي Step Overرا اﻧﺘﺨﺎب ﻛﻨﻴﺪ ﺗﺎ ﺑﻪ ﻓﺎﻳﻞ ﺷﻤﺎ ﺑﺮﮔﺮدد .وﻟﻲ ﺑﺮاي اﻳﻦ ﺟﻮر ﻛﺎرﻫﺎ ﺧﻴﻠﻲ از ﻣﻨﻮي ﺑﺎﻻي compilerﺧﻮدﺗﺎن اﺳﺘﻔﺎده ﻧﻜﻨﻴﺪ )راه ﺳﺎده ﺗﺮي ﻫﻢ ﻫﺴﺖ !((.
ار' ,ط
* را " و )ر! ن ' &
ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )int f(int a { ;a = 2 ;return 0 } )(int main { ;int a = 1 ;)f(a ;cout<< a ;)(getch
28
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output: 1
در ﺗﺤﻠﻴﻞ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﭼﻨﺪ ﻧﻜﺘﻪ ﻫﺴﺖ .اوﻻ aدر ﺗﺎﺑﻊ mainﻫﻴﭻ رﺑﻄﻲ ﺑﻪ aدر ﺗﺎﺑﻊ fﻧﺪارد )ﺗﺎ ﺣﺎﻻ ﺑﻪ اﻳﻦ ﻓﻜﺮ ﻛﺮده ﺑﻮدﻳﺪ ﻛﻪ ﺧﻮد mainﻫﻢ ﻳﻚ ﺗﺎﺑﻊ اﺳﺖ؟
( .وﻗﺘﻲ ِ controlﺑﺮﻧﺎﻣﻪ وارد ﺗﺎﺑﻊ fﺷﺪ ﻣﻜﺎن ﺟﺪﻳﺪي از
ﺣﺎﻓﻈﻪ را در اﺧﺘﻴﺎر ﻣﻲﮔﻴﺮد ،اﺳﻢ آن را aﻣﻲﮔﺬارد و ﻣﻘﺪار aيِ ﺗﺎﺑﻊ mainرا در aي ﺟﺪﻳﺪ copyﻣﻲﻛﻨﺪ. ﻫﻨﮕﺎم ﺧﺮوج از ﺗﺎﺑﻊ a ،fي ﺟﺪﻳﺪ از ﺑﻴﻦ ﻣﻲرود .ﻳﻌﻨﻲ ﺣﺎﻓﻈﻪاي ﻛﻪ ﺑﺎ ﻧﺎم aدر اﺧﺘﻴﺎر ﺑﺮﻧﺎﻣﻪ ﺑﻮد از اﺧﺘﻴﺎر ﺑﺮﻧﺎﻣﻪ ﺧﺎرج ﻣﻲﺷﻮد و ﺑﻪ اﺻﻄﻼح آزاد ﻣﻲﺷﻮد .ﺑﻪ آزاد ﻛﺮدن ﺣﺎﻓﻈﻪي ﮔﺮﻓﺘﻪ ﺷﺪه deleteﻛﺮدن free ،ﻛﺮدن ﻳﺎ destroyﻛﺮدن ﻫﻢ ﻣﻲﮔﻮﻳﻴﻢ .ﺑﺎ اﻳﻦ ﻛﻪ aي ﺟﺪﻳﺪ از ﺑﻴﻦ ﻣﻲرود aي ﻗﺒﻠﻲ ﻫﻤﭽﻨﺎن ﻣﻮﺟﻮد اﺳﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﻄﻤﺌﻦ ﺑﺸﻮﻳﺪ aﻫﺎي دو ﺗﺎ ﺗﺎﺑﻊ ﺑﻪ ﻫﻢ رﺑﻄﻲ ﻧﺪارﻧﺪ در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﻣﻘﺪار aدر ﺗﺎﺑﻊ mainرا ﺑﻌﺪ از اﺟﺮاي ﺗﺎﺑﻊ f
ﭼﺎپ ﻛﺮدهام ﺗﺎ ﺑﺒﻴﻨﻴﺪ ﻣﻘﺪارش ﺗﻐﻴﻴﺮ ﻧﻜﺮده .اﻣﺎ اﮔﺮ واﻗﻌﺎ ﻣﻲﺧﻮاﺳﺘﻴﻢ ﺗﻐﻴﻴﺮ ﻛﻨﺪ ﺑﺎﻳﺪ ﭼﻪ ﻣﻲﻛﺮدﻳﻢ؟ ﺧﻴﻠﻲ ﺳﺎده اﺳﺖ .ﻛﺎﻓﻲ ﺑﻮد ﻓﻘﻂ ﻳﻚ & ﻧﺎﻗﺎﺑﻞ ﺟﻠﻮي ﭘﺎراﻣﺘﺮ aﺑﮕﺬارم .ﻳﻌﻨﻲ ﺑﺮﻧﺎﻣﻪ را ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ دﻫﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )int f(int& a { ;a = 2 ;return 0 } )(int main { ;int a = 1 ;)f(a ;cout<< a ;)(getch }
Output: 2
ﻓﺮق اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﺎ ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ اﻳﻦ اﺳﺖ ﻛﻪ وﻗﺘﻲ ِ controlﺑﺮﻧﺎﻣﻪ ،وارد fﻣﻲﺷﻮد ﺣﺎﻓﻈﻪي ﺟﺪﻳﺪي ﭘﻴﺪا ﻧﻤﻲ- ﻛﻨﺪ و از ﻫﻤﺎن ﺣﺎﻓﻈﻪي ﻗﺒﻠﻲ اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪ .ﭼﻮن اﺳﻢ ﻣﺘﻐﻴﺮﻫﺎ اﻫﻤﻴﺖ ﺧﺎﺻﻲ ﻧﺪارد ﻫﻤﻴﻦ ﺑﺮﻧﺎﻣﻪ را ﻣﻲﺷﻮد ﺑﻪ اﻳﻦ ﺻﻮرت ﻧﻮﺷﺖ: >#include <conio.h >#include <iostream ;using namespace std
29
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )int f(int& b { ;b = 2 ;return 0 } )(int main { ;int a = 1 ;)f(a ;cout<< a ;)(getch }
Output : 2
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﺗﺎﺑﻊ fﻳﻚ ﺧﺮوﺟﻲ دارد .اﻣﺎ ﻣﻦ ﻫﻴﭻ ﻧﻴﺎزي ﺑﻪ ﺧﺮوﺟﻲ آن ﻧﺪارم .در compilerﻫﺎي Borland ﻣﻲﺗﻮان ; return 0را از ﺑﺮﻧﺎﻣﻪ ﺣﺬف ﻛﺮد و اﻳﻦ ﻳﻚ errorﻧﻴﺴﺖ اﻣﺎ ﺑﺎﻋـﺚ ﻣﻲﺷﻮد compilerﻳﻚ warningﺻﺎدر ﻛﻨﺪ ﻛﻪ اﻫﻤﻴﺖ ﭼﻨﺪاﻧﻲ ﻧﺪارد warning ) .ﻳﻌﻨﻲ ﻫﺸﺪار .ﻣﻌﻨﻲ »ﺻﺎدر ﻛﺮدن« را ﻫﻢ ﻛﻪ ﻣﻲ- دوﻧﻴﺪ ﭼﻴﻪ
( .ﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ وﻗﺘﻲ در ﻳﻚ compilerاز Borlandاز returnاﺳﺘﻔﺎده ﻧﺸﻮد ،ﻣﻘﺪار )f(2
ﭼﻪ ﻗﺪر اﺳﺖ؟ )ﺟﻮاب :ﻫﻴﭻ ﻣﻌﻠﻮم ﻧﻴﺴﺖ .آزﻣﺎﻳﺶ ﻛﻨﻴﺪ!( ﺷﺎﻳﺪ ﺗﺎ ﺑﻪ ﺣﺎل ﻓﻬﻤﻴﺪه ﺑﺎﺷﻴﺪ ﻛﻪ ﻣﺜﻼ در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﺑﻪ aدر ) f(a); argumentﻳﺎ آرﮔﻮﻣﺎن( ﻣﻲﮔﻮﻳﻨﺪ و ﺑﻪ bدر ) int f(int bﭘﺎراﻣﺘﺮ .وﻟﻲ ﺧﻴﻠﻲ ﻻزم ﻧﻴﺴﺖ وﺳﻮاس ﺑﻪ ﺧﺮج دﻫﻢ ﻛﻪ ﻣﺒﺎدا اﻳﻦ دو واژه )ﻳﻌﻨﻲ ﭘﺎراﻣﺘﺮ و آرﮔﻮﻣﺎن( ﺟﺎ ﺑﻪ ﺟﺎ اﺳﺘﻔﺎده ﺷﻮﻧﺪ. ﺣﺎﻻ ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ و ﺣﺘﻤﺎ ﻗﺒﻞ از ﺧﻮاﻧﺪن ﺗﻮﺿﻴﺤﺎت ،آن را ﺑﻪ دﻗﺖ ﺑﺮرﺳﻲ ﻛﻨﻴﺪ .ﺧﺮوﺟﻲ ﻫﻢ ﻧﻨﻮﺷﺘﻪاﻳﻢ ﺗﺎ ﺧﻮدﺗﺎن ﺣﺪس ﺑﺰﻧﻴﺪ .اﻳﻦ آزﻣﺎﻳﺶ ﺧﻮﺑﻲ ﺑﺮاي درك ﻋﻤﻴﻖ ﺷﻤﺎ از ﻳﻚ ﺑﺮﻧﺎﻣﻪ اﺳﺖ .وﻟﻲ ﺷﺎﻳﺪ ﻫﻨﻮز ﺗﺠﺮﺑﻪي ﻛﺎﻓﻲ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻴﺪ و ﻛﻤﻲ زود ﺑﺎﺷﺪ ﻛﻪ اﻧﺘﻈﺎر داﺷﺘﻪ ﺑﺎﺷﻢ ﺑﻪ آن ﭼﻴﺰي ﻛﻪ ﻣﻨﻈﻮر ﻣﻦ اﺳﺖ ﺑﺮﺳﻴﺪ .ﺑﻪ ﻫﺮ ﺣﺎل دوﺳﺖ ﻧﺪارم ﺷﻤﺎ را دﺳﺖ ﻛﻢ ﺑﮕﻴﺮم
: >#include <conio.h >#include <iostream ;using namespace std )void f(int& b { ;b = 2 ;cout<< b } )(int main {
30
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)f(1 ;)(getch }
ﻧﺤﻮهي اﺟﺮاي اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ compilerﺷﻤﺎ ﺑﺴﺘﮕﻲ دارد .ﻣﺜﻼ Visual C++ 2005از آن ﻳﻚ errorﻣﻲ- ﮔﻴﺮد و ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻧﻤﻲﻛﻨﺪ .اﻣﺎ BDS 2006ﻓﻘﻂ از آن ﻳﻚ warningﻣﻲﮔﻴﺮد و ﺑﻌﺪ آن را اﺟﺮا ﻣﻲﻛﻨﺪ )اﻳﻦ ﻫﻢ اﺧﺘﺮاع »ﻓﻌﻞ« در زﺑﺎن ﻓﺎرﺳﻲ warning» :ﮔﺮﻓﺘﻦ« .ﺗﻮﻟﺪش ﻣﺒﺎرك ﻓﻮت ﻛﻨﻴﺪ:
( .ﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ
اﺷﺘﺒﺎه در ﻛﺠﺎﺳﺖ ﻛﻪ errorﻳﺎ warningﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد؟ ﻗﺒﻼ ﮔﻔﺘﻢ ﻛﻪ وﻗﺘﻲ ﺑﻪ ﺟﺎي }…{)void f(int b
ﺑﻨﻮﻳﺴﻴﻢ: }…{)void f(int& b
ﺣﺎﻓﻈﻪي ﺟﺪﻳﺪي ﺑﺮاي bدر ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻧﻤﻲﺷﻮد .اﻣﺎ ﺑﺎﻳﺪ ﺣﺎﻓﻈﻪي ﻗﺪﻳﻤﻲ ﺑﺎﺷﺪ ﻛﻪ ﺟﺪﻳﺪش ﺑﻪ وﺟﻮد ﻧﻴﺎﻳﺪ .اﻣﺎ ﻣﻦ در ﺑﺮﻧﺎﻣﻪ ﻧﻮﺷﺘﻪام ;) ،f(1در ﺣﺎﻟﻲ ﻛﻪ 1ﻓﻘﻂ ﻳﻚ ﻣﻘﺪاراﺳﺖ ﻧﻪ ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪ .ﺗﺎﺑﻊ fﺑﻪ ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪ ﻧﻴﺎز دارد وﻟﻲ ﻣﻦ ﺑﺎ ﻧﻮﺷﺘﻦ & intﺑﻪ ﺟﺎي intﺑﻪ آن اﺟﺎزه ﻧﺪادهام ﻛﻪ ﺧﻮدش ﺣﺎﻓﻈﻪاي ﺗﻬﻴﻪ ﻛﻨﺪ و از ﻃﺮف دﻳﮕﺮ ﺣﺎﻓﻈﻪاي ﻫﻢ از ﻃﺮﻳﻖ آرﮔﻮﻣﺎن ﺑﻪ آن ﻧﺪادهام .اﻳﻦ ﺟﺎﺳﺖ ﻛﻪ compilerﺗﺼﻤﻴﻢ ﻣﻲﮔﻴﺮد ﻛﻪ ﭼﻪ ﻛﻨﺪ .آﻳﺎ از compileﻛﺮدن ﻣﻨﺼﺮف ﺷﻮد ﻳﺎ از ﻃﺮﻳﻘﻲ ﺣﺎﻓﻈﻪاي ﺑﻪ وﺟﻮد ﺑﻴﺎورد و ﻫﺮ ﺟﻮر ﺷﺪه ﻛﺎر را ﺑﻪ اﺗﻤﺎم ﺑﺮﺳﺎﻧﺪ ؟ compilerﻫﺎي ﻣﺨﺘﻠﻒ ﻳﻚ ﻛﺎر را از را هﻫﺎي ﻣﺨﺘﻠﻒ اﻧﺠﺎم ﻣﻲدﻫﻨﺪ .ﻣﺜﻼ ﻳﻚ compilerﻣﻤﻜﻦ اﺳﺖ ﺑﺮاي ﻫﺮ ﻣﻘﺪاري ﻛﻪ در ﺑﺮﻧﺎﻣﻪ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد ﻣﺜﻞ ﻳﻚ ﻣﺘﻐﻴﺮ ﺣﺎﻓﻈﻪاي در ﻧﻈﺮ ﺑﮕﻴﺮد ﻣﺜﻼ ﻣﻤﻜﻦ اﺳﺖ در ;int a = 2
اول ﺣﺎﻓﻈﻪاي ﺑﺮاي aاﺧﺘﺼﺎص دﻫﺪ ﺳﭙﺲ ﺣﺎﻓﻈﻪاي ﺑﺮاي ﻳﻚ ﻣﺘﻐﻴﺮ ﻣﺨﻔﻲ اﺧﺘﺼﺎص دﻫﺪ و ﻣﻘﺪار آن را 2 ﺑﮕﺬارد و در ﻧﻬﺎﻳﺖ ﻣﻘﺪار اﻳﻦ ﻣﺘﻐﻴﺮ ﻣﺨﻔﻲ را در aﻛﭙﻲ ﻛﻨﺪ compiler .دﻳﮕﺮي ﻣﻤﻜﻦ اﺳﺖ ﻣﺴﺘﻘﻴﻤﺎ ﻣﻘﺪار a
را 2ﺑﮕﺬارد .ﺑﻪ ﻫﺮ ﺷﻜﻞ ﻛﻪ اﻳﻦ ﻛﺎر اﻧﺠﺎم ﺑﺸﻮد ﻧﺘﻴﺠﻪ ﻳﻜﻲ اﺳﺖ ﻳﻌﻨﻲ a ،ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪ ﻣﻲﺷﻮد ﻛﻪ ﻣﻘﺪارش 2اﺳﺖ .ﻣﻦ و ﺷﻤﺎ ﺑﻪ ﻋﻨﻮان ) programmerﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ( ﻛﺎري ﺑﻪ اﻳﻦ ﻛﺎرﻫﺎ ﻧﺪارﻳﻢ .ﻣﺎ ﻓﻘﻂ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﻣﺎن را در compilerﻣﺤﺒﻮﺑﻤﺎن ﻣﻲﻧﻮﻳﺴﻴﻢ) .ﺳﻌﻲ ﻛﻨﻴﺪ compilerﺧﻮدﺗﻮﻧﻮ را دوﺳﺖ داﺷﺘﻪ ﺑﺎﺷﻴﺪ و روي ﺷﺮﻛﺖ ﺳﺎزﻧﺪهاش ﺗﻌﺼﺐ داﺷﺘﻪ ﺑﺎﺷﻴﺪ .ﻣﻦ compilerﻫﺎي Borlandرو ﺗﻮﺻﻴﻪ ﻣﻲﻛﻨﻢ ﮔﺮﭼﻪ ﺑﺮاي Microsoftﻫﻢ اﺣﺘﺮام ﻗﺎﺋﻠﻢ.
(
31
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ commentه
ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻧﻴﺎز دارد ﻛﻪ در ﻛﻨﺎر ﺧﻴﻠﻲ از دﺳﺘﻮرات ﺑﺮﻧﺎﻣﻪ ،ﺗﻮﺿﻴﺤﺎﺗﻲ ﺑﻨﻮﻳﺴﺪ ﻛﻪ در ﻣﺮاﺟﻌﺎت ﺑﻌﺪي ﺑﻪ راﺣﺘﻲ ﻣﻌﻨﻲ آن ﭼﻴﺰي را ﻧﻮﺷﺘﻪ ﺷﺪه ،ﻣﺘﻮﺟﻪ ﺑﺸﻮد .وﻗﺘﻲ ﺷﻤﺎ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻣﻲﻧﻮﻳﺴﻴﺪ ﺣﺘﻲ اﮔﺮ ﺧﻴﻠﻲ ﻫﻢ ﻃﻮﻻﻧﻲ ﻧﺒﺎﺷﺪ ﺗﺎ ﻣﺪت ﻛﻮﺗﺎﻫﻲ از آن ﺳﺮ در ﻣﻲآورﻳﺪ .وﻗﺘﻲ ﻳﻚ ﻣﺎه ﺑﻌﺪ از ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﺗﺎن ،دوﺑﺎره ﺳﺮاﻏ ﺶ ﻣﻲ- آﻳﻴﺪ ﺗﻨﻬﺎ ﭼﻴﺰي ﻛﻪ ﻣﻲﺑﻴﻨﻴﺪ ﻳﻚ ﻣﺸﺖ دﺳﺘﻮر در ﻫﻢ ﭘﻴﭽﻴﺪه و ﻏﻴﺮ ﻗﺎﺑﻞ ﻓﻬﻢ اﺳﺖ ﻛﻪ ﺑﺮاي ﻓﻬﻤﻴﺪﻧﺶ ﻧﻴﺎز ﺑﻪ ﺗﻮﺿﻴﺢ ﻫﺴﺖ. ﺑﻪ ﺗﻮﺿﻴﺤﺎﺗﻲ ﻛﻪ در ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد و compilerآنﻫﺎ را ﻧﺎدﻳﺪه ﻣﻲﮔﻴﺮد commentﻣﻲﮔﻮﻳﻴﻢ .در C++ﺑﻪ دو ﺷﻜﻞ ﻣﻲﺗﻮان commentﻧﻮﺷﺖ .ﺷﻜﻞ ﻗﺪﻳﻤﻲ ﻛﻪ از Cﺑﻪ ارث ﺑﺮده ﺷﺪه؛ و ﺷﻜﻞ ﺟﺪﻳﺪ ﻛﻪ ﻣﺨﺼﻮص C++اﺳﺖ .در ﺷﻜﻞ ﻗﺪﻳﻤﻲ )ﻛﻪ ﺑﻪ آن C Style Commentﻣﻲﮔﻮﻳﻨﺪ( ﺗﻮﺿﻴﺤﺎت ﺑﻴﻦ »* «/و » «*/ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮﻧﺪ .ﻣﺜﻼ cout<< b; /* This is a comment */
compilerﺑﻪ ﻛﻞ /* This is a comment */
را ﻧﺎدﻳﺪه ﻣﻲﮔﻴﺮد اﻧﮕﺎر اﺻﻼ ﻧﻮﺷﺘﻪ ﻧﺸﺪه ﺑﺎﺷﺪ .ﺷﻜﻞ ﺟﺪﻳﺪ )ﻛﻪ ﺑﻪ آن C++ Style Commentﻣﻲﮔﻮﻳﻨﺪ( ﻳﻚ ﺧﻂ را از ﺟﺎﻳﻲ ﺑﻪ ﺑﻌﺪ ﺣﺬف ﻣﻲﻛﻨﺪ .ﻣﺜﻼ cout<< b; // This is a comment
رﻧﮓ commentﻫﺎ در Editorﻳﻌﻨﻲ آن ﺟﺎﻳﻲ ﻛﻪ ﺑﺮﻧﺎﻣﻪ را در آن ﻣﻲﻧﻮﻳﺴﻴﻢ ﻣﻌﻤﻮﻻ ﻣﺘﻔﺎوت اﺳﺖ .ﻣﻦ رﻧﮓ ﺧﺎﻛﺴﺘﺮي ﺑﺎ زﻣﻴﻨﻪي آﺑﻲ ﭘﺮ رﻧﮓ را ﺑﺮاي commentﻫﺎ اﻧﺘﺨﺎب ﻛﺮدهام. از commentﻫﺎ ،ﻫﻢ ﭼﻨﻴﻦ ،ﻣﻲﺷﻮد ﺑﺮاي ﺣﺬف ﻣﻮﻗﺖ ﺑﺨﺶﻫﺎﻳﻲ از ﺑﺮﻧﺎﻣﻪ اﺳﺘﻔﺎده ﻛﺮد )ﺑﻪ اﻳﻦ ﻛﺎر comment outﻛﺮدن ﻣﻲﮔﻮﻳﻴﻢ( .ﻫﻢ ﭼﻨﻴﻦ رﻳﺰه ﻛﺎريﻫﺎﻳﻲ در ﻣﻮرد commentﻫﺎي ﺗﻮ در ﺗﻮ ﻫﺴﺖ ﻛﻪ ﺗﺮﺟﻴﺢ ﻣﻲدﻫﻢ وﻗﺖ ﺷﻤﺎ را ﺑﺎ آنﻫﺎ ﺗﻠﻒ ﻧﻜﻨﻢ ﭼﻮن در ﻣﻮﻗﻊ ﻧﻴﺎز ﺑﺎ آزﻣﺎﻳﺶ و ﺧﻄﺎ ﺑﺎ آنﻫﺎ آﺷﻨﺎ ﻣﻲﺷﻮﻳﺪ .ﺣﺎﻻ ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺧﻮب دﻗﺖ ﻛﻨﻴﺪ: // a comment
>#include <conio.h >#include <iostream ;using namespace std
//------------------------------------------------*/ )void f(int& b { ;b = 2
32
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ // cout<< b; // This is a comment } */ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@// )(int main { ;)// //f(1 // /* ff */ //////////////////////////// /// ;"cout<< "hello and /*hallo*/ and //hello ;)(getch }
Output: hello and /*hallo*/ and //hello
در ﭘﺎﻳﺎن ﻣﻲﮔﻮﻳﻢ ﻛﻪ در compilerﻫﺎي ﺟﺪﻳﺪ Borlandو Microsoftﻣﻲﺷﻮد commentﻫﺎ را ﻓﺎرﺳﻲ ﻫﻢ ﻧﻮﺷﺖ .وﻟﻲ اﮔﺮ اﻳﻦ ﻛﺎر را ﺑﻜﻨﺒﺪ ﻣﺠﺒﻮر ﻫﺴﺘﻴﺪ ﻓﺎﻳﻞ را در ﻓﺮﻣﺖ ﻏﻴﺮ ) ANSIﻏﻴﺮ اﺳﺘﺎﻧﺪارد( ذﺧﻴﺮه ﻛﻨﻴﺪ. ﺑﻌﻀﻲ compilerﻫﺎ ﻓﻘﻂ ﻓﺎﻳﻞﻫﺎي ANSIرا ﻣﻲﭘﺬﻳﺮﻧﺪ .ﺿﻤﻦ اﻳﻦ ﻛﻪ اﻣﻜﺎن ﻓﺎرﺳﻲ ﻧﻮﻳﺴﻲ اﻳﻦ compilerﻫﺎ ﺧﻴﻠﻲ ﻫﻢ ﻛﺎﻣﻞ ﻧﻴﺴﺖ و اﻳﺮاد دارد .اﮔﺮ اﻧﮕﻴﺴﻲ ﺷﻤﺎ ﺧﻮب ﻧﻴﺴﺖ از ﻓﻴﻨﮕﻠﻴﺶ اﺳﺘﻔﺎده ﻛﻨﻴﺪ.
ر " ه
ا * +ول (' 3 ,ﻧﮕﺎه ﻛﻨﻴﺪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻧﻮﺷﺘﻪام ﻧﻮع ﻳﻚ رﺷﺘﻪ * charاﺳﺖ و * charﻓﻘﻂ 4ﺑﺎﻳﺖ از ﺣﺎﻓﻈﻪ را ﻣﻲﮔﻴﺮد .ﻣﺜﻼ ﻣﻲﺗﻮان ﻧﻮﺷﺖ: ;"char* c = "isn’t long Iranian history packed with defeats? y/n
اﻣﺎ آﻳﺎ ﺗﻤﺎم اﻳﻦ ﺟﻤﻠﻪي ﻃﻮﻻﻧﻲ در 4ﺑﺎﻳﺖ ﺟﺎ ﻣﻲﺷﻮد؟ ﺑﺪون ﺷﻚ اﻳﻦ ﻃﻮر ﻧﻴﺴﺖ .ﭘﺲ اﻳﻦ رﺷﺘﻪ در ﻛﺠﺎي ﺣﺎﻓﻈﻪ ﻗﺮار دارد؟ و اﺻﻼ ﻧﻘﺶ cﭼﻴﺴﺖ؟ ﻣﺜﺎلﻫﺎي ﻛﻠﻴﺸﻪاي و ﺗﻜﺮاري را دوﺳﺖ ﻧﺪارم .ﺑﻪ ﺧﺎﻃﺮ ﻫﻤﻴﻦ از آﺷﻨﺎﻳﻲ ﺷﻤﺎ ﺑﺎ ﻣﺤﻴﻂ )زﻳﺒﺎي
( وﻳﻨﺪوز ﻛﻤﻚ
ﻣﻲﮔﻴﺮم .ﻓﺮض ﻛﻨﻴﺪ ﺷﻤﺎ ﻳﻚ ﻓﻴﻠﻢ 500ﻣﮕﺎ ﺑﺎﻳﺘﻲ در دراﻳﻮ Eﻛﺎﻣﭙﻴﻮﺗﺮ دارﻳﺪ و ﻣﻲﺧﻮاﻫﻴﺪ اﻳﻦ ﻓﻴﻠﻢ از Desktopﻗﺎﺑﻞ دﺳﺘﺮﺳﻲ ﺑﺎﺷﺪ .ﺑﺮاي اﻳﻦ ﻛﺎر روي Desktopﻳﻚ shortcutﺑﻪ آن ﻓﻴﻠﻢ درﺳﺖ ﻣﻲﻛﻨﻴﺪ. ﻣﻲداﻧﻴﺪ ﻛﻪ shortcutﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﺣﺠﻢ ﺑﺴﻴﺎر ﻛﻢ اﺳﺖ وﻟﻲ از ﻃﺮﻳﻖ آن ﺑﻪ ﻳﻚ ﻓﻴﻠﻢ ﭘﺮﺣﺠﻢ دﺳﺘﺮﺳﻲ دارﻳﺪ. )ﻓﺎﻳﻞﻫﺎ ﻣﻌﻤﻮﻻ ﻳﻚ ﭘﺴﻮﻧﺪ ﺳﻪ ﺣﺮﻓﻲ دارن .ﻣﻲدوﻧﻴﺪ ﭘﺴﻮﻧﺪ shortcutﻫﺎ ﭼﻴﻪ؟ .lnkﻛﻪ ﻣﺨﻔﻴﻪ وﻟﻲ ﻣﻲﺷﻪ ﺑﺪون
33
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ دﺳﺘﻜﺎري ﻣﺴﺘﻘﻴﻢ registryو ﻓﻘﻂ ﺑﺎ اﻣﻜﺎﻧﺎت وﻳﻨﺪوز ) (XPﭘﺴﻮﻧﺪ رو ﻇﺎﻫﺮ ﻛﺮد .اﮔﺮ ﻓﻚ ﻣﻲﻛﻨﻴﺪ از ﻫﺮ ﭼﻴﺰ وﻳﻨﺪور ﺳﺮ در ﻣﻴﺎرﻳﺪ ﭘﻴﺪا ﻛﺮدن روش اﻳﻦ ﻛﺎر ﺑﺎﻳﺪ ﺑﺮاﺗﻮن ﺳﺮﮔﺮﻣﻲ ﺧﻮﺑﻲ ﺑﺎﺷﻪ .اﮔﺮ ﺗﻮﻧﺴﺴﻴﺪ در ﻛﻢ ﺗﺮ از 3 ﺳﺎﻋـﺖ اﻳﻦ ﻛﺎر رو اﻧﺠﺎم ﺑﺪﻳﺪ
.ﭘﻴﺸﻨﻬﺎد ﻣﻲﻛﻨﻢ ﻗﺒﻞ از ﻫﺮ ﻛﺎر ﻳﻚ restore pointدرﺳﺖ ﻛﻨﻴﺪ!
(.
cدر ﺑﺎﻻ ﻣﺎﻧﻨﺪ ﻳﻚ shortcutﻋﻤﻞ ﻣﻲﻛﻨﺪ .ﺧﻮد ِ رﺷﺘﻪ در ﺟﺎﻳﻲ دﻳﮕﺮ از ﺣﺎﻓﻈﻪ ﻗﺮار دارد و cاﺷﺎره ﮔﺮي ﺑﻪ آن ﻣﻜﺎن اﺳﺖ .ﺑﺎ ﻧﻮﺷﺘﻦ ;"?char* c = "isn’t short American history packed with victories
اول ﻣﻜﺎﻧﻲ از ﺣﺎﻇﻪ ﺑﻪ اﻧﺪازهي 52ﺑﺎﻳﺖ درﺳﺖ ﻣﻲﺷﻮد .ﺑﻌﺪ اﻳﻦ رﺷﺘﻪ در آن ﻗﺮار ﻣﻲﮔﻴﺮد و cﻃﻮري ﺗﻨﻈﻴﻢ ﻣﻲﺷﻮد ﻛﻪ ﺑﻪ اﻳﻦ رﺷﺘﻪ اﺷﺎره ﻛﻨﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﺑﺎ اﻳﻦ ﻛﻪ رﺷﺘﻪي ﺑﺎﻻ 51ﻛﺎراﻛﺘﺮ ﺑﻴﺶ ﺗﺮ ﻧﺪارد 52ﺑﺎﻳﺖ ﺑﺮاﻳ ﺶ در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد.ﻋﻠﺖ اﻳﻦ اﺳﺖ ﻛﻪ ﻫﻤﻴﺸﻪ در اﻧﺘﻬﺎي ﻫﺮ رﺷﺘﻪ ﺑﺎﻳﺪ ﻛﺎراﻛﺘﺮ')'\0ﻛﻪ ﺑﻪ آن nullﻳﺎ ﻛﺎراﻛﺘﺮ nullﻣﻲﮔﻮﻳﻴﻢ( ﻗﺮار ﺑﮕﻴﺮد .در اﻳﻦ ﺟﺎ ﺧﻮد compilerاﻳﻦ ﻛﺎراﻛﺘﺮ را اﺿﺎﻓﻪ ﻣﻲﻛﻨﺪ و ﺑﺮاي ﻫﻤﻴﻦ ﺑﻪ 52ﺑﺎﻳﺖ از ﺣﺎﻓﻈﻪ ﻧﻴﺎز دارد .اﮔﺮ ﺑﻨﻮﻳﺴﻴﺪ: ;char* c
cﻳﻚ اﺷﺎره ﮔﺮ اﺳﺖ ﻛﻪ ﺑﻪ ﺟﺎي از ﭘﻴﺶ ﺗﻌﻴﻴﻦ ﺷﺪهاي اﺷﺎره ﻧﺪارد .اﻣﺎ ﺑﺎ ﻧﻮﺷﺘﻦ ;"c = "hello
cﺑﻪ ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪ اﺷﺎره ﻣﻲﻛﻨﺪ ﻛﻪ ﺣﺎوي رﺷﺘﻪي " "helloﻫﻤﺮاه ﺑﺎ ﻛﺎراﻛﺘﺮ nullاﻧﺘﻬﺎﻳﻲ )ﻳﺎ ﻫﻤﺎن (terminating nullاﺳﺖ. ﻣﻤﻜﻦ اﺳﺖ ﺑﻪ اﻳﻦ ﻓﻜﺮ ﻛﺮده ﺑﺎﺷﻴﺪ ﻛﻪ ﭼﻪ ﺟﻮري ﺑﻪ ﻛﺎراﻛﺘﺮ nام ﻳﻚ رﺷﺘﻪ دﺳﺘﺮﺳﻲ ﭘﻴﺪا ﻛﻨﻴﻢ .ﻛﺎر ﺳﺎدهاي اﺳﺖ .اﮔﺮ cﻳﻚ رﺷﺘﻪ ﺑﺎﺷﺪ ،ﻛﺎراﻛﺘﺮ nام آن ] c[n-1اﺳﺖ .ﻣﺜﻼ ﻛﺎراﻛﺘﺮ اول آن ] c[0و ﻛﺎراﻛﺘﺮ دوم آن ] c[1اﺳﺖ )ﺑﻪ 0ﻳﺎ 1در اﻳﻦ ﺟﺎ offsetﻣﻲﮔﻮﻳﻴﻢ( .ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﺤﻮهي اﺳﺘﻔﺎده از offsetرا ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;"char* c = "hello cout<< c[4]; // the final character ;]cout<< c[3 ;]cout<< c[2 ;]cout<< c[1 ;cout<< c[0] << endl )'if(c[5] == '\0 ;"cout<< "c[5] is the terminating null ;)(getch
34
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output: olleh c[5] is the terminating null
ﻛﻠﻤﻪي helloﺑﺮﻋﻜﺲ ﺷﺪه .در ﺿﻤﻦ ] c[5ﻛﺎراﻛﺘﺮ nullاﻧﺘﻬﺎﻳﻲ اﺳﺖ. ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻫﻢ ﺷﻜﻞ دﻳﮕﺮ ﻫﻤﻴﻦ ﺑﺮﻧﺎﻣﻪ اﺳﺖ وﻟﻲ اﺻﻼ ﺗﻮﺻﻴﻪ ﻧﻤﻲﺷﻮد .ﭼﻮن ﺣﺎﻓﻈﻪي زﻳﺎد ﺗﺮي ﻧﻴﺎز دارد ) ﻣﮕﺮ ﺗﺤﺖ ﺷﺮاﻳﻂ ﺧﺎﺻﻲ(: >#include <conio.h >#include <iostream ;using namespace std )(int main { cout<< "hello"[4]; // the final character ;]cout<< "hello"[3 ;]cout<< "hello"[2 ;]cout<< "hello"[1 ;cout<< "hello"[0] << endl )'if("hello"[5] == '\0 ;"cout<< "it is the terminating null ;)(getch }
Output: olleh it is the terminating null
در BDS 2006ﺣﺠﻢ ﻓﺎﻳﻞ .exeاﻳﻦ ﺑﺮﻧﺎﻣﻪ از ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ ﺑﻴﺶ ﺗﺮ اﺳﺖ) .اﮔﺮ از ﻗﺒﻞ ﻧﻤﻲداﻧﺴﺘﻴﺪ ﺣﺘﻤﺎ ﺗﺎ ﺑﻪ ﺣﺎل ﻣﺘﻮﺟﻪ ﺷﺪهاﻳﺪ ﻛﻪ ﻧﺘﻴﺠﻪ اﺟﺮاي ﻫﺮ ﺑﺮﻧﺎﻣﻪ ﻳﻚ ﻓﺎﻳﻞ اﺟﺮاﻳﻲ ﺑﺎ ﭘﺴﻮﻧﺪ .exeاﺳﺖ .در آﻳﻨﺪه
ﺑﻴﺶ ﺗﺮ
ﺗﻮﺿﻴﺢ ﺧﻮاﻫﻢ داد(. ﻣﻲﺷﻮد ﺗﻨﻈﻴﻤﺎت compilerﻫﺎي BDS 2006و ) Visual C++ 2005و compilerﻫﺎي دﻳﮕﺮ( را ﻃﻮري ﺗﻐﻴﻴﺮ داد ﻛﻪ ﺑﺎ ﻫﺮ ﺑﺎر ﻧﻮﺷﺘﻦ " "helloﺣﺎﻓﻈﻪاي ﺟﺪﻳﺪ ﺑﺮاي آن اﺧﺼﺎص داده ﺑﺸﻮد .ﺑﺮاي ﺗﻐﻴﻴﺮ ﺗﻨﻈﻴﻤﺎت در BDS 2006ﻣﺴﻴﺮ زﻳﺮ را از ﻃﺮﻳﻖ ﻣﻨﻮﻫﺎ ﻃﻲ ﻛﻨﻴﺪ: Project | Options… | C++ Compiler (bcc32) | Compiling و اﮔﺮ ﺟﻠﻮي ) Merge duplicate strings (-dﻋﻼﻣﺖ ﺗﻴﻚ ﻫﺴﺖ آن را ﭘﺎك ﻛﻨﻴﺪ:
35
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
: اﻳﻦ ﻣﺴﻴﺮ را ﻃﻲ ﻛﻨﻴﺪVisual C++ 2005 در Project | console Properties… :
| Configuration Properties | C/C++ | General | Debug Information Format | Disabled:
36
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ﺣﺎل در ﻫﻤﻴﻦ ﭘﻨﺠﺮه ﻣﻄﻤﺌﻦ ﺷﻮﻳﺪ ﻛﻪ ﺟﻠﻮي Configuration Properties | C/C++ | Code Generation | Enable String Pooling : ﮔﺬاﺷﺘﻪ ﺷﺪه اﺳﺖNo
37
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
- اﻳﻦ ﺗﻨﻈﻴﻤﺎت را ﺑﻪ ﺣﺎﻟﺖ اوﻟﻴﻪ ﺑﺮﮔﺮداﻧﻴﺪ ﭼﻮن ﺑﻘﻴﻪ، را ﻓﺸﺎر دﻫﻴﺪ و ﺑﺮﻧﺎﻣﻪي زﻳﺮ را اﺟﺮا ﻛﻨﻴﺪ )ﺑﻌﺪ از اﺟﺮاOK :(ﻫﺎ اﺟﺮا ﻣﻲﺷﻮﻧﺪcompiler ي ﺑﺮﻧﺎﻣﻪﻫﺎي اﻳﻦ ﻧﻮﺷﺘﻪ ﺗﺤﺖ ﺗﻨﻈﻴﻤﺎت اوﻟﻴﻪي #include <conio.h> #include <iostream> using namespace std; int main() { char* c1 = "hello"; char* c2 = "hello"; if(c1 != c2) cout<< "c1 and c2 are different"; if(c1 == c2) cout<< "c1 == c2"; getch(); }
Output (BDS 2006): c1 and c2 are different Output (Visual C++ 2005): c1 and c2 are different
38
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺧﺮوﺟﻲ اﻳﻦ ﺑﺮﻧﺎﻣﻪ shortcutﺑﻮدن c1و c2را ﻛﺎﻣﻼ ﺛﺎﺑﺖ ﻣﻲﻛﻨﺪ .ﺷﺎﻳﺪ ﺑﭙﺮﺳﻴﺪ ﭘﺲ ﭼﮕﻮﻧﻪ ﻣﺴﺎوي ﺑﻮدن ﺧﻮد دو رﺷﺘﻪ )و ﻧﻪ shortcutﻫﺎي آنﻫﺎ( را ﺑﺮرﺳﻲ ﻛﻨﻴﻢ؟ ﺟﻮاب اﻳﻦ اﺳﺖ ﻛﻪ ﭼﺎرهاي ﻧﻴﺴﺖ ﺑﻪ ﺟﺰ اﻳﻦ ﻛﻪ ﻣﺴﺎوي ﺑﻮدن ﻛﺎراﻛﺘﺮﻫﺎ را ﻳﻜﻲ ﻳﻜﻲ ﺑﺮرﺳﻲ ﻛﻨﻴﻢ .اﻳﻦ ﻛﺎر را در ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﻧﺠﺎم دادهام: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;"char* c1 = "hen ;"char* c2 = "hen )]if(c1[0] == c2[0 )]if(c1[1] == c2[1 )]if(c1[2] == c2[2 )]if(c1[3] == c2[3 ;"cout<< "strings are equal ;)(getch }
Output: strings are equal
ﺷﺎﻳﺪ اﻳﻦ اﺳﺘﻔﺎدهي ﭘﻴﭽﻴﺪه و ﺗﻮ در ﺗﻮ از ،ifﺑﺮﻧﺎﻣﻪ را ﺳﺮﻳﻊ ﺗﺮ ﻛﻨﺪ اﻣﺎ ﭼﻮن ﻓﻬﻢ ﺑﺮﻧﺎﻣﻪ ﻣﺸﻜﻞ ﺗﺮ ﻣﻲﺷﻮد اﺻﻼ ﻣﻨﺎﺳﺐ ﻧﻴﺴﺖ و دﻟﻴﻞ اﻳﻦ ﻛﻪ ﻣﻦ آن را ﺑﻪ ﻛﺎر ﺑﺮدهام اﻳﻦ اﺳﺖ ﻛﻪ ﻫﻨﻮز && را ﻣﻌﺮﻓﻲ ﻧﻜﺮدهام .اﻟﺒﺘﻪ در ﻣﻮاردي اﻳﻦ ﺷﻜﻞ از ﺑﻪ ﻛﺎرﮔﻴﺮي ﺗﻮ در ﺗﻮي ،ifﺑﺮﻧﺎﻣﻪ را آن ﻗﺪر ﺳﺮﻳﻊ ﺗﺮ ﻣﻲﻛﻨﺪ ﻛﻪ از آن ﻧﻤﻲﺷﻮد ﮔﺬﺷﺖ .در اﻳﻦ ﺟﺎ اﻳﻦ ﻃﻮر ﻧﻴﺴﺖ. اﺟﺮاي ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺧﻄﺮﻧﺎك اﺳﺖ )ﺑﻪ ﺧﺼﻮص در compilerﻫﺎي ﻗﺪﻳﻤﻲ( .ﺣﺘﻲ ﻣﻤﻜﻦ اﺳﺖ )ﺑﺴﺘﻪ ﺑﻪ ﻧﻮع compilerو ﺗﻨﻈﻴﻤﺎت آن( در ﺣﻴﻦ اﺟﺮا ،ﺑﺮﻧﺎﻣﻪ ﺑﺎ ﻳﻚ ) exceptionﻛﻪ ﻧﻮﻋﻲ errorاﺳﺖ( ﻣﺘﻮﻗﻒ ﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;char* c ;c[0] = 5 ;)(getch }
Output: empty
39
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻋﻠﺖ ﻣﺨﺮب ﺑﻮدن اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﻳﻦ اﺳﺖ ﻛﻪ ﻣﻌﻠﻮم ﻧﻴﺴﺖ cﺑﻪ ﻛﺠﺎ اﺷﺎره ﻣﻲﻛﻨﺪ و ﻣﻤﻜﻦ اﺳﺖ ﺑﻪ ﺟﺎي ﺣﺴﺎﺳﻲ از ﺣﺎﻓﻈﻪ اﺷﺎره ﻛﻨﺪ .ﻣﻦ ﻣﻘﺪار اوﻟﻴﻦ ﺑﺎﻳﺖ از ﺣﺎﻓﻈﻪاي را ﻛﻪ ﺑﻪ آن اﺷﺎره دارد ﺗﻐﻴﻴﺮ دادهام ﻛﻪ ﻛﺎر ﺧﻄﺮﻧﺎﻛﻲ اﺳﺖ ﭼﻮن ﻣﻌﻠﻮم ﻧﻴﺴﺖ ﻛﺠﺎي ﺣﺎﻓﻈﻪ را دﺳﺘﻜﺎري ﻛﺮدهام .اﮔﺮ ﺧﻮد وﻳﻨﺪوز ﻳﺎ ﺑﺮﻧﺎﻣﻪاي دﻳﮕﺮ در ﺣﺎل اﺳﺘﻔﺎده از اﻳﻦ ﺑﺎﻳﺖ ﺑﺎﺷﺪ ﻛﺎر وﻳﻨﺪوز ﻳﺎ آن ﺑﺮﻧﺎﻣﻪ ﻣﺨﺘﻞ ﻣﻲﺷﻮد .ﭼﻮن compilerﻫﺎي ﻗﺪﻳﻤﻲ اﺻﻼ ﻣﺮاﻗﺐ ﻫﻴﭻ ﭼﻴﺰ ﻧﻴﺴﺘﻨﺪ اﺣﺘﻤﺎل crashزﻳﺎد اﺳﺖ ) .ﻣﻨﻈﻮر ﻣﻦ از crashﻫﻤﺎن ﻫﻨﮓ ﻛﺮدن اﺳﺖ(compiler .ﻫﺎي ﺟﺪﻳﺪ ﺑﻴﺶ ﺗﺮ ﻣﺮاﻗﺒﻨﺪ .در ﻫﺮ ﺣﺎل BDS 2006ﺑﺮﻧﺎﻣﻪ ﺑﺎﻻ را ﺑﺪون ﻫﻴﭻ اﻋﺘﺮاﺿﻲ اﺟﺮا ﻣﻲﻛﻨﺪ وﻟﻲ اﮔﺮ ﺧﻮد ﻣﻦ ﻣﻘﺪار ﻧﺎﻣﺮﺑﻮﻃﻲ ﺑﻪ cﺑﺪﻫﻢ BDS 2006ﻫﻢ ﻳﻚ raise ،exceptionﻣﻲﻛﻨﺪ )ﭼﻪ ﻗﺪر ﻓﺎرﺳﻲ را ﭘﺎس ﻣﻲدارم ﻣﻦ؟! اﺻﻼ ﺑﻬﺘﺮ ﺑﻮد ﻳﻪ دﻓﻌﻪ ﻣﻲﻧﻮﺷﺘﻢ It raises an exception
( .ﺑﻨﺎﺑﺮاﻳﻦ ﺑﺮﻧﺎﻣﻪي زﻳﺮ در ) BDS 2006و
(Visual C++ 2005ﺑﺎﻋـﺚ ﺑﺮوز exceptionﻣﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;char* c = (char *) 1000 ;cout<< c ;)(getch }
Output: It raises an exception.
ﻧﻜﺘﻪي ﻗﺎﺑﻞ ﺗﻮﺟﻪ ﻣﻘﺪار دادن ﺑﻪ cاﺳﺖ .ﮔﻔﺘﻢ cﭼﻬﺎر ﺑﺎﻳﺘﻲ اﺳﺖ .ﺑﺮاي ﻣﻘﺪار دادن ﺑﻪ cﻛﺎﻓﻲ اﺳﺖ ﺑﻪ اﻳﻦ ﭼﻬﺎر ﺑﺎﻳﺖ ﻣﻘﺪار ﺑﺪﻫﻴﻢ .ﻣﻲﺗﻮاﻧﻴﻢ ﻃﻮري ﺑﻪ اﻳﻦ ﭼﻬﺎر ﺑﺎﻳﺖ ﻣﻘﺪار ﺑﺪﻫﻴﻢ ﻛﻪ ﻣﻌﺎدل ﻋﺪدي آن 1000ﺑﺎﺷﺪ .در compilerﻫﺎي Borlandﻫﻨﮕﺎم ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪي ) Cﻧﻪ (C++ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻢ: ;char* c = 1000
اﻣﺎ در C++و compilerﻫﺎي ﺟﺪﻳﺪ ﻛﻪ ﺧﻴﻠﻲ ﻫﻢ ادﻋﺎي ) type-safetyﻳﻌﻨﻲ اﻣﻨﻴﺖ ﻧﻮع( دارﻧﺪ ﺑﺎﻳﺪ ﻧﻮﺷﺖ: ;char* c = (char *) 1000
ﻳﻌﻨﻲ ﺑﺎﻳﺪ ﺑﺎ ﻧﻮﺷﺘﻦ )* (charﺗﺎﻳﻴﺪ ﻛﻨﻴﻢ ﻛﻪ واﻗﻌﺎ ﻣﻲﺧﻮاﻫﻴﻢ 1000ﺑﻪ ﻳﻚ * charﺗﺒﺪﻳﻞ ﺷﻮد و ﭼﻴﺰي را اﺷﺘﺒﺎﻫﺎ ﻧﻨﻮﺷﺘﻪاﻳﻢ .در ﺣﻘﻴﻘﺖ compilerاز ﻣﺎ اﻣﻀﺎي رﺳﻤﻲ ﺑﺮاي ﺗﺎﻳﻴﺪ ﻣﻲﺧﻮاﻫﺪ. ﺑﻪ ﺗﺒﺪﻳﻞﻫﺎﻳﻲ از اﻳﻦ دﺳﺖ »ﺗﺒﺪﻳﻞ ﻧﻮع« ﮔﻔﺘﻪ ﻣﻲﺷﻮد ﻛﻪ ﺑﻌﺪا
درﺑﺎرهي آن ﺑﻴﺶ ﺗﺮ ﺗﻮﺿﻴﺢ ﻣﻲدﻫﻢ .ﻓﻌﻼ ﺗﻨﻬﺎ
ﭼﻴﺰ ﻣﻬﻢ اﻳﻦ اﺳﺖ ﭼﮕﻮﻧﻪ ﺑﻪ cﻣﻘﺪار ﻣﻨﺎﺳﺒﻲ ﺑﺪﻫﻴﻢ ﻛﻪ raise ،exceptionﻧﺸﻮد .ﺑﺎﻳﺪ اول ﺣﺎﻓﻈﻪي ﻣﻨﺎﺳﺐ را
40
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺗﺨﺼﻴﺺ ﺑﺪﻫﻴﻢ وﺳﭙﺲ ﺑﻪ cﻃﻮري ﻣﻘﺪار ﺑﺪﻫﻴﻢ ﻛﻪ ﺑﻪ آن ﺣﺎﻓﻄﻪ اﺷﺎره ﻛﻨﺪ ﺗﺎ دﻳﮕﺮ raise ،exceptionﻧﺸﻮد. ﺑﺮاي اﻳﻦ ﻛﺎر دو راه وﺟﻮد دارد: (1اﺳﺘﻔﺎده از آراﻳﻪ. (2اﺳﺘﻔﺎده از ﻛﻠﻤﻪي ﻛﻠﻴﺪي .new (1ا ."#د از )را
آراﻳﻪﻫﺎ ﺑﻪ ﺷﻤﺎ اﻣﻜﺎن ﺗﺨﺼﻴﺺ ﺣﺎﻓﻈﻪ روي stackرا ﻣﻲدﻫﻨﺪ stack .ﺑﺨﺸﻲ از ﺣﺎﻓﻈﻪ اﺳﺖ ﻛﻪ ﻣﻌﻤﻮﻻ ﺑﺮاي ﻣﺘﻐﻴﺮﻫﺎي درون ﻳﻚ ﺗﺎﺑﻊ ﺑﻪ ﻛﺎر ﻣﻲرود .ﺑﺎ ﺧﺮوج ﻛﺎﻣﻞ از ﺗﺎﺑﻊ ﺣﺎﻓﻈﻪﻫﺎي ﺗﺨﺼﻴﺺ داده ﺷﺪه در ﺗﺎﺑﻊ از stackﺧﻮد ﺑﻪ ﺧﻮد از ﺑﻴﻦ ﻣﻲروﻧﺪ. ﺑﺮاي اﻳﻦ ﻛﻪ 20ﺑﺎﻳﺖ از ﺣﺎﻓﻈﻪي stackرا درﻳﺎﻓﺖ ﻛﻨﻴﻢ ﻣﻲﻧﻮﻳﺴﻴﻢ: ;]char a[20
ﺣﺎﻻ ﻣﻲﺗﻮﻧﻴﻢ ﺑﮕﻮﻳﻴﻢ aاز ﻧﻮع * charاﺳﺖ و ﺑﻪ ﺣﺎﻓﻈﻪاي ﺑﻪ اﻧﺪازهي 20ﺑﺎﻳﺖ اﺷﺎره دارد .ﺑﻪ aﻳﻚ آراﻳﻪ ﺑﻪ ﻃﻮل 20از charﻫﺎ ﻣﻲﮔﻮﻳﻴﻢ .ﻣﺜﺎل زﻳﺮ ﻧﺤﻮهي اﺳﺘﻔﺎده از آراﻳﻪ را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )(void f { ;]char a[20 ;'a[0] = 'b ;'a[1] = 'a ;'a[2] = 'd ;'a[3] = '\0 ;cout<< a } )(int main { ;)(f ;)(getch }
Output: bad
ﻫﻤﻴﻦ ﺑﺮﻧﺎﻣﻪ را ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﺷﻜﻞ ﺳﺎده ﺗﺮي ﻫﻢ ﺑﻨﻮﻳﺴﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std
41
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(void f { ;"char a[20] = "bad ;cout<< a } )(int main { ;)(f ;)(getch }
Output: bad
اﻣﺎ در اﻳﻦ ﺟﺎ ﺑﺮاي ﻳﻚ ﻛﻠﻤﻪي ﺳﻪ ﺣﺮﻓﻲ 20ﺑﺎﻳﺖ اﺧﺘﺼﺎص داده ﺷﺪه ﻛﻪ زﻳﺎد اﺳﺖ .اﮔﺮ ﺑﺨﻮاﻫﻴﻢ دﻗﻴﻘﺎ ﺗﻌﺪاد ﻻزم از ﺑﺎﻳﺖﻫﺎ اﺧﺼﺎص داده ﺑﺸﻮد ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﺟﺎي ;"char a[20] = "bad
ﺑﻨﻮﻳﺴﻴﻢ: ;"char a[] = "bad
ﻳﺎ ﻣﺜﻞ ﻗﺒﻞﻫﺎ ﺑﻨﻮﻳﺴﻴﻢ: ;"char* a = "bad
وﻟﻲ در ﻫﻤﻪي َاﺷﻜﺎل ِ آراﻳﻪاي ﺑﺮاي ﮔﺮﻓﺘﻦ ﺣﺎﻓﻈﻪ ،ﺑﻌـﺪ از ﺧﺮوج از ﺗﺎﺑﻊ ،fﺣﺎﻓﻈﻪ ﺗﺨﺼﻴﺺ داده ﺷﺪه ﭘﺎك ﻣﻲﺷﻮد .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ اﺳﺖ ﻛﻪ ﺑﺮﻧﺎﻣﻪ زﻳﺮ ﺧﺎﻟﻲ از اﺷﻜﺎل ﻧﻴﺴﺖ: >#include <conio.h >#include <iostream ;using namespace std )(char* f { ;"char a[20] = "bad ;return a } )(int main { ;)(cout<< f ;)(getch }
Output (BDS 2006): bad Output (BDS 2006): @◄ÿ
42
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output (Visual C++ 2005): ↕╠╠╠╠(²
اﻳﻦ ﺑﺮﻧﺎﻣﻪ دو ﺑﺎر ﺑﺎ BDS 2006و ﻳﻚ ﺑﺎر ﺑﺎ Visual C++ 2005اﺟﺮا ﺷﺪه اﺳﺖ .ﻧﺘﺎﻳﺞ را ﻣﻲﺑﻴﻨﻴﺪ. ﻋﻠﺖ اﻳﻦ ﻧﺘﺎﻳﺞ ﻋﺠﻴﺐ و ﻏﺮﻳﺐ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺎ ﺧﺮوج از ﺗﺎﺑﻊ fﺣﺎﻓﻈﻪي اﺧﺘﺼﺎص ﻳﺎﻓﺘﻪ آزاد ﻣﻲﺷﻮد و دﻳﮕﺮ ﺗﺤﺖ ﻛﻨﺘﺮل ﺑﺮﻧﺎﻣﻪ ﻧﻴﺴﺖ .در ﺿﻤﻦ compilerﻳﻚ warningﻣﻲﮔﻴﺮد )ﻳﻌﻨﻲ ﺻﺎدر ﻣﻲﻛﻨﺪ
(.
اﻣﺎ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﻪ errorدارد و ﻧﻪ :warning >#include <conio.h >#include <iostream ;using namespace std )(char* f { ;"char* a = "bad ;return a } )(int main { ;)(cout<< f ;)(getch }
Output: bad
از اﻳﻦ ﻣﻲﺗﻮاﻧﻴﻢ ﻧﺘﻴﺠﻪ ﺑﮕﻴﺮﻳﻢ ﻛﻪ وﻗﺘﻲ درون ﺑﺮﻧﺎﻣﻪ ﻧﻮﺷﺘﻪ ﺷﺪ " "badﺣﺎﻓﻈﻪاي ﺑﻪ آن اﺧﺘﺼﺎص داده ﻣﻲ- ﺷﻮد ﻛﻪ ﺗﺎ آﺧﺮ اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﺗﺤﺖ اﺧﺘﻴﺎر اﺳﺖ. (2ا ."#د از new
ﺑﻪ ﺟﺎي ;]char a[10
ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;]char* a = new char [10
اﻣﺎ وﻗﺘﻲ از newاﺳﺘﻔﺎده ﻣﻲﺷﻮد ﺣﺎﻓﻈﻪ ،از heapﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد .ﺧﺎﺻﻴﺖ ﺣﺎﻓﻈﻪي heapاﻳﻦ اﺳﺖ ﻛﻪ ﺑﺎ ﺧﺮوج از ﺗﺎﺑﻊ آزاد ﻧﻤﻲﺷﻮد .ﺑﺮاي آزاد ﻛﺮدن ﺣﺎﻓﻈﻪاي ﻛﻪ از heapﮔﺮﻓﺘﻪ ﺷﺪه و aﺑﻪ آن اﺷﺎره دارد ﺑﺎﻳﺪ ﻧﻮﺷﺖ: ;delete [] a
ﻣﺜﺎل زﻳﺮ ﻧﺤﻮهي اﺳﺘﻔﺎده از newرا ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h
43
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ >#include <iostream ;using namespace std )(char* f { ;]char* a = new char [20 ;'a[0] = 'b ;'a[1] = 'o ;'a[2] = 'y ;'a[3] = '\0 ;return a } )(int main { ;)(char* c = f ;cout<< c ;delete [] c ;)(getch }
Output: boy
ﻫﺮ ﺣﺎﻓﻈﻪاي ﻛﻪ از heapﮔﺮﻓﺘﻪ ﺷﺪه ﺑﺎﻳﺪ ﺑﻪ آن ﺑﺮﮔﺮدد و ﮔﺮﻧﻪ ﻣﻤﻜﻦ اﺳﺖ ﺑﺎ ﻛﻤﺒﻮد ﺣﺎﻓﻈﻪ ﻣﻮاﺟﻪ ﺑﺸﻮﻳﻢ و ﺑﺮﻧﺎﻣﻪي ﻣﺎ ﻧﺘﻮاﻧﺪ ﺑﺮاي ﻣﺪت ﻃﻮﻻﻧﻲ در ﺣﺎل اﺟﺮا و ﻛﺎر ﺑﺎﺷﺪ )ﻫﺮ ﭘﻮﻟﻲ ﻫﻢ ﻛﻪ از ﻛﺴﻲ ﻗﺮض ﻣﻲﮔﻴﺮﻳﻢ ﺑﺎﻳﺪ ﺑﻬﺶ ﺑﺮﮔﺮدوﻧﻴﻢ وﮔﺮﻧﻪ ﻣﻤﻜﻨﻪ دﻓﻌﻪي ﺑﻌﺪ ﺑﻬﻤﻮن ﭘﻮل ﻧﺪه
(
ﭼﻨﺪ ﻧﻜﺘﻪ در ﻣﻮرد رﺷﺘﻪﻫﺎ و ﻛﺎراﻛﺘﺮﻫﺎ: (1ﺑﺮاي ﻛﺎراﻛﺘﺮ tabاز''\tاﺳﺘﻔﺎده ﻛﻨﻴﺪ. (2اﮔﺮ ﺧﻮاﺳﺘﻴﺪ درون ﺑﺮﻧﺎﻣﻪ رﺷﺘﻪاي را در ﭼﻨﺪ ﺧﻂ ﺑﻨﻮﻳﺴﻴﺪ از \ در آﺧﺮ ﻫﺮ ﺧﻂ اﺳﺘﻔﺎده ﻛﻨﻴﺪ ﻣﺜﻼ \ char* c = "I have a marvelous demonstration \n \of this proposition which this margin ;"is too narrow to contain
ﺧﻮد \ ﺟﺰء رﺷﺘﻪ ﻗﺮار ﻧﻤﻲﮔﻴﺮد )راه دﻳﮕﺮي ﻫﻢ ﻫﺴﺖ ﺑﻌﺪا ﻣﻲﮔﻮﻳﻢ(. (3ﺑﺮاي ﻗﺮار دادن ﻛﺎراﻛﺘﺮ (\ (backslashاز '\\' اﺳﺘﻔﺎده ﻛﻨﻴﺪ ﻣﺜﻼ: ;"char* c = "C:\\Program Files\\Borland\\BDS\\4.0\\Bin
44
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (4ﺑﺮاي ﻛﺎراﻛﺘﺮ »"« از '"\' اﺳﺘﻔﺎده ﻛﻨﻴﺪ. (5ﺑﺮاي ﻛﺎراﻛﺘﺮ »'« از ''\' اﺳﺘﻔﺎده ﻛﻨﻴﺪ.
ا ر ! ه و )را ه ﺣﺎل ﻛﻪ ﺑﺎ رﺷﺘﻪﻫﺎ آﺷﻨﺎ ﺷﺪﻳﺪ ﻓﺮﺻﺖ ﺧﻴﻠﻲ ﺧﻮﺑﻲ اﺳﺖ ﻛﻪ اﺷﺎرهاي ﻫﻢ ﺑﻪ اﺷﺎره ﮔﺮﻫﺎ ﺑﻜﻨﻢ .دﻳﺪﻳﺪ ﻛﻪ char
ﻣﻌﺮف ﻳﻚ ﻛﺎراﻛﺘﺮ اﺳﺖ و * charﻣﻲﺗﻮاﻧﺪ ﺑﻪ دﺳﺘﻪاي از charﻫﺎ ﻛﻪ در ﺣﺎﻓﻈﻪ ﭘﺸﺖ ﺳﺮﻫﻢ ﻗﺮار ﮔﺮﻓﺘﻪاﻧﺪ اﺷﺎره ﻛﻨﺪ .اﻳﻦ ﻣﺨﺼﻮص charﻧﻴﺴﺖ int* .ﻫﻢ ﺑﻪ دﺳﺘﻪاي از intﻫﺎ ﻛﻪ در ﺣﺎﻓﻈﻪ ﭘﺸﺖ ﺳﺮ ﻫﻢ ﻗﺮار ﮔﺮﻓﺘﻪ- اﻧﺪ اﺷﺎره ﻣﻲﻛﻨﺪ و * boolﻫﻢ ﺑﻪ دﺳﺘﻪاي از boolﻫﺎ اﺷﺎره ﻣﻲﻛﻨﺪ و... ﺟﺎﻟﺐ ﺗﺮ از ﻫﻤﻪ اﻳﻦ ﻛﻪ ﻣﺜﻼ ** intﺑﻪ دﺳﺘﻪاي از *intﻫﺎ ﻛﻪ در ﺣﺎﻓﻈﻪ ﭘﺸﺖ ﺳﺮ ﻫﻢ ﻗﺮار دارﻧﺪ اﺷﺎره ﻣﻲ- ﻛﻨﺪ .آراﻳﻪﻫﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻨﺪ از ﻫﺮ ﻧﻮﻋﻲ ﺳﺎﺧﺘﻪ ﺑﺸﻮﻧﺪ .ﻣﺜﻼ اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ: ;]int a[10
ﭼﻬﻞ ﺑﺎﻳﺖ از ﺣﺎﻓﻈﻪي stackﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد و aﺑﻪ اﻳﻦ ﺑﺎﻳ ﺖﻫﺎ اﺷﺎره دارد a[0] .ﺑﻪ اوﻟﻴﺖ ﭼﻬﺎر ﺑﺎﻳﺖa[1] ،
ﺑﻪ دوﻣﻴﻦ ﭼﻬﺎر ﺑﺎﻳﺖ و ...اﺷﺎره دارد .ﻫﺮ ] a[iﻳﻚ ﻣﺘﻐﻴﺮ از ﻧﻮع intاﺳﺖ .ﻧﻮع aرا ﻣﻲﺗﻮان * intﮔﺮﻓﺖ. ﺑﻪ ﻣﺜﺎل زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )void f(int* b { ;b[0] = 1 ;b[1] = 2 ;b[2] = 3 } )(int main { ;]int* a = new int [10 ;)f(a ;cout<< a[0] << endl ;cout<< a[1] << endl ;cout<< a[2] << endl ;delete [] a ;]int b[10 ;)f(b ;cout<< b[0] << endl
45
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;cout<< b[1] << endl ;cout<< b[2] << endl ;cout<< b[3] << endl ;)(getch }
Output: 1 2 3 1 2 3 846764765
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻳﻚ ﺑﺎر از heapو ﺑﺎر دﻳﮕﺮ از stackﺣﺎﻓﻈﻪ ﻣﻲﮔﻴﺮﻳﻢ و shortcutآنﻫﺎ را ﺑﻪ ﺗﺎﺑﻊ fﻣﻲ- ﻓﺮﺳﺘﻴﻢ f .از ﻃﺮﻳﻖ اﻳﻦ shortcutﻫﺎ ﺑﻪ ﺳﺮاغ ﺣﺎﻓﻈﻪﻫﺎﻳﻲ ﻣﻲرود ﻛﻪ shortcutﻫﺎ ﺑﻪ آنﻫﺎ اﺷﺎره دارﻧﺪ و ﻣﻘﺪار ﭼﻨﺪ intاول از آنﻫﺎ را ﺗﻐﻴﻴﺮ ﻣﻲدﻫﺪ .ﺳﭙﺲ از fﺧﺎرج ﻣﻲﺷﻮﻳﻢ) .در ﺣﻘﻴﻘﺖ ِ controlﺑﺮﻧﺎﻣﻪ از fﺧﺎرج ﻣﻲﺷﻪ وﮔﺮﻧﻪ ﻣﺎ ﻛﻪ روي ﺻﻨﺪﻟﻲ و ﭘﺸﺖ ﻣﻴﺰ ﻧﺸﺴﺴﻴﻢ و از ﻫﻴﭻ ﺟﺎ ﺧﺎرج ﻧﻤﻲﺷﻴﻢ
( .ﺑﻪ ﺳﺮاغ ﺣﺎﻓﻈﻪﻫﺎي
ﺗﺨﺼﻴﺺ داده ﺷﺪه ﻣﻲروﻳﻢ و ﭼﻨﺪ intاول آنﻫﺎ را ﭼﺎپ ﻣﻲﻛﻨﻴﻢ .ﻋﻠﺖ ﻇﺎﻫﺮ ﺷﺪن ﻋﺪد ﻋﺠﻴﺐ 846764765
اﻳﻦ اﺳﺖ ﻛﻪ ] b[3را ﻛﻪ ﻫﻴﭻ ﻣﻘﺪاري ﺑﻪ آن ﻧﺪاده ﺑﻮدﻳﻢ ﭼﺎپ ﻛﺮدهاﻳﻢ .ﻣﻮﻗﻊ ﮔﺮﻓﺘﻦ ﺣﺎﻓﻈﻪ b[3] ،ﺑﺎ ﻫﻤﻴﻦ ﻣﻘﺪار از stackﮔﺮﻓﺘﻪ ﺷﺪه ﺑﻮد و ﻣﺎ اﺻﻼ آن را ﺗﻐﻴﻴﺮ ﻧﺪادﻳﻢ. ﺑﻪ ﻣﺘﻐﻴﺮﻫﺎﻳﻲ از ﻧﻮع * int* ،charو pointer ، ...ﻳﺎ اﺷﺎره ﮔﺮ ﻣﻲﮔﻮﻳﻴﻢ .ﻳﻚ آراﻳﻪ ﻣﺜﻞ ;]int a[25
را ﻣﻲﺗﻮان ﻳﻚ *intي ﻏﻴﺮ ﻗﺎﺑﻞ ﺗﻐﻴﻴﺮ )ﻳﻌﻨﻲ ﺛﺎﺑﺖ( ﮔﺮﻓﺖ .ﺑﻪ اﻳﻦ ﻣﻌﻨﻲ ﻛﻪ ﻣﺴﺘﻘﻴﻤﺎ ﻣﻘﺪار aرا ﻧﻤﻲﺗﻮان ﺗﻐﻴﻴﺮ داد .ﻳﻌﻨﻲ ﻧﻮﺷﺘﻦ ;]int* a = new int [10 ;]int b[10 ;b = a
ﻳﻚ errorﺑﻪ وﺟﻮد ﻣﻲآورد وﻟﻲ ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;]int* a = new int [10 ;]int b[10 ;a = b
ﻧﻜﺘﻪي دﻳﮕﺮ در ﻣﻮرد آراﻳﻪﻫﺎ دادن ﻣﻘﺪار اوﻟﻴﻪ اﺳﺖ .ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ:
46
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ >#include <conio.h >#include <iostream ;using namespace std )(int main { ;}int a[10] = {1,2 ;}int b[] = {1,2 ;}'char c[3] = {'a','b','\0 ]cout<< a[0] << a[1] << a[2] << a[3] << a[4 ;<< a[5] << a[6] << a[7] << a[8] << a[9] << endl ;cout<< b[0] << b[1] << endl ;cout<< c ;)(getch }
Output: 1200000000 12 ab
ﻓﻘﻂ ﺑﮕﻮﻳﻢ ﻛﻪ ﺑﺎ ﻧﻮﺷﺘﻦ ;}int a[10] = {1,2
ﻫﻤﻪي int 10ﻣﻮﺟﻮد در اﻳﻦ آراﻳﻪ ﺑﻪ ﺟﺰ دو ﺗﺎي اول ﺻﻔﺮ ﻣﻲﺷﻮﻧﺪ .در ﺿﻤﻦ bﺑﻪ دو intﭘﺸﺖ ﺳﺮ ﻫﻢ، اﺷﺎره دارد .ﻫﻢ ﭼﻨﻴﻦ ﺑﻪ ﺟﺎي ;}'char c[3] = {'a','b','\0
ﺧﻴﻠﻲ راﺣﺖ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;"char c[3] = "ab
pointerﻫﺎ ﺣﺘﻤﺎ ﻻزم ﻧﻴﺴﺖ ﺑﻪ دﺳﺘﻪاي ﭼﻨﺪﺗﺎﻳﻲ از ﻣﺘﻐﻴﺮﻫﺎ در ﺣﺎﻓﻈﻪ اﺷﺎره ﻛﻨﻨﺪ .ﻳﻚ pointerﻣﻲﺗﻮاﻧﺪ ﺣﺘﻲ ﺑﻪ ﻓﻘﻂ ﻳﻚ ﻣﺘﻐﻴﺮ )ﻛﻪ ﺣﺎﻓﻈﻪي آن ﺗﺤﺖ اﺧﺘﻴﺎر اﺳﺖ( اﺷﺎره ﻛﻨﺪ. اﮔﺮ aﻣﺘﻐﻴﺮي ﺑﺎ ﻧﻮع intﺑﺎﺷﺪ و ﺑﺨﻮاﻫﻴﻢ ﻳﻚ pointerﺑﻪ آن داﺷﺘﻪ ﺑﺎﺷﻴﻢ از &aاﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ ﻛﻪ ﻳﻚ pointerﺑﻪ aاﺳﺖ .ﺑﻨﺎﺑﺮاﻳﻦ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;int* p = &a
ﺣﺎل اﮔﺮ ﺑﺨﻮاﻫﻴﻢ از pﺑﻪ aﺑﺮﺳﻴﻢ ﻛﺎﻓﻲ اﺳﺖ از *pاﺳﺘﻔﺎده ﻛﻨﻴﻢ .ﻳﻌﻨﻲ *pﻫﻤﺎن aاﺳﺖ ﺑﺎ ﻫﻤﺎن ﻣﻜﺎن از ﺣﺎﻓﻈﻪ .ﻣﺜﺎل زﻳﺮ اﻳﻦ ﺷﻜﻞ از ﻛﺎرﺑﺮد pointerﻫﺎ را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 2
47
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int* p = &a ;*p = 5 ;cout<< a ;)(getch }
Output: 5
از ﺧﺮوﺟﻲ ﻣﻲﺗﻮان ﻓﻬﻤﻴﺪ ﻛﻪ واﻗﻌﺎ ﻣﻜﺎن *pو aدر ﺣﺎﻓﻈﻪ ﻳﻜﻲ اﺳﺖ وﮔﺮﻧﻪ ﺧﺮوﺟﻲ ﺑﺎﻳﺪ 2ﻣﻲﺑﻮد .در ﺿﻤﻦ ﺑﻪ ﺟﺎي ;*p = 5
ﻣﻲﺗﻮاﻧﺴﺘﻴﻢ ﻣﺎﻧﻨﺪ ﻗﺒﻞ ﺑﻨﻮﻳﺴﻴﻢ: ;p[0] = 5
ﭼﻮن pﺑﻪ ﻣﻜﺎﻧﻲ از ﺣﺎﻓﻈﻪ ﺷﺎﻣﻞ ﻓﻘﻂ ﻳﻚ intاﺷﺎره دارد و اﻳﻦ ﺑﺎ وﻗﺘﻲ ﻛﻪ pﺑﻪ ﻣﻜﺎﻧﻲ ﺷﺎﻣﻞ ﭼﻨﺪ intاﺷﺎره داﺷﺘﻪ ﺑﺎﺷﺪ ﻓﺮﻗﻲ ﻧﻤﻲﻛﻨﺪ
.
ﺗﺎ ﺑﻪ ﺣﺎل ﻣﺘﻐﻴﺮﻫﺎ را روي stackدرﺳﺖ ﻣﻲﻛﺮدﻳﻢ و ﺑﻪ ﻫﻤﻴﻦ ﻋﻠﺖ ﺑﺎ ﺧﺮوج از ﺗﺎﺑﻌﻲ ﻛﻪ آنﻫﺎ را ﺳﺎﺧﺘﻪ ،ﺣﺎﻓﻈﻪ ﺧﻮد ﺑﻪ ﺧﻮد آزاد ﻣﻲﺷﺪ .ﺣﺎﻻ ﻣﻲﺗﻮاﻧﻴﻢ ﻳﻚ ﻣﺘﻐﻴﺮ را روي heapﺑﺴﺎزﻳﻢ و ﻫﺮ وﻗﺖ ﺧﻮدﻣﺎن ﺧﻮاﺳﺘﻴﻢ آن را آزاد ﻛﻨﻴﻢ .در ﻣﺜﺎل زﻳﺮ اﻳﻦ ﻛﺎر را اﻧﺠﺎم دادهام: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;]int* p = new int [1 ;*p = 4 ;cout<< *p ;delete [] p ;)(getch }
Output: 4
درواﻗﻊ *pﻫﻤﺎن ﻣﺘﻐﻴﺮي اﺳﺖ ﻛﻪ ﻣﻦ ﺳﺎﺧﺘﻪام .ﻫﻤﻴﻦ ﺑﺮﻧﺎﻣﻪ را ﻣﻲﺗﻮان ﺑﺎ ﺣﺬف ][ﻫﺎ ﺑﻪ ﺻﻮرت ﺳﺎده ﺗﺮ زﻳﺮ ﻧﻮﺷﺖ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int* p = new int
48
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;*p = 4 ;cout<< *p ;delete p ;)(getch }
Output: 4 ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ deleteﻫﻢ ][ ﻧﺪارد .وﻗﺘﻲ ﺗﻨﻬﺎ ﻳﻚ ﻣﺘﻐﻴﺮ ﻣﻲﺳﺎزﻳﻢ ﻻزم ﻧﻴﺴﺖ از ][ ﺑﺮاي newو delete
اﺳﺘﻔﺎده ﻛﻨﻴﻢ .اﻟﺒﺘﻪ ﻳﺎ ﻫﺮ دو را ﺑﺎﻳﺪ ﺑﺪون ][ ﻧﻮﺷﺖ ﻳﺎ ﻫﺮدو را ﺑﺎ ][ ﻧﻪ اﻳﻦ ﻛﻪ ﺑﺮاي ﻳﻜﻲ ][ ﺑﻨﻮﻳﺴﻴﻢ و ﺑﺮاي دﻳﮕﺮي ﻧﻨﻮﻳﺴﻴﻢ .اﮔﺮ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﺨﻮاﻫﻴﻢ ﻧﺎم ﺳﺎده ﺗﺮي )ﻣﺜﻼ (aﺑﻪ *pﺑﺪﻫﻴﻢ ﺑﺎﻳﺪ از & intاﺳﺘﻔﺎده ﻛﻨﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int* p = new int ;int& a = *p ;*p = 4 ;cout<< a ;delete p ;)(getch }
Output: 4 ﻛﻪ در آن *pو aدﻗﻴﻘﺎ ﻳﻚ ﻣﻜﺎن در ﺣﺎﻓﻈﻪ دارﻧﺪ و ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ وﻗﺘﻲ ﻣﻘﺪار *pرا 4ﻗﺮار دادﻳﻢ ،ﻣﻘﺪار a
ﻫﻢ 4ﺷﺪ a .روي heapﻗﺮار دارد .در ﺿﻤﻦ ﭘﺲ از دﺳﺘﻮر ;delete p
اﺳﺘﻔﺎده از aﺧﻄﺮﻧﺎك اﺳﺖ زﻳﺮا aﺑﺨﺸﻲ از ﺣﺎﻓﻈﻪ اﺳﺖ ﻛﻪ آزاد ﺷﺪه و دﻳﮕﺮ ﺗﺤﺖ ﻛﻨﺘﺮل ﺑﺮﻧﺎﻣﻪ ﻧﻴﺴﺖ و اﺳﺘﻔﺎده از آن ﻣﻤﻜﻦ اﺳﺖ ﻣﻮﺟﺐ crashﺷﻮد .ﻣﻌﻨﻲ crashرا ﻗﺒﻼ در ﺻﻔﺤﻪي 31ﮔﻔﺘﻪام) .اﻳﻦ ارﺟﺎع ﻣﻦ ﺑﻪ ﺻﻔﺤﻪي 31ﻛﻤﻲ ﻏﻴﺮ ﻋﺎﻗﻼﻧﻪ اﺳﺖ
زﻳﺮا ﮔﻔﺘﻦ ﻣﻌﻨﻲ crashﺟﺎﻳﻲ ﻛﻢ ﺗﺮي ﻣﻲﮔﺮﻓﺖ
و اﻳﻦ ﻛﺎر ﻣﻦ
ﻣﺜﻞ اﺳﺘﻔﺎده از ﻳﻚ pointerﺑﻪ ﻳﻚ ﻛﺎراﻛﺘﺮ اﺳﺖ .ﭼﻮن ﺣﺠﻢ pointerﭼﻬﺎر ﺑﺮاﺑﺮ ﺧﻮد ﻛﺎراﻛﺘﺮ ﻣﻲﺷﻮد. ( ﺑﻪ ﻣﺘﻐﻴﺮي ﻣﺜﻞ aدر ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﻛﻪ ﺑﺎ & intﺗﻌﺮﻳﻒ ﺷﺪه referenceﻣﻲﮔﻮﻳﻨﺪ .ﻳﺎدﺗﺎن ﻫﺴﺖ ﻛﻪ ﻗﺒﻼ از & intدر ﺗﻌﺮﻳﻒ ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻚ ﺗﺎﺑﻊ اﺳﺘﻔﺎده ﻛﺮدﻳﻢ ﺗﺎ آن ﺗﺎﺑﻊ ﺑﺘﻮاﻧﺪ ﻣﻘﺪار آرﮔﻮﻣﺎن را ﺗﻐﻴﻴﺮ دﻫﺪ.
49
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ referenceﻫﺎ ﻣﺨﺼﻮص C++ﻫﺴﺘﻨﺪ و در زﺑﺎن Cﻣﻮﺟﻮد ﻧﻴﺴﺘﻨﺪ .در زﺑﺎن Cﺑﺮاي اﻳﺠﺎد اﻳﻦ ﻗﺎﺑﻠﻴﺖ در ﺗﺎﺑﻊﻫﺎ از pointerﻫﺎ اﺳﺘﻔﺎده ﻣﻲﺷﻮد.
ا اد / 01
ﺑﺮاي ﻧﺸﺎن دادن ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ ﻧﻮعﻫﺎي ﻣﺨﺘﻠﻔﻲ ﻫﺴﺖ: ﺗﻌﺪاد ﺑﺎﻳﺖ
ﻧﻮع
4 2 4 8
Int Short Long __int64
ﺑﻪ ﺟﺎي longﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ ،long intﺑﻪ ﺟﺎي shortﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ short intو ﺑﻪ ﺟﺎي __int64ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ .long longدر compilerﻫﺎي ﺧﻴﻠﻲ ﻗﺪﻳﻤﻲ intدو ﺑﺎﻳﺘﻲ اﺳﺖ وﻟﻲ اﻣﺮوزه ﭼﻬﺎر ﺑﺎﻳﺘﻲ اﺳﺖ .در Visual C++ 2005ﻣﻲﺷﻮد از __int128ﻫﻢ اﺳﺘﻔﺎده ﻛﺮد اﻣﺎ ﻫﻨﻮز ﺑﻪ ﻃﻮر ﻛﺎﻣﻞ ﺟﺎ ﻧﻴﻔﺘﺎده ﻣﺜﻼ ) sizeof(__int128ﺧﻄﺎ اﻳﺠﺎد ﻣﻲﻛﻨﺪ.
ا اد ا 5ر
ﮔﻔﺘﻢ ﻣﺘﻐﻴﺮي ﻛﻪ ﻧﻮع ﻣﺘﻐﻴﺮي را ﻛﻪ ﻗﺮار اﺳﺖ ﻋﺪدﻫﺎي اﻋﺸﺎري را در ﺧﻮدش ذﺧﻴﺮه ﻛﻨﺪ double ،ﻣﻲﮔﻴﺮﻳﻢ. 8 doubleﺑﺎﻳﺘﻲ اﺳﺖ .ﻧﻮع دﻳﮕﺮي ﻫﺴﺖ ﻛﻪ ﺣﺠﻢ ﻛﻢ ﺗﺮي دارد float .ﻳﻚ ﻧﻮع 4ﺑﺎﻳﺘﻲ اﺳﺖ و ﻣﺜﻞ doubleﺑﺮاي ذﺧﻴﺮهي اﻋﺪاد اﻋﺸﺎري ﺑﻪ ﻛﺎر ﻣﻲرود. در اﻳﻦ ﺟﺎ ﻣﻲﺧﻮاﻫﻢ ﻛﻤﻲ در ﻣﻮرد ﻧﺤﻮهي ذﺧﻴﺮه ﺷﺪن ﻳﻚ ﻋﺪد اﻋﺸﺎري ﺣﺮف ﺑﺰﻧﻢ .ﻫﺮ ﻋﺪدي اﻋﺸﺎري را ﻣﻲ- ﺷﻮد ﺑﻪ ﺷﻜﻞ ﻧﺸﺎن داد ﻛﻪ ﻳﻚ ﻋﺪد اﻋﺸﺎري در ﻣﺒﻨﺎي 2ﺑﻴﻦ .و .اﺳﺖ و ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ در ﻣﺒﻨﺎي 2اﺳﺖ .ﻣﺜﻼ ﻣﻲﺗﻮاﻧﺪ .ﺑﺎﺷﺪ و ﻣﻲﺗﻮاﻧﺪ ﺑﺎﺷﺪ .ﺑﻪ ﻣﺎﻧﺘﻴﺲ ) (mantissaو ﺑﻪ ﻧﻤﺎ ) (exponentﻣﻲﮔﻮﻳﻨﺪ .ﺑﺮاي ذﺧﻴﺮه ﻛﺮدن ﻳﻚ ﻋﺪد اﻋﺸﺎري از ﻫﻤﻴﻦ ﺷﻜﻞ ﻧﻤﺎﻳﺶ اﺳﺘﻔﺎده ﻣﻲﺷﻮد .ﻳﻌﻨﻲ ﺑﻪ ﺟﺎي ذﺧﻴﺮه ﻛﺮدن ﺧﻮد ﻋﺪد ،اول آن را ﺑﻪ ﺷﻜﻞ ﻣﻲﻧﻮﻳﺴﻴﻢ و و را 50
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ذﺧﻴﺮه ﻣﻲﻛﻨﻴﻢ .ﭼﻮن ﻫﻤﻴﺸﻪ ﺑﺎ » « .ﺷﺮوع ﻣﻲﺷﻮد ،ﻧﻴﺎزي ﺑﻪ ذﺧﻴﺮه ﻛﺮدن » « .ﻧﻴﺴﺖ و ﻛﺎﻓﻲ اﻋﺪاد ﺑﻌﺪ از ﻣﻤﻴﺰ را ذﺧﻴﺮه ﻛﻨﻴﻢ .ﻳﻚ ﺑﻴﺖ ﻫﻢ ﺑﺎﻳﺪ ﺑﺮاي ﻣﺸﺨﺺ ﻛﺮدن ﻋﻼﻣﺖ ﻣﺜﺒﺖ ﻳﺎ ﻣﻨﻔﻲ ﻛﻨﺎر ﮔﺬاﺷﺘﻪ ﺷﻮد .ﺟﺪول زﻳﺮ ﺗﻌﺪاد ﺑﻴﺖﻫﺎﻳﻲ ﻛﻪ ﺑﺮاي ﻣﺎﻧﺘﻴﺲ و ﻧﻤﺎ ﺑﻪ ﻛﺎر ﻣﻲرود ﻧﺸﺎن ﻣﻲﻫﺪ: ﺗﻌﺪاد ﺑﻴﺖﻫﺎي ﻣﺎﻧﺘﻴﺲ
ﺗﻌﺪاد ﺑﻴﺖﻫﺎي ﻧﻤﺎ
ﻧﻮع
23 52
8 11
float double
ﭘﺲ ﺑﺰرگ ﺗﺮﻳﻦ ﻋﺪدي را ﻛﻪ در ﻳﻚ ﻣﺘﻐﻴﺮ floatﻣﻲﺷﻮد ذﺧﻴﺮه ﻛﺮد اﻳﻦ اﺳﺖ: . . ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻧﻤﺎ ﻣﻲﺗﻮاﻧﺪ ﻣﺜﺒﺖ ﻳﺎ ﻣﻨﻔﻲ ﺑﺎﺷﺪ .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺑﻴﺶﺗﺮﻳﻦ ﻣﻘﺪارش 127اﺳﺖ .اﮔﺮ از اﻳﻦ ﻣﺤﺎﺳﺒﺎت آﺧﺮي ﺧﻴﻠﻲ ﺳﺮ در ﻧﻤﻲآورﻳﺪ ﻣﻬﻢ ﻧﻴﺴﺖ! اﻳﻦ ﺑﺨﺶ ﺑﻴﺶ ﺗﺮ ﺑﻪ درد اﻃﻼﻋﺎت ﻋﻤﻮﻣﻲ ﻣﻲﺧﻮرد ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ .اﻟﺒﺘﻪ ﺑﻌﺪا اﺷﺎرهﻫﺎﻳﻲ ﺑﻪ اﻳﻦ ﺟﺎ ﻣﻲﻛﻨﻢ. ﺑﻪ ﻧﻤﺎﻳﺶ ﻣﻤﻴﺰ ﺷﻨﺎور ) (floating pointﻣﻲﮔﻮﻳﻨﺪ .ﺟﺰﺋﻴﺎت زﻳﺎدي درﺑﺎرهي دﻗﺖ و ﺟﻤﻊ و ﺿﺮب دو ﻋﺪد ﺑﻪ ﺻﻮرت ﻣﻤﻴﺰ ﺷﻨﺎور ﻫﺴﺖ .ﻣﻦ وارد ﺟﺰﺋﻴﺎت ﻧﻤﻲﺷﻮم. ﺑﺮﻧﺎﻣﻪي زﻳﺮ را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;double r = 123456789123456789 ;cout<< r ;)(getch }
Output (BDS 2006): 1.23457e+17 Output (Visual C++ 2005): 1.23457e+017
51
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﮔﺮﭼﻪ doubleﻣﻲﺗﻮاﻧﺪ اﻋﺪاد ﺧﻴﻠﻲ ﺑﺰرﮔﻲ را دﺧﻮدش ﺟﺎ ﺑﺪﻫﺪ وﻟﻲ ﻫﺮ ﭼﻪ ﻋﺪد ﺑﺰرگ ﺗﺮ ﺷﺪ دﻗﺖ ﻛﻢ و ﻛﻢ ﺗﺮ ﻣﻲﺷﻮد .در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﺧﺮوﺟﻲ ﻣﻲﮔﻮﻳﺪ ﻛﻪ ﻋﺪد ﺑﻪ ﺷﻜﻞ .ذﺧﻴﺮه ﺷﺪه ﻛﻪ اﻳﻦ ﻳﻌﻨﻲ ﺑﻪ ﺟﺎي 123456789123456789ﺑﺎ ﺗﻘﺮﻳﺐ ﻋﺪد 123457000000000000در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﺪه .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻫﻨﮕﺎم compileﻛﺮدن ﻳﻚ warningﻫﻢ درﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﻢ. در Visual C++ 2005ﺑﻪ ﺟﺎي doubleﻣﻲﺗﻮان ﻧﻮﺷﺖ .long floatﻧﻮع long doubleﻫﻢ دارﻳﻢ ﻛﻪ در 10 ،BDS 2006ﺑﺎﻳﺖ اﺳﺖ اﻣﺎ در Visual C++ 2005ﻣﺜﻞ 8 doubleﺑﺎﻳﺖ اﺳﺖ.
' 6 ,ع
ﻫﺮ ﻣﺘﻐﻴﺮ در ﺗﻌﺪادي از ﺑﺎﻳﺖﻫﺎي ﻣﻮﺟﻮد در ﺣﺎﻓﻈﻪ ﻗﺮار ﻣﻲﮔﻴﺮد .ﺳﻮال اﻳﻦ اﺳﺖ ﻛﻪ ﭘﺲ ﭼﻪ ﻓﺮﻗﻲ وﺟﻮد دارد ﺑﻴﻦ ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع intﻛﻪ ﭼﻬﺎر ﺑﺎﻳﺖ اﺳﺖ و ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع * charﻛﻪ آن ﻫﻢ ﭼﻬﺎر ﺑﺎﻳﺖ اﺳﺖ؟ ﻣﻲ- ﺷﻮد ﮔﻔﺖ ﻫﻴﭻ ﻓﺮﻗﻲ ﻧﻴﺴﺖ وﻟﻲ compilerﺑﻴﻦ آنﻫﺎ ﻓﺮق ﻣﻲﮔﺬارد compiler .اﺻﻼ اﺟﺎزه ﻧﻤﻲدﻫﺪ ﻛﻪ ﺑﻲ ﻣﻘﺪﻣﻪ ﻳﻚ intﺑﻪ ﻋﻨﻮان * charاﺳﺘﻔﺎده ﺷﻮد .ﻗﺒﻼ ﮔﻔﺘﻪ ﺑﻮدم ﻛﻪ compilerﻫﺎي Cاﻳﻦ اﺟﺎزه را ﻣﻲدﻫﻨﺪ وﻟﻲ در C++ﺑﺎﻳﺪ ﺑﻪ وﺳﻴﻠﻪي castingﻳﻚ ﺗﺒﺪﻳﻞ ﻧﻮع آﺷﻜﺎر اﻧﺠﺎم ﺑﺸﻮد .اﮔﺮ ﻳﺎدﺗﺎن ﺑﺎﺷﺪ ﻗﺒﻼ ﮔﻔﺘﻪ ﺑﻮدم ﻛﻪ ﺗﻘﺮﻳﺒﺎ ﻫﺮ ﺑﺮﻧﺎﻣﻪي Cﻳﻚ ﺑﺮﻧﺎﻣﻪي C++اﺳﺖ .ﺑﺮﺧﻲ ﺑﺮﻧﺎﻣﻪﻫﺎي Cﺑﺮاي اﺟﺮا ﺷﺪن در C++ﻧﻴﺎز ﺑﻪ ﺗﻐﻴﻴﺮاﺗﻲ ﺟﺰﺋﻲ دارﻧﺪ .ﺑﺮاي ﻣﺜﺎل ﻧﻮﺷﺘﻦ ;int* a = 5000
در Cﻫﻴﭻ ﺧﻄﺎﻳﻲ ﻧﺪارد اﻣﺎ در C++ﺑﺎﻳﺪ ﻧﻮﺷﺖ: ;int* a =(int*) 5000
ﻧﻮﺷﺘﻦ )*(intﺟﻠﻮي 5000ﺑﺎﻋـﺚ ﻣﻲﺷﻮد ﻛﻪ 5000 ،compilerرا ﻳﻚ * intﺑﮕﻴﺮد .ﺑﻪ اﻳﻦ ﺷﻜﻞ از ﺗﺒﺪﻳﻞ ﻧﻮع casting ،ﻣﻲﮔﻮﻳﻨﺪ .ﻻزم ﻧﻴﺴﺖ ﺣﺘﻤﺎ ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎي دو ﻧﻮﻋﻲ ﻛﻪ ﺑﻪ ﻫﻢ ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮﻧﺪ ﺑﺮاﺑﺮ ﺑﺎﺷﻨﺪ؛ اﮔﺮ ﭼﻴﺰي زﻳﺎد ﺑﻴﺎﻳﺪ ﺣﺬف ﻣﻲﺷﻮد و اﮔﺮ ﭼﻴﺰي ﻛﻢ ﺑﻴﺎﻳﺪ ﺑﻪ ﺟﺎﻳﺶ ﺻﻔﺮ ﮔﺬاﺷﺘﻪ ﻣﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std
c; // char* --> int
52
www.pupuol.com
;"= "a )= (int
)(int main { char* c int a
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ char ch1 = (char) c; // char* --> char char ch2 = (char) a; // int --> char ;cout<< (int) ch1 << endl << a << endl << (int) ch2 ;)(getch }
Output (BDS 2006): -78 4219058 -78 Output (Visual C++ 2005): 76 4290124 76
در اﻳﻦ ﻣﺜﺎل ﺳﻌﻲ ﻛﺮدهام ﻧﺸﺎن دﻫﻢ وﻗﺘﻲ از ﭼﻬﺎر ﺑﺎﻳﺖ از ﺣﺎﻓﻈﻪ ﺣﺮف ﻣﻲزﻧﻴﻢ ﻧﻴﺎزي ﺑﻪ ﺑﻴﺎن ﻧﻮع ﻧﻴﺴﺖ .اﻳﻦ compilerاﺳﺖ ﻛﻪ روي ﻧﻮع ﺣﺴﺎس اﺳﺖ وﮔﺮﻧﻪ ﺑﺎﻳﺖﻫﺎي ﺣﺎﻓﻈﻪ از ﺻﻔﺮ و ﻳﻚ درﺳﺖ ﺷﺪهاﻧﺪ و ﻧﻮع ﺑﺮاي آنﻫﺎ ﻣﻌﻨﻲ ﻧﺪارد .ﻗﺒﻞ از ﺗﻮﺿﻴﺢ ﻣﺜﺎل ﺑﺎﻻ ﻻزم اﺳﺖ ﺑﺪاﻧﻴﺪ ﻛﻪ ﻫﺮ ﻛﺎراﻛﺘﺮ ﻳﻚ ﺑﺎﻳﺖ اﺳﺖ و ﺑﻪ ﻣﻘﺪار ﻋﺪدي اﻳﻦ ﺑﺎﻳﺖ ﻛﺪ ) ASCIIﺑﺨﻮاﻧﻴﺪ اَﺳﻜﻲ( ﻛﺎراﻛﺘﺮ ﻣﻲﮔﻮﻳﻨﺪ .ﻣﺜﻼ ﻛﺪ ASCIIﻛﺎراﻛﺘﺮ ' 'aﻋﺪد 97اﺳﺖ. ASCIIﻣﺨﻔﻒ American Standard Code for Information Interchange اﺳﺖ .وﻗﺘﻲ ﺑﺎ coutﻣﺘﻐﻴﺮي از ﻧﻮع charﺑﺎ ﻣﻘﺪار 97را ﭼﺎپ ﻣﻲﻛﻨﻴﻢ cout ،اول ﻧﻮع آن را ﺗﺸﺨﻴﺺ ﻣﻲ- دﻫﺪ و ﭼﻮن ﻣﻲﺑﻴﻨﺪ ﻛﺎراﻛﺘﺮ اﺳﺖ ﺑﻪ ﺟﺎي ﻋﺪد 97ﺷﻜﻞ ﻛﺎراﻛﺘﺮي آن )ﻳﻌﻨﻲ (aرا ﭼﺎپ ﻣﻲﻛﻨﺪ .اﻣﺎ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻣﻘﺪار ﻋﺪدي )ﻳﻌﻨﻲ ﻫﻤﺎن ﻛﺪ (ASCIIرا ﭼﺎپ ﻛﻨﻴﻢ ﺑﺎﻳﺪ ﺑﺎ castingآن ﻛﺎراﻛﺘﺮ را ﺑﻪ intﺗﺒﺪﻳﻞ ﻛﻨﻴﻢ ﺑﻌﺪ ﭼﺎﭘﺶ ﻛﻨﻴﻢ .ﻣﺜﻼ ﺧﺮوﺟﻲ ;'cout<< (int) 'a
اﻳﻦ اﺳﺖ: 97
در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﻳﻚ pointerﻛﻪ ﭼﻬﺎر ﺑﺎﻳﺖ دارد ﺑﻪ وﺟﻮد آﻣﺪه .اول ﺑﻪ aﻛﻪ ﭼﻬﺎر ﺑﺎﻳﺘﻲ اﺳﺖ castﺷﺪه .ﺑﻌﺪ ﻫﻤﺎن pointerﺑﻪ cast ،ch1ﺷﺪه ﻛﻪ ﭼﻮن ch1ﻳﻚ ﺑﺎﻳﺖ ﺑﻴﺶ ﺗﺮ ﻧﺪارد ﺗﻨﻬﺎ ﺑﺎﻳﺖ اول cدرش ﻗﺮار ﮔﺮﻓﺘﻪ. ﺑﻌﺪ aﺑﻪ cast ،ch2ﺷﺪه .ﺑﺎز ﻫﻢ ﭼﻮن ch2ﻳﻚ ﺑﺎﻳﺖ ﺑﻴﺶ ﺗﺮ ﻧﺪارد ﺗﻨﻬﺎ ﺑﺎﻳﺖ اول aدرش ﻗﺮار ﮔﺮﻓﺘﻪ .در ﭘﺎﻳﺎن ﻣﻘﺪار اﻳﻦ ﻣﺘﻐﻴﺮﻫﺎ ﺑﻪ ﺻﻮرت ﻋﺪدي ﭼﺎپ ﺷﺪهاﻧﺪ .ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻣﻘﺪار ch1ﺑﺎ ch2ﻳﻜﻲ اﺳﺖ .ﻋﻠﺘﺶ اﻳﻦ اﺳﺖ ﻛﻪ ﭼﻬﺎر ﺑﺎﻳﺖ aﻛﺎﻣﻼ ﺑﺎ ﭼﻬﺎر ﺑﺎﻳﺖ cﻳﻚ ﺟﻮر اﺳﺖ )ﮔﺮﭼﻪ ﻧﻮع اﻳﻦ دو ﺗﺎ ﻣﺘﻐﻴﺮ از ﻧﻈﺮ compiler
53
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﺘﻔﺎوت اﺳﺖ( .ﭘﺲ ﺑﺎﻳﺖ اول aو cﻫﻢ ﻳﻚ ﺟﻮر اﺳﺖ .ﺑﺎﻳﺖ اول cدر ch1و ﺑﺎﻳﺖ اول aدر ch2ﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ .ﺑﻨﺎﺑﺮاﻳﻦ ﺑﺎﻳﺪ ﻣﻘﺪار ch1و ch2ﻳﻜﻲ ﺑﺎﺷﺪ ﻛﻪ ﻣﻲﺑﻴﻨﻴﻢ ﻫﺴﺖ. در C++ﻫﻢ ﻣﺜﻞ Cﻧﻴﺎز ﻧﻴﺴﺖ ﺑﻨﻮﻳﺴﻴﻢ: ;= (char) 97
char ch
و ﻛﺎﻓﻲ اﺳﺖ ﺑﻪ ﺟﺎي آن ﺑﻨﻮﻳﺴﻴﻢ: ;char ch = 97
ﮔﺮﭼﻪ ﻓﺮم اول ﻫﻢ درﺳﺖ اﺳﺖ .در واﻗﻊ ﺗﺒﺪﻻت ﺑﻴﻦ charو intو ﺑﺮﺧﻲ اﻧﻮاع دﻳﮕﺮ ﺧﻴﻠﻲ ﺳﺎده و ﺑﻪ ﻃﻮر ﺧﻮدﻛﺎر اﻧﺠﺎم ﻣﻲﺷﻮد .ﺑﻪ ﻣﺜﺎل زﻳﺮ دﻗﺖ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 1025 ;char ch = a ;a = ch ;cout<< a ;)(getch }
Output: 1
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ aدر ﻃﻲ اﻳﻦ ﺗﺒﺪﻳﻞ ﻧﻮعﻫﺎ ﺑﺎﻳ ﺖﻫﺎي ﻣﻔﻴﺪ ﺧﻮد را از دﺳﺖ داده اﺳﺖ .ﻣﻨﻈﻮر ار ﺑﺎﻳﺖ ﻣﻔﻴﺪ ،ﺑﺎﻳﺖ ﻏﻴﺮ ﺻﻔﺮ اﺳﺖ a .اول ﺑﻪ اﻳﻦ ﺷﻜﻞ اﺳﺖ:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1
وﻗﺘﻲ ﺑﻪ cast ،chﺷﺪ ch ،ﺑﻪ اﻳﻦ ﺷﻜﻞ اﺳﺖ: 0 0 0 0 0 0 0 0 0 1
ﺑﻌﺪ chﺑﻪ assign ،aﻣﻲﺷﻮد و aﺑﻪ اﻳﻦ ﺷﻜﻞ در ﻣﻲآﻳﺪ: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
54
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﻪ ﺑﺎﻳﺖ اول آن ﻣﺜﻞ chاﺳﺖ و ﺑﻘﻴﻪ ﺑﺎﻳﺖﻫﺎ ﺻﻔﺮﻧﺪ .ﺗﺒﺪﻳﻞ ﻧﻮﻋﻲ ﻛﻪ در ;a = ch
اﻧﺠﺎم ﻣﻲﺷﻮد ﻣﺨﻔﻲ ) (implicitاﺳﺖ .ﺗﺒﺪﻳﻞ ﻧﻮع در ;a = (int) ch
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ آﺷﻜﺎر ) (explicitاﺳﺖ .ﺑﺮاي ﺗﺒﺪﻳﻞ ﻧﻮع آﺷﻜﺎر ﻣﻲﺗﻮاﻧﻴﻢ از ﻛﻠﻤﻪي ﻛﻠﻴﺪي static_cast
ﻫﻢ اﺳﺘﻔﺎده ﻛﻨﻴﻢ و ﺑﻨﻮﻳﺴﻴﻢ: ;)a = static_cast<int>(ch
ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ را ﺗﻐﻴﻴﺮ دادهام و ﺗﺒﺪﻳﻞ ﻧﻮعﻫﺎ را ﺑﺎ explicit ،static_castﻛﺮدهام: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 1025 ;)char ch = static_cast<char>(a ;)a = static_cast<int>(ch ;cout<< a ;)(getch }
Output: 1
ﻗﺒﻼ ﺑﺎ ﺷﻜﻞ ﭘﻴﭽﻴﺪهي ذﺧﻴﺮهي ﻳﻚ ﻋﺪد اﻋﺸﺎري آﺷﻨﺎ ﺷﺪﻳﺪ .ﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ اﮔﺮ ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع doubleﻳﺎ floatﺑﻪ ﻳﻚ int castﺑﺸﻮد ﻧﺘﻴﺠﻪ ﭼﻴﺴﺖ؟ ﻳﺎ اﮔﺮ floatﺑﻪ double castﺑﺸﻮد؟ وﻗﺘﻲ ﭘﺎي اﻋﺪاد اﻋﺸﺎري در ﻣﻴﺎن ﺑﺎﺷﺪ compilerﻧﻈﺎرت ﺑﻴﺶ ﺗﺮي ﺑﺮ castingاﻧﺠﺎم ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;float fl = 1234.5678 ;int in = fl ;cout<< in << endl ;)(getch }
Output: 1234
55
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اوﻻ ﺗﺒﺪﻳﻞ ﻧﻮع در اﻳﻦ ﺟﺎ ) implicitﭘﻨﻬﺎن( اﺳﺖ .ﻳﻌﻨﻲ ﻋﺪد اﻋﺸﺎري را ﻣﻲﺷﻮد ﺑﻪ ﺷﻜﻞ implicitﺑﻪ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ castﻛﺮد .ﻧﺘﻴﺠﻪي اﻳﻦ castﻛﺮدن ﺣﺬف ﺷﺪن ﻗﺴﻤﺖ اﻋﺸﺎري ﻋﺪد اﺳﺖ .در ﻣﺜﺎل ﺑﺎﻻ ﻋﺪد اﻋﺸﺎري 1234.5678ﺑﻪ ﻳﻚ int castﺷﺪه و ﻧﺘﻴﺠﻪ 1234ﻳﻌﻨﻲ ﻗﺴﻤﺖ ﺻﺤﻴﺢ 1234.5678اﺳﺖ .ﻣﻲداﻧﻴﻢ ﻛﻪ floatو intﻫﺮ دو ﭼﻬﺎرﺑﺎﻳﺘﻲ ﻫﺴﺘﻨﺪ .اﻣﺎ آﻳﺎ در ﻣﺜﺎل ﺑﺎﻻ ﺗﺮﻛﻴﺐ ﺻﻔﺮ و ﻳﻚﻫﺎ در flو inﻳﻜﻲ اﺳﺖ؟ ﻣﺴﻠﻤﺎ اﻳﻦ ﺟﻮري ﻧﻴﺴﺖ ﭼﻮن ﻧﻈﺎرت compilerﺑﺎﻋﺚ ﻣﻲﺷﻮد در casingﺗﺮﻛﻴﺐ ﺻﻔﺮو ﻳﻚﻫﺎ ﻋﻮض ﺑﺸﻮد. compilerﻫﻢ ﭼﻨﻴﻦ ﻧﻈﺎرﺗﻲ را ﻣﻮﻗﻊ castﻛﺮدن * charﺑﻪ intاﻧﺠﺎم ﻧﻤﻲدﻫﺪ .در ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺳﻌﻲ ﻛﺮدهام ﻳﻚ ﺟﻮري ﻧﻈﺎرت compilerﺑﻪ castingاز floatﺑﻪ intرا دور ﺑﺰﻧﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;float fl = 1234.5678 ;int* s = (int*) &fl ;int in = *s ;cout<< in << endl ;)(getch }
Output: 1150964267
اﻳﻦ ﻣﺜﺎل ﻫﻤﺎن ﻣﺜﺎل ﻗﺒﻞ اﺳﺖ ﻓﻘﻂ دﻳﮕﺮ ﻧﻈﺎرﺗﻲ ﺑﺮ ﺗﺒﺪﻳﻞ ﻧﻮع اﻧﺠﺎم ﻧﻤﻲﺷﻮد )ﻳﻌﻨﻲ ﺑﻴﺖﻫﺎ دﺳﺖ ﻧﻤﻲﺧﻮرﻧﺪ( .در اﻳﻦ ﺟﺎ &flﻛﻪ ﻧﻮع * floatدارد ﺑﻪ int* castﺷﺪه *(&fl) .دﻗﻴﻘﺎ )ﻫﻢ از ﻧﻈﺮ ﻣﻜﺎن ﺣﺎﻓﻈﻪ ﻫﻢ ﻣﻘﺪار( ﻫﻤﺎن flاﺳﺖ *s .ﻫﻢ ﻫﻤﺎن flاﺳﺖ ﺑﺎ ﻣﻘﺪار و ﻣﻜﺎن ﻳﻚ ﺟﻮر اﻣﺎ *sاز ﻧﻈﺮ compilerﻧﻮع intدارد ﺑﺮاي ﻫﻤﻴﻦ ﻣﻮﻗﻊ ﭼﺎپ ﺑﻪ ﻋﻨﻮان intﭼﺎپ ﻣﻲﺷﻮد .ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻣﻘﺪار ﭼﺎپ ﺷﺪه ﺑﺎ ﻣﺜﺎل ﻗﺒﻞ ﻓﺮق دارد .اﻳﻦ ﺑﻪ ﺧﺎﻃﺮ ﺣﺬف ﻧﻈﺎرت compilerاﺳﺖ.
ﻧﻮع ﺧﺮوﺟﻲ ﻋﻤﻠﮕﺮي ﻣﺜﻞ ﺗﻘﺴﻴﻢ ) (/ﺑﻪ آرﮔﻮﻣﺎنﻫﺎﻳﺶ ﺑﺴﺘﮕﻲ دارد .اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main {
56
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;double r = 3/2 ;cout<< r ;)(getch }
Output: 1
اﻣﺎ ﺷﺎﻳﺪ اﻧﺘﻈﺎر داﺷﺘﻴﺪ ﺑﻪ ﺟﺎي 1ﻋﺪد 1.5ﭼﺎپ ﺷﻮد .اﻣﺎ 1ﭼﺎپ ﺷﺪه .ﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ ﭼﺮا؟ compilerﻧﻮع 3و ﻧﻮع 2را intﻣﻲداﻧﺪ .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻧﻮع 3/2را ﻫﻢ intﻣﻲﮔﻴﺮد و ﻗﺴﻤﺖ اﻋﺸﺎري ﻧﺎدﻳﺪه ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد و ﻓﻘﻂ 1ﭼﺎپ ﻣﻲﺷﻮد .ﻋﻠﺖ اﻳﻦ اﺳﺖ ﻛﻪ ﻧﻮع ﺧﺮوﺟﻲ ﻋﻤﻠﮕﺮ ﺗﻘﺴﻴﻢ ﻛﺎﻣﻼ ﺑﻪ ﻧﻮع ﻋﺪدﻫﺎﻳﻲ ﻛﻪ ﺑﺮ ﻫﻢ ﺗﻘﺴﻴﻢ ﻣﻲ- ﺷﻮد ﺑﺴﺘﮕﻲ دارد .ﺑﺮاي اﻳﻦ ﻛﻪ ﺧﺮوﺟﻲ 1.5ﺑﺸﻮد ﻣﻲﺗﻮاﻧﻴﻢ ﺑﺮﻧﺎﻣﻪ را ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;double r =((double)3)/2 ;cout<< r ;)(getch }
Output: 1.5
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺟﺎي ;double r =((double)3)/2
ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;double r =(double)3/2
ﻳﺎ ﺑﻨﻮﻳﺴﻴﻢ: ;double r = 3.0/2
ﻳﺎ ﺑﻨﻮﻳﺴﻴﻢ: ;double r = 3/2.0
در ﭘﺎﻳﺎن ﮔﻔﺘﻦ اﻳﻦ ﺿﺮوري اﺳﺖ ﻛﻪ (double)2ﻧﻮع doubleدارد و (char*)2ﻫﻢ ﻧﻮع *char
دارد .ﺑﻪ ﺟﺎي (double)2ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ ).double(2
57
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ unsigned
ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ ﺑﺎ ﻧﻮع intﻣﻲﺗﻮاﻧﺪ ﻣﺜﺒﺖ ﻳﺎ ﻣﻨﻔﻲ ﺑﻪ ﺣﺴﺎب ﺑﻴﺎﻳﺪ اﮔﺮﭼﻪ ﺻﻔﺮ و ﻳﻚ ﺑﻮدن ﺑﻴﺖﻫﺎ ﻣﺜﺒﺖ ﻳﺎ ﻣﻨﻔﻲ ﻧﻤﻲﺷﻨﺎﺳﺪ .وﻗﺘﻲ compilerﻣﻲداﻧﺪ ﻳﻚ ﻣﺘﻐﻴﺮ 32ﺑﻴﺘﻲ ﻧﻮع intدارد ﻣﻮﻗﻊ ﭼﺎپ آن ،ﻳﺎ ﻣﻮﻗﻊ اﻧﺠﺎم ﻛﺎرﻫﺎي دﻳﮕﺮ ﻣﺮﺑﻮط ﺑﻪ آن ،ﻃﻮري ﺑﺮﺧﻮرد ﻣﻲﻛﻨﺪ ﻛﻪ اﻧﮕﺎر ﻋﺪد ﺻﺤﻴﺤﻲ اﺳﺖ ﻛﻪ ﻣﻲﺗﻮاﻧﺪ ﻣﻨﻔﻲ ﻳﺎ ﻣﺜﺒﺖ ﺑﺎﺷﺪ. اﮔﺮ ﺑﺨﻮاﻫﻴﻢ compilerﻫﻤﻮاره آن را ﻋﺪدي ﻣﺜﺒﺖ )در واﻗﻊ ﻧﺎﻣﻨﻔﻲ( ﺑﻪ ﺣﺴﺎب ﺑﻴﺎورد ﺑﺎﻳﺪ آن را ﺑﻪ ﺟﺎي int
ﻳﻚ unsigned intﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;unsigned int a = -1 cout<< a << endl; // 4294967295 cout<< (int) a << endl; // -1 cout<< a + 1; // 0 ;)(_getch }
Output: 4294967295 -1 0
در اﻳﻦ ﺟﺎ ﻣﺘﻐﻴﺮ a ِ unsignedﺑﺎ ﻣﻘﺪار اوﻟﻴﻪي -1ﺑﻪ وﺟﻮد آﻣﺪه .ﺑﺎ coutﭼﺎپ ﺷﺪه .اﻣﺎ ﭼﻮن ،a unsignedاﺳﺖ ،ﺷﻜﻞ ﺑﺪون ﻋﻼﻣﺘﺶ ﻳﻌﻨﻲ 4294967295ﭼﺎپ ﺷﺪه a .را ﺑﻪ cast ،intﻛﺮدهام و آن را ﭼﺎپ ﻛﺮدهام .اﻳﻦ ﺑﺎر ﺷﻜﻞ ﻋﻼﻣﺖ دارش ﭼﺎپ ﺷﺪه a+1 .ﻣﻘﺪار ﺻﻔﺮ دارد ﭼﻮن ﺷﻜﻞ ﭼﺎپ و ﻋﻼﻣﺖ دار ﻳﺎ ﺑﻲ ﻋﻼﻣﺖ ﺑﻮدن ﺑﺮ ﻋﻤﻠﮕﺮﻫﺎﻳﻲ ﻣﺜﻞ ﺟﻤﻊ ﺗﺄﺛﻴﺮي ﻧﺪارد. ﺑﻪ ﺟﺎي unsigned intﻣﻲﺷﻮد ﻧﻮﺷﺖ ) unsignedﺣﺘﻲ در ِ compilerﺳﺨﺖ ﮔﻴﺮي ﻣﺜﻞ Visual .(C++ 2005ﺑﻪ ﻋﻼوه ﺑﻪ ﺟﺎي intﻣﻲﺷﻮد ﻧﻮﺷﺖ signed intﻳﺎ .signedﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻳﻚ warningﻣﻲدﻫﺪ ﻛﻪ ﭼﺮا signedﺑﺎ unsignedﻣﻘﺎﻳﺴﻪ ﺷﺪه اﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;signed a = 2 ;unsigned b = -1
58
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )if(a < b ;"cout<< "a warning ;)(_getch }
Output: a warning
ا +ا / 01را ا* ا .از compilerﻳﻚ warningدرﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﺪ. unsigned charو unsigned __int64ﻫﻢ دارﻳﻢ unsigned char .ﺑﺮاي اﻳﻦ ﺑﻪ ﻛﺎر ﻣﻲرود ﻛﻪ ﻛﺪ ASCIIﻳﻚ ﻛﺎراﻛﺘﺮ از 0ﺗﺎ 255ﺣﺴﺎب ﺷﻮد ﻧﻪ از -127ﺗﺎ .127ﺧﻮد ﻛﻠﻤﻪي unsignedﻳﻌﻨﻲ »ﺑﺪون ﻋﻼﻣﺖ )ﻣﻨﻬﺎ(«.
)(getch
ﻣﻲداﻧﺴﺘﻴﺪ ﻛﻪ ﻛﺎر )( getchﮔﺮﻓﺘﻦ ﻳﻚ ﻛﺎراﻛﺘﺮ اﺳﺖ؟ ﻣﺜﺎل زﻳﺮ ﻛﺎر )( getchرا ﻧﺸﺎن ﻣﻲدﻫﺪ .ﺑﺎ ﻓﺸﺎر ﻳﻚ ﻛﻠﻴﺪ از keyboardﻛﺎراﻛﺘﺮ ﻣﺮﺑﻮط ﺑﻪ آن ﻓﻮرا ﭼﺎپ ﻣﻲﺷﻮد .ﺑﺎ ﻓﺸﺎر دوﺑﺎرهي ﻳﻚ ﻛﻠﻴﺪ اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﭘﺎﻳﺎن ﻣﻲﻳﺎﺑﺪ. >#include <conio.h >#include <iostream ;using namespace std )(int main { ;char ch ;)(ch = getch ;cout<< ch ;)(getch }
)Output: (Shift + b B
)( ِ getchﭘﺎﻳﺎﻧﻲ ﻛﻪ در آﺧﺮ اﻳﻦ ﺑﺮﻧﺎﻣﻪ و ﺑﺮﻧﺎﻣﻪﻫﺎي دﻳﮕﺮ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد ﺑﻪ ﺧﺎﻃﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺮﻧﺎﻣﻪ ﻗﺒﻞ از ﭘﺎﻳﺎن اﺟﺮا ،ﻣﻨﺘﻈﺮ ﻓﺸﺎر دادن ﻳﻚ ﻛﻠﻴﺪ ﺑﻤﺎﻧﺪ ﺗﺎ آن ﭼﻪ ﭼﺎپ ﺷﺪه ﻓﻮرا از دﻳﺪ ﻛﺎرﺑﺮ ﻣﺤﻮ ﻧﺸﻮد. ﺑﻪ ﺟﺎي )( getchﻣﻲﺗﻮاﻧﻴﻢ از )( _getchﻫﻢ اﺳﺘﻔﺎده ﻛﻨﻴﻢ Visual C++ 2005 .ﺣﺘﻲ ﺑﺮاي اﺳﺘﻔﺎده از )( getchﻳﻚ warningﻣﻲﮔﻴﺮد و ﻣﻲﮔﻮﻳﺪ از )( _getchاﺳﺘﻔﺎده ﻛﻨﻴﺪ.
59
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 7ار " %ه و ' &ه
ا 34ا 5 6 ,و .78و ;1!/ / <4!6 ...ا :8/ب =!د identifierﻣﻲﮔﻮﻳﻨﺪ. ) identifierرا»آي دن ﺗﻲ ﻓﺎﻳﺮ« ﻳﺎ ﺗﺨﺼﺼﻲ ﺗﺮ /aI'dentIfaIər/ﺗﻠﻔﻆ ﻛﻨﻴﺪ
( .ﻧﻤﻲﺧﻮاﻫﻢ ﻫﻤﻪي
ﻗﻮاﻧﻴﻦ ﺣﺎﻛﻢ ﺑﺮ اﻧﺘﺨﺎب identifierﻫﺎ را ﺑﮕﻮﻳﻢ )ﻗﻮاﻧﻴﻨﻲ ﻣﺜﻞ اﻳﻦ ﻛﻪ ﻧﺒﺎﻳﺪ ﺑﺎ ﻋﺪد ﺷﺮوع ﺷﻮﻧﺪ ،ﻧﺒﺎﻳﺪ ﺗﻜﺮاري ﺑﺎﺷﻨﺪ ،ﻧﺒﺎﻳﺪ ﻛﻠﻤﻪي ﻛﻠﻴﺪي ﺑﺎﺷﻨﺪ و (...ﭼﻮن اﻳﻦ ﻗﻮاﻧﻴﻦ ﺑﻪ ﻣﺤﺾ ﻧﻘﺾ ﺷﺪن ﺑﻪ وﺳﻴﻠﻪي compilerﺗﺬﻛﺮ داده ﻣﻲﺷﻮﻧﺪ .ﺣﺘﻤﺎ در ﻛﺎر ﺑﺎ compilerﺧﻮد ﻣﺘﻮﺟﻪ ﺷﺪهاﻳﺪ ﻛﻪ C++ﺑﺮﻋﻜﺲ زﺑﺎنﻫﺎﻳﻲ ﻣﺜﻞ case- ،BASIC sensitiveاﺳﺖ ﻳﻌﻨﻲ در C++ identifierﻫﺎي Idو idو IDو iDﻛﺎﻣﻼ ﻣﺘﻔﺎوت ﻫﺴﺘﻨﺪ و ﻣﺜﻼ ﺑﺮ ﻋﻜﺲ ِ If ،ifﻳﻚ ﻛﻠﻤﻪي ﻛﻠﻴﺪي ﻧﻴﺴﺖ. identifierﻫﺎﻳﻲ ﻛﻪ ﺗﺎ ﺑﻪ ﺣﺎل ﻣﻦ در ﻣﺜﺎلﻫﺎ ﺑﻪ ﻛﺎر ﺑﺮد هام ﻳﻚ ﻳﺎ دو ﺣﺮف ﺑﻴﺶ ﺗﺮ ﻧﺪاﺷﺘﻨﺪ .اﻣﺎ ﻳﻚ identifierﻣﻲﺗﻮاﻧﺪ ﺧﻴﻠﻲ ﻃﻮﻻﻧﻲﺗﺮ ﺑﺎﺷﺪ .در ﮔﺬﺷﺘﻪ اﺳﻢﻫﺎي اﻧﺘﺨﺎﺑﻲ ﻛﻮﺗﺎه ﺑﻮدﻧﺪ و ﻣﻌﻨﻲ آنﻫﺎ ﺑﺮاي ﻛﺴﻲ ﻛﻪ ﺑﺎر اول آنﻫﺎ را ﻣﻲدﻳﺪ روﺷﻦ ﻧﺒﻮد ﻣﺜﻞ )(.getch اﺳﻢﻫﺎﻳﻲ ﻛﻪ اﻣﺮوزه ﺑﻪ ﻋﻨﻮان identifierاﻧﺘﺨﺎب ﻣﻲﺷﻮد ﺧﻴﻠﻲ ﻛﺎﻣﻞ ﺗﺮ و اﻏﻠﺐ زﻳﺒﺎ ﺗﺮﻫﺴﺘﻨﺪ ﻣﺜﻞ SetFilePointerﻳﺎ CreateWindowﻳﺎ .is_file_created ﭼﻨﺪ روش ﺑﺮاي اﻧﺘﺨﺎب identifierوﺟﻮد .آنﻫﺎ ﺑﻪ ﺗﺮﺗﻴﺐ زﻳﺒﺎﻳﻲ ﻣﺮﺗﺐ ﻛﺮدهام: (1ﺑﺰرگ ﮔﺮﻓﺘﻦ ﺣﺮف اول ﻛﻠﻤﺎت :ﻣﺜﻞ SetFilePointerﻳﺎ .MinimizeAllWindowsاﻳﻦ روش ﺑﻴﺶ ﺗﺮ در ﻣﻮرد ﺗﺎﺑ ﻊﻫﺎ ﻳﻪ ﻛﺎر ﻣﻲرود. (2ﻧﻤﺎد ﮔﺬاري ﺷﺘﺮي ) :(camel-notationدر اﻳﻦ روش اوﻟﻴﻦ ﻛﻠﻤﻪ ﺑﺎ ﺣﺮفﻫﺎي ﻛﻮﭼﻚ و ﺣﺮف اول ﺑﻘﻴﻪي ﻛﻠﻤﻪﻫﺎ ﺑﺎ ﺣﺮف ﺑﺰرگ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد ﻣﺜﻞ newStringﻳﺎ myCarClassﻳﺎ .itsLengthﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ ﭼﺮا اﺳﻢ اﻳﻦ ﺷﻴﻮه ﻧﻤﺎد ﮔﺬاري را ﺷﺘﺮي ﮔﺬاﺷﺘﻪاﻧﺪ ؟ (3اﺳﺘﻔﺎده از :underscoreاﺳﻢﻫﺎﻳﻲ ﻛﻪ ﺗﻮﺿﻴﺢ ﺑﻴﺶ ﺗﺮ ﻣﻲﺧﻮاﻫﻨﺪ و ﻛﺎرﺑﺮد زﻳﺎدي ﻧﺪارﻧﺪ ﺑﺎ اﻳﻦ روش اﻧﺘﺨﺎب ﻣﻲﺷﻮﻧﺪ .در اﻳﻦ روش ﭼﻨﺪ ﻛﻠﻤﻪ ﻛﻪ ﺑﺎ ﺣﺮوف ﻛﻮﭼﻚ ﻧﻮﺷﺘﻪ ﺷﺪهاﻧﺪ ﺑﺎ ) underscoreﻳﻌﻨﻲ »_«(
ﺑﻪ
ﻫﻢ
وﺻﻞ
ﻣﻲﺷﻮﻧﺪ.
ﻣﺜﻞ
.the_number_of_instances
60
www.pupuol.com
is_the_window_maximized
ﻳﺎ
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (4ﻧﻤﺎدﮔﺬاري ﻣﺠﺎري )ﻣﺠﺎرﺳﺘﺎﻧﻲ( :در ا 01روش 3/د?+ار .78 ،,ه !/ ,ع intﺑﺎ iﺷﺮوع ﻣﻲ- ﺷﻮﻧﺪ ،ﻣﺘﻐﻴﺮﻫﺎي ﺑﺎ ﻧﻮع longﺑﺎ lﺷﺮوع ﻣﻲﺷﻮﻧﺪ و . ...ﻣﺜﻼ ;int ia ;bool bx ;long lr
اﻳﻦ روش ﺑﺮاي زﺑﺎنﻫﺎي Cو C++ﺧﻴﻠﻲ ﺧﻮب ﻧﻴﺴﺖ و ﺑﻬﺘﺮ اﺳﺖ از آن اﺳﺘﻔﺎده ﻧﻜﻨﻴﻢ ﭼﻮن ﻛﺴﻲ ﻛﻪ ﺑﺎ ﺑﺮﻧﺎﻣﻪ ﺷﻤﺎ آﺷﻨﺎﻳﻲ ﻛﻤﻲ دارد ﺑﺎ دﻳﺪن ﭼﻨﺪ ﻣﺘﻐﻴﺮ ﻛﻪ ﻣﺜﻼ ﺑﺎ iﺷﺮوع ﻣﻲﺷﻮﻧﺪ ﮔﻴﺞ ﻣﻲﺷﻮد .در ﻛﻞ اﮔﺮ اول ﻫﻤﻪي اﺳﻢﻫﺎ ﻳﻚ ﺟﻮر ﻧﺒﺎﺷﺪ ﺧﻮاﻧﺪن ﺑﺮﻧﺎﻣﻪ راﺣﺖ ﺗﺮ اﺳﺖ. ﺷﺎﻳﺪ ﻣﻦ در اﻳﻦ ﻧﻮﺷﺘﻪ ﺧﻴﻠﻲ ﺳﺮاغ اﻳﻦ روشﻫﺎ ﻧﺮوم .ﺑﺮﻧﺎﻣﻪﻫﺎي اﻳﻦ ﻧﻮﺷﺘﻪ ﺑﺎ ﻫﺪف آﻣﻮزش زﺑﺎن C++ﻧﻮﺷﺘﻪ ﺷﺪهاﻧﺪ و ﺑﺴﻴﺎر ﻛﻮﺗﺎﻫﻨﺪ و ﺑﺎﻳﺪ ﺑﺴﻴﺎر ﺳﺎده ﺑﺎﺷﻨﺪ ﺗﺎ ﺧﻮاﻧﻨﺪه اﺣﺴﺎس ﭘﻴﭽﻴﺪﮔﻲ ﻧﻜﻨﺪ )ﻳﺎ اﺣﺴﺎس دل ﭘﻴﭽﻪ ،ﺳﺮدرد و ﻣﺎﻧﻨﺪ اﻳﻦﻫﺎ
( .وﻟﻲ ﻧﺪاﺷﺘﻦ روش ﻧﺎﻣﮕﺬاري ﺛﺎﺑﺖ در ﺑﺮﻧﺎﻣﻪﻫﺎي ﻃﻮﻻﻧﻲ ﺑﺪون ﺷﻚ ﻣﺴﺎﻟﻪ ﺳﺎز اﺳﺖ .اﻟﺒﺘﻪ در
ﺑﺮﻧﺎﻣﻪﻫﺎي ﻛﻮﺗﺎه ﻳﺎ درون ﺗﺎﺑﻊﻫﺎي ﻛﻮﺗﺎه ﻳﺎ ﻫﺮﺟﺎ ﻛﻪ ﻣﺘﻐﻴﺮ ﻧﻘﺶ ﻛﻮﺗﺎﻫﻲ دارد اﺳﺘﻔﺎده از ﻣﺘﻐﻴﺮﻫﺎي ﺗﻚ ﺣﺮﻓﻲ ﻣﻨﺎﺳﺐ ﺗﺮ اﺳﺖ. ﻧﻜﺘﻪي آﺧﺮ اﻳﻦ ﻛﻪ ﻣﻌﻤﻮﻻ compilerﻫﺎ ،ﻳﻚ ﺣﺪاﻛﺜﺮ ﻃﻮل ﻣﺆﺛﺮ ﺑﺮاي identifierﻫﺎ دارﻧﺪ .اﮔﺮ اﺳﻤﻲ ﻛﻪ ﺷﻤﺎ اﻧﺘﺨﺎب ﻣﻲﻛﻨﻴﺪ از آن ﺣﺪاﻛﺜﺮ ،ﻃﻮﻻﻧﻲ ﺗﺮ ﺑﺎﺷﺪ compilerﻃﻮل اﺿﺎﻓﻪ را ﻧﺎدﻳﺪه ﻣﻲﮔﻴﺮد ﻣﺜﻼ اﮔﺮ اﻳﻦ ﻃﻮل ﻣﺆﺛﺮ 20ﺑﺎﺷﺪ a0123456789012345678xو a0123456789012345678yاز ﻧﻈﺮ compiler ﻳﻜﻲ ﻫﺴﺘﻨﺪ .ﻧﮕﺮان ﻧﺒﺎﺷﻴﺪ! در compilerﻫﺎي ﺟﺪﻳﺪ اﻳﻦ ﻃﻮل ﻣﺆﺛﺮ در ﺣﺎﻟﺖ ) defaultﻳﻌﻨﻲ دﺳﺖ ﻧﺨﻮرده( ﺑﻴﺶ از 200اﺳﺖ و در ﺿﻤﻦ ﺷﻤﺎ ﻣﻲﺗﻮاﻧﻴﺪ اﻳﻦ ﻃﻮل را ﺑﻪ دﻟﺨﻮاه ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﺪ .ﻓﻜﺮ ﻣﻲﻛﻨﻢ در compilerﻫﺎي ﻗﺪﻳﻤﻲ اﻳﻦ ﻃﻮل ﺑﺎﻳﺪ دﺳﺖ ﻛﻢ 32ﺑﺎﺷﺪ.
" %ه 8
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻣﺘﻐﻴﺮﻫﺎﻳﻲ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﻫﻤﻪي ﺗﺎﺑﻊﻫﺎ ﺑﻪ آنﻫﺎ دﺳﺘﺮﺳﻲ دارﻧﺪ ،ﻣﻲﺗﻮاﻧﻴﻢ آنﻫﺎ را ﺧﺎرج از ﻫﻤﻪي ﺗﺎﺑﻊﻫﺎ و در ﻓﻀﺎي ﻋﻤﻮﻣﻲ ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ .اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std ;int a = 25
61
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int b,v,h,l
;v << h << l <<endl ;endl ;endl ;endl ;endl
)(void f { ;a++ ;b = 2 } )(int main { << cout<< l ;)(f << cout<< a << cout<< b ;a = b = 3 << cout<< a << cout<< b ;)(getch }
Output: 0000 26 2 3 3
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻫﻤﻪي ﺗﺎﺑﻊﻫﺎ ﺑﻪ ﻣﺘﻐﻴﺮﻫﺎي ﻋﻤﻮﻣﻲ دﺳﺘﺮﺳﻲ دارﻧﺪ .اول ،ﺑﺮﻧﺎﻣﻪ ﻣﺘﻐﻴﺮﻫﺎي ﻋﻤﻮﻣﻲ را ﺑﺎ ﻣﻘﺪارﻫﺎي اوﻟﻴﻪ ﺷﺎن ﺑﻪ وﺟﻮد ﻣﻲآورد .ﺑﻌﺪا ِ controlﺑﺮﻧﺎﻣﻪ ﺷﺮوع ﻣﻲﻛﻨﺪ ﺑﻪ اﺟﺮاي دﺳﺘﻮرات درون ﺗﺎﺑﻊ )( mainو ﻳﻜﻲ ﻳﻜﻲ ﺟﻠﻮ ﻣﻲرود .وﻗﺘﻲ ﺑﻪ ;)( fرﺳﻴﺪ دﺳﺘﻮرات درون ﺗﺎﺑﻊ fرا اﺟﺮا ﻣﻲﻛﻨﺪ ﻛﻪ اﻳﻦ ﺑﺎﻋﺚ ﻣﻲﺷﻮد ﻣﻘﺪارﻫﺎي ﻣﺘﻐﻴﺮﻫﺎي ﻋﻤﻮﻣﻲ ﻋﻮض ﺑﺸﻮﻧﺪ .ﺗﺎﺑﻊ mainاﻳﻦ ﻣﻘﺪارﻫﺎ را ﭼﺎپ ﻣﻲﻛﻨﺪ .در mainﺑﺎ دﺳﺘﻮر ; ،a=b=3ﻣﻘﺪار ﻣﺘﻐﻴﺮﻫﺎي aو bﻫﻤﺰﻣﺎن ﺑﻪ 3ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮﻧﺪ و aو bدوﺑﺎره ﭼﺎپ ﻣﻲﺷﻮﻧﺪ .ﻣﻲﺗﻮاﻧﻴﺪ; a=b=3را .*!6 .؟ ﺑﻪ ﻋﻨﻮان راﻫﻨﻤﺎﻳﻲ ﻣﻲﮔﻮﻳﻢ ﻛﻪ ﻋﻤﻠﮕﺮ = درﺳﺖ ﻣﺜﻞ ﻳﻚ ﺗﺎﺑﻊ ﺧﺮوﺟﻲ دارد .ﺧﺮوﺟﻲ b=3ﻫﻢ 3اﺳﺖ. راﻫﻨﻤﺎﻳﻲ دوم اﻳﻦ ﻛﻪ b=3اول اﺟﺮا ﻣﻲﺷﻮد.
ه ++و --
ﻗﺒﻼ ﮔﻔﺘﻢ ﻛﻪ a++ﻳﻚ واﺣﺪ ﺑﻪ aاﺿﺎﻓﻪ ﻣﻲﻛﻨﺪ ++a .ﻫﻢ دارﻳﻢ ﻛﻪ ﻣﺜﻞ a++ﻳﻜﻲ ﺑﻪ aاﺿﺎﻓﻪ ﻣﻲﻛﻨﺪ. ﻋﻤﻠﮕﺮ ++وﻗﺘﻲ ﻗﺒﻞ از ﻣﺘﻐﻴﺮ ﺑﻪ ﻛﺎر ﺑﺮود prefix ،و وﻗﺘﻲ ﺑﻌﺪ از ﻣﺘﻐﻴﺮ ﺑﻪ ﻛﺎر ﻣﻲرود postfixﻧﺎﻣﻴﺪه ﻣﻲﺷﻮد.
62
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ وﻗﺘﻲ ++ﺑﺮاي char ،intو ...ﺑﻪ ﻛﺎر ﻣﻲرود ﺑﻴﻦ postfixو prefixﻓﺮﻗﻲ ﻫﺴﺖ .اﮔﺮ aﺑﺎ ﻧﻮع intﺑﺎﺷﺪ، ﻣﻘﺪار ++aﺑﺮاﺑﺮ ﺑﺎ a+1اﺳﺖ وﻟﻲ ﻣﻘﺪار a++ﺑﺮاﺑﺮ aاﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std )(int main { int ;a = 2 ;double b = 2 2 3 3 3
output: output: output: output:
// // // //
;endl ;endl ;endl ;endl
<< << << <<
cout<< a++ cout<< ++b cout<< a cout<< b ;)(_getch }
Output: 2 3 3 3
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ a++ﻳﻜﻲ ﺑﻪ aو ++bﻳﻜﻲ ﺑﻪ bاﺿﺎﻓﻪ ﻛﺮده .اﻣﺎ a++ﻣﻘﺪار 2و ++bﻣﻘﺪار 3دارد .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﻬﺘﺮ ﺑﺎ ﺗﻔﺎوت ﻋﻤﻠﻜﺮد ++ﺑﻪ ﻋﻨﻮان postfixو prefixآﺷﻨﺎ ﺑﺸﻮﻳﺪ دو ﺗﺎﺑﻊ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ: )int preplus(int& a { ;a = a + 1 ;return a } )int postplus(int& a { ;a = a + 1 ;return a - 1 }
ﺣﺎﻻ ﺑﻪ ﺟﺎي a++ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ ) postplus(aو ﺑﻪ ﺟﺎي ++aﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ ) preplus(aو اﻳﻦﻫﺎ ﺑﺎﻫﻢ ﻓﺮﻗﻲ ﻧﺪارﻧﺪ. ﻣﺸﺎﺑﻪ ﻫﻤﻪي ﭼﻴﺰﻫﺎﻳﻲ ﻛﻪ ﺑﺮاي ++ﮔﻔﺘﻢ ﺑﺮاي --ﻫﻢ درﺳﺘﻨﺪ: >#include <conio.h
63
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <iostream> using namespace std; int main() { int a = 2; int b = 2; cout<< a-cout<< --b cout<< a cout<< b _getch();
<< << << <<
endl; endl; endl; endl;
// // // //
output: output: output: output:
2 1 1 1
}
Output: 2 1 1 1
8 ; <' = # و8 عه
ﺧﻮد ﻣﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ ﻧﻮعﻫﺎي ﺟﺪﻳﺪي ﺗﻌﺮﻳﻒ... وbool ،int وﺟﻮد دارد ﻣﺜﻞC++ ﻋﻼوه ﺑﺮ ﻧﻮعﻫﺎﻳﻲ ﻛﻪ در : اﺳﺖ ﻣﻲﻧﻮﻳﺴﻴﻢint ﺑﺮاي ﺗﻌﺮﻳﻒ ﻳﻚ ﻧﻮع ﺟﺪﻳﺪ ﻛﻪ ﺷﺎﻣﻞ دو.ﻛﻨﻴﻢ struct Point { int x; int y; };
: ﺑﺮﻧﺎﻣﻪي زﻳﺮ روش اﺳﺘﻔﺎده از اﻳﻦ ﻧﻮع را ﻧﺸﺎن ﻣﻲدﻫﺪ. دارﻳﻢPoint ﺣﺎﻻ ﻣﺎ ﻧﻮع ﺟﺪﻳﺪي ﺑﺎ ﻧﺎم #include <conio.h> #include <iostream> using namespace std; struct Point { int x; int y; }; int main() { Point p = {10, 20};
64
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;cout<< p.x << endl ;cout<< p.y << endl ;p.x = 100 ;p.y = 200 ;cout<< p.x << endl ;cout<< p.y ;)(getch }
Output: 10 20 100 200
ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﻮع Pointﺗﻌﺮﻳﻒ ﻛﺮدهام و ﺑﻪ ﻣﺆﻟﻔﻪﻫﺎي آن )ﻳﻌﻨﻲ xو (yﻣﻘﺪار اوﻟﻴﻪ دادهام .اﻳﻦ ﻣﻘﺪارﻫﺎي اوﻟﻴﻪ را ﭼﺎپ ﻛﺮدهام .ﻣﻘﺪار ﻣﺆﻟﻔﻪﻫﺎ را ﻋـﻮض ﻛﺮدهام و دوﺑﺎره آنﻫﺎ را ﭼﺎپ ﻛﺮدهام. ﺷﺎﻳﺪ ﺑﺮاﻳﺘﺎن ﺟﺎﻟﺐ ﺑﺎﺷﺪ ﻛﻪ ﺑﺪاﻧﻴﺪ ﺧﻂ ;}Point p = {10, 20
در زﺑﺎن error ،Cدارد و ﺑﻪ ﺟﺎي آن ﺑﺎﻳﺪ ﻧﻮﺷﺖ: ;}struct Point p = {10, 20
اﻳﻦ ﺷﻜﻞ دوم در C++ﻫﻢ ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ. در C++ﻧﻮعﻫﺎﻳﻲ ﻛﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﺪ ﻣﻲﺗﻮاﻧﺪ ﺷﺎﻣﻞ ﺗﺎﺑﻊ ﻫﻢ ﺑﺎﺷﺪ: >#include <conio.h >#include <iostream ;using namespace std struct Point { ;int x ;int y دادن او[VWX YZار void SetInitialValue(int a, int b)// { ;x = a ;y = b } ;} )(int main { ;}Point p = {10, 20 ;cout<< p.x << endl ;cout<< p.y << endl ;)p.SetInitialValue(100,200 ;cout<< p.x << endl ;cout<< p.y ;)(getch
65
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output: 10 20 100 200
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻳﻚ commentﻓﺎرﺳﻲ در ﺑﺮﻧﺎﻣﻪ ﻫﺴﺖ .در ﺟﺎﻳﻲ ﻛﻪ اﻳﻦ commentﻗﺮار دارد ﺗﺎﺑﻊ SetInitialValueﺗﻌﺮﻳﻒ ﺷﺪه ﻛﻪ ﻛﺎر آن دادن ﻣﻘﺪار ﺑﻪ xو yاﺳﺖ .ﺑﺎ اﺳﺘﻔﺎده از اﻳﻦ ﺗﺎﺑﻊ در main
ﻣﻘﺪار p.xو p.yرا ﻋﻮض ﻛﺮدهام .ﺑﻪ ﻧﺤﻮهي ﺑﻪ ﻛﺎرﮔﻴﺮي ﺗﺎﺑﻊ ﻳﺎد ﺷﺪه ﺧﻮب دﻗﺖ ﻛﻨﻴﺪ.
ز 8 ,و > ا 8
ﮔﻔﺘﻢ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺑﺎﻳﺪ زﻳﺒﺎ و ﺧﻮب ﻧﻮﺷﺘﻪ ﺷﻮد .ﺷﺎﻳﺪ اﺻﺮار ﻣﻦ ﺑﻪ زﻳﺒﺎ ﻧﻮﺷﺘﻦ و ﺧﻮب ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ ﺑﺮاﻳﺘﺎن ﻏﻴﺮ ﻋﺎدي ﺑﺎﺷﺪ .اوﻻ ﺑﮕﻮﻳﻢ ﻣﻨﻈﻮر از ﺑﺮﻧﺎﻣﻪ دو ﭼﻴﺰ اﺳﺖ: (1ﻓﺎﻳﻞ .exeاﺳﺖ ﻛﻪ از compileﺷﺪن ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺑﻪ وﺟﻮد ﻣﻲآﻳﺪ. (2ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻛﻪ ﻫﻤﺎن ﺧﻄﻮط و دﺳﺘﻮرات ﺑﺮﻧﺎﻣﻪ اﺳﺖ و source codeﻧﺎم دارد. ﻓﺎﻳﻞ .exeدر ﻧﻬﺎﻳﺖ در دﺳﺖ ﻛﺴﺎﻧﻲ اﺳﺖ ﻛﻪ از ﺷﻤﺎ و از ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ و از ﺧﻴﻠﻲ ﭼﻴﺰﻫﺎي دﻳﮕﺮ ﺑﻲ اﻃﻼع ﻫﺴﺘﻨﺪ .وﻗﺘﻲ آنﻫﺎ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ را ﻣﻲﺑﻴﻨﻨﺪ )ﺑﺮاي ﺧﺮﻳﺪ ،اﺳﺘﻔﺎده و (...اوﻟﻴﻦ ﭼﻴﺰي ﻛﻪ آنﻫﺎ را ﺑﻪ ﺧﻮدش ﺟﺬ ب ﻣﻲﻛﻨﺪ زﻳﺒﺎﻳﻲ ﻇﺎﻫﺮي ﺑﺮﻧﺎﻣﻪ ﺷﻤﺎﺳﺖ .زﻳﺒﺎﻳﻲ ﻇﺎﻫﺮي ﺑﺎ زﻳﺒﺎﻳﻲ ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ و ﻣﺮﺗﺐ ﻧﻮﺷﺘﻦ source codeﻓﺮق ﻣﻲﻛﻨﺪ وﻟﻲ ﺑﻲ ارﺗﺒﺎط ﺑﺎ آن ﻧﻴﺴﺖ .دوﻣﻴﻦ ﭼﻴﺰي ﻛﻪ ﻛﺎرﺑﺮان ِ ﺑﺮﻧﺎﻣﻪ ،ﺑﻪ آن ﺗﻮﺟﻪ دارﻧﺪ ﻛﺎراﻳﻲ ﺑﻲ ﻧﻘﺺ و راﺣﺖ آن اﺳﺖ و اﻳﻦ ﺑﺎﻋـﺚ ﻣﻲﺷﻮد آنﻫﺎ از ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺧﺴﺘﻪ ﻧﺸﻮﻧﺪ. ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﻪ اﻳﻦ دو ﻫﺪف )ﻳﻌﻨﻲ زﻳﺒﺎﻳﻲ ﻇﺎﻫﺮي و ﻛﺎراﻳﻲ ﺑﻲ ﻧﻘﺺ( ﺑﺮﺳﻴﺪ ﺑﺎﻳﺪ ﺑﻪ زﻳﺒﺎﻳﻲ ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻳﻌﻨﻲ ﻣﺮﺗﺐ ﻧﻮﺷﺘﻦ source codeﺗﻮﺟﻪ ﻛﻨﻴﺪ .وﻗﺘﻲ source codeرا ﻣﺮﺗﺐ و زﻳﺒﺎ ﻣﻲﻧﻮﻳﺴﻴﺪ ﺑﻪ ﻃﻮر ﻗﺎﺑﻞ ﻣﻼﺣﻈﻪ- اي اﺣﺘﻤﺎل ﺧﻄﺎ و ﻧﻘﺺ را ﻛﻢ ﻣﻲﻛﻨﻴﺪ ﭼﻮن در اﻳﻦ ﺣﺎﻟﺖ ﺑﻬﺘﺮ ﺑﻪ ﺳﺎﺧﺘﺎر ﺑﺮﻧﺎﻣﻪ ﺗﺴﻠﻂ دارﻳﺪ .از ﻃﺮف دﻳﮕﺮ زﻳﺒﺎﻳﻲ ﺑﺮﻧﺎﻣﻪ ﺑﺎﻋـﺚ ﻣﻲﺷﻮد ﻛﻪ ﺑﺮﻧﺎﻣﻪ را ﺧﻴﻠﻲ ﺑﻴﺶ ﺗﺮ دوﺳﺖ داﺷﺘﻪ ﺑﺎﺷﻴﺪ .اﮔﺮ ﺧﻮد ﺷﻤﺎ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﺗﺎن را دوﺳﺖ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻴﺪ ﭼﮕﻮﻧﻪ از دﻳﮕﺮان اﻧﺘﻈﺎر ﺧﻮاﻫﻴﺪ داﺷﺖ ﻛﻪ ﺑﻪ آن ﻋﻼﻗﻪ ﭘﻴﺪا ﻛﻨﻨﺪ؟
66
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺷﺎﻳﺪ اﺣﺴﺎس ﻣﻲﻛﻨﻴﺪ زﻳﺒﺎ ﻧﻮﺷﺘﻦ در دوﺳﺖ داﺷﺘﻦ ﺷﻤﺎ ﻧﻘﺸﻲ ﻧﺪارد .ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﺑﻪ اﺣﺘﻤﺎل زﻳﺎد اﺷﺘﺒﺎه ﻣﻲﻛﻨﻴﺪ. آﻳﺎ ﺗﺎ ﺑﻪ ﺣﺎل ﻧﺸﺪه ﻛﻪ ﭼﻨﺪ ﺗﺎ از رﻓﻘﺎي ﺷﻤﺎ ﺑﻪ ﻫﻤﺮاه ﻛﻮدﻛﺎﻧﺸﺎن ﺑﻪ ﺧﺎﻧﻪي ﺷﻤﺎ ﺑﻴﺎﻳﻨﺪ؟ آﻳﺎ ﺷﻤﺎ ﺑﻪ ﻛﻮدﻛﺎن ﺷﻴﺮﻳﻦ و زﻳﺒﺎ ﺑﻴﺶ ﺗﺮ از ﻛﻮدﻛﺎن ﮔﻮﺷﻪ ﮔﻴﺮ و ﻧﺎﻣﺮﺗﺐ ﺗﻮﺟﻪ ﻧﻜﺮدهاﻳﺪ؟ اﮔﺮ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ زﻳﺒﺎ ﺑﺎﺷﺪ ﺑﻪ ﻃﻮر ﻧﺎﺧﻮدآﮔﺎه ﺑﻪ آن ﺑﻴﺶ ﺗﺮ ﺗﻮﺟﻪ ﺧﻮاﻫﻴﺪ ﻛﺮد و ﻧﺘﻴﺠﻪي اﻳﻦ ﺗﻮﺟﻪ ﺷﻤﺎ ﺑﺮﻧﺎﻣﻪاي ﺧﻮب و ﭘﺮﻃﺮﻓﺪار اﺳﺖ .ﭘﺲ ﻣﺎ ﺑﺎﻳﺪ ﺑﻪ ﺧﻮب ،ﻛﺎﻣﻞ و زﻳﺒﺎ ﻧﻮﺷﺘﻦ source codeﺗﻮﺟﻪ زﻳﺎدي داﺷﺘﻪ ﺑﺎﺷﻴﻢ .اﻳﻦ ﺑﻪ ﻣﺎ ﻛﻤﻚ ﻣﻲﻛﻨﺪ ﻛﻪ از ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻟﺬت ﺑﺒﺮﻳﻢ ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻳﻚ ﻧﻘﺎش ﺗﺎﺑﻠﻮﻫﺎي ﺧﻮد را دوﺳﺖ دارد و از ﻧﻘﺎﺷﻲ ﻟﺬت ﻣﻲﺑﺮد. ﺣﺎﻻ ﺑﮕﺬارﻳﺪ ﭼﻨﺪ ﻧﻜﺘﻪ در ﻣﻮرد ﺧﻮب ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ ﺑﮕﻮﻳﻢ: (1ﻫﺮﮔﺎه ﭘﺮاﻧﺘﺰ) brace ،ﻳﻌﻨﻲ آﻛﻼد( و ...را ﺑﺎز ﻛﺮدﻳﺪ اول آن را ﺑﺒﻨﺪﻳﺪ ﺑﻌﺪ ﺑﻴﻦ آنﻫﺎ ﭼﻴﺰي را ﻛﻪ ﻣﻲ- ﺧﻮاﻫﻴﺪ ﺑﻨﻮﻳﺴﻴﺪ .ﺑﻪ ﻣﺮاﺣﻞ ﻧﻮﺷﺘﻦ زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ |if |(if |)(if )|(if )|if(a )|> if(a )|if(a > 2 )if(a > 2
)1 )2 )3 )4 )5 )6 )7 )8
brace (2ﻫﺎ ﺑﺎﻳﺪ ﻛﺎﻣﻼ در ﻳﻚ ﺳﺘﻮن ﻗﺮار ﺑﮕﻴﺮﻧﺪ و آن ﭼﻪ ﺑﻴﻦ آنﻫﺎﺳﺖ ﺑﺎﻳﺪ ﺑﻪ اﻧﺪازهي ﻳﻚ tabﺟﻠﻮ ﺗﺮ ﺑﺎﺷﺪ .ﻣﺜﻞ )void f(int b { ;int a = 2 ;cout<< a + b }
در ﻣﺤﻴﻂ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻳﻚ tabﺑﻪ ﻃﻮر defaultﺑﻪ اﻧﺪازهي ﭼﻬﺎر =) spaceﻓﻀﺎي ﺧﺎﻟﻲ( اﺳﺖ. compilerﻫﺎ ﻣﻌﻤﻮﻻ اﺟﺎزهي ﺗﻐﻴﻴﺮ ﻃﻮل tabرا ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻣﻲدﻫﻨﺪ. (3ﺧﻂ ﺑﻌﺪي ﺑﺮاي دﺳﺘﻮراﺗﻲ ﻣﺜﻞ ifﺑﺎﻳﺪ ﺑﻪ اﻧﺪازهي ﻳﻚ tabﺟﻠﻮﺗﺮ ﺷﺮوع ﺑﺸﻮد ﻣﺜﻞ )if(a > 2 ;a = 2
(4ﮔﺎﻫﻲ ﺑﺮاي زﻳﺒﺎﻳﻲ ﻣﻲﺗﻮاﻧﻴﻢ ﻃﺮحﻫﺎي ﺧﻼﻗﺎﻧﻪاي ﺑﻪ ﻛﺎر ﺑﺒﺮﻳﻢ ﻣﺜﻞ ;= 2
67
www.pupuol.com
limo
int
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;= 4 ;= 8
limousine RedLimousine
char double
(5در دو ﻃﺮف ﻋﻤﻠﮕﺮﻫﺎﻳﻲ ﻣﺜﻞ = + ،و ...ﻳﻚ spaceﻗﺮار دﻫﻴﺪ و آنﻫﺎ را ﻧﭽﺴﺒﺎﻧﻴﺪ: ;int c = a + b
در ﭘﺎﻳﺎن ﻣﻲﮔﻮﻳﻢ ﻛﻪ در ﻣﻮاردي ﺑﻴﻦ ﻧﺤﻮهي ﻧﻮﺷﺘﻦ ﻳﻚ ﭼﻴﺰ ﺑﻴﻦ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲﻫﺎ اﺧﺘﻼف وﺟﻮد دارد ﻣﺜﻼ در ﻣﻮرد ﻧﻜﺘﻪ ) (2در ﺑﺎﻻ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﺒﻴﻨﻴﺪ ﭼﻪ ﻗﺪر اﻳﻦ ﭼﻴﺰﻫﺎ ﺑﺮاي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲﻫﺎي ﺣﺮﻓﻪاي ﻣﻬﻢ اﺳﺖ ﺑﻪ ﻳﻜﻲ از ﺳﺎﻳﺖﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﺷﻠﻮغ در internetﺳﺮي ﺑﺰﻧﻴﺪ و در ﻣﻮرد ﻧﻜﺘﻪي ) (2از آنﻫﺎ ﻧﻈﺮ ﺑﺨﻮاﻫﻴﺪ و ﻣﻨﺘﻈﺮ ﺑﻤﺎﻧﻴﺪ و ﺑﺒﻨﻴﺪ ﻛﻪ ﭼﮕﻮﻧﻪ دهﻫﺎ ﭘﻴﺎم از ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲﻫﺎي ﻣﺨﺘﻠﻒ داده ﻣﻲﺷﻮد ﻛﻪ ﻫﻤﻪ ﺑﺎ ﻫﻢ ﻣﺘﻨﺎﻗﻀﻨﺪ. در اﻳﻦ ﻓﺼﻞ ﺳﻌﻲ ﻛﺮدم ﺗﻘﺮﻳﺒﺎ ﺗﻤﺎم ﻣﻄﺎﻟﺐ ﻣﻬﻢ در C++را ﻣﺮور ﻛﻨﻢ .اﻻن ﺷﻤﺎ در ﺳﻄﺢ ﻣﻨﺎﺳﺒﻲ ﺑﺮاي درك ﺟﺰﺋﻴﺎت ﺑﻴﺶ ﺗﺮ ﻗﺮار دارﻳﺪ .در ﻓﺼﻞﻫﺎي آﻳﻨﺪه ﺑﻌﻀﻲ از ﻣﻄﺎﻟﺐ اﻳﻦ ﻓﺼﻞ ﺗﻜﺮار ﻣﻲﺷﻮﻧﺪ .ﭘﺲ از ﻳﻜﻲ دو روز اﺳﺘﺮاﺣﺖ دوﺑﺎره ﺑﺎ ﻣﻦ ﻫﻤﺮاه ﺑﺸﻮﻳﺪ
.
@? '
(1ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ دو ﻋﺪد ﺻﺤﻴﺢ را از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد و ﻣﻴﺎﻧﮕﻴﻦ آنﻫﺎ را ﭼﺎپ ﻛﻨﺪ. (2ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ رﺷﺘﻪاي ﺑﻪ ﻃﻮل 10ﻛﺎراﻛﺘﺮ را درﻳﺎﻓﺖ ﻛﻨﺪ و ﻃﻮل رﺷﺘﻪ را ﻧﺼﻒ ﻛﻨﺪ و آن را ﭼﺎپ ﻛﻨﺪ. )راﻫﻨﻤﺎﻳﻲ :ﻛﺎراﻛﺘﺮ nullرا در ﻣﻜﺎن ﻣﻨﺎﺳﺒﻲ از رﺷﺘﻪ ﺑﮕﺬارﻳﺪ( (3ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ 10ﺗﺎ ﻋﺪد ﺻﺤﻴﺢ را از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد و در ﻳﻚ آراﻳﻪ ﻗﺮار ﺑﺪﻫﺪ. (4ﺗﺎﺑﻌﻲ ﺑﺎ اﻳﻦ prototype ;)bool f(bool prop1,bool prop2
ﺑﻨﻮﻳﺴﺪ ﻛﻪ وﻗﺘﻲ ﻫﺮ دوي prop1و prop2ﻣﻘﺪار trueداﺷﺘﻪ ﺑﺎﺷﻨﺪ trueﺑﺮﮔﺮداﻧﺪ وﮔﺮﻧﻪ falseﺑﺮﮔﺮداﻧﺪ.
68
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ A# ' B
(1ﻣﻲداﻧﻴﺪ ﻛﻪ 64 __int64ﺑﻴﺖ دارد و 32 intﺑﻴﺖ دارد .ﭘﺲ ﻣﻲﺷﻮد ﻫﺮ ﻋﺪد ﺻﺤﻴﺢ ﺑﺎ ﻧﻮع __int64را ﺑﺎ دو ﻋﺪد ﺻﺤﻴﺢ ﺑﺎ ﻧﻮع intﻣﺸﺨﺺ ﻛﺮد .ﭼﻮن longﻫﻢ 32ﺑﻴﺖ دارد ،ﻫﺮ ﻋﺪد ﺻﺤﻴﺢ ﺑﺎ ﻧﻮع ) long longﻛﻪ ﻫﻤﺎن __int64اﺳﺖ( را ﻫﻢ ﻣﻲﺷﻮد ﺑﺎ دو ﺗﺎ longﺳﺎﺧﺖ. اﻟﻒ( ﺗﺎﺑﻌﻲ ﺑﺎ اﻳﻦ :prototype ;)void separate(long long n, long& part1, long& part2
ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ nرا ﻣﻲﮔﻴﺮد و ﺑﻪ part1و part2ﻣﻲﺷﻜﻨﺪ. )راﻫﻨﻤﺎﻳﻲ :ﺑﻬﺘﺮ اﺳﺖ در ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ ﻫﻤﻪي اﻋﺪاد را ﺑﺎ ﺑﻪ ﻛﺎر ﺑﺮدن unsignedﺑﻲ ﻋﻼﻣﺖ ﻛﻨﻴﺪ(. ب( ﺗﺎﺑﻌﻲ ﺑﺎ اﻳﻦ :prototype ;)long long integrate(long& part1, long& part2
ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻛﺎﻣﻼ ﺑﺮ ﻋﻜﺲ ﺗﺎﺑﻊ ﻗﺒﻠﻲ ﻋﻤﻞ ﻣﻲﻛﻨﺪ. ج( ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻳﻚ ﻋﺪد long longﺑﮕﻴﺮد و ﺑﺎ اﺳﺘﻔﺎده از ﺗﺎﺑﻊﻫﺎي ﺑﺎﻻ آن را ﺑﻪ دو longﺑﺸﻜﻨﺪ و دوﺑﺎره آنﻫﺎ را ﺑﻪ ﻫﻢ ﺑﭽﺴﺒﺎﻧﺪ و ﻧﺘﻴﺠﻪ را ﭼﺎپ ﻛﻨﺪ.
69
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ دوم در اﻳﻦ ﻓﺼﻞ ﻗﺼﺪ دارم ﺷﻤﺎ را ﺑﺎ دﺳﺘﻮرات ﻣﻬﻢ C++آﺷﻨﺎ ﻛﻨﻢ .ﺷﺎﻳﺪ ﻻزم ﺑﺎﺷﺪ ﺗﺄﻛﻴﺪ ﻛﻨﻢ ﻛﻪ ﻣﻦ در اﻳﻦ ﻧﻮﺷﺘﻪ ﺷﻤﺎ را ﺑﺎ زﺑﺎن C++آﺷﻨﺎ ﻣﻲﻛﻨﻢ ﻧﻪ ﺑﺎ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﺣﺮﻓﻪاي .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺧﻮﺑﻲ ﺑﺎﺷﻴﺪ اول ﺑﺎﻳﺪ ﺑﻪ ﺧﻮﺑﻲ ﻳﻚ زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻣﺜﻞ C++را ﻳﺎد ﮔﺮﻓﺘﻪ ﺑﺎﺷﻴﺪ .ﻣﻦ ﻓﻜﺮ ﻣﻲﻛﻨﻢ ﻛﺴﻲ ﻛﻪ اﺳﺘﻌﺪاد ﺧﻮﺑﻲ در ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ دارد وﻗﺘﻲ ﻳﻚ زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ را ﻳﺎد ﮔﺮﻓﺖ ﻛﻢ ﻛﻢ ﺧﻮدش ﻣﻲﺗﻮاﻧﺪ ﺗﻜﻨﻴﻚﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ را ﭘﻴﺪا ﻛﻨﺪ و ﺑﺮﻧﺎﻣﻪﻫﺎﻳﻲ ﭘﻴﭽﻴﺪه ﺑﻨﻮﻳﺴﺪ .ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲﻫﺎ ﻣﺨﺘﺮﻋﺎن ﺗﻨﺒﻠﻲ ﻫﺴﺘﻨﺪ ﻛﻪ ﺑﻪ ﺟﺎي اﻳﻦ ﻛﻪ ﺑﺎ اﺑﺰار ﻓﻴﺰﻳﻜﻲ ﮔﺮان و ﮔﺎه ﺧﻄﺮﻧﺎك وﺳﻴﻠﻪاي ﺑﺴﺎزﻧﺪ ،ﺧﻴﻠﻲ راﺣﺖ ﺳﺎﻋـﺖﻫﺎ روي ﻳﻚ ﺻﻨﺪﻟﻲ ﻧﺮم و راﺣﺖ ﻣﻲﻧﺸﻴﻨﻨﺪ و ﻫﺮ ﭼﻨﺪ دﻗﻴﻘﻪ ﻳﻚ ﺑﺎر ﭼﻨﺪ ﻛﻠﻴﺪ از ﺻﻔﺤﻪ ﻛﻠﻴﺪ ﻛﺎﻣﭙﻴﻮﺗﺮ را ﻓﺸﺎر ﻣﻲدﻫﻨﺪ و ﺑﺪون ﻫﻴﭻ ﻣﺸﻜﻠﻲ ﺑﻌﺪ از ﭼﻨﺪ ﻣﺎه ﺑﺮﻧﺎﻣﻪ- اي ﺗﺎزه ﺗﻮﻟﻴﺪ ﻣﻲﻛﻨﻨﺪ و آن را ﺑﺪون اﻳﻦ ﻛﻪ ﻧﻴﺎز ﺑﻪ ﺳﺎﺧﺖ ﻛﺎرﺧﺎﻧﻪي ﺗﻮﻟﻴﺪ ﺑﺎﺷﺪ ﺑﻪ آﺳﺎﻧﻲ ﺗﻜﺜﻴﺮ ﻣﻲﻛﻨﻨﺪ و ﺑﻪ ﻫﺰاران ﻧﻔﺮ ﻣﻲﻓﺮوﺷﻨﺪ
.
ا اع د "#ره در C++
در C++ﺳﻪ ﺟﻮر دﺳﺘﻮر دارﻳﻢ: (1د456ر Z6د /ﺷﺎﻣﻞ (aﻓﺮاﺧﻮاﻧﻲ ﺗﺎﺑﻊ : )1) f(2 )(2) getch
(bﻓﺮاﺧﻮاﻧﻲ ﻋﻤﻠﮕﺮ : "cout<< "hello 2 + 3 3 == 4 a = 3
)1 )2 )3 )4
(cﻣﻘﺪار : 1) 5 '2) 'a "3) "hello
(dﺗﺮﻛﻴﺐ :ﺗﺮﻛﻴﺐ دﺳﺘﻮرﻫﺎي ﺳﺎدهي ﺑﺎﻻ ﺑﺎ » «,ﻣﺜﻞ "a = 3, b = 4, cout<< "hello
70
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
دﺳﺘﻮر ﺳﺎده ﺷﺎﻣﻞ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ ﺟﺪﻳﺪ ﻧﻲ .C4ﻣﺜﺎل زﻳﺮ اﻧﻮاع دﺳﺘﻮرﻫﺎي ﺳﺎده را در ﺧﻮدش دارد: >#include <conio.h >#include <iostream ;using namespace std
// faraa khaani e amalgar
)(void f { ;"cout<< "hello }
)(int main { 'f(); // faraa khaani e taabe 2; // meqdaar 'a'; //meqdaar "hello"; //megdaar 3 == 4; // faraa khaani e amalgar 3, "bye", f() ,3 + 6; // tarkib f(),getch(); // tarkib }
Output: hellohellohello
در اﻳﻦ ﻣﺜﺎل اﻧﻮاع دﺳﺘﻮرﻫﺎي ﺳﺎده را ﻣﻲﺑﻴﻨﻴﺪ .دﺳﺘﻮر ﺗﺮﻛﻴﺐ را در ﻓﺼﻞ ﻗﺒﻞ ﻣﻌﺮﻓﻲ ﻧﻜﺮده ﺑﻮدم .ﺑﺮاي ﺗﻮﺿﻴﺢ ﺑﻴﺶ ﺗﺮ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﺧﻂ ;)(f(),getch
ﻫﻤﺎن ﻛﺎري را ﻣﻲﻛﻨﺪ ﻛﻪ دو ﺧﻂ ;)(f ;)(getch
ﻣﻲﻛﻨﻨﺪ .ﻳﻌﻨﻲ » «,ﻛﺎر ﺧﺎﺻﻲ اﻧﺠﺎم ﻧﻤﻲدﻫﺪ .ﺧﻂﻫﺎي ;2 ;''a ;""hello ;3 == 4
ﻫﻴﭻ ﻛﺎري ﻧﻤﻲﻛﻨﻨﺪ. (2دﺳﺘﻮر _^]\[ ﺷﺎﻣﻞ (aﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ : 1) int a 2) bool b, prop
71
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (bﺗﻌﺮﻳﻒ و ﻣﻘﺪار اوﻟﻴﻪ دادن : ;1) int a = 2 ;2) bool prop = true
(3دﺳﺘﻮر `aﻛﻪ ﻳﻚ دﺳﺘﻮر ﺧﺎﻟﻲ اﺳﺖ ﻛﻪ ﻫﻴﭻ ﻛﺎري ﻧﻤﻲﻛﻨﺪ: )(int main { ; // tohi }
Output: empty
b4cd (4ﺷﺎﻣﻞ دﺳﺘﻪاي از دﺳﺘﻮرات زﻳﺮ ﻛﻪ ﻫﻤﻪ در ﻳﻚ ﺟﻔﺖ ) braceﻳﻌﻨﻲ }{( ﻗﺮار دارﻧﺪ: (aدﺳﺘﻮرﻫﺎي ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ ﺑﺎ »;« در اﻧﺘﻬﺎ (bدﺳﺘﻮرﻫﺎي ﺳﺎده ﺑﺎ »;« در اﻧﺘﻬﺎ (cدﺳﺘﻮر ﺗﻬﻲ ﺑﺎ »;« (dﺧﻮد ﺑﻠﻮكﻫﺎ ﻣﺜﻞ {
)1
;int a ;"cout<< "hello } {
)2
;int a = 3 { ;"cout<< "hello ;cout<< a } } {
)3
;int a = 3 }
در ﻣﺜﺎل زﻳﺮ از ﺑﻠﻮكﻫﺎ اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ: >#include <conio.h >#include <iostream
72
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;using namespace std )(int main { {
// block ;int a = 2 ;cout<< a
}
// end of block // block // block ;"cout<< "hello\n } // end of block ;"cout<< "bye\n
{
{
} )if(2 > 3 { // block ;"cout<< "a block } // end of block ;)(getch }
Output: 2hello bye
ﺑﺎ اﺳﺘﻔﺎده از اﻳﻦ ﺗﻘﺴﻴﻢ ﺑﻨﺪي دﺳﺘﻮرات ﻣﻲﺷﻮد ﺑﺮاي ﺧﻴﻠﻲ ﭼﻴﺰﻫﺎ در C++ﻓﺮﻣﻮلﻫﺎﻳﻲ ﻧﻮﺷﺖ .ﻣﺜﻼ ﻓﺮﻣﻮل ﺗﺎﺑﻊ )( mainﺑﻪ اﻳﻦ ﺻﻮرت اﺳﺖ: )(int main
][b4cd ﻛﻪ ﺑﻪ ﺟﺎي ] [b4cdﻣﻲﺷﻮد ﻣﺜﻼ اﻳﻦ را ﮔﺬاﺷﺖ: { ;"cout<< "hello ;)(getch }
ﻛﻼ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﺎ اﺳﺘﻔﺎده از ﻛﻠﻤﺎﺗﻲ ﻣﺜﻞ د456ر Z6د ،b4cd ،/ا4i ،g6ع و ...ﺑﺮاي اﺟﺰاي C++ ﻓﺮﻣﻮل ﺑﻨﻮﻳﺴﻴﻢ .ﻣﺜﻼ ﻓﺮﻣﻮل ﺗﻌﺮﻳﻒ ﻳﻚ ﺗﺎﺑﻊ را ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ اﻳﻦ ﺻﻮرت ﺑﻨﻮﻳﺴﻴﻢ: )]ا4i] [g6ع[] , … ,ا4i] [g6ع[( ]ا4i] [g6ع[ ][b4cd
73
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ع4i g6ع ا4i g6ع ا4i g6ا
int f(int a, int b) { cout<< a + b; return 0; }
ﻣﺜﻼ
b4cd
«int n\» « ﻣﻲﻧﻮﻳﺴﻢint ع4i Zd ارopm n\» « ﻳﺎint ع4i Zd ]kl5m n\» ﻣﻦ ﺑﻪ ﺟﺎي ﺑﺎﻻf ﻣﺜﻼ ﻓﺮﻣﻮل ﻓﺮاﺧﻮاﻧﻲ ﺗﺎﺑﻊ.«char n\» « ﻣﻲﻧﻮﻳﺴﻢchar ع4i Zd ]kl5m n\» ﻳﺎ ﻣﺜﻼ ﺑﻪ ﺟﺎي :ﺑﻪ اﻳﻦ ﺻﻮرت اﺳﺖ f(int n\);
:اﻳﻦ ﻳﻌﻨﻲ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ f(2);
دارد ﺧﻮدint ﻧﻮعf() از ﻃﺮﻓﻲ ﭼﻮن ﺧﺮوﺟﻲ ﺗﺎﺑﻊ. اﺳﺖint اﺳﺖ ﻳﻌﻨﻲ ﻣﻘﺪاري ﺑﺎ ﻧﻮعint n\ 2 ﭼﻮن .« اﺳﺖint n\ ،f(int n\) »ﻫﺮ: اﻳﻦ ﺟﻤﻠﻪ ﮔﻴﺞ ﻛﻨﻨﺪه ﺗﺮ اﺳﺖ. اﺳﺖint n\ ﻫﻢf(2) :ﭼﻴﺰﻫﺎي ﺗﺎزهاي ﻛﻪ ﮔﻔﺘﻢ در ﻣﺜﺎل زﻳﺮ ﻫﺴﺖ #include <conio.h> #include <iostream> using namespace std; void f(int& a) { cin>> a; // 25 //clrscr(); } int main() { int a; f(a), cout<< a + 1 << endl; // 26 { cout<< a << endl; int a = 4; cout<< a << endl; } cout<< a; // 25 getch();
// block // 25 // 4 // end of block
}
74
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: 25 26 25 4 25
ﺗﺎﺑﻊ )( clrscrﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ را ﭘﺎك ﻣﻲﻛﻨﺪ وﻟﻲ ﭼﻮن Visual C++ 2005آن را ﻧﻤﻲﺷﻨﺎﺳﺪ آن را comment outﻛﺮدهام .ﻧﻜﺘﻪي ﻣﻬﻢ ﺑﻌﺪي اﻳﻦ اﺳﺖ ﻛﻪ وﻗﺘﻲ ﻳﻚ ﻣﺘﻐﻴﺮ در ﻳﻚ ﺑﻠﻮك ﺗﻌﺮﻳﻒ ﺷﺪ ﺑﺎ ﺧﺮوج از ﺑﻠﻮك اﻋﺘﺒﺎر ﻣﺘﻐﻴﺮ ﺑﻪ ﭘﺎﻳﺎن ﻣﻲرﺳﺪ .اﮔﺮ ﺑﺮاي ﺗﻌﺮﻳﻒ ﻳﻚ ﻣﺘﻐﻴﺮ در ﺑﻠﻮك از ﻧﺎﻣﻲ اﺳﺘﻔﺎده ﻛﻨﻴﻢ ﻛﻪ ﻗﺒﻼ اﺳﺘﻔﺎده ﺷﺪه ،از ﺟﺎﻳﻲ ﻛﻪ ﺗﻌﺮﻳﻒ ﺷﺮوع ﻣﻲﺷﻮد ﺗﺎ ﭘﺎﻳﺎن ﺑﻠﻮك ﻣﺘﻐﻴﺮ ﻗﺒﻠﻲ ﻣﺤﻮ ﻣﻲﺷﻮد و آن ﻧﺎم ﺑﺮاي اﺷﺎره ﺑﻪ ﻣﺘﻐﻴﺮ ﺟﺪﻳﺪ ﺑﻪ ﻛﺎر ﻣﻲرود ﻣﺜﻞ aدر ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ. در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ در ﺧﻂ ;f(a), cout<< a + 1 << endl
از » «,اﺳﺘﻔﺎده ﺷﺪه و { ;cout<< a << endl ;int a = 4 ;cout<< a << endl }
ﻳﻚ ﺑﻠﻮك اﺳﺖ .ﺷﺒﺎﻫﺖ ﺑﻠﻮك و » «,اﻳﻦ اﺳﺖ ﻛﻪ ﻫﺮ دو از ﭼﻨﺪ دﺳﺘﻮر ﻛﻮﭼﻚ ﺗﺮ ﻳﻚ دﺳﺘﻮر ﺑﺰرگ ﺗﺮ ﻣﻲﺳﺎزﻧﺪ.
ه ?8 C
a<0ﻳﻌﻨﻲ aﻛﻢ ﺗﺮ از ﺻﻔﺮ اﺳﺖ .ﺣﺎل اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺑﮕﻮﻳﻴﻢ aﻛﻦ ﺗﺮ از ﺻﻔﺮ ﻧﻴﺴﺖ ﻣﻲﻧﻮﻳﺴﻴﻢ ) .!(a<0اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺑﮕﻮﻳﻴﻢ ﻫﻢ a<0و ﻫﻢ -5<aﻣﻲﻧﻮﻳﺴﻴﻢ !« «(a<0)&&(-5<a).و»&&« ﻫﺮ دو ﻋﻤﻠﮕﺮ ﻣﻨﻄﻘﻲ ﻫﺴﺘﻨﺪ. ﻋﻤﻠﮕﺮﻫﺎي ﻣﻨﻄﻘﻲ ﺑﺮاي ﺗﺮﻛﻴﺐ ﮔﺰارهﻫﺎ و ﺳﺎﺧﺘﻦ ﮔﺰارهﻫﺎي ﭘﻴﭽﻴﺪه ﺗﺮ ﺑﻪ ﻛﺎر ﻣﻲروﻧﺪ .ﭼﻨﺪ ﺗﺎ از آنﻫﺎ در اﻳﻦ ﺟﺪول ﻣﻲﺑﻴﻨﻴﺪ:
75
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ < دل ?8 C و
ع > و8E
ل ا> ا 8
bool
\bool n\ && bool n
&&
)(a > 2)||(a < 10
ﻳﺎ
bool
\bool n\ || bool n
||
)!(a > 0
ﻧﻘﻴﺾ
bool
\!bool n
!
ﻣﺜﺎل )(a > 2)&&(a < 10
)اﻟﺒﺘﻪ ﻋﻤﻠﮕﺮ آﺧﺮي رﺑﻄﻲ ﺑﻪ ﺗﺮﻛﻴﺐ ﻧﺪارد
(
) !(a>0ﻳﻌﻨﻲ » aﺑﺰرگ ﺗﺮ از 0ﻧﻴﺴﺖ« و ﻣﻌﺎدل اﺳﺖ ﺑﺎ a<=0ﭼﻮن وﻗﺘﻲ ﻳﻚ ﻋﺪد ﺑﺰرگ ﺗﺮ از ﺻﻔﺮ ﻧﺒﺎﺷﺪ ﻳﺎ ﺻﻔﺮ اﺳﺖ ﻳﺎ ﻣﻨﻔﻲ a» 1(a>2)&&(a<10) .ﻛﻢ ﺗﺮ از 10و aﺑﺰرگ ﺗﺮ از 2اﺳﺖ«. ) (a>2)||(a<10ﻳﻌﻨﻲ »ﻳﺎ aﻛﻢ ﺗﺮ از 10اﺳﺖ ﻳﺎ aﺑﻴﺶ ﺗﺮ از 2اﺳﺖ« ﻛﻪ اﻟﺒﺘﻪ ﻣﻘﺪار aﻫﺮ ﭼﻪ ﺑﺎﺷﺪ ﮔﺰارهاي درﺳﺖ اﺳﺖ .ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a, b ;cin>> a ;cin>> b )if(a >= 0 && b >= 0 ;"cout<< "both positive or zero ;)bool prop = !(a >= 0 && b >= 0 )if(prop ;"cout<< "at least one negative ;)(getch }
Output: 5 -3 at least one negative
ا / 01دو ﻋﺪد را از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد .اﮔﺮ ﻫﻴﭻ ﻛﺪام ﻣﻨﻔﻲ ﻧﺒﻮدﻧﺪ ﭘﻴﺎم both positive or zeroرا ﭼﺎپ ﻣﻲﻛﻨﺪ وﮔﺮﻧﻪ ﭘﻴﺎم at least one negativeرا ﭼﺎپ ﻣﻲﻛﻨﺪ .ﻣﻦ ﺑﻪ ﻋﻨﻮان ﻛﺎرﺑﺮ اﻋﺪاد 5و -3
را ﺑﻪ ﺑﺮﻧﺎﻣﻪ دادهام و ﭘﻴﺎم دوم ﭼﺎپ ﺷﺪه.
76
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﮔﺮ ﺧﻮب ﺑﺎ ﻣﻨﻄﻖ ،آن ﻃﻮري ﻛﻪ در رﻳﺎﺿﻴﺎت ﺑﻴﺎن ﻣﻲﺷﻮد ،آﺷﻨﺎ ﺑﺎﺷﻴﺪ ﺑﺎ ﻣﻨﻄﻖ C++ﻫﻢ ﺧﻴﻠﻲ راﺣﺖ ﻫﺴﺘﻴﺪ. اﻣﺎ ﭼﻮن ﻓﺮض ﻣﻦ ﺑﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﺷﻤﺎ ﺗﺎزه ﻣﻲﺧﻮاﻫﻴﺪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻳﺎد ﺑﮕﻴﺮﻳﺪ و ﻫﻴﭻ ﺳﺎﺑﻘﻪاي در ﺷﻨﺎﺧﺖ ﻣﻨﻄﻖ ﻧﺪارﻳﺪ ﺑﺎﻳﺪ ﻛﻤﻲ درﺑﺎره ﮔﺰارهﻫﺎي ﻣﻨﻄﻘﻲ ﺗﻮﺿﻴﺢ ﺑﺪﻫﻢ. در زﺑﺎن ﻓﺎرﺳﻲ وﻗﺘﻲ ﻛﺴﻲ ﻣﻲﮔﻮﻳﺪ »ﻳﺎ C++ﻳﺎد ﻣﻲﮔﻴﺮم ﻳﺎ «C#ﻣﺎ اﻳﻦ ﺑﺮداﺷﺖ را ﻣﻲﻛﻨﻴﻢ ﻛﻪ او ﻫﺮ دو را ﻳﺎد ﻧﺨﻮاﻫﺪ ﮔﺮﻓﺖ و ﻓﻘﻂ ﻳﻜﻲ را ﻳﺎد ﻣﻲﮔﻴﺮد .در » C++ﻳﺎ« ﻣﻌﻨﻲ »ﻓﻘﻂ ﻳﻜﻲ« ﻧﺪارد .ﺑﻨﺎﺑﺮاﻳﻦ در C++ ﻋﺒﺎرت ) (0<a)||(0<bﻳﻌﻨﻲ »ﻳﺎ 0<bﻳﺎ 0<aﻳﺎ ﻫﺮ دو«. ﭼﻴﺰ دﻳﮕﺮي ﻛﻪ ﺧﻴﻠﻲﻫﺎ را ﺑﻪ ﺷﻚ ﻣﻲاﻧﺪازد اﻳﻦ اﺳﺖ ﻛﻪ آﻳﺎ) (2 <= 3درﺳﺖ اﺳﺖ ﻳﺎ ﻏﻠﻂ .ﻣﻄﻠﺐ ﺷﻚ ﺑﺮاﻧﮕﻴﺰ اﻳﻦ اﺳﺖ ﻛﻪ ﭼﻮن 2ﺑﺎ 3ﻣﺴﺎوي ﻧﻴﺴﺖ ) (2 <= 3ﻫﻢ درﺳﺖ ﻧﻴﺴﺖ .ﻧﺒﺎﻳﺪ ﭼﻨﻴﻦ ﺑﺮداﺷﺘﻲ ﻛﺮد .در واﻗﻊ )(2<=3ﻣﻌﺎدل اﺳﺖ ﺑﺎ ) .(2<3)||(2==3ﺑﻨﺎﺑﺮ آن ﭼﻴﺰي ﻛﻪ در ﻣﻮرد || ﮔﻔﺘﻢ، )(2<3)||(2==3درﺳﺖ اﺳﺖ )زﻳﺮا 2<3درﺳﺖ اﺳﺖ( .ﭘﺲ ) (2<=3درﺳﺖ اﺳﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﻛﺎﻣﻼ ﻣﻄﻤﺌﻦ ﺑﺸﻮﻳﻢ ﺑﺮﻧﺎﻣﻪي زﻳﺮ را ﻣﻲﻧﻮﻳﺴﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { )if(2 <= 3 ;"cout<< "2<=3 ;)(getch }
Output: 2<=3
اﮔﺮ 2<=3درﺳﺖ ﻧﺒﻮد ﭼﻴﺰي ﭼﺎپ ﻧﻤﻲﺷﺪ. اﮔﺮ ﺑﺎ ﻣﻨﻄﻖ رﻳﺎﺿﻲ آﺷﻨﺎ ﺑﺎﺷﻴﺪ دو ﻋﻤﻠﮕﺮ ﺗﺮﻛﻴﺒﻲ دﻳﮕﺮ ﻫﻢ وﺟﻮد دارد .ﻳﻜﻲ »اﮔﺮ ...آﻧﮕﺎه «...و دﻳﮕﺮي »... ﻣﻌﺎدل اﺳﺖ ﺑﺎ .«...ﻋﻤﻠﮕﺮ »اﮔﺮ ...آﻧﮕﺎه «...در C++وﺟﻮد ﻧﺪارد و ﻛﺎرﺑﺮدي ﻫﻢ ﻧﺪارد و اﮔﺮ ﺑﺨﻮاﻫﻴﻢ آن را ﺑﺴﺎزﻳﻢ ﺑﺎﻳﺪ از ﻣﻌﺎدلﻫﺎي ﻣﻨﻄﻘﻲ آن اﺳﺘﻔﺎده ﻛﻨﻴﻢ. ﻋﻤﻠﮕﺮ » ...ﻣﻌﺎدل اﺳﺖ ﺑﺎ «...ﻫﻢ در C++ﻧﻴﺴﺖ .اﻣﺎ ﺑﮕﺬارﻳﺪ در ﻣﻮرد آن ﻛﻤﻲ ﺣﺮف ﺑﺰﻧﻢ .ﻣﻲﮔﻮﻳﻴﻢ دو ﺗﺎ ﮔﺰاره ﻣﺜﻞ pو qﻣﻌﺎدﻟﻨﺪ اﮔﺮ ﻳﺎ ﻫﺮ دو درﺳﺖ ﺑﺎﺷﻨﺪ ﻳﺎ ﻫﺮ دو ﻏﻠﻂ ﺑﺎﺷﻨﺪ .ﻣﺜﻼ 2<3و 4<5ﻫﺮ دو درﺳﺘﻨﺪ و ﺑﻨﺎﺑﺮاﻳﻦ ﻣﻌﺎﻟﻨﺪ 10<10 .و 100<20ﻫﺮ دو ﻏﻠﻄﻨﺪ ﭘﺲ ﻣﻌﺎدﻟﻨﺪ .وﻗﺘﻲ دو ﺗﺎ ﮔﺰاره ﻣﻌﺎدل ﺑﺎﺷﻨﺪ در دﺳﺘﻮر if
ﻓﺮﻗﻲ ﻧﺪارد ﻛﺪام را اﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ .ﻳﻌﻨﻲ )if(2 < 3
77
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;"cout<< "hello
ﻫﻤﺎن ﻛﺎري را ﻣﻲﻛﻨﺪ ﻛﻪ )if(3 < 4 ;"cout<< "hello
ﻣﻲﻛﻨﺪ ﭼﻮن 2<3و 3<4ﻣﻌﺎدﻟﻨﺪ. اﮔﺮ ﮔﺰارهﻫﺎ ﻧﻮع boolداﺷﺘﻪ ﺑﺎﺷﻨﺪ ...» ،ﻣﻌﺎدل اﺳﺖ ﺑﺎ «...ﻫﻤﺎن ﺗﺴﺎوي اﺳﺖ و ﻫﺮ ﺟﺎ ﻻزم ﺑﻮد از ﺗﺴﺎوي اﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ وﻟﻲ اﻳﻦ ﺟﺎ ﻳﻚ ﻣﺸﻜﻠﻲ ﻫﺴﺖ و آن اﻳﻦ ﻛﻪ ﻫﻤﻪي ﮔﺰارهﻫﺎ ﻧﻮع boolﻧﺪارﻧﺪ در واﻗﻊ ﮔﺰرهﻫﺎ ﻣﻲﺗﻮاﻧﻨﺪ ﻫﺮ ﻧﻮﻋﻲ داﺷﺘﻪ ﺑﺎﺷﻨﺪ ﻏﻴﺮ از ﻧﻮعﻫﺎﻳﻲ ﻛﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺧﻮدش ﻣﻲﺳﺎزد )ﻣﺜﻼ ﺑﺎ .(structﻳﻚ ﻣﻘﺪار ﺑﺎ ﻫﺮ ﻧﻮﻋﻲ اﮔﺮ ﻏﻴﺮ ﺻﻔﺮ ﺑﺎﺷﺪ ﮔﺰارهاي درﺳﺖ ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪ و اﮔﺮ ﺻﻔﺮ ﺑﺎﺷﺪ ﮔﺰارهاي ﻏﻠﻂ ﺑﻪ ﺣﺴﺎب ﻣﻲ- آﻳﺪ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { )"if("hello ;"cout<<"hello\n ;double a = 0.141592653 )if(a ;"cout<<"double\n )if(0 ;"cout<<"zero ;)(getch }
Output: hello double
" char* n\ "helloاﺳﺖ و ﻣﻘﺪارش وﻗﺘﻲ ﺑﻪ ﻋﻨﻮان ﻋﺪد در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻏﻴﺮ ﺻﻔﺮ اﺳﺖ .ﺑﻨﺎﺑﺮاﻳﻦ ﻳﻚ ﮔﺰارهي درﺳﺖ ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪ .ﻫﻤﻴﻦ ﻃﻮر در ﻣﻮرد .a ﻫﺮ ﻋﺪد ﻏﻴﺮ ﺻﻔﺮ ﻳﻚ ﮔﺰاره درﺳﺖ ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪ ﭘﺲ ﻣﻤﻜﻦ اﺳﺖ دو ﺗﺎ ﮔﺰارهي درﺳﺖ ﻧﺎﻣﺴﺎوي ﺑﺎﺷﻨﺪ. ﻣﺜﻼ ﻋﺪد 2ﻳﻚ ﮔﺰارهي درﺳﺖ اﺳﺖ ،ﻋﺪد 3ﻫﻢ ﻳﻚ ﮔﺰارهي درﺳﺖ اﺳﺖ ﺑﻨﺎﺑﺮاﻳﻦ 2و 3ﻣﻌﺎدﻟﻨﺪ وﻟﻲ ﻣﺴﺎوي ﻧﻴﺴﺘﻨﺪ .ﭘﺲ ﺑﻬﺘﺮ اﺳﺖ ﺑﺮاي ﻣﻌﺎدل ﺑﻮدن ﮔﺰارهﻫﺎي pو qﺑﻨﻮﻳﺴﻴﻢ .!p==!q ﺑﺮاي اﻳﻦ ﻛﻪ ﺛﺎﺑﺖ ﻛﻨﻴﻢ !p==!qﻣﻌﺎدل ﺑﻮدن ﺑﻮدن را ﻧﺸﺎن ﻣﻲدﻫﺪ ﺑﺎﻳﺪ ﺛﺎﺑﺖ ﻛﻨﻴﻢ !p==!qﻓﻘﻂ وﻗﺘﻲ درﺳﺖ اﺳﺖ ﻛﻪ pو qﻣﻌﺎدل ﺑﺎﺷﻨﺪ ﺑﺮاي اﻳﻦ ﻛﺎر ﺳﻪ ﺣﺎﻟﺖ را در ﻧﻈﺮ ﻣﻲﮔﻴﺮﻳﻢ:
78
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ p (aو qﻫﺮدو درﺳﺖ ﺑﺎﺷﻨﺪ: اﮔﺮ ﻣﻘﺪار 2 ،pﺑﺎﺷﺪ و ﻣﻘﺪار 1 ،qﺑﺎﺷﺪ ﻫﺮ دو درﺳﺘﻨﺪ و در ﻧﺘﻴﺠﻪ ﻣﻌﺎدﻟﻨﺪ !p==!q ،ﻫﻢ ﺑﻪ ﺷﻜﻞ 0==0در ﻣﻲآﻳﺪ ﻛﻪ درﺳﺖ اﺳﺖ و ﻣﻌﺎدل ﺑﻮدن را ﻧﺸﺎن ﻣﻲدﻫﺪ. p (bو qﻫﺮ دو ﻧﺎدرﺳﺖ ﺑﺎﺷﻨﺪ: اﮔﺮ ﻣﻘﺪارﻫﺎي pو qﻫﺮ دو ﺻﻔﺮ ﺑﺎﺷﻨﺪ ﻓﺮﻣﻮل !p==!qﺑﻪ ﺷﻜﻞ 1==1در ﻣﻲآﻳﺪ ﻛﻪ ﺑﺎز ﻣﻌﺎدل ﺑﻮدن را ﻧﺸﺎن ﻣﻲدﻫﺪ. (cﻳﻜﻲ از pو qﻏﻠﻂ و دﻳﮕﺮي درﺳﺖ ﺑﺎﺷﺪ: اﮔﺮ ﻣﻘﺪار 2 ،pﺑﺎﺷﺪ و ﻣﻘﺪار 0 ،qﺑﺎﺷﺪ p ،و qﻣﻌﺎدل ﻧﻴﺴﺘﻨﺪ !p==!q .ﻫﻢ ﺑﻪ ﺷﻜﻞ 0==1در ﻣﻲ- آﻳﺪ ﻛﻪ ﻧﺎدرﺳﺖ اﺳﺖ و ﻣﻌﺎدل ﻧﺒﻮدن را ﻧﺸﺎن ﻣﻲدﻫﺪ. در ﮔﺬﺷﺘﻪ ﺑﻪ ﺟﺎي boolاز intاﺳﺘﻔﺎده ﻣﻲﺷﺪ .ﻫﻨﻮز ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ از intﺑﻪ ﺟﺎي boolاﺳﺘﻔﺎده ﻛﻨﻴﻢ و ﮔﺎﻫﻲ ﺑﻪ ﺧﺎﻃﺮ وﺟﻮد ﺗﺎﺑﻊﻫﺎي ﻗﺪﻳﻤﻲ ﭼﺎرهاي ﺑﻪ ﺟﺰ اﻳﻦ ﻧﻴﺴﺖ .وﻗﺘﻲ ﻳﻚ intﻏﻴﺮ ﺻﻔﺮ ﺑﺎﺷﺪ درﺳﺖ ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪ و اﮔﺮ ﺻﻔﺮ ﺑﺎﺷﺪ ﻏﻠﻂ ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪ .در ﻣﺜﺎل زﻳﺮ ﺑﻪ ﺟﺎي boolاز intاﺳﺘﻔﺎده ﻣﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;)int prop = (2 <= 3 ;cout<< prop << endl )if(prop ;cout<< "prop is true"<< endl ;int w = -5 )if(w && prop ;"cout<< "w and prop are true ;)(getch }
Output: 1 prop is true w and prop are true
در ﻛﺎر ﺑﺎ رﺷﺘﻪﻫﺎ ﮔﺎﻫﻲ از \ char nﺑﻪ ﺟﺎي ﮔﺰاره اﺳﺘﻔﺎده ﻣﻲﺷﻮد.
79
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﻦ در compilerﺷﺮﻛﺖ Microsoftﻧﺘﻮاﻧﺴﺘﻢ ﺑﻪ \ bool nﻏﻴﺮ از 0و 1ﻣﻘﺪار دﻳﮕﺮي ﺑﺪﻫﻢ .اﻣﺎ در BDS 2006ﻛﻪ ﻣﺤﺼﻮل ﺷﺮﻛﺖ Borlandاﺳﺖ ﺑﺎ اﺳﺘﻔﺎده از ﻋﻤﻠﮕﺮ ++اﻳﻦ ﻛﺎر را ﻛﺮدم .اﮔﺮ ﺑﭙﺬﻳﺮﻳﻢ ﻛﻪ ﻳﻚ boolﻏﻴﺮ از 0و 1ﻣﻘﺪار دﻳﮕﺮي ﻧﺪارد ﻣﻲﺗﻮاﻧﻴﻢ ﺑﺮاي ﻣﻌﺎدل ﺑﻮدن boolﻫﺎي pو qﺑﻨﻮﻳﺴﻴﻢ .p==q ﻣﻦ ﻓﻜﺮ ﻣﻲﻛﻨﻢ اﻳﻦ ﺑﺮاي Borlandﻳﻚ ﻧﻘﺺ اﺳﺖ ﻛﻪ اﺟﺎزه ﻣﻲدﻫﺪ ﻛﻪ \ bool nﻣﻘﺪاري ﻏﻴﺮ از 0و 1
داﺷﺘﻪ ﺑﺎﺷﺪ .در ﻫﺮ ﺣﺎل اﺣﺘﻴﺎط در اﻳﻦ ﻻزم اﺳﺖ ﻛﻪ دو ﮔﺰارهي درﺳﺖ ﻫﻤﻴﺸﻪ ﻣﺴﺎوي ﻧﻴﺴﺘﻨﺪ. ﺣﺎﻻ ﺑﮕﺬارﻳﺪ ﺗﻌﺪادي از ﻣﻌﺎدلﻫﺎي ﻣﻨﻄﻘﻲ ﻣﻬﻢ را ﺑﻴﺎن ﻛﻨﻢ .ﻓﺮض ﻛﻨﻴﺪ pو qو rﮔﺰارهﻫﺎﻳﻲ ﻛﺎﻣﻼ ﺛﺎﺑﺖ ﻳﺎ دﺳﺖ ﻛﻢ ﻏﻴﺮ واﺑﺴﺘﻪ ﺑﺎﺷﻨﺪ .ﻣﻌﺎدلﻫﺎي زﻳﺮ را دارﻳﻢ: ﻣﻌﺎدل p p !q !q )(p && r
&& || || && ||
ﻣﻌﺎدل p && q p || q )!(p && q )!(p || q )p && (q || r
q q !p !p )(p && q
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﺑﻪ ﺟﺎي ) !(a > 0 && b > 0ﻣﻲﺷﻮد ﻧﻮﺷﺖ ) !(a > 0) || !(b > 0و ﺑﻪ ﺟﺎي اﻳﻦ ﻫﻢ ﻣﻲﺷﻮد ﻧﻮﺷﺖ .a <= 0 || b <= 0 دو ﻣﺜﺎل ﺑﻌﺪي ﺷﺎﻳﺪ ﺑﻴﺶ از ﺣﺪ ﺗﺨﺼﺼﻲ ﺑﺎﺷﻨﺪ وﻟﻲ ﻣﻨﻈﻮر ﻣﻦ را از ﻏﻴﺮ واﺑﺴﺘﻪ ﺑﻮدن ﮔﺰارهﻫﺎ ﻛﻪ ﭘﻴﺶ از ﺟﺪول ﺑﺎﻻ ﺑﻪ آن اﺷﺎره ﻛﺮدم ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ .ﻓﺮق اﻳﻦ دو ﻣﺜﺎل اﻳﻦ اﺳﺖ ﻛﻪ در ﻳﻜﻲ ;)(bool prop = p() || q
ﻧﻮﺷﺘﻪام و در دﻳﮕﺮي ;)(bool prop = q() || p
ﻣﺜﺎل اول: >#include <conio.h >#include <iostream ;using namespace std ;bool a = true )(bool p { ;return a } )(bool q { ;a = false
80
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ return a; } int main() { bool prop = p() || q(); if(prop == true) cout<< "it is true"; if(prop == false) cout<< "it is false"; getch(); }
Output: it is true
:ﻣﺜﺎل دوم #include <conio.h> #include <iostream> using namespace std; bool a = true; bool p() { return a; } bool q() { a = false; return a; } int main() { bool prop = q() || p(); if(prop == true) cout<< "it is true"; if(prop == false) cout<< "it is false"; getch(); }
Output: it is false
اﺛﺮ ﻣﻲﮔﺬارد و در ﻧﺘﻴﺠﻪp() رويq() ﻣﻌﺎدل ﻧﻴﺴﺖ ﭼﻮن اﺟﺮايp()||q() ﺑﺎq()||p() ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ . اﺟﺮا ﻣﻲﺷﻮدp() و ﺑﻌﺪq() اولq()||p() ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در.اﻳﻦ دو ﮔﺰاره ﻣﺴﺘﻘﻞ ﻧﻴﺴﺘﻨﺪ : ﺑﻪ اﻳﻦ ﻓﺮﻣﻮل ﻧﮕﺎه ﻛﻨﻴﺪ.در اداﻣﻪي اﻳﻦ ﺑﺨﺶ اﺷﺎرهاي ﻫﻢ ﺑﻪ اوﻟﻮﻳﺖ ﻋﻤﻠﮕﺮﻫﺎ ﻣﻲﻛﻨﻢ p || !q && r
اﻳﻦ ﻓﺮﻣﻮل ﺑﻪ ﻋﻨﻮان ﮔﺰاره ﺑﺎ ﻛﺪام ﻳﻚ از ﻓﺮﻣﻮل زﻳﺮ ﻣﻌﺎدل اﺳﺖ؟
81
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (p || (!q)) && r )p || ((!q) && r ))p || (!(q && r
ﺑﺮاي ﻣﻌﻠﻮم ﺷﺪن اﻳﻦ ،ﺑﺎﻳﺪ اوﻟﻮﻳﺖ ) (precedenceاﺛﺮ ﻋﻤﻠﮕﺮﻫﺎي && || ،و ! ﻣﺸﺨﺺ ﺑﺎﺷﺪ .ﺑﻪ اﻳﻦ ﺟﺪول ﻧﮕﺎه ﻛﻨﻴﺪ: ﺗﻮﺿﻴﺤﺎت
ﻋﻤﻠﮕﺮ
ﻋﻤﻠﮕﺮ * ﺑﻪ ﻋﻨﻮان indirection
! *
ﻋﻤﻠﮕﺮ * ﺑﻪ ﻋﻨﻮان ﺿﺮب
* + -
>> <<
ﻣﺜﻼ در <<cout
< > =< =>
== &&
|| در ﻳﻚ ﻓﺮﻣﻮل اول ﺑﺎﻳﺪ ﺑﻪ ﺳﺮاغ ﻋﻤﻠﮕﺮﻫﺎي ﺑﺎ اوﻟﻮﻳﺖ ﺑﺎﻻﺗﺮ ﺑﺮوﻳﻢ .اﻳﻦ ﺟﺪول اوﻟﻮﻳﺖﻫﺎ را ﻧﺸﺎن ﻣﻲدﻫﺪ. ﻋﻤﻠﮕﺮﻫﺎﻳﻲ ﻛﻪ ﺑﺎﻻﺗﺮ ﻗﺮار دارﻧﺪ اوﻟﻮﻳﺖ ﺑﻴﺶ ﺗﺮي دارﻧﺪ .ﻋﻤﻠﮕﺮﻫﺎﻳﻲ ﻛﻪ در ﻳﻚ ﻣﺴﺘﻄﻴﻞ ﻫﺴﺘﻨﺪ اوﻟﻮﻳﺖ ﻳﻜﺴﺎن دارﻧﺪ و اﻳﻦ ﻳﻌﻨﻲ اوﻟﻮﻳﺖ آنﻫﺎ از ﭼﭗ ﺑﻪ راﺳﺖ ﺑﻪ ﺗﺮﺗﻴﺐ ﻇﺎﻫﺮ ﺷﺪن اﺳﺖ .ﺟﺪول ِ ﺧﻴﻠﻲ ﻛﺎﻣﻞ ﺗﺮي را ﻣﻲﺗﻮاﻧﻴﺪ در compiler ِ helpﺧﻮدﺗﺎن ﭘﻴﺪا ﻛﻨﻴﺪ .در ﺑﺎﻻ ،ﻣﻨﻈﻮر از indirectionدﺳﺘﻴﺎﺑﻲ ﺑﻪ ﺣﺎﻓﻈﻪ از ﻃﺮﻳﻖ pointer )اﺷﺎره ﮔﺮ( اﺳﺖ ﻣﺜﻼ *ي ﻗﺮﻣﺰ رﻧﮓ در ;int* p = new int ;*p = 4
ﭼﻮن اوﻟﻮﻳﺖ ! ﺑﻴﺶ ﺗﺮ از && اﺳﺖ ،در p || !q && r
اول ﺑﻪ ﺳﺮاغ ! ﻣﻲروﻳﻢ و اﻳﻦ ﻓﺮﻣﻮل ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮد ﺑﻪ
82
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ p || (!q) && r
ﺑﻌﺪ ﺑﻪ ﺳﺮاغ && ﻣﻲروﻳﻢ و ﺑﻪ )p || ((!q) && r
ﻣﻲرﺳﻴﻢ. در false || true && false && true
اوﻟﻮﻳﺖ ﺑﺎ &&ي اﺳﺖ ﻛﻪ در ﭼﭗ ﻗﺮار دارد: false || (true && false) && true
ﺑﻌﺪ ،آن ﭼﻪ ﻛﻪ داﺧﻞ ﭘﺮاﻧﺘﺰ ﻗﺮار دارد ﺑﻪ ﻋﻨﻮان ﻳﻚ واﺣﺪ ﻣﻲﮔﻴﺮﻳﻢ )اﻧﮕﺎر اﺻﻼ ﻫﻴﭻ ﻋﻤﻠﮕﺮي در آن ﻧﺒﺎﺷﺪ( و ﺑﺎز اوﻟﻮﻳﺖ را ﺑﻪ && ﻣﻲدﻫﻴﻢ: )false || ((true && false) && true
ﺣﺎﻻ ﻣﻲﺗﻮاﻧﻴﺪ ﺣﺴﺎب ﻛﻨﻴﺪ ﻣﻘﺪار اﻳﻦ ﻋﺒﺎرت ﭼﻴﺴﺖ؟ trueﻳﺎ false؟ ﭼﻴﺰ دﻳﮕﺮي ﻛﻪ ﺑﺎﻳﺪ ﺑﻪ آن ﺗﻮﺟﻪ ﻛﺮد ﭘﺮاﻧﺘﺰﻫﺎﺳﺖ .ﺑﺎﻳﺪ ﻣﻘﺪار دروﻧﻲ ﺗﺮﻳﻦ ﭘﺮاﻧﺘﺰ اول ﺣﺴﺎب ﺑﺸﻮد و ﺑﻌﺪ ﻣﻘﺪار ﭘﺮاﻧﺘﺰﻫﺎي ﺑﻴﺮوﻧﻲ ﺗﺮ ﺣﺴﺎب ﺑﺸﻮﻧﺪ .ﻣﺜﻼ در ))p && (q || (r || s || t
اول ) (r || s || tﺳﭙﺲ )) (q || (r || s || tو در ﻧﻬﺎﻳﺖ ﻛﻞ ﻓﺮﻣﻮل ﺑﺎﻳﺪ ﺣﺴﺎب ﺷﻮد. ﺣﺎل ﺑﺮاي اﻣﺘﺤﺎن آن ﭼﻪ در ﻣﻮرد اوﻟﻮﻳﺖ ﮔﻔﺘﻪ ﺷﺪ ﻧﮕﺎﻫﻲ ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺑﻴﻨﺪازﻳﺪ: >#include <conio.h >#include <iostream ;using namespace std
;) << endl ;) << endl ;) << endl
; true ;false ;false p || q && r )p || (q && r (p || q) && r
)(int main { = bool p = bool q = bool r (<<cout (<<cout (<<cout ;)(getch }
Output: 1 1 0
83
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﭘﺮاﻧﺘﺰ ِ ﺑﻌﺪ از << coutﻻزم اﺳﺖ ﭼﻮن << اوﻟﻮﻳﺖ ﺑﺎﻻﺗﺮ دارد .اﻳﻦ ﻣﺜﺎل اﻫﻤﻴﺖ ﭘﺮاﻧﺘﺰﻫﺎ را روﺷﻦ ﻣﻲﻛﻨﺪ. ﮔﺮﭼﻪ ﺟﺪوﻟﻲ ﻣﺜﻞ ﺟﺪول ﺑﺎﻻ اوﻟﻮﻳﺖﻫﺎ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ اﻣﺎ ﺑﻬﺘﺮ اﺳﺖ ﻫﺮ ﺟﺎ ﻣﻤﻜﻦ اﺳﺖ اﺑﻬﺎﻣﻲ ﭘﻴﺶ ﺑﻴﺎﻳﺪ )ﻳﺎ ﺣﺘﻲ اﮔﺮ اﺑﻬﺎﻣﻲ ﭘﻴﺶ ﻧﻴﺎﻳﺪ( از ﭘﺮاﻧﺘﺰﻫﺎ اﺳﺘﻔﺎده ﻛﻨﻴﻢ .اﻳﻦ اﺣﺘﻤﺎل ﺧﻄﺎ را ﻛﻢ و ﺧﻮاﻧﺎ ﺑﻮدن ﺑﺮﻧﺎﻣﻪ را زﻳﺎد ﻣﻲﻛﻨﺪ. اﮔﺮ ﻣﻄﻤﺌﻦ ﺑﺎﺷﻴﻢ ﻛﻪ \ bool nﻣﻘﺪاري ﻏﻴﺮ از ﺻﻔﺮ و ﻳﻚ ﻧﺪارد ﺑﻪ ﺟﺎي p||qﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ .p+qﺑﻪ ﺟﺎي p&&qﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ ) p*qﻳﻌﻨﻲ ﺣﺎﺻﻞ ﺿﺮب ﻣﻘﺪارﻫﺎي pو .(qوﻟﻲ اﻳﻦ ﻛﺎر اﺻﻼ ﺗﻮﺻﻴﻪ ﻧﻤﻲ- ﺷﻮد ﭼﻮن ﻫﻢ Borlandو ﻫﻢ Microsoftﮔﺰارهﻫﺎﻳﻲ دارﻧﺪ ﻛﻪ ﻣﻤﻜﻦ اﺳﺖ ﻣﻘﺪارﻫﺎي ﻏﻴﺮ از 0و 1داﺷﺘﻪ ﺑﺎﺷﻨﺪ. اﮔﺮ ﺑﺨﻮاﻫﻢ ﻣﻲﺗﻮاﻧﻢ ﺣﺘﻲ 300ﺻﻔﺤﻪي دﻳﮕﺮ درﺑﺎرهي ﮔﺰارهﻫﺎ و وﻳﮋﮔﻲﻫﺎﻳﻲ ﭘﻴﭽﻴﺪهي آنﻫﺎ ﺑﻨﻮﻳﺴﻢ .وﻟﻲ در اﻳﻦ ﻧﻮﺷﺘﻪ ﻗﺼﺪ ﺗﻤﺮﻛﺰ ﺑﻴﺶ از ﺣﺪ روي ﻫﻴﭻ ﭼﻴﺰ را ﻧﺪارم و ﻓﻘﻂ ﺳﻌﻲ دارم ﺷﻤﺎ را ﺑﺎ ﻗﺎﺑﻠﻴﺖﻫﺎي C++آﺷﻨﺎ ﻛﻨﻢ .ﮔﺰارهﻫﺎ در ﻫﻤﻪي زﺑﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ وﺟﻮد دارﻧﺪ و ﻣﻲﺷﻮد ﺧﺼﻮﺻﻴﺎت آنﻫﺎ را ﺑﻪ ﻃﻮر ﻛﻠﻲ و ﺧﺎرج از ﻫﺮ زﺑﺎن ﺧﺎﺻﻲ ﮔﻔﺖ ) .ﻣﻮﺿﻮع ﮔﺰارهﻫﺎ از ﻫﺰاران ﺳﺎل ﭘﻴﺶ ﻛﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲﻫﺎي اوﻟﻴﻪ ﺻﻔﺮ و ﻳﻚﻫﺎ را ﺑﺮ دﻳﻮارهي ﻏﺎرﻫﺎ ﺣﻚ ﻣﻲﻛﺮدﻧﺪ ﻣﻮﺿﻮع ﭘﺮ دردﺳﺮي ﺑﻮده اﺳﺖ .اﻟﺒﺘﻪ در آن زﻣﺎنﻫﺎ ﻣﺮدﻣﺎن اﻳﺮان زﻣﻴﻦ آن ﻗﺪر ﻣﺘﻤﺪن ﺑﻮدهاﻧﺪ ﻛﻪ ﺑﺮ ﻛﺘﻴﺒﻪﻫﺎي ﺳﻨﮕﻲ دوران ﻫﺨﺎﻣﻨﺸﻲ ﺑﺮﻧﺎﻣﻪﻫﺎﻳﻲ ﺑﻪ زﺑﺎن C++وﻟﻲ ﺑﻪ ﺧﻂ ﻣﻴﺨﻲ ﻳﺎﻓﺖ ﺷﺪه اﺳﺖ Bjarne Stroustrup .ﺑﻪ دروغ ﺑﻪ ﻋﻨﻮان آﻓﺮﻳﻨﻨﺪهي C++ﻣﻌﺮﻓﻲ ﺷﺪه اﺳﺖ .ﺑﻪ ﻋﻼوه ﻣﺨﺘﺮع C#ﻫﻢ ﺷﺨﺼﻲ اﻳﺮاﻧﻲ ﺑﻪ ﻧﺎم ﺳﻴﺪ ﺑﻦ ﺷﺎرع ﺑﻮده اﺳﺖ ﻛﻪ در 700ﺳﺎل ﭘﻴﺶ ﻣﻲزﻳﺴﺘﻪ .ﻣﺘﺎﺳﻔﺎﻧﻪ اﻣﺮوزه ﻳﻜﻲ از ﻛﺎرﻛﻨﺎن Microsoftﺑﻪ ﻋﻨﻮان ﺑﻪ وﺟﻮد آورﻧﺪهي C#ﻣﻌﺮﻓﻲ ﻣﻲﺷﻮد و اﺳﻢ ﺳﻴﺪ ﺑﻦ ﺷﺎرع ﻫﻢ ﺑﻪ ﻃﻮر ﻣﻐﺮﺿﺎﻧﻪاي ﺑﺪ ﺗﻠﻔﻆ ﻣﻲﺷﻮد و ﺑﺪﺗﺮ از ﻫﻤﻪ اﻳﻦ ﻛﻪ ﻫﻴﭻ ﻛﺲ ﻫﻴﭻ اﻋﺘﺮاﺿﻲ ﻧﻤﻲﻛﻨﺪ.
(
د "#ر if … else
دﺳﺘﻮر ifرا ﻗﺒﻼ ﺑﻪ ﻃﻮر ﺧﻴﻠﻲ ﻧﺎﻗﺼﻲ ﻣﻌﺮﻓﻲ ﻛﺮده ﺑﻮدﻳﻢ .ﻓﺮﻣﻮل ﻛﺎﻣﻞ اﻳﻦ دﺳﺘﻮر ﺑﻪ ﺷﻜﻞ زﻳﺮ اﺳﺖ: )23ارif(/ ]د456ر د4tا /اول[ [else ]]د456ر د4tا /دوم[
ﻣﻨﻈﻮر از ][ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺨﺶ else
84
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ]د456ر د4vا /دوم[ ﻣﻲﺗﻮاﻧﺪ ﺣﺬف ﺷﻮد .اﮔﺮ 23ار /درﺳﺖ ﺑﺎﺷﺪ ،ﻓﻘﻂ ]د456ر د4tا /اول[ اﺟﺮا ﻣﻲﺷﻮد وﮔﺮﻧﻪ ﻓﻘﻂ ]د456ر د4tا /دوم[ اﺟﺮا ﻣﻲﺷﻮد .ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;char a ;cin>> a ;cout<< endl )'if( a == 'a { ;"char* c = "one ;cout<< c } else { ;"char c[] = "two ;cout<< c } ;)(getch }
Output: x two
ا +د!84ر ifدر ا F 01ل را ! Eل ifﻛﻪ ﻗﺒﻞ از ﻣﺜﺎل آورده ﺷﺪه ﻣﻘﺎﻳﺴﻪ ﻛﻨﻴﻢ ﻣﻲﺑﻴﻨﻴﻢ در اﻳﻦ ﺟﺎ ]د456ر د4tا /اول[ ﻋﺒﺎرت اﺳﺖ از { ;"char* c = "one ;cout<< c }
ﻛﻪ ﻳﻚ ﺑﻠﻮك اﺳﺖ] .د456ر د4tا /دوم[ ﻫﻢ ﻳﻚ ﺑﻠﻮك اﺳﺖ: { ;"char c[] = "two ;cout<< c }
23ار /ﻫﻢ در اﻳﻦ ﺟﺎ ' a == 'aا .C4ﭼﻮن ﻣﻦ xرا وارد ﻛﺮدم ' a == 'aﻧﺎدرﺳﺖ ﺷﺪه و ]د456ر
د4tا /دوم[ اﺟﺮا ﺷﺪه.
85
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﻨﻈﻮر ﻣﻦ از د456ر د4tا /اﻳﻦ اﺳﺖ ﻛﻪ ﻫﺮ ﻳﻚ از اﻧﻮاع دﺳﺘﻮرات ﻛﻪ در اول ﻓﺼﻞ ﮔﻔﺘﻢ ﻣﻲﺗﻮاﻧﺪ ﺑﺎﺷﺪ .اﻣﺎ در ﻣﻮرد دﺳﺘﻮر _^]\[ ﻳﻚ ﻧﻜﺘﻪ ﻫﺴﺖ .در )if(true ;int a = 1
aﭘﺲ از »;« اﻋﺘﺒﺎر ﺧﻮد را از دﺳﺖ ﻣﻲدﻫﺪ و ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﻴﺴﺖ. elseﻫﻤﻴﺸﻪ ﺑﻪ ﻧﺰدﻳﻚ ﺗﺮﻳﻦ ifي ﻣﻲﭼﺴﺒﺪ ﻛﻪ داﺧﻞ ﺑﻠﻮك ﻧﺒﺎﺷﺪ ﻣﺜﻼ اﻳﻦ دو ﺗﺎ ﻣﺜﺎل را ﺑﺎ ﻫﻢ ﻣﻘﺎﻳﺴﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { )if(3 > 2 )if(4 > 5 ;"cout<< "4 > 5 else ;"cout<< "else ;)(getch }
Output: else >#include <conio.h >#include <iostream ;using namespace std )(int main { )if(3 > 2 { )if(4 > 5 ;"cout<< "4 > 5 } else ;"cout<< "else ;)(getch }
Output: empty
در ﻣﺜﺎل دوم elseﺑﻪ ifاول ﭼﺴﺒﻴﺪه .ﭼﻮن 3>2درﺳﺖ اﺳﺖ، { )if(4 > 5 ;"cout<< "4 > 5 }
86
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﺟﺮا ﺷﺪه ﻛﻪ ﻫﻴﭻ ﻛﺎري اﻧﺠﺎم ﻧﻤﻲدﻫﺪ .اﻣﺎ در ﻣﺜﺎل اول elseﺑﻪ ifدوم ﭼﺴﺒﻴﺪه .ﭼﻮن در ifاول 3>2
درﺳﺖ اﺳﺖ، )if(4 > 5 ;"cout<< "4 > 5 else ;"cout<< "else
اﺟﺮا ﺷﺪه ﻛﻪ ﻧﺘﻴﺠﻪاش ﭼﺎپ " "elseاﺳﺖ.
for F
ﺣﻠﻘﻪاي ﺑﺎ ﻧﺎم forﺗﻘﺮﻳﺒﺎ در ﻫﻤﻪي زﺑﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﭘﻴﺪا ﻣﻲﺷﻮد اﻣﺎ ﭘﻴﭽﻴﺪه ﺗﺮﻳﻦ ﺷﻜﻞ آن در C++اﺳﺖ. ﻓﺮﻣﻮل ﺣﻠﻘﻪي forﺑﻪ اﻳﻦ ﺷﻜﻞ اﺳﺖ: )د456ر)Z6 (2د23];`a Z\ /ار;[/د456ر)Z6 (1دfor([\]^_ Z\ `a Z\ / ]د456ر) (3د4tا[/
ﭘﺲ ﺳﻪ دﺳﺘﻮر دارﻳﻢ: (1د456ر) (1ﻛﻪ ﻧﻮع آن ﻣﻲﺗﻮاﻧﺪ Z6د [\]^_ Z\ `a Z\ /ﺑﺎﺷﺪ. (2د456ر) (2ﻛﻪ ﻧﻮع آن ﻣﻲﺗﻮاﻧﺪ Z6د `a Z\ /ﺑﺎﺷﺪ. (3د456ر) (3ﻛﻪ ﻫﺮ ﻧﻮﻋﻲ ﻣﻲﺗﻮاﻧﺪ ﺑﺎﺷﺪ. وﻗﺘﻲ ِ controlﺑﺮﻧﺎﻣﻪ ﺑﻪ دﺳﺘﻮر forﺑﺎﻻ ﻣﻲرﺳﺪ اول د456ر) (1را اﺟﺮا ﻣﻲﻛﻨﺪ .ﺑﻌﺪ ﺑﻪ ﺗﺮﺗﻴﺐ ﻣﺮاﺣﻞ زﻳﺮ را آن ﻗﺪر ﺗﻜﺮار ﻣﻲﻛﻨﺪ ﺗﺎ از ﺣﻠﻘﻪ ﺧﺎرج ﺑﺸﻮد: (1اﮔﺮ 23ار /ﻧﺎدرﺳﺖ ﺑﺎﺷﺪ )ﻳﻌﻨﻲ ﺻﻔﺮ ﺑﺎﺷﺪ( از ﺣﻠﻘﻪي forﺧﺎرج ﻣﻲﺷﻮد. (2د456ر) (3را اﺟﺮا ﻣﻲﻛﻨﺪ. (3د456ر) (2را اﺟﺮا ﻣﻲﻛﻨﺪ. ﺑﻨﺎﺑﺮاﻳﻦ د456ر) (3و ﺑﻌﺪ د456ر) (2آن ﻗﺪر ﭘﺸﺖ ﺳﺮ ﻫﻢ اﺟﺮا ﻣﻲﺷﻮﻧﺪ ﺗﺎ آن ﻛﻪ 23ار /ﺻﻔﺮ )ﻧﺎدرﺳﺖ( ﺷﻮد .ﻣﺘﺪاول ﺗﺮﻳﻦ ﺷﻜﻞ اﺳﺘﻔﺎده از ﺣﻠﻘﻪي forﺷﻜﻞ زﻳﺮ اﺳﺖ: )for(int i = 0; i < 10; i++
]د456ر) (3د4tا[/
87
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در واﻗﻊ ﺑﻬﺘﺮ اﺳﺖ ﻛﻪ ﻓﻘﻂ اﻳﻦ ﺷﻜﻞ ﺑﻪ ﺧﺼﻮص از ﺣﻠﻘﻪي forﻣﻮرد اﺳﺘﻔﺎده ﻗﺮار ﺑﮕﻴﺮد .ﺑﺮﻧﺎﻣﻪي زﻳﺮ 10ﺑﺎر helloرا ﭼﺎپ ﻣﻲﻛﻨﺪ. >#include <conio.h >#include <iostream ;using namespace std )(int main { )for(int i = 0; i < 10; i++ ;cout<< "hello" << endl ;)(getch }
Output: hello hello hello hello hello hello hello hello hello hello
ﺷﺎﻳﺪ ﻫﻨﻮز ﻛﺎر forﺑﺮاﻳﺘﺎن ﻣﺒﻬﻢ ﺑﺎﺷﺪ for .ﺑﺮاي اﻳﻦ ﺑﻪ ﻛﺎر ﻣﻲرود ﻛﻪ ﻳﻚ دﺳﺘﻮر را ﭼﻨﺪ ﺑﺎر ﺗﻜﺮار ﻛﻨﺪ .در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ forدﺳﺘﻮر
;"hello" << endl
<< coutرا 10ﺑﺎر اﺟﺮا ﻣﻲﻛﻨﺪ ﻳﺎ ﻣﺜﻼ ﺑﻪ ﺟﺎي ;0 ;1 ;2 ;3 ;4
<<cout <<cout <<cout <<cout <<cout
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: )for(int i = 0; i < 5; i++ ;cout<< i
ﺳﻌﻲ ﻛﻨﻴﺪ اﻳﻦﻫﺎ را ﺑﺎ ﺗﻮﺿﻴﺤﻲ ﻛﻪ اول اﻳﻦ ﺑﺨﺶ در ﻣﻮرد ﻧﺤﻮهي ﻛﺎر forﮔﻔﺘﻢ ﺗﻮﺟﻴﻪ ﻛﻨﻴﺪ ﺗﺎ ﻛﺎﻣﻼ ﺑﺎ دﺳﺘﻮر forآﺷﻨﺎ ﺑﺸﻮﻳﺪ.
88
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺷﻜﻠﻲ ﻏﻴﺮ ﻣﺘﺪاول و ﻧﻤﺎدﻳﻦ از ﺑﻪ ﻛﺎر ﺑﺮدن forرا ﻧﺸﺎن ﻣﻲدﻫﺪ .ﺧﻮب اﺳﺖ روي ﻣﺮاﺣﻞ اﺟﺮاي دﺳﺘﻮرات ﻓﻜﺮ ﻛﻨﻴﺪ و ﺧﺮوﺟﻲ را ﺗﻮﺟﻴﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;bool prop1 = true ;bool prop2 = true )"for(cout<<"statement1\n"; prop1; cout<<"statement2\n { ;"cout<< "statement3\n )if(prop2 == true ;prop2 = false else ;prop1 = false } ;)(getch }
Output: statement1 statement3 statement2 statement3 statement2
ﮔﺮﭼﻪ ﻓﻜﺮ ﻣﻲﻛﻨﻢ ﺣﻴﻦ آﻣﻮزش C++ﻧﺒﺎﻳﺪ ﺧﻴﻠﻲ وارد ﻛﺎرﺑﺮدﻫﺎي واﻗﻌﻲ دﺳﺘﻮرات ﺷﺪ وﻟﻲ ﻣﺜﺎل زﻳﺮ ﺑﺮاي ﻧﺸﺎن دادن ﻗﺪرت ﺣﻠﻘﻪي forﺿﺮوري ﺑﻪ ﻧﻈﺮ ﻣﻲرﺳﺪ .ﺿﻤﻦ اﻳﻦ ﻛﻪ ﻳﻚ ﻳﺎد آوري ﺑﺮاي رﺷﺘﻪﻫﺎﺳﺖ ﻛﻪ آنﻫﺎ را ﻓﺮاﻣﻮش ﻧﻜﻨﻴﺪ .ﺷﺎﻳﺪ ﺑﻪ ﻧﻈﺮ ﻧﻴﺎﻳﺪ اﻣﺎ رﺷﺘﻪ ﺑﻪ ﻋﻨﻮان راﺑﻂ ﺑﻴﻦ ﻛﺎرﺑﺮ و ﺑﺮﻧﺎﻣﻪ از ﻣﻬﻢﺗﺮﻳﻦ ﻗﺴﻤﺖﻫﺎي ﻫﺮ زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﺳﺖ و اﮔﺮ دﻗﺖ ﻛﻨﻴﺪ ﻧﺮم اﻓﺰارﻫﺎي ﭘﺮ ﻛﺎرﺑﺮدي ﻛﻪ ﺑﺎ آنﻫﺎ ﺳﺮ و ﻛﺎر دارﻳﺪ ﻛﺎر اﺻﻠﻴﺸﺎن ﻣﺮﺗﺐ ﻛﺮدن و ﻧﻤﺎﻳﺶ ﻣﻨﺎﺳﺐ رﺷﺘﻪﻫﺎﺳﺖ .در ﻫﺮ ﺣﺎل ﻗﺒﻮل دارم ﻛﻪ ﺑﺮاي ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻛﻪ ﻓﻜﺮ ﻛﺮدن را دوﺳﺖ دارد ،رﺷﺘﻪﻫﺎ ﺧﻴﻠﻲ ﻣﺤﺒﻮﺑﻴﺖ ﻧﺪارﻧﺪ .ﻣﺜﺎل زﻳﺮ ﺷﺎﻣﻞ ﺗﺎﺑﻌﻲ اﺳﺖ ﻛﻪ ﻃﻮل رﺷﺘﻪ را ﻣﻌﻴﻦ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )int GetLength(char* c { ;int i )for(i = 0; c[i] != '\0'; i++ ; ;return i }
89
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(int main { ;)"cout<< GetLength("hello ;)(getch }
Output: 5
در ﺗﺎﺑﻊ )( GetLengthاز اﻳﻦ ﻣﻮﺿﻮع ﻛﻪ آﺧﺮﻳﻦ ﻛﺎراﻛﺘﺮ ﻳﻚ رﺷﺘﻪ ' '\0اﺳﺖ اﺳﺘﻔﺎده ﺷﺪه .ﻣﻘﺪار ﻋﺪدي ' '\0ﺻﻔﺮ اﺳﺖ ﺑﺮاي ﻫﻤﻴﻦ ﺑﻪ ﺟﺎي '!6 c[i] != '\0ا .c[i] != 0 .G1! ./در ﺣﻠﻘﻪي for
دﺳﺘﻮر ﺗﻬﻲ را ﺑﻪ ﻛﺎر ﺑﺮدهام .ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﮔﺎﻫﻲ ﺑﻪ دﺳﺘﻮر ﺗﻬﻲ ﻫﻢ ﻧﻴﺎز دارﻳﻢ .در اﻳﻦ ﻣﺜﺎل ﻃﻮل رﺷﺘﻪي " "helloﭼﺎپ ﺷﺪه. در ﻓﺮﻣﻮل دﺳﺘﻮر forاﮔﺮ د456ر) (1ﻳﻚ د456ر _^]\[ ﺑﺎﺷﺪ ﻣﺘﻐﻴﺮ)ﻫﺎي( ﺗﻌﺮﻳﻔﻲ آن ﺑﺎ ﺧﺮوج از ﺣﻠﻘﻪ ﻏﻴﺮ ﻣﻌﺘﺒﺮ ﻣﻲﺷﻮﻧﺪ .ﻣﺜﻼ در ﻣﺜﺎل ﻗﺒﻞ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ iرا ﻧﻤﻲﺗﻮاﻧﺴﺘﻴﻢ در ﺣﻠﻘﻪي forاﻧﺠﺎم ﺑﺪﻫﻴﻢ و ﺑﻨﻮﻳﺴﻴﻢ: )int GetLength(char* c { )for(int i = 0; c[i] != '\0'; i++ ; ;return i }
ﻧﻜﺘﻪي ﺟﺎﻟﺐ اﻳﻦ اﺳﺖ ﻛﻪ در ﮔﺬﺷﺘﻪ و در compilerﻫﺎي ﻗﺪﻳﻤﻲ اﻳﻦ ﻃﻮر ﻧﺒﻮد و ﻣﻲﺗﻮاﻧﺴﺘﻴﻢ ﺗﺎﺑﻊ)( GetLengthرا ﺑﻪ ﺻﻮرت اﺧﻴﺮ ﻫﻢ ﺑﻨﻮﻳﺴﻴﻢ .در ﻗﺴﻤﺖ optionsدر compilerﻫﺎي ﺟﺪﻳﺪ اﺣﺘﻤﺎﻻ ﮔﺰﻳﻨﻪاي ﺑﺮاي ﺗﻐﻴﻴﺮ اﻳﻦ وﺿﻌﻴﺖ وﺟﻮد دارد .اﻳﻦ ﻣﺴﺎﺋﻞ ﺑﻪ ﺗﺼﻤﻴﻢﻫﺎﻳﻲ ﻛﻪ در ﻣﻮرد اﺳﺘﺎﻧﺪارد C++ﮔﺮﻓﺘﻪ ﻣﻲ- ﺷﻮد ﺑﺴﺘﮕﻲ دارد )و ﻣﺎ ﻛﺎﻣﻼ ﺑﻲ ﺗﻘﺼﻴﺮﻳﻢ
(.
اﮔﺮ ﺑﻪ ﻓﺮﻣﻮﻟﻲ ﻛﻪ ﺑﺮاي ﺣﻠﻘﻪي forاراﺋﻪ ﻛﺮدم ﻧﮕﺎه ﻛﻨﻴﺪ ﻧﻮﺷﺘﻪام ]23ار [/ﻧﻪ 23ار ./اﻳﻦ ﻳﻌﻨﻲ ﻣﻲﺷﻮد ﻧﻮﺷﺖ: )for(int i = 0;; i++ ;
ﻛﻪ ﻣﻌﺎدل اﺳﺖ ﺑﺎ )for(int i = 0; true; i++ ;
90
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﺸﻜﻞ اﻳﻦ ﺟﺎﺳﺖ ﻛﻪ اﮔﺮ اﻳﻦ ﺣﻠﻘﻪ اﺟﺮا ﺷﻮد ،اﺟﺮاي آن ﻫﻴﭻ وﻗﺖ ﺗﻤﺎم ﻧﻤﻲﺷﻮد ﻣﮕﺮ ﺑﺎ زور ﺑﺮﻧﺎﻣﻪ را ﻣﺘﻮﻗﻒ ﻛﻨﻴﻢ .ﺑﺮﻧﺎﻣﻪ زﻳﺮ را اﺟﺮا ﻛﻨﻴﺪ و ﺑﺮاي ﻣﺘﻮﻗﻒ ﻛﺮدن آن از Ctrl + Breakﻛﻤﻚ ﺑﮕﻴﺮﻳﺪ ) ﻛﻠﻴﺪ Break
ﻳﻜﻲ از ﺳﻪ ﻛﻠﻴﺪ ﻛﻨﺎر ﻫﻢ در ﮔﻮﺷﻪي راﺳﺖ و ﺑﺎﻻي keyboardاﺳﺖ( .ﺑﻪ ﻋﻼوه ﻣﻲﺗﻮاﻧﻴﺪ از دﮔﻤﻪﻫﺎي compilerﺑﺮاي ﺗﻮﻗﻒ اﺳﺘﻔﺎده ﻛﻨﻴﺪ )در Visual C++ 2005دﮔﻤﻪي ﻧﺸﺎن داده ﺷﺪه در
را ﻓﺸﺎر دﻫﻴﺪ(. >#include <conio.h >#include <iostream ;using namespace std )(int main { )for(int i = 0;; i++ ; ;)(getch }
Output: empty
راه دﻳﮕﺮي ﻫﻢ ﺑﺮاي ﺧﺮوج از ﺣﻠﻘﻪي forﻫﺴﺖ و آن اﺳﺘﻔﺎده از دﺳﺘﻮر breakاﺳﺖ .اﮔﺮ ِ controlﺑﺮﻧﺎﻣﻪ در ﺣﻠﻘﻪ ﺑﻪ دﺳﺘﻮر breakﺑﺮﺳﺪ ﻓﻮرا از ﺣﻠﻘﻪ ﺧﺎرج ﻣﻲﺷﻮد .ﺑﻪ ﻣﺜﺎل زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ .اﻳﻦ ﻣﺜﺎل اﻋﺪاد 1ﺗﺎ 10را ﭼﺎپ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int i = 1 );;(for { )if(10 < i ;break ;cout<< i << endl ;i++ }
91
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)(getch }
Output: 1 2 3 4 5 6 7 8 9 10
در ا for(;;) / 01ﻣﻌﺎدل اﺳﺖ ﺑﺎ ); for(;trueﻛﻪ در آن دو دﺳﺘﻮر `aﺑﻪ ﻛﺎر رﻓﺘﻪ .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺣﻠﻘﻪي forآن ﻗﺪر ﺧﻄﻮط )if(10 < i ;break ;cout<< i << endl ;i++
را اﺟﺮا ﻣﻲﻛﻨﺪ ﺗﺎ ﺑﻪ breakﺑﺮﺳﺪ و ﺑﻪ ﻣﺤﺾ رﺳﻴﺪن ﺑﻪ breakاز ﺣﻠﻘﻪ ﺧﺎرج ﻣﻲﺷﻮد break .ﭘﺸﺖ ﻳﻚ ifﭘﻨﻬﺎن ﺷﺪه و ifﻓﻘﻂ وﻗﺘﻲ اﺟﺎزهي اﺟﺮاي آن را ﻣﻲدﻫﺪ ﻛﻪ iاز 10ﺑﻴﺶ ﺗﺮ ﺑﺎﺷﺪ .ﻣﻘﺪار iدر ﻫﺮ ﺑﺎر اﺟﺮا ﺑﻪ وﺳﻴﻠﻪي i++ﻳﻜﻲ زﻳﺎد ﻣﻲﺷﻮد ﺗﺎ ﻣﻘﺪارش از 10ﺑﻴﺶ ﺗﺮ ﺷﻮد. دﺳﺘﻮر دﻳﮕﺮي ﻫﻢ ﻫﺴﺖ ﺑﻪ ﻧﺎم .continueﺗﻮﺿﻴﺢ اﻳﻦ دﺳﺘﻮر ﭘﻴﭽﻴﺪه ﺗﺮ اﺳﺖ .ﺑﻪ ﻓﺮﻣﻮل forﺗﻮﺟﻪ ﻛﻨﻴﺪ: )د456ر)23];(2ار;[/د456ر)for((1 ]د456ر)[(3
)ﻛﻪ ﻧﻮع دﺳﺘﻮرﻫﺎ را ﺑﺮاي ﺳﺎدﮔﻲ ﺣﺬف ﻛﺮدهام( .اﻳﻦ ﺣﻠﻘﻪ ﭼﻨﺪﻳﻦ ﺑﺎر د456ر) (3را ﺗﻜﺮار ﻣﻲﻛﻨﺪ .اﻣﺎ اﮔﺮ ِ controlﺑﺮﻧﺎﻣﻪ در د456ر) (3ﺑﻪ دﺳﺘﻮر continueﺑﺮﺳﺪ از ﻛﺎﻣﻞ ﻛﺮدن اﺟﺮاي د456ر) (3ﻣﻨﺼﺮف ﻣﻲﺷﻮد و ﻓﺮض ﻣﻲﻛﻨﺪ اﺟﺮاي د456ر) (3ﻛﺎﻣﻞ ﺷﺪه .ﻫﻴﭻ ﭼﻴﺰ دﻳﮕﺮي ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﺪ .ﻣﺜﻼ ﭘﺲ از اﻳﻦ ﻛﻪ د456ر) (3ﻛﺎﻣﻞ ﺷﺪه ﻓﺮض ﺷﺪ ،د456ر) (2اﺟﺮا ﻣﻲﺷﻮد و 23ار /ﭼﻚ ﻣﻲﺷﻮد .ﻣﺜﺎل زﻳﺮ ﻫﻤﻪ ﭼﻴﺰ را روﺷﻦ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main
92
www.pupuol.com
â&#x20AC;Ťď&#x2DC;ď&#x2DC;ďť? ﺎďş&#x;ďť&#x160; ﺊďş?ﺸďŽ&#x2022;ďş&#x17D; ďť ďťŁďşŞďşďşłďťŞâ&#x20AC;Ź { for(int i = 0; i < 10; i++) { if( i % 2 == 0) // if i is even continue; cout<< i << endl; } getch(); }
Output: 1 3 5 7 9
â&#x20AC;Ť زŮ&#x2C6;ŘŹ اﺳďş&#x2013; ﺧďť&#x201A;â&#x20AC;Źi 8Hâ&#x20AC;Ť Ů&#x201E; Ů&#x2C6;â&#x20AC;ŹF 01â&#x20AC;ŤŘŻŘą اâ&#x20AC;Ź cout<< i << endl;
â&#x20AC;Ť ŘŻŘą Ů&#x2C6;اďť&#x2014;ďť&#x160;â&#x20AC;Ź.â&#x20AC;Ť عﺳﺪ Ů&#x2C6; ďť&#x201C;عا اďş&#x;ﺎاŮ&#x160; ďş&#x2018;ďť ďťŽŮ&#x192; عا ďş&#x2DC;ďť&#x2014;ďť&#x2019; ďť&#x203A;ﺪâ&#x20AC;Źcontinue â&#x20AC;Ť Ů? ďş&#x2018;ﺎďş&#x17D; ďş&#x2018;â&#x20AC;Źcontrol â&#x20AC;ŤŘ§ďş&#x;ﺎا ﺡد زﺎاâ&#x20AC;Ź â&#x20AC;Ť ďş&#x153;ďťź ďş&#x2018;ﺎďş&#x17D;Ů&#x160; ďť&#x2014;ďş&#x2019;ďť&#x17E; عا ďş&#x2014;اŮ&#x2020; ďş&#x2018; ا ﺝعت ﺡďş&#x2013; Ů&#x2C6; ďş&#x2018; ďş&#x;ďş&#x17D;Ů&#x160;â&#x20AC;Ź.â&#x20AC;Ť ﺡďş&#x2019;ďş&#x17D;ďş&#x2013; داعدâ&#x20AC;Źreturn â&#x20AC;Ť ďş&#x2018; ŘŻďşłďş&#x2DC;عâ&#x20AC;Źcontinue :â&#x20AC;Ť اﺳďş&#x2DC;ďť&#x201D;ďş&#x17D;ŘŻŮ&#x2021; ďť&#x203A;ﺎدâ&#x20AC;Źreturn â&#x20AC;Ť ازâ&#x20AC;Źcontinue #include <conio.h> #include <iostream> using namespace std; void block(int& i) { if( i % 2 == 0) // if i is even return; cout<< i << endl; } int main() { for(int i = 0; i < 10; i++) block(i); getch(); }
Output: 1 3 5 7 9
93
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ا / 01ه 3ن LMH , /ا C4ا E 01ق I!Jاز forﺑﻪ ﻳﻚ ﺗﺎﺑﻊ ﺗﺒﺪﻳﻞ ﺷﺪه .در اﻳﻦ ﺟﺎ دﻳﮕﺮ ﻧﻤﻲﺗﻮاﻧﻴﻢ از continueاﺳﺘﻔﺎده ﻛﻨﻴﻢ وﻟﻲ ﺑﺎ returnﺗﻮاﻧﺴﺘﻪاﻳﻢ ﻫﻤﺎن ﻛﺎري را ﺑﻜﻨﻴﻢ ﻛﻪ continueﻣﻲﻛﺮد .وﻗﺘﻲ در ﻳﻚ ﺗﺎﺑﻊ ِ controlﺑﺮﻧﺎﻣﻪ ﺑﻪ returnﻣﻲرﺳﺪ اﺟﺮاي ﺗﺎﺑﻊ را ﻣﺘﻮﻗﻒ ﻣﻲﻛﻨﺪ و از آن ﺧﺎرج ﻣﻲﺷﻮد.
while F
ﺣﻠﻘﻪي whileﺣﺎﻟﺖ ﺧﺎﺻﻲ از ﺣﻠﻘﻪي forاﺳﺖ .ﻓﺮﻣﻮل آن ﻋﺒﺎرت اﺳﺖ از )23ارwhile(/ ]د456ر د4tا[/
و ﻛﺎر آن ﻫﻤﺎن ﻛﺎري اﺳﺖ ﻛﻪ );23ارfor(;/ ]د456ر د4tا[/
اﻧﺠﺎم ﻣﻲدﻫﺪ .ﻳﻌﻨﻲ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ 23ار /درﺳﺖ اﺳﺖ] ،د456ر د4tا [/اﺟﺮا ﻣﻲﺷﻮد. >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int i = 0 )while(i < 10 { ;"cout<< "statement\n ;i++ } ;)(getch }
Output: statement statement statement statement statement statement statement statement statement statement
94
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ whileﺗﺎ زﻣﺎﻧﻲ ﻛﻪ iﻛﻢ ﺗﺮ از 10اﺳﺖ ،ﺧﻄﻮط { ;"cout<< "statement\n ;i++ }
را اﺟﺮا ﻣﻲﻛﻨﺪ. دﺳﺘﻮرﻫﺎي breakو continueﻫﻢ ﻣﺎﻧﻨﺪ ﻗﺒﻞ در whileﻗﺎﺑﻞ اﺳﺘﻔﺎدهاﻧﺪ.
do … while F
ﻓﺮﻣﻮل اﻳﻦ ﺣﻠﻘﻪ ﻋـﺒﺎرت اﺳﺖ از do ]د456ر د4tا[/ ;)23ارwhile(/
و اﻳﻦ ﻣﻌﺎدل اﺳﺖ ﺑﺎ );;(for { ]د456ر د4tا[/ )23ارif(!/ ;break }
در واﻗﻊ ]د456ر د4tا [/ره ا* ا =!د 6ز 23 /ار / /در!= C4د. >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int i = 1 do { ;cout<< i << endl ;i++ } ;)while(i < 10 ;)(getch }
Output: 1
95
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 2 3 4 5 6 7 8 9
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺧﻄﻮط { ;cout<< i << endl ;i++ }
ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ iﻛﻢ ﺗﺮ از 10اﺳﺖ ،اﺟﺮا ﻣﻲﺷﻮﻧﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﺑﻪ ﺟﺎي do { ;cout<< i << endl ;i++ } ;)while(i < 10
ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: do ;cout<< i << endl ;i++ ;)while(i < 10
وﻟﻲ ﻣﻲﺷﻮد ﻧﻮﺷﺖ: do ;cout<< i << endl, i++ ;)while(i < 10
ﻛﻪ دﺳﺘﻮرﻫﺎ ﺑﺎ » «,ﺗﺮﻛﻴﺐ ﺷﺪهاﻧﺪ. ﺧﺎﺻﻴﺖ ﻣﻬﻢ ﺣﻠﻘﻪي do … whileاﻳﻦ اﺳﺖ ﻛﻪ دﺳﺖ ﻛﻢ ﻳﻚ ﺑﺎر ]د456ر د4tا [/اﺟﺮا ﻣﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std )(int main { do ;"cout<< "statement\n ;)while(false
96
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ getch(); }
Output: statement
: ﻫﻢ ﻗﺎﺑﻞ اﺳﺘﻔﺎدهاﻧﺪdo … while درcontinue وbreak دﺳﺘﻮرﻫﺎي #include <conio.h> #include <iostream> using namespace std; int main() { int i = 0; do { i++; if(i > 10) break; if( i % 2 == 0) // if i is even continue; cout<< i << endl; } while(1); getch(); }
Output: 1 3 5 7 9
. ﺑﻪ ﻋﻨﻮان ﮔﺰارهاي درﺳﺖ اﺳﺘﻔﺎده ﻛﺮدهام1 در اﻳﻦ ﺑﺮﻧﺎﻣﻪ از
هG
:اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ const int a = 10;
97
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ آن وﻗﺖ aﺟﺎﻳﻲ از ﺣﺎﻓﻈﻪ اﺳﺖ ﻛﻪ ﻣﻘﺪار آن 10اﺳﺖ وﻟﻲ اﻳﻦ ﻣﻘﺪار ﻗﺎﺑﻞ ﺗﻐﻴﻴﺮ ﻧﻴﺴﺖ )دﺳﺖ ﻛﻢ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﺑﻪ وﺳﻴﻠﻪي aﺑﻪ آن اﺷﺎره ﺷﻮد( .در واﻗﻊ از aﻣﻲﺷﻮد ﻣﺜﻞ ﻳﻚ ﻣﺘﻐﻴﺮ ﻣﻌﻤﻮﻟﻲ اﺳﺘﻔﺎده ﻛﺮد ﺑﻪ اﻳﻦ ﺷﺮط ﻛﻪ ﻣﻘﺪار آن ﺗﻐﻴﻴﺮ ﻧﻜﻨﺪ .ﺑﻪ ﻫﻤﻴﻦ ﻋﻠﺖ ﺑﻪ aﻳﻚ ﺛﺎﺑﺖ ﺑﺎ ﻧﻮع intﻣﻲﮔﻮﻳﻨﺪ .از ﻫﺮ ﻧﻮﻋﻲ ﻣﻲﺗﻮاﻧﻴﻢ ﺛﺎﺑﺖ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻣﺜﻼ ;'const char ch = 'a
ﻧﻜﺘﻪي دﻳﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﻳﻚ ﺛﺎﺑﺖ ﺣﺘﻤﺎ ﺑﺎﻳﺪ initializeﺷﻮد ﻳﻌﻨﻲ ﺑﺎﻳﺪ ﺑﻪ آن ﻣﻘﺪار اوﻟﻴﻪ داد )ﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ ﭼﺮا؟( .ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std ;const int a = 4 )(int main { const int b = ]char c[b << cout<< a << cout<< b ;cout<< c ;)(_getch }
;= a ;""abc ;endl ;endl
Output: 4 4 abc
ﻃﺒﻖ آن ﭼﻴﺰي ﻛﻪ ﮔﻔﺘﻢ 4i n\ const intع اﺳﺖ .ﻳﻚ pointerﺑﻪ اﻳﻦ 4iع ﻃﺒﻖ ﻣﻌﻤﻮل ﺑﻪ ﺷﻜﻞ * const intاﺳﺖ .اﻣﺎ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺧﻮد pointerﺛﺎﺑﺖ ﺑﺎﺷﺪ )ﻧﻪ ﻣﻘﺪاري ﻛﻪ ﺑﻪ آن اﺷﺎره ﻣﻲﻛﻨﺪ( ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;int a ;int* const p = &a
در اﻳﻦ ﺻﻮرت ﻣﻘﺪار pرا ﻧﻤﻲﺷﻮد ﺗﻐﻴﻴﺮ داد اﻣﺎ *pرا ﻣﻲﺷﻮد. ﺑﺮﻧﺎﻣﻪي زﻳﺮ از ﻳﻚ ِ pointerﺛﺎﺑﺖ و از ﻳﻚ pointerﺑﻪ ﻳﻚ ﺛﺎﺑﺖ اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { //--------------------------- part 1 ;'const char ch1 = 'a
98
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;const char* p = &ch1 // *p = 'x'; // error 'cout<< *p; // 'a //--------------------------;"cout<< "\n //--------------------------- part 2 ;'char ch2 = 'b ;char * const q = &ch2 // q = "hello"; // error ;'*q ='y 'cout<< *q; // 'y //--------------------------;)(getch }
Output: a y
ﻣﻘﺪار *pرا ﻧﻤﻲﺷﻮد ﺗﻐﻴﻴﺮ داد ﭼﻮن *pﻳﻚ ﺛﺎﺑﺖ اﺳﺖ .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ' comment out ،*p='xﺷﺪه اﺳﺖ .ﻣﻘﺪار qرا ﻫﻢ ﻧﻤﻲﺷﻮد ﻋﻮض ﻛﺮد ﭼﻮن qﺧﻮدش ﻳﻚ ﺛﺎﺑﺖ اﺳﺖ اﻣﺎ *qﻳﻚ ﺛﺎﺑﺖ ﻧﻴﺴﺖ ﺑﺮاي ﻫﻤﻴﻦ ﻣﻘﺪارش را ﺗﻮاﻧﺴﺘﻪام ﻋﻮض ﻛﻨﻢ. ﺣﺎل ﺑﻪ ﻣﺜﺎل زﻳﺮ و ﻧﺘﻴﺠﻪي ﻋﺠﻴﺐ آن ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;const int a = 4 ;int* const q = (int*) &a ;*q = 5 ;cout<< *(&a) << endl ;cout<< a ;)(_getch }
Output (BDS 2006): 5 4 Output (Visual C++ 2005): 4 4 ﻣﻦ ﺗﻮﺟﻴﻪ ﻣﻨﺎﺳﺒﻲ ﺑﺮاي اﻳﻦ ﻧﺘﻴﺠﻪ ﻧﺪارم .ﺷﻤﺎ ﭼﻪ ﻃﻮر؟ ﺛﺎﺑﺖ aﻣﻘﺪار 4دارد &a .ﻛﻪ ﻧﻮﻋﺶ *const int
اﺳﺖ ﺑﻪ int* castﺷﺪه و در ِ pointerﺛﺎﺑﺖ qﻗﺮار ﮔﺮﻓﺘﻪ .ﺣﺎﻻ ﺑﻪ ﻧﻈﺮ ﻣﻲآﻳﺪ *qﺑﺎﻳﺪ ﻫﻤﺎن aﺑﺎ ﻫﻤﺎن
99
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﻜﺎن ﺣﺎﻓﻈﻪ ﺑﺎﺷﺪ .ﻣﻘﺪار *qرا ﻋﻮض ﻛﺮدهام *(&a).و aرا ﻛﻪ ﺣﺘﻤﺎ ﺑﺎﻳﺪ ﻳﻜﺴﺎن ﺑﺎﺷﻨﺪ ﭼﺎپ ﻛﺮدهامBDS . 2006دو ﻣﻘﺪار ﻣﺘﻔﺎوت را ﭼﺎپ ﻣﻲﻛﻨﺪ .اﺧﺘﻼف ﻧﺘﺎﻳﺞ دو اﻳﻦ compilerﻣﻌﺮوف ﻧﺸﺎن ﻣﻲدﻫﺪ ﻛﻪ ﺧﻮد ﺑﻪ وﺟﻮد آورﻧﺪﮔﺎن compilerﻫﺎ ﺗﺼﻮر ﻛﺎﻣﻠﻲ از pointerﺑﻪ ﺛﺎﺑﺖﻫﺎ ﻧﺪارﻧﺪ .ﻫﻤﻴﻦ ﻣﺜﺎل را ﺗﻐﻴﻴﺮ دادهام و ﺑﻪ ﺟﺎي ;cout<< *(&a) << endl
ﻧﻮﺷﺘﻪام: ;const int* w = &a ;cout<< *(w) << endl
ﻛﻪ ﻇﺎﻫﺮا ﺑﺎ ﻫﻢ ﻫﻴﭻ ﻓﺮﻗﻲ ﻧﺪارﻧﺪ ،ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺑﻪ دﺳﺖ آﻣﺪه: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;const int a = 4 ;int* const q = (int*) &a ;*q = 5 ;const int* w = &a ;cout<< *(w) << endl ;cout<< a ;)(_getch }
Output: 5 4
ﺧﺮوﺟﻲ Visual C++ 2005ﺗﻐﻴﻴﺮ ﻣﻲﻛﻨﺪ و ﻫﺮ دو compilerﻳﻚ ﻧﺘﻴﺠﻪ دارﻧﺪ .اﻳﻦ ﺟﻮر ﻧﺘﺎﻳﺞ ﻧﺸﺎن ﻣﻲ- دﻫﻨﺪ ﻛﻪ ﺷﺎﻳﺪ آن ﭼﻪ compilerاﻧﺠﺎم ﻣﻲدﻫﺪ ﺑﺎ ﺗﺼﻮرﻫﺎي ﺳﺎدهي ﻣﺎ از ﻣﻜﺎنﻫﺎي ﺣﺎﻓﻈﻪ ﻛﻪ ﺑﻪ ﻣﺘﻐﻴﺮﻫﺎ ﻧﺴﺒﺖ داده ﻣﻲﺷﻮد ﭘﻴﭽﻴﺪه ﺗﺮ اﺳﺖ و ﺑﻪ compilerﻫﺎ واﺑﺴﺘﮕﻲ زﻳﺎدي دارد .در ﻫﺮ ﺣﺎل ﺗﺼﻮرﻫﺎي ﺳﺎده ﺑﻬﺘﺮ از آن اﺳﺖ ﻛﻪ ﻫﻴﭻ ﺗﺼﻮري ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻴﻢ. اﮔﺮ ﻋﺪد ،رﺷﺘﻪ و ...ﻣﺴﺘﻘﻴﻤﺎ در ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻇﺎﻫﺮ ﺑﺸﻮﻧﺪ )ﻣﺜﻞ 4و 5در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ( ﻧﻮﻋﻲ ﺛﺎﺑﺖ ﺑﻪ ﺣﺴﺎب ﻣﻲ- آﻳﻨﺪ ﻛﻪ ﺑﻪ آنﻫﺎ اﺻﻄﻼﺣﺎ ﺛﺎﺑﺖ literalﻳﺎ literal constantﮔﻔﺘﻪ ﻣﻲﺷﻮد .ﻣﺜﻼ ﺧﻮد " "helloوﻗﺘﻲ در ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻇﺎﻫﺮ ﻣﻲﺷﻮد ﻳﻚ ﺛﺎﺑﺖ literalاﺳﺖ .ﺑﻪ ﺛﺎﺑﺖﻫﺎﻳﻲ ﻛﻪ ﭘﻴﺶ از اﻳﻦ ﮔﻔﺘﻴﻢ و literalﻧﻴﺴﺘﻨﺪ symbolic constantﻣﻲﮔﻮﻳﻨﺪ .در C++اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻧﻮع aرا ﻣﺸﺨﺺ ﻛﻨﻴﻢ ﺑﺎ ،cout )( typeid(a).nameرا ﭼﺎپ ﻣﻲﻛﻨﻴﻢ .ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﺸﺎن ﻣﻲدﻫﺪ ﻛﻪ ﻣﻤﻜﻦ اﺳﺖ compilerﻧﻮع ﺛﺎﺑﺖﻫﺎ را واﻗﻌﺎ ﺛﺎﺑﺖ ﻧﺪاﻧﺪ: >#include <conio.h >#include <iostream
100
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; int main() { const int a = 5; cout<< typeid(a).name() << endl; cout<< typeid(&a).name() << endl; cout<< typeid("hello").name(); _getch(); }
Output(BDS 2006): int const int * char[6] Output(Visual C++ 2005): int int const * char const [6]
اﺳﺖ در ﺣﺎﻟﻲ ﻛﻪ ﻣﺎ ﻣﻲداﻧﻴﻢint ،a ﻫﺮ دو ﻣﻲﮔﻮﻳﻨﺪ ﻧﻮعVisual C++ 2005 وBDS 2006 ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ 6 ﻋﻠﺖ اﻳﻦ ﻛﻪ آراﻳﻪ. ﻣﻌﺮﻓﻲ ﺷﺪه اﺳﺖ6 ﻫﺎ ﺑﺎ ﻃﻮلchar " آراﻳﻪاي ازhello" ﻧﻮع. اﺳﺖconst int . ﻫﻢ ﻫﺴﺖ0 ﺗﺎﻳﻲ اﻳﻦ اﺳﺖ ﻛﻪ آﺧﺮ ﻫﺮ رﺷﺘﻪ ﻳﻚ ﻛﺎراﻛﺘﺮ5 ﺗﺎﻳﻲ اﺳﺖ ﻧﻪ .int const * ﻧﻮﺷﺘﻪVisual C++ وconst int * ﻧﻮﺷﺘﻪBDS 2006 ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ
switch " ر#د
ﻓﺮﻣﻮل اﻳﻦ دﺳﺘﻮر ﻋﺒﺎرت اﺳﺖ از switch([int n\]) { case [int ع4i Zd (1)ydZz]: [/دZ6،b4cd،`a (1)ر456]د case [int ع4i Zd (2)ydZz]: [/دZ6،b4cd،`a (2)ر456]د . . case [int ع4i Zd (n)ydZz]: [/دZ6،b4cd،`a (n)ر456]د [default:
101
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ]]د456ر)Z6،b4cd،`a (n+1د[/ } اﻳﻦ دﺳﺘﻮر ﺑﻪ ﺗﺮﺗﻴﺐ از ﺑﺎﻻ ﺑﻪ ﭘﺎﻳﻴﻦ ﺑﻪ دﻧﺒﺎل اوﻟﻴﻦ ِ ydZzﺑﻌﺪ از ﻫﺮ caseﻣﻲﮔﺮدد ﻛﻪ ﺑﺎ ]\[int n
ﻣﺴﺎوي ﺑﺎﺷﺪ .اﮔﺮ ﭘﻴﺪا ﻛﺮد ،از آن ﺟﺎ ﺑﻪ ﺑﻌﺪ ﻫﻤﻪي د456رﻫﺎي ﺑﻌﺪ از ﻫﺮ caseو ﺑﻌﺪ از ) defaultدر ﺻﻮرت وﺟﻮد( اﺟﺮا ﻣﻲﺷﻮﻧﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﮔﺬاﺷﺘﻦ defaultﺿﺮوري ﻧﻴﺴﺖ .اﮔﺮ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﺑﻪ defaultﻣﻲرﺳﺪ ﭘﻴﺪا ﻧﻜﺮده ﺑﺎﺷﺪ ﺗﻨﻬﺎ دﺳﺘﻮرات ﭘﺲ از defaultاﻧﺠﺎم ﻣﻲﺷﻮد .اﮔﺮ defaultوﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ و caseﻣﻨﺎﺳﺐ ﻫﻢ ﭘﻴﺪا ﻧﺸﻮد ِ controlﺑﺮﻧﺎﻣﻪ از switchﺧﺎرج ﻣﻲﺷﻮد. ﺑﻪ ﻣﺜﺎلﻫﺎي زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ .ﺧﺮوﺟﻲ آنﻫﺎ ﻫﻤﻪ ﭼﻴﺰ را ﺗﻮﺿﻴﺢ ﻣﻲدﻫﺪ .در ﺿﻤﻦ آن ﭼﻪ در اﻳﻦ ﻣﺜﺎلﻫﺎ ﻣﻲﺑﻴﻨﻴﺪ ﻓﺮاﺗﺮ از ﻓﺮﻣﻮل ﺑﺎﻻﺳﺖ .اﮔﺮ در ﻣﻮرد ﻛﺎرﻛﺮد ﻫﺮ ﻛﺪام ﺷﻚ داﺷﺘﻴﺪ آن را را در compilerﺧﻮد آرام و ﺧﻂ ﺑﻪ ﺧﻂ اﺟﺮا ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 10 )switch(a { case 9: ;cout<< "case 9" << endl case 11: ;cout<< "case 11" << endl case 10: ;cout<< "case 10" << endl case 100: ;cout<< "case 100" << endl } ;"cout<< "end of switch ;)(_getch }
Output: case 10 case 100 end of switch
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻘﺪار aﺑﺮاﺑﺮ ﺑﺎ 10اﺳﺖ و ﺑﻨﺎﺑﺮاﻳﻦ دﺳﺘﻮرات ﻣﻮﺟﻮد در switchﻛﻪ ﺑﻌﺪ از case 10:
ﻗﺮار دارﻧﺪ اﺟﺮا ﻣﻲﺷﻮد. 102
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
#include <conio.h> #include <iostream> using namespace std; int main() { int a = 10; switch(a) { case 101: cout<< "case 101" << endl; case 11: case 10: case 100: cout<< "cases 11, 10, 100" << endl; } cout<< "end of switch"; _getch(); }
Output: cases 11, 10, 100 end of switch
#include <conio.h> #include <iostream> using namespace std; int main() { int a = 10; switch(a) { default: cout<< "default" << endl; case 10: cout<< "case 10" << endl; } cout<< "end of switch"; _getch(); }
Output: case 10 end of switch
#include <conio.h> #include <iostream>
103
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; int main() { int a = 10; switch(a) { default: cout<< "default" << endl; case 11: cout<< "case 11" << endl; } cout<< "end of switch"; _getch(); }
Output: default case 11 end of switch #include <conio.h> #include <iostream> using namespace std; int main() { int a = 10; switch(a) { cout<< "before cases"; case 9: int b; b = 5; cout<< "case 9" << endl; case 10: int a = 2; cout<< "case 10" << endl; cout<< b << endl; } cout<< "end of switch"; _getch(); }
Output: case 10 256 end of switch
اﮔﺮ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺟﺎي int b; b = 5;
104
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﻨﻮﻳﺴﻴﺪ ;int b = 5
compilerﻳﻚ errorﻣﻲﮔﻴﺮد زﻳﺮا caseي ﻛﻪ bدر آن ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد اﺻﻼ اﺟﺮا ﻧﻤﻲﺷﻮد .و ﺑﻨﺎﺑﺮاﻳﻦ b
ﻗﺎﺑﻞ initializeﺷﺪن ﻧﻴﺴﺖ .اﻣﺎ ﺗﻌﺮﻳﻒ ﺑﺪون initializeﻛﺮدن اﻣﻜﺎن ﭘﺬﻳﺮ اﺳﺖ زﻳﺮا ﻣﻌﻤﻮﻻ ﺣﺎﻓﻈﻪي ﻻزم ﺑﺮاي ﻣﺘﻐﻴﺮﻫﺎ در ﺷﺮوع blockﺗﺨﺼﻴﺺ داده ﻣﻲﺷﻮد ﻧﻪ در ﺣﻴﻦ اﺟﺮاي دﺳﺘﻮرات blockدﻗﻴﻘﺎ ﻣﺜﻞ آن ﭼﻴﺰي ﻛﻪ در pascalراﻳﺞ اﺳﺖ) .از روي ﺗﻌﺼﺐ و ﻋﻼﻗﻪ ﺑﻪ C++ﻣﻲﮔﻮﻳﻢ ﻛﻪ زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ در ﻣﻘﺎﺑﻞ C
ﻳﺎ
زﺑﺎنﻫﺎي ﻣﻬﻤﻲ ﻧﻴﺴﺘﻨﺪ
delphiدر ﺑﺮاﺑﺮ C++
pascal
(. >#include <conio.h >#include <iostream ;using namespace std
)(int main { )switch(5 case 5: ;"cout<< "case 5 ;)(_getch }
Output: case 5
ﻣﺎﻧﻨﺪ ﺣﻠﻘﻪﻫﺎ ،در switchﻫﻢ ﻣﻲﺷﻮد از دﺳﺘﻮر breakاﺳﺘﻔﺎده ﻛﺮد و اﺳﺘﻔﺎده از آن ﺑﺎﻋﺚ ﺧﺮوج از switchﻣﻲﺷﻮد .اﺳﺘﻔﺎده از breakدر ﭘﺎﻳﺎن ﻫﺮ caseﺑﺴﻴﺎر ﻣﺘﺪاول اﺳﺖ ﭼﻮن ﺑﺎﻋﺚ ﻣﻲﺷﻮد ﻛﻪ ﻓﻘﻂ ﻳﻚ caseاﺟﺮا ﺷﻮد و caseﻫﺎي ﺑﻌﺪي اﺟﺮا ﻧﺸﻮﻧﺪ. >#include <conio.h >#include <iostream ;using namespace std )(int main { )switch(5 { case 5: ;"cout<< "case 5 ;break case 4: ;"cout<< "case 4 ;break default: {
105
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int a = 4 ;cout<< a ;break } } ;)(_getch }
Output: case 5
د "#ر goto
درون ﻳﻚ ﺗﺎﺑﻊ ا 01د!84ر ﻣﻲﺗﻮاﻧﺪ ِ controlﺑﺮﻧﺎﻣﻪ را ﺑﻪ ﺟﺎي دﻳﮕﺮي از ﺗﺎﺑﻊ ﺑﻔﺮﺳﺘﺪ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺟﺎﻳﻲ از ﺗﺎﺑﻊ ﻋﻼﻣﺖ ﮔﺬاري ﺑﺸﻮد ﺗﺎ ﺑﺘﻮاﻧﻴﻢ controlرا آن ﺟﺎ ﺑﻔﺮﺳﺘﻴﻢ ،ﺑﺎﻳﺪ از labelاﺳﺘﻔﺎده ﻛﺮد ) labelﺷﺒﻴﻪ ﺑﻪ bookmarkدر Microsoft Wordﻳﺎ ﺑﺮﻧﺎﻣﻪﻫﺎي دﻳﮕﺮ اﺳﺖ( .ﺑﺮاي اﻳﺠﺎد ﻳﻚ labelاول ﻳﻚ اﺳﻢ ﺑﺮاي آن اﻧﺘﺨﺎب ﻣﻲﻛﻨﻴﻢ آن اﺳﻢ را در ﻣﺤﻞ ﻣﻮرد ﻧﻈﺮ از ﺗﺎﺑﻊ ﺗﺎﻳﭗ ﻣﻲﻛﻨﻴﻢ و ﭘﺲ از آن » «:ﻣﻲﮔﺬارﻳﻢ .ﻓﺮﻣﻮل gotoﺑﻪ ﺷﻜﻞ زﻳﺮ اﺳﺖ: ;]\goto [label n
ﻣﺜﺎل زﻳﺮ ﻧﺤﻮهي اﺳﺘﻔﺎده از gotoرا ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int i = 1 ;int sum = 0 REP: ;sum = sum + i ;i++ )if(i <= 10 ;goto REP ;cout<< sum ;)(_getch }
Output: 55
106
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺟﻤﻊ اﻋﺪاد 1ﺗﺎ 10را ﭘﻴﺪا و ﭼﺎپ ﻣﻲﻛﻨﺪ .اﮔﺮ در ﻣﻮرد ﻧﺤﻮهي ﻛﺎر آن ﺷﻚ دارﻳﺪ آن را ﻗﺪم ﺑﻪ ﻗﺪم اﺟﺮا ﻛﻨﻴﺪ REP .در اﻳﻦ ﺟﺎ \ label nاﺳﺖ. در ﺷﺮح ﺑﺪي دﺳﺘﻮر gotoزﻳﺎد ﮔﻔﺘﻪ ﻣﻲﺷﻮد .ﮔﻔﺘﻪ ﻣﻲﺷﻮد ﻛﻪ ﺧﻮاﻧﺎﻳﻲ و ﻣﺮﺗﺐ ﺑﻮدن ﺑﺮﻧﺎﻣﻪ ﺑﺎ دﺳﺘﻮر goto
از ﺑﻴﻦ ﻣﻲرود .زﻳﺎد ﺑﻪ ﻛﺎر ﺑﺮدن ،gotoﺑﺮﻧﺎﻣﻪ را آن ﻗﺪر ﺗﻮ در ﺗﻮ ﻣﻲﻛﻨﺪ ﻛﻪ ﻣﺜﻞ اﺳﭙﺎﮔﺘﻲ ﻣﻲﺷﻮدJesse . Libertyﻧﻮﻳﺴﻨﺪهي ﭘﺮ ﻓﺮوش ﺗﺮﻳﻦ ﻛﺘﺎبﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ در ﻳﻜﻲ از ﻛﺘﺎبﻫﺎي ﺧﻮد ﻣﻲﻧﻮﻳﺴﺪ: آﻣﻮزﮔﺎران ﻋﻠﻢ ﻛﺎﻣﭙﻴﻮﺗﺮ 20ﺳﺎل ﮔﺬﺷﺘﻪ را ﺻﺮف ﻓﺮو ﻛﺮدن ﻳﻚ ﺣﺮف در ﺳﺮ داﻧﺶ آﻣﻮزان ﺧﻮد ﻛﺮدهاﻧﺪ: »ﻫﺮﮔﺰ و ﻫﺮﮔﺰ از دﺳﺘﻮر ﺷﻴﻄﺎﻧﻲ gotoاﺳﺘﻔﺎده ﻧﻜﻨﻴﺪ« ”!“Never, ever, ever use goto! It is evil ﺑﺎ وﺟﻮد ﻫﻤﻪي اﻳﻦﻫﺎ ﻣﻦ ﻓﻜﺮ ﻣﻲﻛﻨﻢ اﺳﺘﻔﺎده از gotoﺑﻌﻀﻲ ﺟﺎﻫﺎ اﺟﺘﻨﺎب ﻧﺎﭘﺬﻳﺮ اﺳﺖ .ﻣﺜﻼ اﮔﺮ درون دو ﺣﻠﻘﻪي ﺗﻮ در ﺗﻮ ﺑﺎﺷﻴﻢ ﺑﺮاي ﺧﺮوج ﻫﻢ زﻣﺎن از ﻫﺮ دو ﻧﻤﻲﺗﻮاﻧﻴﻢ از breakاﺳﺘﻔﺎده ﻛﻨﻴﻢ break .ﺗﻨﻬﺎ از ﻳﻜﻲ ﺧﺎرج ﻣﻲﺷﻮد. ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺗﻼش ﻣﻲﻛﻨﺪ اﻋﺪاد ﺻﺤﻴﺢ و و را ﻃﻮري ﭘﻴﺪا ﻛﻨﺪ ﻛﻪ در ﻣﻌﺎدﻟﻪي رﻳﺎﺿﻲ ، ﺻﺪق ﻛﻨﻨﺪ ﺑﺎ اﻳﻦ ﺷﺮط ﻛﻪ .ﺑﻪ آن دﻗﺖ ﻛﻨﻴﺪ اﻣﺎ ﺧﻴﻠﻲ وﻗﺖ ﺻﺮف آن ﻧﻜﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int r, i, j )for(r = 600; r <= 1400; r++ { ;i = j = 0 )while(true { )if (i * i + j * j < r ;i++ else )if (i * i + j * j > r { ;i-- ;j++ } else ;goto END )if(i < 0 ;break
107
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } // while } // for END: cout<< r << endl; cout<< i << endl; cout<< j << endl; _getch(); }
Output: 601 24 5
. اﻳﻦ ﺧﺮوﺟﻲ ﻳﻌﻨﻲ ( در ﻓﻀﺎي ﻋﻤﻮﻣﻲ )ﻳﻌﻨﻲ ﺧﺎرج از ﻫﺮ ﺗﺎﺑﻊ. از ﺗﺎﺑﻌﻲ ﺑﻪ ﺗﺎﺑﻊ دﻳﮕﺮ ﭘﺮﻳﺪgoto در ﭘﺎﻳﺎن ﻣﻲﮔﻮﻳﻢ ﻛﻪ ﻧﻤﻲﺷﻮد ﺑﺎ . ﺑﮕﺬارﻳﻢlabel n\ ﻫﻢ ﻧﻤﻲﺗﻮاﻧﻴﻢ
typedef وtypeid وsizeof ه
ﺑﺎ ﻛﻠﻤﻪي ﻛﻠﻴﺪي.ع را ﻣﺸﺨﺺ ﻛﻨﻴﻢ4i n\ ﻣﻲﺗﻮاﻧﻴﻢ ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎي ﻳﻚ ﻣﺘﻐﻴﺮ ﻳﺎsizeof ﺑﺎ ﻛﻠﻤﻪي ﻛﻠﻴﺪي ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﺳﺘﻔﺎدهﻫﺎي اﺑﺘﺪاﻳﻲ اﻳﻦ دو.ع را ﻣﺸﺨﺺ ﻛﺮد4i n\ ﻣﻲﺷﻮد اﺳﻢ ﻧﻮع ﻳﻚ ﻣﺘﻐﻴﺮ ﻳﺎ اﺳﻢtypeid :ﻛﻠﻤﻪ را ﻧﺸﺎن ﻣﻲدﻫﺪ #include <conio.h> #include <iostream> using namespace std; int main() { double a = 2.365; cout<< sizeof(double) << endl; cout<< sizeof(a) << endl; cout<< typeid(double).name() << endl; cout<< typeid(a).name() << endl; cout<< typeid(sizeof(a)).name() << endl; getch(); }
Output: 8 8 double double
108
www.pupuol.com
тАлянШя╗оянШя╗оя╗Э я╗гя║оя║Яя╗К я║йя║Ня╗зя║╕яоХя║Оя╗й я╗н я╗гя║кя║ня║│я╗ктАм unsigned int
тАл я╗│я╗Мя╗ия╗▓ я╗│я╗Ъ я╗гя║Шя╗Ря╗┤я║отАм.тАл ╪зя║│я║ЦтАм8 тАл я╗гя╗Шя║к╪з╪▒ я╗ля║о я╗Ыя║к╪з┘ЕтАм.тАл я╗│я╗Ья╗▓ я╗ля║┤я║Шя╗ия║ктАмsizeof(a) тАл ┘ИтАмsizeof(double) тАл╪п╪▒ ╪зя╗│я╗ж я║Ся║оя╗зя║Оя╗гя╗ктАм тАл ╪▒╪зтАмa тАл" ╪зя║│я║Ц я╗Ыя╗к я╗зя╗о╪╣тАмdouble" тАл ╪▒я║╖я║Шя╗к┘КтАм╪Мtypeid(a).name() .тАл я╗ля║╕я║Ц я║Ся║Оя╗│я║Ц ╪зя║│я║ЦтАмdouble тАля║Ся║О я╗зя╗о╪╣тАм .тАля╗гя║╕я║ия║║ я╗гя╗▓я╗Ыя╗ия║ктАм Visual тАл ╪зя║Яя║о╪з я╗гя╗▓я║╖я╗о╪п ┘Ия╗Яя╗▓ ╪п╪▒тАмBDS 2006 тАл я║Ся║оя╗зя║Оя╗гя╗к┘К ╪▓я╗│я║о ╪п╪▒тАм.тАл я║Чя╗оя║Яя╗к я╗Ыя╗ия╗┤я║ктАмsizeof тАля║гя║Оя╗╗ я║Ся╗к я╗Ыя║О╪▒я║Ся║о╪п ╪пя╗│яоХя║о┘К ╪з╪▓тАм :тАл ╪зя║Яя║о╪з я╗зя╗дя╗▓я║╖я╗о╪птАмC++ 2005 #include <conio.h> #include <iostream> using namespace std; int main() { int a[] ={1,2,3,4,5,6,7,8,9}; cout<< sizeof(a)/sizeof(int) << endl; cout<< sizeof((int*)a)/sizeof(int) << endl; int*& p = a; cout<< sizeof(p) <<endl; char c[] = "hello"; cout<< sizeof(c)/ sizeof(char) << endl; getch();
// output 9 // output 9 // output 4 // output 6
}
Output (BDS 2006): 9 9 4 6
тАл ╪▒╪з я╗│я╗ЪтАмp ┘Р pointer тАл я║Ся╗Мя║ктАм.тАл ╪▒╪з ян╝я║О┘╛ я╗гя╗▓я╗Ыя╗ия╗┤я╗втАмa тАл я║Чя╗Мя║к╪з╪п я╗Ля╗ия║Оя║╗я║о ╪в╪▒╪зя╗│я╗к┘КтАмsizeof тАл╪п╪▒ ╪зя╗│я╗ж я║Ся║оя╗зя║Оя╗гя╗к я║Ся║О ╪зя║│я║Шя╗Фя║О╪п┘З ╪з╪▓тАм p тАл ╪зя╗гя║О я╗гя╗▓я║Ся╗┤я╗ия╗┤я╗в я╗Ыя╗к я║Ся║ОтАм.тАл я╗│я╗Ья╗▓ я║Ся║Оя║╖я║ктАмa тАл ╪з╪┤ ┘ЗтАмNE O тАл ┘ЖтАмP PJ тАл я╗гя╗▓яоФя╗┤я║оя╗│я╗в я║Чя║О я╗зя╗к я║Чя╗ия╗мя║О я╗гя╗Шя║к╪з╪▒╪┤тАмa тАл я║Ся╗ктАмreference
тАл я║Ся║оя╗зя║Оя╗гя╗к ╪▒╪з я║Чя╗Ря╗┤я╗┤я║о я╗гя╗▓╪пя╗ля╗┤я╗в ┘И я║Ся╗к я║Яя║О┘КтАм.тАл( янШя╗┤я║к╪з я╗Ыя║о╪птАмsizeof тАл ╪▒╪з )я║Ся║ОтАмa тАля╗зя╗дя╗▓я║╖я╗о╪п я║Чя╗Мя║к╪з╪п я╗Ля╗ия║Оя║╗я║отАм int*& p = a;
:тАля╗гя╗▓я╗зя╗оя╗│я║┤я╗┤я╗втАм int* p = a;
:тАл я╗ля╗в я╗Чя║Оя║Ся╗Ю ╪зя║Яя║о╪з я║Ся║Оя║╖я║ктАмVisual C++ 2005 тАля║Чя║О ╪п╪▒тАм #include <conio.h> #include <iostream> using namespace std; int main() { int a[] ={1,2,3,4,5,6,7,8,9};
109
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ // output 9 // output 1 or 9 // output 4 // output 6
;sizeof(a)/sizeof(int) << endl ;sizeof((int*)a)/sizeof(int) << endl ;= a ;sizeof(p) <<endl
<<cout <<cout int* p <<cout
;"char c[] = "hello ;cout<< sizeof(c)/ sizeof(char) << endl ;)(getch }
Output (BDS 2006): 9 9 4 6 Output (Visual C++ 2005): 9 1 4 6
اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻫﻤﺎن ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ اﺳﺖ وﻟﻲ اﻳﻦ ﺑﺎر p referenceﻧﻴﺴﺖ .ﺑﻪ اﺧﺘﻼف ﺧﺮوﺟﻲ دو compilerﺗﻮﺟﻪ ﻛﻨﻴﺪ. ﺧﺮوﺟﻲ Microsoftدر اﻳﻦ ﺟﺎ و در ﺑﻴﺶ ﺗﺮ ﻣﻮارد اﺧﺘﻼف ﺑﺮاي ﻣﻦ ﻗﺎﺑﻞ ﻗﺒﻮل ﺗﺮ از ﺧﺮوﺟﻲ Borland اﺳﺖ .ﺑﺎ اﻳﻦ ﺣﺎل ﻣﻦ compilerﻫﺎي Borlandرا ﺗﺮﺟﻴﺢ ﻣﻲدﻫﻢ ﭼﻮن ﻋﻼوه ﺑﺮ Editorﺑﻬﺘﺮ ﺑﺮاي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ،اﻣﻜﺎﻧﺎت ﻛﺎﻣﻞ ﺗﺮي دارﻧﺪ و ﺳﺨﺖ ﮔﻴﺮي آنﻫﺎ ﻛﻢ ﺗﺮ اﺳﺖ) .ﻛﻼ در ﻫﻤﻪي ﻧﺮم اﻓﺰارﻫﺎي Microsoft ﺧﺴﺎﺳﺘﻲ آزار دﻫﻨﺪه ﭘﻴﺪاﺳﺖ
ﮔﺮﭼﻪ Microsoftدر ﺗﻮزﻳﻊ ﻧﺮم اﻓﺰارﻫﺎي ﺧﻮد ﺳﺨﺎوﺗﻤﻨﺪاﻧﻪ ﻋﻤﻞ ﻣﻲﻛﻨﺪ
)ﻣﺜﻼ ﻧﺮم اﻓﺰارﻫﺎي راﻳﮕﺎن زﻳﺎدي ﭘﺨﺶ ﻣﻲﻛﻨﺪ و ﺣﺘﻲ ﺑﻪ Windowsﻫﺎي ﻛﭙﻲ ﺷﺪه ﺳﺮوﻳﺲ ﻣﻲدﻫﺪ((. ﺗﻮﺟﻪ ﺑﻪ اﻳﻦ ﻧﻜﺘﻪ ﻫﻢ ﺿﺮوري اﺳﺖ ﻛﻪ ﻇﺎﻫﺮا ﻛﺎر اﺻﻠﻲ Borlandاﻳﺠﺎد ﻧﺮم اﻓﺰارﻫﺎي ﻣﺮﺑﻮط ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﺳﺖ اﻣﺎ ﻛﺎر اﺻﻠﻲ Microsoftﺗﻮﻟﻴﺪ ﺳﻴﺴﺘﻢ ﻋﺎﻣﻞ اﺳﺖ. ﻛﻠﻤﻪي ﻛﻠﻴﺪي typedefﺑﺮاي ﺗﻐﻴﻴﺮ اﺳﻢ 4iعﻫﺎ ﺑﻪ ﻛﺎر ﻣﻲرود .ﻣﺜﻼ ﺷﺎﻳﺪ ﻧﻮﺷﺘﻦ unsigned intﺑﺮاي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻣﻨﺎﺳﺐ ﻧﺒﺎﺷﺪ و او ﺑﺨﻮاﻫﺪ اﺳﻢ ﻛﻮﺗﺎه ﺗﺮي را ﺑﻪ ﺟﺎي آن ﺑﮕﺬارد .اﮔﺮ ﺑﺨﻮاﻫﺪ UINTرا ﺑﻪ ﺟﺎي آن ﺑﮕﺬارد ﻣﻲﺗﻮاﻧﺪ از typedefاﺳﺘﻔﺎده ﻛﻨﺪ و ﺑﻨﻮﻳﺴﺪ: ;typedef unsigned int UNIT
ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h
110
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <iostream> using namespace std; int main() { typedef unsigned int UINT; UINT a = 5; cout<< a << endl; cout<< typeid(UINT).name(); getch(); }
Output: 5 unsigned int typedef ﺑﺮاي اﻳﻦ ﻛﻪ ﺷﻜﻞ ﻛﺎرﺑﺮد. ﻳﻜﻲ ﻣﻲﮔﻴﺮدunsigned int را ﺑﺎUINT ،typeid ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ
در ﻛﻨﺎر ﻫﻢint وunsigned ،typedef را ﺑﻪ ﺧﺎﻃﺮ ﺑﺴﭙﺎرﻳﺪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در ﻣﺜﺎل ﺑﺎﻻ ﻛﻠﻤﻪﻫﺎي ﻛﻠﻴﺪي : اﻳﻦ ﻣﺜﺎل را ﻧﮕﺎه ﻛﻨﻴﺪ.ﻗﺮار دارﻧﺪ #include <conio.h> #include <iostream> struct TAG { int a; }; typedef TAG INT,* PINT; using namespace std; int main() { INT a; PINT p = &a; a.a = 2; cout<< a.a << endl; cout<< typeid(a.a).name() << endl; cout<< typeid(p).name() << endl; cout<< typeid(PINT).name() << endl; getch(); }
Output (BDS 2006): 2 int TAG * TAG * Output (Visual C++ 2005): 2 int
111
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ struct TAG * struct TAG *
در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ ﺑﻪ ﺟﺎي struct TAG { int a; }; typedef TAG INT,* PINT;
:ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ typedef struct TAG { int a; } INT,* PINT;
: را ﺣﺬف ﻛﺮد و ﺑﺮﻧﺎﻣﻪ را ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ دادTAG ﻣﻲﺷﻮد ﻛﻠﻤﻪي، ﺣﺘﻲ ﺳﺎده ﺗﺮ.و ﺧﺮوﺟﻲ ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﺪ #include <conio.h> #include <iostream> typedef struct { int a; } INT,* PINT; using namespace std; int main() { INT a; PINT p = &a; a.a = 2; cout<< a.a << endl; cout<< typeid(a.a).name() << endl; cout<< typeid(p).name() << endl; cout<< typeid(PINT).name() << endl; getch(); }
Output (BDS 2006): 2 int INT * INT * Output (Visual C++ 2005): 2 int struct INT * struct INT *
112
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ :ﻣﺜﺎل زﻳﺮ ﻫﻢ ﺟﺎﻟﺐ اﺳﺖ #include <conio.h> #include <iostream> using namespace std; int f(int a,int b) { cout<< a + b; return 0; } int main() { cout<< typeid(f).name() << endl; _getch(); }
Output (BDS 2006): int (*)(int,int) Output (Visual C++ 2005): int __cdecl(int,int)
.__ ﺗﻮﺿﻴﺢ ﺧﻮاﻫﻢ دادcdecl ﺑﻌﺪا در ﻣﻮرد ﻣﻌﻨﻲ : اﻃﻼﻋﺎﺗﻲ در ﻣﻮرد آراﻳﻪﻫﺎ ﺑﻪ دﺳﺖ ﺑﻴﺎورﻳﻢtypeid ﺣﺎﻻ ﺑﮕﺬارﻳﺪ ﺑﺎ #include <conio.h> #include <iostream> using namespace std; int main() { cout<< typeid(new double [2]).name() << endl; char c[12]; cout<< typeid(c).name() << endl; char d[] = "hello"; cout<< typeid(d).name() << endl; _getch(); }
Output(BDS 2006): double * char[12] char[6]
113
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output(Visual C++ 2005): double * char [12] char [6]
default-int 8!H و
Microsoft را ﭘﺸﺘﻴﺒﺎﻧﻲ ﻣﻲﻛﻨﻨﺪ اﻣﺎdefault-int وﻳﮋﮔﻲBorland ﻫﺎيcompiler ﻧﺴﻞﻫﺎي ﻣﺨﺘﻠﻒ اﻳﻦ اﺳﺖ ﻛﻪ در ﻫﺮ ﺟﺎ اﺑﻬﺎﻣﻲdefault-int وﻳﮋﮔﻲ. اﻳﻦ وﻳﮋﮔﻲ را ﭘﺸﺘﻴﺒﺎﻧﻲ ﻧﻤﻲﻛﻨﺪVisual C++ 2005 ﺧﻮدش آن را در ﻧﻈﺮ ﻣﻲﮔﻴﺮد ﻣﺜﻼ ﺑﻪ ﺟﺎيcompiler را ﻧﻨﻮﻳﺴﻴﻢ وint وﺟﻮد ﻧﺪارد ﻣﻲﺗﻮاﻧﻴﻢ ﻛﻠﻤﻪي const int a = 3;
:ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻢ const a = 3;
و ﺑﻪ ﺟﺎي int f(int a, char ch) { … }
:ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ f(a, char ch) { … }
: ﻗﺎﺑﻞ اﺟﺮاﺳﺖBorland ﻫﺎيcompiler ﺑﺮﻧﺎﻣﻪي زﻳﺮ در #include <conio.h> #include <iostream> using namespace std; const M = 4; f(a, b) { cout<< a+b << endl; cout<< typeid(a).name() << endl; return a*b; } int main() { cout<< f(2, 3); getch(); }
Output: 5
114
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int 6
در ﻣﻮرد ﻓﺮم ﭘﺎراﻣﺘﺮﻫﺎي ﺗﺎﺑﻊ fدر ﺑﺎﻻ ﺣﺘﻲ Borlandدو ﺗﺎ warningﻣﻲﮔﻴﺮد )ﻳﻌﻨﻲ ﺻﺎدر ﻣﻲﻛﻨﺪ
(.
ﺑﺮاي رﻓﻊ آنﻫﺎ ﻛﺎﻓﻲ اﺳﺖ ﺟﻠﻮي ﭘﺎراﻣﺘﺮﻫﺎي aو int ، bﺑﮕﺬارﻳﻢ.
LValueو RValue
ﻗﺒﻼ در ﻣﻮرد ﻋﻤﻠﮕﺮ = ﭼﻴﺰﻫﺎﻳﻲ ﮔﻔﺘﻢ .ﮔﻔﺘﻢ ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;int a ;a = 2
اﻣﺎ اﮔﺮ ﺑﻨﻮﻳﺴﻴﺪ: ;2 = a
errorي ﺻﺎدر ﻣﻲﺷﻮد ﭼﻮن 2ﻧﻤﻲﺗﻮاﻧﺪ ﺳﻤﺖ ﭼﭗ ﺗﺴﺎوي ﻗﺮار ﺑﮕﻴﺮد .در اﻳﻦ ﺣﺎﻟﺖ ﻣﻲﮔﻮﻳﻴﻢ 2ﻳﻚ LValueﻧﻴﺴﺖ ) Lﻣﺨﻔﻒ Leftﺑﻪ ﻣﻌﻨﻲ »ﭼﭗ« اﺳﺖ( .ﺑﻪ ﭼﻴﺰي ﻛﻪ )ﺗﻨﻬﺎ( ﻣﻲﺗﻮاﻧﺪ ﺳﻤﺖ راﺳﺖ ﺗﺴﺎوي ﻗﺮار ﮔﻴﺮد ﻳﻚ RValueﻣﻲﮔﻮﻳﻨﺪ ) Rﻣﺨﻔﻒ Rightﺑﻪ ﻣﻌﻨﻲ »راﺳﺖ« اﺳﺖ( 2 .و ﻫﺮ ﺛﺎﺑﺖ دﻳﮕﺮي LValue ﻧﻴﺴﺖ .اﮔﺮ aو bﻣﺘﻐﻴﺮﻫﺎﻳﻲ ﺑﺎ ﻧﻮع intﺑﺎﺷﻨﺪ a+b ،ﻳﻚ LValueﻧﻴﺴﺖ .ﺑﻪ ﺗﺎﺑﻊ زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ: )int f(int a { ;a++ ;return a }
ﺣﺎل اﮔﺮ xﻳﻚ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع intﺑﺎﺷﺪ f(x) ،ﻳﻚ LValueﻧﻴﺴﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﺎﺷﺪ ﺑﺎﻳﺪ ﻳﻚ & ﭘﺲ ﻫﺮ intﺑﮕﺬارﻳﻢ ﻳﻌﻨﻲ ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ را ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ: )int& f(int& a { ;a++ ;return a }
ﻣﺜﺎل زﻳﺮ از اﻳﻦ ﺗﺎﺑﻊ اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )int& f(int& a { ;a++ ;return a
115
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } )(int main { ;int a = 3 ;cout<< f(a) << endl ;f(a) = 300 ;cout<< a ;)(_getch }
Output: 4 300
در ﺣﻘﻴﻘﺖ در ﺗﺎﺑﻊ )( mainﻣﺘﻐﻴﺮ aﻧﻤﺎﻳﻨﺪهي ﻫﻤﺎن ﺟﺎﻳﻲ از ﺣﺎﻓﻈﻪ اﺳﺖ ﻛﻪ ) f(aﻧﻤﺎﻳﻨﺪهي آن اﺳﺖ .ﺑﺮاي ﻳﺎدآوري ﻣﻲﮔﻮﻳﻢ ﻛﻪ اﮔﺮ در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ fﺑﻪ ﺷﻜﻞ زﻳﺮ ﻧﻮﺷﺘﻪ ﺑﺸﻮد: )int& f(int a { ;a++ ;return a }
ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻧﻤﻲﺷﻮد ﭼﻮن aﺗﻨﻬﺎ در ﺗﺎﺑﻊ fﻣﻌﺘﺒﺮ اﺳﺖ وﻟﻲ ) f(aﻛﻪ ﻫﻤﺎن ) aﺑﺎ ﻫﻤﺎن ﺣﺎﻓﻈﻪ( اﺳﺖ ﺑﺎﻳﺪ در )( mainﻫﻢ ﻣﻌﺘﺒﺮ ﺑﺎﺷﺪ .اﮔﺮ ﺑﺨﻮاﻫﻴﻢ اﻳﻦ ﺗﺎﺑﻊ errorاﻳﺠﺎد ﻧﻜﻨﺪ ﻳﺎ ﺟﻠﻮي ﻫﺮ دو intﺑﺎﻳﺪ & ﺑﮕﺬارﻳﻢ ﻳﺎ ﺟﻠﻮ ﻫﻴﭻ ﻛﺪام )ﺑﺎﻳﺪ ﺑﻪ اﻳﻦ ﻣﺴﺄﻟﻪ آن ﻗﺪر ﻓﻜﺮ ﻛﻨﻴﺪ ﻛﻪ ﺑﺘﻮاﻧﻴﺪ آن را ﺑﻪ ﺧﻮﺑﻲ ﺗﻮﺟﻴﻪ ﻛﻨﻴﺪ!
(.
& ' prototypeه و ' &ه ز!8"5
اﮔﺮ ﺗﺎﺑﻊ )( fﭘﻴﺶ از ﺗﺎﺑﻊ )( gﺗﻌﺮﻳﻒ ﺷﺪه ﺑﺎﺷﺪ ﻧﻤﻲﺗﻮاﻧﺪ )( gرا ﻓﺮا ﺑﺨﻮاﻧﺪ .ﺑﻪ ﻧﺎﭼﺎر ﺑﺎﻳﺪ )( gرا ﻗﺒﻞ از )( fﺗﻌﺮﻳﻒ ﻛﺮد .اﻣﺎ اﮔﺮ ﺗﺎﺑﻊ )( gﻫﻢ ﺑﺨﻮاﻫﺪ )( fرا ﻓﺮا ﺑﺨﻮاﻧﺪ ﺑﺎﻳﺪ ﭼﻪ ﻛﺎر ﻛﺮد؟ ﭼﺎرهي ﻛﺎر اﺳﺘﻔﺎده از prototypeاﺳﺖ ِ prototype .ﻳﻚ ﺗﺎﺑﻊ ﻋﺒﺎرت اﺳﺖ از ﺧﺮوﺟﻲ ،اﺳﻢ و ﻧﻮع ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻚ ﺗﺎﺑﻊ و »;«. ﻣﺜﻼ ﺑﺮاي ﺗﺎﺑﻊ )int f(int a, double b { ;cout<< a + b ;return a }
ﻣﻲﺷﻮد prototypeﻫﺎي زﻳﺮ را در ﻧﻈﺮ ﮔﺮﻓﺖ: ;)int f(int a, double b ;)int f(int x, double y
116
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int f(int , double y); int f(int, double);
: اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ. ﺑﺎ ﻫﻢ ﻓﺮﻗﻲ ﻧﺪارﻧﺪprototype و اﻳﻦﻫﺎ ﺑﻪ ﻋﻨﻮان #include <conio.h> #include <iostream> using namespace std; int f(int v,int); int main() { f(5,5); _getch(); } int f(int a,int b) { cout<< a +b; return a; }
Output: 10
. اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪf() ﻣﻲﺗﻮاﻧﺪ ازmain() ، ﻧﻮﺷﺘﻪ ﺷﺪهmain() ﻗﺒﻞ ازf() ِ prototype ﭼﻮن :ﻣﺜﺎل زﻳﺮ ﭘﻴﭽﻴﺪه ﺗﺮ اﺳﺖ #include <conio.h> #include <iostream> using namespace std; void f(bool); // can be commented out void g(bool); void f(bool a) { if(a) { cout<< "f does not call g\n"; return; } else { cout<< "f calls g\n"; g(!a); cout<< "g called\n"; } } void g(bool b) {
117
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< "g calls f\n"; f(b); cout<< "f called\n"; } int main() { g(false); cout<< "g called\n"; _getch(); }
Output: g calls f f calls g g calls f f does not call g f called g called f called g called
. ﺳﻌﻲ ﻛﻨﻴﺪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﺧﻮب ﺗﺤﻠﻴﻞ ﻛﻨﻴﺪ. را ﻓﺮا ﻣﻲﺧﻮاﻧﺪf() ،g() را وg() ،f() در اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ. ﻣﻲﺗﻮاﻧﺪ ﭼﻨﺪ ﺑﺎر ﻗﺒﻞ و ﺑﻌﺪ از ﺗﻌﺮﻳﻒ و ﺣﺘﻲ درون ﺗﺎﺑﻊﻫﺎي دﻳﮕﺮ ﺗﻜﺮار ﺷﻮدprototype ﻳﻚ اﺳﺘﻔﺎده ﻛﺮدهاﻳﻢ ﺗﺎprototype ﻧﻴﺴﺖ اﻣﺎ در ﻣﺜﺎل زﻳﺮ ازprototype ﻳﻚ ﺗﺎﺑﻊ ﺧﻮدش را ﻓﺮا ﺑﺨﻮاﻧﺪ ﻧﻴﺎزي ﺑﻪ : ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢmain() ﺑﺘﻮاﻧﻴﻢ ﺗﺎﺑﻊ را ﺑﻌﺪ از #include <conio.h> #include <iostream> using namespace std; int main() { void f(bool a); // a prototype f(false); _getch(); } void f(bool a) { if(a == true) cout<<"true\n"; else { cout<<"false\n"; f(true); } }
Output: false
118
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ true در اﻳﻦ ﻣﺜﺎل f() ِ prototypeدرون ﺗﺎﺑﻊ )( mainﻧﻮﺷﺘﻪ ﺷﺪه و ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺗﻨﻬﺎ در ﺗﺎﺑﻊ )(main
ﻣﻌﺘﺒﺮ اﺳﺖ .ﺗﺤﻠﻴﻞ اﻳﻦ ﺑﺮﻧﺎﻣﻪ از ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ آﺳﺎن ﺗﺮ اﺳﺖ ﻓﻘﻂ وﻗﺘﻲ در )( fﺑﻪ ﺧﻂ ;)f(true
رﺳﻴﺪﻳﺪ ﮔﻴﺞ ﻧﺸﻮﻳﺪ اﻳﻦ ﺧﻂ در ﺗﺎﺑﻊ )( fﻫﻤﺎن ﻛﺎري را ﻣﻲﻛﻨﺪ ﻛﻪ اﮔﺮ در )( mainﺑﻮد ﻣﻲﻛﺮد. ﺑﻪ ﺗﺎﺑﻌﻲ ﻛﻪ ﻣﺴﺘﻘﻴﻤﺎ ﻳﺎ ﺑﺎ واﺳﻄﻪ ﺧﻮدش را ﻓﺮاﻣﻲﺧﻮاﻧﺪ ،ﺗﺎﺑﻊ ﺑﺎزﮔﺸﺘﻲ ﻣﻲﮔﻮﻳﻨﺪ .ﺗﺎﺑﻊ ﺑﺎزﮔﺸﺘﻲ ﻳﻚ اﺑﺰار ﻗﻮي ﺑﺮاي ﻧﻮﺷﺘﻦ ﺧﻴﻠﻲ از ﺑﺮﻧﺎﻣﻪﻫﺎ اﺳﺖ .ﻣﻤﻜﻦ وﻗﺘﻲ ﺑﺎ ﻗﺪرت ﺧﺎرق اﻟﻌﺎدهي ﺗﺎﺑﻊﻫﺎي ﺑﺎزﮔﺸﺘﻲ آﺷﻨﺎ ﺷﺪﻳﺪ) ،ﻣﺜﻞ ﻣﻦ( آن ﻗﺪر ﺷﻴﻔﺘﻪي آنﻫﺎ ﺑﺸﻮﻳﺪ ﻛﻪ ﺣﺘﻲ ﺑﺮاي ﻛﺎرﻫﺎي ﺧﻴﻠﻲ ﺳﺎده ﻫﻢ ﺑﻪ ﻓﻜﺮ آنﻫﺎ ﺑﻴﻔﺘﻴﺪ .اﻳﻦ ﺧﻴﻠﻲ ﺧﻮب ﻧﻴﺴﺖ ﭼﻮن ﺑﻪ ﻛﺎر ﺑﺮدن ﺗﺎﺑﻊﻫﺎي ﺑﺎزﮔﺸﺘﻲ ﺑﺮﻧﺎﻣﻪ را ﭘﻴﭽﻴﺪه ﻣﻲﻛﻨﺪ و ﮔﺎﻫﻲ راهﻫﺎي ﺑﻬﺘﺮ و ﺑﺎ ﺻﺮﻓﻪ ﺗﺮي ﻫﺴﺖ .ﺑﻬﺘﺮ اﺳﺖ ﻗﺒﻞ از اﺳﺘﻔﺎده از ﺗﺎﺑﻊﻫﺎي ﺑﺎزﮔﺸﺘﻲ ﺳﻌﻲ ﻛﻨﻴﻢ ﺑﺮﻧﺎﻣﻪ را ﺑﺎ ﺣﻠﻘﻪي ﺑﻲ ﻧﻬﺎﻳﺖ )ﻣﺜﻼ }…{)(while(true ﺑﻨﻮﻳﺴﻴﻢ اﮔﺮ ﻧﺸﺪ ﻳﺎ ﺧﻴﻠﻲ ﭘﻴﭽﻴﺪه ﺷﺪ ﺑﻌﺪ ﺑﻪ ﺳﺮاغ ﺗﺎﺑﻊﻫﺎي ﺑﺎزﮔﺸﺘﻲ ﻣﻲروﻳﻢ. ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﺨﺸﻲ از ﻗﺪرت ﺗﺎﺑﻊﻫﺎي ﺑﺎزﮔﺸﺘﻲ را ﺑﻪ ﺷﻤﺎ ﻧﺸﺎن ﺑﺪﻫﻢ ،ﻳﻚ ﺣﻠﻘﻪي whileرا ﺑﺎ ﺗﺎﺑﻊ ﺑﺎزﮔﺸﺘﻲ ﺟﺎﻳﮕﺰﻳﻦ ﻣﻲﻛﻨﻢ .ﻣﺜﺎل زﻳﺮﺣﻠﻘﻪي whileرا ﺑﺮاي ﺟﻤﻊ ﻛﺮدن اﻋﺪاد 1ﺗﺎ 10ﺑﻪ ﻛﺎر ﻣﻲﮔﻴﺮد .در اﻳﻦ ﻣﺜﺎل از ﻋﻤﻠﮕﺮ = +اﺳﺘﻔﺎده ﺷﺪه ﻛﻪ درﺑﺎرهي آن ﭘﺲ ﻣﺜﺎل ﺗﻮﺿﻴﺢ ﻣﻲدﻫﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int n = 0 ;int sum = 0 )while(n < 10 { ;n++ ;sum += n } ;cout<< sum ;)(_getch }
Output: 55
119
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﻨﻈﻮر از ; sum+=nﻫﻤﺎن sum=sum+nاﺳﺖ در ﺿﻤﻦ ﺑﻪ ﺟﺎي a=a/b ،a=a*bو ...ﻣﻲﺷﻮد ﻧﻮﺷﺖ a/=b ،a*=bو . ...ﺑﻪ ﺟﺎي ﺣﻠﻘﻪي whileدر ﺑﺎﻻ ﻣﻲﺷﻮد از ﻳﻚ ﺗﺎﺑﻊ ﺑﺎزﮔﺸﺘﻲ اﺳﺘﻔﺎده ﻛﺮد و ﺑﺮﻧﺎﻣﻪ را اﻳﻦ ﻃﻮري ﺗﻐﻴﻴﺮ داد: >#include <conio.h >#include <iostream ;using namespace std )void f(int& n, int& sum { )if(n < 10 { ;n++ ;sum += n ;)f(n,sum } } )(int main { ;int n = 0 ;int sum = 0 ;)f(n,sum ;cout<< sum ;)(_getch }
Output: 55
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﺎﺑﻊﻫﺎي ﺑﺎزﮔﺸﺘﻲ ﻣﻲﺗﻮاﻧﻨﺪ ﻛﺎر ﺣﻠﻘﻪﻫﺎ را اﻧﺠﺎم دﻫﻨﺪ اﻣﺎ ﻫﻢ ﻧﻮﺷﺘﻦ و ﻫﻢ ﻓﻬﻤﻴﺪن ﻳﻚ ﺣﻠﻘﻪ ﺑﺴﻴﺎر راﺣﺖ ﺗﺮ اﺳﺖ.
& ' pointerه و )را ه
ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﻳﻚ ﻣﺘﻐﻴﺮ ﻳﺎ ﺛﺎﺑﺖ pointerداﺷﺘﻪ ﺑﺎﺷﻴﻢ ﺑﻪ ﻳﻚ ﺗﺎﺑﻊ ﻳﺎ ﻳﻚ آراﻳﻪ ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ pointerداﺷﺘﻪ ﺑﺎﺷﻴﻢ .اﻳﻦ ﻣﺜﺎل ﻧﺤﻮهي ﻛﺎر را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )void f(int a { ;cout<< "a function with argument " << a } )(int main {
120
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ void (*pf)(int); pf = &f; (*pf)(2); _getch();
// a pointer // f(2) hijk
}
Output: a function with argument 2
& )ﻛﻪ ﻣﻲﺗﻮاﻧﻴﻢ آن را ﻣﻜﺎن ﺗﺎﺑﻊ درf را ﺑﺎf ،f ﻫﺎ ﺑﺮاي راﺣﺘﻲ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ در اﺳﺘﻔﺎده از ﺗﺎﺑﻊcompiler اﻣﺎ ﺑﺮاي اﺛﺒﺎت اﻳﻦ دو ﻣﻄﻠﺐ. ﻳﻜﻲ اﺳﺖpf * ﺑﺎ ﺧﻮدpf ﺑﻪ ﻋﻼوه در ﻣﺜﺎل ﺑﺎﻻ.ﺣﺎﻓﻈﻪ ﺗﻌﺒﻴﺮ ﻛﻨﻴﻢ( ﻳﻜﻲ ﻣﻲﮔﻴﺮﻧﺪ :ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ دﻗﺖ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; void f(int a) { cout<< "a function with argument " << a; } int main() { void (*pf)(int); pf = f; cout<< (&f == f) << endl; cout<< (*pf == pf) << endl; pf(8); _getch(); }
Output: 1 1 a function with argument 8
:ﺑﻪ ﻋﻼوه ﻣﻲﺗﻮاﻧﻴﻢ آراﻳﻪاي از ﺗﺎﺑﻊﻫﺎ داﺷﺘﻪ ﺑﺎﺷﻴﻢ #include <conio.h> #include <iostream> using namespace std; void f(int a) { cout<< "f with argument " << a << endl; } void g(int b) { cout<< "g with argument " << b << endl; } int main() { void (*pf[4])(int) = {f, g, f, g};
121
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )for(int i = 0; i < 4; i++ ;)pf[i](i ;)(_getch }
0 1 2 3
argument argument argument argument
Output: f with g with f with g with
اﮔﺮ aﻳﻚ ﻣﺘﻐﻴﺮ ،ﺛﺎﺑﺖ ،ﺗﺎﺑﻊ و ...ﺑﺎﺷﺪ ،ﺑﻪ &aآدرس aﻣﻲﮔﻮﻳﻨﺪ )ﺑﻪ ﺧﻮد & در اﻳﻦ ﺟﺎ ﻋﻤﻠﮕﺮ address-of ﻣﻲﮔﻮﻳﻨﺪ( .ﺑﻪ آن آدرس ﻣﻲﮔﻮﻳﻨﺪ ﭼﻮن ﻣﻜﺎن ﺣﺎﻓﻈﻪ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ )ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻳﻚ shortcutدر Windowsﺷﺎﻣﻞ آدرس ﻳﻚ ﻓﺎﻳﻞ اﺳﺖ( .ﻣﻤﻜﻦ اﺳﺖ اﻳﻦ ﻓﻜﺮ ﺑﻪ ذﻫﻨﺘﺎن رﺳﻴﺪه ﺑﺎﺷﺪ ﻛﻪ آدرس ﻳﻚ ﺗﺎﺑﻊ ﻣﺜﻞ fﺑﺎ ﺧﻮد آن ﻳﻜﻲ اﺳﺖ .اﻣﺎ ﻧﻤﻲﺷﻮد ﺑﺎ ﺻﺮاﺣﺖ اﻳﻦ را ﮔﻔﺖ .در ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻣﻦ compilerﺧﻮدم را در اﻳﻦ ﻣﻮرد ﺑﺎزﺟﻮﻳﻲ ﻛﺮدهام .ﺑﺮاي اﻳﻦ ﻛﺎر از ﻳﻚ pointerدوﮔﺎﻧﻪ )ﻳﻌﻨﻲ pointerي ﻛﻪ ﺑﻪ ﻳﻚ pointerدﻳﮕﺮ اﺷﺎره ﻣﻲﻛﻨﺪ( اﺳﺘﻔﺎده ﻛﺮدهام .اﻳﻦ ﺑﺮﻧﺎﻣﻪ در Visual C++ 2005ﺧﻄﺎ دارد و ﻣﻦ آن را در BDS 2006 اﺟﺮا ﻛﺮدهام: >#include <conio.h >#include <iostream ;using namespace std )void f(int a, int b { ;cout<< a + b << endl } )(int main { ;void (*pf)(int, int) = f; // hijk =&f ;void (**ppf)(int, int) = &pf ;cout<<(**ppf == *ppf) << endl // true cout<<( &(**ppf) == &(*ppf) ) << endl; // false // ppf = f; // error ;)(_getch }
Output (BDS 2006): 1 0
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﻨﻬﺎ pointerاول )ﻳﻌﻨﻲ (&fﺑﺎ ﺧﻮد ﺗﺎﺑﻊ ﻣﺴﺎوي اﺳﺖ. ﺑﺮاي ﺗﻌﺮﻳﻒ pointerﻧﻤﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;)void pf(int, int
122
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﻪ ﻳﻚ ﺗﺎﺑﻊ را ﺑﻪ ﻋﻨﻮان ﭘﺎراﻣﺘﺮpointer اﻣﺎ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ. ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪprototype ﭼﻮن اﻳﻦ ﻳﻚ :ﺗﺎﺑﻌﻲ دﻳﮕﺮ ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﻣﻲﺗﻮاﻧﻴﻢ از اﻳﻦ ﺷﻜﻞ اﺳﺘﻔﺎده ﻛﻨﻴﻢ ﻣﺜﻞ اﻳﻦ #include <conio.h> #include <iostream> using namespace std; void f(int a, void g(int)) { cout<< a << endl; // output: 3 g(a); } void g(int b) { cout<< b * b; // output: 9 } int main() { f(3,g); _getch(); }
Output: 3 9
. را ﻓﺮاﺧﻮاﻧﺪهg() ،pointer ﺑﺎ اﻳﻦf() . ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن داده ﺷﺪهg() ي ﺑﻪ ﺗﺎﺑﻊpointer در اﻳﻦ ﻣﺜﺎل :اﻳﻦ ﻫﻢ ﻣﺜﺎﻟﻲ ﭘﻴﭽﻴﺪه ﺗﺮ #include <conio.h> #include <iostream> using namespace std; void g(int b) { cout<< b*b; } void L( void f(int a, void g(int)) ) { f(3,g); } void f(int a, void g(int)) { cout<< a << endl; g(a); } int main() { L(f); _getch(); }
123
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: 3 9
ا f() 01ﺑﻌﺪ از )( Lﺗﻌﺮﻳﻒ ﺷﺪه f() ،L() ،را ﻓﺮا ﻣﻲﺧﻮاﻧﺪ و ﻣﺸﻜﻠﻲ وﺟﻮد ﻧﺪارد .در اﻳﻦ ﺟﺎ ﻳﻚ ﻧﻜﺘﻪي ﻣﻬﻢ ﻫﺴﺖ و آن اﻳﻦ ﻛﻪ در ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ)( ،Lدو gﻇﺎﻫﺮ ﺷﺪه اﺳﺖ ﻛﻪ gي دوم ﺑﺎ gي اول ﻣﺘﻔﺎوت اﺳﺖ .آن gﻛﻪ در ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎ ﻣﻲﺑﻴﻨﻴﺪ ﻧﺎدﻳﺪه ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد و در ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ ﻧﻤﻲﺷﻮد از آن اﺳﺘﻔﺎده ﻛﺮد. ﺑﻨﺎﺑﺮاﻳﻦ اﮔﺮ ﺗﻌﺮﻳﻒ )( Lرا ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻧﻤﻲﺷﻮد: ) ))void L( void (*f)(int a, void gg(int { ;)f(3,gg }
pointerﺑﻪ آراﻳﻪﻫﺎ ﻫﻢ ﻣﺜﻞ pointerﺑﻪ ﺗﺎﺑﻊﻫﺎﺳﺖ .ﺑﺮاي آﺷﻨﺎﻳﻲ ﺑﺎ آنﻫﺎ ﻣﺜﺎلﻫﺎي زﻳﺮ را ﺑﻪ دﻗﺖ ﺑﺮرﺳﻲ ﻛﻨﻴﺪ. ﻣﺜﺎل زﻳﺮ در Visual C++اﺟﺮا ﻧﻤﻲﺷﻮد و در compilerﻫﺎي Borlandﻫﻢ ﻳﻚ warningدارد: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;]int a[20 ;)cout<< (a == &a ;)(_getch }
Output (BDS 2006): 1
ﻣﺜﺎل زﻳﺮ آراﻳﻪاي از آراﻳﻪﻫﺎ را ﺑﻪ ﻛﺎر ﻣﻲﺑﺮد: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;}int a[5] = {1,2,3,4,5 ;}int b[5] = {6,7,8,9,10 ;}int (*pa[4])[5] = { &a, &b, &a, &b )for(int i = 0; i < 4; i++ { )for(int j = 0; j < 5; j++ ;]cout<< (*pa[i])[j ;cout<< endl
124
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } _getch(); }
Output: 12345 678910 12345 678910
& ﻣﺴﺎوي اﺳﺖ در ﻣﺜﺎل ﺑﺎﻻ ﻧﻤﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﺟﺎيa ﺑﺎa ﺑﺎ اﻳﻦ ﻛﻪ در ﻣﺜﺎل ﭘﻴﺸﻴﻦ دﻳﺪﻳﻢ ﻛﻪ int (*pa[4])[5] = { &a, &b, &a, &b};
:ﺑﻨﻮﻳﺴﻴﻢ int (*pa[4])[5] = { a, b, a, b};
:در ﻣﺜﺎل زﻳﺮ آراﻳﻪﻫﺎ را ﺑﻪ ﻋﻨﻮان ﭘﺎراﻣﺘﺮ ﺑﻪ ﻛﺎر ﮔﺮﻓﺘﻪام #include <conio.h> #include <iostream> using namespace std; #pragma argsused void f(double a[20], int b[]) { char c[12]; cout<< typeid(a).name() << endl; // double * cout<< typeid(b).name() << endl; // int * cout<< typeid(c).name() << endl; // char [12] a = new double [2]; // a is parameter // c = new char [2]; // error } int main() { double a[5] = {1,2,3,4,5}; int b[5] = {6,7,8,9,10}; f(a,b); _getch(); }
Output(BDS 2006): double * int * char[12] Output(Visual C++ 2005): double * int * char [12]
125
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻧﻮع ﭘﺎراﻣﺘﺮ double* ،aاﺳﺖ ﻧﻪ ] .double[12ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺗﻮاﻧﺴﺘﻪاﻳﻢ ﻣﻘﺪار aرا ﻋﻮض ﻛﻨﻴﻢ .اﻣﺎ ﺧﻂ #pragma argsused
اﻳﻦ ﺧﻂ ﻳﻚ دﺳﺘﻮر preprocessorاﺳﺖ و ﻛﺎر آن ﻓﻘﻂ اﻳﻦ اﺳﺖ ﻛﻪ از اﻳﺠﺎد ﻧﻮﻋﻲ warningدر ﺗﺎﺑﻊ ﺑﻌﺪي ﺟﻠﻮﮔﻴﺮي ﻣﻲﻛﻨﺪ .ﺣﺬف آن ﺑﺎﻋﺚ ﻣﻲﺷﻮد warning ،BDS 2006ﻫﺎﻳﻲ در ﻣﻮرد ﻋﺪم اﺳﺘﻔﺎده از ﭘﺎراﻣﺘﺮﻫﺎ ﺻﺎدر ﻛﻨﺪ. ﺑﺮاي ﺟﻠﻮﮔﻴﺮي از ﺑﻪ ﻛﺎرﺑﺮدن اﺳﻢﻫﺎي ﻃﻮﻻﻧﻲ ِ pointerﻫﺎ ﻛﻠﻤﻪي ﻛﻠﻴﺪي typedefﺧﻴﻠﻲ ﺑﻪ درد ﻣﻲﺧﻮرد. ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std ; )typedef int (*WND)(int, int )int f(int a,int b { ;cout<< a + b ;return a } )(int main { ;WND pf =f ;)pf(2,5 ;)(_getch }
Output: 7
در اﻳﻦ ﻣﺜﺎل WNDﺑﻪ ﺟﺎي ) int(*)(int,intﺑﻪ ﻛﺎر ﻣﻲرود .ﻧﺤﻮهي ﺑﻪ ﻛﺎر ﮔﺮﻓﺘﻦ typedefدر اﻳﻦ ﻣﺜﺎل ﻣﻬﻢ اﺳﺖ ﭼﻮن ﻣﻤﻜﻦ اﺳﺖ ﺑﻪ ﻧﻈﺮ ﺑﺮﺳﺪ اﻳﻦ ﻃﻮري ﺑﺎﻳﺪ ﻧﻮﺷﺖ: typedef int (*)(int, int) WND
ﻛﻪ اﻳﻦ ﻏﻠﻂ اﺳﺖ.
126
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ $ @ #include؟ )( getchﺗﺎﺑﻌﻲ اﺳﺖ ﻛﻪ در ﻫﻤﻪي ﺑﺮﻧﺎﻣﻪﻫﺎ آن را ﺑﻪ ﻛﺎر ﻣﻲﮔﻴﺮﻳﻢ .اﻣﺎ اﻳﻦ ﺗﺎﺑﻊ ﻛﺠﺎ ﻣﻌﺮﻓﻲ ﺷﺪه اﺳﺖ؟ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ در ﻓﺎﻳﻞ دﻳﮕﺮي .اﺳﻢ آن ﻓﺎﻳﻞ conio.hاﺳﺖ .ﺑﺎ #includeﻣﺤﺘﻮﻳﺎت ﻓﺎﻳﻞ conio.hرا ﺑﻪ ﻓﺎﻳﻞ ﺧﻮدﻣﺎن اﺿﺎﻓﻪ ﻣﻲﻛﻨﻴﻢ اﻧﮕﺎر اﺻﻼ ﻣﺤﺘﻮﻳﺎت آن را ﺧﻮدﻣﺎن ﺗﺎﻳﭗ ﻛﺮده ﺑﺎﺷﻴﻢ conio.h .و ﻓﺎﻳﻞﻫﺎي دﻳﮕﺮ ﻣﺜﻞ آن )از ﺟﻤﻠﻪ iostream) header fileﻧﺎﻣﻴﺪه ﻣﻲﺷﻮﻧﺪ. ﺧﻮد ﻣﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ header fileدرﺳﺖ ﻛﻨﻴﻢ و از آن اﺳﺘﻔﺎده ﻛﻨﻴﻢ .ﺑﺮاي اﻳﻦ ﻛﺎر ﻣﺤﻠﻲ از hard diskﺧﻮد را ﻣﺸﺨﺺ ﻛﻨﻴﺪ .ﻣﺜﻼ ") ."C:\Headerاز My Computerوارد دراﻳﻮ Cﺷﻮﻳﺪ و ﻳﻚ folderﺟﺪﻳﺪ ﺑﻪ اﺳﻢ headerدرﺳﺖ ﻛﻨﻴﺪ
( .ﺑﻌﺪ در header ِ folderﻳﻚ ﻓﺎﻳﻞ ﺑﻪ اﺳﻢ myheader.txtدرﺳﺖ
ﻛﻨﻴﺪ:
ﻓﺎﻳﻞ myheader.txtرا ﺑﺎز ﻛﻨﻴﺪ و در آن ﺑﻨﻮﻳﺴﻴﺪ: )][unsigned GetLength(char a { ;int i = 0 ;)for(; a[i] != 0; i++ ;return i } ﻣﺜﻞ
127
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ﺣﺎﻻ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را در compilerﺧﻮدﺗﺎن ﺑﻨﻮﻳﺴﻴﺪ و اﺟﺮا ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std >#include <C:\header\myheader.txt )(int main { ;)"cout<< GetLength("hello ;)(_getch }
Output: 5
اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻃﻮل رﺷﺘﻪي " "helloرا ﭼﺎپ ﻣﻲﻛﻨﺪ .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ >#include <C:\header\myheader.txt
را ﺑﺎ ﻫﺮ ﻳﻚ از ﺧﻄﻮط زﻳﺮ ﻣﻲﺷﻮد ﺟﺎﻳﮕﺰﻳﻦ ﻛﺮد: "#include "C:\header\myheader.txt >#include <C:\\header\\myheader.txt "#include "C:\\header\\myheader.txt
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ دﺳﺘﻮرﻫﺎﻳﻲ ﻛﻪ ﺑﺎ #ﺷﺮوع ﻣﻲﺷﻮﻧﺪ دﺳﺘﻮرﻫﺎي preprocessorﻫﺴﺘﻨﺪ .اﻳﻦ ﺟﻮر دﺳﺘﻮرﻫﺎ ﺑﺎﻳﺪ از اول ﺧﻂ ﺷﺮوع ﺷﻮﻧﺪ و آﺧﺮﺷﺎن »;« ﻧﻤﻲﺧﻮاﻫﻨﺪ. اﻣﺎ ﻧﻮﺷﺘﻦ آدرس ﻛﺎﻣﻞ myheader.txtﻛﺎر ﭘﺮ زﺣﻤﺘﻲ اﺳﺖ و ﺑﻪ ﻋﻼوه اﮔﺮ ﻣﺤﻞ آن ﺗﻐﻴﻴﺮ ﻛﻨﺪ ﺑﺎﻳﺪ ﺑﺮﻧﺎﻣﻪ را ﺗﻐﻴﻴﺮ داد .ﺑﺮاي اﻳﻦ ﻛﻪ ﻧﻮﺷﺘﻦ ﻓﻘﻂ اﺳﻢ ﻓﺎﻳﻞ ﻛﺎﻓﻲ ﺑﺎﺷﺪ ﺑﺎﻳﺪ در ﺟﺎي ﻣﻨﺎﺳﺐ در ﺗﻨﻈﻴﻤﺎت compilerﻣﺤﻞ ﻓﺎﻳﻞ را اﺿﺎﻓﻪ ﻛﻨﻴﻢ .ﺑﺮاي اﻳﻦ ﻛﺎر در BDS 2006اﻳﻦ ﻣﺴﻴﺮ را ﻃﻲ ﻛﻨﻴﺪ: Project | Options… | C++ Compiler | Paths and Defines و ﺑﻌﺪ ﻋﺒﺎرت ;C:\headerرا ﺑﻪ ﻧﻮار Include search pathاﺿﺎف ﻛﻨﻴﺪ. در VC++ 2005 Express Editionاﻳﻦ ﻣﺴﻴﺮ را ﻃﻲ ﻛﻨﻴﺪ: Tools | Options… | Projects and Solutions | VC++ Directories |Show directories for: Include files و ﺧﻂ C:\headerرا اﺿﺎﻓﻪ ﻛﻨﻴﺪ .ﺑﻌﺪ از اﻧﺠﺎم اﻳﻦ ﻛﺎرﻫﺎ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ را ﺑﻪ اﻳﻦ ﺻﻮرت ﺑﻨﻮﻳﺴﻴﻢ:
128
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ >#include <conio.h >#include <iostream ;using namespace std >#include <myheader.txt )(int main { ;)"cout<< GetLength("hello ;)(_getch }
Output: 5
ﭘﺴﻮﻧﺪ header fileﻫﺎ در C++دﻟﺨﻮاه اﺳﺖ وﻟﻲ ﻣﻌﻤﻮﻻ .hﻳﺎ .hppاﻧﺘﺨﺎب ﻣﻲﺷﻮد .ﻣﻦ در اﻳﻦ ﻣﺜﺎل .txtرا اﻧﺘﺨﺎب ﻛﺮدهام. ﺑﻪ ﺷﻜﻞ >#include <myheader.txt
angle-bracket formﻣﻲﮔﻮﻳﻨﺪ و ﺑﻪ ﺷﻜﻞ "#include "myheader.txt
quoted formﻣﻲﮔﻮﻳﻨﺪ .ﻓﺮق آنﻫﺎ اﻳﻦ اﺳﺖ ﻛﻪ در دوﻣﻲ myheader.txtرا اول در ﻫﻤﺎن ﻣﻜﺎﻧﻲ ﻛﻪ ﻓﺎﻳﻞ ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﻫﺴﺖ ﺟﺴﺖ و ﺟﻮ ﻣﻲﻛﻨﺪ و اﮔﺮ ﭘﻴﺪا ﻧﺸﺪ ﺑﻌﺪ ﺑﻪ ﺳﺮاغ ﺟﺎﻫﺎي دﻳﮕﺮ ﺑﺮاي ﺟﺴﺖ و ﺟﻮ ﻣﻲرود. در ﻣﺤﻠﻲ ﻛﻪ compilerدر hard diskﻧﺼﺐ ﺷﺪه folderي ﺑﺎ ﻧﺎم includeﭘﻴﺪا ﻣﻲﺷﻮد ﻛﻪ header fileﻫﺎي ﻣﻌﻤﻮل در C++آن ﺟﺎﺳﺖ .ﺑﺮاي آﺷﻨﺎﻳﻲ ﺑﺎ ﺗﺎﺑﻊﻫﺎي ﻣﻮﺟﻮد در اﻳﻦ header fileﻫﺎ از helpدر compilerﺧﻮدﺗﺎن ﻛﻤﻚ ﺑﮕﻴﺮﻳﺪ.
* را " default
ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )void f(int a, int b = 2 { ;cout<< a <<endl
129
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;cout<< b <<endl << endl } )(int main { ;)f(1,3 ;)f(7 ;)(_getch }
Output: 1 3 7 2
در ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎ ﺑﻪ ﺟﺎي int bﻧﻮﺷﺘﻪام .int b=2اﻳﻦ ﺑﻪ ﻣﻦ اﺟﺎزه ﻣﻲدﻫﺪ ﻛﻪ در )( mainﺑﻨﻮﻳﺴﻴﻢ ;) f(7و fﭘﺎراﻣﺘﺮ دوم را ﺑﻪ ﻃﻮر 2 ،defaultﺑﻪ ﺣﺴﺎب ﻣﻲآورد .اﻣﺎ اﮔﺮ آرﮔﻮﻣﺎن دوم را ﻫﻢ ﻣﺸﺨﺺ ﻛﻨﻢ ﻫﻤﺎن آرﮔﻮﻣﺎن در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد ﻣﺜﻞ ;) f(1,3در ﺑﺎﻻ. اﮔﺮ ﻳﻚ ﭘﺎراﻣﺘﺮ ﺗﺎﺑﻊ default ،ﺷﺪ؛ ﻫﻤﻪي ﭘﺎراﻣﺘﺮﻫﺎي ﺑﻌﺪي ﻫﻢ ﺑﺎﻳﺪ defaultﺷﻮﻧﺪ. >#include <conio.h >#include <iostream ;using namespace std )void f(int a = 1, int b = 2, int c = 3 { ;cout<< a << b << c << endl } )(int main { ;)(f ;)f(7 ;)f(7,8 ;)f(7,8,9 ;)(_getch }
Output: 123 823 893
ﻣﻘﺪار defaultرا ﻣﻲﺷﻮد در prototypeﻫﻢ ﻗﺮار داد: >#include <conio.h >#include <iostream
130
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; void f(int x = 2); void f(int x) { cout<< x; } int main() { f(); _getch(); }
Output: 2
prototype در ﺿﻤﻦ اﮔﺮ ﭼﻨﺪ. ﻧﻪ در ﻫﺮ دوprototype ﻳﺎ ﺑﺎﻳﺪ در ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ ﺑﺎﺷﺪ ﻳﺎ درdefault ﻣﻘﺪار در ﻣﺜﺎل ﻗﺒﻞ ﺑﻪ ﺟﺎي. ﻗﺮار دادprototype را ﻧﻤﻲﺷﻮد در ﺑﻴﺶ از ﻳﻚdefault داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻣﻘﺪار void f(int x = 2);
:ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ void f(int = 2);
دنoverload
ﺑﺎ اﺳﺘﻔﺎده از ﻧﻮع آرﮔﻮﻣﺎنﻫﺎ و ﺗﻌﺪادcompiler ﻣﻲﺷﻮد دو ﺗﺎ ﺗﺎﺑﻊ ﻫﻢ اﺳﻢ ﺑﺎ ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎي ﻣﺘﻔﺎوت داﺷﺖ و : اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ. ﺗﺎﺑﻊ ﻣﻨﺎﺳﺐ را ﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪ،آنﻫﺎ #include <conio.h> #include <iostream> using namespace std; #pragma argsused void f(int a) { cout<< "first f" << endl; } #pragma argsused void f(char a) { cout<< "second f" << endl; } int main()
131
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { ;)'f('a ;)'f((int)'a ;)(_getch }
Output: second f first f
' char n\ 'aاﺳﺖ و )' f('aﺗﺎﺑﻊ دون را ﻓﺮا ﻣﻲﺧﻮاﻧﺪ .اﻣﺎ ' int n\ (int)'aاﺳﺖ و )' f((int)'aﺗﺎﺑﻊ اول را ﻓﺮا ﻣﻲﺧﻮاﻧﺪ. اﻣﺎ ﻧﻤﻲﺷﻮد دو ﺗﺎﺑﻊ ﺑﺎ اﺳﻢ و ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻚ ﺟﻮر داﺷﺖ ﺣﺘﻲ اﮔﺮ ﺧﺮوﺟﻲ آنﻫﺎ ﻣﺘﻔﺎوت ﺑﺎﺷﺪ .ﻣﻮاردي ﭘﻴﺶ ﻣﻲآﻳﺪ ﻛﻪ compilerاز اﻧﺘﺨﺎب ﺗﺎﺑﻊ ﻣﻨﺎﺳﺐ ﻋﺎﺟﺰ اﺳﺖ در اﻳﻦ ﻣﻮارد errorﺻﺎدر ﻣﻲﺷﻮد ﻣﺜﻼ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻏﻴﺮ ﻗﺎﺑﻞ اﺟﺮاﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std #pragma argsused )void f(int a, int b = 2 { ;cout<< "first f" << endl } #pragma argsused )void f(int a { ;cout<< "second f" << endl } )(int main { ;)f(2 ;)(_getch }
Output: Error
ﺑﻪ اﺳﺘﻔﺎده از ﻳﻚ ﻧﺎم ﺑﺮاي دو ﺗﺎﺑﻊ overloadﻛﺮدن ﻣﻲﮔﻮﻳﻨﺪ.
132
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ namespaceو using
ﻓﺮض ﻛﻨﻴﺪ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ اﺳﻢ را ﺑﺮاي دو ﺗﺎ ﺗﺎﺑﻊ ﻛﻪ ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻚ ﺟﻮر دارﻧﺪ ﺑﻪ ﻛﺎر ﺑﺒﺮﻳﻢ ﻳﺎ دو ﺗﺎ ﻣﺘﻐﻴﺮ ﻫﻢ اﺳﻢ داﺷﺘﻪ ﺑﺎﺷﻴﻢ .ﺑﺮاي اﻳﻦ ﻛﺎر ﻣﻲﺗﻮاﻧﻴﻢ از namespaceاﺳﺘﻔﺎده ﻛﻨﻴﻢ .ﺑﺮﻧﺎﻣﻪي زﻳﺮ دو namespaceدارد ﻛﻪ در ﻫﺮ ﻛﺪام ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﺎم aﺗﻌﺮﻳﻒ ﺷﺪه: >#include <conio.h >#include <iostream ;using namespace std namespace AA { ;int a = 2 } namespace BB { ;int a = 3 } )(int main { ;"cout<< AA::a << "\n ;"cout<< BB::a << "\n ;)(_getch }
Output: 2 3
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪ ﻣﺘﻐﻴﺮ aﺗﻌﺮﻳﻒ ﺷﺪه در namespace AAﻣﻲﻧﻮﻳﺴﻴﻢ .AA::aاﻳﻦ namespaceﻫﺎ را ﻧﻤﻲﺷﻮد در ﺗﺎﺑﻊ )( mainﺗﻌﺮﻳﻒ ﻛﺮد و ﺗﻌﺮﻳﻒ آنﻫﺎ ﺑﺎﻳﺪ در ﻓﻀﺎي ﻋﻤﻮﻣﻲ ﺑﺎﺷﺪ .ﻣﺜﺎل زﻳﺮ ﺟﺮﺋﻴﺎت ﺑﻴﺶ ﺗﺮي دارد: >#include <conio.h >#include <iostream ;using namespace std namespace AA { ;int a = 2 )(void f { ;a = 6 ;AA::a = 5 } } namespace BB { ;int a = 3 } )(int main
133
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { cout<< AA::a << "\n"; // output: 2 AA::f(); cout<< AA::a << "\n"; // output: 5 _getch(); }
Output: 2 5
و ﻫﻢ ازa ﻣﻲﺷﻮد ﻫﻢ ازa ﻗﺮار دارد ﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪnamespace AA ﻛﻪ درونf ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ در ﺗﺎﺑﻊ - ﺑﺎﻳﺪ ﻛﻠﻤﻪa ﺑﻨﻮﻳﺴﻴﻢAA::a ﻫﻢ ﺑﻪ ﺟﺎيnamespace AA اﮔﺮ ﺑﺨﻮاﻫﻴﻢ در ﺧﺎرج از. اﺳﺘﻔﺎده ﻛﺮدAA::a : را ﺑﻪ ﻛﺎر ﺑﺒﺮﻳﻢusing ي ﻛﻠﻴﺪي #include <conio.h> #include <iostream> using namespace std; namespace AA { int a = 2; } namespace BB { int a = 3; } int main() { using namespace AA; cout<< a << "\n"; // outpout: 2 cout<< AA::a << "\n"; // outpout: 2 cout<< BB::a << "\n"; // outpout: 3 _getch(); }
Output: 2 2 3 using را در ﻓﻀﺎي ﻋﻤﻮﻣﻲ ﻫﻢ ﻣﻲﺷﻮد ﻗﺮار داد و در اﻳﻦ ﺻﻮرت ﻫﻤﻪي ﺗﺎﺑﻊﻫﺎي ﺑﻌﺪ از ﻣﺤﻞ ﻧﻮﺷﺘﻦusing
.ﻣﻲﺗﻮاﻧﻨﺪ از آن اﺳﺘﻔﺎده ﻛﻨﻨﺪ : را ﻧﺸﺎن ﻣﻲدﻫﺪnamespace ﻣﺜﺎل زﻳﺮ ﻛﺎﺑﺮدي ﺟﺎﻟﺐ از #include <iostream> using namespace std; namespace ABC {
134
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <conio.h> } int main() { char ch = ABC::_getch(); cout<< ch; ABC::_getch(); }
Output: a
:ﺣﺎﻻ ﻣﻲﺗﻮاﻧﻴﺪ ﺑﺮﻧﺎﻣﻪ زﻳﺮ را ﺗﻮﺟﻴﻪ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> int main() { std::cout<< "hello"; _getch(); }
Output: hello
: ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ. ﺑﺪون اﺳﻢ ﺑﻪ ﺣﺴﺎب آوردnamespace ﻓﻀﺎي ﻋﻤﻮﻣﻲ را ﻣﻲﺷﻮد ﻳﻚ #include <conio.h> #include <iostream> using namespace std; int a = 1; int b = 2; int main() { int a = 3; cout<< a << endl; cout<< ::a << endl; cout<< b << endl; cout<< ::b << endl; { // block int a = 4; cout<< ::a; } // end of block _getch();
// // // //
output: output: output: output:
3 1 2 2
// output: 1
}
Output: 3 1
135
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 2 2 1
در اﻳﻦ ﻣﺜﺎل ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻫﻤﻴﺸﻪ ﻣﻨﻈﻮر از ::aﻣﺘﻐﻴﺮي اﺳﺖ ﻛﻪ در ﻓﻀﺎي ﻋﻤﻮﻣﻲ ﺗﻌﺮﻳﻒ ﺷﺪه اﺳﺖ .ﺑﻪ ﻋﻼوه ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در ﻫﺮ ﺑﻠﻮك ﻣﻲﺷﻮد ﻣﺘﻐﻴﺮي ﻫﻢ اﺳﻢ ﺑﺎ ﻣﺘﻐﻴﺮﻫﺎي ﺗﻌﺮﻳﻒ ﺷﺪه در ﺧﺎرج آن ﺗﻌﺮﻳﻒ ﻛﺮد.
try ،throwو catch
ﻳﻜﻲ از راهﻫﺎي ﭘﻴﺪا ﻛﺮدن ﺧﻄﺎ ،ﻳﺎ ﺑﺮﻃﺮف ﻛﺮدن ﺧﻮدﻛﺎر آن؛ اﺳﺘﻔﺎده از exceptionاﺳﺖ .وﻗﺘﻲ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﺑﺎ ﻣﺸﻜﻠﻲ ﻣﻮاﺟﻪ ﻣﻲﺷﻮد ﻛﻪ ﻗﺎدر ﺑﻪ اداﻣﻪي ﻛﺎر ﻧﻴﺴﺖ ﺑﺎﻳﺪ ﭼﻪ اﺗﻔﺎﻗﻲ ﺑﻴﻔﺘﺪ؟ ﺑﺪون ﺷﻚ اﻳﻦ ﻛﻪ اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﺑﺪون ﻫﻴﭻ ﻫﺸﺪاري ،ﻧﺎﮔﻬﺎن ﭘﺎﻳﺎن ﻳﺎﺑﺪ اﺗﻔﺎق ﺧﻮﺑﻲ ﻧﻴﺴﺖ .ﻣﻲﺷﻮد از exceptionﺑﺮاي اﻳﺠﺎد راه ﺣﻞ ﻣﻨﺎﺳﺐ ﺗﺮي اﺳﺘﻔﺎده ﻛﺮد. وﻗﺘﻲ ﻳﻚ exceptionرا ﺑﻪ وﺟﻮد ﻣﻲآورﻳﻢ ﻣﻲﮔﻮﻳﻴﻢ exceptionرا throwﻛﺮدهاﻳﻢ و ﺑﻪ ﺟﺎي اﻳﻦ ﻛﻪ ﺑﮕﻮﻳﻴﻢ exceptionدرﺳﺖ ﺷﺪه ﻣﻲﮔﻮﻳﻴﻢ raise ،exceptionﺷﺪه اﺳﺖ )واﻗﻌﺎ ﻛﻪ ﭼﻪ ﻗﺪر ﻣﻦ ﻓﺎرﺳﻲ را دوﺳﺖ دارم
(.
وﻗﺘﻲ ﻳﻚ throw ،exceptionﻣﻲﺷﻮد ِ control ،ﺑﺮﻧﺎﻣﻪ ﺷﺮوع ﻣﻲﻛﻨﺪ ﺑﻪ ﺑﺮﮔﺮداﻧﺪن ﺣﺎﻓﻈﻪﻫﺎي stackو ﺧﺮوج از ﺗﺎﺑﻊﻫﺎ ﺗﺎ اﻳﻦ ﻛﻪ ﺑﻪ )( mainﺑﺮﺳﺪ و اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﻣﺘﻮﻗﻒ ﺷﻮد ﻣﮕﺮ اﻳﻦ ﻛﻪ در ﺟﺎﻳﻲ ﺑﻪ وﺳﻴﻠﻪي tryو catchﺟﻠﻮي آن ﮔﺮﻓﺘﻪ ﺷﻮد .ﻓﺮﻣﻮل throwﺑﻪ اﻳﻦ ﺻﻮرت اﺳﺖ: ;] ydZz Z\ ]kl5mاز ه] 4iع[ throw
ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(void f { ;int a ;cin>> a )if (a < 0 ;"throw "negative not allowed\n } )(int main {
136
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ BEGIN: try { ;)(f } )catch(char* c { ;cout<< c ;goto BEGIN } ;)(_getch }
Output: -5 negative not allowed -7 negative not allowed 4
BDS 2006ﺑﻌﺪ از ﻫﺮ exceptionﻛﻪ throwﻣﻲﺷﻮد ﻳﻚ Message Boxاﻳﺠﺎد ﻣﻲﻛﻨﺪ ﻣﺜﻞ اﻳﻦ:
در اﻳﻦ ﺣﺎﻟﺖ Continueرا ﻓﺸﺎر دﻫﻴﺪ. در اﻳﻦ ﻣﺜﺎل ﺗﺎﺑﻊ )( fدر ﻳﻚ »ﺑﻠﻮك «tryﻗﺮار ﮔﺮﻓﺘﻪ ﻛﻪ ﻫﻨﮕﺎم raiseﺷﺪن ،exceptionﺑﺸﻮد آن را catchﻛﺮد» .ﺑﻠﻮك «catchﺑﺎﻳﺪ ﺑﻼﻓﺎﺻﻠﻪ ﺑﻌﺪ از ﺑﻠﻮك tryﻗﺮار ﺑﮕﻴﺮد و tryﺣﺘﻤﺎ ﺑﺎﻳﺪ }{ داﺷﺘﻪ ﺑﺎﺷﺪ. ﻧﻜﺘﻪي دﻳﮕﺮ آن اﺳﺖ ﻛﻪ BEGIN:ﻧﻤﻲﺗﻮاﻧﺪ درون ﺑﻠﻮك tryﻗﺮار ﺑﮕﻴﺮد. وﻗﺘﻲ در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ ﻳﻚ ﻋﺪد ﻣﻨﻔﻲ وارد ﻣﻲﺷﻮد ﻳﻚ exceptionﺑﺎ ﻧﻮع * throw ،charﻣﻲﺷﻮد .ﺑﺎ throwﺷﺪن control ،ﺑﺮﻧﺎﻣﻪ exceptionرا )ﻛﻪ ﻧﻮع * charدارد( ﺑﺮﻣﻲدارد )و در ﺟﻴﺐ ﺧﻮد ﻣﻲﮔﺬارد ( و از ﺗﺎﺑﻊ fﺧﺎرج ﻣﻲﺷﻮد وﻟﻲ در ﺑﻠﻮك tryﻣﺘﻮﻗﻒ ﻣﻲﺷﻮد و exceptionدر ﺑﻠﻮك ) catchﭘﺲ از ﺑﺎزرﺳﻲ ﺑﺪﻧﻲ( ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد )و ِ controlﺑﺮﻧﺎﻣﻪ ﺑﻪ زﻧﺪان ﻣﻲاﻓﺘﺪ( .ﻗﺮار اﺳﺖ در ﺑﻠﻮك catchاﻋﻤﺎل ﻻزم ﺑﺮاي ﺑﺮﻃﺮف ﺷﺪن ﻣﺸﻜﻞ اﻧﺠﺎم ﺷﻮد .دراﻳﻦ ﺟﺎ ﻣﻦ ﻓﻘﻂ exceptionرا ﭼﺎپ ﻛﺮدهام و ﺑﺎ gotoﺑﺮﻧﺎﻣﻪ را از ﻧﻮ آﻏﺎز ﻛﺮدهام.
137
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
: ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ. ﻗﺮار ﺑﺪﻫﻴﻢtry ﺑﻌﺪ از ﻳﻚ ﺑﻠﻮك، را ﭘﺸﺖ ﺳﺮ ﻫﻢcatch ﻣﻲﺗﻮاﻧﻴﻢ ﭼﻨﺪ ﺑﻠﻮك #include <conio.h> #include <iostream> using namespace std; void i(int a) { if(a == -1) throw "too bad"; } void h(int a) { if(a == 0) throw 97; } void g(int a) { if(a == 1) throw 'a'; } void f() { int a; cin>> a; g(a); h(a); i(a); } int main() { BEGIN: try { f(); } catch(int) { cout<< "int object\n"; goto BEGIN; } catch(char& ch) { cout<< "char object\n"; goto BEGIN; } catch(...) { cout<< "other object\n"; goto BEGIN; } _getch(); }
138
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: -1 other object 0 int object 1 char object 2
)…( catchﻫﺮ ﮔﻮﻧﻪ exceptionرا ﻛﻪ ﺗﺎ ﺑﻪ ﺣﺎل ﮔﺮﻓﺘﻪ ﻧﺸﺪه ﺑﺎﺷﺪ ﻣﻲﮔﻴﺮد .ﻣﺜﺎل ﺑﻌﺪي ﻛﻤﻲ ﻛﺎﺑﺮدي ﺗﺮ اﺳﺖ .در اﻳﻦ ﻣﺜﺎل از newﺑﺮاي ﮔﺮﻓﺘﻦ ﺣﺎﻓﻈﻪ اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ .اﮔﺮ newﻧﺘﻮاﻧﺪ ﺣﺎﻓﻈﻪي ﻣﻮرد ﻧﻴﺎز را اﺧﺬ ﻛﻨﺪ ﺻﻔﺮ ﺑﺮﻣﻲﮔﺮداﻧﺪ )ﻣﻌﻤﻮﻻ در ﻣﻮرد ﺻﻔﺮ ﺳﺨﺖ ﮔﻴﺮيﻫﺎي ﻣﺮﺑﻮط ﺑﻪ ﺗﺒﺪﻳﻞ ﻧﻮع ﻛﻢ ﺗﺮ اﺳﺖ(: >#include <conio.h >#include <iostream ;using namespace std )(int main { BEGIN: ;char* c try { ;]c = new char [99999999 )if(c == 0 ;throw 0 } )catch(... { ;"cout<< "Low Memory } ;)(_getch }
Output: empty
staticدرون & ' B
ﻣﺘﻐﻴﺮﻫﺎﻳﻲ ﻛﻪ در ﻳﻚ ﺗﺎﺑﻊ ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮﻧﺪ ﺑﺎ ﺧﺮوج از آن ﻧﺎﺑﻮد ﻣﻲﺷﻮﻧﺪ .ﺑﺮاي اﻳﻦ ﻛﻪ ﻧﺎﺑﻮد ﻧﺸﻮﻧﺪ ﻣﻮﻗﻊ ﺗﻌﺮﻳﻒ ﺑﺎﻳﺪ از ﻛﻠﻤﻪي ﻛﻠﻴﺪي staticاﺳﺘﻔﺎده ﻛﻨﻴﻢ .اﮔﺮ ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﻪ ﻃﻮر staticﺗﻌﺮﻳﻒ ﺷﻮد در اوﻟﻴﻦ ﻓﺮاﺧﻮاﻧﻲ ﺗﺎﺑﻊ ،اﻳﺠﺎد و initializeﻣﻲﺷﻮد )ﻳﻌﻨﻲ ﻣﻘﺪار اوﻟﻴﻪ داده ﻣﻲﺷﻮد( و ﺗﺎ آﺧﺮ ﺑﺮﻧﺎﻣﻪ از ﺑﻴﻦ ﻧﻤﻲرود .اﮔﺮ ﻣﻘﺪار اوﻟﻴﻪ
139
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﺸﺨﺺ ﻧﺸﺪه ﺑﺎﺷﺪ ،ﻣﻘﺪار اوﻟﻴﻪ ،ﺻﻔﺮ در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد .ﻣﺜﺎل زﻳﺮ اﺳﺘﻔﺎده از ﻳﻚ ﻣﺘﻐﻴﺮ staticرا ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std
-1;// executed only once ;"even\n
"<<
;"odd\n
" <<
)i < 10; i++
)(void f { = static int a ;a++ )if(a % 2 == 0 cout<< a else cout<< a } )(int main { ;for(int i = 0 ;)(f ;)(_getch }
Output: 0 even 1 odd 2 even 3 odd 4 even 5 odd 6 even 7 odd 8 even 9 odd
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ static int a = -1ﺗﻨﻬﺎ ﻳﻚ ﺑﺎر اﺟﺮا ﻣﻲﺷﻮد و در ﻓﺮاﺧﻮاﻧﻲﻫﺎي ﻣﻜﺮر )( fدوﺑﺎره ﺑﻪ وﺟﻮد ﻧﻤﻲآﻳﺪ و ﻣﻘﺪار اوﻟﻴﻪي آن ﻳﻌﻨﻲ -1ﻫﻢ ﺗﻨﻬﺎ ﻳﻚ ﺑﺎر ﺑﻪ آن داده ﻣﻲﺷﻮد .در ﻓﺮاﺧﻮاﻧﻲﻫﺎي ﺑﻌﺪي ِ )(f
ﻣﻘﺪار ﻗﺒﻠﻲ aﺑﺎ a++ﺗﻐﻴﻴﺮ ﻣﻲﻛﻨﺪ .ﺑﺮ ﺣﺴﺐ اﻳﻦ ﻛﻪ aزوج ﻳﺎ ﻓﺮد اﺳﺖ ﭘﻴﺎم ﻣﻨﺎﺳﺐ ﭼﺎپ ﻣﻲﺷﻮد.
register
CPU register 1 registerﺣﺎﻓﻈﻪاي در CPUاﺳﺖ ﻛﻪ ﻣﻌﻤﻮﻻ ﺑﺮاي ﺳﺮﻋـﺖ ﺑﺎﻻﺗﺮ در ﺑﺮﺧﻲ ﺟﺎﻫﺎي ﺑﺮﻧﺎﻣﻪ اﺳﺘﻔﺎده ﻣﻲﺷﻮد .ﻣﺜﻼ ﺷﻤﺎرﺷﮕﺮ ﺣﻠﻘﻪي forرا ﺑﺮاي ﺳﺮﻋـﺖ ﺑﻴﺶ ﺗﺮ ﻣﻲﺷﻮد از CPU register
140
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﮔﺮﻓﺖ ﻧﻪ از .RAMﺑﺮاي ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ در registerﻛﺎﻓﻲ اﺳﺖ ﻗﺒﻞ از ﺗﻌﺮﻳﻒ ﻋﺎدي ،ﻛﻠﻤﻪي ﻛﻠﻴﺪي registerرا ﻗﺮار دﻫﻴﻢ .ﻣﺜﻼ ;register int i = 0
اﻣﺎ compilerﻫﺎي Microsoftﺑﻪ آن ﺑﻲ ﺗﻮﺟﻪ ﻫﺴﺘﻨﺪ و registerﻫﺎ را ﺧﻮدﺷﺎن اﻧﺘﺨﺎب ﻣﻲﻛﻨﻨﺪ. compilerﻫﺎي Borlandﻫﻢ ﺑﺮاي ﺗﺨﺼﻴﺺ ﺣﺎﻓﻈﻪ از registerﻧﺎز زﻳﺎدي دارﻧﺪ .ﺑﺮاي ﻫﻤﻴﻦ ﻣﻲﺗﻮاﻧﻴﺪ آن را ﻓﺮاﻣﻮش ﻛﻨﻴﺪ.
auto
داﺧﻞ ﻫﺮ ﺑﻠﻮك ﻣﻲﺷﻮد ﺑﻪ ﺟﺎي ;int i = 5
ﻧﻮﺷﺖ: ;auto int i = 5
ﻛﻪ ﻓﺮﻗﻲ ﺑﺎ ﻗﺒﻠﻲ ﻧﺪارد ﻓﻘﻂ اﻧﮕﺎر ﺗﺄﻛﻴﺪ ﻛﺮدهاﻳﻢ ﺑﺎ ﺧﺮوج از ﺑﻠﻮك ﻣﺘﻐﻴﺮ ﺑﻪ ﻃﻮر اﺗﻮﻣﺎﺗﻴﻚ از ﺑﻴﻦ ﻣﻲرود.
inline
ﺑﻪ اﻳﻦ ﺗﺎﺑﻊ ﻧﮕﺎه ﻛﻨﻴﺪ: )int plus(int a,int b { ;return a + b }
ﺣﺎل اﮔﺮ در )( mainﺑﻨﻮﻳﺴﻴﻢ: ;)int b = f(2,3
ﻣﻨﻈﻮر ﻣﺎ اﻳﻦ اﺳﺖ ﻛﻪ ;int b = 2 + 3
اﻣﺎ ﻣﻮﻗﻊ اﺟﺮاي ;) int b=f(2,3ﭼﻨﺪ ﻣﺘﻐﻴﺮ ﺑﻪ وﺟﻮد ﻣﻲآﻳﻨﺪ ،ﻣﻘﺪار دﻫﻲ ﻣﻲﺷﻮﻧﺪ و ﺑﻌﺪ ﻧﺎﺑﻮد ﻣﻲﺷﻮﻧﺪ .اﻳﻦ ﻫﺰﻳﻨﻪي ﺳﻨﮕﻴﻨﻲ از ﻧﻈﺮ زﻣﺎن و ﺣﺎﻓﻈﻪ اﺳﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﻪ compilerﭘﻴﺶ ﻧﻬﺎد ﻛﻨﻴﻢ از اﻳﺠﺎد اﻳﻦ ﻣﺘﻐﻴﺮﻫﺎي
141
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﺿﺎﻓﻪ ﺻﺮف ﻧﻈﺮ ﻛﻨﺪ و ; int b=2+3را در ﻧﻈﺮ ﺑﮕﻴﺮد ﻣﻲﺗﻮاﻧﻴﻢ از ﻛﻠﻤﻪي ﻛﻠﻴﺪي inlineﻗﺒﻞ از ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ اﺳﺘﻔﺎده ﻛﻨﻴﻢ و ﺑﻨﻮﻳﺴﻴﻢ: )inline int plus(int a,int b { ;return a + b }
اﻟﺒﺘﻪ اﻳﻦ ﻓﻘﻂ ﻳﻚ ﭘﻴﺶ ﻧﻬﺎد ﺑﻪ compilerاﺳﺖ .در compilerﻫﺎي Microsoftﺑﻪ ﺟﺎي inlineاز ﻛﻠﻤﻪي ﻛﻠﻴﺪي __forceinlineﻫﻢ ﻣﻲﺷﻮد اﺳﺘﻔﺎده ﻛﺮد ﻛﻪ ﻓﺸﺎر ﺑﻴﺶ ﺗﺮي ﺑﻪ compilerﺑﺮاي inline ﻛﺮدن ﺗﺎﺑﻊ ﻣﻲآورد .در ﻫﺮ ﺣﺎل اﻳﻦ ﻛﻪ ﺗﺎﺑﻊ inline ،ﺑﺸﻮد ﻳﺎ ﻧﻪ ﺑﻪ compilerﺑﺴﺘﮕﻲ دارد .روش دﻳﮕﺮي را ﻫﻢ ﺑﺮاي اﻳﺠﺎد ﺗﺎﺑﻊﻫﺎي inlineﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮرﻫﺎي preprocessorﻫﺴﺖ ﻛﻪ در آن inlineﺷﺪن ﻗﻄﻌﻲ اﺳﺖ .در ﻓﺼﻞ ﭼﻬﺎرم ﺑﺎ آن آﺷﻨﺎ ﻣﻲﺷﻮﻳﺪ.
enum
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﭼﻨﺪ ﺛﺎﺑﺖ ﻋﺪدي را ﻫﻢ زﻣﺎن و ﺑﻪ راﺣﺘﻲ ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﻣﻲﺗﻮاﻧﻴﻢ از ﻛﻠﻤﻪ ﻛﻠﻴﺪي enumاﺳﺘﻔﺎده ﻛﻨﻴﻢ .ﺑﺎ ﻧﻮﺷﺘﻦ ;}enum A {a = 10, b = 20, c = 100
ﻧﻮع ﺟﺪﻳﺪي ﺑﺎ ﻧﺎم Aﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد و ﺑﻪ ﻋﻼوه ﺛﺎﺑﺖﻫﺎي aﺑﺎ ﻣﻘﺪار b ،10ﺑﺎ ﻣﻘﺪار 20و cﺑﺎ ﻣﻘﺪار 100 ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮﻧﺪ ﻛﻪ ﻧﻮع ﻫﻤﻪي آنﻫﺎ Aاﺳﺖ .اﻳﻦ ﻣﺜﺎل ﻫﻤﻪ ﭼﻴﺰ را روﺷﻦ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std ;}enum A {a = 10, b = 20, c = 100 )(int main { ;cout<< a << endl ;cout<< b << endl ;cout<< c << endl ;)(cout<< typeid(a).name ;)(_getch }
Output (BDS 2006): 10
142
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 20 100 A Output(Visual C++ 2005): 10 20 100 enum A
اﮔﺮ در اﻳﻦ ﻣﺜﺎل ﺑﻪ bو cﻣﻘﺪار ﻧﺪﻫﻴﻢ compiler ،ﺑﻪ ﺗﺮﺗﻴﺐ ﺑﻪ آنﻫﺎ ﻣﻘﺪارﻫﺎي 11و 12ﻣﻲدﻫﺪ .اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std ;}enum A {a, b = 10, c, d, e = 500, f, g )(int main { ;cout<< a << endl ;cout<< b << endl ;cout<< c << endl ;cout<< d << endl ;cout<< e << endl ;cout<< f << endl ;cout<< g << endl //g = 12; // error ;)(_getch }
Output: 0 10 11 12 500 501 502
ﭘﺲ اﮔﺮ ﺑﻪ ﻳﻜﻲ از ﺛﺎﺑﺖﻫﺎي enumﻣﻘﺪار ﻧﺪﻫﻴﻢ ﺧﻮد compilerﻣﻘﺪاري ﺑﻪ آن ﻣﻲﻫﺪ ﺑﺮاﺑﺮ ﺑﺎ »ﺛﺎﺑﺖ ﻗﺒﻠﻲ ﺑﻪ اﺿﺎﻓﻪي ﻳﻚ« .ﺑﻪ aﻣﻘﺪاري ﻧﺪادهاﻳﻢ و ﻗﺒﻞ از آن ﻫﻢ ﭼﻴﺰي در enumﻧﻴﺴﺖ و compilerﻣﻘﺪار ﺻﻔﺮ را ﺑﻪ آن داده. ﻣﻲﺗﻮاﻧﻴﻢ از ﻧﻮع enumﻣﺘﻐﻴﺮي ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﻛﻪ ﻗﺮار اﺳﺖ ﺗﻨﻬﺎ ﻣﻘﺪارﻫﺎﻳﻲ را ﺑﮕﻴﺮد ﻛﻪ ﺛﺎﺑﺖﻫﺎي enumآنﻫﺎ را ﮔﺮﻓﺘﻪاﻧﺪ:
143
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <conio.h> #include <iostream> using namespace std; enum A {a, b, c}; int main() { A x = c; // x++; // can be an error cout<< sizeof(x) << endl << x; _getch(); }
Output (BDS 2006): 1 2 Output (Visual C++ 2005): 4 2
4 اﻧﺪازهي آن راVisual C++ 2005 را ﻳﻚ ﺑﺎﻳﺖ ﻣﻲﮔﻴﺮد وﻟﻲenum اﻧﺪارهيBDS 2006 ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ را ﭼﻬﺎر ﺑﺎﻳﺖ ﺑﮕﻴﺮدenum ﻫﻢ ﻳﻚBDS 2006 ﺑﺮاي اﻳﻦ ﻛﻪ. اﺳﺖint ﺑﺎﻳﺖ ﻣﻲﮔﻴﺮد ﻛﻪ ﺑﻪ اﻧﺪازهي ﻳﻚ :ﺧﻂ زﻳﺮ را درﺳﺖ ﺑﻪ اول ﺑﺮﻧﺎﻣﻪ اﺿﺎﻓﻪ ﻛﻨﻴﺪ #pragma option –b
راه دﻳﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺎ ﻣﻨﻮﻫﺎ ﻣﺴﻴﺮ زﻳﺮ Project | Options… | C++ Compiler (bcc32) | compiling x++ در اﻳﻦ ﺣﺎﻟﺖ درﻣﺜﺎل ﺑﺎﻻ. ﺗﻴﻚ ﺑﮕﺬارﻳﺪEnums always integer sized(-b) را ﺑﭙﻴﻤﺎﻳﻴﺪ و ﻣﻘﺎﺑﻞ
. ﻧﻤﻲدﻫﺪerror دﻳﮕﺮ - ﻣﻲBDS 2006 ﻧﻤﻲﺷﻮد ﮔﺮﻓﺖ وﻟﻲ درcin را ﺑﺎenum ﻳﻚVisul C++ 2005 درﭘﺎﻳﺎن ﺑﮕﻮﻳﻢ ﻛﻪ در . ﻫﻢ ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮدwarning ﺷﻮد وﻟﻲ ﻳﻚ :ﺣﺎﻻ ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻧﮕﺎه ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; enum {aa = 100 , bb, cc}; int main() { cout<< bb << endl; cout<< typeid(cc).name();
144
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ _getch(); }
Output (BDS 2006): 101 enum Output (Visual C++ 2005): 101 enum <unnamed-tag>
. ﺑﺮاي ﺗﻌﺮﻳﻒ ﺛﺎﺑﺖﻫﺎ ﻣﻨﺎﺳﺐ اﺳﺖenum اﻳﻦ ﺟﻮر ﺑﻪ ﻛﺎر ﺑﺮدن. در اﻳﻦ ﺟﺎ ﺑﺪون اﺳﻢ ﺑﻪ ﻛﺎر رﻓﺘﻪenum : اﺟﺮا ﻧﻤﻲﺷﻮدBDS 2006 ﺑﺮﻧﺎﻣﻪي زﻳﺮ در #include <conio.h> #include <iostream> using namespace std; enum A { aa = 100, bb, cc }; int main() { cout<< A::bb << endl; cout<< typeid(A::bb).name() << endl; _getch(); }
Output (Visual C++ 2005): 101 enum A
Visual C++ 2005 اﻳﻦ در.A::b ﻧﻮﺷﺘﻪ ﺷﺪهb ﻣﻲﮔﻴﺮد اﻳﻦ اﺳﺖ ﻛﻪ ﺑﻪ ﺟﺎيBDS 2006 ﺧﻄﺎﻳﻲ ﻛﻪ . اﺳﺖwarning ﻓﻘﻂ ﻳﻚ وﻟﻲstatic ﻣﻲﺷﻮد ﻧﻮﺷﺖenum ﻣﻮﻗﻊ ﺗﻌﺮﻳﻒ ﺟﻠﻮيVisual C++ 2005 ﻧﻜﺘﻪي دﻳﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ در . از اﻳﻦ ﺧﻄﺎ ﻣﻲﮔﻴﺮدBDS 2006 : را ﻧﺸﺎن ﻣﻲدﻫﺪtypedef ﺑﺎ اﺳﺘﻔﺎده ازenum ﺑﺮﻧﺎﻣﻪي ﺑﻌﺪي ﺗﻌﺮﻳﻒ ﻳﻚ #include <conio.h> #include <iostream> using namespace std;
145
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ typedef enum { aa = 100, bb, cc ;} AA )(int main { ;cout<< bb << endl ;cout<< typeid(bb).name() << endl ;)(_getch }
Output (BDS 2006): 101 AA Output (Visual C++ 2005): 101 enum AA
)را ه @? !
ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻳﻚ آراﻳﻪي ﻣﻌﻤﻮﻟﻲ را ﺑﻪ ﺻﻮرت ;]int a[20
ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﻴﻢ ،ﻣﻲﺗﻮاﻧﻴﻢ ﻳﻚ آراﻳﻪي دوﮔﺎﻧﻪ ﻣﺜﻞ ;]int b[20][30
ﻳﺎ ﻳﻚ آراﻳﻪي ﺳﻪ ﮔﺎﻧﻪ ﻣﺜﻞ ;]int c[20][30][50
ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ .ﺑﺎ bﺑﺎﻳﺪ ﻣﺜﻞ ﻳﻚ ** intو ﺑﺎ cﺑﺎﻳﺪ ﻣﺜﻞ ﻳﻚ *** intرﻓﺘﺎر ﻛﺮد. ﻣﺜﺎل زﻳﺮ ﺟﺪول ﺿﺮب را ﻣﻲﺳﺎزد .در conio.hي ﻛﻪ ﺗﻮﺳﻂ Borlandﺗﻨﻈﻴﻢ ﺷﺪه ،ﺗﺎﺑﻊ )( gotoxyﻫﺴﺖ ﻛﻪ ) cursorﻣﻜﺎن ﻧﻤﺎي ﭼﺸﻤﻚ زن ﻛﻪ ﻣﺤﻞ ﻧﻮﺷﺘﻦ را در ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ ﻧﺸﺎن ﻣﻲدﻫﺪ( را ﺑﻪ ﻣﻜﺎن ﺧﺎﺻﻲ از ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ ﻣﻲﺑﺮد .ﻣﺜﻼ ;)gotoxy(10,10 ;"cout<< "hello
146
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اول cursorرا ﺑﻪ ﺧﻂ دﻫﻢ ﻣﻲﺑﺮد ﺑﻌﺪ 10واﺣﺪ ﺑﻪ ﺳﻤﺖ راﺳﺖ ﻣﻲرود و ﺑﻌﺪ helloرا ﭼﺎپ ﻣﻲﻛﻨﺪ .در compilerﻫﺎي Microsoftاﻳﻦ ﺗﺎﺑﻊ وﺟﻮد ﻧﺪارد .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻛﻪ ﺟﺪول ﺿﺮب را ﻧﻤﺎﻳﺶ ﻣﻲ- دﻫﺪ در Visual C++ 2005اﺟﺮا ﻧﻤﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std )]void Matrix(int a[10][10 { ;int i,j )for(i = 0; i < 10; i++ )for(j = 0; j < 10; j++ ;)a[i][j] = (i+1) * (j+1 } )(int main { ;]int a[10][10 ;)Matrix(a )for(int i = 0; i < 10; i++ { )for(int j = 0; j < 10; j++ { ;)gotoxy(3*(i+1),j+1 ;]cout<< a[i][j } } ;)(getch }
10 20 30 40 50 60 70 80 90 100
)ﻫﺸﺖ ﻧﻪ ﺗﺎ؟ اﮔﺮ ﺑﻠﺪ ﻧﻴﺴﺘﻴﻦ ﭘﺲ از ﻛﻼس ﺑﺮوﻳﺪ ﺑﻴﺮون !
9 18 27 36 45 54 63 72 81 90
8 16 24 32 40 48 56 64 72 80
7 14 21 28 35 42 49 56 63 70
6 12 18 24 30 36 42 48 54 60
5 10 15 20 25 30 35 40 45 50
Output (BDS 2006): 1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16 5 10 15 20 6 12 18 24 7 14 21 28 8 16 24 32 9 18 27 36 10 20 30 40
(
ﺣﺎﻻ ﺑﺎﻳﺪ وارد ﺟﺰﺋﻴﺎت ﺧﺴﺘﻪ ﻛﻨﻨﺪه ﺑﺸﻮﻳﻢ .اوﻻ آراﻳﻪ ﻣﻮﻗﻊ ورود ﺑﻪ ﺗﺎﺑﻊ ﭼﻪ ﺗﻐﻴﻴﺮي ﻣﻲﻛﻨﺪ؟ >#include <conio.h >#include <iostream ;using namespace std
147
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ void f(int a[10][10]) { cout<<"\nin f():\n" << typeid(a).name(); } int main() { int a[10][10]; cout<< typeid(a).name() << endl; f(a); getch(); }
Output (BDS 2006): int[10][10] in f(): int ( *)[10] Output (Visual C++ 2005): int [10][10] in f(): int (*)[10]
: ﻣﻲﺗﻮاﻧﺪ ﻫﺮ ﻳﻚ از ﺣﺎﻟﺖﻫﺎي زﻳﺮ را داﺷﺘﻪ ﺑﺎﺷﺪ و اﻳﻦﻫﺎ ﺑﺎ ﻫﻢ ﻓﺮﻗﻲ ﻧﺪارﻧﺪf()در واﻗﻊ ﭘﺎراﻣﺘﺮ void f(int a[222][10]) void f(int a[][10]) void f(int (*a)[10])
:ﻫﻤﻴﻦ در ﻣﻮرد آراﻳﻪﻫﺎي ﺳﻪ ﮔﺎﻧﻪ و ﺑﻴﺶﺗﺮ ﻫﻢ درﺳﺖ اﺳﺖ #include <conio.h> #include <iostream> using namespace std; void f(int a[10][10][24]) { cout<<"\nin f():\n" << typeid(a).name(); } int main() { int a[10][10][24]; cout<< typeid(a).name() << endl; f(a); getch(); }
Output (BDS 2006): int[10][10][24] in f(): int ( *)[10][24]
148
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output (Visual C++ 2005): ]int [10][10][24 in f(): ]int (*)[10][24
ﺣﺎﻻ اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ: ;]int a[10][24
ﻓﻜﺮ ﻣﻲﻛﻨﻴﺪ ] a[5ﻣﻌﻨﻲ داﺷﺘﻪ ﺑﺎﺷﺪ ﻳﺎ ﻧﻪ؟ ﻣﻌﻨﻲ دارد و ﻣﻌﻨﻲ آن را در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺑﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;]int a[10][24 ;]int b[24 ;cout<< typeid(a[5]).name() << endl ;)(cout<< typeid(b).name ;)(getch }
Output (BDS 2006): ]int[24 ]int[24 Output (Visual C++ 2005): ]int [24 ]int [24
] a[5ﻳﻚ ﺧﻮد ﻳﻚ آراﻳﻪي 24ﺗﺎﻳﻲ از intﻫﺎ اﺳﺖ .ﭘﺲ ﺑﺎ ﻧﻮﺷﺘﻦ ;]int a[10][24
ﻣﺎ 10آراﻳﻪي 24ﺗﺎﻳﻲ ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﻴﻢ ﻛﻪ اﺗﻔﺎﻗﺎ ﺣﺎﻓﻈﻪي آنﻫﺎ ﻫﻢ ﭘﺸﺖ ﺳﺮ ﻫﻢ ﻗﺮار دارد .ﭘﺲ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﮕﻮﻳﻴﻢ آراﻳﻪي دوﮔﺎﻧﻪ آراﻳﻪاي از آراﻳﻪﻫﺎي ﻣﻌﻤﻮﻟﻲ اﺳﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﻄﻤﺌﻦ ﺑﺸﻮﻳﺪ ﻛﻪ ﺣﺎﻓﻈﻪﻫﺎ ﭘﺸﺖ ﺳﺮ ﻫﻢ ﻫﺴﺘﻨﺪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;}int a[3][3] = {1,2,3,4,5,6,7,8,9 ;int* p = (int*)a )for(int i = 0; i < 9; i++ ;" " << ]cout<< p[i ;)(getch
149
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
9
8
7
6
5
4
Output: 1 2 3
pﻳﻚ اﺷﺎره ﮔﺮ ﺑﻪ اول ﺣﺎﻓﻈﻪي ﺗﺨﺼﻴﺺ داده ﺷﺪه ﺑﻪ آراﻳﻪي aاﺳﺖ .اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻧﺸﺎن ﻣﻲدﻫﺪ ﻛﻪ ﺣﺎﻓﻈﻪي آرﻳﻪي دوﮔﺎﻧﻪ در ﻳﻚ ﺟﺎ ﻗﺮار دارد و ﭼﻨﺪ ﺗﻜﻪ و در ﺟﺎﻫﺎي ﻣﺨﺘﻠﻒ ﻧﻴﺴﺖ .ﻣﻲﺗﻮاﻧﺴﺘﻢ ﺑﻪ ﺟﺎي ;int* p = (int*)a
ﺑﻨﻮﻳﺴﻢ: ;)]int* p = &(a[0][0
ﭼﺮا؟ ﻣﺴﺄﻟﻪي دﻳﮕﺮ دادن ﻣﻘﺪار اوﻟﻴﻪ اﺳﺖ ﻛﻪ در ﻣﺜﺎل ﻗﺒﻠﻲ دﻳﺪﻳﺪ .در ﻣﺜﺎلﻫﺎي زﻳﺮ ﻧﺤﻮهي دادن ﻣﻘﺪار اوﻟﻴﻪ و ﻣﺴﺎﺋﻞ ﻣﺮﺑﻮط ﺑﻪ آنﻫﺎ را ﺗﻮﺿﻴﺢ دادهام: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;}int a[3][3] = {1,2,3,4,5,6,7,8,9 ;]cout<< a[1][0 ;)(getch }
Output: 4
ﻣﻲداﻧﻴﻢ ﻛﻪ ] a[3][3ﺳﻪ آراﻳﻪي ﺳﻪ ﺗﺎﻳﻲ اﺳﺖ .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ 2 ،1و 3در آراﻳﻪي اول 5 ،4 ،و 6در آراﻳﻪ- ي دوم و 8 ،7و 9در آراﻳﻪي ﺳﻮم ﻗﺮار ﻣﻲﮔﻴﺮد .ﻣﻨﻈﻮر از ] a[1][0آراﻳﻪي دوم و intاول آن اﺳﺖ ﻛﻪ ﻣﻘﺪارش 4اﺳﺖ .ﺑﻪ ﻣﻨﻈﻮر راﺣﺘﻲ ِ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ C++ ،ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ اﺟﺎزه ﻣﻲدﻫﺪ ﻛﻪ ﺑﺮاي روﺷﻦ ﺗﺮ ﺑﻮدن ﺑﺮﻧﺎﻣﻪ ،ﻫﻨﮕﺎم دادن ﻣﻘﺪار اوﻟﻴﻪ از ) braceﻳﻌﻨﻲ }{(ﻫﺎي اﺿﺎﻓﻪ ﻫﻢ اﺳﺘﻔﺎده ﻛﻨﺪ ﻳﻌﻨﻲ ﺑﻪ ﺟﺎي ;}int a[3][3] = {1,2,3,4,5,6,7,8,9
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;}}int a[3][3] = {{1,2,3},{4,5,6},{7,8,9
ﻳﺎ ﺣﺘﻲ ;}int a[3][3] = {1,2,3,{4,5,6},7,8,9
150
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺣﺎﻻ ﻓﺮض ﻛﻨﻴﺪ ﻣﻲﺧﻮاﻫﻴﻢ ﻳﻚ آراﻳﻪي دوﮔﺎﻧﻪ روي heapدرﺳﺖ ﻛﻨﻴﻢ .اﻳﻦ ﻛﺎر دو ﺣﺎﻟﺖ دارد .ﮔﻔﺘﻢ ﻛﻪ آراﻳﻪي دوﮔﺎﻧﻪ ،آراﻳﻪاي از آراﻳﻪﻫﺎﺳﺖ .در ﺣﺎﻟﺖ اول ،ﻣﺎ آراﻳﻪاي را ﻛﻪ از آراﻳﻪﻫﺎﺳﺖ روي stackﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﻴﻢ اﻣﺎ ﺧﻮد آراﻳﻪﻫﺎ را از heapﻣﻲﮔﻴﺮﻳﻢ: ;]int* a[10 )for(int i = 0; i < 10; i++ ;]a[i] = new int [24
ﻛﻪ ﻣﺜﻞ ;]int a[10][24
اﺳﺖ .ﺣﺎﻟﺖ دوم اﻳﻦ اﺳﺖ ﻛﻪ ﻛﻼ ﻫﻤﻪ ﭼﻴﺰ روي heapﺑﺎﺷﺪ: ;int x = 3 ;int y = 4 ;]int** a = new int* [x )for(int i = 0; i < x; i++ ;]a[i] = new int [y
ﺧﻮﺑﻲ آن اﻳﻦ اﺳﺖ ﻛﻪ ﻫﻢ xو ﻫﻢ yﻣﺘﻐﻴﺮ اﺳﺖ و ﻻزم ﻧﻴﺴﺖ ﺛﺎﺑﺖ ﺑﺎﺷﻨﺪ ﻣﻲداﻧﻴﺪ ﻛﻪ در ;]int a[x][y
xو yﺑﺎﻳﺪ ﺛﺎﺑﺖ ﺑﺎﺷﻨﺪ .در ﻣﺜﺎل زﻳﺮ از ﺣﺎﻟﺖ دوم اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std
;" = " << a[i][j] << endl
)(int main { ;int x = 3 ;int y = 4 ;]int** a = new int* [x )for(int i = 0; i < x; i++ ;]a[i] = new int [y )for(int i = 0; i < x; i++ )for(int j = 0; j < y; j++ ;a[i][j] = i + j )for(int i = 0; i < x; i++ )for(int j = 0; j < y; j++ << cout<< i << " + " << j ;)(getch }
0 1 2 3 1 2
151
www.pupuol.com
= = = = = =
Output: 0 + 0 0 + 1 0 + 2 0 + 3 1 + 0 1 + 1
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 3 4 2 3 4 5
2 3 0 1 2 3
= = = = = =
+ + + + + +
1 1 2 2 2 2
وﻗﺘﻲ ﺑﻪ اﻳﻦ ﺷﻜﻞ آراﻳﻪاي از heapدرﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﻢ روﺷﻦ اﺳﺖ ﻛﻪ ﻣﻤﻜﻦ اﺳﺖ ﺣﺎﻓﻈﻪﻫﺎي ﺗﺨﺼﻴﺺ داده ﺷﺪه ﭘﺸﺖ ﺳﺮ ﻫﻢ ﻧﺒﺎﺷﻨﺪ و ﺗﻜﻪ ﺗﻜﻪ و در ﺟﺎﻫﺎي ﻣﺨﺘﻠﻒ ﺑﺎﺷﻨﺪ .ﻣﺜﺎل زﻳﺮ اﻳﻦ ﮔﻔﺘﻪ را ﺛﺎﺑﺖ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;]int* a[3 )for(int i = 0; i < 3; i++ ;]a[i] = new int [3 ;a[0][0] = 1, a[0][1] = 2, a[0][2] = 3 ;a[1][0] = 4, a[1][1] = 5, a[1][2] = 6 ;a[2][0] = 7, a[2][1] = 8, a[2][2] = 9 ;)]int* p = &(a[0][0 )for(int i = 0; i < 9; i++ ;" " << ]cout<< p[i ;cout<< endl ;int* q = (int*) a )for(int i = 0; i < 9; i++ ;" " << ]cout<< q[i ;)(getch }
Output (BDS 2006): 1245112
3
9388112
9
1245036
12 4 5 6 12 7 9388128 9388144 3
1 2 3 9388112
Output (Visual C++ 2005): 3552424
3562736
-33686019 -1414812757 -1414812757 0 0 589833 3562968 3563040 -858993460 1245112 4269318 1
1 2 3 3562896
وﻗﺘﻲ روي heapﻫﺴﺘﻴﻢ ﻣﻲﺗﻮاﻧﻴﻢ آراﻳﻪﻫﺎي ﻋﺠﻴﺐ وﻏﺮﻳﺐ ﺗﺮي ﻫﻢ ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﻣﺜﻼ آراﻳﻪﻫﺎي از آراﻳﻪﻫﺎي ﺑﺎ ﻃﻮل ﻣﺘﻔﺎوت: ;int x =5 ;]int** a = new int* [x )for(int i = 0; i < x; i++ ;]a[i] = new int [i+1
152
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ وﻟﻲ ﺑﺎﻳﺪ ﻣﻮاﻇﺐ ﺑﺎﺷﻴﻢ ﻛﻪ ﺑﻪ ﺣﺎﻓﻈﻪي اﺧﺘﺼﺎص ﻧﻴﺎﻓﺘﻪ دﺳﺖ ﻧﺰﻧﻴﻢ ﻣﺜﻼ در اﻳﻦ aي اﺧﻴﺮ ] a[0][4ﺑﻪ ﺟﺎﻳﻲ از ﺣﺎﻓﻈﻪ اﺷﺎره دارد ﻛﻪ ﺗﺤﺖ ﻛﻨﺘﺮل ﻣﺎ ﻧﻴﺴﺖ و دﺳﺘﻜﺎري آن ﻣﻲﺗﻮاﻧﺪ ﺧﻄﺮﻧﺎك ﺑﺎﺷﺪ. اﮔﺮ آراﻳﻪاي )روي heapﻳﺎ (stackﻣﺜﻞ aداﺷﺘﻪ ﺑﺎﺷﻴﻢ ﺑﺮاي دﺳﺘﻴﺎﺑﻲ ﺑﻪ ﻋﻨﺼﺮ دوم از آراﻳﻪي ﺳﻮم ﻣﻲﻧﻮﻳﺴﻴﻢ ] a[2][1وﻟﻲ ﻣﻲﺗﻮاﻧﻴﻢ از راه ﻃﺒﻴﻌﻲ ﻫﻢ اﺳﺘﻔﺎده ﻛﻨﻴﻢ و ﺑﻨﻮﻳﺴﻴﻢ ) *(*(a+2)+1ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;}int a[3][3] = {1,2,3,4,5,6,7,8,9 ;cout<< a[2][1] << endl ;cout<< *(a[2] + 1) << endl ;)cout<< *(*(a+2)+1 ;)(getch }
Output: 8 8 8
اﻳﻦ در ﻣﻮرد آراﻳﻪاي ﻣﺜﻞ ;]int a[10
ﻫﻢ درﺳﺖ اﺳﺖ و ﻣﺜﻼ ] a[2ﺑﺮاﺑﺮ اﺳﺖ ﺑﺎ ) *(a+2ﭼﻮن aﻫﻤﺎن ] &a[0و a+2ﻫﻤﺎن ].&a[2 در ﻧﻬﺎﻳﺖ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ ﺗﻘﺮﻳﺒﺎ در ﻫﻤﻪي ﺟﺎﻫﺎﻳﻲ ﻛﻪ آراﻳﻪي دوﮔﺎﻧﻪ ﻻزم اﺳﺖ ﻣﻲﺷﻮد از آراﻳﻪي ﻣﻌﻤﻮﻟﻲ اﺳﺘﻔﺎده ﻛﺮد :ﻓﺮض ﻛﻨﻴﺪ xو yدو ﻋﺪد ﺻﺤﻴﺢ ﻣﺜﺒﺖ و ﺛﺎﺑﺖ ﺑﺎﺷﻨﺪ .وﻗﺘﻲ ﻣﻲﻧﻮﻳﺴﻴﻢ: ;]int a[x][y
ﺑﻪ ﺗﻌﺪاد ﺣﺎﺻﻞ ﺿﺮب xو int ،yروي ﺣﺎﻓﻈﻪ ﺑﻪ وﺟﻮد ﻣﻲآﻳﺪ .ﻣﺎ ﻣﻲﺗﻮاﻧﻴﻢ ﻫﻤﻴﻦ ﺣﺎﻓﻈﻪ را ﺑﺎ ﻧﻮﺷﺘﻦ ;]int b[x*y
ﺑﻪ وﺟﻮد ﺑﻴﺎورﻳﻢ .اﻣﺎ ﺳﺆال اﻳﻦ اﺳﺖ ﻛﻪ ﺑﻪ ﺟﺎي ] a[i][jﭼﻪ ﺑﻨﻮﻳﺴﻴﻢ؟ ﺧﻴﻠﻲ ﺳﺎده اﺳﺖ ﺑﺎﻳﺪ ﻧﻮﺷﺖ ] .b[i*y+jدر واﻗﻊ ﻗﺮار اﺳﺖ xﺗﺎ آراﻳﻪ ﺑﻪ ﻃﻮل yداﺷﺘﻪ ﺑﺎﺷﻴﻢ a[i][j] .ﺑﻪ ﻋﻨﺼﺮ )(j+1ام از )(i+1اﻣﻴﻦ آراﻳﻪ اﺷﺎره دارد .ﺑﺮاي ،bﻣﻲﺗﻮاﻧﻴﻢ ﺗﺼﻮر ﻛﻨﻴﻢ ﻛﻪ اﻳﻦ xآراﻳﻪ ،ﭘﺸﺖ ﺳﺮ ﻫﻢ ﻫﺴﺘﻨﺪ .ﺑﺮاي
153
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ دﺳﺘﺮﺳﻲ ﺑﻪ )(i+1اﻣﻴﻦ آراﻳﻪ ،ﺑﺎﻳﺪ iآراﻳﻪ ﺑﻪ ﻃﻮل yرا ﭘﺸﺖ ﺳﺮ ﺑﮕﺬارﻳﻢ ﻳﻌﻨﻲ i*yو ﺑﻌﺪ ﺑﺮاي رﺳﻴﺪن ﺑﻪ )(j+1اﻣﻴﻦ intﻣﻲﻧﻮﻳﺴﻴﻢ ] .b[i*y+jﺑﺮاي روﺷﻦ ﺷﺪن ﻣﻄﻠﺐ ﻓﺮض ﻛﻨﻴﺪ x == 3 y == 4
ﺣﺎﻻ دو ﺷﻜﻞ ﻧﻤﺎﻳﺶ آراﻳﻪ را ﻣﻘﺎﻳﺴﻪ ﻛﻨﻴﺪ: ][2][3
][2][2
][2][1
][2][0
][1][3
][1][2
][1][1
][1][0
][0][3
][0][2
][0][1
][0][0
][11
][10
][9
][8
][7
][6
][5
][4
][3
][2
][1
][0
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻋﻨﺼﺮ ] [i][jﻫﻤﺎن ﻋﻨﺼﺮ ] [4*i+jاﺳﺖ .ﻣﺜﻼ ﻋﻨﺼﺮ ] [2][3ﺑﺮاﺑﺮ اﺳﺖ ﺑﺎ ﻋﻨﺼﺮ ][4*2+3ﻳﻌﻨﻲ ].[11 ﺑﺎ اﻳﻦ روش ﻣﻲﺗﻮاﻧﻴﻢ ﺣﺘﻲ در heapﻫﻢ ﺣﺎﻓﻈﻪي ﭘﺸﺖ ﺳﺮ ﻫﻤﻲ ﺑﺮاي آراﻳﻪﻫﺎﻳﻲ ﻛﻪ در ﻋﻤﻞ دوﮔﺎﻧﻪ ﻫﺴﺘﻨﺪ داﺷﺘﻪ ﺑﺎﺷﻴﻢ .ﻣﺜﺎل ﺟﺪول ﺿﺮب را ﺑﺎ اﻳﻦ روش و ﺑﺎ آراﻳﻪي ﻣﻌﻤﻮﻟﻲ ﺗﻜﺮار ﻣﻲﻛﻨﻢ: >#include <conio.h >#include <iostream ;using namespace std )]void Matrix(int a[100 { ;int i,j )for(i = 0; i < 10; i++ )for(j = 0; j < 10; j++ a[i * 10 + j] = (i+1) * (j+1);// where the action is }
)i++ )< 10; j++ ;)( i + 1),j + 1 * 10 + j];// where the action is
154
www.pupuol.com
)(int main { ;]int a[100 ;)Matrix(a ;for(int i = 0; i < 10 { for(int j = 0; j { * gotoxy(3 cout<< a[i } } ;)(getch }
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output (BDS 2006): 1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16 5 10 15 20 6 12 18 24 7 14 21 28 8 16 24 32 9 18 27 36 10 20 30 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
void* ا ر ! ع
ﺑﻨﺎﺑﺮاﻳﻦ ﺑﺮاي ﻣﺸﺨﺺ ﺷﺪن ﺟﺎﻳﻲ از ﺣﺎﻓﻈﻪ ﻛﻪ ﺑﻪ. ﭼﻬﺎر ﺑﺎﻳﺖ اﺳﺖpointer ﻫﺎي اﻣﺮوزي ﻫﺮcompiler در در واﻗﻊ ﻧﻮع.ي از ﻫﺮ ﻧﻮع اﺳﺖvoid* pointer . ﻧﻴﺴﺖpointer آن اﺷﺎره ﻣﻲﺷﻮد ﻧﻴﺎزي ﺑﻪ داﻧﺴﺘﻦ ﻧﻮع : ﺑﻴﺶ ﺗﺮ زﻣﺎﻧﻲ ﺑﻪ ﻛﺎر ﻣﻲرود ﻛﻪ ﻧﻮع ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻚ ﺗﺎﺑﻊ ﻣﺸﺨﺺ ﻧﻴﺴﺖC در.ﻣﺸﺨﺼﻲ ﻧﺪارد #include <conio.h> #include <iostream> using namespace std; const int INT = 100; const int CHAR = 101; bool isLess(void* a, void* b, int type) { if(type == INT) { int* aa = (int*)a; int* bb = (int*)b; if(*aa < *bb) return true; } else if(type == CHAR) { char* aa = (char*)a; char* bb = (char*)b; if(*aa < *bb) return true; } return false; } int main() { char ch1 = 'a'; char ch2 = 'b'; cout<< isLess(&ch1,&ch2,CHAR) << endl;
155
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int a = 2 ;int b = 3 ;cout<< isLess(&a,&b,INT) << endl ;)(getch }
Output: 1 1
ﺗﺎﺑﻊ )( isLessﻣﻲﺑﻴﻨﺪ ﻛﻪ آﻳﺎ *aاز *bﻛﻢ ﺗﺮ اﺳﺖ ﻳﺎ ﻧﻪ *a .و *bﻣﻤﻜﻦ اﺳﺖ intﻳﺎ charﺑﺎﺷﻨﺪ. ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﺎﺑﻊ )( isLessﺑﻴﺶ از ﺣﺪ ﻃﻮﻻﻧﻲ اﺳﺖ .در C++اﻣﻜﺎن اﺳﺘﻔﺎده از templateﻧﻴﺎز ﺑﻪ * voidرا در اﻳﻦ ﻣﻮارد ﺗﻘﺮﻳﺒﺎ از ﺑﻴﻦ ﻣﻲﺑﺮد. در C++دو ﺧﻂ ;int a ;char* c = &a
ﺧﻄﺎ دارد اﻣﺎ ;int a ;void* c = &a
ﺧﻄﺎ ﻧﺪارد.
@? '
ﺣﺎل ﺷﻤﺎ دﺳﺖ ﻛﻢ آن ﭼﻪ را در زﺑﺎن Cﻫﺴﺖ ﻣﻲداﻧﻴﺪ و ﺑﻪ اﻧﺪازهي ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ Cﻗﺪرت دارﻳﺪ و ﻣﻲ- ﺗﻮاﻧﻴﺪ ﺑﺮﻧﺎﻣﻪﻫﺎي ﺧﻮﺑﻲ ﺑﻨﻮﻳﺴﻴﺪ .ﻓﻜﺮ ﻣﻲﻛﻨﻢ در اﻳﻦ ﺷﺮاﻳﻂ اﻧﺠﺎم ﭼﻨﺪ ﺗﻤﺮﻳﻦ ﻻزم ﺑﺎﺷﺪ. (1ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻋﺪد ﺻﺤﻴﺢ nرا از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد و ﺑﻌﺪ nﻋﺪد ﺻﺤﻴﺢ از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد و آن ﻫﺎ را ﭼﺎپ ﻛﻨﺪ .ﺑﺮﻧﺎﻣﻪ را ﻃﻮري ﺑﻨﻮﻳﺴﻴﺪ ﻓﻘﻂ ﺑﻪ اﻧﺪازه ﻻزم و ﻧﻪ ﺑﻴﺶ ﺗﺮ ﺣﺎﻓﻈﻪ ﺑﺨﻮاﻫﺪ. راﻫﻨﻤﺎﻳﻲ :ﺣﺎﻓﻈﻪ را از heapﺑﮕﻴﺮﻳﺪ. (2ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ در ﺗﺎﺑﻊ )( mainاز ﺳﻪ ﺑﻠﻮك ﺗﻮ در ﺗﻮ اﺳﺘﻔﺎده ﺑﺸﻮد و در ﻫﺮ ﺑﻠﻮك ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﺎم aﺑﺎ ﻣﻘﺪار ﻣﺘﻔﺎوت ﻣﻮﺟﻮد ﺑﺎﺷﺪ .ﺑﻌﺪ ﺑﺮﻧﺎﻣﻪ را ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﺪ ﺑﻪ ﻃﻮري ﻛﻪ ﻫﺮ ﺑﻠﻮك ﺑﺎ ﻳﻚ ﺗﺎﺑﻊ ﺟﺎﻳﮕﺰﻳﻦ ﺑﺸﻮد. (3ﻓﺮض ﻛﻨﻴﺪ در C++ﻋﻤﻠﮕﺮﻫﺎي ﻣﻨﻄﻘﻲ && || ،و ! وﺟﻮد ﻧﺪارﻧﺪ .ﺗﺎﺑﻊﻫﺎي ;)bool and(bool a,bool b
156
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)bool or(bool a,bool b ;)bool not(bool a
را ﻃﻮري ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻛﺎر ﻋﻤﻠﮕﺮﻫﺎي ﻣﻨﻄﻘﻲ را اﻧﺠﺎم دﻫﻨﺪ. راﻫﻨﻤﺎﻳﻲ :از ifاﺳﺘﻔﺎده ﻛﻨﻴﺪ. (4ﺗﺎﺑﻌﻲ ﺑﺎ prototypeزﻳﺮ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻳﻚ رﺷﺘﻪ از ﻛﺎرﺑﺮ ﻣﻲﮔﻴﺮد ﻳﻚ ﻛﭙﻲ از آن رﺷﺘﻪ در heap درﺳﺖ ﻣﻲﻛﻨﺪ و اﺷﺎره ﮔﺮي ﺑﻪ رﺷﺘﻪي ﺟﺪﻳﺪ ﺑﺮ ﻣﻲﮔﺮداﻧﺪ: ;)char* newStr(char* old_string
اﻳﻦ ﺗﺎﺑﻊ زﻳﺎد ﺑﻪ درد ﻣﻲﺧﻮرد .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﺧﺮوﺟﻲ ﺑﺎﻳﺪ ﺑﻌﺪ از اﺗﻤﺎم ﻛﺎر ][ deleteﺷﻮد .ﺳﻌﻲ ﻛﻨﻴﺪ ﻳﻚ header fileﺑﺮاي ﺧﻮدﺗﺎن درﺳﺖ ﻛﻨﻴﺪ و اﻳﻦ ﮔﻮﻧﻪ ﺗﺎﺑﻊﻫﺎ در آن ﻗﺮار ﺑﺪﻫﻴﺪ. (5ﺗﺎﺑﻌﻲ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻳﻚ آراﻳﻪ از intﻫﺎ )ﺑﻪ ﻫﻤﺮاه ﻃﻮل آراﻳﻪ( را ﻣﻲﮔﻴﺮد و ﺑﺪون اﻳﻦ ﻛﻪ آراﻳﻪي ﺟﺪﻳﺪي ﺗﻌﺮﻳﻒ ﻛﻨﺪ ﺗﺮﺗﻴﺐ intﻫﺎ را ﺑﺮﻋﻜﺲ ﻣﻲﻛﻨﺪ .ﻣﺜﻼ اﮔﺮ اول 1,2,3,4,5,6ﺑﻮد ﺑﻌﺪ از از اﺟﺮاي ﺗﺎﺑﻊ ﻣﻲﺷﻮد .6,5,4,3,2,1 راﻫﻨﻤﺎﻳﻲ :ﭼﻨﺪ ﻣﺪاد رﻧﮕﻲ را ﻛﻨﺎر ﻫﻢ ﺑﮕﺬارﻳﺪ و دوﺗﺎ دوﺗﺎ ﺟﺎي آنﻫﺎ را ﻋﻮض ﻛﻨﻴﺪ ﺗﺎ ﺗﺮﺗﻴﺐ آنﻫﺎ ﻣﻌﻜﻮس ﺷﻮد .ﺑﻌﺪ ﺑﺮﻧﺎﻣﻪ را ﻃﻮري ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻫﻤﺎن ﻛﺎر ﺷﻤﺎ را اﻧﺠﺎم ﺑﺪﻫﺪ. (6ﻳﻚ structﺑﺎ ﻧﺎم studentدرﺳﺖ ﻛﻨﻴﺪ ﻛﻪ ﻧﺎم ،ﺷﻤﺎرهي ردﻳﻒ و ﻧﻤﺮهي ﻳﻚ داﻧﺶ آﻣﻮز را ﻧﮕﻪ ﻣﻲدارد .آراﻳﻪاي از اﻳﻦ structدرﺳﺖ ﻛﻨﻴﺪ و از ﻛﺎرﺑﺮ ﺑﺨﻮاﻫﻴﺪ ﺑﻪ ﻫﻤﻪي ﻋﻨﺎﺻﺮ آراﻳﻪ )ﻛﻪ ﻫﺮ ﻛﺪام ﻳﻚ studentاﺳﺖ( ﻣﻘﺪار ﺑﺪﻫﺪ .ﺑﻌﺪ ﺑﺮﻧﺎﻣﻪ ﺑﺎﻳﺪ ﺷﺎﮔﺮد اول )ﻳﻌﻨﻲ ﻛﺴﻲ ﻛﻪ ﺑﻴﺶ ﺗﺮﻳﻦ ﻧﻤﺮه را ﮔﺮﻓﺘﻪ( ﻣﻌﺮﻓﻲ ﻛﻨﺪ. (7اﻳﻦ ﺗﺎﺑﻊﻫﺎ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ: ;const int n = 5 )int f(const void* a,const void* b { ;)int aa = *((int*)a ;)int bb = *((int*)b )if(bb > aa ;return -1 ;)return (aa > bb } )]void sort(int a[n { ;)qsort(a,n,sizeof(int),f }
اﻳﻦ ﻛﻪ اﻳﻦﻫﺎ دﻗﻴﻘﺎ ﭼﻪ ﻛﺎر ﻣﻲﻛﻨﻨﺪ زﻳﺎد ﻣﻬﻢ ﻧﻴﺴﺖ ﺗﻨﻬﺎ ﭼﻴﺰ ﻣﻬﻢ اﻳﻦ اﺳﺖ ﻛﻪ ﺗﺎﺑﻊ )( sortﻳﻚ آراﻳﻪي 5ﻋﻨﺼﺮي از intﻫﺎ را از ﻛﻮﭼﻚ ﺑﻪ ﺑﺰرگ ﻣﺮﺗﺐ ﻣﻲﻛﻨﺪ.
157
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ اﺳﻢ و اﻣﺘﻴﺎز 5ورزﺷﻜﺎر را از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد ،اﻣﺘﻴﺎزﻫﺎ را از ﻛﻮﭼﻚ ﺑﻪ ﺑﺰرگ ﻣﺮﺗﺐ ﻛﻨﺪ و اﺳﻢ ورزﺷﻜﺎران را ﺑﻪ ﺗﺮﺗﻴﺐ اﻣﺘﻴﺎز آنﻫﺎ )از ورزﺷﻜﺎر ﺑﺎ ﻛﻢ ﺗﺮﻳﻦ اﻣﺘﻴﺎز ﺑﻪ ورزﺷﻜﺎر داراي ﺑﻴﺶ ﺗﺮﻳﻦ اﻣﺘﻴﺎز( ﺑﻨﻮﻳﺴﺪ .از structاﺳﺘﻔﺎده ﻧﻜﻨﻴﺪ. (8ﺑﺎ اﺳﺘﻔﺎده از ﺗﺎﺑﻊ )( sortدر ﺗﻤﺮﻳﻦ ﻗﺒﻞ ﺗﺎﺑﻌﻲ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻳﻚ آراﻳﻪي 5ﺗﺎﻳﻲ را ﺑﮕﻴﺮد و از ﺑﺰرگ ﺑﻪ ﻛﻮﭼﻚ ﻣﺮﺗﺐ ﻛﻨﺪ )ﻧﻪ از ﻛﻮﭼﻚ ﺑﻪ ﺑﺰرگ( .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻧﺒﺎﻳﺪ ﺑﻪ ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊﻫﺎي )( fو )( sortدر ﺗﻤﺮﻳﻦ ﻗﺒﻞ دﺳﺖ ﺑﺰﻧﻴﺪ.
' ه A#
اﻧﺠﺎم ﺗﻤﺮﻳﻦﻫﺎي ﺳﺨﺖ ،اﻓﺮاد ﺑﺎ اﺳﺘﻌﺪاد را واﻗﻌﺎ ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻋﻼﻗﻪ ﻣﻨﺪ ﻣﻲﻛﻨﺪ .ﺑﺮاي ﻫﻤﻴﻦ ﻻزم اﺳﺖ ﭼﻨﺪ ﺗﻤﺮﻳﻦ ﺳﺨﺖ را ﻫﻢ ﺣﻞ ﻛﻨﻴﺪ .در واﻗﻊ ﻣﻲﺧﻮاﻫﻢ اﻧﺠﺎم ﻫﺮ ﻳﻚ از آنﻫﺎ دﺳﺖ ﻛﻢ ﻳﻚ ﺳﺎﻋﺖ از وﻗﺖ ارزﺷﻤﻨﺪ
ﺷﻤﺎ را ﺑﮕﻴﺮد
.اﻟﺒﺘﻪ اﻳﻦ در ﺑﺮاﺑﺮ زﻣﺎﻧﻲ ﻛﻪ ﭘﺮوژهﻫﺎي ﺳﻨﮕﻴﻦ ﺗﺮ از ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻣﻲﮔﻴﺮد
ﻫﻴﭻ اﺳﺖ .ﺗﻮﺻﻴﻪ ﻣﻲﻛﻨﻢ )
( ﺗﺎ ﻧﻴﻤﻲ از آنﻫﺎ را اﻧﺠﺎم ﻧﺪادهاﻳﺪ ﻓﺼﻞﻫﺎي ﺑﻌﺪي را ﺷﺮوع ﻧﻜﻨﻴﺪ .ﻣﻦ در اﻳﻦ
ﻧﻮﺷﺘﻪ ﻓﻘﻂ زﺑﺎن C++را ﻳﺎد ﻣﻲدﻫﻢ ﻧﻪ ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺻﻮرت ﺣﺮﻓﻪاي .ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻳﻚ ﻗﺪرت ذاﺗﻲ اﺳﺖ ﻛﻪ ﻧﻤﻲﺷﻮد آن را ﻳﺎد داد .اﻟﺒﺘﻪ روش ﺣﻞ ﺑﻌﻀﻲ از ﻣﺴﺎﺋﻞ ﻣﻮﺟﻮد اﺳﺖ اﻣﺎ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺑﺎﻳﺪ ﺑﺘﻮاﻧﺪ ﻣﺴﺄﻟﻪﻫﺎي ﺟﺪﻳﺪ را ﺧﻮدش ﺣﻞ ﻛﻨﺪ .اﻣﻴﺪوارم در ﺣﻞ ﺗﻤﺮﻳﻦﻫﺎي زﻳﺮ ﻣﻮﻓﻖ ﺑﺎﺷﻴﺪ
.
(1اﻟﻒ( ﻣﻲداﻧﻴﺪ ﻛﻪ ﻳﻚ ﻋﺪد اول ،ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ و ﺑﺰرگ ﺗﺮ از 1اﺳﺖ ﻛﻪ ﺣﺎﺻﻞ ﺿﺮب دو ﻋﺪد ﺻﺤﻴﺢ و ﺑﺰرگ ﺗﺮ از 1دﻳﮕﺮ ﻧﻴﺴﺖ .ﻣﺜﻼ اول ﻧﻴﺴﺖ ﭼﻮن و و ﺻﺤﻴﺢ ﺑﺰرگ ﺗﺮ از ﻫﺴﺘﻨﺪ .وﻟﻲ اول اﺳﺖ ﭼﻮن ﻓﻘﻂ ﻣﻲﺷﻮد ﻧﻮﺷﺖ ﻳﺎ .ﺗﺎﺑﻌﻲ ﺑﺎ ِ prototypeزﻳﺮ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻋﺪدﻫﺎي اول را ﻣﺸﺨﺺ ﻛﻨﺪ: ;)bool isprime(unsigned a
158
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ب( ﻫﺮ ﻋﺪد ﺻﺤﻴﺢ ﻣﺜﺒﺖ را ﻣﻲﺷﻮد ﺑﻪ ﺻﻮرت ﺣﺎﺻﻞ ﺿﺮﺑﻲ از ﻋﺪدﻫﺎي اول ﻧﻮﺷﺖ ﻣﺜﻼ
.ﺗﺎﺑﻌﻲ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ ﺑﮕﻴﺮد و ﺗﺠﺰﻳﻪ آن را ﺑﻪ ﻋﻮاﻣﻞ اول ﺑﻨﻮﻳﺴﺪ ﻣﺜﻼ ﺑﺎ دادن ﺧﺮوﺟﻲ زﻳﺮ را ﭼﺎپ ﻛﻨﺪ: 2^2 3^3 7^1
(2ﺑﺎ ﻛﻤﻚ )( gotoxyﺗﺎﺑﻌﻲ ﺑﺎ اﻳﻦ :prototype ;)void Table(int x,int y
ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻣﺜﻼ ﺧﺮوﺟﻲ ) Table(4,3ﺑﻪ اﻳﻦ ﺻﻮرت ﺑﺎﺷﺪ:
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻣﺤﻴﻂ ﮔﺮاﻓﻴﮕﻲ ﻧﻴﺴﺖ )اﮔﺮ compilerﺷﻤﺎ )( gotoxyﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ ﺑﺎﻳﺪ ﺷﻤﺎ را از اﻧﺠﺎم اﻳﻦ ﺗﻤﺮﻳﻦ ﻣﻌﺎف ﻛﻨﻢ ﮔﺮﭼﻪ اﻧﺠﺎم آن ﺧﻴﻠﻲ آﻣﻮزﻧﺪه اﺳﺖ(.
(3ﺗﺎﺑﻌﻲ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ آراﻳﻪي از اﻋﺪاد ﺻﺤﻴﺢ را ﺑﮕﻴﺮد و ﻣﺮﺗﺐ ﻛﻨﺪ .ﻣﺜﻼ اﮔﺮ )( fآن ﺗﺎﺑﻊ ﺑﺎﺷﺪ ﺑﺎ اﺟﺮاي ;}int a[5] = {1,7,4,-1 ,5 ;)f(a
] a[0ﺗﺎ ] a[4ﻋﺒﺎرت ﺑﺎﺷﻨﺪ از 5 ،4 ،1 ،-1و 7ﻛﻪ از ﻛﻮﭼﻚ ﺑﻪ ﺑﺰرگ ﻣﺮﺗﺐ ﺷﺪهاﻧﺪ .ﺧﻮدﺗﺎن ﻓﻜﺮ ﻛﻨﻴﺪ و راﻫﻲ ﺑﺮاي ﻣﺮﺗﺐ ﻛﺮدن ﭘﻴﺪا ﻛﻨﻴﺪ و دﻧﺒﺎل راهﻫﺎﻳﻲ ﻛﻪ ﻗﺒﻼ ﭘﻴﺪا ﺷﺪه ﻧﮕﺮدﻳﺪ.
159
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (4ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﺣﺎﺻﻞ ! 50را ﭼﺎپ ﻛﻨﺪ 50! .ﺣﺎﺻﻞ ﺿﺮب اﻋﺪاد 1ﺗﺎ 50اﺳﺖ) .ﺣﻞ اﻳﻦ ﺗﻤﺮﻳﻦ ﺧﺴﺘﻪ ﻛﻨﻨﺪه اﺳﺖ .ﺑﻪ ﻫﺮ ﺣﺎل ﺗﻼش ﺧﻮدﺗﺎن را ﺑﻜﻨﻴﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻓﻘﻂ ﺑﺎﻳﺪ ﺟﻮاب ﭼﺎپ ﺷﻮد و ﮔﺮﻧﻪ ﻫﻴﭻ ﻧﻮﻋﻲ )ﺣﺘﻲ (__int128ﻇﺮﻓﻴﺖ ﻛﺎﻓﻲ ﺑﺮاي اﻳﻦ ﻛﻪ اﻳﻦ ﻋﺪد را در ﺧﻮد ﺟﺎ ﺑﺪﻫﺪ ﻧﺪارد. ﺟﻮاب: 30414093201713378043612608166064768844377641568960512000000000000
(5ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻋﺪد nرا از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد ،ﺑﻌﺪ اول nﺗﺎ ﻋﺪد از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد و ﺑﻌﺪ آنﻫﺎ را ﭼﺎپ ﻛﻨﺪ .ﺗﺮﺗﻴﺐ ﭼﺎپ ﻣﻬﻢ ﻧﻴﺴﺖ .از ﺣﺎﻓﻈﻪي heapاﺳﺘﻔﺎده ﻧﻜﻨﻴﺪ .از آراﻳﻪ اﺳﺘﻔﺎده ﻧﻜﻨﻴﺪ. راﻫﻨﻤﺎﻳﻲ :از ﻳﻚ ﺗﺎﺑﻊ ﺑﺎزﮔﺸﺘﻲ اﺳﺘﻔﺎده ﻛﻨﻴﺪ.
160
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ # 6Lم در دو ﻓﺼﻞ ﻗﺒﻞ ﺗﻘﺮﻳﺒﺎ ﺑﺎ ﻧﺼﻒ اﻣﻜﺎﻧﺎت C++آﺷﻨﺎ ﺷﺪﻳﺪ .در اﻳﻦ ﻓﺼﻞ ﺑﺎ ﻧﺼﻒ دﻳﮕﺮ C++ﻳﻌﻨﻲ ﺑﺎ ﻗﻮي ﺗﺮﻳﻦ اﺑﺰار C++ﻛﻪ ﺑﺮگ ﺑﺮﻧﺪهي C++در ﺑﺮاﺑﺮ Cاﺳﺖ آﺷﻨﺎ ﻣﻲﺷﻮﻳﺪ .اﻳﻦ اﺑﺰار ﻛﻼس ) (classاﺳﺖ. ﺗﻘﺮﻳﺒﺎ ﻫﻤﻪي ﺑﺮﻧﺎﻣﻪﻫﺎي C++در ﻗﺎﻟﺐ ﻛﻼسﻫﺎ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮﻧﺪ .ﻛﻼسﻫﺎ ﻛﻤﻚ ﻣﻲﻛﻨﻨﺪ ﻛﻪ ارﺗﺒﺎﻃﻲ ﺑﻴﻦ دﻧﻴﺎي واﻗﻌﻲ و دﻧﻴﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﺑﺮﻗﺮار ﺷﻮد class .در واﻗﻊ ﻫﻤﺎن structاﺳﺖ ﻛﻪ در ﻓﺼﻞ اول ﺑﺎ آن آﺷﻨﺎ ﺷﺪﻳﺪ )ﺑﻪ ﺟﺰ ﻳﻚ ﺗﻔﺎوت ﺧﻴﻠﻲ ﺟﺮﺋﻲ( .ﺑﻪ ﻫـﺮ ﻛﻼس ﻣﻲﺷﻮد ﺑﻪ ﭼﺸﻢ ﻳﻚ ﻣﻮﺟﻮد واﻗﻌﻲ ﻧﮕﺎه ﻛﺮد ﻛﻪ وﻳﮋﮔﻲ- ﻫﺎﻳﻲ دارد و ﻛﺎرﻫﺎﻳﻲ را ﻣﻲﺗﻮاﻧﺪ اﻧﺠﺎم ﺑﺪﻫﺪ .ﻣﺜﻼ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﺑﻨﻮﻳﺴﻴﻢ ﻛﻪ ﺷﻄﺮﻧﺞ ﺑﺎزي ﻣﻲﻛﻨﺪ ﻣﻲ- ﺗﻮاﻧﻴﻢ ﻳﻚ ﻛﻼس ﺑﺎ ﻧﺎم boardداﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﻧﻘﺶ ﺻﻔﺤﻪي ﺷﻄﺮﻧﺞ را دارد .ﻳﻚ ﻛﻼس ﺑﺎ ﻧﺎم pawnداﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﻧﻘﺶ ﭘﻴﺎدهي ﺷﻄﺮﻧﺞ را ﺑﺎزي ﻣﻲﻛﻨﺪ و . ...ﻛﻼس pawnﻣﻲﺗﻮاﻧﺪ ﺗﺎﺑﻌﻲ ﺑﻪ ﻧﺎم moveداﺷﺘﻪ ﺑﺎﺷﺪ ﻛﻪ ﺣﺮﻛﺖ ﭘﻴﺎده را ﺑﻪ ﻋﻬﺪه دارد و ﻛﻼس boardﻣﻲﺗﻮاﻧﺪ ﻳﻚ آراﻳﻪي دوﮔﺎﻧﻪي 8در 8داﺷﺘﻪ ﺑﺎﺷﺪ.
Oسه
S Q1س !/ Q1ع * 1ا .C4ر Tن struct LFاﺳﺖ ﻛﻪ ﻗﺒﻼ ﻣﻌﺮﻓﻲ ﻛﺮدم .ﺑﺮاي اﻳﺠﺎد ﻳﻚ ﻛﻼس از اﻳﻦ ﻓﺮﻣﻮل اﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ: ]اclass [g6 { public: ;د456ر _^]\[)od (1ون opmار او k ;د456ر _^]\[)od (2ون opmار او k . . _^]\[ _(1) dZ _^]\[ _(2) dZ . . . ;}
161
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ د456رﻫﺎي _^]\[ ﻧﺒﺎﻳﺪ initializeﺷﻮﻧﺪ .اﮔﺮ ﻃﺮز اﺳﺘﻔﺎده از structﻫﺎ را ﻓﺮاﻣﻮش ﻛﺮدهاﻳﺪ ﺑﻬﺘﺮ اﺳﺖ دوﺑﺎره ﻧﮕﺎﻫﻲ ﺑﻪ آنﻫﺎ ﺑﻴﻨﺪازﻳﺪ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class Car { public: ;double speed )void SetSpeed(double x { ;speed = x } )(int GetSpeed { ;return speed } ;} )(int main { ;}Car myCar = {0 ;)myCar.SetSpeed(10 ;)(cout<< myCar.GetSpeed ;)(getch }
Output: 10
اول ،ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﺎم myCarاز ﻧﻮع Carﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد myCar .ﺷﺎﻣﻞ ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﻮع doubleﺑﻪ ﻧﺎم speedاﺳﺖ .ﺗﺎﺑﻊﻫﺎ ﺣﺎﻓﻈﻪاي از myCarاﺷﻐﺎل ﻧﻤﻲﻛﻨﻨﺪ )ﺑﺎ sizeofاﻣﺘﺤﺎن ﻛﻨﻴﺪ!( .ﻣﻲﺗﻮاﻧﺴﺘﻢ ﺑﺎ ﻧﻮﺷﺘﻦ myCar.speed = 10ﻣﻘﺪار speedدر myCarرا ﺗﻐﻴﻴﺮ ﺑﺪﻫﻢ اﻣﺎ اﻳﻦ ﻛﺎر را ﺑﺎ )( SetSpeedاﻧﺠﺎم دادهام .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺗﻮاﻧﻴﺪ ﺑﻪ ﺟﺎي classﺑﻨﻮﻳﺴﻴﺪ structو ﻫﻴﭻ ﭼﻴﺰ ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﺪ. ﻣﻲﺑﻴﻨﻴﻢ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﺗﺎﺑﻊﻫﺎي )( SetSpeedو)( GetSpeedﻫﺴﺖ ﻧﻴﺎزي ﺑﻪ دﺳﺘﺮﺳﻲ ﻣﺴﺘﻘﻴﻢ ﺑﻪ speed
ﻧﻴﺴﺖ .ﭘﺲ ﻣﻲﺗﻮاﻧﻴﻢ دﺳﺘﺮﺳﻲ ﺑﻪ speedرا ﻣﺤﺪود ﻛﻨﻴﻢ .ﺑﺮاي اﻳﻦ ﻛﺎر ﺑﻪ ﺟﺎي اﻳﻦ ﻛﻪ speedرا در ﻣﺤﺪودهي publicﻣﻌﺮﻓﻲ ﻛﻨﻴﻢ ﺑﺎﻳﺪ آن را در ﻣﺤﺪودهي privateﻣﻌﺮﻓﻲ ﻛﻨﻴﻢ ﻣﺜﻞ ﻣﺜﺎل زﻳﺮ )اﻣﺎ دﻳﮕﺮ ﻧﻤﻲﺷﻮد )ﺑﺎ } (={0ﺑﻪ myCarﻣﻘﺪار اوﻟﻴﻪ داد(: >#include <conio.h >#include <iostream ;using namespace std
162
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ class Car { private: ;double speed public: )void SetSpeed(double x { ;speed = x } )(int GetSpeed { ;return speed } ;} )(int main { ;Car myCar ;)myCar.SetSpeed(10 ;)(cout<< myCar.GetSpeed ;)(getch }
Output: 10
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ در)( mainﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ ; myCar.speed = 10ﭼﻮن دﺳﺘﺮﺳﻲ ﺑﻪ speedﻣﺤﺪود ﺷﺪه و ﻓﻘﻂ ﺗﺎﺑﻊﻫﺎي ﻣﻮﺟﻮد در ﻛﻼس ﺣﻖ اﺳﺘﻔﺎده و ﺗﻐﻴﻴﺮ آن را دارﻧﺪ .در اﻳﻦ ﻣﺜﺎل ﻣﻲﺷﻮد private:را ﺣﺬف ﻛﺮد زﻳﺮا در classاﮔﺮ ﻣﺸﺨﺺ ﻧﺸﻮد ﺑﻪ ﻃﻮر ،defaultﻫﻤﻪ ﭼﻴﺰ privateاﺳﺖ .در structﻫﻤﻪ ﭼﻴﺰ ﺑﻪ ﻃﻮر public ،defaultاﺳﺖ .اﻳﻦ ﺗﻨﻬﺎ ﻓﺮق structو classاﺳﺖ. ﺑﻪ ﻣﺘﻐﻴﺮﻫﺎي درون ﻳﻚ ﻛﻼس member variableو ﺑﻪ ﺗﺎﺑﻊﻫﺎي درون آن member functionﻳﺎ methodﻣﻲﮔﻮﻳﻨﺪ. methodﻫﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻨﺪ privateﺗﻌﺮﻳﻒ ﺷﻮﻧﺪ )ﺗﻠﻔﻆ» :ﭘﺮاي وت« ﻣﻌﻨﻲ» :ﺧﺼﻮﺻﻲ«
( .ﺷﺎﻳﺪ ﺑﮕﻮﻳﻴﺪ ﭼﺮا
اﺻﻼ ﭼﻴﺰي را privateﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ .دﻟﻴﻞ آن اﻳﻦ اﺳﺖ ﻛﻪ در ،C++ﻫﺪف از اﻳﺠﺎد ﻳﻚ ﻛﻼس ،ﺳﺎﺧﺘﻦ ﻳﻚ ﺟﻮر ﻛﭙﺴﻮل اﺳﺖ ﻛﻪ ﻛﺎرﺑﺮ )ﻛﻪ اﻳﻦ ﺑﺎر ﻫﻤﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ اﺳﺖ( ﻗﺮار ﻧﻴﺴﺖ ﺑﻪ ﻫﻤﻪي ﻗﺴﺖﻫﺎي آن اﺣﺎﻃﻪ داﺷﺘﻪ ﺑﺎﺷﺪ .ﺑﺎ ﻛﻼس ﻓﻘﻂ ﭼﻴﺰﻫﺎﻳﻲ را در اﺧﺘﻴﺎر ﻛﺎرﺑﺮ ﻗﺮار ﻣﻲدﻫﻴﻢ ﻛﻪ ﺑﻪ درد او ﻣﻲﺧﻮرد .ﭼﺮا ﺑﺎﻳﺪ ذﻫﻦ او ﻣﺸﻐﻮل ﻳﺎد ﮔﺮﻓﺘﻦ ﻃﺮز اﺳﺘﻔﺎده از methodﻫﺎﻳﻲ ﺑﺸﻮد ﻛﻪ اﺻﻼ ﺑﺎ آنﻫﺎ ﻛﺎر ﻧﺪارد؟ در ﺿﻤﻦ اﺳﺘﻔﺎده از اﻳﻦ ﺟﻮر methodﻫﺎ ﻣﻤﻜﻦ اﺳﺖ در ﻛﺎر ﻛﻼس اﺧﺘﻼل اﻳﺠﺎد ﻛﻨﺪ و ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ اﻣﻦ ﻛﻪ از اﻫﺪاف ﻛﻼسﻫﺎﺳﺖ
163
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ از ﺑﻴﻦ ﺑﺮود .ﻣﺜﻼ ﻓﺮض ﻛﻨﻴﺪ ﻛﻼﺳﻲ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﺑﻪ ﻋﻨﻮان رﺷﺘﻪ از آن اﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ .اﻳﻦ ﻛﻼس ﺣﺘﻤﺎ ﻣﺘﻐﻴﺮﻫﺎ و ﺗﺎﺑﻊﻫﺎﻳﻲ ﺑﺮاي ﺗﺨﺼﻴﺺ ﺣﺎﻓﻈﻪ دارد .اﻣﺎ ﻛﺎرﺑﺮ ﻧﺒﺎﻳﺪ ﺑﻪ آنﻫﺎ دﺳﺘﺮﺳﻲ داﺷﺘﻪ ﺑﺎﺷﺪ ﭼﻮن آنﻫﺎ ﺑﻪ ﻃﻮر ﺧﻮدﻛﺎر در ﻣﻮﻗﻊ ﻟﺰوم ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮﻧﺪ .ﻳﻚ ﻛﻼس ﻣﺜﻞ ﻳﻚ ﺳﺎزﻣﺎن )ﻣﺜﻼ ﺑﻴﻤﺎرﺳﺘﺎن( اﺳﺖ ﻛﻪ ﻣﺮاﺟﻌﻴﻦ ﻓﻘﻂ ﺑﻪ ﺑﻌﻀﻲ ﺑﺨﺶﻫﺎي آن ﻣﺮاﺟﻌﻪ ﻣﻲﻛﻨﻨﺪ .ﻫﻤﺮاه ﺑﻴﻤﺎر در اﺗﺎق ﻣﺮاﻗﺒﺖﻫﺎي وﻳﮋه ﺟﺎﻳﻲ ﻧﺪارد .ﻫﻤﻴﻦ ﻣﺤﺪودﻳﺖ- ﻫﺎﺳﺖ ﻛﻪ ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻧﻈﻢ ﻣﻲدﻫﺪ و اﺷﺘﺒﺎﻫﺎت را در ﺑﺮﻧﺎﻣﻪي ﻧﻬﺎﻳﻲ ﻛﻢ ﻣﻲﻛﻨﺪ.
constructorو destructor
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻣﻮﻗﻊ اﻳﺠﺎد ﻣﺘﻐﻴﺮ از ﻧﻮع ﻳﻚ ﻛﻼس ﻛﺎري اﻧﺠﺎم ﺑﺸﻮد ،از constructorاﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ. constructorﻳﻚ ِ methodﻫﻢ اﺳﻢ ﺑﺎ ﺧﻮد ﻛﻼس اﺳﺖ و ﺧﺮوﺟﻲ ﻧﺪارد .در ﻣﺜﺎل زﻳﺮ ﻧﺤﻮهي ﻛﺎر constructorرا ﻣﻲﺑﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class Cat { public: Cat() // constructor { ;)(Meow(); Meow ;cout<< endl ;)(Meow(); Meow ;age = 0 } private: )(void Meow { ;"cout<< "Meow\n } public: ;int age ;} )(int main { ;Cat Tom ;Tom.age = 3 ;)(getch }
164
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: Meow Meow Meow Meow
ﺗﺎﺑﻊ Meow() privateﺗﻌﺮﻳﻒ ﺷﺪه Cat() constructor .اﺳﺖ .ﻣﻲﺑﻴﻨﻴﺪ ﻫﻴﭻ ﺧﺮوﺟﻲاي ﺑﺮاي آن ﻣﺸﺨﺺ ﻧﺸﺪه و ﺑﻪ ﻃﻮر ﻛﻠﻲ ﻧﺒﺎﻳﺪ ﺑﺮاي constructorﺧﺮوﺟﻲ ﻣﺸﺨﺺ ﻛﺮد Cat() .ﭼﻨﺪ ﺑﺎر ﺗﺎﺑﻊ )( Meowرا ﻣﻲﺧﻮاﻧﺪ و ﻣﺘﻐﻴﺮ ageرا ﺻﻔﺮ ﻗﺮار ﻣﻲدﻫﺪ .ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﺮﺗﻴﺐ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮﻫﺎ و methodﻫﺎ در ﻳﻚ ﻛﻼس ﻣﻬﻢ ﻧﻴﺴﺖ .درﺿﻤﻦ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ دو ﺑﺎر از ﻛﻠﻤﻪي ﻛﻠﻴﺪي publicاﺳﺘﻔﺎده ﺷﺪه. در ﺗﺎﺑﻊ )( mainﻣﺴﺘﻘﻴﻤﺎ constructorﺧﻮاﻧﺪه ﻧﺸﺪه اﺳﺖ وﻟﻲ ﭼﻮن ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﻮع Catﺗﻌﺮﻳﻒ ﺷﺪه، constructorﻓﺮا ﺧﻮاﻧﺪه ﻣﻲﺷﻮد. constructorﻧﻤﻲﺗﻮاﻧﺪ ﺧﺮوﺟﻲ داﺷﺘﻪ ﺑﺎﺷﺪ وﻟﻲ ﻣﻲﺗﻮاﻧﺪ ﭘﺎراﻣﺘﺮ داﺷﺘﻪ ﺑﺎﺷﺪ و ﻣﻲﺗﻮاﻧﺪ overloadﺷﻮد ﻳﻌﻨﻲ ﻣﻲﺷﻮد ﭼﻨﺪ constructorداﺷﺖ .اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class Cat { public: )(Cat { ;"cout<< "not a mouse } )Cat(int a { ;cout<< "a cat with age: " << a << endl } )Cat(int a, char* c { ;cout<< c << endl } ;} )(int main { ;)"Cat Tom(2,"I am Tom ;)Cat Romeo(3 ;Cat Juliet ;)(getch }
165
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: I am Tom a cat with age: 3 not a mouse
ﺑﻪ ﻧﺤﻮهي ﺗﻌﺮﻳﻒ ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع Catﺗﻮﺟﻪ ﻛﻨﻴﺪ .آرﮔﻮﻣﺎنﻫﺎ ﺑﻌﺪ از ﻣﺘﻐﻴﺮ ﻧﻮﺷﺘﻪ ﺷﺪهاﻧﺪ compiler .ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﻧﻮع و ﺗﻌﺪاد ﻣﺘﻐﻴﺮﻫﺎ constructorﻣﻨﺎﺳﺐ را اﻧﺘﺨﺎب ﻣﻲﻛﻨﺪ. ﻧﻮعﻫﺎي ﻃﺒﻴﻌﻲ ﻣﺜﻞ intرا ﻫﻢ ﻣﻲﺷﻮد ﻧﻮﻋﻲ ﻛﻼس ﺑﻪ ﺣﺴﺎب آورد ﻛﻪ ﺣﺪاﻗﻞ دو constructorدارد .ﻳﻜﻲ ﺑﺪون ﭘﺎراﻣﺘﺮ و ﻳﻜﻲ ﺑﺎ ﻳﻚ ﭘﺎراﻣﺘﺮ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;)int a(23 ;int b ;cout<< a ;)(getch }
Output: 23
ﭘﺲ 23ﺑﻪ ﻋﻨﻮان ﻣﻘﺪار اوﻟﻴﻪي aدر ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﺪه .اﻣﺎ ﺗﺎ ﺑﻪ ﺣﺎل ﻣﻘﺪار اوﻟﻴﻪ را ﺑﺎ = ﻣﻲدادﻳﻢ .ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ constructorﻫﺎي ﺗﻚ ﭘﺎراﻣﺘﺮي را ﻣﻲﺷﻮد ﺑﺎ = ﻫﻢ ﻓﺮاﺧﻮاﻧﺪ: >#include <conio.h >#include <iostream ;using namespace std class Cat { public: )(Cat { ;"cout<< "not a mouse } )Cat(int a { ;cout<< "a cat with age: " << a << endl } ;} )(int main { ;Cat Tom = 2
166
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ getch(); }
Output: a cat with age: 2 int ﺑﻪ ﻃﻮر ﻧﻤﺎدﻳﻦ ﻣﻲﺗﻮاﻧﻴﻢ ﺗﺼﻮر ﻛﻨﻴﻢ. )ﻣﺜﻞ( دادن ﻣﻘﺪار اوﻟﻴﻪ اﺳﺖconstructor ﭘﺲ دادن ﭘﺎراﻣﺘﺮ ﺑﻪ
:ﻳﻚ ﻛﻼس اﺳﺖ ﺑﻪ اﻳﻦ ﺷﻜﻞ class int { int a; public: int(){} int(int x) { a = x; } };
.اﻟﺒﺘﻪ اﻳﻦ ﺗﺼﻮر ﻛﺎﻣﻞ ﻧﻴﺴﺖ و ﺑﺎ آﺷﻨﺎﻳﻲ ﺑﻴﺶ ﺗﺮ ﺑﺎ ﻛﻼس ﻣﻲﺗﻮاﻧﺪ ﻛﻢ ﻛﻢ ﻛﺎﻣﻞ ﺗﺮ ﺷﻮد در ﻣﺜﺎل زﻳﺮ ﻳﻚ رﺷﺘﻪ ﺑﻪ ﻳﻚ. ﺗﺒﺪﻳﻞ ﻧﻮع ﺑﻪ آن ﻛﻼس را اﻣﻜﺎن ﭘﺬﻳﺮ ﻣﻲﻛﻨﺪ، ِ ﻳﻚ ﻛﻼسconstructor : ﻣﻲﺷﻮدcast ﻛﻼس #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat() // constructor 1 { // does nothing } Cat(char* str) // constructor 2 { cout<< "The constructor called\n"; cout<< str << "\n\n"; } }; int main() { (Cat)"hello"; // explicit casting Cat Tom; Tom = "Tom"; // implicit casting getch(); }
Output:
167
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ The constructor called hello The constructor called Tom
در واﻗﻊ " "Tomﻛﻪ ﻧﻮﻋﺶ * charاﺳﺖ ﺑﻪ \ Cat cast nﺷﺪه .در اﻳﻦ ﻣﺜﺎل ﺑﻪ ﺟﺎي ;"(Cat)"hello
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;)"Cat("hello
و اﻳﻦ دو ﺗﺎ ﺑﺎ ﻫﻢ ﻓﺮﻗﻲ ﻧﺪارﻧﺪ .ﺑﻪ ﺟﺎي ' (int)'aﻫﻢ ﻣﻲﺷﻮد ﻧﻮﺷﺖ )'.int('a اﮔﺮ ﺑﺮاي ﻛﻼﺳﻲ ﻣﺜﻞ ﻛﻼس Cat constructorﻫﺎﻳﻲ ﺑﻨﻮﻳﺴﻴﻢ ﻛﻪ ﻫﻤﻪ ﭘﺎراﻣﺘﺮ دارﻧﺪ )ﺣﺪاﻗﻞ ﻳﻚ ﭘﺎراﻣﺘﺮ( دﻳﮕﺮ ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: ;Cat Tom
و ﺣﺘﻤﺎ دادن ﭘﺎراﻣﺘﺮ )ﻳﻌﻨﻲ آرﮔﻮﻣﺎن
( ﻻزم اﺳﺖ .ﻣﺜﻼ اﮔﺮ ﻳﻜﻲ از constructorﻫﺎ دو ﭘﺎراﻣﺘﺮ ﺑﺎ ﻧﻮع int
داﺷﺘﻪ ﺑﺎﺷﺪ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;)Cat Tom(2,3
دادن آرﮔﻮﻣﺎن ﺑﻪ constructorرا initializeﻛﺮدن ﻳﺎ دادن ﻣﻘﺪار اوﻟﻴﻪ ﻣﻲﮔﻮﻳﻴﻢ. ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻗﺒﻼ ﮔﻔﺘﻢ اﮔﺮ constructorي ﺗﻨﻬﺎ ﻳﻚ ﭘﺎراﻣﺘﺮ داﺷﺘﻪ ﺑﺎﺷﺪ ﻣﻲﺷﻮد ﺑﺮاي initializeﻛﺮدن از »=« اﺳﺘﻔﺎده ﻛﺮد. ﺑﻪ ﻫﺮ ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع ﻳﻚ ﻛﻼس ﻳﻚ ) objectﻳﺎ (instanceاز آن ﻛﻼس ﻣﻲﮔﻮﻳﻴﻢ .در ﻣﺜﺎل ﻗﺒﻞ Tomﻳﻚ objectاز ﻧﻮع Catاﺳﺖ .ﺑﺮاي ﻛﻼس Catدر ﻣﺜﺎل ﻗﺒﻞ )" Cat("TTﻳﻚ objectاز ﻧﻮع Catاﺳﺖ .در واﻗﻊ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﮕﻮﻳﻴﻢ constructorﻳﻚ Catﻣﻲﺳﺎزد و آن را ﺑﺮﻣﻲﮔﺮداﻧﺪ )ﻳﻌﻨﻲ returnﻣﻲﻛﻨﺪ(. constructorﺑﻪ ﻣﻌﻨﻲ »ﺳﺎزﻧﺪه« اﺳﺖ. >#include <conio.h >#include <iostream ;using namespace std class Cat { public:
168
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )Cat(int a, int b { ;"cout<< "Constructor called\n } ;} )(int main { ;)Cat Tom = Cat(1,1 ;cout<< typeid(Cat(1,1)).name() << endl ;))cout<< sizeof(Cat(1,1 ;)(getch }
Output (BDS 2006): Constructor called Cat 8 Output (Visual C++ 2005): Constructor called class Cat 1
) ِ Cat(1,1اوﻟﻲ constructor ،را ﻓﺮا ﻣﻲﺧﻮاﻧﺪ ﻳﻚ objectاز ﻧﻮع Catدرﺳﺖ ﻣﻲﻛﻨﺪ .دو ) ِ Cat(1,1ﺑﻌﺪي constructorرا ﻓﺮا ﻧﻤﻲﺧﻮاﻧﻨﺪ ﭼﻮن آرﮔﻮﻣﺎنﻫﺎي typeidو sizeofﻫﺴﺘﻨﺪ .ﻣﻲ- ﺑﻴﻨﻴﺪ ﻛﻪ در compilerﻫﺎي Borlandو Microsoftاﺳﻢ ﻛﻼس ﺑﺎ typeidﻣﺘﻔﺎوت ﺑﻴﺎن ﻣﻲﺷﻮد )ﻣﺜﻞ structﻳﺎ enumﻛﻪ ﻗﺒﻼ دﻳﺪﻳﺪ( .ﻛﻼس Catﻫﻴﭻ ﻣﺘﻐﻴﺮي ﻧﺪارد ﭘﺲ ﻇﺎﻫﺮا ﺑﺎﻳﺪ ) sizeاﻧﺪازه( آن ﺻﻔﺮ ﺑﺎﺷﺪ اﻣﺎ compilerﻫﺎي ﻣﺨﺘﻠﻒ ﻫﺮ ﻛﺪام ﭼﻴﺰي اﻋﻼم ﻣﻲﻛﻨﻨﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﺑﻪ ﺟﺎي ;)Cat Tom = Cat(1,1
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;)class Cat Tom = Cat(1,1
ﺣﺘﻲ ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;)struct Cat Tom = Cat(1,1
ﻧﻤﻲﺷﻮد ﻳﻚ constructorرا ﺑﻪ ﻋﻨﻮان ﻳﻚ methodﻓﺮاﺧﻮاﻧﺪ ﻣﺜﻼ در ﻣﺜﺎل ﺑﺎﻻ ﻧﻤﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ ).Tom.Cat(1,2 constructorﻳﻌﻨﻲ »ﺳﺎزﻧﺪه« ﻳﻌﻨﻲ ﻣﺘﻐﻴﺮ را ﻣﻲﺳﺎزد .ﺣﺎل ﻛﻪ ،constructorﻣﺘﻐﻴﺮ را ﻣﻲﺳﺎزد ﻣﻮﻗﻊ از ﺑﻴﻦ رﻓﺘﻦ ﻣﺘﻐﻴﺮ ،ﻣﺘﻐﻴﺮ ﺑﺎﻳﺪ ﺧﺮاب ﺷﻮد .ﻣﻲﺗﻮاﻧﻴﻢ ﻳﻚ methodﺧﺮاب ﻛﻨﻨﺪه ﻫﻢ داﺷﺘﻪ ﺑﺎﺷﻴﻢ .ﺑﻪ methodﺧﺮاب
169
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻫﻢ اﺳﻢ، ﻫﻢdestructor . ﺧﺮوﺟﻲ ﻧﺪاردconstructor ﻣﺎﻧﻨﺪdestructor . ﻣﻲﮔﻮﻳﻨﺪdestructor ﻛﻨﻨﺪه constructor ، ﺑﺮ ﻋﻜﺲdestructor .ﻛﻼس اﺳﺖ ﺑﻪ ﺟﺰ اﻳﻦ ﻛﻪ ﺟﻠﻮي اﺳﻢ آن ﻳﻚ »~« ﻗﺮار ﻣﻲﮔﻴﺮد را ﺑﺮ ﻋﻜﺲdestructor . ﻛﺮدoverload ﻧﻤﻲﺗﻮاﻧﺪ ﭘﺎراﻣﺘﺮ داﺷﺘﻪ ﺑﺎﺷﺪ و ﺑﻨﺎﺑﺮاﻳﻦ ﻧﻤﻲﺷﻮد آن را : ﻓﺮاﺧﻮاﻧﺪmethod ﻣﻲﺷﻮد ﻣﺜﻞ ﻳﻚconstructor #include <conio.h> #include <iostream> using namespace std; class Mouse { public: int a; Mouse() { cout<< "Constructor called\n"; } ~Mouse() // dectructor { cout<< "Destructor called\n"; } }; int main() { { Mouse Jerry; } getch(); }
Output: Constructor called Destructor called
. ﺑﻪ ﻣﺤﺾ ﺧﺮوج از ﺑﻠﻮك اﻳﻦ ﻣﺘﻐﻴﺮ ﺑﺎﻳﺪ از ﺑﻴﻦ ﺑﺮود. ﺗﻌﺮﻳﻒ ﺷﺪهMouse از ﻧﻮعJerry در ﻳﻚ ﺑﻠﻮك ﻣﺘﻐﻴﺮ . ﻫﻢ ﺧﻮد ﺑﻪ ﺧﻮد از ﺑﻴﻦ ﻣﻲرودJerry.a ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮد وdestructor
copy constructor
:prototype اﮔﺮ ﺗﺎﺑﻌﻲ ﺑﺎ اﻳﻦ void f(Cat pussy_cat);
170
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﻣﺎ ﭼﻪ.( ﻣﻲﺷﻮدcopy) ﻛﭙﻲpussy_cat درTom ﭼﻪ اﺗﻔﺎﻗﻲ ﻣﻲاﻓﺘﺪ؟f(Tom) داﺷﺘﻪ ﺑﺎﺷﻴﻢ و ﺑﻨﻮﻳﺴﻴﻢ ﻳﻜﻲ ﻳﻜﻲ ﻛﭙﻲCat ﺟﻮري ﻛﭙﻲ ﻣﻲﺷﻮد؟ اﮔﺮ ﻧﺤﻮهي ﻛﭙﻲ ﺷﺪن را ﻣﺸﺨﺺ ﻧﻜﻨﻴﻢ ﻣﺘﻐﻴﺮﻫﺎي ﺗﻌﺮﻳﻒ ﺷﺪه در :ﻣﻲﺷﻮﻧﺪ ﻣﺜﻞ اﻳﻦ #include <conio.h> #include <iostream> using namespace std; class Cat { public: int a; int b; }; void f(Cat pussy_cat) { cout<< pussy_cat.a << endl; cout<< pussy_cat.b; } int main() { Cat Tom = {2,5}; f(Tom); getch(); }
Output: 2 5
. ﻣﻘﺪار ﻣﺴﺎوي دارﻧﺪTom.a وpussy_cat.a ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ copy constructor اﻳﻦ ﻛﺎر ﺑﻪ وﺳﻴﻠﻪي.ﻣﻤﻜﻦ اﺳﺖ ﺑﺨﻮاﻫﻴﻢ ﻧﺤﻮهي ﻛﭙﻲ ﻛﺮدن را ﺧﻮدﻣﺎن ﻣﺸﺨﺺ ﻛﻨﻴﻢ اﺳﺖ ﻛﻪ ﺗﻨﻬﺎconstructor ﻳﻚCat ﺑﺮاي ﻳﻚ ﻛﻼس ﻣﺜﻞcopy constructor ﻳﻚ.اﻣﻜﺎن ﭘﺬﻳﺮ اﺳﺖ : اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ. داردCat& ﻳﻚ ﭘﺎراﻣﺘﺮ ﺑﺎ ﻧﻮع #include <conio.h> #include <iostream> using namespace std; class Cat { public: int a; int b; Cat(int aParam, int bParam) { a = aParam; b = bParam;
171
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } Cat(Cat& argument) { cout<< "copy constructor called\n"; a = argument.b; b = argument.a; } }; void f(Cat pussy_cat) { cout<< pussy_cat.a << endl; cout<< pussy_cat.b; } int main() { Cat Tom(2,5); f(Tom); getch(); }
Output: copy constructor called 5 2
ﻗﺮار ﻣﻲﮔﻴﺮدpussy_cat درTom ﻣﻮﻗﻌﻲ ﻛﻪ.(5 و2 ﭼﺎپ ﺷﺪه )ﻧﻪ2 و5 ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺑﺮ ﺧﻼف ﻣﻌﻤﻮل ﻫﻢ ﺗﻌﺮﻳﻒCat ﻣﻲﺗﻮاﻧﻴﻢ ﻣﺘﻐﻴﺮي ﺑﺎ ﻧﻮعCat ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ در ﻛﻼس. ﺧﻮاﻧﺪه ﻣﻲﺷﻮدcopy constructor .( داردCat ﻧﻮعcopy constructor ﻛﻨﻴﻢ )ﭘﺎراﻣﺘﺮ ﻧﺪارد ﻣﺘﻐﻴﺮﻫﺎ ﺑﻪ ﻃﺮﻳﻖcopy constructor ،Zoo ﺣﺎﻻ ﺑﻪ ﻣﺜﺎل زﻳﺮ دﻗﺖ ﻛﻨﻴﺪ ﻛﻪ ﻧﺸﺎن ﻣﻲدﻫﺪ وﻗﺘﻲ ﻛﻼس :ﻋﺎدي ﻳﻜﻲ ﻳﻜﻲ ﻛﭙﻲ ﻣﻲﺷﻮﻧﺪ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default constructor Cat(Cat& argument) // copy constructor { cout<< "Cat copy constructor called\n"; } }; class Zoo { public: Cat Romeo; Cat Juliet;
172
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Zoo(){} // default constructor }; void f(Zoo aZoo){} int main() { Zoo London_Zoo; f(London_Zoo); getch(); }
Output: Cat copy constructor called Cat copy constructor called
ِ ﺷﺪنcopy ﻧﺪارد ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻣﻮﻗﻊcopy constructor دارد وﻟﻲCat دو ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮعZoo ﻣﻲﺑﻴﻨﻴﺪ ﻳﻚLondon_Zoo) . ﻓﺮاﺧﻮاﻧﺪه ﺷﺪهاﻧﺪJuliet وRomeo ﻫﺎيcopy constructor ،London_Zoo ﻗﺼﺪ ﻣﻦ از اﻳﻦ ﻣﺜﺎل اﺻﻼ ﺗﻮﻫﻴﻦ ﺑﻪ »ﺑﺎغ وﺣﺶ ﻟﻨﺪن« ﻳﺎ ﺑﻪ »روﻣﺌﻮ. copy داﺷﺘﻪ ﺑﺎﺷﺪ دﻳﮕﺮZoo copy constructor اﻣﺎ اﮔﺮ.(
ﺑﺎغ وﺣﺶ اﺳﺖ ﻛﻪ ﺗﻨﻬﺎ دو ﮔﺮﺑﻪ دارد و ژوﻟﻴﺖ« ﻧﻴﺴﺖ ﺑﺪ ﺑﺮداﺷﺖ ﻧﻜﻨﻴﺪ
: ﺧﻮاﻧﺪه ﻧﻤﻲﺷﻮدCat ِ ﻛﻼسconstructor #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default constructor Cat(Cat& argument) // copy constructor { cout<< "Cat copy constructor called\n"; } }; class Zoo { public: Cat Romeo; Cat Juliet; Zoo(){} // default constructor Zoo(Zoo& aZoo) { cout<< "Zoo copy constructor called\n"; } }; void f(Zoo aZoo){} int main() { Zoo London_Zoo;
173
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ f(London_Zoo); getch(); }
Output: Zoo copy constructor called
از آن ﺟﺎﻳﻲ ﻛﻪ. واﮔﺬار ﺷﺪهZoo ﻣﻮﺟﻮد درcopy constructor در واﻗﻊ وﻇﻴﻔﻪي ﻛﭙﻲ ﻛﺮدن ﻛﺎﻣﻼ ﺑﻪ اﺳﺘﻔﺎده ﻛﻨﻴﻢ و اﻳﻦ ﻛﺎر ﺑﻪ ﻃﻮر ﻃﺒﻴﻌﻲ اﻣﻜﺎنCat ﻣﻮﺟﻮد درcopy constructor ﻣﻤﻜﻦ اﺳﺖ ﺑﺨﻮاﻫﻴﻢ از : را اﻧﺠﺎم دﻫﺪ ﻣﺜﻼ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢcopy constructor داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﻛﺎرCat ﻧﺪارد ﺑﻬﺘﺮ اﺳﺖ ﺗﺎﺑﻌﻲ در #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default constructor void CopyFunction(Cat& pussy) { cout<< "copying Cat\n"; } Cat(Cat& argument) // copy constructor { CopyFunction(argument); } }; class Zoo { public: Cat Romeo; Cat Juliet; Zoo(){} // default constructor Zoo(Zoo& aZoo) { Romeo.CopyFunction(aZoo.Romeo); Juliet.CopyFunction(aZoo.Juliet); cout<< "Zoo copy constructor called\n"; } }; void f(Zoo aZoo){} int main() { Zoo London_Zoo; f(London_Zoo); getch(); }
Output:
174
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ copying Cat copying Cat Zoo copy constructor called
)از ﺟﻤﻠﻪconstructor را ﺗﻌﺮﻳﻒ ﻛﺮدهام اﻳﻦ اﺳﺖ ﻛﻪ ﻧﻤﻲﺷﻮد ﻳﻚCopyFunction() ﻋﻠﺖ اﻳﻦ ﻛﻪ .( را ﺑﻪ ﺷﻜﻠﻲ ﻛﻪ در اﻳﻦ ﺟﺎ ﻧﻴﺎز دارﻳﻢ ﻓﺮاﺧﻮاﻧﺪcopy constructor داﺷﺘﻪ ﺑﺎﺷﺪconstructor در ﺑﺎﻻ ﭼﻪZoo ﻫﺎ ﻧﻤﻲاﻓﺘﺪ ﻳﻌﻨﻲconstructor ﺑﺪ ﻧﻴﺴﺖ ﺑﮕﻮﻳﻢ ﻛﻪ اﻳﻦ اﺗﻔﺎق ﺑﺮاي : ﻓﺮا ﺧﻮاﻧﺪه ﻣﻲﺷﻮدCat ِ constructor ،Zoo ﻫﻨﮕﺎم ﺗﻌﺮﻳﻒ ﻳﻚ ﻣﺘﻐﻴﺮ ﺟﺪﻳﺪ ﺑﺎ ﻧﻮع،ﭼﻪ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat() // default constructor { cout<< "Cat constructor\n"; } }; class Zoo { public: Cat Romeo; Cat Juliet; Zoo() // default constructor { cout<< "Zoo constructor\n"; } }; int main() { Zoo London_Zoo; getch(); }
Output: Cat constructor Cat constructor Zoo constructor
ﻣﻮﺟﻮد درcopy constructor ﺑﻪ ﻋﻬﺪهيZoo درCat ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﮔﺮﭼﻪ وﻇﻴﻔﻪي ﻛﭙﻲ ﻛﺮدن ﻣﺘﻐﻴﺮﻫﺎي در اﻳﻦC++ ﻣﻦ ﻓﻜﺮ ﻣﻲﻛﻨﻢ. ﺧﻮدﺷﺎن اﺳﺖconstructor اﺳﺖ وﻟﻲ وﻇﻴﻪي ﺳﺎﺧﺘﻦ آنﻫﺎ ﺑﻪ ﻋﻬﺪهيZoo .ﻣﻮارد ﻛﻤﻲ ﺿﻌﻒ دارد ﻛﻪ ﺑﺎﻳﺪ اﺻﻼح ﺷﻮد 175
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ داﺷﺘﻪ ﺑﺎﺷﺪ )ﭼﻮن در اﻳﻦCat ﻧﻮعCat& ﻧﻤﻲﺗﻮاﻧﺪ ﺑﻪ ﺟﺎي ﻧﻮعCat ِ ﻛﻼسcopy constructor ﭘﺎراﻣﺘﺮ ﻣﻲﺗﻮاﻧﺪ ﻳﻜﻲ از اﻳﻦ اﺷﻜﺎل را داﺷﺘﻪcopy constructor ﺑﻪ ﻃﻮر ﻛﻠﻲ.(ﺻﻮرت ﺧﻮدش ﺑﻪ ﻛﭙﻲ ﺷﺪن ﻧﻴﺎز دارد :ﺑﺎﺷﺪ Cat(Cat&) Cat(const Cat&) Cat(const Cat&, int = 1); Cat(const Cat&, int = 1 , char = 8); . .
: ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ. ﻗﺎﺑﻞ ﺣﺬف اﺳﺖconst ﻛﻪ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){}// default costructor Cat(Cat& argument, int = 5 ,char y = 4) // copy constructor { cout<< "copy constrcutor\n"; } }; void f(Cat pussy){} int main() { Cat Romeo,Juliet; f(Romeo); getch(); }
Output: copy constructor
: ﻓﺮا ﺧﻮاﻧﺪه ﻣﻲﺷﻮدcopy constructor ﻫﻢreturn ﻣﻮﻗﻊ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat() { // do nothing }
176
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Cat(const Cat& pussy) { cout<< "copy constructor"; } }; Cat f() { Cat Tom; return Tom; } int main() { f(); _getch(); }
Output: copy constructor Tom داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﺑﺎﻳﺪ ﻳﻚ ﻛﭙﻲ ازmain() ﺑﺮاي اﻳﻦ ﻛﻪ ﺷﻴﺌﻲ در. اﺳﺖf() ﻳﻚ ﺷﻲء درTom در واﻗﻊ
. اﻧﺠﺎم ﻣﻲدﻫﺪcopy constructor اﻳﻦ ﻛﺎر را.ﺗﻬﻴﻪ ﻛﻨﻴﻢ : ﻓﺮا ﺧﻮاﻧﺪه ﺷﺪهcopy constructor در ﻣﺜﺎل زﻳﺮ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default costructor Cat(const Cat& argument) // copy constructor { cout<< "copy constructor\n"; } }; int main() { Cat Tom; Cat kitten = Tom; getch(); }
Output: copy constructor
ﺣﺎل اﮔﺮ ﺑﻪ ﺟﺎي. ﺑﺎ ﺗﺴﺎوي ﻓﺮاﺧﻮاﻧﺪه ﺷﺪهCat ﺗﻚ ﭘﺎراﻣﺘﺮي ﻛﻼسconstructor در اﻳﻦ ﻣﺜﺎل Cat kitten = Tom;
177
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﻨﻮﻳﺴﻴﻢ: ;Cat& kitten = Tom
ﻳﺎ ﺑﻨﻮﻳﺴﻴﻢ: ;)Cat& kitten(Tom
دﻳﮕﺮ copy constructorﻓﺮا ﺧﻮاﻧﺪه ﻧﻤﻲﺷﻮد .اﻳﻦ ﻓﻘﻂ در ﻣﻮرد copy constructorدرﺳﺖ اﺳﺖ.
explicit
از ﻓﺼﻞﻫﺎي ﮔﺬﺷﺘﻪ ﺑﻪ ﻳﺎد دارﻳﺪ ﻛﻪ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;'char ch = 'a ;int a = ch
و ﺑﻪ ﻃﻮر ﭘﻨﻬﺎن ) implicit) chﻛﻪ ﻧﻮع charدارد ﺑﻪ ﻳﻚ intﺗﺒﺪﻳﻞ ﻣﻲﺷﻮد .و ﻣﻲداﻧﻴﺪ ﻛﻪ ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: ;int* p = new int ;int a = p
و ﺑﻪ ﺟﺎي آن ﺑﺎﻳﺪ ﻧﻮﺷﺖ: ;int* p = new int ;int a = (int) p
ﺑﻪ اﻳﻦ ﺟﻮر ﺗﺒﺪﻳﻞ ﻧﻮع ،ﺗﺒﺪﻳﻞ ﻧﻮع آﺷﻜﺎر ) (explicitﻣﻲﮔﻮﻳﻴﻢ. ﮔﻔﺘﻢ ﻛﻪ اﮔﺮ ﻛﻼس Catﻳﻚ constructorﺑﻪ ﺷﻜﻞ )Cat(int a }{...
داﺷﺘﻪ ﺑﺎﺷﺪ آن وﻗﺖ ﻣﻲﺷﻮد ﻳﻚ ﺗﺒﺪﻳﻞ ﻧﻮع ﭘﻨﻬﺎن داﺷﺖ: ;Cat Tom = 2
ﺣﺎﻻ اﮔﺮ ﻧﺨﻮاﻫﻴﻢ ﻳﻚ ،constructorاﺟﺎزهي ﺗﺒﺪﻳﻞ ﻧﻮع ﭘﻨﻬﺎن ﺑﺪﻫﺪ ﻛﺎﻓﻲ اﺳﺖ ﺟﻠﻮي ﺗﻌﺮﻳﻒ constructorﺑﻨﻮﻳﺴﻴﻢ explicitﺗﺎ ﺗﻨﻬﺎ ﺗﺒﺪﻳﻞ ﻧﻮع آﺷﻜﺎر ﻗﺎﺑﻞ اﻧﺠﺎم ﺑﺎﺷﺪ: >#include <conio.h >#include <iostream ;using namespace std class Cat { public:
178
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )explicit Cat(int d { ;"cout<< "Cat(int d)\n } ;}
// error
)(int main { ;// Cat Romeo = 2 ;Cat Tom = (Cat) 2 ;)(getch }
Output: )Cat(int d
ﺣﺎل ﻣﺎ ﻣﻲﺗﻮاﻧﻴﻢ ﺗﺼﻮر ﺧﻮدﻣﺎن را از intﺑﻪ ﻋﻨﻮان ﻛﻼس ﺑﻪ اﻳﻦ ﺻﻮرت ﻛﺎﻣﻞ ﺗﺮ ﻛﻨﻴﻢ: class int { ;int a public: }{)(int )int(int x { ;a = x } )explicit int(char* c { ;a = (int) c } ;}
ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ اﻳﻦ ﻓﻘﻂ ﻳﻚ ﺗﺼﻮر اﺳﺖ ﻛﻪ ﻣﺎ را ﺑﺎ intآﺷﻨﺎ ﺗﺮ ﻣﻲﻛﻨﺪ .وﮔﺮﻧﻪ اﻳﻦ ﺧﻄﻮط در C++ اﺳﺎﺳﺎ ﺧﻄﺎ دارﻧﺪ.
this
Eض Cat .ﻳﻚ ﻛﻼس ﺑﺎﺷﺪ و Tomﻳﻚ objectﺑﺎ ﻧﻮع Catو Catﻳﻚ methodداﺷﺘﻪ ﺑﺎﺷﺪ .آﻳﺎ اﻳﻦ methodﺑﻪ Tomدﺳﺘﺮﺳﻲ دارد؟ ﻣﺜﻼ ﻣﻲﺗﻮاﻧﺪ Tomرا returnﻛﻨﺪ؟ ﺟﻮاب ﻣﺜﺒﺖ اﺳﺖ .در ﻫﺮ methodاز ﻳﻚ ﻛﻼس ﺑﺎ ﻛﻠﻤﻪي ﻛﻠﻴﺪي thisﻣﻲﺷﻮد ﺑﻪ objectي ﻛﻪ از ﻧﻮع آن ﻛﻼس ﺳﺎﺧﺘﻪ ﺷﺪه )و
179
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ methodاز ﻃﺮﻳﻖ آن ﻓﺮاﺧﻮاﻧﻲ ﻣﻲﺷﻮد( دﺳﺘﺮﺳﻲ داﺷﺖ .ﻣﺜﻼ در ﻫﺮ methodدر Catﻛﻪ ﺑﻪ وﺳﻴﻠﻪي Tom
ﻓﺮاﺧﻮاﻧﺪه ﺷﻮد thisﻳﻚ pointerﺑﻪ Tomاﺳﺖ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class Boy { public: ;int intelligence )(Boy f { ;intelligence = 5 ;this -> intelligence = 5 ;return *this } ;} )(int main { ;Boy Hubert ;)(Boy Hub = Hubert.f ;cout<< Hub.intelligence ;)(_getch }
Output: 5
روﺷﻦ اﺳﺖ ﻛﻪ intelligenceو this->intelligenceﻳﻜﻲ ﻫﺴﺘﻨﺪ .در اﻳﻦ ﺟﺎ thisوﻗﺘﻲ ﺑﻪ ﻛﺎر رﻓﺘﻪ ﻛﻪ ﺗﺎﺑﻊ )( fﺑﺎ Hubertﻓﺮاﺧﻮاﻧﺪه ﺷﺪه و ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺑﻪ Hubertاﺷﺎره دارد .در واﻗﻊ this
ﻫﻤﺎن &Hubertاﺳﺖ.
operatorه
وﻗﺘﻲ aو int ،bﺑﺎﺷﻨﺪ ،ﻣﻲﺷﻮد آنﻫﺎ را ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻛﺮد و a+bﻫﻢ \ int nاﺳﺖ .ﺣﺎﻻ اﮔﺮ Romeoو Julietدو ﺷﻲء ﺑﺎ ﻧﻮع Catﺑﺎﺷﻨﺪ ،آﻳﺎ ﻣﻲﺷﻮد آنﻫﺎ را ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻛﺮد؟ operatorﻫﺎ اﻳﻦ ﻛﺎر را اﻣﻜﺎن ﭘﺬﻳﺮ ﻣﻲﻛﻨﻨﺪ) operator .ﻋﻤﻠﮕﺮ( در واﻗﻊ ﻳﻚ ﺗﺎﺑﻊ اﺳﺖ .ﻣﺜﻼ operator +ﻳﻚ ﺗﺎﺑﻊ ﺑﺎ دو ﭘﺎراﻣﺘﺮ اﺳﺖ. وﻟﻲ ﺑﺮاي ﻓﺮاﺧﻮاﻧﻲ ،operatorﺑﻪ ﺟﺎي اﻳﻦ ﻛﻪ ﺑﻨﻮﻳﺴﻴﻢ ) +(2,3ﻣﻲﻧﻮﻳﺴﻴﻢ .2+3ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream
180
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; class Cat { public: Cat(){} // default costructor Cat(const Cat& argument) // copy constructor { cout<< "copy constructor\n"; } }; #pragma argsused Cat operator + (Cat pussy1, Cat pussy2) { return pussy1; } int main() { Cat Romeo, Juliet; Cat kitten; kitten = Romeo + Juliet; getch(); }
Output: copy constructor copy constructor copy constructor
ﻣﻌﻨﻲ دارد و ﻛﺎر آنBorland ﻫﺎيcompiler ﺗﻨﻬﺎ در#pragma argsused ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻣﻲداﻧﻴﺪ ﻧﻮﺷﺘﻦ آن در. اﺳﺖ ﻛﻪ ﺑﻪ ﺧﺎﻃﺮ اﺳﺘﻔﺎده ﻧﻜﺮدن از ﻳﻚ ﭘﺎراﻣﺘﺮ ﺑﻪ وﺟﻮد ﻣﻲآﻳﺪwarning ﺣﺬف ﻳﻚ ﻓﺮاcopy constructor ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺳﻪ ﺑﺎر. درﺳﺖ ﻣﻲﻛﻨﺪwarning ﻳﻚMicrosoft ﻫﺎيcompiler اﮔﺮ ﺑﺨﻮاﻫﻴﻢ دو ﺗﺎ از اﻳﻦﻫﺎ ﻛﻢ ﺷﻮد ﻣﻲﺗﻮاﻧﻴﻢ ﺑﺎ اﺿﺎﻓﻪ.return دو ﺗﺎ ﺑﺮاي ﭘﺎراﻣﺘﺮﻫﺎ و ﻳﻜﻲ ﺑﺮاي.ﺧﻮاﻧﺪه ﺷﺪه : ﺑﺮﻧﺎﻣﻪ را ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ،& ﻛﺮدن دو #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default costructor Cat(const Cat& argument) // copy constructor { cout<< "copy constructor\n"; } };
181
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #pragma argsused Cat& operator + (Cat& pussy1, Cat pussy2) { return pussy1; } int main() { Cat Romeo, Juliet; Cat kitten; kitten = Romeo + Juliet; getch(); }
Output: copy constructor
اﮔﺮ در. ﻫﻢ & ﺑﮕﺬارﻳﺪpussy2 ﺧﻮاﻧﺪه ﻧﺸﻮد ﻗﺒﻞ از ﭘﺎراﻣﺘﺮcopy constructor اﮔﺮ ﻣﻲﺧﻮاﻫﻴﺪ اﺻﻼ اﻳﺮادBDS 2006 (update1)، ﺣﺬف ﺷﻮدconst ﻛﻠﻤﻪي ﻛﻠﻴﺪيcopy constructor ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ در و ﻧﺴﺨﻪﻫﺎي ﻗﺪﻳﻤﻲMicrosoft ِ compiler . اﺳﺖcompiler ﻣﻲﮔﻴﺮد و اﺟﺮا ﻧﻤﻲﺷﻮد ﻛﻪ ﺑﻪ ﻧﻈﺮ اﺷﻜﺎل از ﺗﺎﺑﻊoperator اﮔﺮ ﺑﻪ ﺟﺎيBDS 2006 وﻟﻲ. اﻳﻦ اﺷﻜﺎل را ﻧﻤﻲﮔﻴﺮﻧﺪBorland ﻫﺎيcompiler ﺗﺮ .ﺑﮕﺬارﻳﻢ اﻳﺮاد ﻧﻤﻲﮔﻴﺮد : ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ. ﺑﺎﺷﺪpostfix ﻳﺎprefix آﺷﻨﺎ ﻫﺴﺘﻴﺪ و ﻣﻲداﻧﻴﺪ ﻛﻪ ﻣﻲﺗﻮاﻧﺪ++ ﺑﺎ ﻋﻤﻠﮕﺮ #include <conio.h> #include <iostream> using namespace std; class Cat {}; void operator ++(Cat& pussy) // prefix { cout<< "prefix\n"; } void operator ++(Cat& pussy,int) // postfix { cout<< "postfix\n"; } int main() { Cat Tom; ++Tom; // prefix Tom++; // postfix _getch(); }
Output:
182
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ prefix postfix
ﺗﻌﺪاد ﭘﺎراﻣﺘﺮﻫﺎي ﻫﺮ ،operatorﻣﺸﺨﺺ اﺳﺖ و ﻧﻤﻲﺷﻮد اﻳﻦ ﺗﻌﺪاد را ﺗﻐﻴﻴﺮ داد اﻣﺎ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ در ﺑﺎﻻ دو ﺑﺎر operator ++را ﺗﻌﺮﻳﻒ ﻛﺮدهام و در ﻳﻜﻲ از ﺗﻌﺮﻳﻒﻫﺎ ﺑﻴﺶ از ﻳﻚ ﭘﺎرﻣﺘﺮ ﺑﻪ ﻛﺎر ﺑﺮدهام .اﻳﻦ در واﻗﻊ اﻣﻜﺎﻧﻲ اﺳﺖ ﻛﻪ زﺑﺎن C++در اﺧﺘﻴﺎر ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻗﺮار داده ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺑﺘﻮاﻧﺪ ﺑﮕﻮﻳﺪ ﻛﻪ ++ﺑﻪ ﻋﻨﻮان ﻳﻚ postfixﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد .ﭘﺎراﻣﺘﺮ دوم اﺻﻼ اﺳﺘﻔﺎده ﻧﻤﻲﺷﻮد .ﺑﺮاي ﺗﻮﺿﻴﺢ ﺑﻴﺶ ﺗﺮ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ ++ﻣﻲﺗﻮاﻧﺪ ﺑﻪ ﻋﻨﻮان prefixاﺳﺘﻔﺎده ﺑﺸﻮد ﻣﺜﻞ ++aﻳﺎ ﺑﻪ ﻋﻨﻮان postfixاﺳﺘﻔﺎده ﺑﺸﻮد ﻣﺜﻞ .a++اﮔﺮ operator ++ﺑﻪ ﻋﻨﻮان prefixﺗﻌﺮﻳﻒ ﺷﻮد ﻣﻲﺗﻮاﻧﻴﻢ آن را ﺑﻪ ﻋﻨﻮان postfixﻫﻢ ﺑﻪ ﻛﺎر ﺑﺒﺮﻳﻢ اﻣﺎ ﻳﻚ warningﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد .ﻋﻜﺲ اﻳﻦ اﻣﻜﺎن ﻧﺪارد ﻳﻌﻨﻲ اﮔﺮ operator ++ﺑﻪ ﻋﻨﻮان postfixﺗﻌﺮﻳﻒ ﺷﻮد ﻧﻤﻲﺷﻮد آن را ﺑﻪ ﻋﻨﻮان prefixﺑﻪ ﻛﺎر ﺑﺮد. ﻣﻲداﻧﻴﻢ ﻛﻪ وﻗﺘﻲ aﻳﻚ intﺑﺎﺷﺪ ﺑﺎ ﻣﻘﺪار ،2ﺧﺮوﺟﻲ ++aﺑﺮاﺑﺮ ﺑﺎ 3و ﺧﺮوﺟﻲ a++ﺑﺮاﺑﺮ ﺑﺎ 2اﺳﺖ .ﺑﻬﺘﺮ اﺳﺖ ﻣﺎ ﻫﻢ اﻳﻦ ﻗﺎﻋﺪه را در ﺧﺮوﺟﻲ postfixو prefixرﻋﺎﻳﺖ ﻛﻨﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std class Cat { public: ;int a ;} Cat operator ++(Cat& pussy) // prefix { ;pussy.a++ ;Cat ret = pussy ;return ret } Cat operator ++(Cat& pussy,int) // postfix { ;Cat ret = pussy ;pussy.a++ ;return ret } )(int main { ;}Cat Tom = {2 ;}Cat Silvester = {2 cout<< (Tom++).a << endl; // output: 2 cout<< (++Silvester).a << endl; // output: 3 cout<< Tom.a << endl; // output: 3
183
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< Silvester.a
<< endl; // output: 3
_getch(); }
Output: 2 3 3 3
.اﻟﺒﺘﻪ ﻫﻤﻪ ﭼﻴﺰ در دﺳﺖ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ اﺳﺖ و اﮔﺮ او ﺑﺨﻮاﻫﺪ ﻣﻲﺗﻮاﻧﺪ ﺧﺮوﺟﻲ را ﻫﺮ ﭼﻴﺰي ﺑﮕﺬارد :– ﻫﻢ درﺳﺖ اﺳﺖ- ﮔﻔﺘﻢ ﺑﺮاي++ ﻣﺸﺎﺑﻪ ﭼﻴﺰﻫﺎﻳﻲ ﻛﻪ ﺑﺮاي #include <conio.h> #include <iostream> using namespace std; class Cat {}; void operator --(Cat& pussy) // prefix { cout<< "prefix\n"; } void operator --(Cat& pussy,int) // postfix { cout<< "postfix\n"; } int main() { Cat Tom; --Tom; // prefix Tom--; // postfix _getch(); }
Output: prefix postfix
:ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; class Cat {
184
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ public: ;int a ;} )Cat& operator - (Cat& pussy, int a { ;pussy.a -= a ;return pussy } )(int main { ;}Cat Tom = {20}, Kitty = {10 ;Tom - 2 = Kitty ;cout<< Tom.a ;)(_getch }
Output: 10 در اﻳﻦ ﺟﺎ Tom-2ﺑﺮ ﺧﻼف ﻣﻌﻤﻮل ﻳﻚ LValueاﺳﺖ زﻳﺮا ﻣﺎ اﻳﻦ ﻃﻮر ﺧﻮاﺳﺘﻪاﻳﻢ .در واﻗﻊ ﻧﻮﺷﺘﻦ Tom-
2=Kittyﺑﺎ Tom=Kittyﻓﺮﻗﻲ ﻧﺪارد .ﻧﻮﺷﺘﻦ Tom-3ﺳﻪ واﺣﺪ از Tom.aﻛﻢ ﻣﻲﻛﻨﺪ اﻟﺒﺘﻪ ﺑﻬﺘﺮ ﺑﻮد ﺑﺮاي اﻳﻦ ﻛﺎر از = operator -اﺳﺘﻔﺎده ﻣﻲﻛﺮدﻳﻢ. وﻗﺘﻲ ﻳﻚ operatorﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﻴﻢ ﺣﺪاﻗﻞ ﻳﻜﻲ از ﭘﺎراﻣﺘﺮﻫﺎي آن ﺑﺎﻳﺪ ) classﻳﺎ (structﺑﺎﺷﺪ. operatorﺟﺪﻳﺪ ﻧﻤﻲﺷﻮد ﺧﻠﻖ ﻛﺮد .ﻣﺜﻼ ** operatorﻧﺪارﻳﻢ .ﺗﻌﺪاد ﭘﺎراﻣﺘﺮﻫﺎي operatorﻫﺎي ﻣﻮﺟﻮد را ﻫﻢ ﻧﻤﻲﺷﻮد ﻋﻮض ﻛﺮد. ﺣﺎﻻ ﺑﮕﺬارﻳﺪ ﺗﻨﻬﺎ operatorﺳﻪ ﭘﺎراﻣﺘﺮي C++ﻳﻌﻨﻲ ?:را ﻣﻌﺮﻓﻲ ﻛﻨﻢ .اﻳﻦ operatorرا ﺑﺮاي ﻛﻼسﻫﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﻛﺎر ﺑﺒﺮﻳﻢ و ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻧﻤﻲﺗﻮاﻧﺪ آن را ﺗﻌﺮﻳﻒ ﻛﻨﺪ .ﻓﺮﻣﻮل اﻳﻦ operatorاﻳﻦ اﺳﺖ: ]opmار)4i Zd (2ع)opm] : [(2ار)4i Zd (1ع)23] ? [(1ار[/
ﻛﻪ ﻧﻮع ﺧﺮوﺟﻲ آن 4iع) (1ﻳﺎ 4iع) (2اﺳﺖ4i .ع) (1و 4iع)(2ﺑﺎﻳﺪ ﻳﻚ ﺟﻮر ﺑﺎﺷﻨﺪ ﻳﺎ ﺣﺪاﻗﻞ ﻳﻜﻲ از 4iع) (1ﻳﺎ 4iع) (2ﻗﺎﺑﻞ ﺗﺒﺪﻳﻞ ﺑﻪ دﻳﮕﺮي ﺑﺎﺷﺪ .اﻳﻦ ﻛﻪ ﻛﺪام ﻗﺎﺑﻞ ﺗﺒﺪﻳﻞ ﺑﻪ دﻳﮕﺮي ﺑﺎﺷﺪ ﺑﻪ compiler ﺑﺴﺘﮕﻲ دارد .اﮔﺮ ]23ار [/درﺳﺖ ﺑﺎﺷﺪ ﺧﺮوﺟﻲ اﻳﻦ ﻋـﺒﺎرت opmار) (1اﺳﺖ وﮔﺮﻧﻪ ﺧﺮوﺟﻲ opmار) (2اﺳﺖ .ﻣﺜﺎل زﻳﺮ ﻃﺮز اﺳﺘﻔﺎده از اﻳﻦ ﻋﻤﻠﮕﺮ را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std
185
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int main() { int a = 2; int b = 3; char* c = a > b ? "hello" : "bye"; cout<< c; _getch(); }
Output: bye
. دارﻧﺪchar* " اﺳﺖ ﻛﻪ ﻫﺮ دو ﻧﻮعbye" ( ﻫﻤﺎن2)ارopm " وhello" ( ﻫﻤﺎن1)ارopm در اﻳﻦ ﺟﺎ ﻧﺴﺒﺖc " ﺑﻪbye" ( ﻳﻌﻨﻲ2)ارopm ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ. اﺳﺖ ﻛﻪ در اﻳﻦ ﺟﺎ ﻧﺎدرﺳﺖ اﺳﺖa>b ﻫﻤﺎن/ار23 . ﭼﺎپ ﺷﺪهc در ﻧﻬﺎﻳﺖ.داده ﺷﺪه :در واﻗﻊ ﻓﺮﻣﻮل ﺑﺎﻻ ﻣﻌﺎدل اﺳﺖ ﺑﺎ اﻳﻦ f(/ار23,( 1) ع4i
Zd (1)ارopm,(2)ع4i Zd (2)ارopm)
: ﺑﻪ اﻳﻦ ﺷﻜﻞ ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮدf() ﻛﻪ در آن (1)ع4i f(bool a,(1)ع4i x,(2)ع4i y) {
(1)ع4i yy = y; // convert if(a) return x; else return yy; }
:ﻳﺎ اﻳﻦ ﻃﻮري ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد (2)ع4i f(bool a,(1)ع4i x,(2)ع4i y) {
(2)ع4i xx = x; // convert if(a) return xx; else return y; }
:ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; struct A { int a;
186
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }; int main() { char ch = 'a'; int a = 5; cout<< ( 5 < 'a' ? a : ch
) << endl;
A X = {1}, Y = {5}; cout<< typeid(false ? X : Y).name(); _getch(); }
Output (BDS 2006): 5 A Output (Visual Studio 2005): 5 struct A
را ﻣﻲﺷﻮدch ﻫﻢ ﻧﻮع ﻧﻴﺴﺘﻨﺪa وch ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﺑﺎ اﻳﻦ ﻛﻪ. ﭼﺎپ ﺷﺪهa ، ﮔﺰارهاي درﺳﺖ اﺳﺖ5<'a' ﭼﻮن اﻳﻦ را در ﻣﺜﺎل ﺑﻌﺪي. اﻳﺮادي )ﺑﺮاي ﻫﻢ ﻧﻮع ﻧﺒﻮدن( ﻧﻤﻲﮔﻴﺮدcompiler ﻛﺮد و ﺑﻨﺎﺑﺮاﻳﻦcast ،int n\ ﺑﻪ اﺳﺖ ﭼﺎپA را ﻛﻪ ﻫﻤﺎنX دوم ﻫﻢ ﺑﺎﻳﺪ ﮔﻔﺖ ﻛﻪ اﺳﻢ ﻧﻮع ﻣﺘﻐﻴﺮcout در ﻣﻮرد.ﺑﻪ ﻃﻮر واﺿﺢ ﺗﺮ ﺧﻮاﻫﻴﺪ دﻳﺪ .ﻣﻲﻛﻨﺪ #include <conio.h> #include <iostream> using namespace std; class A { public: int a; A(int pa) { cout<< "constructor\n"; } }; int main() { A a(1); int b = 2; cout<< typeid(a.a < b ? a : (A)b ).name(); _getch(); }
Output (BDS 2006):
187
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ constructor A Output (Visual C++ 2005): constructor class A
ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﺟﺎيVisual C++ 2005 در cout<< typeid(a.a < b ? a : (A)b ).name();
:ﺑﻨﻮﻳﺴﻴﻢ cout<< typeid(a.a < b ? a : b ).name();
، ﺗﺒﺪﻳﻞ ﺷﻮدA n\ ﻣﻲﺗﻮاﻧﺪ ﺑﻪb ﻋﻠﺖ اﻳﻦ ﻛﻪ. ﺷﻮدA cast ﻣﻲﺗﻮﻧﺪ ﺑﻪb و ﺧﺮوﺟﻲ ﺑﺮﻧﺎﻣﻪ ﻓﺮﻗﻲ ﻧﻤﻲﻛﻨﺪ ﭼﻮن . ﻣﻨﺎﺳﺐ اﺳﺖconstructor وﺟﻮد : داﺷﺘﻪ ﺑﺎﺷﻨﺪprototype ﻫﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻨﺪoperator #include <conio.h> #include <iostream> using namespace std; class Bubble{}; Bubble& operator << (Bubble& emp, int b); int main() { Bubble bout; bout<< 2 << 3 << 4; _getch(); } Bubble& operator << (Bubble& emp, int b) { cout<< b << endl; return emp; }
Output: 2 3 4
188
www.pupuol.com
â&#x20AC;Ťď&#x2DC;ď&#x2DC;ďť? ﺎďş&#x;ďť&#x160; ﺊďş?ﺸďŽ&#x2022;ďş&#x17D; ďť ďťŁďşŞďşďşłďťŞâ&#x20AC;Ź â&#x20AC;Ť ďźďťŽŮ&#x2020;â&#x20AC;Ź.â&#x20AC;Ť ďť&#x203A;Ů&#x201E; ďť&#x203A;â&#x20AC;Źmain() â&#x20AC;Ť اﺳďş&#x2DC;ďť&#x201D;ďş&#x17D;ŘŻŮ&#x2021; ďť&#x203A;ﺎدŮ&#x2021;ا ďş&#x2014;ďş&#x17D; ďş&#x2018;ďş&#x2DC;ا ďş&#x2014;ďť&#x152;ﺎďť&#x2019; عا ďş&#x2018; ďş&#x2018;ďť&#x152;ﺪ از ďş&#x2014;ďş&#x17D;ďş&#x2018;ďť&#x160;â&#x20AC;Źprototype â&#x20AC;ŤŘŻŘą ا ďş&#x;ďş&#x17D; از ďťłďť&#x161;â&#x20AC;Ź â&#x20AC;Ť Ů&#x2C6; ا ďť&#x203A;ďş&#x17D;Řą عا ﺡدâ&#x20AC;Źbout<< 2 << 3 â&#x20AC;Ť ďťłďť&#x153; اﺳďş&#x2013; ďş&#x2014;ا ďş&#x2018;ﺴâ&#x20AC;Źbout â&#x20AC;Ť اعâ&#x20AC;ŹV bout<< 2 â&#x20AC;ŤďťŁďť&#x2DC;ﺪاعâ&#x20AC;Ź .â&#x20AC;ŤďťŤďť¤ďť´ďťŚ ďť&#x192;ع ادا دادâ&#x20AC;Ź :â&#x20AC;Ť عا Ů&#x2C6;اداع ďş&#x2018; ﺧاﺪŮ&#x2020; Ů&#x2C6; ﺡďş&#x2DC; ďť&#x203A;سďş&#x17D;Ů&#x160; ﺧدďş&#x17D;Ů&#x2020; ďş&#x2018;ďť&#x153;â&#x20AC;Źcin â&#x20AC;Ť Ů&#x2C6;â&#x20AC;Źcout â&#x20AC;ŤďťŤďş&#x17D; ďş&#x2014;اâ&#x20AC;Źoperator â&#x20AC;Ťďş&#x2018;ďş&#x17D; اﺳďş&#x2DC;ďť&#x201D;ďş&#x17D;ŘŻŮ&#x2021; ازâ&#x20AC;Ź #include <conio.h> #include <iostream> using namespace std; class Int { public: int a; }; ostream& operator << (ostream&,Int& right_side) { cout<< right_side.a << endl; cout<< "printed\n"; return cout; } istream& operator >> (istream&,Int& right_side) { cin>> right_side.a; cout<< "got\n"; return cin; } int main() { Int a; cin>> a; cout<< a; _getch(); }
Output: 9 got 9 printed
â&#x20AC;Ť ďş&#x2018;ďť&#x2DC;Ů&#x160; ﺴďş&#x17D;ďş&#x2039;ďť&#x17E; ﺎďş&#x2018;ء ďş&#x2018; ا ďş&#x2018;ﺎďş&#x17D; ﺡďş&#x2019; ďş&#x2018;â&#x20AC;Ź.â&#x20AC;Ť داعدâ&#x20AC;Źistream â&#x20AC;Ť ؚâ&#x20AC;Źcin â&#x20AC;Ť Ů&#x2C6;â&#x20AC;Źostream â&#x20AC;Ť ؚâ&#x20AC;Źcout . *!6 .â&#x20AC;Ťďş&#x2018;ﺎďş&#x17D;Ů&#x160; ďť&#x2014;ďş&#x2019;ďť&#x17E; اﺳďş&#x2013;â&#x20AC;Ź
189
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در ﻳﻚmethod ﻫﺎ را ﺑﻪ ﻋﻨﻮان ﺗﺎﺑﻊﻫﺎي ﻋﻤﻮﻣﻲ دﻳﺪﻳﻢ اﻣﺎ ﻣﻲﺷﻮد آنﻫﺎ را ﺑﻪ ﻋﻨﻮانoperator ﺗﺎ ﺑﻪ ﺣﺎل وﻗﺘﻲ. ﺗﻌﺮﻳﻒ ﻛﺮدmethod ﻫﺎ را ﺗﻨﻬﺎ ﻣﻲﺷﻮد ﺑﻪ ﻋﻨﻮانoperator در واﻗﻊ ﺑﻌﻀﻲ از.ﻛﻼس ﻫﻢ ﺑﻪ ﻛﺎر ﺑﺮد ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﻴﻢ ﭘﺎراﻣﺘﺮ اول را ﺑﺎﻳﺪ ﺣﺬف ﻛﻨﻴﻢ ﭼﻮن ﺧﻮد ﺑﻪ ﺧﻮدmethod را ﺑﻪ ﻋﻨﻮانoperator ﻳﻚ :* ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن اول ﻗﺮار داردthis ﻓﺮض ﺑﺮ آن اﺳﺖ ﻛﻪ #include <conio.h> #include <iostream> using namespace std; class Int { public: int a; Int(int pa) { a = pa; } Int operator+(Int IntObject) { Int ret(a); ret.a += IntObject.a; return ret; } }; ostream& operator << (ostream&,Int& right_side) { cout<< right_side.a << endl; cout<< "printed\n\n"; return cout; } int main() { Int a(5); Int r = a + a; cout<< r; Int s = a + 2; cout<< s; _getch(); }
Output: 10 printed 7
190
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ printed
ﻧﻮﺷﺘﻪام a+aو ﻣﻘﺪارش را در rﮔﺬاﺷﺘﻪام r .ﻣﺠﻤﻮع دو aاﺳﺖ .ﺑﺎ ،coutﻛﻪ ﻗﺒﻼ ﺑﻪ آن ﻳﺎد دادهام ﭼﮕﻮﻧﻪ Intﻫﺎ را ﭼﺎپ ﻛﻨﺪ r ،را ﭼﺎپ ﻛﺮدم .ﺷﺎﻳﺪ ﺑﮕﻮﻳﻴﺪ ﭼﺮا ﺧﻴﻠﻲ راﺣﺖ ﻧﻨﻮﺷﺘﻢ ; .cout<<a+aﻋﻠﺖ اﺷﻜﺎﻟﻲ اﺳﺖ ﻛﻪ در compilerﻫﺎي Borlandوﺟﻮد دارد و از اﻳﻦ ﻃﺮز ﻧﻮﺷﺘﻦ اﻳﺮاد ﻣﻲﮔﻴﺮدVisual C++ 2005 . اﺷﻜﺎﻟﻲ ﻧﻤﻲﮔﻴﺮد .اﻳﻦ ﻳﻚ ﺿﻌﻒ ﻃﺮاﺣﻲ در compilerﻫﺎي Borlandاﺳﺖ )و ﻣﻦ ﺑﺎ ﻧﺎاﻣﻴﺪي آرزو ﻣﻲﻛﻨﻢ ﻛﻪ Borlandدر ﻛﻨﺎر ﺳﻮد اﻗﺘﺼﺎدي ﺑﻪ ﻛﻴﻔﻴﺖ compilerﻫﺎي ﺧﻮد ﻫﻢ ﻓﻜﺮ ﻛﻨﺪ
( .ﻧﻜﺘﻪي دﻳﮕﺮ در
اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﻳﻦ اﺳﺖ ﻛﻪ ﻧﻮﺷﺘﻪام a+2در ﺣﺎﻟﻲ ﻛﻪ ﻧﻮع int ،2اﺳﺖ ﻧﻪ .Intاﻳﻦ ﻫﻴﭻ اﻳﺮادي ﻧﺪارد ﭼﻮن constructorي ﻫﺴﺖ ﻛﻪ intرا ﺑﻪ Int castﻣﻲﻛﻨﺪ )ﺑﻪ ﻃﻮر ﭘﻨﻬﺎن(. ﻳﻚ operatorرا ﻣﻲﺷﻮد ﻛﺎﻣﻼ ﻣﺜﻞ ﻳﻚ ﺗﺎﺑﻊ ﻓﺮاﺧﻮاﻧﺪ: >#include <conio.h >#include <iostream ;using namespace std class Int { public: ;int a )int operator- (int x { ;return a - x } ;} )(int main { ;}Int a = {5 ;)cout<< a.operator- (3 ;)(_getch }
Output: 2
اﻣﺎ در ﻣﻮرد ++ﺑﻪ ﻋﻨﻮان prefixو :postfix >#include <conio.h >#include <iostream ;using namespace std class Int { public: ;int a )( void operator++
191
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { cout<< "prefix\n"; } void operator ++(int) { cout<< "postfix\n"; } }; int main() { Int a = {5}; a.operator++ (); // prefix a.operator++ (2); // postfix _getch(); }
Output: prefix postfix
. در اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﻣﻲﺷﻮد ﺑﺎ ﻫﺮ ﻋﺪد ﺻﺤﻴﺢ دﻳﮕﺮي ﻋﻮض ﻛﺮد ﭼﻮن از ﻣﻘﺪار آن اﺳﺘﻔﺎده ﻧﻤﻲﺷﻮد2 ﻋﺪد : اﺳﺖoperator[] ﻳﻜﻲ از آنﻫﺎ. ﺑﺎﺷﻨﺪmethod ﻫﺎ ﺑﺎﻳﺪ ﺣﺘﻤﺎoperator ﮔﻔﺘﻢ ﺑﻌﻀﻲ از #include <conio.h> #include <iostream> using namespace std; class Boy { public: int intelligence; void operator [](int a) { intelligence = a; } }; int main() { Boy Hub; Hub[136]; cout<< Hub.intelligence; _getch(); }
Output: 136
192
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ : اﺳﺖ ﺑﺎ ﺗﻌﺪاد ﭘﺎراﻣﺘﺮ دﻟﺨﻮاهoperator )( ﻫﻢ ﻳﻚ #include <conio.h> #include <iostream> using namespace std; class Boy { public: int intelligence; void operator ()(int a,int b) { intelligence = a + b; } }; int main() { Boy Hub; Hub(100,36); cout<< Hub.intelligence; _getch(); }
Output: 136
new
:ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; int main() { int* a = new int (123); cout<< *a; _getch(); }
Output: 123
: ﺣﺎل ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ.* ﻣﻘﺪار اوﻟﻴﻪ ﻣﻲدﻫﺪa ( ﺑﻪ123) ﭘﺲ #include <conio.h> #include <iostream> using namespace std;
193
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
// output: 123 // output: 1024
;)int (123 ;endl ;)(a) int (1024 ;endl
// output: 10
)int main(void { int* a = new << cout<< *a int* b = new << cout<< *a ;*a = 10 ;cout<< *b ;)(getch }
Output: 123 1024 10
در اﻳﻦ ﺟﺎ aﻣﺜﻞ ﻗﺒﻞ ﺑﻪ ﻃﻮر ﻋﺎدي ﺗﻌﺮﻳﻒ ﺷﺪه *b .ﺣﺎﻓﻈﻪاي اﺳﺖ ﻛﻪ روي *aﮔﺮﻓﺘﻪ ﺷﺪه اﺳﺖ و ﺑﺮاي ﻫﻤﻴﻦ aو bﻣﺴﺎوي ﻫﺴﺘﻨﺪ .ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ وﻗﺘﻲ ﺑﻪ *aﻣﻘﺪار ﻣﻲدﻫﻴﻢ ﻣﻘﺪار *bﻫﻢ ﻋﻮض ﻣﻲﺷﻮد. >#include <conio.h >#include <iostream ;using namespace std )(int main { ;)int* a = new(nothrow) int(8 ;cout<< *a ;delete a ;)(getch }
Output: 8 در اﻳﻦ ﻣﺜﺎل ﺣﺎﻓﻈﻪاي ﺑﺮاي ﻳﻚ intاﺧﺘﺼﺎص داده ﺷﺪه و ﺑﺎ initialize ،8ﺷﺪه اﺳﺖ .وﺟﻮد )(nothrow
ﻳﺎ ) (std::nothrowﺑﺎﻋﺚ ﻣﻲﺷﻮد ﻛﻪ در ﺻﻮرت ﻋﺪم اﺧﺘﺼﺎص ﺣﺎﻓﻈﻪ raise ،exceptionﻧﺸﻮد و ﻓﻘﻂ ﺻﻔﺮ ﺑﺮﮔﺮداﻧﺪه ﺷﻮد. newرا ﻣﻲﺗﻮاﻧﻴﻢ overloadﻛﻨﻴﻢ )در ﻛﻞ از ﻧﻈﺮ اﺻﻄﻼﺣﻲ ﻳﻚ ﻋﻤﻠﮕﺮ ) (operatorرا ﺗﻌﺮﻳﻒ ﻧﻤﻲﻛﻨﻴﻢ ﺑﻠﻜﻪ overloadﻣﻲﻛﻨﻴﻢ(.
ﺣﺎل ﺑﻪ ﻣﺜﺎﻟﻲ از overloadﻛﺮدن operator newﺗﻮﺟﻪ ﻛﻨﻴﺪ: 194
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <conio.h> #include <iostream> using namespace std; class Int { public: int a; int b; void* operator new(unsigned x) { cout<< "new operator called\n"; return &x; } }; int main() { new Int; // called new Int [5]; // not called cout<< typeid(*new Int).name(); // not called _getch(); }
Output (BDS 2006): new operator called Int Output (Visual C++ 2005): new operator called class Int
:ﭼﻨﺪ ﻧﻜﺘﻪ ﻫﺴﺖ . ﺑﺎﺷﺪvoid* ( ﺧﺮوﺟﻲ ﺑﺎﻳﺪ1 ي ﻛﻪ در ﻳﻜﻲ ازtypedef )ﻳﺎ ﺑﺎunsigned ﺑﺎﻳﺪ ﻧﻮعoperator new ( اوﻟﻴﻦ ﭘﺎراﻣﺘﺮ2 . ( داﺷﺘﻪ ﺑﺎﺷﺪsize_t ﻫﺎ ﻫﺴﺖ ﻧﻮعheader file ﺷﺪه اﺳﺖ و ﺑﺮاي ﻫﻤﻴﻦ اﺳﺘﻔﺎده از ﻧﻮع ﺑﺮداري )ﻳﻌﻨﻲnew overload ( در اﻳﻦ ﻣﺜﺎل ﻓﻘﻂ ﻧﻮع اﺳﻜﺎﻟﺮ3 . ﻣﺎ ﻧﺸﺪه اﺳﺖoperator در اﻳﻦ ﻣﺜﺎل ﺑﺎﻋﺚ ﻓﺮاﺧﻮاﻧﻲnew (آراﻳﻪاي . اﺳﺖ ﻓﺮاﺧﻮاﻧﺪه ﻧﻤﻲﺷﻮدtypeid آرﮔﻮﻣﺎنnew ( وﻗﺘﻲ4 . داردInt* ( ﺧﺮوﺟﻲ در ﻫﺮ ﺣﺎل ﻧﻮع5
195
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ . را ﻧﺪارﻳﻢthis وb وa ﺣﻖ اﺳﺘﻔﺎده ازoperator new ( در ﺗﻌﺮﻳﻒ6 :ﻣﺜﺎل زﻳﺮ ﻛﺎﻣﻞ ﺗﺮ اﺳﺖ #include <conio.h> #include <iostream> using namespace std; class Int { public: int a; int b; Int(){}; // default constructor; Int(const Int&) // copy constrcutor { cout<< "copy constructor\n"; } void* operator new(unsigned x,int y, double z) { cout<< "new operator called\n"; cout<< "y: " << y << endl; cout<< "z: " << z << endl; cout<< "x: " << x << endl; return &Int(); } }; int main() { Int t; Int* u = new(3,2.3) Int (t); getch(); }
Output: new operator called y: 3 z: 2.3 x: 8 copy constructor
- ﭘﺎراﻣﺘﺮ اول را ﻧﺎدﻳﺪه ﻣﻲ، ﻣﻮﻗﻊ ﻓﺮاﺧﻮاﻧﻲ. اﺿﺎﻓﻪ ﺷﺪهoperator new در اﻳﻦ ﻣﺜﺎل ﭘﺎراﻣﺘﺮﻫﺎي دﻳﮕﺮي ﻫﻢ ﺑﻪ در ﭘﺮاﻧﺘﺰ ﻗﺮارInt ﻛﻪ ﺑﻌﺪ ازt ﻣﺘﻐﻴﺮ. ﻟﻴﺴﺖ ﻣﻲﻛﻨﻴﻢnew ﮔﻴﺮﻳﻢ و ﻣﻘﺪارﻫﺎي ﭘﺎراﻣﺘﺮﻫﺎي ﺑﻌﺪي را ﺑﻌﺪ از ﭘﺎراﻣﺘﺮ اول ﻳﻌﻨﻲ. اﺳﺘﻔﺎده ﻣﻲﺷﻮدcopy constructor ﺑﺮاي دادن اﻳﻦ ﻣﻘﺪار اوﻟﻴﻪ از. ﻣﻘﺪار اوﻟﻴﻪ اﺳﺖ،ﮔﺮﻓﺘﻪ را در ﺑﺮ دارد و ﺑﺪون اﻳﻦ ﻛﻪ ﻣﺎ ﺑﻪ آن ﻣﻘﺪار ﺑﺪﻫﻴﻢ ﺧﻮد ﺑﻪsizeof(Int) ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎي ﻣﻮرد ﻧﻴﺎز ﻳﻌﻨﻲx
196
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﺳﺖ ﻳﺎ ازheap اﺧﺘﺼﺎص داده ﻣﻲﺷﻮد ازnew ﺑﻪ ﻧﻈﺮ ﺷﻤﺎ ﺣﺎﻓﻈﻪاي ﻛﻪ در اﻳﻦ ﺟﺎ ﺑﺎ.ﺧﻮد ﻣﻘﺪار ﻣﻲﮔﻴﺮد ؟stack new overload ﻫﻤﻴﻦ ﺑﺮﻧﺎﻣﻪ را ﺑﺎ ﺑﺎ ﺗﻐﻴﻴﺮات ﺑﺴﻴﺎر ﺟﺰﺋﻲ ﻣﻲﺷﻮد ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ داد ﻛﻪ ﺷﻜﻞ ﺑﺮداري
:(ﻣﻲﺷﻮد )و دﻳﮕﺮ ﻣﻘﺪار دادن اوﻟﻴﻪ ﺑﻪ ﺷﻜﻞ ﻗﺒﻞ ﻣﻌﻨﻲ ﻧﺪارد #include <conio.h> #include <iostream> using namespace std; class Int { public: int a; int b; Int(){}; // default constructor; Int(const Int&) // copy constrcutor { cout<< "copy constructor\n"; } void* operator new[] (unsigned x,int y, double z) { cout<< "new operator called\n"; cout<< "y: " << y << endl; cout<< "z: " << z << endl; cout<< "x: " << x << endl; return &Int(); } }; int main() { Int* u = new(3,2.3) Int [4]; getch(); }
Output: new operator called y: 3 z: 2.3 x: 32
دارد ﻛﻪ32 در اﻳﻦ ﺟﺎ ﻣﻘﺪارx . ﺧﻮاﻧﺪه ﻧﺸﺪهcopy constructor ﭼﻮن اﻳﻦ ﺟﺎ ﻣﻘﺪار اوﻟﻴﻪاي در ﻛﺎر ﻧﻴﺴﺖ . اﺳﺖInt ﺑﺮاﺑﺮ ﺑﺎ ﺣﺠﻢ ﭼﻬﺎر را ﻣﻲﺷﻮد ﺗﻨﻬﺎ ﺑﻪ ﻳﻜﻲ از ﺷﻜﻞﻫﺎيoperator delete[] ﻳﺎoperator delete در ﻳﻚ ﻛﻼس :زﻳﺮ ﺗﻌﺮﻳﻒ ﻛﺮد
197
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ void void void void
operator operator operator operator
delete(void* a) {...} delete(void* a,unsigned x) {...} delete[](void* a) {...} delete[](void* a,unsigned x) {...}
ﻣﻨﺎﺳﺐ )ﺑﺮ ﺣﺴﺐnew constructor ،در ﻫﺮ ﺣﺎل ﭼﻪ ﺧﻮدﻣﺎن ﺗﻌﺮﻳﻒ ﻛﺮده ﺑﺎﺷﻴﻢ ﭼﻪ ﺗﻌﺮﻳﻒ ﻧﻜﺮده ﺑﺎﺷﻴﻢ را ﺑﺮاي ﻳﻚ ﻛﻼس ﻣﺜﻞnew اﮔﺮ ﻋﻤﻠﮕﺮ. را ﻓﺮا ﻣﻲﺧﻮاﻧﺪdestructor ،delete ﺗﻌﺪاد و ﻧﻮع ﭘﺎراﻣﺘﺮﻫﺎ( و ﻫﻤﻴﺸﻪ، در ﻧﻈﺮ ﺑﮕﻴﺮﻳﻢCat ﺗﺨﺼﻴﺺ داده ﻣﻲﺷﻮد؛Cat اول ﺣﺎﻓﻈﻪاي ﺑﺮاي ﻳﻚ ﺷﻲء ﺟﺪﻳﺪ ﺑﺎ ﻧﻮع ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮد و ﺑﻪ ﺷﻜﻠﻲ ﻧﺎﺷﻴﺎﻧﻪ درCat ﻣﻲﺷﻮد ﺑﻪoperator new return ﺑﻌﺪ آن ﭼﻴﺰي ﻛﻪ در ﺗﻌﺮﻳﻒ ﺷﻲء ﺟﺪﻳﺪ ﻛﭙﻲ ﻣﻲﺷﻮد؛ . ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮدconstructor در ﻧﻬﺎﻳﺖ :ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; int Globe = 1111; class Cat { public: int age; Cat() { cout<< "Cat constructor\n"; age++; } void* operator new(unsigned) { cout<< "operator new\n"; return &Globe; } }; int main() { Cat* Tom_Home = new Cat; cout<< *( (int*) Tom_Home ) << endl; cout<< Tom_Home -> age << endl; getch(); }
// output: 1112 // output: 1112
Output: operator new Cat constructor 1112 1112
198
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻧﻮﺷﺘﻪام اﺟﺮا ﻣﻲﺷﻮد ﺑﻌﺪoperator new اول دﺳﺘﻮراﺗﻲ ﻛﻪ ﺧﻮد ﻣﻦ ﺑﺮاي ﺗﻌﺮﻳﻒnew Cat ﺑﺎ ﻧﻮﺷﺘﻦ 1111 ﻣﻲﺷﻮد )ﻣﻘﺪارcopy ﺷﺪه در آن ﺑﻪ ﻃﻮر ﻧﺎﺷﻴﺎﻧﻪايreturn ﺑﻪ وﺟﻮد ﻣﻲآﻳﺪ و ﻣﻘﺪارCat ﺷﻴﺌﻲ از ﻧﻮع
- ﻳﻜﻲ زﻳﺎد ﻣﻲage ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮد )ﻣﻘﺪارdefault constructor ﻗﺮار ﻣﻲﮔﻴﺮد( و در ﻧﻬﺎﻳﺖage در داردCat* ﻛﻪ ﻧﻮعTom_Home ﻳﻌﻨﻲ اول.( (* ﭼﺎپ ﺷﺪهint*) Tom_Home ) اولcout ﺑﺎ.(ﺷﻮد اﻳﻦ ﻣﻘﺪار ﻫﻤﺎن ﻣﻘﺪاري اﺳﺖ ﻛﻪ ﺑﺎ ﭼﻬﺎر. ﺷﺪه و ﺑﻌﺪ ﻣﻘﺪاري ﻛﻪ ﺑﻪ آن اﺷﺎره ﻣﻲﺷﻮد ﭼﺎپ ﺷﺪهint* cast ﺑﻪ . ﭼﺎپ ﺷﺪهage در آﺧﺮ.* درﺳﺖ ﻣﻲﺷﻮدTom_Home ﺑﺎﻳﺖ اول :ﻣﺜﺎل زﻳﺮ ﻛﺎﻣﻞ ﺗﺮ اﺳﺖ #include <conio.h> #include <iostream> using namespace std; int Globe = 1111; class Cat { public: int age; Cat(int a) { cout<< "Cat constructor\n"; age = a; } ~Cat() { cout<< "Cat destructor\n"; age = -235; } void* operator new(unsigned) { cout<< "operator new\n"; return &Globe; } void operator delete(void*) { cout<< "operator delete\n"; } }; int main() { Cat* Tom_Home = new Cat(235); // operator new // Cat constructor cout<< Tom_Home -> age << endl; // 235 delete Tom_Home; // Cat destructor // operator delete cout<< Tom_Home -> age << endl; // -235 getch(); }
199
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: operator new Cat constructor 235 Cat destructor operator delete -235
ﺑﻌﺪ ازoperator delete ﺧﻮاﻧﺪه ﻣﻲﺷﻮد وﻟﻲconstructor ﻗﺒﻞ ازoperator new operator delete وoperator new اﮔﺮ ﺗﻌﺮﻳﻒﻫﺎي ﺧﻮدﻣﺎن. ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮدdestructor
: ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮﻧﺪdestructor وconstructor را ﺣﺬف ﻛﻨﻴﻢ ﺑﺎز ﻫﻢ #include <conio.h> #include <iostream> using namespace std; int Globe = 1111; class Cat { public: int age; Cat(int a) { cout<< "Cat constructor\n"; age = a; } ~Cat() { cout<< "Cat destructor\n"; age = -235; } }; int main() { Cat* Tom_Home = new Cat(235); cout<< Tom_Home -> age << endl; delete Tom_Home; cout<< Tom_Home -> age << endl; getch(); }
Output: Cat constructor 235 Cat destructor 9371816
200
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ conversion operator
دﻳﺪﻳﻢ ﻛﻪ اﮔﺮ ﻛﻼس Catﻳﻚ constructorﺑﻪ ﺷﻜﻞ ) Cat(intداﺷﺘﻪ ﺑﺎﺷﺪ ،ﻫﺮ intرا ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ Cat castﻛﻨﻴﻢ .آﻳﺎ ﻋﻜﺲ اﻳﻦ ﻫﻢ ﻣﻤﻜﻦ اﺳﺖ؟ آﻳﺎ ﻣﻲﺷﻮد ﻳﻚ Catرا ﺑﻪ int castﻛﺮد؟ اﻳﻦ ﻛﺎر وﻗﺘﻲ اﻣﻜﺎن ﭘﺬﻳﺮ اﺳﺖ ﻛﻪ conversion operatorﻣﻨﺎﺳﺐ در ﻛﻼس Catﺗﻌﺮﻳﻒ ﺷﺪه ﺑﺎﺷﺪ: >#include <conio.h >#include <iostream ;using namespace std class Int { public: ;int a )( operator int { ;"cout<< "conversion operator\n ;return a } ;} )(int main { ;}Int A = {5 ;int x = A ;cout<< x ;)(getch }
Output: conversion operator 5
Aﺑﺎ conversion operatorﺑﻪ intﺗﺒﺪﻳﻞ ﺷﺪه اﺳﺖ conversion operator .ﭘﺎراﻣﺘﺮ و ﺧﺮوﺟﻲ ﻧﺪارد وﻟﻲ در آن از returnاﺳﺘﻔﺎده ﻣﻲﺷﻮد ﺗﺎ ﻧﻮع ﻣﻮرد ﻧﻈﺮ returnﺷﻮد.
= operator
درﺑﺎرهي = operatorﭼﻴﺰي اﺿﺎﻓﻪ ﺑﺮ آن ﭼﻪ در ﻣﻮرد operatorﮔﻔﺘﻢ ﻧﺪارم .ﻣﻤﻜﻦ اﺳﺖ ﻗﺒﻼ ﺑﻪ اﻳﻦ ﻓﻜﺮ ﻛﺮده ﺑﺎﺷﻴﺪ ﻛﻪ اﻳﻦ operatorو copy constructorﺑﻪ ﻫﻢ ﻣﺮﺑﻮط ﻫﺴﺘﻨﺪ .اﻣﺎ در ﺣﻘﻴﻘﺖ اﻳﻦ ﻃﻮر ﻧﻴﺴﺖ. اﮔﺮ در ﻳﻚ ﻛﻼس = operatorﺗﻌﺮﻳﻒ ﻧﺸﻮد compilerﻳﻜﻲ ﺑﻪ ﺻﻮرت defaultدر ﻧﻈﺮ ﻣﻲﮔﻴﺮد ﻛﻪ ﻣﺘﻐﻴﺮﻫﺎ را ﻳﻜﻲ ﻳﻜﻲ assignﻣﻲﻛﻨﺪ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ:
201
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default constructor Cat(const Cat&) // copy constructor { cout<< "Cat copy constructor\n"; } void operator = (const Cat&) { cout<< "Cat assigns\n"; } }; class House { Cat Tom; }; void f(House Mine){} // a function int main() { House Mine,Yours; Mine = Yours; f(Yours); getch(); }
Output: Cat assigns Cat copy constructor
، ﻣﻲﺷﻮدYours copy ﻣﻲﺷﻮد و وﻗﺘﻲassign ﻫﻢYours.Tom ، ﻣﻲﺷﻮدYours assign ﻣﻲﺑﻴﻨﻴﺪ وﻗﺘﻲ operator = وHouse copy constructor ﺣﺎﻻ اﮔﺮ ﺑﻪ ﻛﻼس. ﻣﻲﺷﻮدcopy ﻫﻢYours.Tom
: ﻓﺮاﺧﻮاﻧﺪه ﻧﻤﻲﺷﻮدCat اﺿﺎﻓﻪ ﻛﻨﻴﻢ دﻳﮕﺮ ﭼﻴﺰي از #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default constructor Cat(const Cat&) //copy constructor { cout<< "Cat copy constructor\n"; }
202
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ void operator = (const Cat&) { cout<< "Cat assigns\n"; } }; class House { public: Cat Tom; House(){} // default constructor House(const House&) //copy constructor { cout<< "House copy constructor\n"; } void operator = (const House&) { cout<< "House assigns\n"; } }; void f(House Mine){} // a function int main() { House Mine,Yours; Mine = Yours; f(Mine); getch(); }
Output: House assigns House copy constructor
ﻛﺮدن )ﻳﻌﻨﻲ ﻣﺴﺎويassign ﻛﺮدن )ﻳﻌﻨﻲ ﻣﻘﺪار اوﻟﻴﻪ دادن( ﺑﺎinitialize ﺷﺎﻳﺪ ﺗﺎ ﺑﻪ ﺣﺎل ﺑﺎ ﺧﻮد ﻣﻲﮔﻔﺘﻴﺪ copy ﻛﺮدنinitialize وﻟﻲ. را ﻓﺮا ﻣﻲﺧﻮاﻧﺪoperator = ﻛﺮدنassign .ﻗﺮار دادن( ﻓﺮﻗﻲ ﻧﺪارد : را ﻓﺮا ﻣﻲﺧﻮاﻧﺪconstructor #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat(){} // default constructor Cat(const Cat&) // copy constructor { cout<< "Cat copy constructor\n"; } void operator = (const Cat&) { cout<< "Cat assigns\n"; }
203
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }; int main() { Cat Yours; Cat Mine = Yours; getch(); }
Output: Cat copy constructor
private constructor
. ﺗﻌﺮﻳﻒ ﺷﻮﻧﺪ ﻧﻤﻲﺗﻮاﻧﻴﻢ ﻣﺘﻐﻴﺮي از ﻧﻮع آن ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢprivate ﻫﺎي ﻳﻚ ﻛﻼسconstructor اﮔﺮ ﻫﻤﻪي :ﻣﺜﻼ ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﺟﺮا ﻧﻤﻲﺷﻮد #include <conio.h> #include <iostream> using namespace std; class Cat { private: Cat(){} // default constructor }; int main() { Cat Tom; getch(); }
Output: error
:اﻣﺎ ﺑﺎ اﻳﻦ ﻛﻼس ﻣﻲﺷﻮد ﻧﻮﺷﺖ Cat* Tom;
:وﻟﻲ ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ Cat* Tom = new Tom;
ﺑﺎﻳﺪ ﻓﺮاﺧﻮاﻧﺪهconstructor را ﺧﻮد ﻣﺎ ﺗﻌﺮﻳﻒ ﻛﺮده ﺑﺎﺷﻴﻢ )زﻳﺮا ﻣﻲداﻧﻴﺪ ﻛﻪ در ﻧﻬﺎﻳﺖnew ﺣﺘﻲ اﮔﺮ : اﺳﺖprivate ،constructor ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻫﻢ اﺟﺮا ﻧﻤﻲﺷﻮد زﻳﺮا.(ﺑﺸﻮد #include <conio.h> #include <iostream>
204
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; class Cat { Cat(){} }; class House { public: Cat a; }; int main() { House MyHouse; getch(); }
Output: error
compiler ﻧﻤﻲﺗﻮاﻧﺪ ﻣﺘﻐﻴﺮش را ﺑﺴﺎزد و ﺑﺮاي ﻫﻤﻴﻦHouse ، اﺳﺖCat private ِ constructor ﭼﻮن .ﻧﻤﻲﺗﻮاﻧﺪ ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻛﻨﺪ ﺑﻪ ﺷﻜﻞconstructor ﻳﻚcompiler ي ﺑﺮاي ﻛﻼس ﻧﻨﻮﻳﺴﻴﻢ ﺧﻮدconstructor اﮔﺮ ﻫﻴﭻ Cat(){}
وﻟﻲ اﮔﺮ ﻣﺎ ﻫﺮ ﺟﻮر. ﻣﻲﮔﻮﻳﻨﺪdefault constructor اﺳﺖ و ﺑﻪ آنpublic در ﻧﻈﺮ ﻣﻲﮔﻴﺮد ﻛﻪ در ﻧﻈﺮ ﻧﻤﻲﮔﻴﺮد وcompiler ي را ﺧﻮدconstructor دﻳﮕﺮ ﭼﻨﻴﻦ،ي ﺑﻪ ﻛﻼس اﺿﺎﻓﻪ ﻛﻨﻴﻢconstructor ﻫﺎي ﺧﻮدmethod ﻣﻲﺗﻮاﻧﻨﺪ درprivate ﻫﺎيconstructor .اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺧﻮدﻣﺎن ﺑﺎﻳﺪ آن را اﺿﺎﻓﻪ ﻛﻨﻴﻢ :ﻛﻼس اﺳﺘﻔﺎده ﺑﺸﻮﻧﺪ #include <conio.h> #include <iostream> using namespace std; class Cat { Cat(int a) { age = a; } public: Cat() { *this = Cat(123); } int age; };
205
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int main() { Cat Tom; cout<< Tom.age; getch(); }
Output: 123
در ﺿﻤﻦ از. را ﻓﺮا ﻣﻲﺧﻮاﻧﺪprivate constructor ﻳﻚdefault constructor در اﻳﻦ ﻣﺜﺎل . ﺑﺮاي ﻛﻼس در ﻧﻈﺮ ﻣﻲﮔﻴﺮد اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪdefault ﺑﻪ ﻃﻮرcompiler ﻛﻪoperator=
ﻫﺎ دﺳﺘﺮﺳﻲ ﻧﺪارﻧﺪ ﻧﻤﻲﺗﻮاﻧﻨﺪ آن را ﻓﺮاprivate ﺗﺎﺑﻊﻫﺎﻳﻲ ﻛﻪ ﺑﻪ، ﺗﻌﺮﻳﻒ ﺷﻮدprivate ،destructor اﮔﺮ : ﻣﺜﻼ ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﺟﺮا ﻧﻤﻲﺷﻮد.ﺑﺨﻮاﻧﻨﺪ #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat() { cout<< "copy constructor\n"; } private: ~Cat() { cout<< "destructor"; } }; int main() { Cat Tom; _getch(); }
Output: Error
ﻓﺮا ﺧﻮاﻧﺪه ﺑﺸﻮد در ﺣﺎﻟﻲ ﻛﻪdestructor ﺑﺎﻳﺪmain() ﻋﻠﺖ اﺟﺮا ﻧﺸﺪن اﻳﻦ ﻛﻪ ﻣﻮﻗﻊ ﭘﺎﻳﺎن اﺟﺮاي وﻟﻲ اﮔﺮ ﺑﻪ ﺟﺎي. ﺣﻖ اﺳﺘﻔﺎده از آن را ﻧﺪاردmain() اﺳﺖ وprivate ،destructor Cat Tom;
206
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ :ﺑﻨﻮﻳﺴﻴﻢ Cat* pTom = new Cat;
:ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻣﻲﺷﻮد #include <conio.h> #include <iostream> using namespace std; class Cat { public: Cat() { cout<< "copy constructor\n"; } private: ~Cat() { cout<< "destructor"; } }; int main() { Cat* pTom = new Cat; // delete pTom; // error _getch(); }
Output: copy constructor
هmember ار او دادن
ﺷﻮﻧﺪ اﻣﺎ راه دﻳﮕﺮيinitialize ﮔﻔﺘﻢ ﻛﻪ ﻣﺘﻐﻴﺮﻫﺎﻳﻲ ﻛﻪ در ﻳﻚ ﻛﻼس ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮﻧﺪ ﻧﺒﺎﻳﺪ ﺑﻪ ﺷﻜﻞ ﻋﺎدي : اﻳﻦ راه را در ﻣﺜﺎل زﻳﺮ ﻣﻲﺑﻴﻨﻴﺪ. ﻛﺮدن آنﻫﺎ ﻫﺴﺖinitialize ﺑﺮاي #include <conio.h> #include <iostream> using namespace std; class Cat {
207
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ public: Cat(int a): age(1234) { cout<< "Cat constructor\n"; } int age; }; int main() { Cat Tom(8); cout<< Tom.age; getch(); }
Output: Cat constructor 1234
: ﻧﻮﺷﺘﻪام،constructor در اﻳﻦ ﺟﺎ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺑﻌﺪ از ﭘﺮاﻧﺘﺰ ﭘﺎﻳﺎﻧﻲ ِ ﻟﻴﺴﺖ ِ ﭘﺎراﻣﺘﺮﻫﺎي : age(1234)
. ﺷﻮد1234 initialize ﺑﺎage ﻳﻌﻨﻲ ﻣﺘﻐﻴﺮ:age(1234) . رﻓﺘﻪامconstructor و ﺑﻌﺪ ﺑﻪ ﺳﺮاغ ﺗﻌﺮﻳﻒ اﻣﺎ اﻳﻦ ﻛﺎرage=1234 ﻗﺮار دﻫﻴﻢconstructor ﺷﺎﻳﺪ ﺑﮕﻮﻳﻴﺪ ﻣﻲﺗﻮاﻧﺴﺘﻴﻢ ﺑﻪ ﺟﺎي اﻳﻦ ﻛﺎر در ﺗﻌﺮﻳﻒ ﺑﻪ. ﺷﻮﻧﺪinitialize ﻫﺎ و ﺛﺎﺑﺖﻫﺎ ﺑﺎﻳﺪ ﺣﺘﻤﺎreference . اﺳﺖassignment ﻧﻴﺴﺖ ﺑﻠﻜﻪinitialization ﺧﻴﻠﻲassignment ﺑﺎinitialization ﻋﻼوه در ﻣﻮرد ﻣﺘﻐﻴﺮﻫﺎﻳﻲ ﻛﻪ ﻧﻮﻋـﺸﺎن ﻳﻚ ﻛﻼس اﺳﺖ ﻣﻤﻜﻦ اﺳﺖ : ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ.ﻓﺮق ﻛﻨﺪ #include <conio.h> #include <iostream> using namespace std; class mustache { public: mustache(int) { cout<< "int constructor\n"; } mustache(const mustache&) { cout<< "mustache copy constructor\n"; } void operator= (const mustache&) { cout<< "operator= \n"; } }; class Cat
208
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { const int Const; int& Ref; mustache itsMustache; public: Cat(int& a): age(1234), Const(456), Ref(a), itsMustache(789) /*where the action is*/ { cout<< "Cat constructor\n"; } int age; }; int main() { int a = 5555; Cat Tom(a); cout<< Tom.age; getch(); }
Output: int constructor Cat constructor 1234
ﭼﻨﺪ.ﻫﺎﻳﻲ ﻛﻪ اﺣﺘﻤﺎل ﻓﺮاﺧﻮﻧﻲ آنﻫﺎ ﻫﺴﺖ ﺗﻌﺮﻳﻒ ﺷﺪهconstructor ﻫﺎ وmethod ﺑﺎmustache ﻛﻼس دوم اﻳﻦ ﻛﻪ در.« ﺟﺪا ﻣﻲﻛﻨﻴﻢ,» آنﻫﺎ را ﺑﺎ،ﻧﻜﺘﻪ ﻣﻬﻢ ﻫﺴﺖ اول اﻳﻦ ﻛﻪ ﻣﻮﻗﻊ ﻣﻘﺪار اوﻟﻴﻪ دادن ﺑﻪ ﭼﻨﺪ ﻣﺘﻐﻴﺮ Cat اﺳﺘﻔﺎده ﻛﺮد و ﺳﻮم اﻳﻦ ﻛﻪ ﻧﻤﻲﺷﻮد در ﻛﻼسconstructor ﻣﻘﺪار اوﻟﻴﻪ دادن ﻣﻲﺷﻮد از ﭘﺎراﻣﺘﺮﻫﺎي
ﺑﻪ ﺟﺎي mustache itsMustache;
:ﻧﻮﺷﺖ mustache itsMustache(789);
اﺻﻼmustache ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻛﻼس.و ﺑﺎﻳﺪ ﺣﺘﻤﺎ ﭘﺎراﻣﺘﺮ آن را ﺑﻪ ﺷﻜﻠﻲ ﻛﻪ در ﻣﺜﺎل ﻫﺴﺖ ﺑﻪ آن داد . ﺑﺪون ﭘﺎراﻣﺘﺮ( ﻧﺪاردconstructor )ﻳﻌﻨﻲdefault constructor
209
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ methodه inline
ﻣﻲداﻧﻴﺪ ﻛﻪ ﺗﺎﺑﻊﻫﺎي ﻣﻌﻤﻮﻟﻲ ﻣﻲﺗﻮاﻧﻨﺪ prototypeداﺷﺘﻪ ﺑﺎﺷﻨﺪ اﻣﺎ آﻳﺎ methodﻫﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻨﺪ prototype داﺷﺘﻪ ﺑﺎﺷﻨﺪ؟ ﺑﻠﻪ وﻟﻲ class A { public: ;)(void f )(void f { } ;}
errorدارد .ﺑﻪ ﺟﺎي آن ﺑﺎﻳﺪ ﻧﻮﺷﺖ: class A { public: ;)(void f ;} )(void A::f { }
ﻳﻌﻨﻲ ﺑﺎﻳﺪ ﺗﻌﺮﻳﻒ )( fﺑﻪ ﺧﺎرج از ﻓﻀﺎي ﻛﻼس ﻣﻨﺘﻘﻞ ﺷﻮد .ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﻌﻠﻮم ﺷﻮد اﻳﻦ ﺗﻌﺮﻳﻒ ﻣﺮﺑﻮط ﺑﻪ ﻛﻼس Aاﺳﺖ ﺑﻌﺪ از ﻧﻮع ﺧﺮوﺟﻲ و ﻗﺒﻞ از اﺳﻢ ﺗﺎﺑﻊ A::اﺿﺎﻓﻪ ﺷﺪه A::f .اﺳﻢ ﻛﺎﻣﻞ ﺗﺎﺑﻊ )( fاﺳﺖ .در ﻣﺜﺎل زﻳﺮ از اﺳﻢ ﻛﺎﻣﻞ اﺳﺘﻔﺎده ﺷﺪه: >#include <conio.h >#include <iostream ;using namespace std class A { public: ;)(void f ;)void g(int = 5 ;} )(void A::f { ;"cout<< "A::f called\n } )void A::g(int a { " << cout<< a ;"A::g called\n } )(int main { ;A a ;)(a.f ;)(a.A::f
210
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ a.g(); _getch(); }
Output: A::f called A::f called 5 A::g called
ﺑﺮايdefault ﻣﻘﺪار. ﻳﻚ ﺑﺎر ﺑﻪ ﻃﻮر ﻋﺎدي و ﻳﻚ ﺑﺎر ﺑﺎ اﺳﻢ ﻛﺎﻣﻞ. را ﻓﺮاﺧﻮاﻧﺪهاﻳﻢf() دو ﺑﺎرa از ﻃﺮﻳﻖ ﻫﻢ زﻣﺎن ﻧﺒﺎﻳﺪ ﻣﻘﺪارprototype اﻣﺎ ﺗﻌﺮﻳﻒ و. در ﺗﻌﺮﻳﻒ ﻧﻮﺷﺖprototype را ﻣﻲﺷﻮد ﺑﻪ ﺟﺎيg() ﭘﺎراﻣﺘﺮ . داﺷﺘﻪ ﺑﺎﺷﻨﺪdefault : اﺳﺖinline ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﻴﻢ ﻛﻪmethod در ﻣﺜﺎل زﻳﺮ ﻳﻚ #include <conio.h> #include <iostream> using namespace std; class A { public: A(){} // default constructor A(const A&) // copy constructor { cout<< "A copy constructor\n"; } }; class B { public: void f(A a); }; inline void B::f(A a) { cout<< "B::f\n"; } int main() { B b; A a; b.f(a); _getch(); }
Output: A copy constructor B::f
211
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﺎ ﻧﻮﺷﺘﻦ inlineﻗﺒﻞ از ﺗﻌﺮﻳﻒ methodﺑﻪ compilerﭘﻴﺸﻨﻬﺎد ﻣﻲﺷﻮد ﻛﻪ آن را inlineﺑﮕﻴﺮد .ﻣﻲﺑﻴﻨﻴﺪ ﺑﺎ اﻳﻦ ﻛﻪ inlineﻧﻮﺷﺘﻪ ﺷﺪه copy constructorﻓﺮاﺧﻮاﻧﺪه ﺷﺪه اﺳﺖ ﻛﻪ اﻟﺒﺘﻪ ﭼﻨﺪان ﻣﻄﻠﻮب ﻧﻴﺴﺖ )ﺣﺘﻲ اﮔﺮ ﺑﻪ ﺟﺎي inlineدر compilerﻫﺎي Microsoftاز __forceinlineاﺳﺘﻔﺎده ﻛﻨﻴﻢ ﺑﺎز ﻫﻢ copy constructorﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮد(. اﮔﺮ ﺗﻌﺮﻳﻒ methodرا داﺧﻞ ﻛﻼس ﺑﻨﻮﻳﺴﻴﻢ ﺑﻪ ﻃﻮر ﺧﻮدﻛﺎر inline ،methodﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪ .ﻳﻌﻨﻲ ﺑﻪ ﺟﺎي class B { public: ;)void f(A a ;} )inline void B::f(A a { ;"cout<< "B::f\n }
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: class B { public: )void f(A a { ;"cout<< "B::f\n } ;}
و اﻳﻦ ﻓﺮﻗﻲ ﺑﺎ inlineﺗﻌﺮﻳﻒ ﻛﺮدن )( fﻧﺪارد .در اﻳﻦ ﺣﺎﻟﺖ ﻣﻲﮔﻮﻳﻴﻢ )( fﺑﻪ ﻃﻮر ﭘﻨﻬﺎن )ﻳﺎ )implicit inlineاﺳﺖ.
%" ،typedefه 8و Oس
ﻗﺒﻼ ﻧﺤﻮهي اﺳﺘﻔﺎدهي زﻳﺮ از typedefرا ﺑﺎ structﮔﻔﺘﻪام: >#include <conio.h >#include <iostream ;using namespace std typedef class { public: ;int a
212
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } MyClass; int main() { MyClass a = {5}; cout<< a.a; _getch(); }
Output: 5
داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﭼﻮن ﻫﻴﭻ اﺳﻤﻲ ﺑﺮاي ﺧﻮد ﻛﻼس اﻧﺘﺨﺎب ﻧﻜﺮدهاﻳﻢ و ﺑﺮايconstructor اﻣﺎ در اﻳﻦ ﺟﺎ ﻧﻤﻲﺗﻮاﻧﻴﻢ . اﻧﺘﺨﺎب ﻛﻨﻴﻢconstructor ﻫﻤﻴﻦ ﻧﻤﻲﺗﻮاﻧﻴﻢ اﺳﻤﻲ ﺑﺮاي : اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ.آﻳﺎ ﻣﻲﺷﻮد از ﻧﻮع ﻳﻚ ﻛﻼس ﻣﺘﻐﻴﺮ ﻋﻤﻮﻣﻲ ﺗﻌﺮﻳﻒ ﻛﺮد؟ ﻣﺴﻠﻢ اﺳﺖ ﻛﻪ ﺑﻠﻪ #include <conio.h> #include <iostream> using namespace std; class Cat { public: int age; }; Cat Tom; int main() { cout<< Tom.age; _getch(); }
Output: 0
:ﻫﻤﻴﻦ را ﻣﻲﺷﻮد ﺳﺎده ﺗﺮ ﻧﻮﺷﺖ #include <conio.h> #include <iostream> using namespace std; class Cat { public: int age; } Tom; int main() { cout<< Tom.age;
213
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ _getch(); }
Output: 0
ﺑﺎ داﻧﺴﺘﻦ اﻳﻦ. )ﻳﻌﻨﻲ »;«( ﻣﻲﮔﺬارﻳﻢsemicolon ﺣﺎﻻ دﻳﮕﺮ ﻣﻌﻠﻮم اﺳﺖ ﻛﻪ ﭼﺮا ﺑﻌﺪ از ﺗﻌﺮﻳﻒ ﻳﻚ ﻛﻼس ﻛﻪ ﻣﻲﺷﻮد ﻣﺘﻐﻴﺮ ﻋﻤﻮﻣﻲ ﺑﺎ ﻧﻮع ﻳﻚ ﻛﻼس داﺷﺖ ﻣﻤﻜﻦ اﺳﺖ وﺳﻮﺳﻪ ﺷﻮﻳﻢ ﻛﻪ ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﻢ ﻛﻪ اﺻﻼ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﺎﺑﻌﻲ ﻫﺴﺖ ﻛﻪ ﻳﻚ آراﻳﻪ. ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺟﻮاب اﻳﻦ وﺳﻮﺳﻪ اﺳﺖ. ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪmain() ﻛﺎري ﺑﻪ :را ﻣﺮﺗﺐ ﻣﻲﻛﻨﺪ #include <conio.h> #include <iostream> using namespace std; void arrange(int input[],int output[]) { int x[5] = {0}; int i,j; for(i = 0; i < 5; i++) for (j = 0; j < 5; j++) { if(input[j] <= input[i]) x[i]++; if(i < j && input[j] == input[i]) x[i]--; } for(i = 0; i < 5; i++) output[ x[i] - 1 ] = input[i]; } class Main { public: Main() { int a[5] = {7,73,1,19,10}; int b[5],i; arrange(a,b); for (i=0; i < 5; i++) cout<< b[i] << endl; _getch(); } } Bailando; int main(){}
Output: 1
214
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 7 10 19 73
آراﻳﻪي aﺑﻪ ﺗﺎﺑﻊ arrangeداده ﺷﺪه ﺗﺎ ﻣﺮﺗﺐ ﺷﻮد و ﻣﺮﺗﺐ ﺷﺪهي آن در آراﻳﻪي bﻗﺮار ﺑﮕﻴﺮد .ﺑﻌﺪ آراﻳﻪي bﭼﺎپ ﺷﺪه .اﮔﺮ ﺑﻪ زﺣﻤﺖ ﻣﻲاﻓﺘﻴﺪ ﺧﻴﻠﻲ ﺑﻪ دﻧﺒﺎل ﻓﻬﻤﻴﺪن ﻧﺤﻮهي ﻛﺎر ﺗﺎﺑﻊ )( arrangeﻧﺒﺎﺷﻴﺪ وﻟﻲ اﮔﺮ ﻣﻲ- ﺧﻮاﻫﻴﺪ ﺑﺪاﻧﻴﺪ )( arrangeﭼﮕﻮﻧﻪ ﻛﺎر ﻣﻲﻛﻨﺪ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ اﻳﻦ ﺗﺎﺑﻊ را ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﺗﺎﺑﻊ وارون در رﻳﺎﺿﻴﺎت ﻃﺮاﺣﻲ ﻛﺮدهام .ﺗﺎﺑﻊﻫﺎي ﺑﻬﺘﺮي ﻫﻢ ﻫﺴﺖ .در header fileﻫﺎي اﻏﻠﺐ compilerﻫﺎ ﻫﻢ اﻳﻦ ﺟﻮر ﺗﺎﺑﻊﻫﺎ ﭘﻴﺪا ﻣﻲﺷﻮﻧﺪ )ﺑﻪ دﻧﺒﺎل qsortﺑﮕﺮدﻳﺪ(. ﺑﺎ دﻳﺪن اﻳﻦ ﻣﺜﺎل ﺑﻪ ﻳﺎد C#ﻧﻴﻔﺘﺎدﻳﺪ؟ اﻟﺒﺘﻪ راه دﻳﮕﺮي ﻫﻢ ﻫﺴﺖ ﻛﻪ ﺗﺎﺑﻊ )( mainرا دور ﺑﺰﻧﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int f { ;"cout<< "hello ;)(_getch ;return 0 } ;)(int a = f }{)(int main
Output: hello
ا static P
وﺟﻮد constructorﺑﺎﻋـﺚ ﻣﻲﺷﻮد ﻛﻪ ﺑﺘﻮاﻧﻴﻢ compilerرا رواﻧﺸﻨﺎﺳﻲ ﻛﻨﻴﻢ و آن را ﺑﻬﺘﺮ ﺑﺸﻨﺎﺳﻴﻢ .در ﺑﺮﻧﺎﻣﻪ زﻳﺮ ﺑﺎ اﺳﺘﻔﺎده از constructorﺗﺮﺗﻴﺐ اﻳﺠﺎد ﻣﺘﻐﻴﺮﻫﺎي staticﻣﺸﺨﺺ ﺷﺪه اﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std
215
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
;"A constructor\n
class A { public: )A(int w { " << cout<< w } ;} )(void f { ;)static A y(3 } ;)A b(1 ;)static A a(2 )(int main { ;)(f ;)(f ;)(getch }
Output: 1 A constructor 2 A constructor 3 A constructor ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻣﺘﻐﻴﺮ ﻋﻤﻮﻣﻲ bاول و ﺑﻌﺪ ﻣﺘﻐﻴﺮ a ِ staticﺳﺎﺧﺘﻪ ﺷﺪه .در ﺿﻤﻦ ﻣﺘﻐﻴﺮ staticﻣﻮﺟﻮد در ﺗﺎﺑﻊ )(f
ﻗﺒﻞ از ﻓﺮاﺧﻮاﻧﻲ ﺗﺎﺑﻊ اﺻﻼ ﺑﻪ وﺟﻮد ﻧﻴﺎﻣﺪه اﺳﺖ و ﺑﺎ ﻓﺮاﺧﻮاﻧﻲ ﻣﺠﺪد )( fدوﺑﺎره اﻳﺠﺎد ﻧﻤﻲﺷﻮد .ﺳﻮاﻟﻲ ﻛﻪ ﺑﻪ وﺟﻮد ﻣﻲآﻳﺪ اﻳﻦ اﺳﺖ ﻛﻪ staticﺑﻮدن ﻣﺘﻐﻴﺮ ﻋﻤﻮﻣﻲ ) (globalﭼﻪ ﻓﺮﻗﻲ ﺑﺎ staticﻧﺒﻮدن آن دارد؟ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﺑﻪ ﺷﻜﻞ ﻛﻨﻮﻧﻲ در ﻳﻚ ﻓﺎﻳﻞ ﺑﺮﻧﺎﻣﻪ ﻣﻲﻧﻮﻳﺴﻴﻢ ﻧﻮﺷﺘﻦ ﻳﺎ ﻧﻨﻮﺷﺘﻦ آن ﺑﺮاي ﻣﺘﻐﻴﺮﻫﺎي ﻋﻤﻮﻣﻲ ﻓﺮﻗﻲ ﻧﺪارد .در ﻓﺼﻞ ﭼﻬﺎرم ﺑﻴﺶ ﺗﺮ ﺗﻮﺿﻴﺢ ﻣﻲدﻫﻢ. درون ﻳﻚ ﻛﻼس ﻫﻢ ،ﻣﻲﺷﻮد ﻣﺘﻐﻴﺮ ﻳﺎ ﺗﺎﺑﻊ staticداﺷﺖ .ﺗﺎﺑﻊﻫﺎي staticدر ﻳﻚ ﻛﻼس ﻓﻘﻂ از ﻣﺘﻐﻴﺮﻫﺎي staticآن ﻛﻼس ﻣﻲﺗﻮاﻧﻨﺪ اﺳﺘﻔﺎده ﻛﻨﻨﺪ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std ;const unsigned PINK = 13865423 class Pink { public: )(static void Run
216
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { ;"cout<< "Pink Running\n ;color++ } ; static int color ;} ;int Pink::color = PINK )(int main { ;)(Pink::Run ;Pink Panther ;)(Panther.Run ;)(_getch }
Output: Pink Running Pink Running
ﭼﻴﺰﻫﺎي ﻣﻬﻢ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ را در ﻟﻴﺴﺖ زﻳﺮ آوردهام: (1ﺗﺎﺑﻊ Run() ِ staticﻣﺘﻐﻴﺮ color ِ staticرا ﻛﻪ ﺑﻌﺪ از آن ﻣﻌﺮﻓﻲ ﺷﺪه ﺗﻐﻴﻴﺮ ﻣﻲدﻫﺪ. (2ﺑﻪ ﻣﺘﻐﻴﺮ static colorﺣﺘﻤﺎ ﺑﺎﻳﺪ ﻣﻘﺪار اوﻟﻴﻪ داده ﺑﺸﻮد و اﻳﻦ ﻛﺎر ﻓﻘﻂ ﺧﺎرج از ﻛﻼس اﻣﻜﺎن ﭘﺬﻳﺮ اﺳﺖ .ﻣﺜﻼ ﻧﻤﻲﺷﻮد دادن ﻣﻘﺪار اوﻟﻴﻪ را داﺧﻞ ﻓﻀﺎي ﻛﻼس اﻧﺠﺎم داد ﻳﺎ از constructorﺑﺮاي آن ﻛﻤﻚ ﮔﺮﻓﺖ. (3ﺑﻪ ﺟﺎي ;int Pink::color = PINK
ﻣﻲﺗﻮاﻧﻴﻢ !: .G1 ;)int Pink::color(PINK
(4ﺑﺮاي ﻓﺮاﺧﻮاﻧﻲ ﻳﻚ ﺗﺎﺑﻊ staticدر ﻳﻚ ﻛﻼس ﻧﻴﺎزي ﺑﻪ داﺷﺘﻦ ﻳﻚ objectاز آن ﻛﻼس ﻧﻴﺴﺖ و اﮔﺮ ﺗﺎﺑﻊ staticﻣﺜﻞ ﻣﺜﺎل ﺑﺎﻻ publicﺑﺎﺷﺪ ﺑﺎ ﻧﻮﺷﺘﻦ اﺳﻢ ﻛﺎﻣﻠﺶ ﻣﻲﺷﻮد آن را ﻓﺮاﺧﻮاﻧﺪ. (5از ﻳﻚ objectﻫﻢ ﻣﻲﺷﻮد ﺑﺮاي ﻓﺮاﺧﻮاﻧﻲ ﺗﺎﺑﻊ staticﻛﻤﻚ ﮔﺮﻓﺖ .درﺳﺖ ﻣﺜﻞ ﻳﻚ method ﻣﻌﻤﻮﻟﻲ. ﻣﺘﻐﻴﺮﻫﺎي staticدر اﺑﺘﺪاي ﺑﺮﻧﺎﻣﻪ ﻳﻚ ﺑﺎر ﺑﻪ وﺟﻮد ﻣﻲ آﻳﻨﺪ و ﺗﺎ ﭘﺎﻳﺎن ﺑﺮﻧﺎﻣﻪ از ﺑﻴﻦ ﻧﻤﻲروﻧﺪ .ﺑﺮاي ﺗﺴﺖ اﻳﻦ ﻣﻄﻠﺐ ،ﺑﮕﺬارﻳﺪ دوﺑﺎره ﺑﻪ ﺳﺮاغ رواﻧﺸﻨﺎﺳﻲ compilerﺑﺮوﻳﻢ: >#include <conio.h
217
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <iostream> using namespace std; class Test { public: Test(int a) { cout<< "Test copy constructor\n"; } }; class Boy { static Test test; }; Test Boy::test(5); // initialization int main() { cout<< "hello\n"; Boy Hub, Charlie; _getch(); }
Output: Test copy constructor hello
: راه دﻳﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﻪ ﻃﻮر ﻃﺒﻴﻌﻲ ﺑﻨﻮﻳﺴﻴﻢ. ﻗﺎﺑﻞ ﺗﻮﺟﻪ اﺳﺖtest در اﻳﻦ ﻣﺜﺎل ﻧﺤﻮهي دادن ﻣﻘﺪار اوﻟﻴﻪ ﺑﻪ Test Boy::test = 5;
ﺗﻌﺮﻳﻒ ﻧﻤﻲﺷﺪ ﺑﺎز ﻫﻢ ﻫﻤﻴﻦ ﺧﺮوﺟﻲ را ﻣﻲداﺷﺘﻴﻢ ﻛﻪ از ﺳﺎﺧﺘﻪ ﺷﺪن ﻣﺘﻐﻴﺮBoy اﮔﺮ ﻫﻴﭻ ﻣﺘﻐﻴﺮي ﻫﻢ از ﻧﻮع ﻓﻘﻂ ﻳﻚ ﺑﺎرtest ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ.( ﺣﻜﺎﻳﺖ داردmain() در اﺑﺘﺪاي ﺑﺮﻧﺎﻣﻪ )و ﻗﺒﻞ از ﻓﺮا ﺧﻮاﻧﻲtest ِ static . دوﺑﺎره ﺳﺎﺧﺘﻪ ﻧﺸﺪهCharlie وHub ﺳﺎﺧﺘﻪ ﺷﺪه و ﺑﺎ اﻳﺠﺎد : ﺑﺎﺷﺪ ﻣﻲﺷﻮد ﺑﻪ آن در ﺧﻮد ﻛﻼس ﺑﻪ ﺷﻜﻞ ﻃﺒﻴﻌﻲ ﻣﻘﺪار اوﻟﻴﻪ دادstatic ،int اﮔﺮ ﺛﺎﺑﺘﻲ از ﻧﻮع #include <conio.h> #include <iostream> using namespace std; class A { public: const static int a = 235711; }; int main() { cout<< A::a;
218
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ _getch(); }
Output: 235711
ﺧﻮﺑﻲ آنﻫﺎ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﻴﻦ ﻫﻤﻪي. ﺑﻪ ﭼﻪ درد ﻣﻲﺧﻮرﻧﺪstatic ﺷﺎﻳﺪ ﺑﺎ ﺧﻮدﺗﺎن ﻓﻜﺮ ﻛﺮده ﺑﺎﺷﻴﺪ ﻛﻪ ﻣﺘﻐﻴﺮﻫﺎي اﺳﺘﻔﺎده ﺷﺪهstatic در ﻣﺜﺎل زﻳﺮ از ﻳﻚ ﻣﺘﻐﻴﺮ.ﻫﺎ(ي ﻳﻚ ﻛﻼس ﻣﺸﺘﺮك ﻫﺴﺘﻨﺪobject ﻫﺎ )ﻳﻌﻨﻲinstance :ﻫﺎي ﻣﻮﺟﻮد از ﻳﻚ ﻛﻼس را ﻣﻲﺷﻤﺎردobject اﺳﺖ ﻛﻪ ﺗﻌﺪاد #include <conio.h> #include <iostream> using namespace std; class Class { public: static int number_of_Classes; Class() // constructor { cout<< "a new class constructed\n"; number_of_Classes++; } ~Class() // destructor { cout<< "a class destructed\n"; number_of_Classes--; } }; int Class::number_of_Classes = 0; int main() { Class avvale_alef; Class avvale_be; { // a block Class avvale_jim; } cout<< endl << "number of existing classes: " << Class::number_of_Classes; _getch(); }
Output: a new class constructed a new class constructed a new class constructed a class destructed number of existing classes: 2
219
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ templateه
ﻛﻼسﻫﺎ و ﺗﺎﺑﻊﻫﺎ را ﻣﻲﺷﻮد ﺑﻪ ﺻﻮرت templateﻧﻮﺷﺖ .اول templateﻫﺎ را ﺑﺮاي ﺗﺎﺑﻊﻫﺎي ﻣﻌﻤﻮﻟﻲ ﻣﻌﺮﻓﻲ ﻣﻲﻛﻨﻢ .ﻗﺒﻼ ﺗﺎﺑﻊ زﻳﺮ را ﺑﺮاي ﻣﺮﺗﺐ ﻛﺮد ﻳﻚ آراﻳﻪ از 5ﻋﺪد ﺑﺎ ﻧﻮع intﻣﻌﺮﻓﻲ ﻛﺮدم: )][void arrange(int input[],int output { ;}int x[5] = {0 ;int i,j )for(i = 0; i < 5; i++ )for (j = 0; j < 5; j++ { )]if(input[j] <= input[i ;x[i]++ )]if(i < j && input[j] == input[i ;x[i]-- } )for(i = 0; i < 5; i++ ;]output[ x[i] - 1 ] = input[i }
ﺣﺎل اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻫﻤﻴﻦ ﺗﺎﺑﻊ را ﻃﻮري ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ ﻛﻪ آراﻳﻪاي از doubleﻫﺎ را ﻣﺮﺗﺐ ﻛﻨﺪ ،ﺑﺎﻳﺪ ﺗﺎﺑﻊ )( arrangeرا overloadﻛﻨﻴﻢ و ﺑﻨﻮﻳﺴﻴﻢ: )][void arrange(double input[],double output { ;}int x[5] = {0 ;int i,j )for(i = 0; i < 5; i++ )for (j = 0; j < 5; j++ { )]if(input[j] <= input[i ;x[i]++ )]if(i < j && input[j] == input[i ;x[i]-- } )for(i = 0; i < 5; i++ ;]output[ x[i] - 1 ] = input[i }
overloadﻛﺮدن اﻳﻦ ﺗﺎﺑﻊ ﺑﺮاي charو اﻧﻮاع دﻳﮕﺮي ﻛﻪ ﺧﻮد ﻣﺎ ﺑﻪ وﺟﻮد آوردهاﻳﻢ ﻳﺎ ﻫﻨﻮز ﺑﻪ وﺟﻮد ﻧﻴﺎورده- اﻳﻢ ﻛﺎر ﺳﺨﺘﻲ اﺳﺖ و ﺟﺎي زﻳﺎدي ﻣﻲﮔﻴﺮد و اﺣﺘﻤﺎل ﺧﻄﺎ ﻫﻨﮕﺎم overloadﻛﺮدن ﺑﺎﻻ ﻣﻲرود .راﻫﻲ ﻛﻪ C++ﭘﻴﺸﻨﻬﺎد ﻣﻲﻛﻨﺪ اﺳﺘﻔﺎده از ﻛﻠﻤﻪي ﻛﻠﻴﺪي templateاﺳﺖ .ﻣﺜﺎل زﻳﺮ ﻧﺤﻮهي اﺳﺘﻔﺎده از آن را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream
220
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; template <typename Type> void arrange(Type input[],Type output[]) { int x[5] = {0}; int i,j; for(i = 0; i < 5; i++) for (j = 0; j < 5; j++) { if(input[j] <= input[i]) x[i]++; if(i < j && input[j] == input[i]) x[i]--; } for(i = 0; i < 5; i++) output[ x[i] - 1 ] = input[i]; } int main() { int a[5] = {7,73,1,19,10}; int b[5],i; arrange(a,b); for (i = 0; i < 5; i++) cout<< b[i] << endl; _getch(); }
Output: 1 7 10 19 73
ﻣﻲﺗﻮﻧﺪ ﻳﻚ ﻣﻘﺪار ﺑﺮايint عﻫﺎ ﻫﺴﺘﻨﺪ ﻣﺜﻼ4i ﻳﻚ ﺟﻮر ﻣﺘﻐﻴﺮ اﺳﺖ ﻛﻪ ﻣﻘﺪارﻫﺎي آنType در اﻳﻦ ﻣﺜﺎل راb وa ﻣﻲرﺳﺪ ﻧﻮعarrange(a,b); ﺑﺮﻧﺎﻣﻪ ﺑﻪcontrol وﻗﺘﻲ.ع اﺳﺖ4i ﻳﻚint ﺑﺎﺷﺪ ﭼﻮنType ﺑﻌﺪ از اﻳﻦ ﻛﻪ ﻧﻮع. ﻣﺸﺨﺺ ﻣﻲﺷﻮدtemplate در ﺗﻌﺮﻳﻒType ﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪ و ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻣﺘﻐﻴﺮ ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﻮﺿﻮع. ﺑﻪ ﻃﻮر ﻋﺎدي اﺟﺮا ﻣﻲﺷﻮد و آراﻳﻪ را ﻣﺮﺗﺐ ﻣﻲﻛﻨﺪarrange() ﺗﺸﺨﻴﺺ داده ﺷﺪ :ﺗﺸﺨﻴﺺ ﻧﻮع ﺑﻬﺘﺮ ﻣﻌﻠﻮم ﺑﺸﻮد ﺑﻪ ﻣﺜﺎل ﺳﺎده ﺗﺮي ﻧﻴﺎز دارﻳﻢ #include <conio.h> #include <iostream> using namespace std; template<typename T> void f(T x)
221
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { cout<< typeid(x).name() << endl; } int main() { f(2); f('a'); f(true); _getch(); }
Output: int char bool
. ﺑﻪ راﺣﺘﻲ ﻧﻮع را ﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪcompiler ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ :
ﻛﻢ ﺗﺮ ﺑﻪ زﺣﻤﺖ ﺑﻴﻔﺘﺪcompiler ﻣﻲﺗﻮاﻧﻴﻢ ﺣﺘﻲ ﻧﻮع را ﻣﻮﻗﻊ ﻓﺮاﺧﻮاﻧﻲ ﺑﻪ ﻃﻮر واﺿﺢ ﺑﻴﺎن ﻛﻨﻴﻢ ﺗﺎ
#include <conio.h> #include <iostream> using namespace std; template<typename T> void f(T x) { cout<< typeid(x).name() << endl; } int main() { f<int>('a'); // where the action is _getch(); }
Output: int
دﻳﮕﺮcompiler ﻗﺮار ﻣﻲﮔﻴﺮد وT ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن در ﻣﺘﻐﻴﺮint ،f < ﺑﻌﺪ ازint> در اﻳﻦ ﺟﺎ ﺑﺎ ﻧﻮﺷﺘﻦ . ﻧﻤﻲرودT ﺧﻮدش ﺑﻪ دﻧﺒﺎل ﺗﻌﻴﻴﻦ : واﻗﻌﺎ ﺧﻮدش ﻧﻤﻲﺗﻮاﻧﺪ ﻧﻮع را ﺗﺸﺨﻴﺺ دﻫﺪ و راﻫﻨﻤﺎﻳﻲ ﻻزم اﺳﺖcompiler در ﺑﺮﺧﻲ ﻣﻮارد ﻫﻢ #include <conio.h> #include <iostream> using namespace std; template<typename T> void f() {
222
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;T t ;cout<< typeid(t).name() << endl } )(int main { ;)(>f<int ;)(_getch }
Output: int
در اﻳﻦ ﺟﺎ compilerﻧﻤﻲﺗﻮاﻧﺪ ﺧﻮدش ﻧﻮع را ﺗﺸﺨﻴﺺ دﻫﺪ و ﻣﺎ ﺑﺎﻳﺪ آن را ﻣﺸﺨﺺ ﻛﻨﻴﻢ .ﺑﺮاي اﻳﻦ ﻛﻪ Tﺑﺮاي ﻣﺎ ﻫﻨﮕﺎم ﻧﮕﺎه ﻛﺮدن ﺑﻪ ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻣﺒﻬﻢ ﻧﺒﺎﺷﺪ ﻣﻲﺗﻮاﻧﻴﻢ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺟﺎي ;T t
ﺑﻨﻮﻳﺴﻴﻢ: ;typename T t
ﻳﻌﻨﻲ ﺑﻪ ﺟﺎي Tﻛﻪ ﻳﻚ ﻣﺘﻐﻴﺮ از 4iعﻫﺎﺳﺖ ﻣﻲﺷﻮد از typename Tاﺳﺘﻔﺎده ﻛﺮد ﺗﺎ ﺗﺄﻛﻴﺪ ﺑﺸﻮد ﻛﻪ Tﻳﻚ ﻣﺘﻐﻴﺮ از 4iعﻫﺎﺳﺖ. templateﻫﺎ ﻣﻲﺗﻮاﻧﻨﺪ ﭘﺎراﻣﺘﺮﻫﺎي ﺑﻴﺸﺘﺮي داﺷﺘﻪ ﺑﺎﺷﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std >template<int a, typename T, typename S )(void f { ;cout<< a << endl ;cout<< typeid(T).name() << endl ;cout<< typeid(S).name() << endl } )(int main { ;)(>f<2,char,int ;)(_getch }
Output: 2 char int
اﻟﺒﺘﻪ ﭘﺎراﻣﺘﺮﻫﺎ ﻓﻘﻂ ﻣﻲﺗﻮاﻧﻨﺪ typenameو ﻧﻮعﻫﺎي ﻃﺒﻴﻌﻲ )ﺑﻪ ﺟﺰ floatو (voidﺑﺎﺷﻨﺪ و ﻧﻤﻲﺗﻮاﻧﻴﻢ ﻳﻚ ﭘﺎراﻣﺘﺮ از ﻧﻮع ﻳﻚ ﻛﻼس داﺷﺘﻪ ﺑﺎﺷﻴﻢ .ﺑﻪ ﺟﺎي floatﻣﻲﺷﻮد از doubleاﺳﺘﻔﺎده ﻛﺮد.
223
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )ﻛﺎري ﻛﻪ در ﮔﺬﺷﺘﻪ ﻗﺒﻞ از اﺿﺎﻓﻪ ﺷﺪن ﻛﻠﻤﻪي ﻛﻠﻴﺪيclass ﻣﻲﺷﻮد ﻧﻮﺷﺖtypename ﺑﻪ ﺟﺎي :( اﻧﺠﺎم ﻣﻲﺷﺪC++ ﺑﻪtypename #include <conio.h> #include <iostream> using namespace std; class A{}; template<int a, class T, typename S> void f() { cout<< a << endl; cout<< typeid(typename T).name() << endl; cout<< typeid(S).name() << endl; } int main() { f<2,char,int>(); _getch(); }
Output: 2 char int
: را ﻧﺸﺎن ﻣﻲدﻫﺪclass template ﻣﺜﺎل زﻳﺮ ﻳﻚ. ﺑﺎﺷﻨﺪtemplate ﻛﻼسﻫﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻨﺪ #include <conio.h> #include <iostream> using namespace std; template<typename T, typename S, int a> class A { public: T f(typename S x) { cout<< x << endl; return static_cast<T>(x); } }; int main() { A<int,double,4> a; cout<< a.f(4.25); _getch(); }
Output: 4.25
224
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 4 double راS وint راT ،main() در. ﻫﻤﻪ ﭼﻴﺰ ﻣﺜﻞ ﺗﺎﺑﻊﻫﺎﺳﺖ.ﭼﻴﺰ ﻣﺒﻬﻤﻲ در ﻣﻮرد اﻳﻦ ﻣﺜﺎل وﺟﻮد ﻧﺪارد
. اﺳﺘﻔﺎده ﺷﺪهint ﺑﻪdouble ازx ﺑﺮاي ﺗﺒﺪﻳﻞ ﻧﻮعstatic_cast از.ﮔﺮﻓﺘﻪام . داﺷﺘﻪ ﺑﺎﺷﻨﺪdefault ﻫﺎي ﺗﺎﺑﻊ ﻣﻲﺗﻮاﻧﻨﺪ ﻣﻘﺪارtemplate ﻫﺎي ﻛﻼس ﺑﺮ ﻋﻜﺲtemplate ﭘﺎراﻣﺘﺮﻫﺎي :ﻣﺜﻼ ﻣﺜﺎل ﻗﺒﻞ را ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ #include <conio.h> #include <iostream> using namespace std; template<typename T, typename S = double, int a = 4> class A { public: T f(typename S x) { cout<< x << endl; return static_cast<T>(x); } }; int main() { A<int> a; cout<< a.f(4.25); _getch(); }
Output: 4.25 4
:ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; template<class T> class A { public: void f(); }; void A<int>::f() { cout<< "int\n"; }
225
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(int main { ;A<int> a ;)(a.f ;)(_getch }
Output: int
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﭼﮕﻮﻧﻪ ﻳﻚ ﺗﺎﺑﻊ در class templateرا ﺑﻪ ﺻﻮرت ﻏﻴﺮ inlineﺗﻌﺮﻳﻒ ﻛﺮدهام .اﻣﺎ اﻳﻦ ﺗﻌﺮﻳﻒ ﻓﻘﻂ ﺑﺮاي ﺣﺎﻟﺘﻲ اﺳﺖ ﻛﻪ ﭘﺎراﻣﺘﺮ Tﺑﺮاﺑﺮ ﺑﺎ intﺑﺎﺷﺪ و اﮔﺮ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺟﺎي ;A<int> a
ﺑﻨﻮﻳﺴﻴﻢ ; A<char> aﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻧﻤﻲﺷﻮد .ﻳﻚ راه ﺣﻞ اﻳﻦ ﻣﺸﻜﻞ آن اﺳﺖ ﻛﻪ )( fرا ﺑﻪ ﻫﻤﻴﻦ ﺷﻜﻞ ﺑﺮاي charﻫﻢ ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ اﻣﺎ راه ﺑﻬﺘﺮ آن اﺳﺖ ﻛﻪ آن را ﻣﺜﻞ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺑﻪ ﻃﻮر ﻛﻠﻲ ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std >template<class T class A { public: ;)(void f ;} >template<class T )(void A<T>::f { ;"cout<< "T\n } )(int main { ;A<double> a ;)(a.f ;)(_getch }
Output: T
ﺳﻮاﻟﻲ ﻛﻪ اﻳﻦ ﺟﺎ ﭘﻴﺶ ﻣﻲآﻳﺪ اﻳﻦ اﺳﺖ ﻛﻪ در ﻣﺜﺎل ﻗﺒﻠﻲ اﮔﺮ ﻋﻼوه ﺑﺮ ﺗﻌﺮﻳﻒ ﻛﻠﻲ )( ،fﺗﻌﺮﻳﻒﻫﺎﻳﻲ وﻳﮋه، ﺑﺮاي ﻧﻮعﻫﺎي ﺧﺎص ﻫﻢ اﻧﺠﺎم ﺑﺪﻫﻴﻢ آن وﻗﺖ ﻛﺪام ﺗﻌﺮﻳﻒ اﻧﺘﺨﺎب ﻣﻲﺷﻮد؟ در ﻣﺜﺎل زﻳﺮ اﻳﻦ ﻛﺎر را اﻧﺠﺎم دادهام: >#include <conio.h >#include <iostream
226
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;using namespace std >template<class T class A { public: ;)(void f ;} >template<class T )(void A<T>::f { ;"cout<< "T\n } )(void A<int>::f { ;"cout<< "int\n } )(void A<char>::f { ;"cout<< "char\n } )(int main { ;A<int> a ;A<char> b ;A<double> c ;)(a.f ;)(b.f ;)(c.f ;)(_getch }
Output: int char T
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﻌﺮﻳﻒ ﻧﺰدﻳﻚ ﺗﺮ ﭘﻴﺪا و اﺳﺘﻔﺎده ﻣﻲﺷﻮد. ﻓﺮض ﻛﻨﻴﺪ ﻛﻼﺳﻲ ﺑﻪ اﻳﻦ ﺷﻜﻞ >template <typename T class A ;}{...
دارﻳﻢ .اﮔﺮ 4i n\ UUUع ﺑﺎﺷﺪ ﻣﻲﺷﻮد ﺑﻪ ﭘﺎراﻣﺘﺮ Tﻣﻘﺪار UUUرا داد و ﻧﻮﺷﺖ > .A<UUUﻣﺜﻼ ﭼﻮن int
ﻳﻚ ﻧﻮع اﺳﺖ ﻣﻲﺷﻮد ﻧﻮﺷﺖ > .A<intاﻣﺎ ﺧﻮد > A<intﻫﻢ ﻳﻚ ﻧﻮع اﺳﺖ ﭘﺲ ﺑﺎﻳﺪ ﺑﺸﻮد ﻧﻮﺷﺖ >> .A<A<intدر واﻗﻊ ﻣﻲﺷﻮد اﻳﻦ ﻛﺎر را ﻛﺮد وﻟﻲ ﺑﺮاي اﻳﻦ ﻛﻪ >> compilerدر آﺧﺮ آن را operator ﺑﻪ ﺣﺴﺎب ﻧﻴﺎورد ﺑﺎﻳﺪ ﻳﻚ ﻓﺎﺻﻠﻪ ﺧﺎﻟﻲ ﺑﻴﻦ > و > ﺑﮕﺬارﻳﻢ و ﺑﻨﻮﻳﺴﻴﻢ > > .A<A<intاﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream
227
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; template <typename T> class A { public: typename T a; }; int main() { A<A<int> > a; a.a.a = 2; cout<< a.a.a; _getch(); }
Output: 2
ﭘﺲ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ. داردA<int> ﺑﺎ ﻧﻮعa ﻣﺘﻐﻴﺮي ﺑﻪ ﻧﺎمA<A<int> > دارد وA<A<int> > ﻧﻮعa a.a.a ﭘﺲ ﻣﻲﺷﻮد ﻧﻮﺷﺖ. داردint ﺑﺎ ﻧﺎمa ﻫﻢ ﻋﻀﻮي ﺑﻪ ﻧﺎمA<int> اﻣﺎ. داردA<int> ﻛﻪ ﻧﻮعa.a
. داردint ﻛﻪ ﻧﻮع ﺑﻪ ﻣﺜﺎلﻫﺎي زﻳﺮ دﻗﺖ. ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮﻧﺪ ﻗﺪرت ﺧﻮﺑﻲ در ﺗﺸﺨﻴﺺ ﻧﻮع دارﻧﺪtemplate ﺗﺎﺑﻊﻫﺎﻳﻲ ﻛﻪ ﺑﻪ ﺻﻮرت :ﻛﻨﻴﺪ #include <iostream> using namespace std; template <typename T> class A { public: typename T a; }; template<class T> void f(A<A<T> > x, A<T> y) { cout<< x.a.a << endl; cout<< y.a << endl; cout<< typeid(T).name(); } int main() { A<A<int> > a; a.a.a = 2; f(a,a.a); _getch(); }
228
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: 2 2 int
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ )( fﭘﺎراﻣﺘﺮ Tرا ﺗﺸﺨﻴﺺ داده و ﺑﻨﺎﺑﺮاﻳﻦ ﻣﻲﺗﻮاﻧﺴﺘﻴﻢ در ﺗﺎﺑﻊ ﻣﺜﻼ از * Tﻫﻢ اﺳﺘﻔﺎده ﻛﻨﻴﻢ. زﺑﺎن C++ﻳﺎ compilerﻫﺎي آن در ﻣﻮرد templateﻫﺎ ﻛﻤﻲ ﺿﻌﻒ دارﻧﺪ .ﺧﻴﻠﻲ از ﭼﻴﺰﻫﺎﻳﻲ ﻛﻪ در compilerﻫﺎي ﻗﺪﻳﻤﻲ در ﻧﺤﻮهي ﻛﺎر templateﻫﺎ ﺑﻮد اﻻن ﺗﻐﻴﻴﺮ ﻛﺮده وﻟﻲ ﻫﻨﻮز اﻳﺮادﻫﺎي زﻳﺎدي ﭘﻴﺪا ﻣﻲ- ﺷﻮد .اﮔﺮ ﺑﺨﻮاﻫﻢ راﺣﺖ ﺗﺮ ﺑﮕﻮﻳﻢ ﺧﻴﻠﻲ روي templateﻫﺎ ﻛﺎر ﻧﺸﺪه اﺳﺖ .اﻟﺒﺘﻪ ﺑﺎز ﻫﻢ ﻣﺜﻞ ﻫﻤﻴﺸﻪ compilerﻫﺎي Microsoftﺑﻬﺘﺮ ﻋﻤﻞ ﻣﻲﻛﻨﻨﺪ )و ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺑﺮﻧﺪه ﻣﻲﺷﻮﻧﺪ
ﺑﺎ اﻳﻦ ﺣﺎل ﻣﻦ ﻫﻤﻴﺸﻪ
Borlandرا در ﺳﺎﺧﺖ compilerﺑﻪ Microsoftﺗﺮﺟﻴﺢ ﻣﻲدﻫﻢ(. ﻣﺜﺎل زﻳﺮ ﻧﻜﺘﻪاي ﺗﺎزه در ﻣﻮرد ﻧﻮع ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻚ templateدارد: >#include<conio.h >#include <iostream ;using namespace std >template <typename T, T x class A { public: ;typename T a )(void f { ;cout<< x <<endl } ;} )(int main { ;A<int,3> a ;)(a.f ;)(_getch }
Output: 3
ﭼﻴﺰ ﺟﺪﻳﺪ اﻳﻦ اﺳﺖ ﻛﻪ ﻧﻮع ﭘﺎراﻣﺘﺮ دوم ﻫﻤﺎن ﭘﺎراﻣﺘﺮ اول اﺳﺖ. ﻧﻜﺘﻪي ﻣﻬﻢ دﻳﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ آرﮔﻮﻣﺎنﻫﺎي ﻳﻚ templateﺑﺎﻳﺪ ﺛﺎﺑﺖ ﺑﺎﺷﻨﺪ .ﻣﺜﻼ در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ ﻧﻤﻲﺷﻮد ﺗﺎﺑﻊ )( mainرا ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ داد: )(int main { ;int b = 8
229
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ A<int,b> a; a.f(); _getch(); }
:اﻣﺎ ﻣﻲﺷﻮد آن را ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ داد int main() { const int b = 3; A<int,b> a; a.f(); _getch(); }
: اﺟﺮا ﻧﻤﻲﺷﻮدBDS 2006 ﺑﺮﻧﺎﻣﻪي زﻳﺮ در #include<conio.h> #include <iostream> using namespace std; template <typename T, T x, T* p> class A { public: typename T a; void f() { cout<< x << endl; } }; int main() { int* const b = 0; A<int,4,b> a; a.f(); _getch(); }
Output (Visual C++ 2005): 4
: ﻛﺮدtemplate ﺗﻨﻬﺎ را ﻫﻢmethod ﻣﻲﺷﻮد ﻳﻚ #include<conio.h> #include <iostream> using namespace std; class A { public: template <typename T> void f(T x) {
230
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< x << endl; } }; int main() { A a; a.f<int>(5); a.f('a'); _getch(); }
Output: 5 a
را ﺑﻪ ﺗﺸﺨﻴﺺ ﻧﻮعcompiler ﮔﻔﺘﻪاﻳﻢ و ﺑﺎر دﻳﮕﺮ ﺧﻮدcompiler در اﻳﻦ ﺟﺎ ﻳﻚ ﺑﺎر ﻧﻮع را ﺧﻮد ﻣﺎ ﺑﻪ .واداﺷﺘﻪاﻳﻢ ﭼﻨﺪ ﺑﺎر ﺑﮕﻮﻳﻢ
را ﺑﻪ ﺧﺎرج از ﻛﻼس ﻣﻨﺘﻘﻞ ﻫﺴﺖ؟ )ﻣﻦ ﻛﻪ ﻧﺘﻮاﻧﺴﺘﻢf() اﻣﺎ آﻳﺎ ﻣﻲﺗﻮان ﺗﻌﺮﻳﻒ .(
از ﻣﻦ ﮔﻔﺘﻦ ﺑﻮد.ﻫﺎ اﻳﺮاد دارﻧﺪtemplate
: ﻛﺮد؟ ﺑﻠﻪ ﺧﻴﻠﻲ ﺳﺎدهtemplate ، ﺷﺪهtemplate را در ﻳﻚ ﻛﻼسmethod ﺣﺎل آﻳﺎ ﻣﻲﺷﻮد ﻳﻚ #include<conio.h> #include <iostream> using namespace std; template <typename T> class A { public: template <typename S> void f(S x,T y) { cout<< x << " } };
" << y << endl;
int main() { A<double> a; a.f<int>(5,3.1415); a.f(6,9.99); _getch(); }
Output:
231
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 3.1415 9.99
5 6
اﻳﻦ ﻫﻢ ﻣﻘﺪار اواﻳﻪ دادن ﺑﻪ ﻣﺘﻐﻴﺮﻫﺎي staticدر ﻳﻚ :template >#include<conio.h >#include <iostream ;using namespace std >template <typename T class A { public: ;static T a ;} >template <typename T ;T A<T>::a = 25 )(int main { ;cout<< A<double>::a ;)(_getch }
Output: 25
friend
ﮔﻔﺘﻢ ﻛﻪ ﺑﻌﻀﻲ اﻋﻀﺎي ﻛﻼس privateﻫﺴﺘﻨﺪ و ﻓﻘﻂ methodﻫﺎي آن ﻛﻼس ﺣﻖ اﺳﺘﻔﺎده از آنﻫﺎ را دارﻧﺪ. اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ ﺗﺎﺑﻊ دﻳﮕﺮ ﻏﻴﺮ از methodﻫﺎي ﻛﻼس ﺑﻪ privateﻫﺎ )و در واﻗﻊ ﻛﻞ اﻋﻀﺎ( دﺳﺘﺮﺳﻲ داﺷﺘﻪ ﺑﺎﺷﺪ ﺑﺎﻳﺪ در ﻛﻼس اﻋﻼم ﻛﻨﻴﻢ ﻛﻪ ﻓﻼن ﺗﺎﺑﻊ اﺟﺎزهي اﻳﻦ ﻛﺎر را دارد .ﺑﺮاي دادن اﻳﻦ اﺟﺎزه از ﻛﻠﻤﻪي ﻛﻠﻴﺪي friendاﺳﺘﻔﺎده ﻣﻲﺷﻮد .ﺑﺎﻳﺪ ِ prototypeﺗﺎﺑﻊ ﻣﻮرد ﻧﻈﺮ ﻫﻤﺮاه ﺑﺎ ﻛﻠﻤﻪي ﻛﻠﻴﺪي friendدر ﺟﻠﻮي آن، در ﻓﻀﺎي ﻛﻼس ﻗﺮار داده ﺑﺸﻮد: >#include<conio.h >#include <iostream ;using namespace std class A { private: ;int x )(~A
232
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { cout<< "A destructor\n"; cout<< x << endl; cout<< y << endl; } public: int y; friend void f(); }; void f() { A a; a.x = 2; a.y = 3; } int main() { f(); _getch(); }
Output: A destructor 2 3
در ﺿﻤﻦ. را داردA ازobject ﻫﺎي ﻫﺮprivate ﺣﻖ اﺳﺘﻔﺎده ازf() ﻣﻲﮔﻮﻳﺪ ﻛﻪ
friend void f();
( ﺑﻮدنpublic ﻳﺎprivate ﻣﺤﻞ آن ﻫﻢ )از ﻧﻈﺮ. اﺳﺖprototype ﻣﺜﻞ ﻳﻚ ﺟﻮر
friend void f();
ﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ و اﻳﻦ ﻫﻴﭻ اﺷﻜﺎﻟﻲ ﺑﻪ وﺟﻮدprivate در ﻗﺴﻤﺖdestructor در اﻳﻦ ﻣﺜﺎل.ﻓﺮﻗﻲ ﻧﺪارد ﻛﻪx ﺑﻪf() در اﻳﻦ ﻣﺜﺎل ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ. اﺳﺖfriend ﻳﻚf() ﺑﻪ آن ﻧﻴﺎز دارد وf() ﻧﻴﺎورده ﭼﻮن ﻓﻘﻂ ﺗﺎﺑﻊ : ﻛﺮدfriend از ﻳﻚ ﻛﻼس دﻳﮕﺮ را ﻫﻢ ﻣﻲﺷﻮدmethod ﻳﻚ. اﺳﺖ دﺳﺘﺮﺳﻲ داردprivate ﻣﺘﻐﻴﺮي #include<conio.h> #include <iostream> using namespace std; class B { public: void f(); }; class A { private: int x; ~A() { cout<< "A destructor\n"; cout<< x << endl; cout<< y << endl; }
233
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ public: ;int y ;)(friend void B::f ;} )(void B::f { ;A a ;a.x = 2 ;a.y = 3 } )(int main { ;)((new B) -> f ;)(_getch }
Output: A destructor 2 3
ﺷﺎﻳﺪ ﺑﭙﺮﺳﻴﺪ ﭼﺮا ﺗﻌﺮﻳﻒ )( fرا در ﺧﺎرج از ﻛﻼس Bو ﺑﻌﺪ از ﻛﻼس Aاﻧﺠﺎم دادهام؟ ﻋﻠﺘﺶ اﻳﻦ اﺳﺖ ﻛﻪ در ﺗﻌﺮﻳﻒ )( fاز Aاﺳﺘﻔﺎده ﺷﺪه و ﺑﺮاي ﻫﻤﻴﻦ ﺑﺎﻳﺪ ﺑﻌﺪ از ﻛﻼس Aﺗﻌﺮﻳﻒ ﺷﻮد .ﭘﺲ ﭼﺮا ﻛﻼس Bرا ﺑﻪ ﺑﻌﺪ از A
ﻣﻨﺘﻘﻞ ﻧﻜﺮدم؟ ﭼﻮن در ﻛﻼس Aﺑﻪ Bاﺷﺎره ﺷﺪه و ﺑﺎﻳﺪ ﻗﺒﻼ ﻣﻌﺮﻓﻲ ﺷﺪه ﺑﺎﺷﺪ در ﺿﻤﻦ ﺧﻮد )( fﻫﻢ ﺑﺎﻳﺪ ﻗﺒﻼ ﻣﻌﺮﻓﻲ ﺷﺪه ﺑﺎﺷﺪ. ﻣﻲﺷﻮد ﻛﻞ methodﻫﺎي ﻳﻚ ﻛﻼس دﻳﮕﺮ را ﻫﻢ ﺑﺎ friendاﻋﻼم ﻛﺮدن آن ﻛﻼس friend ،ﻛﺮد: >#include <conio.h >#include <iostream ;using namespace std
;""A destructor\n ;x << endl ;y << endl
;B
class A { private: ;int x )(~A { <<cout <<cout <<cout } public: ;int y friend class ;} class B
234
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { public: void B::f() { A a; a.x = 2; a.y = 3; } }; int main() { (new B) -> f(); _getch(); }
Output: A destructor 2 3
. اﺳﺖA ﺑﺮايfriend ﻳﻚB اﺳﺘﻔﺎده ﻛﻨﻨﺪ ﭼﻮن ﻛﻼسA ﻫﺎيprivate ﻣﻲﺗﻮاﻧﻨﺪ ازB ﻫﺎيmethod ﻫﻤﻪي : ﻣﻲﺷﻮد اﻳﻦ ﻣﺜﺎل رو اﻳﻦ ﺟﻮري ﺗﻐﻴﻴﺮ دادBorland وMicrosoft ﻫﺎيcompiler در #include <conio.h> #include <iostream> using namespace std; class B; // change class A { private: int x; ~A() { cout<< cout<< cout<< } public: int y; friend B; //
"A destructor\n"; x << endl; y << endl;
change
}; class B { public: void B::f() { A a; a.x = 2; a.y = 3; } };
235
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int main() { (new B) -> f(); _getch(); }
Output: A destructor 2 3
در اﻳﻦ ﺟﺎ class B;
: ﻋﻤﻞ ﻣﻲﻛﻨﺪ ﺑﺮاي ﻫﻢ وﻗﺘﻲ ﻣﻲﻧﻮﻳﺴﻴﻢB ﺑﺮاي ﻛﻼسprototype ﻣﺜﻞ ﻳﻚ ﺟﻮر friend B;
. اﻳﻦ ﺷﻜﻞ را ﻧﻤﻲﭘﺬﻳﺮدdev C++ version 4.9.9.2 اﻟﺒﺘﻪ. ﻳﻚ ﻛﻼس اﺳﺖB ﻣﻲداﻧﺪ ﻛﻪcompiler : ﻧﺸﺎن داده ﺷﺪه اﺳﺖtemplate ﻛﺮدن ﻳﻚfriend در ﻣﺜﺎل ﺑﻌﺪ ﻧﺤﻮهي #include <conio.h> #include <iostream> using namespace std; class A { private: int t; template<typename S> friend void f(S s); }; template <typename S> void f(S s) { A a; a.t = 5; cout<< s; } int main() { f<char>(97); _getch(); }
Output: a
236
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در ﻣﻮردBDS 2006 (update1) . ﺑﺎﺷﺪtemplate ﻫﻢ ﻣﻲﺗﻮاﻧﺪA ﺧﻮد ﻛﻼسVisual C++ 2005 در ﺑﻪ ﻫﻤﻴﻦ ﻋﻠﺖ. ﻫﺴﺖcompiler اﻋﺘﺮاف ﺷﺪه ﻛﻪ اﺷﻜﺎﻟﻲ اﺳﺎﺳﻲ درerror ﻣﻲدﻫﺪ و در ﭘﻴﺎمerror اﻳﻦ : اﺟﺮا ﻧﻤﻲﺷﻮدBorland ِ compiler ﺑﺮﻧﺎﻣﻪي زﻳﺮ در #include <conio.h> #include <iostream> using namespace std; template<typename T> class A { private: T t; template<typename S> friend void f(S s); }; template <typename S> void f(S s) { A<int> a; a.t = 5; cout<< s; } int main() { f<char>(97); _getch(); }
Output (Visual C++ 2005): a
اﻳﺠﺎد ﺷﺪه اﻳﻦ ﺑﺮﻧﺎﻣﻪﻫﺎ ﻣﻤﻜﻦtemplate ﺑﺮاي اﻃﻼع ﺷﻤﺎ ﻣﻲﮔﻮﻳﻢ ﻛﻪ ﺑﻪ ﻋﻠﺖ ﺗﻐﻴﻴﺮاﺗﻲ ﻛﻪ در ﻣﻮرد ﻛﺎرﺑﺮد .ﻫﺎي ﻗﺪﻳﻤﻲ ﺗﺮ اﺟﺮا ﻧﺸﻮﻧﺪcompiler اﺳﺖ در
member ع ? ان
ﻫﻢprivate اﻳﻦ ﻧﻮع ﻣﻲﺗﻮاﻧﺪ. ﻣﻲﺷﻮد ﻳﻚ ﻧﻮع را در ﻓﻀﺎي ﻛﻼس ﺗﻌﺮﻳﻒ ﻛﺮدtypedef ﺑﺎ اﺳﺘﻔﺎده از :ﺑﺎﺷﺪ #include <conio.h>
237
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <iostream> using namespace std; class A { private: typedef int int1; public: typedef int int2; int1 f(int1 x,int2 y) { return x + y; } }; int main() { //A::int1 x = 2; // error int x = 2; A::int2 y = 3; A a; cout<< a.f(x,y); _getch(); }
Output: 5
. ﻧﻤﻲﺷﻮد ﺑﻪ اﻳﻦ ﺷﻜﻞ اﺳﺘﻔﺎده ﻛﺮدA::int1 از. اﺳﺖmain() درA::int2 اوﻟﻴﻦ ﭼﻴﺰ ﻣﻬﻢ اﺳﺘﻔﺎده از cout ، اﺳﺖint1 private اﺳﺖ وint1 از ﻧﻮعf() دوﻣﻴﻦ ﭼﻴﺰ ﻣﻬﻢ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺎ اﻳﻦ ﻛﻪ ﺧﺮوﺟﻲ
. را ﭼﺎپ ﻛﻨﺪf() ﺗﻮاﻧﺴﺘﻪ ﺧﺮوﺟﻲ :در ﻳﻚ ﻛﻼس ﻣﻲﺷﻮد ﻧﻮع ﻛﻼﺳﻲ ﻫﻢ ﺗﻌﺮﻳﻒ ﻛﺮد #include <conio.h> #include <iostream> using namespace std; class A { private: int a; class B { public: void f(); }; public: B b; A(){}// default constructor class C
238
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { public: ;int a ;} ;)B g(B ;} )(void A::B::f { ;"cout<< "A::B::f()\n // cout<< a; // error } #pragma argsused )A::B A::g(B c { ;"cout<< "g called\n ;return *new B } )(int main { // A::B b; // error ;}A::C c = {3 ;cout<< c.a << endl ;A a ;)a.g(a.b ;)(_getch }
Output: 3 g called
اوﻻ ﺑﮕﻮﻳﻢ ﻛﻪ default constructorي ﻛﻪ ﻣﻲﺑﻴﻨﻴﺪ ﺑﺮاي اﻳﻦ اﺳﺖ ﻛﻪ ﺟﻠﻮي ﻳﻚ exceptionﻧﺎﻣﻨﺎﺳﺐ در Microsoft ِ compilerﮔﺮﻓﺘﻪ ﺑﺸﻮد .ﻫﻤﻪي methodﻫﺎ در ﺧﺎرج از ﻣﺤﻴﻂ ﻛﻼﺳﺸﺎن ﺗﻌﺮﻳﻒ ﺷﺪهاﻧﺪ .اول ﺑﻪ ﻧﺤﻮهي ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ )( fدر ﻛﻼس Bﺗﻮﺟﻪ ﻛﻨﻴﺪ .ﺑﻌﺪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در )( mainﻧﻤﻲﺷﻮد از ﻧﻮع Bاﺳﺘﻔﺎده ﻛﺮد ﭼﻮن privateاﺳﺖ .ﺑﺮاي ﻫﻤﻴﻦ ﻣﺠﺒﻮر ﺑﻮدم ﺑﺮاي اﻣﺘﺤﺎن ﺗﺎﺑﻊ )( gﻣﺘﻐﻴﺮ bرا ﻛﻪ ﻧﻮع Bدارد در ﺧﻮد ﻛﻼس Aﺑﺴﺎزم ﺗﺎ ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن )( gاﺳﺘﻔﺎده ﺑﺸﻮد .ﺑﻪ ﺗﻌﺮﻳﻒ )( gﻧﮕﺎه ﻛﻨﻴﺪ .ﻧﻮع ﺧﺮوﺟﻲ را ﺑﺎ اﺳﻢ ﻛﺎﻣﻞ ) (A::Bﻣﺸﺨﺺ ﻛﺮدهام اﻣﺎ ﻧﻮع ﭘﺎراﻣﺘﺮ را ﺑﺎ اﺳﻢ ﻏﻴﺮ ﻛﺎﻣﻞ ) (Bﻣﺸﺨﺺ ﻛﺮدهام .ﺑﻪ ﺗﻌﺮﻳﻒ )( fﻧﮕﺎه ﻛﻨﻴﺪ ﺑﺨﺸﻲ ﻛﻪ comment outﺷﺪه ﻳﻚ ﺧﻄﺎ ﺑﻪ وﺟﻮد ﻣﻲآورد ﭼﻮن ﻛﻼس Bﻧﻤﻲﺗﻮاﻧﺪ از اﻋﻀﺎي Aاﺳﺘﻔﺎده ﻛﻨﺪ )ﻛﻪ اﻳﻦ ﻣﺤﺪودﻳﺖ ﭼﻨﺪان ﺟﺎﻟﺐ ﻧﻴﺴﺖ ﻳﻌﻨﻲ ﻣﻦ ﻓﻜﺮ ﻣﻲﻛﻨﻢ اﮔﺮ اﺳﺘﻔﺎده از ﻣﺘﻐﻴﺮﻫﺎي Aﻣﻤﻜﻦ ﺑﻮد اﻧﻌﻄﺎف ﭘﺬﻳﺮي زﺑﺎن ﺑﻴﺶ ﺗﺮ ﻣﻲﺷﺪ ﮔﺮﭼﻪ ﻣﺸﻜﻼﺗﻲ ﻫﻢ ﺑﻪ وﺟﻮد ﻣﻲآﻣﺪ( .در ﻛﻼس Cﻣﻲﺗﻮاﻧﺴﺘﻴﻢ ﻧﻮع aرا ﺑﻪ ﺟﺎي B ،intﺑﮕﻴﺮﻳﻢ.
239
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ methodه const
اﮔﺮ ﻳﻚ methodرا constﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ،دﻳﮕﺮ ﻧﻤﻲﺗﻮاﻧﺪ ﻣﺘﻐﻴﺮﻫﺎي ﻛﻼس ﺧﻮدش را ﺗﻐﻴﻴﺮ دﻫﺪ .ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ .در اﻳﻦ ﻣﺜﺎل ﻧﺤﻮهي constﻛﺮدن ﻳﻚ methodو ﻫﻢ ﭼﻨﻴﻦ ﺟﺎﻳﻲ ﻛﻪ constﺑﻮدن ﺑﺎﻋـﺚ اﻳﺠﺎد errorﻣﻲﺷﻮد ﻣﺸﺨﺺ ﺷﺪه: >#include <conio.h >#include <iostream ;using namespace std ;int univ class A { ;int a public: void f(int aParam) const { ;"cout<< "A::f()\n // a = aParam; error ;cout<< aParam ;aParam = 10 ;univ = 10 } ;} )(int main { ;A a ;)a.f(10022008 ;)(_getch }
Output: )(A::f 10022008
در اﻳﻦ ﻣﺜﺎل f() constاﺳﺖ و ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻧﻤﻲﺗﻮاﻧﺪ ﻣﻘﺪار aرا ﻋﻮض ﻛﻨﺪ ﭼﻮن aاز اﻋﻀﺎي ﻛﻼس اﺳﺖ. وﻟﻲ ﻫﻤﺎن ﻃﻮر ﻛﻪ ﻣﻲﺑﻴﻨﻴﺪ ﻣﻲﺗﻮاﻧﺪ ﭘﺎراﻣﺘﺮ ﺧﻮدش ﻳﺎ ﻣﺘﻐﻴﺮﻫﺎي ﻋﻤﻮﻣﻲ را ﺗﻐﻴﻴﺮ ﺑﺪﻫﺪ. اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ const ِ methodرا در ﺧﺎرج از ﻛﻼس ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﺑﺎﻳﺪ constرا ﻫﻢ در prototypeو ﻫﻢ در ﺗﻌﺮﻳﻒ ﺑﻨﻮﻳﺴﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std class A { public:
240
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ void f(int a) const; }; void A::f(int a) const { cout<< a; } int main() { A a; int d(5);// i.e.: int d = 5 a.f(d); _getch(); }
Output: 5
. ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﺪ5 را ﺑﺎ ﻣﻘﺪار اوﻟﻴﻪيd ﻣﺘﻐﻴﺮint d(5) در اﻳﻦ ﻣﺜﺎل :ﺣﺎﻻ ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; class A { public: void f(int a) const { cout<< "First f()\n"; } void f(int a) { cout<< "Second f()\n"; } }; int main() { A a; int variable = 1387; const int constant = 2009; a.f(variable); // second f() a.f(constant); // second f() _getch(); }
Output: Second f() Second f()
241
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ دو ﺗﺎ ﺗﺎﺑﻊ ﺑﺎ اﺳﻢ و ﭘﺎراﻣﺘﺮﻫﺎي ﻳﻜﺴﺎن دارﻳﻢ .ﻓﻘﻂ ﻳﻜﻲ constاﺳﺖ و آن ﻳﻜﻲ ﻧﻴﺴﺖ .در ﺗﺎﺑﻊ )( ،mainﻣﺘﻐﻴﺮ variableو ﺛﺎﺑﺖ constantﻣﻌﺮﻓﻲ ﺷﺪهاﻧﺪ .اﻳﻦ ﻣﺘﻐﻴﺮ و ﺛﺎﺑﺖ را ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن )( fﺑﻪ ﻛﺎر ﺑﺮدهام .در ﻫﺮ دو ﻣﻮرد )( fدوم ﻓﺮاﺧﻮاﻧﺪه ﺷﺪه .اﻣﺎ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺑﺎ اﻳﻦ ﺟﻮر ﻓﺮاﺧﻮاﻧﻲ ﻫﺮ دو ﺗﺎﺑﻊ )( fﺧﻮاﻧﺪه ﺑﺸﻮﻧﺪ ﻛﺎﻓﻲ اﺳﺖ ﺟﻠﻮي ﭘﺎراﻣﺘﺮ )( fدوم & ﺑﮕﺬارﻳﻢ: >#include <conio.h >#include <iostream ;using namespace std class A { public: void f(int a) const { ;"cout<< "First f()\n } void f(int& a) // change { ;"cout<< "Second f()\n } ;} )(int main { ;A a int ;variable = 1387 ;const int constant = 2009 )(a.f(variable); // second f )(a.f(constant); // first f ;)(_getch }
Output: )(Second f )(First f
ﺷﺎﻳﺪ ﺑﮕﻮﻳﻴﺪ اﺻﻼ ﻧﻴﺎزي ﺑﻪ constﺑﻮدن )( fاول ﻧﻴﺴﺖ اﻣﺎ اﮔﺮ آن را ﺣﺬف ﻛﻨﻴﺪ compilerﺑﺮاي ;)!6 3/ a.f(dا f() /ﻣﻨﺎﺳﺐ را ﺗﺸﺨﻴﺺ ﺑﺪﻫﺪ.
inheritance
در اﻃﺮاف ﻣﺎ ﺳﺎﻋـﺖﻫﺎي زﻳﺎدي ﭘﻴﺪا ﻣﻲﺷﻮد ﻛﻪ زﻣﺎن را ﺑﻪ ﻣﺎ ﻧﺸﺎن ﻣﻲدﻫﻨﺪ .ﺳﺎﻋﺘﻲ ﻛﻪ ﺑﻪ دﺳﺘﻤﺎن ﻣﻲﺑﻨﺪﻳﻢ ﺳﺎﻋـﺖ ﻣﭽﻲ اﺳﺖ .ﺳﺎﻋﺖ ﻣﭽﻲ ﻳﻚ ﺟﻮر ﺳﺎﻋﺖ اﺳﺖ .از ﻃﺮﻓﻲ ﺳﺎﻋﺖ دﻳﻮاري ﻳﺎ ﺷﻤﺎﻃﻪاي )ﻫﻤﺎن alarm
242
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ - ﺷﺒﻴﻪ ﺳﺎزي ﻛﻨﻴﻢ ﻣﻲ، ﺣﺎﻻ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ اﻳﻦ ﺳﺎﻋﺖﻫﺎ را در ﺑﺮﻧﺎﻣﻪ.( ﻫﻢ ﻧﻮﻋﻲ ﺳﺎﻋـﺖ اﺳﺖ
ﺧﻮدﻣﺎنclock
)ﻳﻌﻨﻲ ﻳﻚ ﺳﺎﻋﺖ ﻛﻠﻲ ﻛﻪ ﻣﻲﺗﻮاﻧﺪ ﻫﺮ ﺟﻮر ﺳﺎﻋﺘﻲ ﺑﺎﺷﺪ( درﺳﺖ ﻛﻨﻴﻢ و ﻣﺜﻼClock ﺗﻮاﻧﻴﻢ ﻛﻼﺳﻲ ﺑﻪ اﺳﻢ : اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ. اﻳﺠﺎد ﻛﻨﻴﻢClock را ﺑﺎ اﺿﺎﻓﻪ ﻛﺮدن ﭼﻴﺰﻫﺎﻳﻲ ﺑﻪAlarm_Clock ﻛﻼس #include <conio.h> #include <iostream> using namespace std; class Clock { public: int second_hand; int minute_hand; int hour_hand; }; class AlarmClock: public Clock { public: bool bell; }; class GrandfatherClock { double pendulum; }; int main() { AlarmClock myAlarmClock ; GrandfatherClock yourClock; myAlarmClock.second_hand = 5; cout<< myAlarmClock.second_hand; getch(); }
Output: 5
در واﻗﻊ class AlarmClock: public Clock { public: bool bell; };
ﻣﻌﺎدل اﺳﺖ ﺑﺎ class AlarmClock { public: int second_hand;
243
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int minute_hand ;int hour_hand public: ;bool bell ;}
ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻦ ﻫﻤﻪ ﭼﻴﺰ در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ روﺷﻦ اﺳﺖ و ﻧﻴﺎزي ﺑﻪ ﺗﻮﺿﻴﺢ ﻧﺪارد .وﻗﺘﻲ ﻣﻲﻧﻮﻳﺴﻴﻢ: class AlarmClock: public Clock { public: ;bool bell ;}
ﻣﻲﮔﻮﻳﻴﻢ ﻛﻼس AlarmClockاز ﻛﻼس ) Clockﺑﻪ ﻃﻮر public) deriveﺷﺪه و ﻣﺘﻐﻴﺮﻫﺎي minute_hand ،second_handو hour_handرا از Clockرا ﺑﻪ ارث ﺑﺮده )ﻳﺎ inheritﻛﺮده اﺳﺖ( .ﺑﻪ Clockﻳﻚ - baseﻛﻼس ﺑﺮاي AlarmClockﻣﻲﮔﻮﻳﻴﻢ. ﻳﻚ ﻛﻼس اﻋﻀﺎي privateﻛﻼس دﻳﮕﺮ را ﺑﻪ ارث ﻧﻤﻲﺑﺮد ﻳﻌﻨﻲ اﮔﺮ class A { private: ;char a ;}
ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: class B : public A { )(void f { ;'a = 'a } ;}
ﭼﻮن Aﺑﻪ ﻛﻼسﻫﺎي deriveﺷﺪه ﺣﻖ اﺳﺘﻔﺎده از privateﻫﺎي ﺧﻮدش را ﻧﻤﻲدﻫﺪ .وﻟﻲ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻣﺘﻐﻴﺮي در Aﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﻛﻪ ﻫﻢ privateﺑﺎﺷﺪ و ﻫﻢ ﺑﻪ ارث ﺑﺮده ﺷﻮد ﺑﺎﻳﺪ ﭼﻪ ﺑﻜﻨﻴﻢ؟ ﺑﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي publicو ) access specifier ،privateﺑﺨﻮاﻧﻴﺪ »اك ﺳﺲ اس ﭘﻪ ﺳﻲ ﻓﺎﻳﺮ «
(
ﻣﻲﮔﻮﻳﻨﺪ .ﻋﻼوه ﺑﺮ publicو privateﻳﻚ ِ access specifierدﻳﮕﺮ ﻫﻢ ﻫﺴﺖ و آن protected
اﺳﺖ protected .ﻫﻤﺎن ﭼﻴﺰي اﺳﺖ ﻛﻪ ﺑﻪ دﻧﺒﺎﻟﺶ ﻫﺴﺘﻴﻢ .اﻳﻦ access specifierدر واﻗﻊ ﻫﻤﺎن privateاﺳﺖ ﻛﻪ اﺟﺎزهي )inheritanceﻳﻌﻨﻲ ﺑﻪ ارث ﺑﺮدن( را ﻫﻢ ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std class A
244
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { private: ;int a ;int b protected: ;char ch ;} class B : public A { )(void f { ;'ch = 'a } ;} )(int main { cout<< sizeof(A) << endl; // output: 12 cout<< sizeof(B) << endl; // output: 12 ;)(_getch }
Output: 12 12
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ chدر )( fﻗﺎﺑﻞ دﺳﺘﺮﺳﻲ اﺳﺖ .ﺑﻪ ﺧﺮوﺟﻲ ﻛﻪ اﻧﺪازهي ) (sizeﻛﻼسﻫﺎي Aو Bرا ﻧﺸﺎن ﻣﻲدﻫﺪ، ﻧﮕﺎه ﻛﻨﻴﺪ .اﻧﺪازهي ﻛﻼس Aﺑﺮاﺑﺮ ﺑﺎ 12اﺳﺖ .ﺷﺎﻳﺪ ﺑﭙﺮﺳﻴﺪ ﻛﻪ ﭼﺮا اﻳﻦ ﻃﻮر اﺳﺖ وﻗﺘﻲ Aﻓﻘﻂ دو _int Z
ﭼﻬﺎر ﺑﺎﻳﺘﻲ و \ char nﻳﻚ ﺑﺎﻳﺘﻲ دارد ﻛﻪ روي ﻫﻢ 9ﺑﺎﻳﺖ ﻫﺴﺘﻨﺪ؟ ﻋﻠﺖ اﻳﻦ اﺳﺖ ﻛﻪ اﻧﺪازهي ﻳﻚ ﻛﻼس ﻣﻀﺮﺑﻲ از ﺑﺰرگ ﺗﺮﻳﻦ اﻧﺪازهي ﻣﺘﻐﻴﺮﻫﺎي ﻃﺒﻴﻌﻲ آن اﺳﺖ )اﮔﺮ ﻣﺘﻐﻴﺮي از ﻧﻮع ﻛﻼس دﻳﮕﺮي ﻫﻢ ﺑﺎﺷﺪ ﺑﺎﻳﺪ ﺑﻴﻦ ﻣﺘﻐﻴﺮﻫﺎي ﻃﺒﻴﻌﻲ آن ﻛﻼس ﻫﻢ ﮔﺸﺖ و ﺑﺰرگ ﺗﺮﻳﻦ اﻧﺪازه را ﭘﻴﺪا ﻛﺮد( .ﻣﻨﻈﻮر از ﻣﺘﻐﻴﺮ ﻃﺒﻴﻌﻲ ،ﻣﺘﻐﻴﺮي از ﻧﻮع- ﻫﺎي اوﻟﻴﻪ )ﻣﺜﻞ float ،double ،intو (...اﺳﺖ .اﻧﺪازهي ﻛﻼس Bﻫﻢ ﺑﺎ اﻧﺪازهي ﻛﻼس Aﺑﺮاﺑﺮ اﺳﺖ اﮔﺮ ﭼﻪ ﺑﻪ ﻳﻜﻲ از ﻣﺘﻐﻴﺮﻫﺎي Aدﺳﺘﺮﺳﻲ ﻧﺪارد. ﺑﻪ ﺗﺮﺗﻴﺐ آزادي دﺳﺘﺮﺳﻲ ﻣﻲﺷﻮد ﻧﻮﺷﺖ .private < protected < publicﻳﻚ ﻛﻼس ﻋﻼوه ﺑﺮ publicﻣﻲﺗﻮاﻧﺪ ﺑﻪ ﻃﻮر privateﻳﺎ protectedﻫﻢ deriveﺑﺸﻮد. ﺗﺎ اﻳﻦ ﺟﺎ ﻣﻲداﻧﻴﻢ وﻗﺘﻲ ﻛﻼس Bاز ﻛﻼس ) Aﺑﻪ ﻃﻮر public) deriveﻣﻲﺷﻮد ﻫﻤﻪي اﻋﻀﺎي ﻏﻴﺮ ِ private Aﺑﻪ Bاﺿﺎﻓﻪ ﻣﻲﺷﻮﻧﺪ .ﺣﺎﻻ اﮔﺮ ﻛﻼس Bاز ﻛﻼس Aﺑﻪ ﺻﻮرت derive ،privateﺷﻮد ،اﻋﻀﺎي private ِ Aﺑﻪ Bاﺿﺎﻓﻪ ﻧﻤﻲﺷﻮﻧﺪ و ﺑﻘﻴﻪي اﻋﻀﺎي Aﺑﻪ ﻣﺤﺪودهي privateدر Bاﺿﺎﻓﻪ ﻣﻲﺷﻮﻧﺪ: >#include <conio.h
245
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <iostream> using namespace std; class A { private: int a; protected: int b; public: int c; }; class B: private A { public: void f() { //a = 1; // error b = 2; c = 3; } }; class C: private B { public: void g() { //a = 1; // error //b = 2; // error //c = 3; // error } }; int main() { B b; //b.a = 1; // error //b.b = 2; // error //b.c = 3; // error b.f(); _getch(); }
Output: empty A دﺳﺘﺮﺳﻲ ﻧﺪارد ﭼﻮن در ﻛﻼسa ﺑﻪB::f() . دارﻧﺪerror ، ﺷﺪهcomment out ﺑﺨﺶﻫﺎﻳﻲ ﻛﻪ
ﺑﻪ ﻃﻮرB ﻫﺴﺘﻨﺪ ) ﭼﻮنc) private وb ) ﻳﻌﻨﻲB ﻫﻤﻪي ﻣﺘﻐﻴﺮﻫﺎي. اﺳﺖ و ﺑﻪ ارث ﺑﺮده ﻧﻤﻲﺷﻮدprivate ،B ﻛﻪ ازC ﺑﻪ ﻫﻤﻴﻦ ﻋﻠﺖ ﻛﻼس.(ي ﻧﺪاردpublic ﻳﺎprotected ﺷﺪه و ﺧﻮدش ﻣﺘﻐﻴﺮderive ،private - ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﻬﺘﺮ اﻳﻦ ﻣﺜﺎل را درك ﻛﻨﻴﺪ ﻣﻲﺗﻮاﻧﻢ ﺑﮕﻮﻳﻢ ﻛﻼس. ﻋﻤﻼ ﻫﻴﭻ ﻣﺘﻐﻴﺮي ﻧﺪارد، ﺷﺪه اﺳﺖderive : ﻋﻤﻼ ﺑﻪ اﻳﻦ ﺷﻜﻞ ﻫﺴﺘﻨﺪC وB ﻫﺎي class B
246
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { private: int b; int c; public: void f() { //a = 1; // error b = 2; c = 3; } }; class C { private: void f() { //a b = c = } public: void g() { //a //b //c } };
= 1; // error 2; 3;
= 1; // error = 2; // error = 3; // error
. اﺿﺎﻓﻪ ﺷﺪهاﻧﺪprivate ﺑﻪ ﺑﺨﺶA اﺿﺎﻓﻪ ﻧﺸﺪهاﻧﺪ ﺑﻘﻴﻪي اﻋﻀﺎيB ﺑﻪA درprivate ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ اﻋﻀﺎي - اﺿﺎﻓﻪ ﻧﻤﻲB ﺑﻪA ِ private اﻋﻀﺎي، ﺑﺸﻮدderive ،protected ﺑﻪ ﺻﻮرتA از ﻛﻼسB اﮔﺮ ﻛﻼس : اﺿﺎﻓﻪ ﻣﻲﺷﻮﻧﺪB درprotected ﺑﻪ ﻣﺤﺪودهيA ﺷﻮﻧﺪ و ﺑﻘﻴﻪي اﻋﻀﺎي #include <conio.h> #include <iostream> using namespace std; class A { private: int a; protected: int b; public: int c; }; class B: protected A { public: void f() { //a = 1; //error
247
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ b = 2; c = 3; } }; class C: protected B { public: void f() { // a = 1; //error b = 2; c = 3; } }; int main() { B b; //b.a = 1; //error //b.b = 2; //error //b.c = 3; //error b.f(); _getch(); }
Output: empty
: در ﻋﻤﻞ ﺑﻪ اﻳﻦ ﺷﻜﻞ اﺳﺖB در اﻳﻦ ﻣﺜﺎل ﻛﻼس class B: protected A { protected: int b; int c; public: void f() { //a = 1; //error b = 2; c = 3; } };
. اﺿﺎﻓﻪ ﻧﻤﻲﺷﻮﻧﺪB ﺑﻪA ِ private اﻋﻀﺎي، ﺷﻮدderive ،public ﺑﻪ ﺻﻮرتA از ﻛﻼسB اﮔﺮ ﻛﻼس ﻫﻢ ﺑﻪA ِ public اﺿﺎﻓﻪ ﻣﻲﺷﻮﻧﺪ و اﻋﻀﺎيB ِ protected ﺑﻪ ﻣﺤﺪودهي،A ِ protected اﻋﻀﺎي : اﺿﺎﻓﻪ ﻣﻲﺷﻮﻧﺪB ِ public ﻣﺤﺪودهي #include <conio.h> #include <iostream> using namespace std; class A { private:
248
www.pupuol.com
â&#x20AC;Ťď&#x2DC;ď&#x2DC;ďť? ﺎďş&#x;ďť&#x160; ﺊďş?ﺸďŽ&#x2022;ďş&#x17D; ďť ďťŁďşŞďşďşłďťŞâ&#x20AC;Ź int a; protected: int b; public: int c; }; class B: public A { public: void f() { //a = 1; //error b = 2; c = 3; } }; class C: public B { public: void f() { //a = 1; //error b = 2; c = 3; } }; int main() { B b; //b.a = 1; //error //b.b = 2; //error b.c = 3; b.f(); _getch(); }
Output: empty
:â&#x20AC;Ť ďť&#x2039; ďş&#x2018; ا ﺡďť&#x153;ďť&#x17E; اﺳďş&#x2013;â&#x20AC;ŹB â&#x20AC;ŤŘłâ&#x20AC;ŹS â&#x20AC;Ť Ů&#x201E;â&#x20AC;ŹF 01â&#x20AC;ŤŘŻŘą اâ&#x20AC;Ź class B: public A { protected: int b; public: int c; void f() { //a = 1; //error b = 2; c = 3; } };
249
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ : ﺑﺸﻮدderive ﻳﻚ ﻛﻼس ﻣﻲﺗﻮاﻧﺪ از ﭼﻨﺪ ﻛﻼس #include <conio.h> #include <iostream> using namespace std; class A { private: int a; protected: int b; public: int c; }; class B { private: int d; protected: int e; public: int f; }; class C: public B, protected A { void g() { //a = 4; //error b = 4; } }; int main() { cout<< sizeof(C) << " = " << sizeof(A) << " + " << sizeof(B); C c; c.f = 4; _getch(); }
Output: 24 = 12 + 12
ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻦ ﻛﻪ در. ﺷﺪهderive ،protected ﺑﻪ ﻃﻮرA و ازpublic ﺑﻪ ﻃﻮرB ازC در اﻳﻦ ﺟﺎ ﻛﻼس ﻫﻤﻪيC . اﺳﺖB وA ﺑﺮاﺑﺮ ﺑﺎ ﻣﺠﻤﻮع اﻧﺪازهي ﻛﻼسﻫﺎيC اﻧﺪازهي، ﻣﺴﺘﻘﻴﻤﺎ ﻫﻴﭻ ﻣﺘﻐﻴﺮي ﺗﻌﺮﻳﻒ ﻧﺸﺪهC ﺧﻮد . را در اﺧﺘﻴﺎر داردB وA ﻛﻼسﻫﺎيprivate ﻣﺘﻐﻴﺮﻫﺎي ﻏﻴﺮ :ﻛﻼسﻫﺎي ﻣﺜﺎل ﻗـﺒﻞ را ﺑﺎ ﻧﻤﻮدار زﻳﺮ ﻧﺸﺎن ﻣﻲدﻫﻢ
250
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ﺑﻪ اﻳﻦ. اﺳﺖprivate و رﻧﮓ ﻗﺮﻣﺰ ﺑﻪ ﻣﻌﻨﻲprotected رﻧﮓ ﻧﺎرﻧﺠﻲ ﺑﻪ ﻣﻌﻨﻲ،public رﻧﮓ آﺑﻲ ﺑﻪ ﻣﻌﻨﻲ . )ﺑﺨﻮاﻧﻴﺪ »ﻫﻲ ﻳﺮ آر ﻛﻲ«( ﻣﻲﮔﻮﻳﻴﻢhierarchy ﺟﻮر ﻧﻤﻮدارﻫﺎ - ﻫﻢ ﻓﺮاﺧﻮاﻧﺪه ﻣﻲB وA ﻫﺎيconstructor ، ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢC از ﻧﻮعobject ﺑﺎﻻ وﻗﺘﻲ ﻳﻚhierarchy ﺑﺎ : اﻣﺎ ﺑﺎ ﭼﻪ ﺗﺮﺗﻴﺒﻲ؟ ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ.ﺷﻮﻧﺪ #include <conio.h> #include <iostream> using namespace std; class A { public: A() { cout<< } ~A() { cout<< } }; class B { public: B() { cout<< } ~B() { cout<< } }; class C: public B, { public: C() {
"A constructor\n";
"A destructor\n";
"B constructor\n";
"B destructor\n"; protected A
251
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< "C constructor\n"; } ~C() { cout<< "C destructor\n"; } }; int main() { {
// a block C c;
} _getch(); }
Output: B constructor A constructor C constructor C destructor A destructor B destructor
. ﻓﺮاﺧﻮاﻧﺪه ﺷﺪهاﻧﺪA وB ﻫﺎيconstructor ﺑﺎ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ. ﺷﺪهA derive و ﺑﻌﺪ ازB ﺑﻪ ﺗﺮﺗﻴﺐ اول ازC ﻫﺎ دﻗﻴﻘﺎ ﻋﻜﺲ ﻓﺮاﺧﻮاﻧﻲdestructor ﻣﻲﺑﻴﻨﻴﺪ ﺗﺮﺗﻴﺐ ﻓﺮاﺧﻮاﻧﻲ. ﻓﺮاﺧﻮاﻧﺪه ﺷﺪهconstructor C ﺑﻌﺪ .ﻫﺎﺳﺖconstructor ﭘﺎراﻣﺘﺮدار داﺷﺘﻪ ﺑﺎﺷﺪ ﭼﮕﻮﻧﻪ ﺑﺎﻳﺪ آن را ﻓﺮا ﺧﻮاﻧﺪ؟ ﺑﺮاي اﻳﻦ ﻛﺎر ﺑﺎﻳﺪB constructor ﻳﺎA ﺣﺎﻻ اﮔﺮ : ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ. اﻳﺠﺎد ﺷﻮدC ﺑﺮاي دادن ﭘﺎراﻣﺘﺮﻫﺎ در ﻛﻼس، ﻣﻨﺎﺳﺐconstructor #include <conio.h> #include <iostream> using namespace std; class A { public: A(int a, int b) { cout<< "A constructor:\n"; cout<< a << endl; // output: 1387 cout<< b << "\n\n"; // output: 12 } }; class B { public: B(int twelve) { cout<< "B constructor:\n"; cout<< twelve << "\n\n"; // output: 12
252
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } }; class C: public B, public A { public: int x; C(int a, int b, int c): A(a,b), B(12), x(21) { cout<< "C constructor:\n"; cout<< c << "\n\n"; // output: 2 } }; int main() { C c(1387,12,2); cout<< "x equals: " << c.x; // output: x equals 21 _getch(); }
Output: B constructor: 12 A constructor: 1387 12 C constructor: 2 x equals: 21
- base ﻛﻼسﻫﺎﺳﺖ )در اﻳﻦ ﻣﺜﺎل- base ﻣﻨﺎﺳﺐ ﻧﻮﻋﻲ ﻣﻘﺪار اوﻟﻴﻪ دادن ﺑﻪconstructor ﻓﺮاﺧﻮاﻧﺪن،ﭘﺲ را ﻫﻢ ﻣﻲﺷﻮد ﺑﻪ ﻫﻤﻴﻦ ﺷﻴﻮه ﻓﺮاﺧﻮاﻧﺪ اﻣﺎ ﻓﺮا ﺧﻮاﻧﺪن آن ﻻزمdefault constructor .( ﻫﺴﺘﻨﺪB وA ﻛﻼسﻫﺎ )در ﺻﻮرت ﻋﺪم ﻓﺮاﺧﻮاﻧﻲdefault constructor ﻧﻴﺴﺖ ﭼﻮن ﻫﻤﺎن ﻃﻮر در ﻣﺜﺎلﻫﺎي ﻗﺒﻠﻲ دﻳﺪﻳﺪ ﻛﻪ .ﻫﺎي دﻳﮕﺮ( ﺧﻮد ﺑﻪ ﺧﻮد ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮدconstructor اﮔﺮ در
253
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ي ﻣﻮﺟﻮد درa ﺑﻪb.a ﺑﺎﺷﺪ آن وﻗﺖB ﺑﺎ ﻧﻮعobject ﻳﻚb داﺷﺘﻪ ﺑﺎﺷﻨﺪ وa ﻫﺮ ﻛﺪام ﻋﻀﻮي ﺑﺎ اﺳﻢB وA : ﺑﺎﻳﺪ از اﺳﻢ ﻛﺎﻣﻞ اﺳﺘﻔﺎده ﻛﻨﻴﻢA ي ﻣﻮﺟﻮد درa ﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪ. اﺷﺎره داردB #include <conio.h> #include <iostream> using namespace std; class A { public: int a; A():a(0) {} }; class B: public A { public: int a; }; int main() { B b; b.a = 123456; cout<< b.A::a << endl; cout<< b.B::a; _getch(); }
Output: 0 123456
در ﺣﻘﻴﻘﺖ در
254
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
وﻗﺘﻲ Bاز اﺳﻤﻲ اﺳﺘﻔﺎده ﻛﻨﺪ ،ﻫﻤﻪي اﻋﻀﺎي Aﺑﺎ آن اﺳﻢ )در objectﻫﺎي B) hideﻣﻲﺷﻮﻧﺪ )ﻳﻌﻨﻲ ﭘﻨﻬﺎن ﻣﻲ- ﺷﻮﻧﺪ( .ﻣﺜﻼ اﮔﺮ int n\ Bﺑﺎ اﺳﻢ wداﺷﺘﻪ ﺑﺎﺷﺪ ﻫﻤﻪي ﻣﺘﻐﻴﺮﻫﺎ و ﺗﺎﺑﻊﻫﺎي Aﺑﺎ ﻧﺎم hide ،wﻣﻲﺷﻮﻧﺪ .ﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪ اﻋﻀﺎي hideﺷﺪه ﺑﺎﻳﺪ ﺣﺘﻤﺎ از اﺳﻢ ﻛﺎﻣﻞ آنﻫﺎ اﺳﺘﻔﺎده ﻛﺮد .اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class A { public: )void w(int { ;"cout<< "A::w()\n } ;} class B: public A { public: ;int w ;} )(int main { ;B b // b.w(31415); // error ;b.w = 5 ;)b.A::w(31415 ;cout<< b.B::w ;)(_getch }
Output: )(A::w 5
255
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ methodه virtual
اﻳﻦ hierarchyرا در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ
ﺣﺎﻓﻈﻪاي ﻛﻪ ﺑﻪ ﻫﺮ objectﺑﺎ ﻧﻮع Bداده ﻣﻲﺷﻮد ﺑﻪ اﻳﻦ ﺻﻮرت اﺳﺖ:
point
B
A
ﭘﺲ ﻫﺮ pointerي ﻛﻪ ﺑﻪ اول ﺣﺎﻓﻈﻪي ﻳﻚ objectﺑﺎ ﻧﻮع Bاﺷﺎره دارد ،ﺑﻪ اول ﺣﺎﻓﻈﻪي ﻳﻚ objectﺑﺎ ﻧﻮع Aﻫﻢ اﺷﺎره دارد .ﺑﻪ زﺑﺎن ﺳﺎده ﻫﺮ pointerﺑﻪ \B pointer nي ﺑﻪ \ A nاﺳﺖ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class A { public: ;int a )(void f { ;"cout<< "A::f()\n } ;} class B: public A
256
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { public: int a; void f() { cout<< "B::f()\n"; } }; int main() { B b; b.A::a = 1; b.B::a = 2; A* pA = &b; B* pB = &b; pA -> f(); pB -> f(); cout<< pA -> a << endl; cout<< pB -> a; _getch(); }
Output: A::f() B::f() 1 2
ذﺧﻴﺮهB ﺑﻪpointer و در ﻳﻚA ﺑﻪpointer ﺗﻌﺮﻳﻒ ﺷﺪه و آدرس آن در ﻳﻚB در اﻳﻦ ﺟﺎ ﻣﺘﻐﻴﺮي از ﻧﻮع وA اﻋﻀﺎيA ﺑﻪpointer ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ. ﻓﺮاﺧﻮاﻧﺪه ﺷﺪهf() و ﺗﺎﺑﻊa ﻫﺎ ﻣﺘﻐﻴﺮpointer ﺑﻪ وﺳﻴﻠﻪي اﻳﻦ.ﺷﺪه :ﻫﺎ ﻫﻢ درﺳﺖ اﺳﺖreference ﻫﻤﻴﻦ در ﻣﻮرد. را ﻓﺮا ﻣﻲﺧﻮاﻧﺪB اﻋﻀﺎيB ﺑﻪpointer #include <conio.h> #include <iostream> using namespace std; class A { public: int a; void f() { cout<< "A::f()\n"; } }; class B: public A { public: int a; void f()
257
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { cout<< "B::f()\n"; } }; int main() { B b; b.A::a = 1; b.B::a = 2; A& rA = b; B& rB = b; rA.f(); rB.f(); cout<< rA.a << endl; cout<< rB.a; _getch(); }
Output: A::f() B::f() 1 2
. اﺳﺖB از ﻧﻮعb اﺳﺖ وA ي ﺑﻪrA reference در ﺣﺎﻟﻲ ﻛﻪA& rA = b; ﭼﻴﺰ ﻣﻬﻢ اﻳﻦ اﺳﺖ ﻛﻪ ﻧﻮﺷﺘﻢ ﺑﻪrA.f() وpA->f() اﻣﻜﺎﻧﻲ را در اﺧﺘﻴﺎر ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻣﻲﮔﺬارد ﻛﻪ ﺑﺎﻋﺚ ﻣﻲﺷﻮد در دو ﻣﺜﺎل ﺑﺎﻻC++ ( « ل-ِ ر ﭼﻮ و-ِ )ﺑﺨﻮاﻧﻴﺪ »وvirtual راA اﮔﺮ ﺗﺎﺑﻊ ﻣﻮﺟﻮد در. را ﻓﺮاﺑﺨﻮاﻧﻨﺪB::f() ،A::f() ﺟﺎي : دوﺑﺎره اﺟﺮا ﻣﻲﻛﻨﻢf() ﻛﺮدنvirtual دو ﻣﺜﺎل ﺑﺎﻻ را ﺑﺎ.ﻛﻨﻴﻢ اﻳﻦ اﺗﻔﺎق ﻣﻲاﻓﺘﺪ #include <conio.h> #include <iostream> using namespace std; class A { public: int a; virtual void f() { cout<< "A::f()\n"; } }; class B: public A { public: int a; void f() {
258
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< "B::f()\n"; } }; int main() { B b; b.A::a = 1; b.B::a = 2; A* pA = &b; B* pB = &b; pA -> f(); pB -> f(); cout<< pA -> a << endl; cout<< pB -> a; _getch(); }
Output: B::f() B::f() 1 2
ﺑﻪpointer وﻗﺘﻲ ﻳﻚ. ﺷﺪهvirtual اﻳﻦ ﺗﺎﺑﻊA درf() ﺟﻠﻮي ﺗﺎﺑﻊvirtual ﺑﺎ ﻧﻮﺷﺘﻦ ﻛﻠﻤﻪي ﻛﻠﻴﺪي ﻫﻤﻴﻦ. را ﻓﺮا ﺧﻮاﻧﺪB ﻣﻮﺟﻮد درf() ﺑﺎ آن ﻣﻲﺷﻮد، ﻣﻲﺷﻮدcast ،A ﺑﻪpointer ﺑﻪ ﻳﻚB ازobject ﻳﻚ :ﻫﺎ ﻣﻲاﻓﺘﺪreference اﺗﻔﺎق در ﻣﻮرد #include <conio.h> #include <iostream> using namespace std; class A { public: int a; virtual void f(); }; void A::f() { cout<< "A::f()\n"; } class B: public A { public: int a; void f() { cout<< "B::f()\n"; } };
259
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int main() { B b; b.A::a = 1; b.B::a = 2; A& rA = b; B& rB = b; rA.f(); rB.f(); cout<< rA.a << endl; cout<< rB.a; _getch(); }
Output: B::f() B::f() 1 2
ﺗﻨﻬﺎ درvirtual ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻛﻠﻤﻪي ﻛﻠﻴﺪي. ﺗﻌﺮﻳﻒ ﺷﺪهA در ﺧﺎرج ار ﻛﻼسvirtual f() ﺗﺎﺑﻊ . وﺟﻮد داردprototype
رو ﺑﻪ رو اﮔﺮ درhierarchy ﻣﺜﻼ در. ﺑﻮدن ﺑﻪ ارث ﻣﻲرﺳﺪvirtual اﻳﻦ ﺗﺎﺑﻊ ﺑﺮاي، ﺗﻌﺮﻳﻒ ﺷﺪه ﺑﺎﺷﺪvirtual ﺑﻪ ﺻﻮرتf() ﺗﺎﺑﻊA ﺑﻪ ﻣﺜﺎل ﺑﻌﺪي. ﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﺪvirtual ﻫﻢC ﻫﺎي ﺑﺎ ﻧﻮعobject :ﻧﮕﺎه ﻛﻨﻴﺪ
#include <conio.h> #include <iostream> using namespace std; class A
260
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { public: virtual void f() { cout<< "A::f()\n"; } }; class B: public A { public: void f() { cout<< "B::f()\n"; } }; class C: public B { public: void f() { cout<< "C::f()\n"; } }; int main() { C c; A& rA = c; B& rB = c; C& rC = c; rA.f(); rB.f(); rC.f(); _getch(); }
Output: C::f() C::f() C::f()
: ﺧﺮوﺟﻲ ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﻣﻲﻛﻨﺪ، ﻛﻨﻴﻢvirtual راB::f() ،A::f() ﻛﺮدنvirtual ﺣﺎل اﮔﺮ ﺑﻪ ﺟﺎي A::f() C::f() C::f()
: ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ ﺧﺮوﺟﻲ ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﺪf() )ﻣﺴﺘﻘﻴﻤﺎ( ﺗﺎﺑﻊB ﺑﺎﺷﺪ و ﻛﻼسA::f() virtual وﻟﻲ اﮔﺮ #include <conio.h> #include <iostream>
261
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;using namespace std class A { public: )(virtual void f { ;"cout<< "A::f()\n } ;} class B: public A ;}{ class C: public B { public: )(void f { ;"cout<< "C::f()\n } ;} )(int main { ;C c ;A& rA = c ;B& rB = c ;C& rC = c ;)(rA.f ;)(rB.f ;)(rC.f ;)(_getch }
Output: )(C::f )(C::f )(C::f
اﻟﺒﺘﻪ ﺑﺎ )( A::f() ،rA.A::fﺧﻮاﻧﺪه ﻣﻲﺷﻮد و virtualﺑﻮدن وﻗﺘﻲ ﻣﺆﺛﺮ اﺳﺖ ﻛﻪ اﺳﻢ ﻛﺎﻣﻞ ﺑﻴﺎن ﻧﺸﻮد. ﮔﻔﺘﻪ ﻣﻲﺷﻮد ﻛﻪ compilerﻫﺎ ﺑﺮاي اﻳﺠﺎد ﻳﻚ ﺗﺎﺑﻊ ِ ،virtualﻳﻚ ) v-tableﻳﺎ virtual function (tableاﻳﺠﺎد ﻣﻲﻛﻨﻨﺪ ﺗﺎ ﺗﺎﺑﻊﻫﺎي virtualدرﺳﺖ ﻓﺮا ﺧﻮاﻧﺪه ﺷﻮﻧﺪ .اﻳﺠﺎد ﻫﻤﭽﻴﻦ tableي ﻫﺰﻳﻨﻪي ﺣﺎﻓﻈﻪاي دارد وﻟﻲ ﺑﻪ ﻣﺤﺾ اﻳﺠﺎد آن ﺑﻴﺶ ﺗﺮ ﻫﺰﻳﻨﻪ اﻧﺠﺎم ﺷﺪه و virtualﻛﺮدن ﺑﻘﻴﻪي methodﻫﺎي ﻛﻼس ﻫﺰﻳﻨﻪي ﺑﺴﻴﺎر ﻛﻢ ﺗﺮي دارد .ﻛﻼﺳﻲ ﻛﻪ ﺑﺮاي آن v-tableوﺟﻮد دارد )ﻳﻌﻨﻲ ﺗﺎﺑﻊ virtualدارد( polymorphic
262
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﻼسﻫﺎ ﻫﻢ ﺑﻪ ارث رﺳﻴﺪه ﺑﺎﺷﺪ ﺑﺎز ﻫﻢ ﻣﻲﮔﻮﻳﻴﻢ- base ﻣﻮرد ﻧﻈﺮ ازvirtual ﺣﺘﻲ اﮔﺮ ﺗﺎﺑﻊ.ﻧﺎﻣﻴﺪه ﻣﻲﺷﻮد . اﺳﺖpolymorphic ﻛﻼس
virtual destructor
: رو ﺑﻪ رو اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢhierarchy ﺑﺎ A* pA = new B; delete pA;
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻋﻼوه. ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮدA ِ ﻛﻼسdestructor ﺗﻨﻬﺎ ﻫﻢ ﻓﺮاﺧﻮاﻧﺪهB ِ ﻛﻼسA destructor ِ ﻛﻼسdestructor ﺑﺮ ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﻳﻦ را. ﻛﻨﻴﻢvirtual راA ِ destructor ﺑﺎﻳﺪ،ﺷﻮد :ﻧﺸﺎن ﻣﻲدﻫﺪ
#include <conio.h> #include <iostream> using namespace std; class A { public: A() { cout<< "A constructor\n"; } virtual ~A() { cout<< "A destructor\n"; } }; class B: public A { public: B() {
263
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< "B constructor\n"; } ~B() { cout<< "B destructor\n"; } }; class C: public B { public: C() { cout<< "C constructor\n"; } ~C() { cout<< "C destructor\n"; } }; int main() { A* c = new C; delete c; _getch(); }
Output: A constructor B constructor C constructor C destructor B destructor A destructor
: را از اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺣﺬف ﻛﻨﻴﻢ ﺧﺮوﺟﻲ ﺑﻪ اﻳﻦ ﺻﻮرت اﺳﺖvirtual اﮔﺮ ﻛﻠﻤﻪي A B C A
constructor constructor constructor destructor
. ﺷﻮدvirtual ﻫﻢdestructor اﻳﺠﺎد ﺷﺪ ﺑﻬﺘﺮ اﺳﺖv-table ﮔﻔﺘﻪ ﻣﻲﺷﻮد وﻗﺘﻲ ﻳﻚ : داردerror ﺑﺮﻧﺎﻣﻪي زﻳﺮ #include <conio.h> #include <iostream> using namespace std;
264
www.pupuol.com
тАлянШя╗оянШя╗оя╗Э я╗гя║оя║Яя╗К я║йя║Ня╗зя║╕яоХя║Оя╗й я╗н я╗гя║кя║ня║│я╗ктАм class A { private: virtual ~A() { cout<< "A destructor\n"; _getch(); } }; class B: public A {}; int main() { new B; _getch(); }
Output: error
:тАл я║Ся║Оя╗│я║к я║гя║к╪зя╗Чя╗Ю я╗│я╗Ья╗▓ ╪з╪▓ ╪зя╗│я╗ж ╪п┘И я╗Ыя║О╪▒ ╪▒╪з я╗Ыя║о╪птАмerror тАля║Ся║о╪з┘К я║Ся║о я╗Гя║о┘Б я║╖я║к┘ЖтАм .public тАл~ ╪п╪▒ я╗гя║дя║к┘И╪п┘З┘КтАмA() тАл( я╗Чя║о╪з╪▒ ╪п╪з╪п┘ЖтАм1 .virtual тАл( я║гя║м┘Б я╗Ыя╗ая╗дя╗к┘КтАм2
dynamic_cast
.тАл я║гя║оя╗Ыя║Ц я╗Ыя║о╪птАмhierarchy Q1 тАл =!╪п ╪п╪▒тАмdynamic_cast тАл ╪з╪▓тАм%тАл( ╪птАм84тАл ╪зтАм :тАл ╪▒┘И я║Ся╗к ╪▒┘И я╗зяоХя║О┘З я╗Ыя╗ия╗┤я║ктАмhierarchy тАля║Ся╗ктАм :тАл╪зяоФя║о я║Ся╗ия╗оя╗│я║┤я╗┤я╗втАм A* pA = new N;
тАл я╗Ыя╗ия╗┤я╗в╪ЯтАмX* cast тАл ╪▒╪з я║Ся╗ктАмpA тАля║Ся╗Мя║к ян╝я╗к я║Яя╗о╪▒┘К я║Ся║Оя╗│я║ктАм :тАл╪п╪▒ ┘И╪зя╗Чя╗К я╗Ыя║Оя╗Уя╗▓ ╪зя║│я║Ц я║Ся╗ия╗оя╗│я║┤я╗┤я╗втАм X* pX = (X*) pA;
265
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ راه رﺳﻤﻲ ﺗﺮ آن اﺳﺘﻔﺎده از static_castاﺳﺖ ﻛﻪ ﻗﺒﻼ ﻣﻌﺮﻓﻲ ﻛﺮده ﺑﻮدم. ﺣﺎﻻ اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ: ;B* pB = new B
ﻣﻌﻠﻮم اﺳﺖ ﻛﻪ pBرا ﻧﺒﺎﻳﺪ ﺑﻪ X* castﻛﺮد وﻟﻲ اﮔﺮ اﻳﻦ ﻛﺎر را ﺑﻜﻨﻴﻢ compiler ،اﻳﺮادي ﻧﻤﻲﮔﻴﺮد و ﺗﻨﻬﺎ ﻛﺎري ﻛﻪ ﻣﻲﻛﻨﺪ اﻳﻦ اﺳﺖ ﻛﻪ ﻃﻮل ﺣﺎﻓﻈﻪاي را ﻛﻪ pointerﺑﻪ آن اﺷﺎره ﻣﻲﻛﻨﺪ ﺗﻐﻴﻴﺮ ﻣﻲدﻫﺪ .ﺑﺮاي روﺷﻦ ﺷﺪن ﻣﻄﻠﺐ ﻓﻜﺮ ﻛﻨﻴﺪ ﻛﻪ Bﺣﺎﻓﻈﻪي ﺑﺴﻴﺎر ﻛﻢ ﺗﺮي ﻧﺴﺒﺖ ﺑﻪ Xﻣﻲﮔﻴﺮد .در اﻳﻦ ﺻﻮرت castﻛﺮدن pBﺑﻪ pointerي ﺑﻪ Xﺑﺎﻋـﺚ ﻣﻲﺷﻮد pBﺑﻪ ﺑﺨﺶﻫﺎﻳﻲ ازﺣﺎﻓﻈﻪ اﺷﺎره داﺷﺘﻪ ﺑﺎﺷﺪ ﻛﻪ اﺻﻼ ﺗﺨﺼﻴﺺ داده ﻧﺸﺪهاﻧﺪ. اﺟﺮاي ﻣﺜﺎل زﻳﺮ ﻛﻪ hierarchyﺑﺎﻻ را دارد ،در BDS 2006ﻳﻚ exceptionاﻳﺠﺎد ﻣﻲﻛﻨﺪ .در Visual C++ 2005ﻇﺎﻫﺮا ﺑﺪون ﺧﻄﺎ اﺟﺮا ﻣﻲﺷﻮد )ﻫﻤﻮاره در اﻳﻦ ﻧﻮﺷﺘﻪ ﺗﻨﻈﻴﻤﺎت defaultدر ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد ﻣﮕﺮ آن ﻛﻪ ﮔﻔﺘﻪ ﺷﻮد(: >#include <conio.h >#include <iostream ;using namespace std class A ;}{ class B: public A ;}{ class X { public: ;int x ;int y ;int z ;int u ;int v ;} class Y: public X ;}{ class N: public B, public Y ;}{ )(int main { ;B* pB = new B ;X* pX = (X*) pB cout<< sizeof(B) << endl; // output: 8 , 1 cout<< sizeof(X) << endl; // output: 20 ;cout<< pX
266
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;pX -> v = 8 ;)(_getch }
Output (BDS 2006): 8 20 904358
Output (Visual C++ 2005): 1 20 00365D98 وﻟﻲ اﮔﺮ از static_castاﺳﺘﻔﺎده ﻛﻨﻴﻢ ،ﻫﻴﭻ ﻳﻚ از دو compilerﺑﺎﻻ اﺟﺎزهي castﻛﺮدن از * Bﺑﻪ *X
را ﻧﻤﻲدﻫﻨﺪ .ﻳﻌﻨﻲ ;)X* pX = static_cast<X*>(pB
ﺧﻄﺎ دارد .ﺑﻪ ﺟﺎي آن ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;)N* pN = static_cast<N*>(pB ;)X* pX = static_cast<X*>(pN
ﺑﻪ ﻫﺮ ﺣﺎل اﻳﻦ ﭼﻨﺪان ﻣﻄﻠﻮب ﻧﻴﺴﺖ .ﭼﺮا compilerﺑﺎﻳﺪ castﻛﻨﺪ در ﺣﺎﻟﻲ ﻛﻪ اﻳﻦ castﻛﺮدن ﻣﻤﻜﻦ اﺳﺖ ﻣﻮﺟﺐ اﻳﺠﺎد run-time errorﺑﺸﻮد؟ )error ،run-time errorي اﺳﺖ ﻛﻪ ﻫﻨﮕﺎم اﺟﺮاي ﺑﺮﻧﺎﻣﻪ )و ﻧﻪ ﻫﻨﮕﺎم compileﻳﺎ linkﺷﺪن( ﺑﻪ وﺟﻮد ﻣﻲآﻳﺪ(. ﻛﻠﻤﻪي ﻛﻠﻴﺪي dynamic_castﻣﺸﻜﻞ را ﺣﻞ ﻣﻲﻛﻨﺪ .ﻗﺒﻞ از اﻳﻦ ﻛﻪ ﭼﻴﺰي در ﻣﻮرد dynamic_castﺑﮕﻮﻳﻢ ﺑﺎﻳﺪ upcastو downcastرا ﻣﻌﺮﻓﻲ ﻛﻨﻢ .ﻓﺮض ﻛﻨﻴﺪ ﻳﻚ hierarchyﻣﺜﻞ اﻳﻦ دارﻳﻢ:
267
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
در واﻗﻊ. اﻧﺠﺎم ﺷﺪهupcast ﻣﻲﮔﻮﻳﻴﻢ ﻳﻚ، ﺷﻮدX* cast ﺑﻪY* ﺷﻮد ﻳﺎ ﻳﻚA* cast ﺑﻪB* ﻳﺎN* اﮔﺮ ﻳﻚ hierarchy ﭘﺎﻳﻴﻦ آﻣﺪن از.( )از راه ﺧﻄﻮط و ﺑﺪون ﭘﺎﻳﻴﻦ آﻣﺪنhierarchy ﻛﺮدن ﻳﻌﻨﻲ ﺑﺎﻻ رﻓﺘﻦ ازupcast cross-cast ﺣﺮﻛﺖ ﻛﺮدن از ﻣﺴﻴﺮﻫﺎي ﻋﺠﻴﺐ و ﻏﺮﻳﺐ ﺗﺮ را ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢ. ﮔﻔﺘﻪ ﻣﻲﺷﻮدdowncast ﻫﻢ .ﺑﻨﺎﻣﻴﻢ ﻓﻘﻂ،dynamic_cast اﺳﺖ وﻟﻲ ﺑﺎstatic_cast درﺳﺖ ﻣﺜﻞdynamic_cast روش اﺳﺘﻔﺎده از ﻛﺮدن ﭼﻴﺰ ﺧﺎص وupcast ﺑﺮاي.ﻫﺎي ﻛﻼس اﻣﻜﺎن ﭘﺬﻳﺮ اﺳﺖreference ﻛﻼسﻫﺎ ﻳﺎpointer ﺗﺒﺪﻳﻞﻫﺎي :ﻣﻬﻤﻲ ﺑﺮاي ﮔﻔﺘﻦ ﻧﻴﺴﺖ #include <conio.h> #include <iostream> using namespace std; class A {}; class B: public A {}; int main() { B* pB = new B; A* pA = dynamic_cast<A*>(pB); _getch(); }
Output: empty
ﺑﺎﺷﺪ ﻳﻌﻨﻲ ﺣﺪاﻗﻞ ﻳﻚpolymorphic ﺑﺎﻳﺪ ﻛﻼسdynamic_cast ﻛﺮدن ﺑﺎdowncast ﺑﺮاي :( ﺑﺎﺷﺪvirtual ،destructor ﻛﻼس ﻣﻨﺎﺳﺐ ﻣﻮﺟﻮد ﺑﺎﺷﺪ )ﻳﺎ- base در ﻳﻚvirtual method #include <conio.h> #include <iostream> using namespace std; class A { virtual void f(){}; };
268
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ class B: public A {}; int main() { A* pA = new B; B* pB = dynamic_cast<B*>(pA); cout<< pB; _getch(); }
Output (BDS 2006): 904358 Output (Visual C++ 2005): 00365FB8
اﮔﺮ اﻳﻦ ﻃﻮر. ﻛﻨﺪB* cast ﻣﻲﺗﻮاﻧﺪ آن را ﺑﻪdynamic_cast ، اﺷﺎره داردB در ﻋﻤﻞ ﺑﻪ ﻳﻚpA ﭼﻮن : ﺗﺸﺨﻴﺺ ﻣﻲداد و ﺻﻔﺮ ﺑﺮﻣﻲﮔﺮداﻧﺪdynamic_cast ﻧﺒﻮد #include <conio.h> #include <iostream> using namespace std; class A { virtual void f(){}; }; class B: public A {}; int main() { A* pA = new A; B* pB = dynamic_cast<B*>(pA); cout<< pB; _getch(); }
Output (BDS 2006): 0 Output (Visual C++ 2005): 00000000
اﻳﻦ ﺑﺮﺗﺮي. ﺧﺮوﺟﻲ ﺻﻔﺮ ﻧﻴﺴﺖ، ﺑﻨﻮﻳﺴﻴﺪstatic_cast ،dynamic_cast اﮔﺮ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺟﺎي در ﺣﻴﻦdynamic_cast ﺑﻪ اﻳﻦ ﻣﻌﻨﻲ اﺳﺖ ﻛﻪdynamic .ﻫﺎي دﻳﮕﺮcast اﺳﺖ ﺑﺮdynamic_cast
269
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ) (run-timeﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪ ﻛﻪ آﻳﺎ ﺣﺎﻓﻈﻪي ﻻزم وﺟﻮد دارد ﻳﺎ ﻧﻪ و اﮔﺮ ﺣﺎﻓﻈﻪي ﻻزم ﺗﺨﺼﻴﺺ داده ﻧﺸﺪه ﺑﺎﺷﺪ ﺻﻔﺮ ﺑﺮ ﻣﻲﮔﺮداﻧﺪ static_cast .ﺣﻴﻦ اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﺣﺎﻓﻈﻪ را ﭼﻚ ﻧﻤﻲﻛﻨﺪ و ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ اﻋﺘﻤﺎد ﻣﻲﻛﻨﺪ .ﻣﺜﺎل زﻳﺮ اﻫﻤﻴﺖ run-timeﺑﻮدن را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std class A { ;}{)(virtual void f ;} class B: public A ;}{ )(int main { ;A* pA ;int decide ;cin>> decide )if(decide == 1 ;pA = new B else ;pA = new A ;)B* pB = dynamic_cast<B*>(pA ;cout<< pB ;)(_getch }
Output (BDS 2006): 1 9245fc Output (Visual C++ 2005): 2 00000000
در اﻳﻦ ﻣﺜﺎل ﺣﻴﻦ compileو ﻗﺒﻞ از اﺟﺮا ﻧﻤﻲﺷﻮد ﺗﺸﺨﻴﺺ داد ﻛﻪ آﻳﺎ pAﺣﺎﻓﻈﻪي ﻻزم ﺑﺮاي castﺷﺪن ﺑﻪ * Bرا دارد ﻳﺎ ﻧﻪ .اﻣﺎ dynamic_castدر ﺣﻴﻦ اﺟﺮا آن را ﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪ و ﺑﺮاي ﻫﻤﻴﻦ dynamicاﺳﺖ ﻧﻪ static. dynamicﻳﻌﻨﻲ »ﭘﻮﻳﺎ« و staticﻳﻌﻨﻲ »اﻳﺴﺘﺎ« )
( .در اﻳﻦ ﻣﺜﺎل ﺑﻪ ﺟﺎي ;)B* pB = dynamic_cast<B*>(pA ;cout<< pB
ﻣﻲﺗﻮاﻧﺴﺘﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: ;)void* pV = dynamic_cast<void*>(pA
270
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< pV;
ي ﺑﻪ ﻛﻞ ﺣﺎﻓﻈﻪي ﺗﺨﺼﻴﺺ داده ﺷﺪه داﺷﺘﻴﻢ در ﺿﻤﻦ ﺑﻌﺪ ازpointer در اﻳﻦ ﺣﺎﻟﺖ ﺻﻔﺮ ﺑﺮﮔﺮداﻧﺪه ﻧﻤﻲﺷﺪ و آن ﻧﻮﺷﺘﻦ dynamic_cast<B*>(pV);
. ﺑﻪ ﻧﻮع ﻛﻼﺳﻲ ﻧﻴﺴﺖpV pointer ﺧﻄﺎ دارد ﭼﻮن :ﻫﺎ ﻫﻢ ﻣﻲﺷﻮد ﺑﻪ ﻛﺎر ﺑﺮدreference را ﺑﺮايdynamic_cast #include <conio.h> #include <iostream> using namespace std; class A { public: virtual void f(){}; }; class B: public A { public: void f() { cout<< "f() in B\n"; } }; int main() { A& rA = *new B; B& rB = dynamic_cast<B&>(rA); rA.f(); rB.f(); _getch(); }
Output: f() in B f() in B
: ﻧﮕﺎه ﻛﻨﻴﺪhierarchy ﺑﻪ اﻳﻦ
271
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
در اﻳﻦ hierarchyاﺷﻜﺎﻟﻲ وﺟﻮد ﻧﺪارد E .ﺑﻪ ﻃﻮر ﻏﻴﺮ ﻣﺴﺘﻘﻴﻢ دو ﺑﺎر از ﻛﻼس A deriveﺷﺪه و دو ﻛﭙﻲ از A
را در ﺧﻮد دارد. ﻓﺮض ﻛﻨﻴﺪ pointerي ﺑﺎ ﻧﻮع * Dﺑﻪ ﺣﺎﻓﻈﻪاي ﺑﻪ اﻧﺪازهي Eدارﻳﻢ و ﻣﻲﺧﻮاﻫﻴﻢ اﻳﻦ pointerرا ﺑﻪ A* cast
ﻛﻨﻴﻢ ﻛﻪ Aي ﺳﻤﺖ ﭼﭗ ﻣﻮرد ﻧﻈﺮ اﺳﺖ .ﺑﺎ static_castاﻳﻦ ﻛﺎر را ﻧﻤﻲﺷﻮد ﻣﺴﺘﻘﻴﻤﺎ اﻧﺠﺎم داد .ﺑﺎﻳﺪ اﻳﻦ ﻛﺎر در دو ﻣﺮﺣﻠﻪ اﻧﺠﺎم ﺷﻮد .اول ﺑﺎ static_castاز hierarchyﭘﺎﻳﻴﻦ ﻣﻲآﻳﻴﻢ و ﺑﻌﺪ در ﺟﻬﺖ ﻣﻨﺎﺳﺐ ﺑﺎﻻ ﻣﻲروﻳﻢ .در ﻣﺜﺎل زﻳﺮ ﻫﻤﻴﻦ ﻛﺎر را ﺑﺎ dynamic_castاﻧﺠﺎم دادهام: >#include <conio.h >#include <iostream ;using namespace std class A { public: ;int a }{)A(int aParam): a(aParam ;} class B: public A { public: ;}{)B(): A(1000 ;}
272
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ class C: public A { public: C(): A(2000){}; }; class D { public: virtual ~D(){} }; class E: public B, public C, public D { public: E(): B(),C(),D() {} }; int main() { D* pD = new E; E* pE = dynamic_cast<E*>(pD); B* pB = dynamic_cast<B*>(pE); A* pA = dynamic_cast<A*>(pB); cout<< pA -> a; _getch(); }
Output: 1000
. ﻛﺮدهامE downcast ﺑﻪD ازdynamic_cast اول ﺑﺎ.ي ﻛﻼسﻫﺎي اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪhierarchy ﺑﻪ راvirtual اﻳﻦ ﻋﻀﻮ. داﺷﺘﻪ ﺑﺎﺷﺪvirtual ﺑﺎﺷﺪ ﻳﻌﻨﻲ ﻋﻀﻮD polymorphic ﺑﺮاي اﻳﻦ ﻛﺎر ﻻزم اﺳﺖ ﺑﻌﺪ. ﻧﺒﻮدD ﺑﻮدنpolymorphic ﻧﻴﺎزي ﺑﻪ، اﺳﺘﻔﺎده ﻣﻲﻛﺮدمstatic_cast اﮔﺮ از. ﮔﺮﻓﺘﻪامdestructor ﺳﻤﺖ ﭼﭗA اﻣﺎ ﺑﺎﻳﺪ ﻣﻄﻤﺌﻦ ﺷﺪ ﻛﻪ واﻗﻌﺎ ﺑﻪ. ﺳﻤﺖ ﭼﭗ رﺳﻴﺪهامA ﻛﺮدهام و در ﻧﻬﺎﻳﺖ ﺑﻪB upcast ﺑﻪE از ﺑﺮايA::a و1000 ﺳﻤﺖ ﭼﭗ ﻣﻘﺪارA ﺑﺮايA::a ﻫﺎ را ﺑﺮرﺳﻲ ﻛﻨﻴﺪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪconstructor .رﻓﺘﻪام . ﺳﻤﺖ ﭼﭗ رﺳﻴﺪهامA اﺳﺖ ﭘﺲ واﻗﻌﺎ ﺑﻪ1000 دارد و ﺧﺮوﺟﻲ2000 ﺳﻤﺖ راﺳﺖ ﻣﻘﺪارA اﮔﺮ ﺑﻪ ﺟﺎي E* pE = dynamic_cast<E*>(pD); B* pB = dynamic_cast<B*>(pE); A* pA = dynamic_cast<A*>(pB);
:ﻣﻲﻧﻮﺷﺘﻢ A* pA = dynamic_cast<A*>(pD);
273
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻣﻲﺷﺪ وﻟﻲ ﻳﻚ exceptionﻫﻢ raiseﻣﻲﺷﺪ ﭼﻮن ﻣﻌﻠﻮم ﻧﺸﺪه ﻛﻪ ﻣﻨﻈﻮر ﻛﺪام Aاﺳﺖ .ﻳﻌﻨﻲ ﺑﺮﻧﺎﻣﻪ compile-time errorﻧﻤﻲداﺷﺖ اﻣﺎ ﻳﻚ run-time errorﻣﻲداﺷﺖ .اﮔﺮ از static_castاﺳﺘﻔﺎده ﻣﻲﻛﺮدم compile-time errorاﻳﺠﺎد ﻣﻲﺷﺪ .اﮔﺮ در اﻳﻦ ﻣﺜﺎل destructorرا privateﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻧﻤﻲﺷﻮد.
virtual inheritance
ﺑﻪ اﻳﻦ hierarchyﻧﮕﺎه ﻛﻨﻴﺪ:
ﻫﺮ objectﺑﺎ ﻧﻮع Nدو ﻛﭙﻲ از Aدارد .ﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪ اﻋﻀﺎي ﻫﺮ ﻳﻚ از اﻳﻦ دو Aﺑﺎﻳﺪ از ﻣﺴﻴﺮ ﻣﻨﺎﺳﺐ اﺳﺘﻔﺎده ﻛﺮد: >#include <conio.h >#include <iostream ;using namespace std class A { public: ;int a ;} class B: public A ;}{ class C: public A ;}{
274
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ class N: public B, public C ;}{ )(int main { ;N n // n.a = 1; // error // n.A::a = 1; // error ;n.B::a = 1111 ;n.C::a = 2222 ;cout<< n.B::a << endl ;cout<< n.C::a << endl ;)(_getch }
Output: 1111 2222
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺗﻨﻬﺎ ﻳﻚ ﻛﭙﻲ از Aدرﺳﺖ ﺷﻮد ﺑﺎﻳﺪ از virtual inheritanceاﺳﺘﻔﺎده ﻛﻨﻴﻢ .در اﻳﻦ ﺻﻮرت ﻳﻚ hierarchyﻣﺜﻞ ﺷﻜﻞ زﻳﺮ ﺧﻮاﻫﻴﻢ داﺷﺖ:
ﺑﺮاي اﻳﻦ ﻛﺎر ﻛﺎﻓﻲ اﺳﺖ ﺷﺎﺧﻪﻫﺎﻳﻲ ﻛﻪ ﻣﻲﺧﻮاﻫﻴﻢ ﺑﻪ ﻳﻚ Aﻣﺸﺘﺮك ﻣﺘﺼﻞ ﺑﺎﺷﻨﺪ virtual ،ﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ. ﺑﻪ اﻳﻦ hierarchyﻧﮕﺎه ﻛﻨﻴﺪ: 275
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
: ﻗﺎﺑﻞ ﺗﻌﺮﻳﻒ ﻫﺴﺘﻨﺪhierarchy ﻛﻼسﻫﺎﻳﻲ ﺑﺎ اﻳﻦBorland ﻫﺎيcompiler در #include <conio.h> #include <iostream> using namespace std; class A { public: int a; A() { cout<< "A constructor\n"; } ~A() { cout<< "A destructor\n"; } }; class B: virtual public A { public: B() { cout<< "B constructor\n"; } ~B() { cout<< "B destructor\n"; } };
276
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ class C: virtual public A { public: C() { cout<< "C constructor\n"; } ~C() { cout<< "C destructor\n"; } }; class D: public A { public: D() { cout<< "D constructor\n"; } ~D() { cout<< "D destructor\n"; } }; class E: public B,virtual public A, public C, public D { public: E() { cout<< "E constructor\n"; } ~E() { cout<< "E destructor\n"; } }; int main() { { E e; } _getch(); }
Output (BDS 2006): A constructor B constructor C constructor A constructor D constructor
277
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ constructor destructor destructor destructor destructor destructor destructor
E E D A C B A
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﻨﻬﺎ دو Aﺳﺎﺧﺘﻪ ﺷﺪه اﺳﺖ .ﺑﻌﺪ از اﻳﻦ ﻛﻪ اوﻟﻴﻦ Aﺳﺎﺧﺘﻪ ﺷﺪ B ،ﺳﺎﺧﺘﻪ ﻣﻲﺷﻮد و ﺑﺪون اﻳﻦ ﻛﻪ A
دﻳﮕﺮي ﺳﺎﺧﺘﻪ ﺷﻮد C ،ﺳﺎﺧﺘﻪ ﻣﻲﺷﻮد .ﺑﻌﺪ Aدوم ﺳﺎﺧﺘﻪ ﻣﻲﺷﻮد و ﺑﻌﺪ از آن Dﺑﺎ اﺳﺘﻔﺎده از اﻳﻦ Aدوم ﺳﺎﺧﺘﻪ ﻣﻲﺷﻮد .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻓﻘﻂ ﺷﺎﺧﻪﻫﺎي A ،virtualﻣﺸﺘﺮك دارﻧﺪdestructor .ﻫﺎ ﺑﺎ ﺗﺮﺗﻴﺐ ﻋﻜﺲ constructorﻫﺎ ﻓﺮاﺧﻮاﻧﺪه ﻣﻲﺷﻮﻧﺪ. Microsoft Visual C++ 2005از hierarchyاﻳﻦ ﻣﺜﺎل اﻳﺮاد ﻣﻲﮔﻴﺮد و ﻣﻲﮔﻮﻳﺪ ﻛﻪ Eﻧﺒﺎﻳﺪ ﺑﻪ ﻃﻮر ﻣﺴﺘﻘﻴﻢ از A deriveﺷﻮد )ﭼﻪ virtualو ﭼﻪ ﻏﻴﺮ .(virtualدر ﻣﻮرد ﻏﻴﺮ virtualﻣﻲﺷﻮد ﺗﺎ ﺣﺪي ﺑﻪ آن ﺣﻖ داد ﭼﻮن دﺳﺘﺮﺳﻲ ﺑﻪ A::aﻛﻪ base classﻣﺴﺘﻘﻴﻢ اﺳﺖ وﺟﻮد ﻧﺪارد )در واﻗﻊ ﻧﻤﻲﺷﻮد ﺑﻪ compiler ﻓﻬﻤﺎﻧﺪ ﻛﻪ ﻣﻨﻈﻮر ﻣﺎ ﻛﺪام Aاﺳﺖ(.
ﺑﻴﺎﻳﻴﺪ ﺑﺮﻧﺎﻣﻪاي ﺑﺮاي hierarchyﺳﺎدهي زﻳﺮ ﺑﻨﻮﻳﺴﻴﻢ:
>#include <conio.h
278
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <iostream> using namespace std; class A { public: int a; A() { cout<< "A constructor\n"; } ~A() { cout<< "A destructor\n"; } }; class B: virtual public A { public: B() { cout<< "B constructor\n"; } ~B() { cout<< "B destructor\n"; } }; class C: virtual public A { public: C() { cout<< "C constructor\n"; } ~C() { cout<< "C destructor\n"; } }; class D: public B,public C { public: D() { cout<< "D constructor\n"; } ~D() { cout<< "D destructor\n"; } }; int main()
279
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { { D e; cout<< endl; e.a = 2; cout<< e.B::a cout<< e.C::a cout<< e.A::a cout<< e.a cout<< endl;
<< << << <<
endl; endl; endl; endl;
} _getch(); }
Output: A constructor B constructor C constructor D constructor 2 2 2 2 D C B A
destructor destructor destructor destructor
abstract سهO
اﺳﺖ( ﻛﻼﺳﻲ اﺳﺖ ﻛﻪ ﺗﺎﺑﻊAbstract Data Type )ﻛﻪ ﻣﺨﻔﻒADT ( ﻳﺎabstract) ﻳﻚ ﻧﻮع اﻧﺘﺰاﻋﻲ method ﻛﺮدن ﻳﻚpure ﺑﺮاي. ﻣﻲﮔﻮﻳﻴﻢabstract ﺑﻪ ﭼﻨﻴﻦ ﻛﻼﺳﻲ ﻛﻼس. داﺷﺘﻪ ﺑﺎﺷﺪpure virtual در."=0" آن در ﻛﻼس ﻧﻮﺷﺖprototype ﺑﺎﻳﺪ اوﻻ ﺗﻌﺮﻳﻒ آن را ﺑﻪ ﺧﺎرج از ﻛﻼس ﺑﺮد و ﺛﺎﻧﻴﺎ ﺟﻠﻮي : اﺳﺖf() pure virtual ﻛﻼس زﻳﺮ class A { public:
280
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;int a ;virtual void f() = 0 )(A { ;"cout<< "A constructor\n } )(~A { ;"cout<< "A destructor\n } ;} )(void A::f { ;"cout<<"f()\n }
در اﻳﻦ ﺣﺎﻟﺖ ﻛﻼس abstractاﺳﺖ و f() pureاﺳﺖ .ﺗﻨﻬﺎ methodﻫﺎي virtual؛ ﻣﻲﺗﻮاﻧﻨﺪ pure ﺑﺎﺷﻨﺪ .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ pure virtualﺑﺎ pureﺧﺎﻟﻲ ﻓﺮﻗﻲ ﻧﺪاردmethod .ﻫﺎي pureﻧﻴﺎزي ﺑﻪ ﺗﻌﺮﻳﻒ ﻧﺪارﻧﺪ وﻟﻲ اﮔﺮ ﺑﺮاي آنﻫﺎ ﺗﻌﺮﻳﻒ ﻗﺮار ﺑﺪﻫﻴﻢ در BDS 2006ﺑﺎﻳﺪ ﺧﺎرج از ﻛﻼس ﺑﺎﺷﺪ .در Visual C++ 2005ﺗﻌﺮﻳﻒ را ﻣﻲﺷﻮد ﺑﻪ ﺻﻮرت ﻋﺎدي در ﺧﻮد ﻛﻼس ﻫﻢ اﻧﺠﺎم داد: class A { public: ;int a virtual void f() = 0 { ;"cout<<"f()\n } ;}
ﻫﻴﭻ objectي ﻧﻤﻲﺷﻮد از ﻧﻮع ﻳﻚ ﻛﻼس ِ abstractﺗﻌﺮﻳﻒ ﻛﺮد .ﻳﻌﻨﻲ ﺑﺎ ﻛﻼس Aﻛﻪ abstractاﺳﺖ ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: ;A a
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;A* p
وﻟﻲ ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: ;A* p = new A
ﭼﻮن ﻫﻴﭻ instanceي از ﻳﻚ ﻛﻼس abstractﻧﻤﻲﺷﻮد داﺷﺖ ) instanceﻳﻌﻨﻲ .(objectﻛﻼسﻫﺎي abstractﺗﻨﻬﺎ ﺑﻪ درد base classﺑﻮدن ﻣﻲﺧﻮرﻧﺪ .ﻳﻌﻨﻲ ﻓﻘﻂ ﺑﻪ اﻳﻦ درد ﻣﻲﺧﻮرﻧﺪ ﻛﻪ ﻛﻼسﻫﺎي دﻳﮕﺮي از آنﻫﺎ deriveﺷﻮﻧﺪ .اﮔﺮ ﻛﻼسﻫﺎي deriveﺷﺪه ﻫﻤﻪي ﺗﺎﺑﻊﻫﺎي pureرا دوﺑﺎره ﺗﻌﺮﻳﻒ ﻧﻜﻨﻨﺪ ،ﺧﻮد ِ ﺗﺎﺑﻊﻫﺎي pureرا ﺑﻪ ارث ﻣﻲﺑﺮﻧﺪ و در ﻧﺘﻴﺠﻪ ﺧﻮدﺷﺎن ﻫﻢ abstractﻣﻲﺷﻮﻧﺪ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ:
281
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <conio.h> #include <iostream> using namespace std; class A { public: int a; virtual void f() = 0; virtual void g() = 0; }; void A::f() { cout<<"f()\n"; } class B: public A { public: void g() { cout<<"g() is defined\n"; } }; class C: public B { public: void f() { A::f(); cout<< "f() is redefined\n"; } }; int main() { C c; c.f(); c.g(); //c.A::g(); // error _getch(); }
Output: f() f() is redefined g() is defined
282
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ راg() ،B . داده ﻧﺸﺪهg() ﻫﻴﭻ ﺗﻌﺮﻳﻔﻲ ﺑﺮايA در. ﻫﺴﺘﻨﺪabstract ﻫﺮ دوB وA در اﻳﻦ ﻣﺜﺎل ﻛﻼسﻫﺎي ﻫﻢf() ،C در ﻛﻼس. را ﺗﻌﺮﻳﻒ ﻧﻜﺮدهf() ِ pure اﺳﺖ ﭼﻮن ﻫﻨﻮز ﺗﺎﺑﻊabstract ﺗﻌﺮﻳﻒ ﻛﺮده ﺑﺎ اﻳﻦ ﺣﺎل . ﻧﻴﺴﺖabstract دﻳﮕﺮC . در اﻳﻦ ﺗﻌﺮﻳﻒ ﺑﻪ ﻛﺎر ﺑﺮده ﺷﺪهA درf() ﺗﻌﺮﻳﻒ ﻗﺒﻠﻲ.ﺗﻌﺮﻳﻒ ﺷﺪه ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ. ﺑﻪ ﻋﻨﻮان ﻳﻚ ﻗﺎﻟﺐ ﺑﺮاي ﺗﻌﺮﻳﻒ ﻛﻼسﻫﺎي دﻳﮕﺮ ﺑﻪ ﻛﺎر ﻣﻲروﻧﺪabstract ﻛﻼسﻫﺎي Truck ،( )ﻣﺎﺷﻴﻦCar )وﺳﻴﻠﻪي ﻧﻘﻠﻴﻪ( را ﺑﻪ ﻋﻨﻮان ﻳﻚ ﻗﺎﻟﺐ دارد و ﻛﻼسﻫﺎيVehicle ﻛﻨﻴﺪ ﻛﻪ GetFuel() ِ pure ﺗﺎﺑﻊVehicle ﻛﻼس. ﺷﺪهاﻧﺪderive )ﻫﻮاﭘﻴﻤﺎ( از آنairplane )ﻛﺎﻣﻴﻮن( و
:)ﺳﻮﺧﺖ ﮔﺮﻓﺘﻦ( را در ﺧﻮدش دارد #include <conio.h> #include <iostream> using namespace std; class Vehicle { public: virtual void GetFuel() = 0; }; class Car: public Vehicle { public: void GetFuel() { cout<< "Get gasoline\n"; } }; class Truck: public Vehicle { public: void GetFuel() { cout<< "Get diesel\n"; } }; class Airplane { public: void GetFuel() { cout<<"Get fuel\n"; } }; int main() { Car Speedy; Airplane Jimbo;
283
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Speedy.GetFuel(); Jimbo.GetFuel(); _getch(); }
Output: Get gasoline Get fuel
. ﻣﻲﺷﻮدabstract ﺷﻮد ﻛﻼس ﺣﺎوي آن ﺗﺎﺑﻊpure ،در ﻧﻬﺎﻳﺖ ﻣﻲﮔﻮﻳﻢ ﻛﻪ ﺣﺘﻲ اﮔﺮ ﻳﻚ ﺗﺎﺑﻊ
compiler > ? سه درO د از."#ا
C++ و زﺑﺎنcompiler ﻣﻲﺷﻮد ﺷﻨﺎﺧﺖ ﺑﻬﺘﺮي ازdestructor ﻫﺎ وconstructor ،ﺑﺎ اﺳﺘﻔﺎده از ﻛﻼس . در اﻳﻦ ﺟﺎ ﭼﻨﺪ ﺗﺎ ﻣﺜﺎل ﻣﻲآورم.(ﭘﻴﺪا ﻛﺮد )ﻳﻌﻨﻲ ﻣﻲﺷﻮد آنﻫﺎ را رواﻧﺸﻨﺎﺳﻲ ﻛﺮد :ﺑﻪ اﻳﻦ ﻣﺜﺎل ﺗﻮﺟﻪ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; class A { public: A() { cout<< "an A is constructed\n"; } ~A() { cout<< "an A is destroyed\n"; } }; int main() { { A a[3][2]; } _getch(); }
Output: an A is constructed an A is constructed
284
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ an an an an an an an an an an
A A A A A A A A A A
is is is is is is is is is is
constructed constructed constructed constructed destroyed destroyed destroyed destroyed destroyed destroyed
. ﻓﺮاﺧﻮاﻧﺪه ﺷﺪهconstructor (ﻫﺎي ﻣﻮﺟﻮد در آراﻳﻪint ﺑﺎر )ﺑﻪ ﺗﻌﺪاد6 ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ : ﻣﺜﺎل زﻳﺮ ﻗﺎﺑﻞ ﺗﻮﺟﻪ اﺳﺖthrow وcatch ،try در ﻣﻮرد ﻛﻠﻤﻪﻫﺎي ﻛﻠﻴﺪي #include <conio.h> #include <iostream> using namespace std; class A { public: A() { cout<< "an A is constructed\n"; } ~A() { cout<< "an A is destroyed\n"; } A(const A& a) { cout<< "A copy constructor\n"; } }; void f() { A a; throw a; } int main() { try { f(); } catch(A& a) { } _getch(); }
285
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output (BDS 2006): an A is constructed A copy constructor A copy constructor an A is destroyed an A is destroyed an A is destroyed Output (Visual C++ 2005) : an A is constructed A copy constructor an A is destroyed an A is destroyed
... وconstructor ﻫﺎ ﻓﻘﻂ ﻣﺜﻞ ﻳﻚ اﺳﻢ ِ ﺟﺎﻳﮕﺰﻳﻦ ﻋﻤﻞ ﻣﻲﻛﻨﻨﺪ وreference ﺑﺮﻧﺎﻣﻪي ﺑﻌﺪي ﻧﺸﺎن ﻣﻲدﻫﺪ :را ﻓﺮا ﻧﻤﻲﺧﻮاﻧﻨﺪ #include <conio.h> #include <iostream> using namespace std; class A { public: A() { cout<< "an A is constructed\n"; } ~A() { cout<< "an A is destroyed\n"; } A(const A& a) { cout<< "A copy constructor\n"; } void operator = (A a) { cout<< "operator =\n"; } }; int main() { { A a; A& b = a; } _getch(); }
286
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: an A is constructed an A is destroyed
. اﺳﺖa اﺳﻢ دﻳﮕﺮي ﺑﺮايb ﻫﻴﭻ ﭼﻴﺰ را ﻓﺮا ﻧﺨﻮاﻧﺪه و اﻧﮕﺎرb ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺗﻌﺮﻳﻒ
هunion
ﻳﻚ ﻛﻼس اﺳﺖ ﺑﺎ اﻳﻦ ﻓﺮق ﻛﻪ ﺣﺎﻓﻈﻪي ﻫﻤﻪي ﻣﺘﻐﻴﺮﻫﺎي آن در ﻳﻚ ﺟﺎ ﻗﺮار دارد و از ﻳﻚ ﺟﺎunion ﻳﻚ :ﺷﺮوع ﻣﻲﺷﻮد #include <conio.h> #include <iostream> using namespace std; union A { int a; char ch; void f() { cout<< "f()"; } A():a(0),ch(0) { cout<< "constructor\n"; } }; int main() { A a; a.ch = 97; cout<< a.a << endl; a.f(); _getch(); }
Output: constructor 97 f()
287
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ unionدر اﻳﻦ ﻣﺜﺎل درﺳﺖ ﻣﺜﻞ ﻳﻚ structﻋﻤﻞ ﻣﻲﻛﻨﺪ ﺑﺎ اﻳﻦ ﻓﺮق ﻛﻪ A::chاز ﻫﻤﺎن ﺣﺎﻓﻈﻪاي اﺳﺘﻔﺎده ﻣﻲﻛﻨﺪ ﻛﻪ A::aاﺳﺘﻔﺎده ﻣﻲﻛﻨﺪ .ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ وﻗﺘﻲ ﻣﻘﺪار 97ﺑﻪ chداده ﺷﺪه a ،ﻫﻢ ﻫﻤﻴﻦ ﻣﻘﺪار را ﮔﺮﻓﺘﻪ اﺳﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺛﺎﺑﺖ ﺷﻮد ﻣﻜﺎن ﺣﺎﻓﻈﻪ ﻳﻜﻲ اﺳﺖ ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std class Cat { ;int a ;int b ;} union A { ;Cat Tom ;int a ;char ch ;}
;endl ;endl ;endl ;endl ;endl
<< << << << <<
)(int main { ;A a cout<< (int) &a.Tom cout<< (int) &a.a cout<< (int) &a.ch )cout<< sizeof(A )(cout<< typeid(A).name ;)(_getch }
Output (BDS 2006): 1245060 1245060 1245060 8 A Output (Visual C++ 2005): 1245020 1245020 1245020 8 union A
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻣﻜﺎن ﺣﺎﻓﻈﻪي ﻫﺮ ﺳﻪ ﻣﺘﻐﻴﺮ unionدر اﻳﻦ ﻣﺜﺎل ﻳﻜﻲ اﺳﺖ .اﻧﺪازهي unionﺑﺮاﺑﺮ ﺑﺎ اﻧﺪازهي ﺑﺰرگ ﺗﺮﻳﻦ ﻣﺘﻐﻴﺮ آن اﺳﺖ.
288
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻓﺮق دﻳﮕﺮ unionﺑﺎ structاﻳﻦ اﺳﺖ ﻛﻪ ﻧﻤﻲﺷﻮد از آن unionدﻳﮕﺮي را deriveﻛﺮد ﻳﻌﻨﻲ ﻧﻤﻲﺗﻮاﻧﺪ - baseﻛﻼس ﺑﺎﺷﺪ. ﻣﺜﺎل زﻳﺮ ﻛﺎرﺑﺮد ﺟﺎﻟﺒﻲ از ﻳﻚ unionرا ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std
;a1 ;a2 ;a3 ;a4
union A { public: ;int a struct { char char char char ;} ;}
)(int main { ;}A a = {44 + 22*256 + 11*256*256 ;int x ;x = (unsigned char)a.a1 cout << x << endl; // first byte ;x = (unsigned char)a.a2 ;cout << x // second byte ;)(_getch }
Output: 44 22
در اﻳﻦ ﻣﺜﺎل ﻳﻚ unionدارﻳﻢ ﻛﻪ ﺑﻪ وﺳﻴﻠﻪي آن \ int nﻳﻌﻨﻲ A::aرا ﺑﻪ ﭼﻬﺎر ﺑﺎﻳﺖ ﺷﻜﺴﺘﻪاﻳﻢ .در )( mainﺑﻪ اﻳﻦ intﻣﻘﺪاري دادهام و دو ﺑﺎﻳﺖ اول آن را ﭼﺎپ ﻛﺮدهام .ﻧﻜﺘﻪي ﻗﺎﺑﻞ ﺗﻮﺟﻪ در اﻳﻦ ﻣﺜﺎل اﻳﻦ اﺳﺖ ﻛﻪ structﺗﻌﺮﻳﻒ ﺷﺪه در unionﻫﻴﭻ اﺳﻤﻲ ﻧﺪارد و ﻫﻴﭻ ﻣﺘﻐﻴﺮي ﻫﻢ از ﻧﻮع آن وﺟﻮد ﻧﺪارد .اﻋﻀﺎي آن در واﻗﻊ اﻋﻀﺎي Aﺑﻪ ﺣﺴﺎب ﻣﻲآﻳﻨﺪ ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻧﻮﺷﺘﻴﻢ .a.a1اﻟﺒﺘﻪ ﺣﺎﻓﻈﻪي a1و ﻣﺜﻼ a2ﻳﻜﻲ ﻧﻴﺴﺖ. اﮔﺮ union A { ;int a
289
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int b; };
:ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ A u = {1,2};
: ﻳﻚ ﻣﺘﻐﻴﺮ ﺑﻴﺶ ﺗﺮ ﻧﺪاردA ﭼﻮن ﻋﻤﻼ #include <conio.h> #include <iostream> using namespace std; union A { int a; int b; char ch; }; int main() { A u = {97}; cout<< u.a << endl; cout<< u.b << endl; cout<< u.ch; _getch(); }
Output 97 97 a
يunion ﺑﻪ ﭼﻨﻴﻦ. اﻳﺠﺎد ﭼﻨﺪ ﻣﺘﻐﻴﺮ ﺑﺎ ﻳﻚ ﻣﻜﺎن ﺣﺎﻓﻈﻪ اﺳﺖunion ﻳﻜﻲ دﻳﮕﺮ از ﻛﺎرﺑﺮدﻫﺎي : ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ. ﻳﻌﻨﻲ »ﺑﻲ اﺳﻢ« ﻣﻲﮔﻮﻳﻨﺪanonymous #include <conio.h> #include <iostream> using namespace std; int main() { union { int a; int b; }; a = 1387; cout<< b;
290
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)(_getch }
Output: 1387
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ aو bدر ﻳﻚ ﻣﻜﺎن از ﺣﺎﻓﻈﻪ ﻗﺮار دارﻧﺪ. unionﻫﺎي anonymousﻧﻤﻲﺗﻮاﻧﻨﺪ ﺗﺎﺑﻊ ﻳﺎ ﻋﻀﻮ ﻏﻴﺮ publicداﺷﺘﻪ ﺑﺎﺷﻨﺪ.
@? '
(1ﻛﻼس زﻳﺮ ﺷﺮوﻋﻲ ﺑﺮ ﻳﻚ ﻛﻼس ﻛﺎﻣﻞ ﺑﺮاي رﺷﺘﻪﻫﺎﺳﺖ: class String { ;char* c public: )String(char* normal_string { ;)int len = StringLength(normal_string ;]c = new char [len + 1 )for(unsigned i = 0; i <= len; i++ ;]c[i] = normal_string[i } )(~String { ;delete [] c } )void Print(void { ;cout<< c << endl } ;}
آن را ﺑﺎ اﺿﺎﻓﻪ ﻛﺮدن operator+ﺑﺮاي ﺗﺮﻛﻴﺐ رﺷﺘﻪﻫﺎ و ][ operatorﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪ iاﻣﻴﻦ ﻛﺎراﻛﺘﺮ ﻛﺎﻣﻞ ﺗﺮ ﻛﻨﻴﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ اﻳﻦ ﻛﻼس ﺣﺎﻓﻈﻪ را از heapﻣﻲﮔﻴﺮد وﻟﻲ ﺑﻪ ﺧﺎﻃﺮ وﺟﻮد destructorﻣﻨﺎﺳﺐ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻧﻴﺎزي ﻧﺪارد ﻣﺪام ﻣﺮاﻗﺐ ][ deleteﻛﺮدن ﺣﺎﻓﻈﻪ ﺑﺎﺷﺪ .ﺑﻬﺘﺮ اﺳﺖ اﻳﻦ ﻛﻼس را ﺗﺎ آن ﺟﺎ ﻛﻪ ﻣﻤﻜﻦ اﺳﺖ ﻛﺎﻣﻞ ﻛﻨﻴﺪ و در header fileﻣﺨﺼﻮص ﺧﻮدﺗﺎن ﻗﺮار ﺑﺪﻫﻴﺪ. (2ﺑﺮﻧﺎﻣﻪي زﻳﺮ را ﺑﺒﻴﻨﻴﺪ: >#include <conio.h >#include <iostream
291
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; class Data { public: char a[80][25]; int current_x; int current_y; void Reset() { current_x = 0; current_y = 0; for(int i = 0; i < 80; i++) for(int j= 0; j < 25; j++) a[i][j] = 0; } Data() { Reset(); } void Add(char ch) { a[current_x][current_y] = ch; if(current_x < 79) current_x++; else { current_x = 0; current_y ++; } } }; class Screen: public Data { public: Screen() {} void Clear() { system("CLS"); } void PaintWindow() { Clear(); for(int j = 0; j < 25; j++) for(int i = 0; i < 80; i++) { bool b = (i == 79) && (j == 24); if(!b) cout<< a[i][j]; }
292
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } )Screen& operator << (char right_side { ;)Add(right_side ;)(PaintWindow ;return *this } ;} )(int main { ;Screen cout1, cout2 ;'cout1 << 'a' << 'b ;'cout2 << 'x' << 'y ;'cout1 << 'c' << 'd ;)(_getch }
Sس Screenﻳﻚ ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ را در ﺧﻮدش ﺟﺎي ﻣﻲدﻫﺪ .ﺑﺎ Screenﻣﻲﺗﻮاﻧﻴﺪ ﭼﻨﺪ ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ در ﺣﺎﻓﻈﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ .دﺳﺘﻮر ;)"system("CLS
ﺻﻔﺤﻪي ﻧﻤﺎﻳﺶ را ﭘﺎك ﻣﻲﻛﻨﺪ .در ﻓﺼﻞ ﺑﻌﺪ درﺑﺎرهي آن ﺑﻴﺶ ﺗﺮ ﻣﻲﮔﻮﻳﻢ. اﻟﻒ( ﻛﻼس Screenرا ﻃﻮري ﺗﻐﻴﻴﺮ دﻫﻴﺪ ﻛﻪ ﺑﺎ cout1و cout2ﺑﺘﻮاﻧﻴﻢ رﺷﺘﻪﻫﺎ را ﻫﻢ ﭼﺎپ ﻛﻨﻴﻢ. ب( ﻛﺎري ﻛﻨﻴﺪ ﻛﻪ ﭼﺎپ ﺑﺎ cout1و cout2ﺳﺮﻳﻊ ﺗﺮ ﺷﻮد. راﻫﻨﻤﺎﻳﻲ :وﻗﺘﻲ ] a[i][jﻣﻘﺪار ﺻﻔﺮ دارد ﻻزم ﻧﻴﺴﺖ ﭼﺎپ ﺷﻮد. ج( ﺗﺎﺑﻌﻲ ﺑﻪ ﻧﺎم )( gotoxyﺑﻪ ﺑﺮﻧﺎﻣﻪ اﺿﺎﻓﻪ ﻛﻨﻴﺪ ﻛﻪ ﻣﻜﺎن ﺟﺎري را ﻛﻪ ﺑﺎ current_xو current_yﻣﺸﺨﺺ ﻣﻲﺷﻮد ﺗﻐﻴﻴﺮ ﺑﺪﻫﺪ. د( آﻳﺎ ﻓﺮاﻳﻨﺪ ﭼﺎپ ﻛﺎراﻛﺘﺮ ' '\nدرﺳﺖ اﺳﺖ؟ اﮔﺮ درﺳﺖ ﻧﻴﺴﺖ آن را اﺻﻼح ﻛﻨﻴﺪ. ه( ﻛﻼسﻫﺎي ﺑﺮﻧﺎﻣﻪ را ﻃﻮري ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﺪ ﻛﻪ اﮔﺮ ﺑﻴﺶ از 25ﺧﻂ ﭼﺎپ ﻛﻨﻴﻢ ،ﺧﻂ اول ﺣﺬف ﺷﻮد و ﻣﺤﺘﻮﻳﺎت آراﻳﻪي ﺑﺎ آن ﭼﻪ در ﺻﻔﺤﻪ ﻧﻤﺎﻳﺶ ﻣﻲﺑﻴﻨﻴﺪ ﻳﻜﻲ ﺑﺎﺷﺪ. (3ﺑﺎ ﻛﻼسﻫﺎ ﻳﻚ ﺗﻠﻮﻳﺰﻳﻮن را ﻛﻪ ﻛﻨﺘﺮل از راه دور دارد ﺷﺒﻴﻪ ﺳﺎزي ﻛﻨﻴﺪ:
293
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (aﻛﻼﺳﻲ ﺑﺎ ﻧﺎم TVﺑﺴﺎزﻳﺪ ﻛﻪ ﻧﻤﺎﻳﻨﺪهي ﺗﻠﻮﻳﺰﻳﻮن ﺑﺎﺷﺪ int n\ TV .دارد ﻛﻪ ﻧﺸﺎن دﻫﻨﺪه- ي ﻛﺎﻧﺎل در ﺣﺎل ﻧﻤﺎﻳﺶ اﺳﺖ. (bﻛﻼﺳﻲ ﺑﺴﺎزﻳﺪ ﻛﻪ ﻧﻤﺎﻳﻨﺪهي ﻣﻮﺟﻲ ﺑﺎﺷﺪ ﻛﻪ از ﻛﻨﺘﺮل از راه دور ﺑﻪ ﺗﻠﻮﻳﺰﻳﻮن ﻣﻲرﺳﺪ. (cﻛﻼﺳﻲ ﺑﺴﺎزﻳﺪ ﻛﻪ ﻧﻤﺎﻳﻨﺪهي ﺧﻮد ﻛﻨﺘﺮل از راه دور ﺑﺎﺷﺪ ﻫﻤﺮاه ﺑﺎ methodﻫﺎﻳﻲ ﻛﻪ ﻫﺮ ﻛﺪام ﻧﻤﺎﻳﻨﺪهي ﻳﻚ دﮔﻤﻪ از ﻛﻨﺘﺮل ﻫﺴﺘﻨﺪ و constructorآن ﻳﻚ ﭘﺎراﻣﺘﺮ از ﻧﻮع TVدارد. ﺑﺎ ﻓﺸﺎر ﻫﺮ دﮔﻤﻪ ،ﻣﻮﺟﻲ ﺑﻪ ﺗﻠﻮﻳﺰون ارﺳﺎل ﻣﻲﺷﻮد ﻛﻪ ﻛﺎﻧﺎل آن را ﺗﻐﻴﻴﺮ ﻣﻲدﻫﺪ.
' ه A#
(1ﭼﻨﺪ وﻗﺖ ﭘﻴﺶ در ﻳﻜﻲ از forumﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ﻣﺴﺄﻟﻪاي را ﻣﻄﺮح ﻛﺮدم ﻛﻪ ﺑﻌﻀﻲ از ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲﻫﺎي ﺣﺮﻓﻪاي آن ﺟﺎ ﮔﻔﺘﻨﺪ ﻛﻪ ﻧﻮﺷﺘﻦ اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﻣﻜﺎن ﻧﺪارد .ﺑﻌﺪ ﻣﻦ و ﻳﻜﻲ دﻳﮕﺮ از اﻋﻀﺎي forumﻫﺮ ﻛﺪام آن ﺑﺮﻧﺎﻣﻪ را ﻧﻮﺷﺘﻴﻢ .راه ﺣﻞ ﻣﻦ از ﻛﻼسﻫﺎ و ﻗﺎﺑﻠﻴﺖﻫﺎي آنﻫﺎ ﻛﻤﻚ ﻣﻲﮔﺮﻓﺖ و ﺗﻨﻬﺎ در C++ﻣﻌﻨﻲ داﺷﺖ .راه ﺣﻞ دﻳﮕﺮ ﺟﺎﻟﺐ ﺗﺮ ﺑﻮد و در Cﻫﻢ ﻣﻌﻨﻲ داﺷﺖ .ﻣﺴﺄﻟﻪ اﻳﻦ اﺳﺖ: »ﺑﺪون اﺳﺘﻔﺎده از آراﻳﻪ و ﺑﺪون اﺳﺘﻔﺎده از ﺣﺎﻓﻈﻪي heapﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﻋﺪد nرا از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد .ﺑﻌﺪ nﺗﺎ ﻋﺪد ﺻﺤﻴﺢ از ﻛﺎرﺑﺮ ﺑﮕﻴﺮد و در ﻧﻬﺎﻳﺖ اﻋﺪاد ﺻﺤﻴﺢ را ﺑﺎ ﻫﻤﺎن ﺗﺮﺗﻴﺒﻲ ﻛﻪ وارد ﺷﺪهاﻧﺪ ﭼﺎپ ﻛﻨﺪ« اﮔﺮ اﻳﻦ ﻣﺴﺄﻟﻪ را ﺣﻞ ﻛﺮدﻳﺪ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ اﺳﺘﻌﺪاد ﺧﻮﺑﻲ در ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ دارﻳﺪ .اﮔﺮ ﻧﺘﻮاﻧﺴﺘﻴﺪ ﺣﻞ ﻛﻨﻴﺪ ﻣﻲﺗﻮاﻧﻴﺪ ﺑﺎ ﺟﻤﻠﻪﻫﺎﻳﻲ ﻣﺜﻞ »ﻧﻮﺷﺘﻦ اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﺻﻼ ﭼﻪ ﻓﺎﻳﺪهاي دارد؟« ﻳﺎ »وﻗﺘﻲ ﻣﻲﺷﻮد از آراﻳﻪ اﺳﺘﻔﺎده ﻛﻨﻴﻢ ﭼﺮا ﻧﻜﻨﻴﻢ؟« ﻧﺎﺗﻮاﻧﻲ ﺧﻮدﺗﺎن را ﺗﻮﺟﻴﻪ ﻛﻨﻴﺪ
.
(2اول ﻛﻼﺳﻲ ﺑﺎ ﻧﺎم Integerﺑﻨﻮﻳﺴﺪ ﻛﻪ ﻫﺮ objectاز آن ،ﻧﻤﺎﻳﻨﺪهي ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ )ﻣﺜﻞ - ،12 0 ، 126و (...ﺑﺎﺷﺪ و اﻳﻦ ﻋﺪد ﺻﺤﻴﺢ ﻫﺮ ﭼﻘﺪر ﻫﻢ ﻃﻮﻻﻧﻲ ﺑﺎﺷﺪ )ﻣﺜﻼ 200رﻗﻢ( ﻣﺸﻜﻠﻲ ﭘﻴﺶ ﻧﻴﺎﻳﺪ و اﻋﻤﺎل ﺿﺮب ،ﺟﻤﻊ و ...در ﻛﻼس ﺑﺎﺷﺪ و constructorﻫﺎ و ﻋﻤﻠﮕﺮﻫﺎي ﻣﻨﺎﺳﺐ را ﻫﻢ داﺷﺘﻪ ﺑﺎﺷﺪ .ﺑﻌﺪ ﻛﻼس و ﺗﻌﺮﻳﻒﻫﺎي ﻣﺮﺑﻮط ﺑﻪ آن را در ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﻧﺎم integer.hﻗﺮار ﺑﺪﻫﻴﺪ و ﻓﺎﻳﻞ را در ﻣﻜﺎن ﻣﻨﺎﺳﺐ ﺑﮕﺬارﻳﺪ ﺗﺎ compilerﻗﺎدر ﺑﻪ ﭘﻴﺪا ﻛﺮدن آن ﺑﺎﺷﺪ .ﻫﻤﻪي اﻳﻦ ﺗﻌﺮﻳﻒﻫﺎ ﺑﺎﻳﺪ ﻃﻮري ﺑﺎﺷﺪ ﻛﻪ ﺑﺮﻧﺎﻣﻪ زﻳﺮ اﺟﺮا ﺷﻮد و ﺧﺮوﺟﻲ آن ﻋﺪد ﻧﺸﺎن داده ﺷﺪه ﺑﺎﺷﺪ:
294
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
>#include <conio.h >#include <iostream ;using namespace std "#include "integer.h )(int main { ;"Integer a = "12345678901234567890123456789012345678901234567890 ;a++ ;cout<< a << endl ;)cout<< factorial(50 }
Output: 12345678901234567890123456789012345678901234567891 30414093201713378043612608166064768844377641568960512000000000000
ﻣﻨﻈﻮر از factorialﻫﻤﺎن ﻓﺎﻛﺘﻮرﻳﻞ اﺳﺖ ) 50ﻓﺎﻛﺘﻮرﻳﻞ ﺑﺮاﺑﺮ ﺑﺎ ﺣﺎﺻﻞ ﺿﺮب 1ﺗﺎ 50اﺳﺖ(. آﻳﺎ 11111111111111111111111111111111111111111ﻋﺪدي اول اﺳﺖ؟ )آﻳﺎ ﺗﻨﻬﺎ راه ﭘﺎﺳﺦ ﮔﻮﻳﻲ ﻧﻮﺷﺘﻦ ﺗﺎﺑﻊ ﺗﺸﺨﻴﺺ اﻋﺪاد اول اﺳﺖ؟!( (3اﻟﻒ( اﮔﺮ ﺑﺎزي checkersرا ﺑﻠﺪ ﻫﺴﺘﻴﺪ .ﺑﺮﻧﺎﻣﻪ ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﺑﺎ ﺷﻤﺎ checkersﺑﺎزي ﻛﻨﺪ) .ﻧﻮﺷﺘﻦ آن ﻣﻤﻜﻦ اﺳﺖ ﺣﺘﻲ ﻳﻚ ﻣﺎه از وﻗﺖ ﺷﻤﺎ را ﺑﮕﻴﺮد(. ب( ﺑﺪون ﺗﻮﺟﻪ زﻳﺎد ﺑﻪ ﮔﺮاﻓﻴﻚ )ﻣﺜﻼ ﺑﺎ اﺳﺘﻔﺎده از اﺳﻢﻫﺎي اﺳﺘﺎﻧﺪارد ﻣﻬﺮهﻫﺎ و ﺧﺎﻧﻪﻫﺎ( ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﻳﺴﻴﺪ ﻛﻪ ﺷﻄﺮﻧﺞ ﺑﺎزي ﻛﻨﺪ .ﺑﻌﺪ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﺗﺎن را ﺑﺎ ﺑﺮﻧﺎﻣﻪﻫﺎي ﻣﻌﺘﺒﺮ )ﻣﺜﻼ ICC Dasherﻛﻪ از http://www.chessclub.comﻗﺎﺑﻞ downloadاﺳﺖ( ﻣﻘﺎﻳﺴﻪ ﻛﻨﻴﺪ )زور آزﻣﺎﻳﻲ ﻛﻨﻴﺪ(. ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪاي ﻋﺎﻟﻲ ﻣﻤﻜﻦ اﺳﺖ ﭼﻨﺪ ﻣﺎه از وﻗﺖ ﺷﻤﺎ را ﺑﮕﻴﺮد!
295
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Q@ 6Lرم در ﺳﻪ ﻓﺼﻞ ﻗﺒﻞ ﺷﻤﺎ ﺗﺎ اﻧﺪازهي زﻳﺎدي ﺑﺎ اﺑﺰارﻫﺎي ﻣﻮﺟﻮد در C++آﺷﻨﺎ ﺷﺪﻳﺪ .در اﻳﻦ ﻓﺼﻞ ﻗﺼﺪ دارم ﺷﻤﺎ را ﺑﺎ C++ﺑﻬﺘﺮ آﺷﻨﺎ ﻛﻨﻢ .اﺷﺎرهاي ﺑﻪ دﺳﺘﻮرات preprocessorﺧﻮاﻫﻢ داﺷﺖ .ﻧﺤﻮهي ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ در ﭼﻨﺪ ﻓﺎﻳﻞ و ﺑﺮﺧﻲ ﻣﺴﺎﺋﻞ ﻣﺮﺑﻮط ﺑﻪ آن را ﻣﻲﮔﻮﻳﻢ و ﺗﻮﺿﻴﺤﺎﺗﻲ درﺑﺎرهي ﺑﺮﺧﻲ header fileﻫﺎ و ﺑﻌﻀﻲ ﺗﻮاﺑﻊ ﺧﺎص آنﻫﺎ ﺧﻮاﻫﻢ داد )ﻣﺜﻞ ﺗﺎﺑﻊﻫﺎي ﻣﺮﺑﻮط ﺑﻪ اﻳﺠﺎد ﻓﺎﻳﻞ روي .(hard diskدر ﭘﺎﻳﺎن ﻓﻘﻂ ﺑﺮاي ﺗﺸﻮﻳﻖ ﺷﻤﺎ ﺑﺮﻧﺎﻣﻪاي ﺳﺎده ﺑﺮاي ﻣﺤﻴﻂ وﻳﻨﺪوز ﺑﺪون ﺗﻮﺿﻴﺢ زﻳﺎد اراﺋﻪ ﻣﻲﻛﻨﻢ.
د "#ره preprocessor
دﺳﺘﻮرﻫﺎي ،preprocessorدﺳﺘﻮرﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ ﻛﻪ ﻗﺒﻞ از compileﺷﺪن ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻣﻲﺷﻮﻧﺪ و ﺗﻐﻴﻴﺮاﺗﻲ را در ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ و در compilerاﻳﺠﺎد ﻣﻲﻛﻨﻨﺪ .ﺑﻌﺪ از اﺟﺮاي ﺑﺮﻧﺎﻣﻪ و اﻧﺠﺎم ﺗﻐﻴﻴﺮات ﻻزم در source code )ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ( و compiler ،compilerﺷﺮوع ﺑﻪ compileﻛﺮدن ﻣﺘﻦ ﺗﻐﻴﻴﺮ ﻛﺮدهي ﺑﺮﻧﺎﻣﻪ ﻣﻲﻛﻨﺪ .ﻫﺮ دﺳﺘﻮر preprocessorﻣﻌﻤﻮﻻ در ﻳﻚ ﺧﻂ ﺟﺪاﮔﺎﻧﻪ ﻗﺮار ﻣﻲﮔﻴﺮد ﻛﻪ ﺑﺎ #ﺷﺮوع ﻣﻲﺷﻮد .اﻏﻠﺐ compilerﻫﺎ رﻧﮓ ﺧﺎﺻﻲ ﺑﺮاي آنﻫﺎ در ﻧﻈﺮ ﻣﻲﮔﻴﺮﻧﺪ وﻟﻲ ﻣﻲﺷﻮد آن را ﺗﻐﻴﻴﺮ داد .ﻣﻦ رﻧﮓ ﺳﺒﺰ را در زﻣﻴﻨﻪي آﺑﻲ ﭘﻴﺶ ﻧﻬﺎد ﻣﻲ- ﻛﻨﻢ:
ﺑﻪ اﺣﺘﻤﺎل زﻳﺎد compilerﺷﻤﺎ ﻗﺎﺑﻠﻴﺖ ﺗﻐﻴﻴﺮ رﻧﮓ ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ و fontآن را دارد. در ﻣﻮاردي ﻛﻪ دﺳﺘﻮر ﺑﻴﺶ از ﺣﺪ ﻃﻮﻻﻧﻲ ﺑﺎﺷﺪ و ﻗﺮار دادن آن در ﻳﻚ ﺳﻄﺮ ﻣﻨﺎﺳﺐ ﻧﺒﺎﺷﺪ ﺑﺎ ﮔﺬاﺷﺘﻦ »\« در اﻧﺘﻬﺎي ﺧﻂ ﻣﻲﺷﻮد ﺑﻘﻴﻪي دﺳﺘﻮر را ﺑﻪ ﺳﻄﺮ ﺑﻌﺪي ﻣﻨﺘﻘﻞ ﻛﺮد.
296
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در ﻣﻮرد دﺳﺘﻮر #includeﻗﺒﻼ ﺗﻮﺿﻴﺤﺎت ﻛﺎﻓﻲ دادهام و ﻧﻴﺎزي ﺑﻪ ﺗﻮﺿﻴﺢ ﺑﻴﺶ ﺗﺮ ﻧﻴﺴﺖ .ﺑﻌﺪ از آن ،ﻣﻬﻢ ﺗﺮﻳﻦ دﺳﺘﻮر ،preprocessorدﺳﺘﻮر #defineاﺳﺖ .دﺳﺘﻮر #defineﺑﺮاي ﺟﺎﻳﮕﺰﻳﻦ ﻛﺮدن ﺑﻪ ﻛﺎر ﻣﻲرود .ﻣﺜﻼ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻫﻤﻪي forﻫﺎ در ﺑﺮﻧﺎﻣﻪ ﺑﻪ ifﺗﺒﺪﻳﻞ ﺷﻮﻧﺪ ﻣﻲﻧﻮﻳﺴﻴﻢ: #define for if
ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std #define for if )(int main { )for(2 > 1 ;"cout<< "2 > 1 ;)(_getch }
Output: 2 > 1
در اﻳﻦ ﻣﺜﺎل preprocessorاول forرا ﺑﺎ ifﺟﺎﻳﮕﺰﻳﻦ ﻣﻲﻛﻨﺪ و ﺑﻌﺪ ﺑﺮﻧﺎﻣﻪ compileﻣﻲﺷﻮد .اﮔﺮ ﺑﺨﻮاﻫﻴﺪ source codeرا ﺑﻌﺪ از ﻋﻤﻠﻜﺮد preprocessorﺑﺒﻨﻴﺪ در Visual C++ 2005ﻣﺴﻴﺮ زﻳﺮ | Project | console Properties… | Configuration Properties | C/C++ Preprocessor را از ﻃﺮﻳﻖ ﻣﻨﻮ ﻃﻲ ﻛﻨﻴﺪ و ﺟﻠﻮي Generate Preprocessed Fileﻗﺮار دﻫﻴﺪ Without Line ):Numbers(/EP /P
297
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ﺑﻌﺪ ﺑﺮﻧﺎﻣﻪ را compileﻛﻨﻴﺪ و از ﻃﺮﻳﻖ Windowsﺑﻪ ﻣﻜﺎﻧﻲ ﺑﺮوﻳﺪ ﻛﻪ ﻓﺎﻳﻞ .cppﺑﺮﻧﺎﻣﻪ ﺷﻤﺎ ﻗﺮار دارد. ﻓﺎﻳﻠﻲ ﺑﺎ ﭘﺴﻮﻧﺪ .iﻣﺸﺎﻫﺪه ﻣﻲﻛﻨﻴﺪ ﻛﻪ ﻫﻤﺎن ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ ﺑﻌﺪ از preprocessﺷﺪن اﺳﺖ:
در BDS 2006ﻓﺎﻳﻞ preprocessﺷﺪه را ﺑﻪ راﺣﺘﻲ ﻣﻲﺷﻮد ﺑﺎ ) right – clickراﺳﺖ ﻛﻠﻴﻚ( ﻛﺮدن روي ﻓﺎﻳﻞ در ﻗﺴﻤﺖ Project Managerو اﻧﺘﺨﺎب Preprocessدﻳﺪ:
298
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
: اﮔﺮ ﺑﻨﻮﻳﺴﻴﻢ. )ﻣﺎﻛﺮو( ﻣﻌﺮوف اﺳﺖmacro ﻣﻲﺷﻮد ﭼﻴﺰي ﺷﺒﻴﻪ ﺑﻪ ﺗﺎﺑﻊ ﺗﻌﺮﻳﻒ ﻛﺮد ﻛﻪ ﺑﻪ#define ﺑﺎ #define f(x) cout<< x;
: ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ. ﻣﻌﺎدل اﺳﺖcout<< 3; ﺑﺎ ﻧﻮﺷﺘﻦf(3) آن وﻗﺖ ﻧﻮﺷﺘﻦ #include <conio.h> #include <iostream> using namespace std; #define f(x) cout<< x; int main() { f(3<<endl) f("hello") _getch(); }
Output: 3 hello
. ﺗﺒﺪﻳﻞ ﻣﻲﺷﻮدcout<< 3<<endl; ﺑﻪf(3<<endl) ﺷﺪنcompile در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻗﺒﻞ از :اﻳﻦ ﻣﺜﺎل ﺟﺎﻟﺐ اﺳﺖ #include <conio.h> #include <iostream> using namespace std;
299
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #define f(a) about )(int main { ;int f(s) = 4 ;cout<< about ;)(_getch }
Output: 4
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ aدر aboutﺑﻪ ﻋﻨﻮان ﻣﺘﻐﻴﺮ در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻧﺸﺪه .ﺑﺮاي ﻫﻤﻴﻦ اﺳﺖ ﻛﻪ ﻧﻨﻮﺷﺘﻢ: ;cout<< sbout
ﻣﺎﻛﺮوﻫﺎ ﻣﻲﺗﻮاﻧﻨﺪ ﭼﻨﺪ ﺗﺎ ﻣﺘﻐﻴﺮ ﻫﻢ داﺷﺘﻪ ﺑﺎﺷﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std #define mul1(a,b) a*b )#define mul2(a,b) (a)*(b )(int main { ;mul1(int,p) = new int ;cout<< mul1(2+3,2) << endl ;)cout<< mul2(2+3,2 ;)(_getch }
Output: 8 10
در واﻗﻊ آن ﭼﻪ در )( mainﻣﻲﺑﻴﻨﻴﺪ ﺑﻪ ﺻﻮرت زﻳﺮ در ﻣﻲآﻳﺪ و ﺑﻌﺪ compileﻣﻲﺷﻮد: ;int*p = new int ;cout<< 2+3*2 << endl ;)cout<< (2+3)*(2 ;)(_getch
ﭼﻮن اوﻟﻮﻳﺖ * ﺑﻴﺶ از +اﺳﺖ 2+3*2 ،ﺑﺮاﺑﺮ ﺑﺎ 2+6ﺑﺮاﺑﺮ ﺑﺎ 8اﺳﺖ (2+3)*(2) .ﺑﺮاﺑﺮ ﺑﺎ 5*2ﺑﺮاﺑﺮ ﺑﺎ 10اﺳﺖ. ﻣﺜﺎلﻫﺎي ﺑﺎﻻ ﻛﻤﻲ ﻏﻴﺮ ﻣﺘﻌﺎرﻓﻨﺪ .ﻣﻌﻤﻮﻻ از ) #defineﻛﻪ ﺑﻪ آن دﺳﺘﻮر »ﺗﻌﺮﻳﻒ« ﻣﻲﮔﻮﻳﻨﺪ( ﺑﻪ ﻳﻜﻲ از ﺷﻜﻞ- ﻫﺎي زﻳﺮ اﺳﺘﻔﺎده ﻣﻲﺷﻮد: #define M 2
300
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﻪ ﻣﺜﻞ ﺗﻌﺮﻳﻒ ﻳﻚ ﺛﺎﺑﺖ اﺳﺖ و )#define SQR(x) (x)*(x
ﻛﻪ ﺑﺮاي ﺗﻌﺮﻳﻒ ﺗﻮان 2اﺳﺖ و #define _CPP
ﻛﻪ ﺗﻘﺮﻳﺒﺎ ﻫﻴﭻ ﻛﺎري ﻧﻤﻲﻛﻨﺪ ﺑﻪ ﺟﺰ اﻳﻦ ﻛﻪ ﻣﻲﮔﻮﻳﺪ _CPPﺗﻌﺮﻳﻒ ﺷﺪه اﺳﺖ )اﻟﺒﺘﻪ _CPPﻫﺎي ﻣﻮﺟﻮد در ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ را ﻫﻢ ﭘﺎك ﻣﻲﻛﻨﺪ( و #define far
ﻛﻪ ﻛﻠﻤﻪي farرا )ﻛﻪ از C++ﺣﺬف ﺷﺪه( از ﺑﺮﻧﺎﻣﻪ ﺣﺬف ﻣﻲﻛﻨﺪ. ﻛﺎرﺑﺮد ﻣﻬﻢ دﻳﮕﺮ #defineﺗﻌﺮﻳﻒ ﻣﺎﻛﺮوﻫﺎﻳﻲ اﺳﺖ ﻛﻪ ﻗﺮار اﺳﺖ ﻛﺎر ﺗﺎﺑﻊ inlineرا اﻧﺠﺎم دﻫﻨﺪ .ﻣﺜﻼ >#include <conio.h >#include <iostream ;using namespace std )#define f(x,y \ { \ ;int a = x \ ;a++ \ \ ;cout<< a << endl ;cout<< x+y \ } )(int main { ;)f(2,3 ;)(_getch }
Output: 3 5
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﺑﻪ ﺟﺎي ) f(2,3ﻳﻚ blockﻗﺮار ﻣﻲﮔﻴﺮد ﭼﻮن ) f(x,yرا ﻳﻚ blockﮔﺮﻓﺘﻴﻢ .ﻣﻲ- ﺗﻮاﻧﺴﺘﻴﻢ ﺑﺎ ﺣﺬف { و } در #defineدﺳﺘﻮرات را ﺑﺪون blockﺗﻌﺮﻳﻒ ﻛﻨﻴﻢ .در BDS 2006رﻧﮓ آن ﭼﻪ ﭘﺲ از #defineﻗﺮار ﻣﻲﮔﻴﺮد ﺑﺎ ﺧﻮد #defineﻳﻜﻲ اﺳﺖ:
301
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در اﻳﻦ ﻛﺎرﺑﺮد f() ،ﺑﻪ ﻧﺎﭼﺎر inlineاﺳﺖ ﭼﻮن ﻗﺒﻞ از اﻳﻦ ﻛﻪ compilerﻛﺎري ﺑﻜﻨﺪ preprocessor ،آن را ﺟﺎﻳﮕﺰﻳﻦ ﻣﻲﻛﻨﺪ و اﺻﻼ ﻓﺮﺻﺖ ﺗﺼﻤﻴﻢ ﮔﻴﺮي ﺑﺮاي ﺧﻮد compilerﺑﺎﻗﻲ ﻧﻤﻲﻣﺎﻧﺪ) .از compilerﻛﺴﻲ ﺧﻴﺮي ﻧﺪﻳﺪه اﺳﺖ ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﺗﺎ ﺣﺪ اﻣﻜﺎن از آن درﺧﻮاﺳﺘﻲ ﻧﻤﻲﻛﻨﻴﻢ ﻣﺜﻼ درﺧﻮاﺳﺖ inlineﻛﺮدن ﺗﺎﺑﻊ ﻳﺎ
registerﻛﺮدن ﻣﺘﻐﻴﺮ
(
ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std "#define f(x) "x )(int main { ;)cout<< f(hello ;)(_getch }
Output: x
ﻣﻦ ﻣﻲﺧﻮاﺳﺘﻢ helloﭼﺎپ ﺷﻮد وﻟﻲ xﭼﺎپ ﺷﺪه .ﻋﻠﺘﺶ اﻳﻦ اﺳﺖ ﻛﻪ در x ،#defineدر " "xﺑﻪ ﻋﻨﻮان ﻣﺘﻐﻴﺮ در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻧﺸﺪه .ﺑﺮاي اﻳﻦ ﻛﻪ helloﭼﺎپ ﺑﺸﻮد ﺑﺎﻳﺪ #defineرا ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ ﺑﺪﻫﻢ: #define f(x) #x
ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﺑﻪ اﻳﻦ ﺻﻮرت در ﺑﻴﺎﻳﺪ: >#include <conio.h >#include <iostream ;using namespace std #define f(x) #x )(int main { ;)cout<< f(hello ;)(_getch }
Output: hello
302
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺣﺎﻻ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ) f(x,yﻳﻚ ﻣﺎﻛﺮو ﺑﺎﺷﺪ ﻛﻪ xو yرا ﭘﺸﺖ ﺳﺮ ﻫﻢ ﺑﻪ ﺻﻮرت رﺷﺘﻪ ﭼﺎپ ﻣﻲﻛﻨﺪ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: #define f(x,y) #x###y
در واﻗﻊ ﻋﻤﻠﮕﺮ ##ﻛﺎر ﭼﺴﺒﺎﻧﺪن رﺷﺘﻪﻫﺎ را ﺑﻪ ﻫﻢ اﻧﺠﺎم ﻣﻲدﻫﺪ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std #define f(x,y) #x##":/Windows/System32/"###y )(int main { ;)cout<< f(C,TweakUI.exe ;)(_getch }
Output: C:/Windows/System32/TweakUI.exe
اﻳﻦ ﻣﺜﺎل را ﻣﻲﺷﻮد ﺑﻪ ﺻﻮرت زﻳﺮ ﻫﻢ ﺗﻐﻴﻴﺮ داد: >#include <conio.h >#include <iostream ;using namespace std #define f(x,y) #x##":/Windows/System32/"##y )(int main { ;)"cout<< f(C,"TweakUI.exe ;)(_getch }
Output: C:/Windows/System32/TweakUI.exe
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ اﻳﻦ ﻛﺎرﺑﺮد از #defineﮔﺎﻫﻲ ﻣﻲﺗﻮاﻧﺪ ﻛﺎر ﺑﺎ رﺷﺘﻪﻫﺎ را ﺧﻴﻠﻲ راﺣﺖ ﻛﻨﺪ .اﻣﺎ ﻛﺎر آن ﻣﻨﺤﺼﺮ ﺑﻪ رﺷﺘﻪﻫﺎي ﺛﺎﺑﺖ literalاﺳﺖ. اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﺧﻄﻮﻃﻲ را ﺑﺮﺣﺴﺐ اﻳﻦ ﻛﻪ ﭼﻴﺰي #defineﺷﺪه ﻳﺎ ﻧﺸﺪه اﺿﺎﻓﻪ ﻳﺎ ﻛﻢ ﻛﻨﻴﻢ از دﺳﺘﻮرات preprocessor #ifdefو #ifndefاﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ .ﺑﺮاي اﻳﻦ ﻛﻪ ﭼﻴﺰي را ﻛﻪ #defineﺷﺪه ﭘﺎك ﻛﻨﻴﻢ )اﻧﮕﺎر #defineﻧﺸﺪه ﺑﺎﺷﺪ( از #undefاﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ .ﻣﺜﺎل زﻳﺮ ﻛﺎرﺑﺮد اﻳﻦﻫﺎ را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std
303
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #define POLITE int main() { //-----------------------------------------part 1 #ifdef POLITE cout<< "What is your name, please?(1)\n"; // printed #endif #ifndef POLITE cout<< "Name?(1)\n"; #endif //------------------------------------end of part 1 #undef POLITE // where the action is //-----------------------------------------part 2 #ifdef POLITE // not defined cout<< "What is your name, please?(2)\n"; // not printed #endif #ifndef POLITE cout<< "Name?(2)\n"; // printed #endif //------------------------------------end of part 2 _getch(); }
Output: What is your name, please?(1) Name?(2)
. ﺑﻪ وﺟﻮد آﻣﺪه#ifndef ﻳﺎ#ifdef ﺑﺮاي ﭘﺎﻳﺎن ﺑﺨﺸﻲ ﺑﻪ ﻛﺎر ﻣﻲرود ﻛﻪ ﺑﺎ#endif : ﻛﺮد#undef ﻣﺎﻛﺮوﻫﺎ را ﻫﻢ ﻣﻲﺷﻮد #include <conio.h> #include <iostream> using namespace std; int f(int) { return 0; } #define f(x) x*x int main() { cout<< f(2) << endl; #undef f cout<< f(2) << endl; getch(); }
304
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: 4 0
)f(2ي اول ﺑﻪ وﺳﻴﻠﻪي ﻣﺎﻛﺮوي fﺑﺎ 2*2ﺟﺎﻳﮕﺰﻳﻦ ﻣﻲﺷﻮد و ﺗﺎﺑﻊ )( ِ fﺗﻌﺮﻳﻒ ﺷﺪه ﻓﺮاﺧﻮاﻧﻲ ﻧﻤﻲﺷﻮد .ﺑﺎ #undefﻣﺎﻛﺮوي fﻏﻴﺮ ﻓﻌﺎل ﻣﻲﺷﻮد و )f(2ي دوم را رﻫﺎ ﻣﻲﻛﻨﺪ ﺗﺎ compilerﺑﻪ وﺿﻌﻴﺖ آن رﺳﻴﺪﮔﻲ ﻛﻨﺪ و compilerﻫﻢ ﺗﺎﺑﻊ )( fرا ﻓﺮا ﻣﻲﺧﻮاﻧﺪ
.
اﮔﺮ ﻳﻚ header fileﺑﺎ ﻧﺎم myheader.hدرﺳﺖ ﻛﻨﻴﺪ ﻣﻤﻜﻦ اﺳﺖ ﻻزم ﺑﺸﻮد ﭼﻨﺪ ﺑﺎر آن را #include ﻛﻨﻴﺪ .اﻣﺎ اﻏﻠﺐ اﻳﻦ ﻛﺎر اﻣﻜﺎن ﭘﺬﻳﺮ ﻧﻴﺴﺖ ﻣﺜﻼ اﮔﺮ myheader.hﺣﺎوي ﺧﻄﻮط زﻳﺮ ﺑﺎﺷﺪ: >#include <iostream ;using namespace std )(void f { ;"cout<< "a function in header file\n } ;int a
در اﻳﻦ ﺣﺎﻟﺖ ﻧﻤﻲﺷﻮد ﻧﻮﺷﺖ: >#include <myheader.h >#include <myheader.h
ﺑﺮاي اﻳﻦ ﻛﻪ اﻳﻦ ﻛﺎر اﻣﻜﺎن ﭘﺬﻳﺮ ﺑﺎﺷﺪ ﻣﻲﺗﻮاﻧﻴﻢ از #defineاﺳﺘﻔﺎده ﻛﻨﻴﻢ و myheader.hرا ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﻢ: #ifndef __MYHEADER #define __MYHEADER >#include <iostream ;using namespace std )(void f { ;"cout<< "a function in header file\n } ;int a #endif
در اﻳﻦ ﺻﻮرت ﺣﺘﻲ اﮔﺮ 10ﺑﺎر ﻫﻢ myheader.hرا #includeﻛﻨﻴﻢ اﺷﻜﺎﻟﻲ ﭘﻴﺶ ﻧﻤﻲآﻳﺪ و ﺑﺎ ﻳﻚ ﺑﺎر #includeﻛﺮدن ﻓﺮﻗﻲ ﻧﺪارد ﭼﻮن در دﻓﻌﻪﻫﺎي ﺑﻌﺪي __MYHEADERﺗﻌﺮﻳﻒ ﺷﺪه و #ifndefﻣﺎﻧﻊ دوﺑﺎره ﺧﻮاﻧﻲ header fileﻣﻲﺷﻮد. دﺳﺘﻮر preprocessorﻣﻬﻢ ﺑﻌﺪي دﺳﺘﻮر #ifاﺳﺖ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream
305
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; int main() { const int a = 2; #if a > 2 cout<< "if\n"; #else cout<< "else\n"; #endif getch(); }
Output: else
در اﻳﻦa ﺛﺎﺑﺖ ﺑﻮدنBDS 2006 در. را در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺑﻴﻨﻴﺪ#endif و#else ،#if ﻧﺤﻮهي ﻛﺎرﺑﺮد ﺑﻪ ﻣﺜﺎل زﻳﺮ ﻛﻪ. اﻟﺰاﻣﻲ ﻧﻴﺴﺖ وﻟﻲ اﻳﻦ ﺧﻴﻠﻲ ﻫﻢ ﺧﻮب ﻧﻴﺴﺖVisual C++ 2005 ﺑﺮﻧﺎﻣﻪ اﻟﺰاﻣﻲ اﺳﺖ اﻣﺎ در : اﺟﺮا ﻧﻤﻲﺷﻮد ﻧﮕﺎه ﻛﻨﻴﺪBDS 2006 در #include <conio.h> #include <iostream> using namespace std; int main() { int a = 2; a++; #if a > 2 cout<< "if\n"; #else cout<< "else\n"; #endif getch(); }
Output (Visual C++ 2005): else
:#else if اﺳﺖ ﺑﻪ ﻣﻌﻨﻲ#elif دﺳﺘﻮر دﻳﮕﺮ. ﻧﺎدﻳﺪه ﮔﺮﻓﺘﻪ ﺷﺪهa++ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ #include <conio.h> #include <iostream> using namespace std; #define M 5 int main() { #if M > 5 cout<< "if\n"; #else if M == 5 cout<< "else if\n"; #endif
306
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #if M > 5 ;"cout<< "if\n #elif M == 5 ;"cout<< "elif\n #endif ;)(_getch }
Output: else if elif
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺳﻌﻲ ﻛﺮدهام ﻣﻌﺎدل ﺑﻮدن #elifو #else ifرا ﻧﺸﺎن دﻫﻢ. دﺳﺘﻮر preprocessorدﻳﮕﺮ دﺳﺘﻮر #pragmaاﺳﺖ .اﻳﻦ دﺳﺘﻮر در compilerﻫﺎي ﮔﻮﻧﺎﮔﻮن ﺑﻪ ﺷﻜﻞﻫﺎي ﻣﺨﺘﻠﻔﻲ اﺳﺘﻔﺎده ﻣﻲﺷﻮد .دو #pragmaي زﻳﺮ را ﻗﺒﻼ در BDS 2006ﻣﻌﺮﻓﻲ ﻛﺮدهام: #pragma argsused #pragma option –b
ﻛﻪ –bاﺳﻢ ﻳﻜﻲ از ﺗﻨﻈﻴﻤﺎت compilerاﺳﺖ .ﺑﺮاي ﻳﺎﻓﺘﻦ اﺳﻢ ﺗﻨﻈﻴﻤﺎت ﺑﺎﻳﺪ از ﻃﺮﻳﻖ ﻣﻨﻮﻫﺎ آن ﺗﻨﻈﻴﻢ را ﭘﻴﺪا ﻛﻨﻴﺪ .اﺳﻢ ﻫﺮ ﺗﻨﻈﻴﻢ ﻛﻨﺎر آن ﻧﻮﺷﺘﻪ ﺷﺪه. #pragmaي زﻳﺮ در Visual C++ 2005ﻛﺎرﺑﺮد دارد: #pragma region
ﺗﻜﻪ ﺑﺮﻧﺎﻣﻪ زﻳﺮ را در ﺟﺎﻳﻲ از editorدر Visual C++ 2005ﺑﻨﻮﻳﺴﻴﺪ: #pragma region ;int a = 2 ;cout<< a #pragma endregion
ﺑﻪ اﻳﻦ ﻋﻜﺲ ﻧﮕﺎه ﻛﻨﻴﺪ:
ﻛﺎر #pragma regionﺑﻪ وﺟﻮد آوردن ﭼﻴﺰي اﺳﺖ ﻛﻪ دور آن ﺧﻂ ﻛﺸﻴﺪهام .ﺑﺎ clickروي آن ﺑﺨﺸﻲ از codeﺑﻪ ﻃﻮر ﻣﻮﻗﺖ ﻧﺎﭘﺪﻳﺪ ﻣﻲﺷﻮد.
307
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ precompiled headers
وﻗﺘﻲ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻣﻲﻧﻮﻳﺴﺪ ﺑﺎرﻫﺎ آن را compileﻣﻲﻛﻨﺪ ﺗﺎ ﺑﻪ ﺷﻜﻞ ﻣﻮرد ﻧﻈﺮ در آﻳﺪ .اﮔﺮ ﻗﺮار ﺑﺎﺷﺪ ﻫﺮ ﺑﺎر ﻛﻪ ﺑﺮﻧﺎﻣﻪ را compileﻣﻲﻛﻨﻴﻢ ﻫﻤﻪي header fileﻫﺎﻳﻲ ﻛﻪ اﺻﻼ ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﻨﺪ compile ﺷﻮﻧﺪ زﻣﺎن compileﺷﺪن ﺑﻲ ﺧﻮدي زﻳﺎد ﻣﻲﺷﻮد )ﺑﻪ ﺧﺼﻮص در .(Visual C++ 2005ﺑﺮاي ﺟﻠﻮﮔﻴﺮي از اﻳﻦ ﻫﺪر رﻓﺘﻦ زﻣﺎنcompiler ،ﻫﺎ ﺑﺨﺶﻫﺎي از source codeرا ﻛﻪ ﺗﻐﻴﻴﺮ ﻧﻤﻲﻛﻨﻨﺪ precompileﻣﻲﻛﻨﻨﺪ و ﺣﺎﺻﻞ را در fileي ذﺧﻴﺮه ﻣﻲﻛﻨﻨﺪ ﺗﺎ ﻧﻴﺎزي ﺑﻪ compileﺷﺪن ﻣﺠﺪد ﻧﺒﺎﺷﺪ .اﮔﺮ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ ﺑﻪ compiler اﺟﺎزهي precompileﻛﺮدن ﺑﺪﻫﺪ ﺑﺮﻧﺎﻣﻪي او ﺳﺮﻳﻊ ﺗﺮ compileﻣﻲﺷﻮد .ﺑﺎ اﺳﺘﻔﺎده از ﺗﻨﻈﻴﻤﺎت compiler ﻣﻲﺗﻮاﻧﻴﺪ precompilationرا ﻛﻨﺘﺮل ﻛﻨﻴﺪ. ﺑﺮاي ﻣﺘﻮﻗﻒ ﺷﺪن precompilationاز #pragma hdrstop
اﺳﺘﻔﺎده ﻣﻲﺷﻮد .در BDS 2006ﻗﺮار دادن اﻳﻦ #pragmaدر header fileﻫﺎ ﻫﻴﭻ اﺛﺮي ﻧﺪارد .ﺑﺮاي ﭘﻴﺪا ﻛﺮدن ﺟﺰﺋﻴﺎت ﺑﻴﺶ ﺗﺮ درﺑﺎرهي precompilationﺑﺎﻳﺪ ﺑﻪ helpﻣﻮﺟﻮد در compilerﺧﻮدﺗﺎن ﻣﺮاﺟﻌﻪ ﻛﻨﻴﺪ ،BDS 2006 .اﻃﻼﻋﺎت helpدر compilerﻫﺎي Microsoftرا ﻫﻢ ﻧﻤﺎﻳﺶ ﻣﻲدﻫﺪ .اﻳﻦ ﻧﺒﺎﻳﺪ ﺷﻤﺎ را ﮔﻤﺮاه ﻛﻨﺪ.
وه از * ; <' R
ﻣﺎﻛﺮوﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ ﻛﻪ ﺑﺪون اﻳﻦ ﻛﻪ در header fileﺧﺎﺻﻲ ﺗﻌﺮﻳﻒ ﺷﺪه ﺑﺎﺷﻨﺪ ﻳﺎ ﻣﺎ آنﻫﺎ را ﺗﻌﺮﻳﻒ ﻛﺮده ﺑﺎﺷﻴﻢ ﻗﺎﺑﻞ اﺳﺘﻔﺎدهاﻧﺪ .اﻳﻦ ﻣﺎﻛﺮوﻫﺎ را از ﭘﻴﺶ ﺗﻌﺮﻳﻒ ﺷﺪه ) (predefinedﻣﻲﮔﻮﻳﻨﺪ. (1ﻣﺎﻛﺮوي __:__DATE اﻳﻦ ﻣﺎﻛﺮو ﺗﺎرﻳﺦ را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;cout<< __DATE__ << endl ;)(cout<< typeid(__DATE__).name ;)(_getch
308
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output (BDS 2006): Feb 22 2009 char[12] Output (Visual C++ 2005): Feb 22 2009 char const [12]
:__FILE__ ( ﻣﺎﻛﺮوي2 :اﻳﻦ ﻣﺎﻛﺮو اﺳﻢ و آدرس ﻓﺎﻳﻠﻲ را ﻛﻪ در آن ﻗﺮار دارد ﺑﺮ ﻣﻲﮔﺮداﻧﺪ #include <conio.h> #include <iostream> using namespace std; int main() { cout<< __FILE__ << endl; _getch(); }
Output (BDS 2006): G:\Important Files\My Documents\Borland Studio Projects\Console\Console.cpp Output (Visual C++ 2005): c:\documents and settings\administrator\my documents\visual studio 2005\projects \console\console\console.cpp :__LINE__ ( ﻣﺎﻛﺮوي3
:ﺷﻤﺎرهي ﺧﻄﻲ را ﻛﻪ آن ﻗﺮار ﮔﺮﻓﺘﻪ ﺑﺮﻣﻲﮔﺮداﻧﺪ #include <conio.h> /* 1 */ #include <iostream> /* 2 */ using namespace std; /* 3 */ // /* 4 */ #define M cout<< __LINE__ << endl; /* 5 */ // /* 6 */ void f() /* 7 */ { /* 8 */ cout<< __LINE__ << endl; /* 9 */ } /* 10 */ int main() /* 11 */ { /* 12 */ cout<< __LINE__ << endl; /* 13 */ cout<< __LINE__ << endl; /* 14 */ M /* 15 */ f(); /* 16 */ cout<< typeid(__LINE__).name(); /* 17 */
309
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)(_getch
/* 18 */ /* 19 */
}
Output (BDS 2006): 13 14 15 9 int Output (Visual C++ 2005): 13 14 15 9 long
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ 15 ،Mرا ﭼﺎپ ﻛﺮده ﻧﻪ .5 (4ﻣﺎﻛﺮوي __:__TIME اﻳﻦ ﻣﺎﻛﺮو زﻣﺎن )ﺳﺎﻋﺖ( آﺧﺮﻳﻦ compilationرا ﺑﺮﻣﻲﮔﺮداﻧﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;cout<< __TIME__ << endl ;)(_getch }
Output: 22:34:49
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ __ __TIMEزﻣﺎن آﺧﺮﻳﻦ ﺑﺎر compileﻛﺮدن ﺑﺮﻧﺎﻣﻪ را ﻧﺸﺎن ﻣﻲدﻫﺪ ﻧﻪ زﻣﺎن ﺣﺎل را. (5ﻣﺎﻛﺮوي __:__TIMESTAMP زﻣﺎن آﺧﺮﻳﻦ ﺗﻐﻴﻴﺮ ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ در ﻓﺎﻳﻠﻲ ﻛﻪ ﻣﺎﻛﺮو در آن ﻗﺮار دارد را ﺑﺮ ﻣﻲﮔﺮداﻧﺪ .اي ﻣﺎﻛﺮو در BDS 2006و Dec_C++وﺟﻮد ﻧﺪارد: >#include <conio.h >#include <iostream ;using namespace std
310
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(int main { ;cout<< __TIMESTAMP__ << endl ;)(_getch }
Output (Visual C++ 2005): Sun Feb 22 22:57:07 2009
در Visual C++ 2005ﻣﺎﻛﺮوﻫﺎي از ﭘﻴﺶ ﺗﻌﺮﻳﻒ ﺷﺪهي دﻳﮕﺮي ﻫﻢ ﻫﺴﺘﻨﺪ ﻛﻪ ﻣﻲﺗﻮاﻧﻴﺪ آنﻫﺎ را در help ﭘﻴﺪا ﻛﻨﻴﺪ.
" در @? 6
در Visual C++ 2005در ﻗﺴﻤﺖ Solution Explorerروي ﻗﺴﻤﺖ ﻧﺸﺎن داده ﺷﺪه در ﺷﻜﻞ زﻳﺮ right-clickﻛﻨﻴﺪ )ﻛﻠﻴﻚ راﺳﺖ )ﻳﺎ ﻛﻠﻴﻚ ﭼﭗ اﮔﺮ ﭼﭗ دﺳﺖ ﻫﺴﺘﻴﺪ
و روي … New Itemﻛﻪ در ﺷﻜﻞ زﻳﺮ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻠﻴﻚ ﻛﻨﻴﺪ:
ﭘﻨﺠﺮه زﻳﺮ ﺑﺎز ﻣﻲﺷﻮد .ﺑﻪ ﻗﺴﻤﺖ Codeﺑﺮوﻳﺪ:
311
www.pupuol.com
((:
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
و ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﭘﺴﻮﻧﺪ .hو ﻓﺎﻳﻞ دﻳﮕﺮي ﺑﺎ ﭘﺴﻮﻧﺪ .cppﺑﻪ ﺑﺮﻧﺎﻣﻪي ﺧﻮدﺗﺎن اﺿﺎﻓﻪ ﻛﻨﻴﺪ .ﺑﺎﻳﺪ ﻓﺎﻳﻞﻫﺎي ﺷﻤﺎ ﺑﻪ اﻳﻦ ﺻﻮرت ﺑﺎﺷﻨﺪ:
ﺷﻜﻞ زﻳﺮ ﻧﺤﻮهي اﻧﺠﺎم ﻫﻤﻴﻦ ﻛﺎر را در BDS 2006ﻧﺸﺎن ﻣﻲدﻫﺪ:
312
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
BDS 2006ﻓﺎﻳﻞ .cppو .hرا ﻫﻤﺰﻣﺎن وارد ﻣﻲﻛﻨﺪ و ﻓﺎﻳﻞﻫﺎي ﺷﻤﺎ را ﺑﻪ اﻳﻦ ﺻﻮرت ﻧﺸﺎن ﻣﻲدﻫﺪ:
ﺑﻪ ﻓﺎﻳﻞ .bpfﻛﻪ ﺧﻮد ﺑﻪ ﺧﻮد ﻫﻨﮕﺎم اﻳﺠﺎد ﺑﺮﻧﺎﻣﻪ ﺑﻪ وﺟﻮد آﻣﺪه ﻛﺎري ﻧﺪارﻳﻢ اﻳﻦ ﻓﺎﻳﻞ ﺗﻨﻬﺎ ﺑﻪ وﺳﻴﻠﻪي Project ) Managerﻳﻌﻨﻲ ﻗﺴﻤﺘﻲ ﻛﻪ در ﺷﻜﻞ ﻗﺒﻞ ﻧﺸﺎن دادهام( اﺳﺘﻔﺎده ﻣﻲﺷﻮد .ﻓﺎﻳﻞ .rcﻧﺸﺎن داده ﺷﺪه در اﻳﻦ ﺟﺎ ﻓﻘﻂ وﻇﻴﻔﻪي دادن iconﻣﻨﺎﺳﺐ ﺑﺮاي ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ را دارد و ﺣﺘﻲ اﮔﺮ آن را ﺣﺬف ﻛﻨﻴﺪ ﻣﺸﻜﻠﻲ ﭘﻴﺶ ﻧﻤﻲآﻳﺪ ﻣﻤﻜﻦ اﺳﺖ ﺑﻪ ﺟﺎي آن ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﭘﺴﻮﻧﺪ .resﻗﺮار داﺷﺘﻪ ﺑﺎﺷﺪ. وﻗﺘﻲ در Unit1.cpp ،BDS 2006و Unit1.hاﺿﺎﻓﻪ ﻣﻲﺷﻮﻧﺪ ﭼﻴﺰﻫﺎﻳﻲ از ﻗﺒﻞ در آنﻫﺎ ﻧﻮﺷﺘﻪ ﺷﺪه .ﻓﻌﻼ آنﻫﺎ را ﭘﺎك ﻛﻨﻴﺪ و ﻓﺎﻳﻞﻫﺎ را ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﺪ و ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻛﻨﻴﺪ: Console.cpp: >#include <conio.h "#include "Unit1.h )(int main
313
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { f(); Cat Tom; Tom.Meow(); _getch(); }
Unit1.cpp: #pragma hdrstop #include "Unit1.h" void f() { cout<<"f()\n"; } void Cat::Meow() { cout<< "Meow\n"; }
Unit1.h: #ifndef Unit1H #define Unit1H #include<iostream> using namespace std; void f(); class Cat { public: void Meow(); }; #endif
Output: f() Meow
)و ﺧﻮد آن را( ﭘﺎك ﻛﻨﻴﺪ و ازproject داد ﻫﻤﻪي ﻓﺎﻳﻞﻫﺎيerror ،Visual C++ 2005 درlinker اﮔﺮ روي ﻫﺎرد ﺑﺮوﻳﺪ ﻛﻪ ﻣﻌﻤﻮﻻproject و ﻓﺎﻳﻞﻫﺎ را ﺑﺴﺎزﻳﺪ )ﺑﺮاي اﻳﻦ ﻛﺎر ﺑﺎﻳﺪ ﺑﻪ ﻣﺤﻞ ذﺧﻴﺮه ﺷﺪنproject ﻧﻮ در وﺿﻌﻴﺘﻲcompiler ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﻧﺒﺎﻳﺪ.( ﻗﺮار داردMy Document / Visual Studio 2005 در ﻣﻌﻤﻮﻻ ﺗﻨﻈﻴﻤﺎت را ﻃﻮري ﺗﻐﻴﻴﺮproject از ﻧﻮ ﺳﺎﺧﺘﻦ. ﺷﺪه ﺗﻮﻟﻴﺪ ﻛﻨﺪpreprocess ﻗﺮار داﺷﺘﻪ ﺑﺎﺷﺪ ﻛﻪ ﻓﺎﻳﻞ ِ ﺧﻮد ﺷﻤﺎ ﻣﻲﺗﻮاﻧﻴﺪ ﺑﺎ ﭘﻴﻤﺎﻳﺶ ﻣﺴﻴﺮ. ﺷﺪه ﺗﻮﻟﻴﺪ ﻧﺸﻮدpreprocess ﻣﻲدﻫﺪ ﻛﻪ ﻓﺎﻳﻞ Project | console Properties… | Configuration Properties | C/C++ | Generate Preprocessed File | No
314
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ compilerرا ﻃﻮري ﺗﻨﻈﻴﻢ ﻛﻨﻴﺪ ﻛﻪ ﻓﺎﻳﻞ preprocessﺷﺪه اﻳﺠﺎد ﻧﻜﻨﺪ. در ﻓﺎﻳﻞ Unit1.hﺳﻪ ﺧﻂ زﻳﺮ وﺟﻮد دارد ﻛﻪ ﺑﺎﻋﺚ ﻣﻲﺷﻮد ﻣﺤﺘﻮﻳﺎت ﻓﺎﻳﻞ ﺑﻴﺶ از ﻳﻚ ﺑﺎر وارد source codeﻧﺸﻮد: #ifndef Unit1H #define Unit1H #endif
در Visual C++ 2005ﺑﻪ ﺟﺎي آنﻫﺎ ﻣﻲﺷﻮد در اول header fileﻧﻮﺷﺖ: #pragma once
ﻧﻜﺘﻪي ﻣﻬﻢ اﻳﻦ اﺳﺖ ﻛﻪ ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ )( fو ِ methodﻣﻮﺟﻮد در ﻛﻼس Catدر Unit1.cppاﺳﺖ و Unit1.cppاﺻﻼ در #include ،console.cppﻧﺸﺪه و ﺑﻪ ﺟﺎي آن ﻓﻘﻂ #include ،Unit1.hﺷﺪه. ﺑﻪ اﻳﻦ ﻧﻜﺘﻪ ﻫﻢ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﮔﺮﭼﻪ #ifndef Unit1H #define Unit1H #endif
ﻓﻘﻂ ﻳﻚ ﺑﺎر اﺟﺎزه وارد ﺷﺪن ﻣﺤﺘﻮﻳﺎت Unit1.hرا ﻣﻲدﻫﺪ Unit1.h ،در ﻫﺮ ﻛﺪام از دو ﻓﺎﻳﻞ .cpp #includeﺷﺪه.
extern
ﺑﺎ اﺳﺘﻔﺎده از ﻛﻠﻤﻪي ﻛﻠﻴﺪي externﻣﻲﺗﻮاﻧﻴﺪ از ﺗﺎﺑﻌﻲ ﻛﻪ در ﻓﺎﻳﻞ دﻳﮕﺮي ﺗﻌﺮﻳﻒ ﺷﺪه اﺳﺘﻔﺎده ﻛﻨﻴﺪ ﺑﺪون اﻳﻦ ﻛﻪ آن ﻓﺎﻳﻞ #includeﺑﺸﻮد .ﺑﻪ ﻋﺒﺎرت ﺑﻬﺘﺮ ﺑﺎ externﺗﺎﺑﻊ ﻳﺎ ﻣﺘﻐﻴﺮي را ﻣﻌﺮﻓﻲ ﻣﻲﻛﻨﻴﻢ و ﺗﻌﺮﻳﻒ آن را در ﻓﺎﻳﻞ دﻳﮕﺮي اﻧﺠﺎم ﻣﻲدﻫﻴﻢ .ﻓﺎﻳﻞﻫﺎي projectرا ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﺑﺪﻫﻴﺪ و ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻛﻨﻴﺪ: Console.cpp: >#include <conio.h >#include <iostream ;using namespace std ;)(extern void f ;extern int a )(int main { ;)(f ;cout<< a
315
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)(_getch }
Unit1.cpp: >#include <iostream ;using namespace std )(void f { ;"cout<<"f() in Unit1.cpp\n } ;int a = 123456
Unit1.h: #ifndef Unit1H #define Unit1H #endif
Output: f() in Unit1.cpp 123456
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ aدر console.cppﺑﻪ ﺻﻮرت externﻣﻌﺮﻓﻲ ﺷﺪه و در Unit1.cppدوﺑﺎره ﻣﻌﺮﻓﻲ ﺷﺪه و ﺑﻪ آن ﻣﻘﺪار اوﻟﻴﻪ داده ﺷﺪه .وﻗﺘﻲ در console.cppﻣﻘﺪار آن را ﭼﺎپ ﻣﻲﻛﻨﻴﻢ ﻣﻘﺪار اوﻟﻴﻪي داده ﺷﺪه در Unit1.cppﭼﺎپ ﻣﻲﺷﻮد .اﮔﺮ ﻛﻠﻤﻪي externرا ﭘﺎك ﻛﻨﻴﺪ ﻳﺎ ﺑﻪ aدر console.cppﻣﻘﺪار اوﻟﻴﻪ ﺑﺪﻫﻴﺪ در BDS 2006ﻳﻚ warningو در Visual C++ 2005ﻳﻚ errorدرﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﺪ ﻛﻪ ﻣﻲﮔﻮﻳﺪ a
دوﺑﺎر ﺗﻌﺮﻳﻒ ﺷﺪه f() .ﻫﻢ ﺑﻪ ﻫﻤﻴﻦ ﺻﻮرت ﺗﻌﺮﻳﻒ ﺷﺪه. اﮔﺮ ﺑﻪ aدر console.cppﻣﻘﺪار اوﻟﻴﻪ ﺑﺪﻫﻴﺪ در BDS 2006ﻳﻚ warningدرﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﺪ وﻟﻲ ﺑﺮﻧﺎﻣﻪ اﺟﺮا ﻣﻲﺷﻮد و ﻣﻘﺪار اوﻟﻴﻪي داده ﺷﺪه در console.cppﭼﺎپ ﻣﻲﺷﻮد. اﮔﺮ;a = 123456
و ﻣﻲﺷﻮد ﻓﻘﻂ ﻧﻮﺷﺖ اﮔﺮ
intﺣﺬف ﺷﻮد ﻳﻚ linker errorدرﻳﺎﻓﺖ ﻣﻲﺷﻮد .اﻟﺒﺘﻪ ﻧﻴﺎزي ﺑﻪ دادن ﻣﻘﺪار اوﻟﻴﻪ ﻧﻴﺴﺖ ;a
.int
;int a = 123456
ﺑﻪ ﺟﺎي Unit1.cppدر Unit1.hﻧﻮﺷﺘﻪ ﺷﻮد ،ﺑﺎز ﻫﻢ linker errorدارﻳﻢ .در
واﻗﻊ ﺑﺎ ﻧﻮﺷﺘﻦ extern compilerدر ﻓﺎﻳﻞﻫﺎي ﺑﺎ ﭘﺴﻮﻧﺪ .cppﺑﻪ دﻧﺒﺎل ﺗﻌﺮﻳﻒ ﻣﻲﮔﺮدد ﻧﻪ ..hﺑﻪ ﺟﺎي ;extern int a
ﻣﻲﺷﻮد ﻧﻮﺷﺖ: ;extern "C++" int a
316
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در " "C++ﺣﺮف
C
Xرگ ر ر 0P3 . 8Eا Z1 6 C4در ) .c /!GY L1 E Q1ﻛﻪ
ﻣﺮﺑﻮط ﺑﻪ زﺑﺎن Cاﺳﺖ( ﻗﺮار داﺷﺘﻪ ﺑﺎﺷﺪ ،در اﻳﻦ ﺻﻮرت ﺑﻬﺘﺮ اﺳﺖ )وﻟﻲ ﻓﻌﻼ اﻟﺰاﻣﻲ ﻧﻴﺴﺖ( ﻛﻪ ﺑﻨﻮﻳﺴﻴﻢ: ;extern "C" int a
اﻟﺒﺘﻪ )اﮔﺮ ﻓﺎﻳﻠﻲ ﻗﺒﻼ ﺑﺎ compile ،C ِ compilerﻧﺸﺪه ﺑﺎﺷﺪ "C++" (،و " "Cﺑﻴﺶ ﺗﺮ ﺑﺮاي ﺗﻮﺿﻴﺢ ﻫﺴﺘﻨﺪ و ﻋﻤﻼ ﻧﻘﺶ ﺧﺎﺻﻲ ﻧﺪارﻧﺪ .در ﺑﺨﺶ ﺑﻌﺪ ﺑﻪ ﻃﻮر ﻣﻔﺼﻞ درﺑﺎرهي externو ﻛﺎﺑﺮدﻫﺎي آن ﺗﻮﺿﻴﺢ ﻣﻲدﻫﻢ.
linkage
ﺑﻌﺪ از آﺷﻨﺎﻳﻲ ﺑﺎ ﻧﻮﺷﺘﻦ ﺑﺮﻧﺎﻣﻪ در ﭼﻨﺪ ﻓﺎﻳﻞ ،ﺧﻮب اﺳﺖ ﻛﻤﻲ درﺑﺎرهي وﻳﮋﮔﻲﻫﺎي ﺑﺮﻧﺎﻣﻪﻫﺎي C++و ﻣﺮاﺣﻞ ﺳﺎﺧﺘﻦ ﻳﻚ ﻓﺎﻳﻞ اﺟﺮاﻳﻲ ﺣﺮف ﺑﺰﻧﻢ. ﻳﻚ ﺑﺮﻧﺎﻣﻪي C++از ﭼﻨﺪ ﺗﺎ ﻓﺎﻳﻞ ﺗﺸﻜﻴﻞ ﺷﺪه ﻛﻪ ﺑﻪ ﻫﺮ ﻛﺪام ﻳﻚ source fileﻣﻲﮔﻮﻳﻴﻢ .ﻫﺮ source fileرا ﻫﻤﺮاه ﺑﺎ ﻫﻤﻪي ﻓﺎﻳﻞﻫﺎﻳﻲ ﻛﻪ در آن #includeﻣﻲﺷﻮد ﻳﻚ translation unitﻳﺎ ﻓﻘﻂ unit ﻣﻲﮔﻮﻳﻴﻢ .ﺑﻌﺪ از compileﻛﺮدن ﺑﺮﻧﺎﻣﻪ ،از ﻫﺮ unitﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﭘﺴﻮﻧﺪ .objﺳﺎﺧﺘﻪ ﻣﻲﺷﻮد .ﺑﺮاي اﻳﻦ ﻛﻪ از ﺻﺤﺖ اﻳﻦ ﺣﺮف ﻣﻄﻤﺌﻦ ﺑﺸﻮﻳﺪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻛﻨﻴﺪ: Console.cpp: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;)(_getch }
Unit1.cpp: ;int s
Unit1.h: #ifndef Unit1H #define Unit1H #endif
Output: empty
317
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﻌﺪ از اﺟﺮاي ﺑﺮﻧﺎﻣﻪ در folderﻣﺨﺼﻮص projectﺧﻮد ﺑﮕﺮدﻳﺪ ﺗﺎ ﻓﺎﻳﻞﻫﺎي ﻧﺸﺎن داده ﺷﺪه در ﺗﺼﻮﻳﺮ زﻳﺮ را ﭘﻴﺪا ﻛﻨﻴﺪ:
اﻳﻦ دو ﺗﺎ ﻓﺎﻳﻞ ﻗﺒﻞ از ﺑﻪ وﺟﻮد آﻣﺪن ﻓﺎﻳﻞ .exeي ﺑﺮﻧﺎﻣﻪ اﻳﺠﺎد ﻣﻲﺷﻮﻧﺪ و linkerﺑﺎ اﺳﺘﻔﺎده از آنﻫﺎ )و ﻓﺎﻳﻞ- ﻫﺎي دﻳﮕﺮ( )و ارﺗﺒﺎط دادن آنﻫﺎ( ﻓﺎﻳﻞ .exeرا ﻣﻲﺳﺎزد. ارﺗﺒﺎط دادن ﻓﺎﻳﻞﻫﺎ ﺗﻮﺳﻂ linkerﺑﺮاي ﺳﺎﺧﺘﻦ ﻓﺎﻳﻞ .exeي ﻧﻬﺎﻳﻲ linkageﮔﻔﺘﻪ ﻣﻲﺷﻮد .در واﻗﻊ اﮔﺮ ﻳﻚ ﻣﺘﻐﻴﺮ ﻳﺎ ﺗﺎﺑﻊ )ﻳﺎ ﻫﺮ identifierدﻳﮕﺮ( دو ﻳﺎ ﭼﻨﺪ ﺑﺎر در ﻳﻚ unitﻳﺎ ﭼﻨﺪ unitﺗﻌﺮﻳﻒ ﺑﺸﻮﻧﺪ ﻣﻮﻗﻊ ﻓﺮاﺧﻮاﻧﻲ اﻳﻦ ﻣﺴﺎﻟﻪ ﻫﺴﺖ ﻛﻪ ﺑﺎﻳﺪ ﺑﻪ ﻛﺪام ﺗﻌﺮﻳﻒ ﻣﺮاﺟﻌﻪ ﺑﺸﻮد .ﻣﺜﻼ اﮔﺮ ﻳﻚ intﺑﺎ ﻧﺎم aدر ﻓﻀﺎي ﻋﻤﻮﻣﻲ دو ﺗﺎ unitﻣﺨﺘﻠﻒ ﺗﻌﺮﻳﻒ ﺑﺸﻮﻧﺪ و در ﺗﺎﺑﻌﻲ ﻧﻮﺷﺘﻪ ﺷﻮد a؛ ﻣﻨﻈﻮر ﻛﺪام aاﺳﺖ و ﺑﺎﻳﺪ ﺑﻪ ﻛﺪام aﻣﺮاﺟﻌﻪ ﻛﺮد؟ linkageدر واﻗﻊ ﻓﺮاﻳﻨﺪ ﻣﺮاﺟﻌﻪ ﺑﻪ ﺗﻌﺮﻳﻒ ﻣﻨﺎﺳﺐ اﺳﺖ. ﻫﺮ ) identifierﻣﺜﻼ ﻫﺮ ﺗﺎﺑﻊ ﻳﺎ ﻫﺮ ﻣﺘﻐﻴﺮ( ﻣﻤﻜﻦ اﺳﺖ ﻳﻜﻲ از دو روش linkageزﻳﺮ را داﺷﺘﻪ ﺑﺎﺷﺪ: linkage (1دا> 8
در اﻳﻦ روش identifierدر ﺳﺮ ﺗﺎ ﺳﺮ ﻳﻚ unitﺗﻨﻬﺎ ﻧﻤﺎﻳﻨﺪهي ﻳﻚ ﭼﻴﺰ اﺳﺖ. > linkage (2ر8E
در ا 01روش X.[ Q1 ,% 1 3/ identifierدر ه unitﻫﺎي ا.C4 ا ا روش linkageرا ﻣﺸﺨﺺ ﻛﻨﻴﻢ از ﻛﻠﻤﺎت ﻛﻠﻴﺪي staticﺑﺮاي linkageداﺧﻠﻲ و externﺑﺮاي linkageﺧﺎرﺟﻲ اﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: Console.cpp: >#include <conio.h "#include "unit1.h ;)(static void f
318
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(int main { ;)(f ;)(_getch } )(void f { ;"cout<< "f() in console.cpp\n }
Unit1.cpp: "#include "unit1.h )(void f { ;"cout<< "f() in unit1.cpp\n }
Unit1.h: #ifndef Unit1H #define Unit1H >#include <iostream ;using namespace std #endif
Output: f() in console.cpp
ﺗﺎﺑﻊ )( fدر console.cppﺑﻪ ﺻﻮرت staticﻣﻌﺮﻓﻲ ﺷﺪه و ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ روش linkageآن داﺧﻠﻲ اﺳﺖ و linkerﺑﺮاي ﺗﻌﺮﻳﻒ ﻛﺎﻣﻞ آن ،ﺗﻨﻬﺎ ﻓﺎﻳﻞ ) console.cppﻳﺎ اﮔﺮ ﺑﺨﻮاﻫﻢ دﻗﻴﻖ ﺗﺮ ﺑﮕﻮﻳﻢ ﻓﺎﻳﻞ (console.objرا ﻣﻲﮔﺮدد .اﮔﺮ ﻛﻠﻤﻪي ﻛﻠﻴﺪي staticﺣﺬف ﺷﻮد ﻳﺎ ﺑﻪ ﺟﺎي آن externﻗﺮار ﺑﮕﻴﺮد compilerﻫﺎي Microsoftﻳﻚ errorاز ﻃﺮف linkerﻣﻲﮔﻴﺮﻧﺪ .ﭘﻴﺎم اﻳﻦ errorاﻳﻦ اﺳﺖ ﻛﻪ )( fدر دو ﺟﺎ ﺗﻌﺮﻳﻒ ﺷﺪه .در واﻗﻊ وﻗﺘﻲ ﻛﻠﻤﻪي staticﺣﺬف ﺑﺸﻮد linkerﻛﻞ ﻓﺎﻳﻞﻫﺎ را ﺑﺮاي ﭘﻴﺪا ﻛﺮدن ﺗﻌﺮﻳﻒ ،ﻣﻲﮔﺮدد و دو ﺗﻌﺮﻳﻒ ﭘﻴﺪا ﻣﻲﻛﻨﺪ و ﭘﻴﺎم ﺧﻄﺎ ﺻﺎدر ﻣﻲﻛﻨﺪ .اﻟﺒﺘﻪ BDS 2006ﻫﻴﭻ errorي ﻧﻤﻲﮔﻴﺮد ﺷﺎﻳﺪ ﺑﻪ اﻳﻦ ﻋﻠﺖ ﻛﻪ linkerﻫﺎي Borlandﺑﻪ ﻣﺤﺾ ﭘﻴﺪا ﻛﺮدن اوﻟﻴﻦ ﺗﻌﺮﻳﻒ ،ﮔﺸﺘﻦ را ﻣﺘﻮﻗﻒ ﻣﻲﻛﻨﻨﺪ. ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ اﮔﺮ در ﻳﻚ unitﺑﻨﻮﻳﺴﻴﻢ: ;)(extern void f
ﻣﻲﺷﻮد ﺗﻌﺮﻳﻒ ِ )( fرا در ﻫﻤﺎن unitﻳﺎ در unitﻫﺎي دﻳﮕﺮ اﻧﺠﺎم داد و اﮔﺮ در ﻳﻚ unitﺑﻨﻮﻳﺴﻴﻢ: ;)(static void f
319
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺗﻌﺮﻳﻒ ِ)( fرا ﺑﺎﻳﺪ ﻓﻘﻂ در ﻫﻤﺎن unitﻧﻮﺷﺖ. ﻧﻮﺷﺘﻦ ﻛﻠﻤﻪي ﻛﻠﻴﺪي externﺑﺮاي ﺗﺎﺑﻊﻫﺎ اﻟﺰاﻣﻲ ﻧﻴﺴﺖ .ﺗﺎﺑﻊﻫﺎﻳﻲ ﻛﻪ ﺟﻠﻮي آنﻫﺎ ﻛﻠﻤﻪي ﻛﻠﻴﺪي static
ﻧﻴﺴﺖ ﺑﻪ ﻃﻮر linkage ،defaultﺧﺎرﺟﻲ دارﻧﺪ ﻳﻌﻨﻲ ;)(extern void f
ﺑﺎ ;)(void f
ﻓﺮﻗﻲ ﻧﺪارد. ﺣﺎﻻ ﻓﺎﻳﻞﻫﺎﻳﻲ را ﻣﻄﺎﺑﻖ ﺷﻜﻞﻫﺎي زﻳﺮ ﺑﻪ projectﺧﻮدﺗﺎن اﺿﺎﻓﻪ ﻛﻨﻴﺪ. ﺑﺮاي :Visual C++ 2005
ﻛﻪ ﺗﻨﻬﺎ ﺑﺮاي دﺳﺘﻪ ﺑﻨﺪي دو ﻓﻮﻟﺪر ﺑﺎ ﻧﺎمﻫﺎي unit1و unit2ﺑﺮاي ﻓﺎﻳﻞﻫﺎ ﺳﺎﺧﺘﻪام )ﺑﻪ اﻳﻦ ﻓﻮﻟﺪرﻫﺎ filterﻫﻢ ﻣﻲﮔﻮﻳﻨﺪ(. ﺑﺮاي :BDS 2006
320
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
:ﺑﺮﻧﺎﻣﻪي زﻳﺮ را در ﻓﺎﻳﻞﻫﺎي اﻳﺠﺎد ﺷﺪه ﺑﻨﻮﻳﺴﻴﺪ و اﺟﺮا ﻛﻨﻴﺪ console.cpp: #include <conio.h> #include "unit1.h" extern A a; void f(); int main() { f(); _getch(); }
Unit1.h: #ifndef Unit1H #define Unit1H #include <iostream> using namespace std; class A { public: int a; A(int a) { cout<< "A initialized in Unit1\n"; A::a = a; } }; #endif
Unit1.cpp: #include "unit1.h"
321
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ A a = 123456;
Unit2.h: #ifndef Unit2H #define Unit2H #include <iostream> using namespace std; void f(); class A { public: int a; A(int a) { cout<< "A initialized in Unit2\n"; A::a = a; } }; #endif
Unit2.cpp: #include "Unit2.h" extern A a; void f() { cout<< a.a << endl; cout<<"f() in unit2.cpp\n"; }
Output: A initialized in Unit1 123456 f() in unit2.cpp
. اﻧﺠﺎم ﺷﺪهUnit1.cpp ﻣﻌﺮﻓﻲ ﺷﺪه و ﺗﻌﺮﻳﻒ آن درa ﻣﺘﻐﻴﺮ ﻋﻤﻮﻣﻲUnit2.cpp و درconsole.cpp در ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ ﻧﻮﺷﺘﻦ. ﻳﻚ ﭘﺎراﻣﺘﺮي داردconstructor ﺗﻨﻬﺎ ﻳﻚA ﻛﻼس A a;
اﻣﺎ ﻧﻮﺷﺘﻦ.ﺧﻄﺎ دارد extern A a;
اﺳﺖ( ﺑﻪconstructor ﺧﻄﺎ ﻧﺪارد ﭼﻮن ﻓﻘﻂ ﻳﻚ ﻣﻌﺮﻓﻲ اﺳﺖ و دادن ﻣﻘﺪار اوﻟﻴﻪ )ﻛﻪ ﻫﻤﺎن ﭘﺎراﻣﺘﺮ . ﺳﭙﺮده ﺷﺪهUnit1.cpp
322
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻳﻚ ﻧﻜﺘﻪي ﻗﺎﺑﻞ ﻣﻼﺣﻈﻪ و ﻣﻬﻢ اﻳﻦ اﺳﺖ ﻛﻪ دو ﻛﻼس ﺑﺎ ﻧﺎم ،Aﺗﻌﺮﻳﻒ ﺷﺪه .ﻛﻼس Aدر Unit2ﺑﺎ ﻛﻼس A
در دو unitدﻳﮕﺮ ﻣﺘﻔﺎوت اﺳﺖ .ﺗﻌﺮﻳﻒ ﻫﺮ دو ﻛﻼس Aﻳﻚ ﺟﻮر اﺳﺖ اﻣﺎ ﻣﻲﺷﺪ ﺗﻌﺮﻳﻒ ﻛﻼس Aدر Unit2.hرا ﻃﻮر دﻳﮕﺮي اﻧﺠﺎم داد BDS 2006 .ﺣﺘﻲ اﺟﺎزه ﻣﻲدﻫﺪ ﺑﻪ ﺟﺎي ،Aاﺳﻢ دﻳﮕﺮي اﻧﺘﺨﺎب ﺷﻮد .اﻣﺎ در ﻫﺮ ﺣﺎل روﺷﻦ اﺳﺖ ﻛﻪ ﻧﻤﻲﺷﻮد ﻧﺎم ﻣﺘﻐﻴﺮ aرا ﻋﻮض ﻛﺮد
.در BDS 2006ﺣﺘﻲ ﻣﻲﺷﻮد ﺑﻪ ﺟﺎي ;A a = 123456
ﻧﻮﺷﺖ ;int a = 123456
اﻟﺒﺘﻪ در اﻳﻦ ﺻﻮرت دﻳﮕﺮ constructorﻛﻼس Aﻓﺮاﺧﻮاﻧﺪه ﻧﻤﻲﺷﻮد. ﮔﻔﺘﻢ ﻛﻪ دو ﺗﺎ روش ﺑﺮاي linkageدارﻳﻢ ،روش داﺧﻠﻲ linkageو روش ﺧﺎرﺟﻲ .linkageﺣﺎﻻ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻛﻪ دو ﻧﻮع linkageﺑﺮاي ﺗﺎﺑﻊﻫﺎ و ﻣﺘﻐﻴﺮﻫﺎ ﻫﺴﺖ linkage .از ﻧﻮع Cو linkageاز ﻧﻮع .C++ﺑﺎ "extern "C
ﻧﻮع linkageرا Cو ﺑﺎ "extern "C++
ﻧﻮع linkageرا C++ﻗﺮار ﻣﻲدﻫﻴﻢ .وﻗﺘﻲ ﻧﻮع C ،linkageﺑﺎﺷﺪ؛ linkerﻛﻤﻲ ﻣﺘﻔﺎوت ﻋﻤﻞ ﻣﻲﻛﻨﺪ .اﻟﺒﺘﻪ اﻳﻦ ﺗﻨﻬﺎ در ﻣﻮرد ﺗﺎﺑﻊﻫﺎ اﻫﻤﻴﺖ دارد .وﻗﺘﻲ ،compilerﺑﺮﻧﺎﻣﻪي C++ي را compileﻣﻲﻛﻨﺪ در ﻛﻨﺎر اﺳﻢ ﺗﺎﺑﻊﻫﺎ ،ﻧﻮع ﻣﺘﻐﻴﺮﻫﺎي آنﻫﺎ را ﻫﻢ ﻗﺮار ﻣﻲدﻫﺪ ﺗﺎ overloadﻛﺮدن ﺗﺎﺑﻊﻫﺎ ﻣﻤﻜﻦ ﺑﺎﺷﺪ .ﺑﻪ اﻳﻦ ﻛﺎر name manglingﻣﻲﮔﻮﻳﻨﺪ .وﻗﺘﻲ ﻗﺮار اﺳﺖ linkerﺗﺎﺑﻌﻲ را ﺑﻪ ﻓﺎﻳﻞﻫﺎي Cارﺗﺒﺎط ﺑﺪﻫﺪ ﻧﺒﺎﻳﺪ آن ﺗﺎﺑﻊ ﺑﺎ name compile ،manglingﺷﺪه ﺑﺎﺷﺪ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﻪ compilerﺑﮕﻮﻳﻴﻢ در ﻣﻮرد ﻳﻚ ﺗﺎﺑﻊ name manglingاﻧﺠﺎم ﻧﺪﻫﺪ ﺑﺎﻳﺪ ﻧﻮع linkageآن ﺗﺎﺑﻊ را Cﻗﺮار ﺑﺪﻫﻴﻢ .ﻣﺜﺎلﻫﺎي زﻳﺮ ﻧﺸﺎن ﻣﻲدﻫﻨﺪ ﻛﻪ ﭼﻪ ﻃﻮر ﻣﻲﺷﻮد ﻧﻮع linkageﻳﻚ ﻳﺎ ﭼﻨﺪ ﺗﺎﺑﻊ را Cﻗﺮار داد: ;)extern "C" void f(int "extern "C { ;)(int g ;)void h(int,char } "extern "C { >#include <stdio.h }
323
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﻟﺒﺘﻪ اﮔﺮ ﺑﻪ stdio.hو ﺑﻘﻴﻪي header fileﻫﺎي ﻣﻌﺮوف Cﻧﮕﺎه ﻛﻨﻴﺪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ در آنﻫﺎ در ﺟﺎي ﻣﻨﺎﺳﺐ "extern "C
ﺑﻪ ﻛﺎر رﻓﺘﻪ و دﻳﮕﺮ ﻧﻴﺎزي ﻧﻴﺴﺖ ﻣﺎ اﻳﻦ ﻛﺎر را اﻧﺠﺎم ﺑﺪﻫﻴﻢ .ﺑﻨﺎﺑﺮاﻳﻦ ﺑﺪون ﻫﻴﭻ ﻣﺸﻜﻠﻲ ﻣﻲﺷﻮد
آنﻫﺎ را در ﺑﺮﻧﺎﻣﻪﻫﺎي C++ﺑﻪ ﻛﺎر ﺑﺮد. ﺑﻴﺎﻳﻴﺪ ﻛﺎري ﺟﺎﻟﺐ اﻧﺠﺎم ﺑﺪﻫﻴﻢ .ﻣﻲﺧﻮاﻫﻴﻢ ﻳﻚ ﻓﺎﻳﻞ .cppرا compileﻛﻨﻴﻢ ﺗﺎ ﻳﻚ ﻓﺎﻳﻞ .objﺑﻪ وﺟﻮد آﻳﺪ و ﺑﻌﺪ از اﻳﻦ ﻓﺎﻳﻞ .objدر ﺑﺮﻧﺎﻣﻪاي دﻳﮕﺮ اﺳﺘﻔﺎده ﻛﻨﻴﻢ. ﻓﺎﻳﻞﻫﺎﻳﻲ ﺑﺎ ﻧﺎمﻫﺎي object.cpp ،console.cppو object.hﺑﻪ projectاﺿﺎﻓﻪ ﻛﻨﻴﺪ و ﺑﺮﻧﺎﻣﻪ زﻳﺮ را در آنﻫﺎ ﺑﻨﻮﻳﺴﻴﺪ و compileﻛﻨﻴﺪ ﺗﺎ ﻓﺎﻳﻞ object.objاﻳﺠﺎد ﺷﻮد: console.cpp: >#include <conio.h )(int main { ;)(_getch }
object.h: #ifndef objectH #define objectH >#include <iostream ;using namespace std ;)(void DabaDee ;)(void DabaDie #endif
object.cpp: "#include "object.h )(void DabaDee { ;"cout<< "void DabaDee()\n } )(void DabaDie { ;"cout<< "void DabaDie()\n }
Output: empty
ﺣﺎﻻ دو ﻓﺎﻳﻞ object.cppو object.hرا از projectﺣﺬف ﻛﻨﻴﺪ و ﻓﺎﻳﻞ object.objاﻳﺠﺎد ﺷﺪه را ﺑﻪ ﻓﺎﻳﻞ- ﻫﺎي projectاﺿﺎﻓﻪ ﻛﻨﻴﺪ .ﺑﺎﻳﺪ projectﺷﻤﺎ ﺑﻪ اﻳﻦ ﺷﻜﻞ ﺑﺎﺷﺪ:
324
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ :BDS 2006 در
:Visual C++ 2005 و در
: ﺑﻨﻮﻳﺴﻴﺪ و اﺟﺮا ﻛﻨﻴﺪconsole.cpp ﺑﺮﻧﺎﻣﻪ زﻳﺮ را در #include <conio.h> #include <iostream> using namespace std; void DabaDee(); void DabaDie(); int main() { DabaDee(); DabaDie(); _getch(); }
Output: void DabaDee() void DabaDie()
325
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊﻫﺎيlinker در اﻳﻦ ﺑﺮﻧﺎﻣﻪ void DabaDee(); void DabaDie();
. ﺑﻪ ﻛﺎر ﻣﻲﺑﺮدconsole.exe ﭘﻴﺪا ﻣﻲﻛﻨﺪ و آنﻫﺎ را در اﻳﺠﺎدobject.obj را در ﻣﻲﺷﻮد ﺧﻂ زﻳﺮproject ﺑﻪobject.obj ﺑﻪ ﺟﺎي اﺿﺎﻓﻪ ﻛﺮدنBDS 2006 در #pragma link "object.obj"
ﺑﻌﺪ ازconsole.cpp را در void DabaDee(); void DabaDie();
در اﻳﻦ ﺻﻮرت ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺷﻜﻞ.اﺿﺎﻓﻪ ﻛﺮد #include <conio.h> #include <iostream> using namespace std; void DabaDee(); void DabaDie(); #pragma link "object.obj" int main() { DabaDee(); DabaDie(); _getch(); }
" درobject.obj" ﺑﻪ ﺟﺎي. ﻧﻴﺴﺖproject ﺑﻪobject.obj در ﻣﻲآﻳﺪ و دﻳﮕﺮ ﻧﻴﺎزي ﺑﻪ اﺿﺎﻓﻪ ﻛﺮدن #pragma link "object.obj"
. روي ﻫﺎرد ﻛﺎﻣﭙﻴﻮﺗﺮ را ﻗﺮار دادobject.obj ﻣﻲﺷﻮد آدرس ﻛﺎﻣﻞ ﺳﺎﺧﺘﻪاﻳﻢ ﺑﻪ ﺟﺎيC++ ِ compiler را ﺑﺎobject.obj ﭼﻮن void DabaDee(); void DabaDie();
:ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ extern "C++" { void DabaDee(); void DabaDie(); }
:وﻟﻲ ﻧﻤﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ extern "C" { void DabaDee(); void DabaDie();
326
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
ﺣﺎﻻ اﮔﺮ اﻳﻦ ﻓﺎﻳﻞ object.objﺗﻮﺳﻂ ﻳﻚ C ِ compilerﺳﺎﺧﺘﻪ ﺷﺪه ﺑﻮد ﺑﻪ ﺟﺎي ;)(void DabaDee ;)(void DabaDie
در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ ﻣﻲﻧﻮﺷﺘﻴﻢ: "extern "C { ;)(void DabaDee ;)(void DabaDie }
در ﭘﺎﻳﺎن اﻳﻦ ﺑﺨﺶ ﻣﻲﮔﻮﻳﻢ ﻛﻪ externرا ﻣﻲﺷﻮد درون ﻳﻚ ﺗﺎﺑﻊ ﻫﻢ ﺑﻪ ﻛﺎر ﺑﺮد .ﺑﻪ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻛﻪ در ﻓﻘﻂ ﻳﻚ unitﻧﻮﺷﺘﻪ ﺷﺪه دﻗﺖ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;extern int a ;cout<< a ;)(_getch } ;int a = 123456
Output: 123456
در ﺗﺎﺑﻊ )( mainﻣﺘﻐﻴﺮ aﻫﻤﺎن ﻣﺘﻐﻴﺮ aﻋﻤﻮﻣﻲ اﺳﺖ ﻛﻪ ﺑﻌﺪ از )( mainﺗﻌﺮﻳﻒ ﺷﺪه .اﺣﺘﻤﺎﻻ linkerﺑﻌﺪ از رﺳﻴﺪن ﺑﻪ ;extern int a
ﺷﺮوع ﺑﻪ ﮔﺸﺘﻦ ﺑﺮاي ﺗﻌﺮﻳﻒ ﻣﻲﻛﻨﺪ و ﺑﻪ ;int a = 123456
ﻣﻲرﺳﺪ و آن را ﺑﻪ ﻋﻨﻮان ﺗﻌﺮﻳﻒ اﻧﺘﺨﺎب ﻣﻲﻛﻨﺪ .ﺑﺮﻧﺎﻣﻪ را ﺑﻪ ﺻﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﻣﻲدﻫﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 5
327
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { extern int a; cout<< a; } _getch(); } int a = 123456;
Output: 123456 main() را ﻗﺒﻞ ازa اﮔﺮ. در ﻓﻀﺎي ﻋﻤﻮﻣﻲ ﺑﻪ دﻧﺒﺎل ﺗﻌﺮﻳﻒ ﻣﻲﮔﺮدد ﻧﻪ داﺧﻞ ﺗﺎﺑﻊﻫﺎlinker ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ
. ﻣﻌﺮﻓﻲ ﺷﻮدextern ﺑﺎmain() ﺗﻌﺮﻳﻒ ﻣﻲﻛﺮدم دﻳﮕﺮ ﻻزم ﻧﺒﻮد در
6 ?@ و در#define
: ﻣﺜﺎل زﻳﺮ اﻳﻦ را ﻧﺸﺎن ﻣﻲدﻫﺪ. ﻣﻌﺘﺒﺮ اﺳﺖunit ﺷﻮد ﺗﻨﻬﺎ در ﻫﻤﺎن#define ﭼﻴﺰيunit اﮔﺮ در ﻳﻚ console.cpp: #include "Unit1.h" void f(); int main() { #if defined BLUE_AND_FRIENDS cout<<"BLUE_AND_FRIENDS #defined in Console.cpp\n"; #endif #undef BLUE_AND_FRIENDS f(); _getch(); } #undef BLUE_AND_FRIENDS
Unit1.h: #define BLUE_AND_FRIENDS #include <conio.h> #include <iostream> using namespace std;
Unit1.cpp: #include "Unit1.h" void f() { #if defined BLUE_AND_FRIENDS cout<<"BLUE_AND_FRIENDS #defined in Unit1.cpp\n";
328
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #endif #undef BLUE_AND_FRIENDS } #undef BLUE_AND_FRIENDS
Output: BLUE_AND_FRIENDS #defined in Console.cpp BLUE_AND_FRIENDS #defined in Unit1.cpp
Unit1.hﺑﻴﻦ دو unitﺑﺮﻧﺎﻣﻪ ﻣﺸﺘﺮك اﺳﺖ .در ﭘﺎﻳﺎن ِ ﻫﺮ #undef ،BLUE_AND_FRIENDS ،unitﺷﺪه وﻟﻲ اﻳﻦ ﻛﺎر BLUE_AND_FRIENDS ،را در unitدﻳﮕﺮ #undefﻧﻜﺮده اﺳﺖ .ﻫﻤﻴﻦ در ﻣﻮرد #includeﻫﻢ درﺳﺖ اﺳﺖ.
از اﻳﻦ ﺑﻪ ﺑﻌﺪ ﻣﺎﻧﻨﺪ ﻓﺼﻞﻫﺎي ﻗﺒﻞ ﺑﺮﻧﺎﻣﻪﻫﺎ را در ﻓﻘﻂ ﻳﻚ unitﻣﻲﻧﻮﻳﺴﻴﻢ.
6Tه R ; "Aا اد
] Q1د ' \.را در ) Editorﺟﺎﻳﻲ ﻛﻪ ﺑﺮﻧﺎﻣﻪ را ﺗﺎﻳﭗ ﻣﻲﻛﻨﻴﻢ( ﻣﻲﺷﻮد ﺑﻪ ﺳﻪ ﺷﻜﻞ ﻧﺸﺎن داد: (1در ﻣﺒﻨﺎي :10ﻣﺜﻼ 1364
(2در ﻣﺒﻨﺎي :8ﻣﺜﻼ ) 02524ﺻﻔﺮ ﻃﺮف ﭼﭗ ﻧﺸﺎﻧﻪي ﻣﺒﻨﺎي 8اﺳﺖ( (3در ﻣﺒﻨﺎي :16ﻣﺜﻼ ) 0x554 ( 0xﺻﻔﺮ اﻛﺲ( در ﻃﺮف ﭼﭗ ﻧﺸﺎﻧﻪي ﻣﺒﻨﺎي 16اﺳﺖ( رﻗﻢﻫﺎﻳﻲ ﻛﻪ در ﻣﺒﻨﺎي 8ﻣﻲﺷﻮد ﻧﻮﺷﺖ 0,1,2,3,4,5,6,7ﻫﺴﺘﻨﺪ و رﻗﻢﻫﺎﻳﻲ ﻛﻪ در ﻣﺒﻨﺎي 16ﻣﻲﺷﻮد ﻧﻮﺷﺖ ﻋﺒﺎرﺗﻨﺪ از .0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,fﺑﻪ ﺟﺎي a,b,c,d,e,fﻣﻲﺷﻮد از A,B,C,D,E,Fﻫﻢ اﺳﺘﻔﺎده ﻛﺮد. ﺑﺮاي ﭼﺎپ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ در ﻣﺒﻨﺎي 8از octو ﺑﺮاي ﭼﺎپ در ﻣﺒﻨﺎي 16از hexاﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ: >#include <conio.h >#include <iostream ;using namespace std )(int main { ;int a = 1364 ;cout<< oct << a << endl
329
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ cout<< hex << a; _getch(); }
Output: 2524 554
:ﺣﺎﻻ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﺑﺒﻴﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; int main() { cout<< 25L << endl; cout<< typeid(25L).name(); _getch(); }
Output: 25 long
درlong اﺳﺖ اﻣﺎ ﭼﻮن اﻧﺪازهيlong وﻟﻲ از ﻧﻮع25 اﺳﺘﻔﺎده ﺷﺪه ﻛﻪ ﻫﻤﺎن ﻋﺪد25L در اﻳﻦ ﺑﺮﻧﺎﻣﻪ از . ﮔﺮﻓﺖ25 را ﻣﻲﺷﻮد ﻫﻤﺎن25L ، ﻳﻜﻲ اﺳﺖint ﻫﺎي اﻣﺮوزي ﺑﺎcompiler اﻳﻦ.25LL ﻣﺜﻼ. ﻣﻲﮔﺬارﻳﻢLL __ در آﺧﺮ ﻋﺪدint64 ﻳﺎ ﻫﻤﺎنlong long ﺑﺮاي ﻣﺸﺨﺺ ﺷﺪن ﻧﻮع :ﺑﺮﻧﺎﻣﻪ را ﺑﺒﻴﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; int main() { cout<< typeid(25).name() << endl; // output: int cout<< typeid(25LL).name() << endl; // output: __int64 cout<< typeid((long long)25).name() << endl; // output: __int64 cout<< typeid(0xffffffffffff).name() << endl; // output: __int64 _getch(); }
Output: int __int64 __int64
330
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ __int64
ﺧﻮد ﺑﻪ ﺧﻮدcompiler ، ﺟﺎ ﺑﺸﻮدint ﭼﻮن ﻃﻮﻻﻧﻲ ﺗﺮ از آن اﺳﺖ ﻛﻪ در0xffffffffffff در ﻣﻮرد .__ در ﻧﻈﺮ ﻣﻲﮔﻴﺮدint64 آن را
ر
ه
2 ﺣﺎﻻ اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﺗﻨﻬﺎ. ﺑﻴﺖ دارد32 ﺑﺎﻳﺖ ﻳﺎ ﺑﻪ ﻋـﺒﺎرت دﻳﮕﺮ4 int n\ ﺑﻴﺖ داﺷﺘﻪ ﺑﺎﺷﺪ ﺑﺎﻳﺪ ﭼﻪ ﻛﻨﻴﻢ؟ #include <conio.h> #include <iostream> using namespace std; struct A { unsigned d : 1; } ; int main() { A a; cout<< typeid(a.d).name() << endl; a.d = 0; for(int i = 0; i < 10; i++) { a.d++; cout<< a.d << endl; } _getch(); }
Output: unsigned int 1 0 1 0 1 0 1 0 1 0
331
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در اﻳﻦ ﻣﺜﺎل A::dﻧﻮع unsignedدارد وﻟﻲ ﺑﻴﺶ از ﻳﻚ ﺑﻴﺖ ﻧﺪارد ﺑﺮاي ﻫﻤﻴﻦ ﻳﺎ ﻣﻘﺪار ﺻﻔﺮ دارد ﻳﺎ ﻳﻚ و ﻏﻴﺮ از اﻳﻦ دو ﻣﻘﺪار ،ﻣﻘﺪار دﻳﮕﺮي ﻧﻤﻲﺗﻮاﻧﺪ ﺑﮕﻴﺮد .در struct A { ;unsigned d : 1 ; }
» «: 1ﻳﻌﻨﻲ dﻓﻘﻂ ﻳﻚ ﺑﻴﺖ ﺣﺎﻓﻈﻪ داﺷﺘﻪ ﺑﺎﺷﺪ .ﺑﻪ اﻋﻀﺎﻳﻲ از ﻳﻚ ﻛﻼس ﻛﻪ ﺗﻌﺪاد ﺑﻴﺖﻫﺎي آنﻫﺎ را ﺧﻮدﻣﺎن ﻣﺸﺨﺺ ﻣﻲﻛﻨﻴﻢ bit fieldﻣﻲﮔﻮﻳﻨﺪ .در ﺑﺎﻻ dﻳﻚ bit fieldاﺳﺖ. ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std struct A { ;int a : 1 ;int b : 3 ;}
// output: 4
)(int main { ;}A u = {1,2 ;)cout<< sizeof(A ;)(_getch }
Output: 4
ﺑﻪ اﻳﻦ دو ﻧﻜﺘﻪ ﺑﺎﻳﺪ دﻗﺖ ﻛﺮد: (1روش ﻣﻘﺪار اوﻟﻴﻪ دادن ﺑﻪ bit fieldﻫﺎ (2اﻧﺪازهي Aﺗﻨﻬﺎ 4ﺑﺎﻳﺖ اﺳﺖ ﺑﺎ اﻳﻦ ﻛﻪ دو ﻣﺘﻐﻴﺮ ﺑﺎ ﻧﻮع intدارد .ﻋﻠﺖ اﻳﻦ اﺳﺖ ﻛﻪ aو bروي ﻫﻢ رﻓﺘﻪ ﺑﺎ 4ﺑﻴﺖ ﺑﻴﺶ ﺗﺮ ﻧﻴﺎز ﻧﺪارﻧﺪ و در \ int nﺟﺎ ﻣﻲﺷﻮﻧﺪ. bit fieldﻫﺎ ﺗﻨﻬﺎ ﺑﺎﻳﺪ از اﻧﻮاع integralﻳﻌﻨﻲ ،short ،unsigned ،int ،unsigned char ،char __int64 ،unsigned shortو unsigned __int64ﺑﺎﺷﺪ integral .ﻳﻌﻨﻲ »ﻋﺪد ﺻﺤﻴﺤﻲ« )و رﺑﻄﻲ ﺑﻪ اﻧﺘﮕﺮال در رﻳﺎﺿﻲ ﻧﺪارد( .در __int128 ،Visual C++ 2005ﻫﻢ دارﻳﻢ ﻛﻪ ﻫﻨﻮز وﻳﮋﮔﻲﻫﺎي
332
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﺎﻣﻞ دﻳﮕﺮ اﻧﻮاع integralﺑﻪ آن داده ﻧﺸﺪه و ﺣﺘﻲ ﻋﻀﻮ ﻣﻌﻤﻮﻟﻲ ﻛﻼس ﻫﻢ ﻧﻤﻲﺗﻮاﻧﺪ ﺑﺎﺷﺪ ﭼﻪ ﺑﺮﺳﺪ ﺑﻪ bit .field ﺑﺮﻧﺎﻣﻪي زﻳﺮ در BDS 2006و Visual C++ 2005ﺧﺮوﺟﻲ ﻣﺘﻔﺎوت دارد: >#include <conio.h >#include <iostream ;using namespace std struct A { ;unsigned __int64 a : 1 ;short b : 3 ;} )(int main { ;)cout<< sizeof(A ;)(_getch }
Output: 8 Output (Visual C++ 2005): 16
ﻣﻲداﻧﻴﻢ در ﻳﻚ intﻳﻚ ﺑﻴﺖ ﺑﺮاي ﻋﻼﻣﺖ ﻣﺜﺒﺖ ﻳﺎ ﻣﻨﻔﻲ ﻛﻨﺎر ﮔﺬاﺷﺘﻪ ﻣﻲﺷﻮد .ﺣﺎﻻ اﮔﺮ ﻳﻚ intدرﺳﺖ ﻛﻨﻴﻢ ﻛﻪ ﻳﻚ ﺑﻴﺖ ﺑﻴﺶ ﺗﺮ ﻧﺪارد ﺑﺎﻳﺪ آن ﺑﻴﺖ ﺑﺮاي ﻋﻼﻣﺖ اﺳﺘﻔﺎده ﺑﺸﻮد وﻟﻲ در اﻳﻦ ﺣﺎﻟﺖ ﺑﻴﺘﻲ ﺑﺮاي ﺧﻮد ﻋﺪد ﺑﺎﻗﻲ ﻧﻤﻲﻣﺎﻧﺪ .در اﻳﻦ ﺣﺎﻟﺖ ﻣﻌﻨﻲ آن ﭼﻴﺴﺖ؟ >#include <conio.h >#include <iostream ;using namespace std struct A { ;int a : 1 ;} )(int main { ;A u ;u.a = 0 cout<< u.a << endl; // output: 0 ;u.a = 1 cout<< u.a << endl; // output : -1 ;int x = u.a
333
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;cout<< x ;)(_getch
// output : -1
}
Output: 0 -1 -1
ﭘﺲ ﻣﻘﺪار 1ﺑﺮاي A::aﻣﻌﻨﻲ -1و ﻣﻘﺪار 0ﻣﻌﻨﻲ 0دارد .ﺑﺒﻴﻨﻴﺪ ﻛﻪ وﻗﺘﻲ ﻣﻘﺪار u.aدر xﻗﺮار ﮔﺮﻓﺘﻪ ،ﻣﻘﺪار -1 ،xﺷﺪه ﻧﻪ 0ﻳﺎ .1 اﮔﺮ ﺗﻌﺪاد ﺑﻴﺖﻫﺎ را ﻣﺜﻼ ﺑﺮاي ﻳﻚ 50 intﺑﮕﻴﺮﻳﻢ ،در BDS 2006ﺗﻨﻬﺎ 32ﺑﻴﺖ ﻳﻌﻨﻲ ﺣﺪاﻛﺜﺮ ﻃﻮل در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std struct A { ;unsigned char a : 9 ;} )(int main { ;A u cout<< sizeof(A) << endl; // output: 1 ;u.a = 0xff cout<< (int) u.a << endl; // output: 255 ;u.a++ cout<< (int) u.a << endl; // output: 0 ;)(_getch }
Output (BDS 2006): 1 255 0
Visual C++ 2005اﻳﻦ ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻧﻤﻲﻛﻨﺪ .ﻣﻘﺪار 0xffﻛﻪ ﻫﻤﺎن 255اﺳﺖ ﺑﻪ u.aداده ﺷﺪه .اﻳﻦ ﺑﺰرگ ﺗﺮﻳﻦ ﻋﺪدي اﺳﺖ ﻛﻪ ﻣﻲﺷﻮد ﺑﻪ u.aداد .وﻗﺘﻲ ﺑﺎ ++ﻳﻜﻲ ﺑﻪ آن اﺿﺎﻓﻪ ﻛﺮدهاﻳﻢ ﻣﻘﺪارش ﺻﻔﺮ ﺷﺪه. اﮔﺮ ﺑﻪ ﺟﺎي 9ﺟﻠﻮي aﻋﺪد -6ﺑﮕﺬارﻳﻢ ﺑﺮﻧﺎﻣﻪ در BDS 2006ﻫﻢ اﺟﺮا ﻧﻤﻲﺷﻮد. sizeofدر ﻣﻮرد bit fieldﻫﺎ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﻴﺴﺖ.
334
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ :ﻫﺎ در ﻳﻚ ﻛﻼس ﻛﺎﻣﻞ ﻫﻢ ﻗﺎﺑﻞ اﺳﺘﻔﺎدهاﻧﺪbit field #include <conio.h> #include <iostream> using namespace std; class A { private: unsigned a : 15;
// use 15 bits only
public: A(): a(1387) { cout<< a << endl; } ~A() { cout<< "~A()" << endl; _getch(); } }; int main() { A u; }
Output: 1387 ~A()
. ﻣﻘﺪار اوﻟﻴﻪ داده ﺷﺪه ﻫﻢ دﻗﺖ ﻛﻨﻴﺪconstructor ﭼﻪ ﻃﻮر درa ﺑﻪ اﻳﻦ ﻛﻪ : ﻧﻜﺘﻪﻫﺎي دﻳﮕﺮي ﻫﻢ ﻫﺴﺖunion اﻣﺎ در ﻣﻮرد.union ﻣﻲﺷﻮد ﻧﻮﺷﺖclass در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ ﺑﻪ ﺟﺎي #include <conio.h> #include <iostream> using namespace std; union A { int a : 4; int b : 4; }; int main() { A u = {3}; cout<< u.b << "\n"; u.a = 5; cout<< u.b << "\n"; _getch(); }
Output:
335
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 3 5
ﺑﻪ u.aﻣﻘﺪار 5دادهاﻳﻢ .ﻣﻲﺑﻴﻨﻴﻢ ﻛﻪ u.bﻫﻢ ﻣﻘﺪار 5ﮔﺮﻓﺘﻪ .ﻣﻤﻜﻦ اﺳﺖ ﺑﺨﻮاﻫﻴﻢ آدرس u.aو ) u.bﻳﻌﻨﻲ &u.aو (&u.bرا ﭼﻚ ﻛﻨﻴﻢ و ﺑﺒﻴﻨﻴﻢ آﻳﺎ آدرس ﻳﻜﻲ اﺳﺖ ﻳﺎ ﻧﻪ .اﻣﺎ & ﺑﺮاي bit fieldﻫﺎ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﻴﺴﺖ ﻳﻌﻨﻲ &u.a errorدارد.
ه
"8
] ^J3ه & ،| ,و ^ ﻋﻤﻠﮕﺮﻫﺎي ﺑﻴﺘﻲ ﮔﻔﺘﻪ ﻣﻲﺷﻮﻧﺪ .اﮔﺮ aو bدو ﻣﻘﺪار integralﺑﺎﺷﻨﺪ ﺑﻪ اﻳﻦ ﺷﻜﻞ a: 0 0 1 0 1 1 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 0 1 0 0 0 1 b: 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 1 0 1 1 0 1 1 0 1 0 0 1 0
در اﻳﻦ ﺻﻮرت a|b ،a&bو a^bﺑﻪ اﻳﻦ ﺷﻜﻞ ﻫﺴﺘﻨﺪ: b: 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 1 0 1 1 0 1 0 0 0 0 b: 1 0 1 1 1 1 1 1 1 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 0 1 0 0 1 1 b: 1 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 1
& 0 | 1 ^ 1
a 0 a 0 a 0
• ﻳﻚ ﺑﻴﺖ در a&bﻳﻚ اﺳﺖ ﻓﻘﻂ اﮔﺮ ﺑﻴﺖﻫﺎي ﻣﺘﻨﺎﻇﺮ در aو bﻫﺮ دو ﻳﻚ ﺑﺎﺷﻨﺪ. • ﻳﻚ ﺑﻴﺖ در a&bﺻﻔﺮ اﺳﺖ ﻓﻘﻂ اﮔﺮ ﺑﻴﺖﻫﺎي ﻣﺘﻨﺎﻇﺮ در aو bﻫﺮ دو ﺻﻔﺮ ﺑﺎﺷﻨﺪ. • ﻳﻚ ﺑﻴﺖ در a^bﺻﻔﺮ اﺳﺖ ﻓﻘﻂ اﮔﺮ ﺑﻴﺖﻫﺎي ﻣﺘﻨﺎﻇﺮ در aو bﻣﺴﺎوي ﺑﺎﺷﻨﺪ. ﺷﻜﻞﻫﺎي ﺑﺎﻻ را ﺑﺒﻴﻨﻴﺪ .در اﻳﻦ ﺷﻜﻞ ﺑﻴﺖﻫﺎي آﺑﻲ ﺑﺎ ﻫﻢ ﻣﺘﻨﺎﻇﺮﻧﺪ ﻫﻤﻴﻦ ﻃﻮر در ﻣﻮرد ﺑﻴﺖﻫﺎي ﻧﺎرﻧﺠﻲ ،ﺑﻨﻔﺶ و ﺳﺒﺰ .ﻣﺜﻼ ﺑﻴﺖﻫﺎي ﻧﺎرﻧﺠﻲ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ: • ﺑﻴﺖ ﻧﺎرﻧﺠﻲ در a&bﺻﻔﺮ اﺳﺖ ﭼﻮن ﻫﺮ دو ﺑﻴﺖ ﻧﺎرﻧﺠﻲ در aو bﻳﻚ ﻧﻴﺴﺘﻨﺪ. • ﺑﻴﺖ ﻧﺎرﻧﺠﻲ در a|bﻳﻚ اﺳﺖ ﭼﻮن ﻫﺮ دو ﺑﻴﺖ ﻧﺎرﻧﺠﻲ در aو bﺻﻔﺮ ﻧﻴﺴﺘﻨﺪ. • ﺑﻴﺖ ﻧﺎرﻧﺠﻲ در a^bﻳﻚ اﺳﺖ ﭼﻮن ﺑﻴﺖﻫﺎي ﻧﺎرﻧﺠﻲ در aو bﻣﺴﺎوي ﻧﻴﺴﺘﻨﺪ. ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ:
336
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <conio.h> #include <iostream> using namespace std; int main() { int a = 0xff0f0fff; int b = 0x0f0ff0f0; cout<< hex << (a & b) << endl; // output: 0x0f0f00f0; cout<< hex << (a | b) << endl; // output: 0xff0fffff; cout<< hex << (a ^ b) << endl; // output: 0xf000ff0f; _getch(); }
Output: f0f00f0 ff0fffff f000ff0f
ﻧﻤﺎﻳﻨﺪهي ﭼﻬﺎر ﺑﻴﺖ ﭘﺸﺖ ﺳﺮ ﻫﻢ ﺷﺎﻣﻞ ﻳﻚ اﺳﺖ و ﻫﺮ ﺻﻔﺮ ﻧﻤﺎﻳﻨﺪهي ﭼﻬﺎر ﺑﻴﺖf ﻫﺮ0xff0f0fff در . ﺧﺮوﺟﻲ ﺗﻮﺿﻴﺤﺎت ﻗﺒﻠﻲ را ﺗﺄﻳﻴﺪ ﻣﻲﻛﻨﺪ.ﭘﺸﺖ ﺳﺮﻫﻢ ﺷﺎﻣﻞ ﺻﻔﺮ اﺳﺖ :ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺟﺎﻟﺐ اﺳﺖ #include <conio.h> #include <iostream> using namespace std; union A { char a; struct { unsigned b1 : 1; unsigned b2 : 1; unsigned b3 : 1; unsigned b4 : 1; unsigned b5 : 1; unsigned b6 : 1; unsigned b7 : 1; unsigned b8 : 1; }; A(char x): a(x) { cout << b8 << b7 << b6 << b5 << b4 << b3 << b2 << b1 << endl; } }; int main() { char ch1 = 101;
337
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 01100101 11001010 01000000 11101111 10101111
// // // // //
;char ch2 = 202 ;)A a(ch1 ;)A b(ch2 ;)A c(ch1 & ch2 ;)A d(ch1 | ch2 ;)A e(ch1 ^ ch2 ;)(_getch }
Output: 01100101 11001010 01000000 11101111 10101111
ﻛﻼس Aﺑﻴﺖﻫﺎي ﻳﻚ ﻛﺎراﻛﺘﺮ را ﭼﺎپ ﻣﻲﻛﻨﺪ. دو ﻋﻤﻠﮕﺮ دﻳﮕﺮ ﻫﻢ ﺑﺮاي ﻛﺎر ﺑﺎ ﺑﻴﺖﻫﺎ دارﻳﻢ ﻳﻌﻨﻲ << و >> .اﮔﺮ aﻋﺪدي ﺻﻴﺢ ﺑﺎﺷﺪ a<<6 ،ﻫﻤﺎن aاﺳﺖ ﻛﻪ ﺑﻴﺖﻫﺎي آن 6واﺣﺪ ﺑﻪ ﺳﻤﺖ ﭼﭗ رﻓﺘﻪاﻧﺪ و a>>4ﻫﻤﺎن aاﺳﺖ ﻛﻪ ﺑﻴﺖﻫﺎي آن ﭼﻬﺎر واﺣﺪ ﺑﻪ ﺳﻤﺖ ﭼﭗ رﻓﺘﻪاﻧﺪ .ﻳﻌﻨﻲ اﮔﺮ ﺑﻪ اﻳﻦ ﺻﻮرت ﺑﺎﺷﺪ: a: 0 0 1 0 1 1 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 0 1 0 0 0 1
آن وﻗﺖ a<<6: 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 0 0
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ 6ﺑﻴﺖ ﻛﻪ آنﻫﺎ را ﺑﺎ رﻧﮓ آﺑﻲ ﻧﺸﺎن دادهام از ﺳﻤﺖ ﭼﭗ ﺣﺬف ﺷﺪهاﻧﺪ و 6ﺑﻴﺖ ﺻﻔﺮ ﻛﻪ ﺑﺎ رﻧﮓ ﻧﺎرﻧﺠﻲ ﻧﺸﺎن دادهام ﺑﻪ ﺳﻤﺖ راﺳﺖ اﺿﺎﻓﻪ ﺷﺪهاﻧﺪ .اﻧﮕﺎر ﺑﻴﺖﻫﺎ 6واﺣﺪ ﺑﻪ ﭼﭗ رﻓﺘﻪ ﺑﺎﺷﻨﺪ .و a: 0 0 1 0 1 1 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 0 1 0 0 0 1 a>>4: 0 0 0 0 0 0 1 0 1 1 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 0 1
در اﻳﻦ ﺟﺎ ﻫﻢ ،ﺧﺎﻧﻪﻫﺎي آﺑﻲ ﺣﺬف ﺷﺪهاﻧﺪ و ﺧﺎﻧﻪﻫﺎي ﻧﺎرﻧﺠﻲ اﺿﺎﻓﻪ ﺷﺪهاﻧﺪ .اﻧﮕﺎر ﺑﻴﺖﻫﺎ 4واﺣﺪ ﺑﻪ ﺳﻤﺖ راﺳﺖ رﻓﺘﻪ ﺑﺎﺷﻨﺪ .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ a>>4ﻳﺎ a<<6ﻣﻘﺪار aرا ﺗﻐﻴﻴﺮ ﻧﻤﻲدﻫﻨﺪ.
338
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ :ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﻮﺟﻪ ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; int main() { int a = 0xfffffff << 4; cout<< hex << a; _getch(); }
Output: fffffff0
و. ﭼﻬﺎر واﺣﺪ ﺑﻪ ﺳﻤﺖ ﭼﭗ رﻓﺘﻪاﻧﺪ0xfffffff ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺑﻴﺖﻫﺎي #include <conio.h> #include <iostream> using namespace std; int main() { cout<< hex << (0xffffffff >> -16) << endl; cout<< hex << (0xffffffff >> 16) << endl; _getch(); }
Output (BDS 2006): ffff ffff Output (Visual C++): 0 ffff
. ﻫﻢ ﻓﺮﻗﻲ ﻧﺪارﻧﺪDev C++ در. ﻓﺮﻗﻲ ﻧﺪارﻧﺪ16 و-16 ،BDS 2006 در. .
:ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺑﺮاي ﺷﻨﺎﺧﺘﻦ ﻧﻮعﻫﺎ ﻻزم اﺳﺖ #include <conio.h> #include <iostream> using namespace std; int main() { char
ch = 1;
339
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ int in = 2; long lo = 3L; __int64 ll = 4LL; cout<< typeid(ch cout<< typeid(in cout<< typeid(lo cout<< typeid(ll cout<< typeid(ll cout<< typeid(ll cout<< typeid(ll _getch();
<< << << << << << &
ch).name() in).name() lo).name() ll).name() in).name() lo).name() in).name()
<< << << << << << <<
endl; endl; endl; endl; endl; endl; endl;
// // // // // // //
int int long __int64 __int64 __int64 __int64
}
Output: int int long __int64 __int64 __int64 __int64
. را ﻫﻢ ﺑﺮرﺳﻲ ﻛﺮد++ ﻳﺎ+ اﻟﺒﺘﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را ﻣﻲﺷﻮد ﺧﻴﻠﻲ ﻛﺎﻣﻞ ﺗﺮ از اﻳﻦ ﻧﻮﺷﺖ و ﻋﻤﻠﮕﺮﻫﺎي دﻳﮕﺮ ﻣﺜﻞ
: را ﭼﺎپ ﻣﻲﻛﻨﺪlong long n\ ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻧﻤﺎﻳﺶ ﺑﻴﺘﻲ #include <conio.h> #include <iostream> using namespace std; int main() { long long
a = 0xf0f0f0f0f0f0f0fLL;
for(int i = 8 * sizeof(long long); 0 <= i; i--) { __int64 one = (1LL << i); bool bit = (a & one) >> i; cout<< bit; } _getch(); }
Output:
0000011110000111100001111000011110000111100001111000011 1100001111
340
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ __int64 ﻣﻲداﻧﻴﺪ ﻛﻪ.اﻣﻴﻦ ﺑﻴﺖ ﺻﻔﺮﻧﺪi اﺳﺖ ﻛﻪ ﻫﻤﻪي ﺑﻴﺖﻫﺎي آن ﺑﻪ ﺟﺰlong long n\ one
اﺳﺖ وa امi ام آن ﺑﺮاﺑﺮ ﺑﺎ ﺑﻴﺖi اﺳﺖ ﻛﻪ ﺑﻴﺖlong long n\ a&one . اﺳﺖlong long ﻫﻤﺎن ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ در. اﺳﺖa اﻣﻴﻦ ﺑﻴﺖi ﺑﺮاﺑﺮ ﺑﺎ ﻣﻘﺪارbit ﻣﻘﺪار.ﺑﻘﻴﻪي ﺑﻴﺖﻫﺎﻳﺶ ﺻﻔﺮﻧﺪ . داردlong long ﻧﻮع، ﻧﺸﺎن دﻫﻨﺪه اﻳﻦ اﺳﺖ ﻛﻪ ﻋﺪدLL ،0xf0f0f0f0f0f0f0fLL
calling convention
calling ﺑﻪ ﻫﺮ ﻛﺪام از اﻳﻦ روشﻫﺎ ﻳﻚ.ﻳﻚ ﺗﺎﺑﻊ ﺑﻪ روشﻫﺎي ﻣﺨﺘﻠﻔﻲ ﻣﻲﺗﻮاﻧﺪ ﻳﻚ ﺗﺎﺑﻊ دﻳﮕﺮ را ﻓﺮا ﺑﺨﻮاﻧﺪ :ﻫﺎ را در ﭘﺎﻳﻴﻦ ﻣﻲﺑﻴﻨﻴﺪcalling convention ﺑﻌﻀﻲ از. ﻣﻲﮔﻮﻳﻨﺪconvention cdecl //not keyword in Visual C++ 2005 _cdecl __cdecl _stdcall __stdcall _fastcall __fastcall __thiscall // not keyword in BDS 2006 pascal _pascal __pascal _fortran //not keyword in Visual C++ 2005 __fortran //not keyword in Visual C++ 2005
اول ﺑﮕﺬارﻳﺪ ﻧﺤﻮهي ﺑﻪ ﻛﺎر ﮔﺮﻓﺘﻦ آنﻫﺎ را.آﻣﻮﺧﺘﻦ اﻳﻦ ﻛﻪ دﻗﻴﻘﺎ ﻫﺮ ﻛﺪام از اﻳﻦﻫﺎ ﭼﻪ ﻛﺎر ﻣﻲﻛﻨﻨﺪ ﻧﻴﺎز ﻧﻴﺴﺖ : را ﻧﺸﺎن ﻣﻲدﻫﺪcalling convention اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻧﺤﻮهي ﻣﺸﺨﺺ ﻛﺮدن.ﺑﺒﻴﻨﻴﻢ #include <conio.h> #include <iostream> using namespace std; int __stdcall f(int a); int main() { cout<< f(5) << endl; cout<< typeid(f).name(); _getch(); } int __stdcall f(int a) { return a * a;
341
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output (BDS 2006): 25 int (__stdcall *)(int) Output (Visual C++ 2005): 25 int __stdcall(int)
__ ﻣﻲﺷﻮدstdcall ﺑﻪ ﺟﺎي. ﺑﻪ ﻛﺎر ﻣﻲرودWindows __ ﺑﻴﺶ ﺗﺮ ﺑﺮاي ﺑﺮﻧﺎﻣﻪﻫﺎيstdcall ._ ﻫﻢ ﻧﻮﺷﺖ و ﺧﺮوﺟﻲ ﻓﺮﻗﻲ ﻧﻤﻲﻛﻨﺪstdcall :ﺣﺎﻻ ﺑﻪ اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻧﮕﺎه ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; int __cdecl f(int a); int main() { cout<< f(5) << endl; cout<< typeid(f).name(); _getch(); } int _cdecl f(int a) { return a * a; }
Output (BDS 2006): 25 int (*)(int) Output (Visual C++ 2005): 25 int __cdecl(int)
._ ﻳﺎ ﻫﺮ دو را ﺣﺬف ﻛﻨﻴﻢ ﺧﺮوﺟﻲ ﻫﻴﭻ ﺗﻐﻴﻴﺮي ﻧﻤﻲﻛﻨﺪcdecl __ ﻳﺎcdecl در اﻳﻦ ﺟﺎ اﮔﺮ در اﻳﻦ روش ﻗﺒﻞ از اﺳﻢ ﻳﻚ. اﺳﺘﻔﺎده ﻛﻨﺪC از روش اﺳﻢ ﮔﺬاريcompiler __ ﺑﺎﻋـﺚ ﻣﻲﺷﻮد ﻛﻪcdecl . ﮔﺬاﺷﺘﻪ ﻣﻲﺷﻮﻧﺪstack ﭘﺎراﻣﺘﺮﻫﺎ از راﺳﺖ ﺑﻪ ﭼﭗ روي. اﺿﺎﻓﻪ ﻣﻲﺷﻮدunderscore . ﮔﺬاﺷﺘﻪ ﻣﻲﺷﻮﻧﺪstack ﭘﺎراﻣﺘﺮﻫﺎ از راﺳﺖ ﺑﻪ ﭼﭗ روي. ِ اﺳﺘﺎﻧﺪارد اﺳﺖcall convention ،__stdcall
342
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﺮاي اﻃﻼﻋﺎت ﺑﻴﺶ ﺑﻪ helpدر compilerﺗﺎن ﻧﮕﺎه ﻛﻨﻴﺪ.
در ﺑﺨﺶﻫﺎي ﺑﻌﺪي ﺑﺎ ﺑﻌﻀﻲ ﺗﻮاﺑﻊ و ﻛﻼسﻫﺎي ﻣﻮﺟﻮد در ﭼﻨﺪ header fileﭘﺮﻛﺎرﺑﺮد آﺷﻨﺎ ﻣﻲﺷﻮﻳﻢ.
stdio.h
ﺷﺎﻳﺪ ﻣﻬﻢ ﺗﺮﻳﻦ ﺗﺎﺑﻊ در stdio.hﺗﺎﺑﻊ printfﺑﺎﺷﺪ printf .ﺗﺎﺑﻌﻲ اﺳﺖ ﺑﺎ ﺣﺪاﻗﻞ ﻳﻚ ﭘﺎراﻣﺘﺮ .در واﻗﻊ ﺗﻌﺪاد آرﮔﻮﻣﺎنﻫﺎي آن ﻣﻲﺗﻮاﻧﺪ ﻫﺮ ﻋﺪدي ﺑﺎﺷﺪ .ﺳﺎده ﺗﺮﻳﻦ ﻛﺎرﺑﺮد آن ﭼﺎپ ﻳﻚ رﺷﺘﻪ اﺳﺖ: >#include <conio.h >#include <stdio.h )(int main { ;)"printf("hello ;)(_getch }
Output: hello ﺑﺮاي اﻳﻦ ﻛﻪ ﻳﻚ ﻳﺎ ﭼﻨﺪ intرا ﺑﺎ printfﭼﺎپ ﻛﻨﻴﻢ در رﺷﺘﻪاي ﻛﻪ ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن اول ﺑﻪ printf
داده ﻣﻲﺷﻮد در ﻣﻜﺎن ﻣﻮرد ﻧﻈﺮ ﻣﻲﻧﻮﻳﺴﻴﻢ %iو ﺑﻌﺪ اﻋﺪادي را ﻛﻪ ﺑﺎﻳﺪ ﭼﺎپ ﺑﺸﻮﻧﺪ ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎنﻫﺎي ﺑﻌﺪي ﺑﻪ printfﻣﻲدﻫﻴﻢ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <stdio.h )(int main { int year ;= 2008 ; int month = 12 int day = 5 ; ;)printf("year: %i \nmonth: %i \nday: %i",year,month, day ;)(_getch }
Output: year: 2008 month: 12
343
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ day: 5
ﺑﻪ ﻃﻮر ﻛﻠﻲ اﻳﻦ ﺟﺪول را دارﻳﻢ: ﭼﻴﺰي ﻛﻪ ﭼﺎپ ﻣﻲﺷﻮد
1387 2008 2008 4294967295 2524 aab4 AAB4 Hello 1.234500e+01 1.234500E+01 12.345000 1234567.89000000 12.345 12.345
ﻣﻘﺪار
ﻧﻤﺎد در آرﮔﻮﻣﺎن اول
1387 2008 2008 -1 1364 43700 43700 ""hello 12.345 12.345 12.345 134567.89 12.345 12.345
%i %d %10d %u %o %x %X %s %e %E %f %6.8f %g %G
در اﻳﻦ ﺟﺪول %10d ،اﺑﺘﺪا ﺑﻪ اﻧﺪازهي 10ﻛﺎراﻛﺘﺮ ﺟﺎ ﺑﺎز ﻣﻲﻛﻨﺪ و ﺑﻌﺪ ﻋﺪد را در آن ﻣﻲﻧﻮﻳﺴﺪ .ﺑﻪ ﺟﺎي 10
ﻣﻲﺷﻮد ﻫﺮ ﻋﺪد دﻳﮕﺮي ﻫﻢ ﻗﺮار داد .در ،8 %6.8fﺗﻌﺪاد اﻋﺸﺎر را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ %u .ﺑﺮاي ﭼﺎپ ﻋﺪد ﺑﻪ ﺻﻮرت unsignedاﺳﺖ %o .ﺑﺮاي ﻣﺒﻨﺎي 8و %xﺑﺮاي ﻣﺒﻨﺎي 16اﺳﺖ .ﻣﻨﻈﻮر از 1.234500e+01و ،1.234500E+01ﻋﺪد .اﺳﺖ. ﺣﺎﻻ ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <stdio.h )(int main { "printf("this is a string: %s\n ;)"this is an int: %d\n","hello", 123 ;)(_getch }
Output: this is a string: hello this is an int: 123
اوﻟﻴﻦ آرﮔﻮﻣﺎن printfدر اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﻳﻦ اﺳﺖ: ""this is an int: %d\n","hello
ﻛﻪ compilerآن را ﺑﻪ
344
www.pupuol.com
""this is a string: %s\n
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ""this is a string: %s\nthis is an int: %d\n","hello
ﺗﺒﺪﻳﻞ ﻣﻲﻛﻨﺪ .ﺑﻪ ﻃﻮر ﻛﻠﻲ وﻗﺘﻲ دو رﺷﺘﻪي ﺛﺎﺑﺖ ِ literalرا ﻛﻨﺎر ﻫﻢ ﺑﻨﻮﻳﺴﻴﻢ compilerﺣﻴﻦ compile ﻛﺮدن ،آنﻫﺎ را ﺑﻪ ﻫﻢ ﻣﻲﭼﺴﺒﺎﻧﺪ .ﻣﺜﻼ ""bye
" "helloﺑﺎ " "hellobyeﻣﻌﺎدل اﺳﺖ.
دوﻣﻴﻦ آرﮔﻮﻣﺎن "hello" ،printfاﺳﺖ ﻛﻪ ﺑﻪ ﺟﺎي %sدر رﺷﺘﻪ آرﮔﻮﻣﺎن اول ﻗﺮار ﻣﻲﮔﻴﺮد .آرﮔﻮﻣﺎن ﺳﻮم ﻫﻢ ﺑﻪ ﺟﺎي %dﻗﺮار ﻣﻲﮔﻴﺮد. ﺑﺮاي اﻳﻦ ﻛﻪ ﺧﻮد %ﭼﺎپ ﻣﻲﺷﻮد ﻣﻲﺷﻮد در آرﮔﻮﻣﺎن اول ،printfاز %%اﺳﺘﻔﺎده ﻛﺮد. ﻣﺜﺎل زﻳﺮ ﺟﺎﻟﺐ اﺳﺖ: >#include <conio.h >#include <stdio.h )(int main { ;int i )for(i = 3; i < 20; i+=2 ;)printf("%*d\n",i,123 ;)(_getch }
Output: 123 123 123 123 123 123 123 123 123
در ;) V printf("%*d\n",i,123ار iﺑﻪ ﺟﺎي * ﻗﺮار ﻣﻲﮔﻴﺮد .ﻣﺜﻼ اﮔﺮ ﻣﻘﺪار 10 ،iﺑﺎﺷﺪ اﻧﮕﺎر ﻧﻮﺷﺘﻪاﻳﻢ ;).printf("%10d\n",123 ﺑﺎ وﺟﻮد coutدر C++ﻧﻴﺎز زﻳﺎدي ﺑﻪ printfاﺣﺴﺎس ﻧﻤﻲﺷﻮد .ﺗﺎﺑﻊ دﻳﮕﺮي در stdio.hﻫﺴﺖ ﺑﺎ ﻧﺎم sprintfﻛﻪ ﻣﺜﻞ printfﻋﻤﻞ ﻣﻲﻛﻨﺪ وﻟﻲ ﭼﻴﺰي را ﻛﻪ ﻗﺮار اﺳﺖ ﭼﺎپ ﻛﻨﺪ در ﻳﻚ رﺷﺘﻪ ﻣﻲﮔﺬارد .ﻣﺜﺎل زﻳﺮ ﻫﻤﻪ ﭼﻴﺰ را روﺷﻦ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std >#include <stdio.h
345
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(int main { ;]char c[100 "sprintf(c, "this is a string: %s\n ;)"this is an int: %d\n","hello", 123 ;cout<< c ;)(_getch }
Output: this is a string: hello this is an int: 123
ﭼﻴﺰي را ﻛﻪ printfﭼﺎپ ﻣﻲﻛﻨﺪ sprintf ،در cﻣﻲرﻳﺰد c cout .ﭼﺎپ ﺷﺪه .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺷﻮد >#include <stdio.h
را ﺣﺬف ﻛﺮد ﭼﻮن وﻗﺘﻲ iostream #includeﺷﻮد ﻧﻴﺎزي ﺑﻪ #includeﻛﺮدن stdio.hﻧﻴﺴﺖ. sprintfﻛﺎرﺑﺮد زﻳﺎدي دارد ﭼﻮن ﺗﻮاﻧﺎﻳﻲ ﺑﻲ ﻧﻈﻴﺮي در ﺳﺎﺧﺘﻦ رﺷﺘﻪ دارد و ﺑﻪ راﺣﺘﻲ ﻣﻲﺗﻮاﻧﺪ رﺷﺘﻪﻫﺎي ﭘﻴﭽﻴﺪهاي ﺑﺴﺎزد ) sprintﻳﻌﻨﻲ »ﺑﻪ ﺳﺮﻋﺖ دوﻳﺪن«
وﻟﻲ ﻫﻴﭻ رﺑﻄﻲ ﺑﻪ sprintfﻧﺪارد
(.
stdlib.h
ﻣﻬﻢ ﺗﺮﻳﻦ ﺗﺎﺑﻊ در ،stdlib.hﺗﺎﺑﻊ ;)*int system(const char
اﺳﺖ .اﻳﻦ ﺗﺎﺑﻊ ﺑﺎﻋﺚ اﺟﺮاي دﺳﺘﻮرات commandدر ﺳﻴﺴﺘﻢ ﻋﺎﻣﻞ ﻣﻲﺷﻮد .اﮔﺮ ﻧﻤﻲداﻧﻴﺪ دﺳﺘﻮرات commandﭼﻪ ﻫﺴﺘﻨﺪ ﺑﺎﻳﺪ ﺑﮕﻮﻳﻢ ﻫﻤﺎن دﺳﺘﻮرات ﺳﻴﺴﺘﻢ ﻋﺎﻣﻞ DOSﻫﺴﺘﻨﺪ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﺎ آنﻫﺎ آﺷﻨﺎ ﺑﺸﻮﻳﺪ در وﻳﻨﺪوز XPاﻳﻦ ﻣﺴﻴﺮ را ﻃﻲ ﻛﻨﻴﺪ: Start | All Programs | Accessories | Command Prompt
346
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
اﻳﻦ ﭘﻨﺠﺮه ﺑﺎز ﻣﻲﺷﻮد:
347
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
dir ﺗﺎﻳﭗ ﻛﻨﻴﺪ. را ﻓﺸﺎر ﺑﺪﻫﻴﺪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻧﻮﺷﺘﻪﻫﺎي ﭘﻨﺠﺮه ﭘﺎك ﻣﻲﺷﻮﻧﺪEnter وCLS در آن ﺗﺎﻳﭗ ﻛﻨﻴﺪ
اﻳﻦﻫﺎ ﻫﻤﺎن ﻓﺎﻳﻞﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ ﻛﻪ در ﻣﻜﺎﻧﻲ. را ﻓﺸﺎر ﺑﺪﻫﻴﺪ ﻣﻲﺑﻴﻨﻴﺪ اﺳﻢ ﻳﻚ ﺳﺮي ﻓﺎﻳﻞ ﻇﺎﻫﺮ ﻣﻲﺷﻮدEnter و . ﻫﺴﺘﻨﺪcommand دﺳﺘﻮرﻫﺎيdir وcls .ﻛﻪ »ﻣﻜﺎن ﺟﺎري« ﮔﻔﺘﻪ ﻣﻲﺷﻮد ﻗﺮار دارﻧﺪ : اﻳﻦ ﻣﺜﺎل را ﺑﺒﻴﻨﻴﺪ. ﻣﻲﺗﻮاﻧﻴﻢ اﻳﻦ دﺳﺘﻮرﻫﺎ را در ﺑﺮﻧﺎﻣﻪي ﺧﻮدﻣﺎن ﺑﻪ ﻛﺎر ﺑﮕﻴﺮﻳﻢsystem ﺑﺎ ﺗﺎﺑﻊ #include <stdlib.h> int main() { system("dir"); system("Pause"); }
Output (BDS 2006): Volume in drive G has no label. Volume Serial Number is ACBD-7F35 Directory of G:\Important Files\My Documents\Borland Studio Projects\Console\De bug_Build 02/28/2009 02/28/2009 02/28/2009 02/28/2009 02/13/2009 02/28/2009
04:25 04:25 04:25 04:25 04:01 04:25
AM AM AM AM AM AM
<DIR> <DIR> 12,288 2,568 5,788 196,608
348
www.pupuol.com
. .. Console.exe Console.obj Console.res Console.tds
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 12/09/2008 12/05/2008 12/05/2008 12/05/2008
05:06 PM 0 hello.txt 01:12 PM 58,119 object.obj 08:09 PM 57,881 Unit1.obj 02:47 AM 154,408 Unit2.obj 8 File(s) 487,660 bytes 2 Dir(s) 13,251,207,168 bytes free Press any key to continue . . . Output (Visual C++ 2005): Volume in drive C has no label. Volume Serial Number is 0061-53EB Directory of c:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\console\console 02/28/2009 04:27 AM <DIR> . 02/28/2009 04:27 AM <DIR> .. 02/28/2009 04:27 AM 154 console.cpp 11/20/2008 02:48 PM 663,806 console.i 02/22/2009 05:57 AM 3,581 console.vcproj 02/28/2009 04:28 AM 1,424 console.vcproj.CPPBUILDER.Administrator.u ser 02/28/2009 04:27 AM <DIR> Debug 12/05/2008 01:16 PM 123 object.cpp 12/05/2008 01:38 PM 123 object.h 02/05/2009 12:58 AM 51 Resource.rc 12/30/2008 01:54 PM 3 stdout 12/05/2008 08:16 PM 180 unit1.cpp 12/05/2008 08:16 PM 89 unit1.h 11/20/2008 02:49 PM 652,339 unit1.i 12/05/2008 02:56 AM 103 unit2.cpp 12/05/2008 11:53 AM 138 unit2.h 13 File(s) 1,322,114 bytes 3 Dir(s) 13,482,811,392 bytes free Press any key to continue . . .
دﺳﺘﻮرات._ را اﻧﺠﺎم ﻣﻲدﻫﺪgetch() ﺑﺎﻋـﺚ ﺗﻮﻗﻒ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺷﻮد و در واﻗﻊ ﻫﻤﺎن ﻛﺎرpause دﺳﺘﻮر . را ﻣﻲﺷﻮد ﺑﺎ ﺣﺮوف ﻛﻮﭼﻚ ﻳﺎ ﺑﺰرگ ﻧﻮﺷﺖcommand : ﻣﻲﺳﺎزدCommand Prompt ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﭼﻴﺰي ﺷﺒﻴﻪ ﺑﻪ #include <iostream> using namespace std;
349
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ )(int main { ;"char a[200] = "CLS )while(true { ;)system(a ;cin>> a (if && 'a[0] == 'e' && a[1] == 'x )'a[2] == 'i' && a[3] == 't ;break } }
Output: pause Press any key to continue . . . exit
Pauseرا ﺧﻮد ﻣﻦ ﻧﻮﺷﺘﻪام و Enterرا ﻓﺸﺎر دادهام exit , 3J 08=!/ .و ﺑﻌﺪ ﻓﺸﺎر Enterاﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﺗﻤﺎم ﻣﻲﺷﻮد .وﻗﺘﻲ #include ،iostreamﻣﻲﺷﻮد ،دﻳﮕﺮ ﻧﻴﺎزي ﺑﻪ stdlib.hﻧﻴﺴﺖ.
)ر! نه ' & )(main
ﺗﺎﺑﻊ )( mainﻣﻲﺗﻮاﻧﺪ دو ﭘﺎراﻣﺘﺮ داﺷﺘﻪ ﺑﺎﺷﺪ: ;)][int main(int a, char* c
ﻣﻲﺷﻮد ﻣﻮﻗﻊ اﺟﺮاي ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﺑﻪ آن آرﮔﻮﻣﺎن داد .ﻣﺜﻼ اﮔﺮ bc.exeﻳﻚ ﺑﺮﻧﺎﻣﻪ در دراﻳﻮ C:ﺑﺎﺷﺪ ﺑﺮاي اﺟﺮاي آن در ) Command Promptﻛﻪ در ﺑﺨﺶ ﻗﺒﻞ ﮔﻔﺘﻢ ﭼﻪ ﻃﻮر آن را ﺑﺎز ﻛﻨﻴﺪ( ﻣﻲﺷﻮد ﻧﻮﺷﺖ: c:\bc.exe hello bye
اﻳﻦ ﺧﻂ ﺑﺎﻋﺚ اﺟﺮاي ﺑﺮﻧﺎﻣﻪي bc.exeﻣﻲﺷﻮد و helloو byeدو آرﮔﻮﻣﺎن ﺑﺮاي ﺑﺮﻧﺎﻣﻪي bc.exeﻫﺴﺘﻨﺪ ﻛﻪ ﻣﻮﻗﻊ اﺟﺮا ﺑﻪ آن داده ﻣﻲﺷﻮﻧﺪ .در واﻗﻊ اﻳﻦ آرﮔﻮﻣﺎن ﺑﻪ ﭘﺎراﻣﺘﺮﻫﺎي ﺗﺎﺑﻊ )( mainداده ﻣﻲﺷﻮﻧﺪ. در compilerﺗﺎن ﺑﺮﻧﺎﻣﻪي زﻳﺮ را اﺟﺮا ﻛﻨﻴﺪ ﺗﺎ از آن ﻓﺎﻳﻞ console.exeدرﺳﺖ ﺑﺸﻮد: >#include <iostream ;using namespace std )][int main(int a, char* c {
350
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)"system("cls cout<< a ;<< endl ;cout<< c[0] << endl ;cout<< c[1] << endl ;cout<< c[2] << endl ;cout<< c[3] << endl ;"cout<< "bye\n ;)"system("pause }
در BDS 2006و در Visual C++ 2005اﺟﺮاي آن ﻳﻚ exceptionﺑﻪ وﺟﻮد ﻣﻲآورد .آن را ﻧﺎدﻳﺪه ﺑﮕﻴﺮﻳﺪ .ﭼﻮن ﻗﺼﺪ ﻣﺎ ﻓﻘﻂ اﻳﺠﺎد ﻓﺎﻳﻞ console.exeاز ﺑﺮﻧﺎﻣﻪ اﺳﺖ. ﻓﺎﻳﻞ console.exeرا ﻣﺴﺘﻘﻴﻤﺎ در دراﻳﻮ Cﻳﻌﻨﻲ در آدرس \ C:ﻛﭙﻲ ﻛﻨﻴﺪ .ﺣﺎﻻ Command Promptرا ﺑﺎز ﻛﻨﻴﺪ و در آن اﻳﻦ را ﺑﻨﻮﻳﺴﻴﺪ: c:\console.exe yek do se
و Enterرا ﻓﺸﺎر ﺑﺪﻫﻴﺪ .ﭘﻨﺠﺮهي Command Promptﺑﻪ اﻳﻦ ﺷﻜﻞ ﻣﻲﺷﻮد:
ﭘﺲ در ;)][int main(int a, char* c
yekآرﮔﻮﻣﺎن اول اﺳﺖ do ،آرﮔﻮﻣﺎن دوم اﺳﺖ و seآرﮔﻮﻣﺎن ﺳﻮم c[1] .ﺑﻪ آرﮔﻮﻣﺎن اول c[2] ،ﺑﻪ آرﮔﻮﻣﺎن دوم و ] c[3ﺑﻪ آرﮔﻮﻣﺎن ﺳﻮم اﺷﺎره دارد c[0] .اﺳﻢ و آدرس ﻓﺎﻳﻞ اﺟﺮاﻳﻲ ﻳﻌﻨﻲ console.exeرا دارد .ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ]c[iﻫﺎ رﺷﺘﻪ ﻫﺴﺘﻨﺪ a .ﻣﻘﺪار 4دارد ﻛﻪ ﻳﻜﻲ ﺑﻴﺶ ﺗﺮ ﺗﻌﺪاد آرﮔﻮﻣﺎنﻫﺎﺳﺖ. 351
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در BDS 2006ﻣﻲﺷﻮد در ﻣﺤﻴﻂ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ ) (IDEآرﮔﻮﻣﺎنﻫﺎ را ﺑﻪ ﺑﺮﻧﺎﻣﻪ داد ﺗﺎ ﻛﺎر ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ راﺣﺖ ﺗﺮ ﺑﺎﺷﺪ .ﺑﺮاي اﻳﻦ ﻛﺎ در BDS 2006اﻳﻦ ﻣﺴﻴﺮ را ﻃﻲ ﻛﻨﻴﺪ: …Run | Parameters اﻳﻦ ﭘﻨﺠﺮه ﺑﺎز ﻣﻲﺷﻮد:
در ﻗﺴﻤﺖ Parametersﻣﻲﺗﻮاﻧﻴﻢ آرﮔﻮﻣﺎن را وارد ﻛﻨﻴﻢ. ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ آرﮔﻮﻣﺎنﻫﺎ ﺑﺎ ﻳﻚ ﺟﺎي ﺧﺎﻟﻲ ﺟﺪا ﻣﻲﺷﻮﻧﺪ .ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﺳﻢ و آدرس و ﻫﻤﻪي آرﮔﻮﻣﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪ را ﭼﺎپ ﻣﻲﻛﻨﺪ .ﺑﻪ آن آرﮔﻮﻣﺎنﻫﺎي yek do se char panj 25
را دادهام و آن را اﺟﺮا ﻛﺮدهام: >#include <iostream
352
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; int main(int argc, char *argv[]) { for(int i = 0; i < argc; i++) cout<< argv[i] << endl; system("pause"); }
Output: G:\Important Files\My Documents\Borland Studio Projects\Console\Debug_Build\Cons ole.exe yek do se char panj 25 Press any key to continue . . .
. ﺑﺴﻴﺎر ﻣﺘﺪاول اﺳﺖmain() ﺑﺮاي ﭘﺎراﻣﺘﺮ دومargv ﺑﺮاي ﭘﺎراﻣﺘﺮ اول و اﺳﻢargc اﺳﻢ
stdarg.h
( ﺗﻌﺪاد ﻣﺘﻐﻴﺮﻫﺎي )ﻳﻌﻨﻲ ﭘﺎراﻣﺘﺮﻫﺎي، ﻛﻤﻲ ﺗﻌﺠﺐ ﻛﺮده ﺑﺎﺷﻴﺪ ﭼﻮن ﺗﺎ ﻗﺒﻞ از آنprintf ﺷﺎﻳﺪ ﺑﺎ دﻳﺪن ﺗﺎﺑﻊ ﻣﺎ ﻫﻢ ﻣﻲﺗﻮاﻧﻴﻢstdarg.h ﺑﺎ اﺳﺘﻔﺎده از. ﻧﺎﻣﺸﺨﺺ اﺳﺖprintf ﺗﺎﺑﻊﻫﺎ ﻣﺸﺨﺺ ﺑﻮد اﻣﺎ ﺗﻌﺪاد ﻣﺘﻐﻴﺮﻫﺎي : ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ.ﻫﻤﭽﻴﻦ ﺗﺎﺑﻊﻫﺎﻳﻲ ﺑﻨﻮﻳﺴﻴﻢ #include <conio.h> #include <stdio.h> #include <stdarg.h> void Print(int a, ...) { va_list marker; va_start(marker,a); int* p = (int*)marker; int i = 0; printf("%d\n",a); for(i = 0; p[i] != 0; i++) printf("%d\n",p[i]); } int main() {
353
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)Print(1,2,3,4,5,6,7,8,9,0 ;)(_getch }
Output: 1 2 3 4 5 6 7 8 9
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﺎﺑﻊ )( Printﻫﻤﻪي آرﮔﻮﻣﺎنﻫﺎي ﺧﻮد را ﻛﻪ ﻗﺮار اﺳﺖ ﻧﻮع intداﺷﺘﻪ ﺑﺎﺷﻨﺪ ﭼﺎپ ﻣﻲﻛﻨﺪ. ﻣﺘﻐﻴﺮ ) markerﻛﻪ در واﻗﻊ ﻳﻚ pointerاﺳﺖ( از ﻧﻮع va_listﺗﻌﺮﻳﻒ ﺷﺪه و ﻗﺮار اﺳﺖ ﺑﻪ ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎ ﺑﻪ ﺟﺰ ﭘﺎراﻣﺘﺮ اول اﺷﺎره داﺷﺘﻪ ﺑﺎﺷﺪ va_start .ﻳﻚ ﻣﺎﻛﺮو اﺳﺖ و ﻛﺎر آن اﻳﻦ اﺳﺖ ﻛﻪ markerرا ﻃﻮري ﺗﻐﻴﻴﺮ ﺑﺪﻫﺪ ﻛﻪ ﺑﻪ ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎي ﻣﺸﺨﺺ ﻧﺸﺪه اﺷﺎره ﻛﻨﺪ .اوﻟﻴﻦ آرﮔﻮﻣﺎن آن marker
و دوﻣﻴﻦ آرﮔﻮﻣﺎن آن )اﺳﻢ( آﺧﺮﻳﻦ ﭘﺎراﻣﺘﺮ ﻣﺸﺨﺺ ﺷﺪه اﺳﺖ .ﺑﻌﺪ از اﺟﺮاي آن marker ،ﺑﻪ ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎي ﻣﺸﺨﺺ ﻧﺸﺪه اﺷﺎره دارد .ﭼﻮن ﻗﺮار اﺳﺖ ﭘﺎراﻣﺘﺮﻫﺎ ،ﻧﻮع intداﺷﺘﻪ ﺑﺎﺷﻨﺪ pرا ﺑﺎ ﻧﻮع * intﻣﻌﺮﻓﻲ ﻛﺮدهام و markerرا ﺑﻪ آن ﻧﺴﺒﺖ دادهام .ﻣﻘﺎدﻳﺮ ﭘﺎراﻣﺘﺮﻫﺎ )ﻛﻪ ﻗﺮار اﺳﺖ ﺑﻪ ﺻﻔﺮ ﺧﺘﻢ ﺷﻮﻧﺪ( ﭼﺎپ ﻣﻲﺷﻮﻧﺪ. راه رﺳﻤﻲ ﺗﺮي ﻫﻢ ﺑﺮاي اﺳﺘﻔﺎده از markerﻫﺴﺖ و آن ﺑﻪ ﻛﺎر ﺑﺮدن ﻣﺎﻛﺮوي )( va_argاﺳﺖ .ﻣﺜﺎل ﻗﺒﻞ را ﻣﻲﺷﻮد ﺑﻪ اﻳﻦ ﺻﻮرت ﺗﻐﻴﻴﺮ داد: >#include <conio.h >#include <stdio.h >#include <stdarg.h )void Print(int a, ... { ;va_list marker ;)va_start(marker,a ;int i ))for(i = a; i != 0; i = va_arg(marker,int ;)printf("%d\n",i } )(int main { ;)Print(1,2,3,4,5,6,7,8,9,0 ;)(_getch }
Output:
354
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 1 2 3 4 5 6 7 8 9
در واﻗﻊ اﮔﺮ markerرا ﺑﻪ ﻋﻨﻮان ﻟﻴﺴﺖ ﭘﺎراﻣﺘﺮﻫﺎ ﺑﮕﻴﺮﻳﻢ ﻣﺜﻼ ﻟﻴﺴﺖ زﻳﺮ 2,3,4,5,6,7,8,9,0
ﺑﺎ ﻧﻮﺷﺘﻦ )i = va_arg(marker,int
ﭼﻬﺎر ﺑﺎﻳﺖ اول ﻟﻴﺴﺖ ﺟﺪا ﻣﻲﺷﻮد و در iﻗﺮار ﻣﻲﮔﻴﺮد و ﺑﻌﺪ اﺟﺮاي اﻳﻦ دﺳﺘﻮر marker ،ﺑﻪ اﻳﻦ ﻟﻴﺴﺖ: 3,4,5,6,7,8,9,0
اﺷﺎره دارد .آرﮔﻮﻣﺎن دوم )( va_argﻧﻮع ﻣﺘﻐﻴﺮ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ .در اﻳﻦ ﻣﺜﺎل ﻓﺮض ﺑﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﻣﺘﻐﻴﺮﻫﺎﻳﻲ ﻛﻪ ﺑﻪ )( Printداده ﻣﻲﺷﻮﻧﺪ ،ﻫﻤﻪ ﻧﻮع intداﺷﺘﻪ ﺑﺎﺷﻨﺪ .ﺣﺎل ﺑﻴﺎﻳﻴﺪ ﺗﺎﺑﻌﻲ ﺑﻨﻮﻳﺴﻴﻢ ﻛﻪ ﻣﺸﺎﺑﻪ printfﻋﻤﻞ ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <iostream ;using namespace std >#include <stdarg.h )void f(char* c,... { ;va_list marker ;)va_start(marker,c ;unsigned r = 0 )while(true { )]switch(c[r++ { case 'd': { ;)double d = va_arg(marker,double ;cout<< d << endl ;break } case 'i': { ;)int i = va_arg(marker,int ;cout<< i << endl ;break } default:
355
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;return } } } )(int main { ;)f("iddii", 1, 2.3, 4.56, 7, 8 ;)(_getch }
Output: 1 2.3 4.56 7 8 در اﻳﻦ ﺑﺮﻧﺎﻣﻪ آرﮔﻮﻣﺎن اول ﺗﺎﺑﻊ ،ﻳﻚ رﺷﺘﻪ اﺳﺖ ﻛﻪ ﺑﺎﻳﺪ ﺗﻨﻬﺎ ﺷﺎﻣﻞ ﻛﺎراﻛﺘﺮﻫﺎي ' 'iﺑﻪ ﻧﺸﺎﻧﻪي intو ''d ﺑﻪ ﻧﺸﺎﻧﻪي doubleﺑﺎﺷﺪ .آرﮔﻮﻣﺎنﻫﺎي ﺑﻌﺪي ﺗﺎﺑﻊ ﺑﺎﻳﺪ ﻃﺒﻖ آن ﭼﻪ در رﺷﺘﻪ ﻫﺴﺖ از ﻧﻮع intﻳﺎ double
ﺑﺎﺷﻨﺪ f() .اﻳﻦ آرﮔﻮﻣﺎنﻫﺎ را ﭼﺎپ ﻣﻲﻛﻨﺪ .ﻣﻲﺷﻮد ﺑﻪ ﺟﺎي )void f(char* c,...
ﻧﻮﺷﺖ: )void f(char* c...
ﻛﻪ در آن » «,ﺣﺬف ﺷﺪه .در BDS 2006در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻣﻲﺷﻮد >#include <stdarg.h
را ﺣﺬف ﻛﺮد. آﻳﺎ ﻣﻲﺷﻮد ﺗﺎﺑﻌﻲ ﺑﺎ prototypeزﻳﺮ داﺷﺖ؟ ;)void f(...
در )( va_startﭘﺎراﻣﺘﺮ دوم ﺑﺎﻳﺪ ﻧﺎم آﺧﺮﻳﻦ ﭘﺎراﻣﺘﺮ ﻣﺸﺨﺺ ﺑﺎﺷﺪ وﻟﻲ اﻳﻦ ﺟﺎ ﻫﻴﭻ ﭘﺎراﻣﺘﺮ ﻣﺸﺨﺼﻲ ﻧﺪارﻳﻢ ﺑﻨﺎﺑﺮاﻳﻦ ﻧﻤﻲﺗﻮاﻧﻴﻢ از )( va_startاﺳﺘﻔﺎده ﻛﻨﻴﻢ .در ﻫﺮ ﺣﺎل ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﺟﺮا ﻣﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std )void f(... { ;"cout<< "hello\n } )(int main { ;)f(1,2
356
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ;)(cout<< typeid(f).name ;)(_getch }
Output (BDS 2006): hello )void (*)(... Output (Visual C++ 2005): hello )void __cdecl(...
در compilerﻫﺎي ﻗﺪﻳﻤﻲ ﺗﺮ ،Borlandدر [ ! 0.رد!6 ,اT , * .8G/ر !+ن دوم )( & ،va_startﺑﮕﺬارﻳﻢ.
map
mapﻳﻚ header fileﻣﺨﺼﻮص C++اﺳﺖ و در Cﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﻴﺴﺖ .ﻳﺎد ﮔﺮﻓﺘﻦ header fileﻫﺎي C++ﺳﺨﺖ ﺗﺮ از header fileﻫﺎي Cاﺳﺖ ﭼﻮن ﺑﺎ اﺳﺘﻔﺎده از ﻛﻼسﻫﺎ ﻧﻮﺷﺘﻪ ﺷﺪهاﻧﺪ .ﺑﻪ ﺷﻜﻞ زﻳﺮ ﻧﮕﺎه ﻛﻨﻴﺪ:
در اﻳﻦ ﺗﺼﻮﻳﺮ 1 ،ﺑﻪ 30ﻧﮕﺎﺷﺘﻪ ﺷﺪه .ﻫﻤﻴﻦ ﻃﻮر 2ﺑﻪ 10و 3ﺑﻪ 20ﻧﮕﺎﺷﺘﻪ ﺷﺪه .ﺑﻪ ﺟﺎي ﺑﻪ ﻛﺎر ﺑﺮدن ﺷﻜﻞ ﺑﺎﻻ ﻣﻲﺗﻮاﻧﻴﻢ ﺑﻨﻮﻳﺴﻴﻢ: )(1,30 )(2,10 )(3,20
357
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺑﻪ ﻣﻌﻨﻲ ﻧﮕﺎﺷﺖmapping . ﻧﺎم دارﻧﺪ( ﻳﻚ ﻧﮕﺎﺷﺖ ﻣﻲﮔﻮﻳﻴﻢpair ﺑﻪ ﻣﺠﻤﻮﻋﻪي اﻳﻦﻫﺎ )ﻛﻪ دوﺗﺎﻳﻲ ﻣﺮﺗﺐ ﻳﺎ ﻛﻪ درmap در ﻣﺜﺎل زﻳﺮ از ﻛﻼس. از ﻫﻤﻴﻦ ﻛﻠﻤﻪ ﮔﺮﻓﺘﻪ ﺷﺪهmap ِ header file اﺳﺖ و اﺣﺘﻤﺎﻻ اﺳﻢ ﻣﻌﺮﻓﻲ ﺷﺪه اﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ و ﻧﮕﺎﺷﺖmap ِ header file (1,10.1) (2,20.2) (3,30.3)
-ﻫﺎي( اﻳﻦ ﻧﮕﺎﺷﺖ را ﭼﺎپ ﻣﻲpair =) ِ ﺟﺎي ﻣﻲدﻫﻴﻢ و ﺑﻌﺪ دوﺗﺎﻳﻲﻫﺎي ﻣﺮﺗﺐmap از ﻧﻮعobject را در ﻳﻚ :ﻛﻨﻴﻢ #include <conio.h> #include <iostream> using namespace std; #include <map> typedef map<int,double> typedef MAP::iterator typedef pair<int,double>
MAP; ITERATOR; PAIR;
int main() { MAP m; ITERATOR it; m.insert(PAIR(1, 10.1)); m.insert(PAIR(2, 20.2)); m.insert(PAIR(3, 30.3)); for(it = m.begin(); it != m.end(); it++) { cout << it -> first << " "; cout << it -> second << "\n"; } _getch(); }
Output: 1 10.1 2 20.2 3 30.3
ﻫﺎيpair ﻳﻚ ﻧﮕﺎﺷﺖ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ ﻛﻪmap<int,double> . اﺳﺖtemplate در واﻗﻊ ﻳﻚmap در اﻳﻦ ﻣﺜﺎل ﺑﺮاي راﺣﺘﻲ. داردdouble ﻧﻮعy وint ﻧﻮعx (ﻫﺴﺘﻨﺪ ﻛﻪx,y) آن ﺑﻪ ﺷﻜﻞ . ﻧﺸﺎن دادهامMAP را ﺑﺎmap<int,double> ( از ﻧﻮع آنobject آن )ﻳﻌﻨﻲ ﻫﺮinstance ﻫﻢ در واﻗﻊ ﻛﻼﺳﻲ اﺳﺖ ﻛﻪ ﻫﺮpair<int,double> . داردdouble ﻧﻮعy وint ﻧﻮعx (اﺳﺖ ﻛﻪx,y) ﺑﻪ ﺷﻜﻞpair ﻧﻤﺎﻳﻨﺪهي ﻳﻚ 358
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ map<int,double>::iteratorﻳﻚ ﻧﻮع اﺳﺖ ﻛﻪ ﻗﺮار اﺳﺖ ﻫﺮ instanceآن ﺑﻪ ﻳﻚ pairدر ﻧﮕﺎﺷﺖ اﺷﺎره ﻛﻨﺪ .اﻳﻦ ﻧﻮع را ﺑﺎ ITERATORﻧﺸﺎن دادهام. در ﺗﺎﺑﻊ )( mainﻳﻚ ﻧﮕﺎﺷﺖ ﺑﺎ ﻧﺎم mﺗﻌﺮﻳﻒ ﻛﺮدهام ﻛﻪ ﻗﺮار اﺳﺖ pairﻫﺎﻳﻲ ﺑﻪ ﺷﻜﻞ )(x,yداﺷﺘﻪ ﺑﺎﺷﺪ ﻛﻪ در آن xﻧﻮع intو yﻧﻮع doubleدارد. insertﻳﻚ methodاﺳﺖ ﻛﻪ ﻛﺎر آن اﺿﺎﻓﻪ ﻛﺮدن ﻳﻚ pairﺑﻪ ﻧﮕﺎﺷﺖ اﺳﺖpair .ﻫﺎي )(1,10.1 )(2,20.2 )(3,30.3
ﺑﺎ اﺳﺘﻔﺎده از insertﺑﻪ ﻧﮕﺎﺷﺖ mاﺿﺎﻓﻪ ﺷﺪهاﻧﺪ .ﺑﻌﺪ از اﺟﺮاي )( it ،it = m.beginﺑﻪ اوﻟﻴﻦ pair اﺷﺎره ﻣﻲﻛﻨﺪ .ﺑﺎ اﺟﺮاي it ،it++ﺑﻪ pairﺑﻌﺪي اﺷﺎره ﻣﻲﻛﻨﺪ .در ﺣﻠﻘﻪي forوﻗﺘﻲ itﺑﺮاﺑﺮ ﺷﺪ ﺑﺎ )( m.endﻫﻤﻪي pairﻫﺎ ﭼﺎپ ﺷﺪهاﻧﺪ .وﻗﺘﻲ itﺑﻪ (x,y) ِ pairاﺷﺎره دارد it-> first ،ﺑﺮاﺑﺮ ﺑﺎ x
و it->secondﺑﺮاﺑﺮ ﺑﺎ yاﺳﺖ. اﮔﺮ mﻳﻚ ﻧﮕﺎﺷﺖ ﺑﺎﺷﺪ و ) (x,yدر mﺑﺎﺷﺪ ﻣﻲﮔﻮﻳﻴﻢ x ،mرا ﺑﻪ yﻣﻲﻧﮕﺎرد .از وﻳﮋﮔﻲﻫﺎي ﻳﻚ ﻧﮕﺎﺷﺖ اﻳﻦ اﺳﺖ ﻛﻪ ﻳﻚ ﭼﻴﺰ را ﺑﻪ ﭼﻨﺪ ﭼﻴﺰ ﻧﻤﻲﻧﮕﺎرد ﻳﻌﻨﻲ اﮔﺮ xرا ﺑﻪ yﺑﻨﮕﺎرد x ،را ﺑﻪ ﻫﻴﭻ ﭼﻴﺰ دﻳﮕﺮي ﻧﻤﻲﻧﮕﺎرد .ﻣﺜﺎل ﻗﺒﻞ را ﻛﻤﻲ ﺗﻐﻴﻴﺮ ﻣﻲدﻫﻢ ﺗﺎ اﻳﻦ ﻣﻄﺐ روﺷﻦ ﺑﺸﻮد: >#include <conio.h >#include <iostream ;using namespace std >#include <map ;MAP ;ITERATOR ;PAIR
>typedef map<int,double typedef MAP::iterator >typedef pair<int,double )(int main { ;MAP m ;ITERATOR it ;))m.insert(PAIR(1, 10.1 ;))m.insert(PAIR(2, 20.2 ;))m.insert(PAIR(1, 30.3
)for (it = m.begin(); it != m.end(); it++ { ;" " << cout << it -> first ;"cout << it -> second << "\n } ;)(_getch }
359
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: 1 10.1 2 20.2
اﻳﻦ ﻣﺜﺎل دﻗﻴﻘﺎ ﻣﺎﻧﻨﺪ ﻣﺜﺎل ﻗﺒﻞ اﺳﺖ ﺑﺎ اﻳﻦ ﻓﺮق ﻛﻪ در اﻳﻦ ﺟﺎ ﺑﻪ ﺟﺎي )(3,30.1ﺳﻌﻲ ﻛﺮدهام ) (1,30.1را ﺑﻪ ﻧﮕﺎﺷﺖ mوارد ﻛﻨﻢ .اﻣﺎ ) (1,30.1ﺑﻪ mوارد ﻧﺸﺪه اﺳﺖ ﭼﻮن اﮔﺮ وارد ﻣﻲﺷﺪ 1ﺑﻪ دو ﻋﺪد ﻣﺨﺘﻠﻒ ﻧﮕﺎﺷﺘﻪ ﻣﻲﺷﺪ ﻛﻪ اﻳﻦ اﻣﻜﺎن ﻧﺪارد. ﻣﺴﺄﻟﻪي دﻳﮕﺮ )ﺷﺎﻳﺪ ﻫﻢ ﻣﺴﺌﻠﻪي دﻳﮕﺮ
( ﺗﺮﺗﻴﺐ pairﻫﺎ در ﻳﻚ ﻧﮕﺎﺷﺖ اﺳﺖ: >#include <conio.h >#include <iostream ;using namespace std >#include <map ;MAP ;ITERATOR ;PAIR
>typedef map<int,int typedef MAP::iterator >typedef pair<int,int )(int main { ;MAP m ;ITERATOR it ;))m.insert(PAIR(21, 10 ;))m.insert(PAIR(3, 10 ;))m.insert(PAIR(7, 30
)for (it = m.begin(); it != m.end(); it++ { ;" " << cout << it -> first ;"cout << it -> second << "\n } ;)(_getch }
Output: 3 10 7 30 21 10
در ﻳﻚ pairﻣﺜﻞ ) (x,yﺑﻪ xﻣﺆﻟﻔﻪي اول و ﺑﻪ yﻣﺆﻟﻔﻪي دوم ﻣﻲﮔﻮﻳﻴﻢ )ﻣﺆﻟﻔﻪ را /mo alle fe/ﺑﺎ ﻳﻚ ﺗﺸﺪﻳﺪ روي »ل« ﺗﻠﻔﻆ ﻛﻨﻴﺪ
(pair .ﻫﺎي ﻳﻚ ﻧﮕﺎﺷﺖ ﺑﻪ ﻃﻮر ﺧﻮدﻛﺎر ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﻣﺆﻟﻔﻪي اول از ﻛﻮﭼﻚ
ﺑﻪ ﺑﺰرگ ﻣﺮﺗﺐ ﻣﻲﺷﻮﻧﺪ .ﺑﺮﻧﺎﻣﻪي ﺑﺎﻻ اﻳﻦ را ﻧﺸﺎن ﻣﻲدﻫﺪ .در ﺿﻤﻦ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ) (3,10و ) (21,10ﺑﺎ اﻳﻦ ﻛﻪ ﻣﺆﻟﻔﻪي دوم ﻣﺴﺎوي دارﻧﺪ ﻫﺮ دو در mﻫﺴﺘﻨﺪ .ﻧﮕﺎﺷﺖ ﻧﻤﻲﺗﻮاﻧﺪ ﻳﻚ ﭼﻴﺰ را ﺑﻪ ﭼﻨﺪ ﭼﻴﺰ ﺑﻨﮕﺎرد اﻣﺎ ﻣﻲ- ﺗﻮاﻧﺪ ﭼﻨﺪ ﭼﻴﺰ را ﺑﻪ ﻳﻚ ﭼﻴﺰ ﺑﻨﮕﺎرد در اﻳﻦ ﺟﺎ 3و 21ﻫﺮ دو ﺑﻪ 10ﻧﮕﺎﺷﺘﻪ ﺷﺪهاﻧﺪ.
360
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻣﻤﻜﻦ اﺳﺖ ﺑﺨﻮاﻫﻴﻢ ﻳﻚ ﺗﺮﺗﻴﺐ دﻳﮕﺮ ﺑﺮاي ﻣﺮﺗﺐ ﻛﺮدن داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻳﻌﻨﻲ pairﻫﺎ ﺑﺎ ﻗﺎﻋﺪهي دﻳﮕﺮي ﻣﺮﺗﺐ ﺑﺸﻮﻧﺪ ﻣﺜﻼ اﮔﺮ ﻣﺆﻟﻔﻪﻫﺎي اول ِ pairﻫﺎ ﻋﺪد ﺑﺎﺷﻨﺪ ،ﻣﻤﻜﻦ اﺳﺖ ﺑﺨﻮاﻫﻴﻢ اول اﻋﺪاد زوج ﻣﺮﺗﺐ و ﭼﺎپ ﺑﺸﻮﻧﺪ و ﺑﻌﺪ اﻋﺪاد ﻓﺮد .ﺑﺮاي اﻳﻦ ﻛﻪ ﺑﺘﻮاﻧﻴﻢ ﻫﻤﭽﻴﻦ ﺗﺮﺗﻴﺒﻲ را ﺑﻪ mapﺑﺸﻨﺎﺳﺎﻧﻴﻢ ﺑﺎﻳﺪ ﺑﺎ ِ template >template<class Type >struct less : public binary_function <Type, Type, bool { bool operator()(const Type& _Left, ;const Type& _Right) const ;}
آﺷﻨﺎ ﺑﺸﻮﻳﻢ ﻛﻪ ﺑﺎ #includeﻛﺮدن mapدر اﺧﺘﻴﺎر ﻣﺎ ﻗﺮار ﻣﻲﮔﻴﺮد .ﻗﺒﻞ از آﺷﻨﺎﻳﻲ ﺑﺎ ﻛﺎرﺑﺮد less؛ ﺑﻪ ﻧﺤﻮه- ي ﺗﻌﺮﻳﻒ lessﺗﻮﺟﻪ ﻛﻨﻴﺪ less ِ template .از ﻛﻼﺳﻲ ﺳﺎﺧﺘﻪ ﺷﺪه ﻛﻪ ﺧﻮدش از ﻛﻼﺳﻲ دﻳﮕﺮ derive ﺷﺪه )اﻟﺒﺘﻪ ﻣﻨﻈﻮر از ﻛﻼس ﻫﻤﺎن structاﺳﺖ!(. در () ،lessﻳﻚ operatorﺑﺎ دو ﭘﺎراﻣﺘﺮ اﺳﺖ .ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ: >#include <conio.h >#include <iostream ;using namespace std >#include <map )(int main { ;less<int> A ;cout<< A(3,2) << endl ;cout<< A(2,3) << endl ;)(_getch }
Output: 0 1
) A(3,2ﺑﺮاﺑﺮ ﺑﺎ falseاﺳﺖ ﭼﻮن 3ﻛﻢ ﺗﺮ از 2ﻧﻴﺴﺖ و ) A(2,3ﺑﺮاﺑﺮ ﺑﺎ trueاﺳﺖ ﭼﻮن 2ﻛﻢ ﺗﺮ از 3اﺳﺖ .ﻋﻠﺖ اﻳﻦ ﻛﻪ compilerﻛﻢ ﺗﺮ ﺑﻮدن ِ 2از 3را ﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪ اﻳﻦ اﺳﺖ ﻛﻪ 2و 3ﻧﻮع intدارﻧﺪ و ﺑﺮاي intﺗﻌﺮﻳﻒ )( ) operatorﻳﺎ ﺗﻌﺮﻳﻒ ﻋﻤﻠﮕﺮﻫﺎﻳﻲ ﻣﺜﻞ »ﻛﻮﭼﻚ ﺗﺮ ﻣﺴﺎوي«( ﻣﻮﺟﻮد اﺳﺖ. ﻣﺜﺎل ﻗﺒﻞ را ﻃﻮري ﺗﻐﻴﻴﺮ ﻣﻲدﻫﻴﻢ ﻛﻪ ﻧﺘﻴﺠﻪ ﺑﺮ ﻋﻜﺲ ﺷﻮد ﻳﻌﻨﻲ 3ﻛﻢ ﺗﺮ از 2در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﻮد و 2ﺑﻴﺶ ﺗﺮ از ) !!!3
اﻳﻦ ﭼﻴﺰﻫﺎ ﻓﻘﻂ از ﻧﻈﺮ رﻳﺎﺿﻴﺪاﻧﺎن ﻣﺤﺘﺮم ﻋﺎدي ﺑﻪ ﻧﻈﺮ ﻣﻲرﺳﺪ(: >#include <conio.h >#include <iostream
361
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ using namespace std; #include <map> class Less : public less<int> { public: bool operator () (int x, int y) { return y < x; } }; int main() { Less A; cout<< A(3,2) << endl; cout<< A(2,3) << endl; _getch(); }
Output: 1 0
ﺣﺎﻻ ﺑﺎ اﺳﺘﻔﺎده.y<x ﻧﻮﺷﺘﻪامx<y ﺑﻪ ﺟﺎيoperator () ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻧﺘﺎﻳﺞ ﺑﺮ ﻋﻜﺲ اﺳﺖ ﭼﻮن در ﺗﻌﺮﻳﻒ :ﻫﺎ را از ﺑﺰرگ ﺑﻪ ﻛﻮﭼﻚ ﻣﺮﺗﺐ ﻛﻨﺪpair ﻳﻚ ﻧﮕﺎﺷﺖ را وادار ﻣﻲﻛﻨﻴﻢ ﻛﻪ،از اﻳﻦ #include <conio.h> #include <iostream> using namespace std; #include <map> class Less : public less<int> { public: bool operator () (int x, int y) { return y < x; } }; typedef map<int,int,Less> typedef MAP::iterator typedef pair<int,int>
MAP; ITERATOR; PAIR;
int main() { MAP m; ITERATOR it; m.insert(PAIR(21, 10)); m.insert(PAIR(3, 10)); m.insert(PAIR(7, 30)); for (it = m.begin(); it != m.end(); it++)
362
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { cout << it -> first << " "; cout << it -> second << "\n"; } _getch(); }
Output: 21 10 7 30 3 10
در ﺑﺎﻻي ﺑﺮﻧﺎﻣﻪ اﺿﺎﻓﻪ ﺷﺪه وLess اﻳﻦ ﺑﺮﻧﺎﻣﻪ درﺳﺖ ﻣﺜﻞ ﻳﻜﻲ از ﺑﺮﻧﺎﻣﻪﻫﺎي ﭘﻴﺸﻴﻦ اﺳﺖ ﺑﺎ اﻳﻦ ﻓﺮق ﻛﻪ ﻛﻼس ﺑﻪ ﺟﺎي typedef map<int,int>
MAP;
:ﻧﻮﺷﺘﻪاﻳﻢ typedef map<int,int,Less>
MAP;
ﺣﺎﻻ ﻫﻤﻴﻦ ﺑﺮﻧﺎﻣﻪ را ﻃﻮري ﺗﻐﻴﻴﺮ ﻣﻲدﻫﻴﻢ.ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ اﻳﻦ ﺑﺎر ﻣﺆﻟﻔﻪﻫﺎي اول از ﺑﺰرگ ﺑﻪ ﻛﻮﭼﻚ ﻣﺮﺗﺐ ﺷﺪهاﻧﺪ ﻛﻪ اﺑﺘﺪا ﻣﺆﻟﻔﻪﻫﺎي اول زوج را از ﻛﻮﭼﻚ ﺑﻪ ﺑﺰرگ و ﺑﻌﺪ ﻣﺆﻟﻔﻪﻫﺎي اول ﻓﺮد را از ﻛﻮﭼﻚ ﺑﻪ ﺑﺰرگ ﺑﻨﻮﻳﺴﺪ :(
)»اول« در اﻳﻦ ﺟﺎ ﻫﻴﭻ رﺑﻄﻲ ﺑﻪ »ﻋﺪد اول« ﻧﺪارد
#include <conio.h> #include <iostream> using namespace std; #include <map> class Less : public less<int> { public: bool operator () (int x, int y) { if(x%2 == 0 && y%2 == 1) return true; if(x%2 == 1 && y%2 == 0) return false; return x < y; } }; typedef map<int,int,Less> MAP; typedef MAP::iterator ITERATOR; typedef pair<int,int> PAIR; int main() { MAP m; ITERATOR it; m.insert(PAIR(1, 10)); m.insert(PAIR(2, 20)); m.insert(PAIR(3, 30));
363
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ m.insert(PAIR(4, 40)); m.insert(PAIR(5, 50)); m.insert(PAIR(6, 60)); for (it = m.begin(); it != m.end(); it++) { cout << it -> first << " "; cout << it -> second << "\n"; } _getch(); }
Output: 2 20 4 40 6 60 1 10 3 30 5 50
.ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﻣﺆﻟﻔﻪﻫﺎي اول زوج ﺑﺮ ﻣﺆﻟﻔﻪﻫﺎي اول ﻓﺮد ﺑﺮﺗﺮي داده ﺷﺪهاﻧﺪ ﺑﺎ ﺑﻪ ﻛﺎر. ﺑﻪ ﻳﻚ ﻧﮕﺎﺷﺖ را ﺧﻴﻠﻲ راﺣﺖ ﻛﺮدpair ﻣﻲﺷﻮد ﻛﺎر وارد ﻛﺮدنoperator []ﺑﺎ اﺳﺘﻔﺎده از : ﺑﻪ اﻳﻦ ﻣﺜﺎل ﻧﮕﺎه ﻛﻨﻴﺪ.pair<int,int> ﻫﺴﺖ ﻧﻪ ﺑﻪinsert() ﻧﻪ ﻧﻴﺎزي ﺑﻪ،ﮔﺮﻓﺘﻦ آن #include <conio.h> #include <iostream> using namespace std; #include <map> typedef map<int,int> int main() { MAP m; m[1] = 10; m[2] = 20; m[3] = 30; m[4] = 40; m[5] = 50; m[6] = 60;
MAP;
int i; for (i = 1; i <= 6; i++) { cout << i << " " << m[i] << endl; } _getch(); }
Output: 1 10 2 20 3 30 4 40
364
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 5 50 6 60
ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺑﺮﻧﺎﻣﻪ ﺧﻴﻠﻲ ﺳﺎده و ﻗﺎﺑﻞ ﻓﻬﻢ ﺗﺮ ﺷﺪه اﺳﺖ. ﻣﺜﺎل زﻳﺮ ﺟﺎﻟﺐ اﺳﺖ .در اﻳﻦ ﻣﺜﺎل ﻓﺮق ﻛﻮﭼﻚ ﺑﻴﻦ insertو ][ operatorﻣﺸﺨﺺ ﺷﺪه: >#include <conio.h >#include <iostream ;using namespace std >#include <map >typedef map<int,int ;MAP )(int main { ;MAP m ;m[1] = 10 ;m[1] = 20 ;cout << 1 << " " << m[1] << endl ;)(_getch }
Output: 1 20
insertوﻗﺘﻲ ﻗﺎﻧﻮن ﻧﮕﺎﺷﺖﻫﺎ )ﻳﻌﻨﻲ ﻗﺎﻧﻮن »ﻳﻚ ﭼﻴﺰ ﺑﻪ ﭼﻨﺪ ﭼﻴﺰ ﻧﮕﺎﺷﺘﻪ ﻧﻤﻲﺷﻮد«( در ﺷﺮف ﻧﻘﺾ ﻗﺮار ﻣﻲ- ﮔﺮﻓﺖ )
( از وارد ﻛﺮدن ِ pairﻧﻘﺾ ﻛﻨﻨﺪه ،ﺧﻮدداري ﻣﻲﻛﺮد اﻣﺎ ِ operator [] pairﻗﺒﻠﻲ را ﺣﺬف
ﻣﻲﻛﻨﺪ و ِ pairﺟﺪﻳﺪ را ﺟﺎي آن ﻣﻲﻧﺸﺎﻧﺪ. ﻗﺎﻧﻮن ﻧﮕﺎﺷﺖﻫﺎ در واﻗﻊ ﻫﻤﺎن ﺷﺮﻃﻲ اﺳﺖ ﻛﻪ رﻳﺎﺿﻴﺪاﻧﺎن ﺑﺮاي »ﺗﺎﺑﻊ« ﺑﻮدن ﻳﻚ ﻣﺠﻤﻮﻋﻪ از زوجﻫﺎي ﻣﺮﺗﺐ )ﻳﻌﻨﻲ ﻫﻤﺎن pairﻫﺎ( ﻣﻲﮔﺬارﻧﺪ .وﺟﻮد آن ﻣﺰاﻳﺎﻳﻲ دارد .اﻣﺎ ﻣﻤﻜﻦ اﺳﺖ در ﺟﺎﻫﺎﻳﻲ ﻣﺰاﺣﻢ ﻛﺎر ﻣﺎ ﺑﺎﺷﺪ .اﮔﺮ ﺑﺨﻮاﻫﻴﻢ اﻳﻦ ﻗﺎﻧﻮن اﻋﻤﺎل ﻧﺸﻮد ﺑﻪ ﺟﺎي mapاز multimapاﺳﺘﻔﺎده ﻣﻲﻛﻨﻴﻢ multimap .ﺗﺎ ﺣﺪ زﻳﺎدي ﻣﺜﻞ mapﻋﻤﻞ ﻣﻲﻛﻨﺪ .ﻣﺜﺎل زﻳﺮ ﻧﺤﻮهي ﺑﻪ ﻛﺎر ﺑﺮدن آن را ﻧﺸﺎن ﻣﻲدﻫﺪ: >#include <conio.h >#include <iostream ;using namespace std >#include <map ;MAP ;ITERATOR ;PAIR
>typedef multimap<int,double typedef MAP::iterator >typedef pair<int,double
;it
365
www.pupuol.com
)(int main { ;MAP m ITERATOR
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ m.insert(PAIR(1, 10.1)); m.insert(PAIR(2, 20.2)); m.insert(PAIR(1, 30.3)); for (it = m.begin(); it != m.end(); it++) { cout << it -> first << " "; cout << it -> second << "\n"; } _getch(); }
Output: 1 10.1 1 30.3 2 20.2
string
ﺑﺎstring ﻫﺴﺖ و ﻛﻼسbasic_string ي ﺑﻪ ﻧﺎمstring template ِ header file در typedef basic_string<char> string;
ﻛﺎرﺑﺮد داردC++ ﻓﻘﻂ درstring ﺑﺎ اﻳﻦ ﻛﻪ. ﻛﺎر ﺑﺎ رﺷﺘﻪﻫﺎ را آﺳﺎن ﻣﻲﻛﻨﺪstring ﻛﻼس.ﺗﻌﺮﻳﻒ ﺷﺪه .ﻳﺎد ﮔﺮﻓﺘﻦ آن ﺧﻴﻠﻲ راﺣﺖ اﺳﺖ : را ﻧﺸﺎن ﻣﻲدﻫﺪ+ و ﻋﻤﻠﻜﺮد ﻋﻤﻠﮕﺮstring ﻫﺎﻳﻲ ازinstance ﻣﺜﺎل زﻳﺮ اﻳﺠﺎد #include <conio.h> #include <iostream> using namespace std; #include <string> int main() { string str1 = "hello"; string str2 ("bye"); cout<< str1 + str2; _getch(); }
Output: hellobye
. دو رﺷﺘﻪ را ﺑﻪ ﻫﻢ ﻣﻲﭼﺴﺒﺎﻧﺪoperator + ﺑﻨﺎﺑﺮاﻳﻦ
366
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ :(ﺑﺎ < و == ﻣﻲﺷﻮد ﺗﺮﺗﻴﺐ را ﻣﺸﺨﺺ ﻛﺮد )ﻣﺜﻞ ﺗﺮﺗﻴﺐ ﻛﻠﻤﺎت در ﻳﻚ ﻓﺮﻫﻨﮓ ﻟﻐﺖ #include <conio.h> #include <iostream> using namespace std; #include <string> int main() { string str1 = "abc"; string str2 = "abd"; if(str1 == str2) cout<<"they are equal"; else if(str1 < str2) cout<< "str1 < str2"; else cout<< "str2 < str1"; _getch(); }
Output: str1 < str2
:ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﺜﺎل ﺑﻬﺘﺮي زده ﺑﺎﺷﻢ اﻳﻦ ﺑﺮﻧﺎﻣﻪ را اﺟﺮا ﻛﻨﻴﺪ #include <conio.h> #include <iostream> using namespace std; #include <map> #include <string> typedef map<string,int> typedef MAP::iterator int main() { MAP m; m["hello"] m["bye"] m["Bye"] m["mouse"] m["horton"] m["ratatouille"]
= = = = = =
MAP; ITERATOR;
0; 0; 0; 0; 0; 0;
for (ITERATOR it = m.begin(); it != m.end(); it++) cout << it -> first << endl; _getch(); }
Output: Bye bye hello
367
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ horton mouse ratatouille
ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ ﺑﻪ ﺟﺎي.در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻛﻠﻤﺎت ﺑﺎ اﺳﺘﻔﺎده از ﻳﻚ ﻧﮕﺎﺷﺖ ﻣﺮﺗﺐ ﺷﺪهاﻧﺪ string str = "hello"; m[str] = 0;
:ﻧﻮﺷﺘﻪام m["hello"] = 0;
ﺗﺒﺪﻳﻞ ﻣﻲﻛﻨﺪ ﺑﻪ ﺟﺎيstring را ﺑﻪC Style ﺑﺎﺷﺪ ﻛﻪ رﺷﺘﻪﻫﺎي ﻣﻌﻤﻮﻟﻲconstructor وﻗﺘﻲ ﻳﻚ . ﺑﺎﺷﺪ ﻣﺜﻞ اﻳﻦ ﺟﺎ ﻣﻲﺷﻮد ﻳﻚ رﺷﺘﻪي ﻣﻌﻤﻮﻟﻲ ﻧﻮﺷﺖstring آرﮔﻮﻣﺎﻧﻲ ﻛﻪ ﺑﺎﻳﺪ : ﺟﺎي دو رﺷﺘﻪ را ﻋﻮض ﻣﻲﻛﻨﺪswap() ﺗﺎﺑﻊ #include <conio.h> #include <iostream> using namespace std; #include <string> int main() { string s1("hello"), s2("bye"); cout<< s1 << "\t" << s2 << endl; swap(s1,s2); cout<< s1 << "\t" << s2 << endl; _getch(); }
Output: hello bye
bye hello
. ﭼﺎپ ﻣﻲﻛﻨﺪ، راstring ﺑﻪ راﺣﺘﻲ ﻳﻚcout ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ : ﻛﺮدن ﺑﻪ ﻛﺎر ﻣﻲرودassign ﺑﺮايoperator = #include <conio.h> #include <iostream> using namespace std; #include <string> int main() { string s1("hello"),s2; s2 = s1; cout<< s2; _getch();
368
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output: hello
string.h
ﻧﻴﺎز ﺑﻪstring ِ header file وﺟﻮد. اﺳﺖC ﺑﺮاي ﻛﺎر ﺑﺎ رﺷﺘﻪﻫﺎﺳﺖ و ﻣﺨﺼﻮص زﺑﺎنstring.h . ﻫﺴﺖ ﻛﻪ ﻧﻤﻲﺷﻮد از آنﻫﺎ ﮔﺬﺷﺖstring.h ﻛﻢ ﻛﺮده اﻣﺎ ﺗﻮاﺑﻌﻲ درC++ را درstring.h : ﻃﻮل ﻳﻚ رﺷﺘﻪ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪstrlen() ﺗﺎﺑﻊ #include <conio.h> #include <iostream> using namespace std; #include <string.h> int main() { cout<< strlen("hello"); _getch(); }
Output: 5
: ﻛﺮدن ﻳﻚ رﺷﺘﻪ در رﺷﺘﻪي دﻳﮕﺮ اﺳﺖcopy ﺑﺮايstrcpy() ﺗﺎﺑﻊ #include <conio.h> #include <iostream> using namespace std; #include <string.h> int main() { char a[100] = {0}; strcpy(a,"hello"); cout<< a; _getch(); }
Output: hello
: ﺑﺮاي ﻣﻘﺎﻳﺴﻪي دو رﺷﺘﻪ ﺑﻪ ﻛﺎر ﻣﻲرودstrcmp() ﺗﺎﺑﻊ #include <conio.h> #include <iostream> using namespace std;
369
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <string> int main() { if(strcmp("ab","ac") < 0) cout<<"ab < ac"
"\n";
if(strcmp("hello","bye") > 0) cout<< "hello > bye"
"\n";
if(strcmp("C++","C++") == 0) cout<<"C++ == C++" _getch();
"\n";
}
Output: ab < ac hello > bye C++ == C++
در ﺿﻤﻦ ﺑﻪ ﺟﺎي."hello" > "bye" ﻳﻌﻨﻲstrcmp("hello","bye") > 0 ﺗﻮﺟﻪ ﻛﻨﻴﺪ . ﻧﻮﺷﺘﻪامstring ﻓﻘﻂstring.h : ﺑﺮاي ﭼﺴﺒﺎﻧﺪن دو رﺷﺘﻪ ﺑﻪ ﻛﺎر ﻣﻲرودstrcat() ﺗﺎﺑﻊ #include <conio.h> #include <iostream> using namespace std; #include <string.h> int main() { char a[100] = "hello"; strcat(a,"bye"); cout<< a; _getch(); }
Output: hellobye
." را داردhello" ﻛﺎراﻛﺘﺮ دﻳﮕﺮ5 ﺣﺎﻓﻈﻪي ﻻزم ﺑﺮايa ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ
vector
ﻫﺎ در ﺣﻘﻴﻘﺖdouble ازvector ﻳﻚ. داردvector ﻛﻼﺳﻲ ﺑﺎ ﻧﺎم- template ﻫﻢheader file اﻳﻦ ﻳﻚ ﭼﻨﺪ ﺗﺎﻳﻲ ﻣﺮﺗﺐ ﺑﻪ ﺷﻜﻞ
370
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ (1.1, 1.4, 5, 3.14, 1, 0)
. ﻣﺆﻟﻔﻪ6 اﺳﺖ ﺑﺎvector ﭼﻨﺪﺗﺎﻳﻲ ﺑﺎﻻ ﻳﻚ.اﺳﺖ ﺑﺴﻴﺎري از دﺳﺘﻮرات ﺷﺒﻴﻪ ﺑﻪ ﻛﻼس. ﻣﺆﻟﻔﻪ اﻳﺠﺎد ﻣﻲﻛﻨﺪ و ﻣﺆﻟﻔﻪﻫﺎ را ﭼﺎپ ﻣﻲﻛﻨﺪ5 ﺑﺎvector ﻣﺜﺎل زﻳﺮ ﻳﻚ : اﺳﺖmap #include <conio.h> #include <iostream> using namespace std; #include <vector> typedef vector<double> typedef Vector::iterator
Vector; Iterator;
int main() { Vector v(5); v[0] = 5.5; v[1] = 4.4; v[2] = 3.3; v[3] = 2.2; v[4] = 1.1; Iterator it; for(it = v.begin(); it != v.end(); it++) cout<< *it << endl; _getch(); }
Output: 5.5 4.4 3.3 2.2 1.1
ﻣﺆﻟﻔﻪﻫﺎit ﺑﻪ ﻣﺆﻟﻔﻪﻫﺎ ﻣﻘﺪار داده ﺷﺪه و ﺑﺎ اﺳﺘﻔﺎده ازoperator [] ﺑﺎ، ﻣﺆﻟﻔﻪ اﻳﺠﺎد ﺷﺪه5 ﺑﺎvector ﻳﻚ .ﭼﺎپ ﺷﺪهاﻧﺪ : اﺿﺎﻓﻪ ﻛﺮدvector ﻣﻲﺷﻮد ﻳﻚ ﻣﺆﻟﻔﻪ را ﺑﻪ ﻳﻚinsert ِ method ﺑﺎ اﺳﺘﻔﺎده از #include <conio.h> #include <iostream> using namespace std; #include <vector> typedef vector<int> typedef Vector::iterator
Vector; Iterator;
int main() {
371
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Vector v[0] = v[1] = v[2] = v[3] = v[4] =
v(5); 10; 20; 30; 40; 50;
Iterator it = v.begin() + 2; v.insert(it, 25); for(it = v.begin(); it != v.end(); it++) cout<< *it << endl; _getch(); }
Output: 10 20 25 30 40 50
: ﺑﻪ اﻳﻦ ﺻﻮرت دارﻳﻢvector در اﻳﻦ ﻣﺜﺎل اول ﻳﻚ (10, 20, 30, 40, 50)
را ﺑﻪ25 ﻋﺪدinsert() وit ﺑﺎ اﺳﺘﻔﺎده از. را ﻃﻮري ﺗﻨﻈﻴﻢ ﻣﻲﻛﻨﻴﻢ ﻛﻪ ﺑﻪ ﻣﺆﻟﻔﻪي ﺳﻮم اﺷﺎره ﻛﻨﺪit ﺑﻌﺪ . اﺿﺎﻓﻪ ﻣﻲﻛﻨﻴﻢ ﻛﻪ ﻗﺒﻞ از ﻣﺆﻟﻔﻪي ﺳﻮم ﻗﺮار ﻣﻲﮔﻴﺮدvector : اﺿﺎﻓﻪ ﻣﻲﻛﻨﺪvector ﻳﻚ ﻣﺆﻟﻔﻪ ﺑﻪ اﻧﺘﻬﺎيpush_back() ِ method #include <conio.h> #include <iostream> using namespace std; #include <vector> typedef vector<int> typedef Vector::iterator
Vector; Iterator;
int main() { Vector v(5); v[0] = 10; v[1] = 20; v[2] = 30; v[3] = 40; v[4] = 50; v.push_back(60); Iterator it; for(it = v.begin(); it != v.end(); it++) cout<< *it << endl; _getch();
372
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ }
Output: 10 20 30 40 50 60
ِ vector اول،در اﻳﻦ ﻣﺜﺎل (10, 20, 30, 40, 50)
اﺿﺎﻓﻪ ﻣﻲﻛﻨﻴﻢ ﺗﺎvector را ﺑﻪ ﺗﻪ اﻳﻦ60 ،push_back() را دارﻳﻢ و ﺑﺎ (10, 20, 30, 40, 50, 60)
.ﺑﻪ دﺳﺖ ﺑﻴﺎﻳﺪ ﻓﺮم اول ﺗﻨﻬﺎ ﻳﻚ ﻣﺆﻟﻔﻪ را. دو ﻓﺮم از آن ﻫﺴﺖ. اﺳﺖvector ﺑﺮاي ﺣﺬف ﻣﺆﻟﻔﻪﻫﺎ ازerase() ِ method :ﺣﺬف ﻣﻲﻛﻨﺪ #include <conio.h> #include <iostream> using namespace std; #include <vector> typedef vector<int> typedef Vector::iterator int main() { Vector v[0] = v[1] = v[2] = v[3] = v[4] =
Vector; Iterator;
v(5); 10; 20; 30; 40; 50;
Iterator it = v.begin() + 2; v.erase(it); for(it = v.begin(); it != v.end(); it++) cout<< *it << endl; _getch(); }
Output: 10 20 40 50
373
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ : را ﻫﻢ زﻣﺎن ﺣﺬف ﻣﻲﻛﻨﺪvector ﻳﻚ ردﻳﻒ از ﻣﺆﻟﻔﻪﻫﺎيerase() ﻓﺮم دوم #include <conio.h> #include <iostream> using namespace std; #include <vector> typedef vector<int> typedef Vector::iterator int main() { Vector v(5); v[0] = 10; v[1] = 20; v[2] = 30; v[3] = 40; v[4] = 50;
Vector; Iterator;
Iterator it1 = v.begin() + 1; Iterator it2 = v.begin() + 4; v.erase(it1,it2); for(Iterator it = v.begin(); it != v.end(); it++) cout<< *it << endl; _getch(); }
Output: 10 50
ﻣﺆﻟﻔﻪﻫﺎي دوم ﺗﺎ ﭼﻬﺎرم راerase() . ﺑﻪ ﻣﺆﻟﻔﻪي ﭘﻨﺠﻢ اﺷﺎره داردit2 ﺑﻪ ﻣﺆﻟﻔﻪي دوم وit1 در اﻳﻦ ﺑﺮﻧﺎﻣﻪ .ﺣﺬف ﻛﺮده : )ﻳﻌﻨﻲ ﺗﻌﺪاد ﻣﺆﻟﻔﻪﻫﺎي آن( را ﻣﺸﺨﺺ ﻛﺮدvector ﻣﻲﺷﻮد ﻃﻮلsize() ِ method ﺑﺎ #include <conio.h> #include <iostream> using namespace std; #include <vector> typedef vector<char> int main() { Vector v(5); v[0] = 'h'; v[1] = 'e'; v[2] = 'l'; v[3] = 'l'; v[4] = 'o'; cout<< v.size(); _getch(); }
Vector;
374
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ Output: 5
fstream.h
ﻧﻤﻲﺧﻮاﻫﻢ ﺧﻴﻠﻲ در ﻣﻮرد fstream.hﺗﻮﺿﻴﺢ ﺑﺪﻫﻢ .ﺗﻨﻬﺎ ﻣﻲﮔﻮﻳﻢ ﭼﻪ ﻃﻮر ﻣﻲﺷﻮد روي hard disk ﻛﺎﻣﭙﻴﻮﺗﺮ ﻳﻚ ﻓﺎﻳﻞ اﻳﺠﺎد ﻛﺮد و در آن ﭼﻴﺰي ﻧﻮﺷﺖ ﻳﺎ اﻃﻼﻋﺎت آن را ﺧﻮاﻧﺪ .ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﻧﺎم blue.txtدر دراﻳﻮ C:اﻳﺠﺎد ﻣﻲﻛﻨﺪ و در آن ﻣﻲﻧﻮﻳﺴﻴﺪ ”: “I’m Blue >#include <conio.h >#include <fstream ;using namespace std )(int main { ;)"ofstream fout("C:\\blue.txt ;"fout<< "I'm Blue ;)(fout.close ;)(_getch }
Output: empty
دﻗﺖ ﻛﻨﻴﺪ ﻛﻪ foutدر اﻳﻦ ﻣﺜﺎل ﻣﺜﻞ coutﺑﻪ ﻛﺎر رﻓﺘﻪ .ﺣﺎﻻ از ﻃﺮﻳﻖ My Computerﺑﻪ دراﻳﻮ C :ﺑﺮوﻳﺪ و ﻓﺎﻳﻠﻲ ﺑﺎ ﻧﺎم blue.txtرا ﭘﻴﺪا ﻛﻨﻴﺪ .ﻓﺎﻳﻞ را ﺑﺎز ﻛﻨﻴﺪ ﺗﺎ ﻣﺤﺘﻮﻳﺎت آن را ﺑﺒﻴﻨﻴﺪ:
375
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﻪ در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ اﻳﺠﺎد ﺷﺪ ﺑﺎز ﺷﻮد و اﻃﻼﻋﺎت آن ﺧﻮاﻧﺪهblue.txt ﺣﺎﻻ ﺑﺮﻧﺎﻣﻪي زﻳﺮ را اﺟﺮا ﻛﻨﻴﺪ ﺗﺎ ﻓﺎﻳﻞ :ﺑﺸﻮد #include <conio.h> #include <fstream> #include <iostream> using namespace std; int main() { ifstream fin("C:\\blue.txt"); char a[100] = {0}; fin.getline(a,99); fin.close(); cout<< a; _getch(); }
Output: I'm Blue
ﻛﺎراﻛﺘﺮ( ﺑﺨﻮاﻧﺪ99 ﺳﻌﻲ ﻣﻲﻛﻨﺪ ﻛﺎراﻛﺘﺮﻫﺎي ذﺧﻴﺮه ﺷﺪه در ﻓﺎﻳﻞ را )ﺗﺎ ﺣﺪاﻛﺘﺮgetline() در اﻳﻦ ﺑﺮﻧﺎﻣﻪ . ﺑﺮﻳﺰدa و در .ﺑﻌﺪا ﺑﻴﺶ ﺗﺮ ﺑﺮاي ﻓﺎﻳﻞﻫﺎ ﺗﻮﺿﻴﺢ ﻣﻲدﻫﻢ
math.h
: ﻣﺜﻼ ﺗﺎﺑﻊ ﺗﻮان.(
! ﺗﺎﺑﻊﻫﺎي ﻣﺮﺑﻮط ﺑﻪ رﻳﺎﺿﻴﺎت ﻗﺮار دارد )رﻳﺎﺿﻴﺎت؟header file در اﻳﻦ
#include <conio.h> #include <iostream> using namespace std; #include <math.h> int main() { cout<< pow(2.0,3); _getch(); }
Output: 8
376
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ در اﻳﻦ ﻣﺜﺎل ﻣﺤﺎﺳﺒﻪ و ﭼﺎپ ﺷﺪه .اﮔﺮ ﺑﻪ ﺟﺎي 2.0ﺑﻨﻮﻳﺴﻴﻢ Visual C++ 2005 ،2اﻳﺮاد ﻣﻲﮔﻴﺮد. ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻫﻢ در Visual C++ 2005اﺟﺮا ﻧﻤﻲﺷﻮد: >#include <conio.h >#include <iostream ;using namespace std >#include <math.h )(int main { ;)(cout<< typeid(pow).name ;)(_getch }
Output (BDS 2006): )double (*)(double,double
ﺗﺎﺑﻊ )() sinﺳﻴﻨﻮس( ﻫﻢ در math.hﻫﺴﺖ وﻟﻲ آرﮔﻮﻣﺎن آن ﺑﺎﻳﺪ ﺑﺮ ﺣﺴﺐ رادﻳﺎن ﺑﺎﺷﺪ ﻧﻪ درﺟﻪ: >#include <conio.h >#include <iostream ;using namespace std >#include <math.h ;const double Pi = 3.1415926535 )(int main { ;)cout<< sin(Pi/4 ;)(_getch }
Output: 0.707107
اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺳﻴﻨﻮس 45درﺟﻪ را ﭼﺎپ ﻛﺮده.
windows.h
ﻛﺎرﺑﺮد اﺻﻠﻲ اﻳﻦ header fileﺑﺮاي ﺑﺮﻧﺎﻣﻪﻫﺎي وﻳﻨﺪوز اﺳﺖ .اﻣﺎ ﭼﻴﺰﻫﺎﻳﻲ زﻳﺎدي در آن ﻫﺴﺖ ﻛﻪ ﻻزم ﻧﻴﺴﺖ ﺣﺘﻤﺎ در ﻳﻚ Windows Applicationﺑﻪ ﻛﺎر رود .ﺑﻪ ﭼﻨﺪ ﻣﻮرد از آنﻫﺎ اﺷﺎره ﻣﻲﻛﻨﻢ .ﺑﺮﻧﺎﻣﻪﻫﺎﻳﻲ ﻛﻪ در اﻳﻦ ﺑﺨﺶ ﻧﻮﺷﺘﻪام ﺗﻨﻬﺎ در BDS 2006آزﻣﺎﻳﺶ ﺷﺪهاﻧﺪ.
377
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
اﺑﺘﺪا ﺗﺎﺑﻊ )( MessageBoxرا ﻣﻌﺮﻓﻲ ﻣﻲﻛﻨﻢ ﻛﻪ ﻳﻚ Message Boxاﻳﺠﺎد ﻣﻲﻛﻨﺪ: >#include <conio.h >#include <windows.h )(int main { "MessageBox(0, "This is a message\n "This is a message box", ;)"This is Caption", MB_OK ;)(_getch }
Output (BDS 2006):
ﺣﺎﻻ ﻣﻲﺧﻮاﻫﻢ در ﻣﻮرد اﻳﺠﺎد ﻓﺎﻳﻞﻫﺎ و ﻛﺎر ﺑﺎ آنﻫﺎ ﺑﻴﺶ ﺗﺮ ﺣﺮف ﺑﺰﻧﻢ windows.h .اﺑﺰارﻫﺎﻳﻲ ﻛﺎﻣﻞ و ﻗﻮي ﺑﺮاي ﻛﺎر ﺑﺎ ﻓﺎﻳﻞﻫﺎ روي ﻫﺎرد در اﺧﺘﻴﺎر ﻣﺎ ﻗﺮار ﻣﻲدﻫﺪ. ﺑﺮاي اﻳﻦ ﻛﻪ اﻃﻼﻋﺎت را روي ﺣﺎﻓﻈﻪي داﺋﻢ ﻛﺎﻣﭙﻴﻮﺗﺮ ذﺧﻴﺮه ﻛﻨﻴﻢ ﺑﺎﻳﺪ ﻳﻚ ﻓﺎﻳﻞ اﻳﺠﺎد ﻛﻨﻴﻢ ،ﻓﺎﻳﻞ را ﺑﺎز ﻛﻨﻴﻢ، اﻃﻼﻋﺎت را در آن ﻗﺮار ﺑﺪﻫﻴﻢ و ﺑﻌﺪ ﻓﺎﻳﻞ را ﺑﺒﻨﺪﻳﻢ. ﺑﻪ ﻫﺮ ﻓﺎﻳﻞ ﺑﺎز ﻳﻚ ﻋﺪد ﻧﺴﺒﺖ داده ﻣﻲﺷﻮد ﻛﻪ ﺑﻪ آن handleﻣﻲﮔﻮﻳﻴﻢ .ﺑﺎ ﻛﻤﻚ handleﻣﻲﺗﻮاﻧﻴﻢ ﺑﻪ ﻓﺎﻳﻞ دﺳﺘﺮﺳﻲ داﺷﺘﻪ ﺑﺎﺷﻴﻢ .وﻗﺘﻲ ﻳﻚ ﻓﺎﻳﻞ ﺑﺎز اﺳﺖ ﻳﻚ ﻣﻜﺎن ﺟﺎري در آن ﻓﻌﺎل اﺳﺖ .ﺑﺮاي اﻳﻦ ﻛﻪ ﺗﺼﻮري از ﻣﻜﺎن ﺟﺎري داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﭘﺴﻮﻧﺪ .txtرا در ﻣﺤﻴﻂ وﻳﻨﺪوز ﺑﺎز ﻛﻨﻴﺪ ﻣﻲﺑﻴﻨﻴﺪ ﻛﻪ ﺧﻄﻲ اﻓﻘﻲ )ﻛﻪ caretﻧﺎم دارد( در آن ﭼﺸﻤﻚ ﻣﻲزﻧﺪ:
378
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ
ﻣﻜﺎن caretدر واﻗﻊ ﻫﻤﺎن ﻣﻜﺎن ﺟﺎري اﺳﺖ .وﻗﺘﻲ ﺑﺎ keyboardﭼﻴﺰي ﺗﺎﻳﭗ ﻣﻲﻛﻨﻴﺪ ﺣﺮوف در ﻣﻜﺎن ﺟﺎري ﻗﺮار ﻣﻲﮔﻴﺮﻧﺪ .وﻗﺘﻲ ﻓﺎﻳﻠﻲ ﺑﺎز ﺑﺎﺷﺪ ﻳﻚ ﻣﻜﺎن ﺟﺎري دارد ﻛﻪ ﻣﻲﺷﻮد آن ﻣﻜﺎن را ﻣﺜﻞ caretﺟﺎ ﺑﻪ ﺟﺎ ﻛﺮد ﺗﺎ دادهﻫﺎ در ﺟﺎي ﻣﻨﺎﺳﺐ وارد ﺑﺸﻮﻧﺪ. ﻫﺮ ﻓﺎﻳﻞ ﻳﻚ اﻧﺪازه دارد ﻛﻪ ﺑﺮاﺑﺮ اﺳﺖ ﺑﺎ ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎﻳﻲ ﻛﻪ در آن ﻧﻮﺷﺘﻪ ﺷﺪه. ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ ﻧﺎم hello.txtدر آدرس \ C:درﺳﺖ ﻣﻲﻛﻨﺪ و آن را ﺑﺎز ﻣﻲﻛﻨﺪ و در آن ﻣﻲﻧﻮﻳﺴﺪ ”.“Glad to meet you >#include <conio.h >#include <windows.h )(int main { = HANDLE han (CreateFile ""C:\\hello.txt , FILE_ALL_ACCESS , FILE_SHARE_WRITE | FILE_SHARE_READ, 0 , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , 0 ;) ;"char a[] = "Glad to meet you ;)unsigned len = strlen(a ;unsigned long data_actually_wrote (WriteFile han, a, len, &data_actually_wrote,
379
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ 0 ;) ;)(_getch }
Output (BDS 2006): empty
ﺑﻌﺪ از اﺟﺮاي ﺑﺮﻧﺎﻣﻪ ﺑﻪ \ C:ﺳﺮي ﺑﺰﻧﻴﺪ و hello.txtرا ﺑﺎز ﻛﻨﻴﺪ:
ﺑﺎ ﺗﺎﺑﻊ )( CreateFileﻣﻲﺷﻮد ﻓﺎﻳﻞ ﺟﺪﻳﺪي اﻳﺠﺎد ﻛﺮد ﻳﺎ ﻓﺎﻳﻠﻲ را ﻛﻪ از ﻗﺒﻞ ﻣﻮﺟﻮد اﺳﺖ ﺑﺎز ﻛﺮد .اﺻﻼ از ﺷﻜﻞ ﻛﺎرﺑﺮد آن در ﺑﺮﻧﺎﻣﻪ ﻧﺘﺮﺳﻴﺪ ﺗﻨﻬﺎ ﭼﻴﺰﻫﺎﻳﻲ ﻛﻪ ﺑﺎ آنﻫﺎ ﻛﺎر دارﻳﻢ ﺧﺮوﺟﻲ و آرﮔﻮﻣﺎن اول اﻳﻦ ﺗﺎﺑﻊ ﻫﺴﺘﻨﺪ. ﺧﺮوﺟﻲhandle ،ي ﺑﻪ ﻓﺎﻳﻞ ﺑﺎز ﺷﺪه ﻣﻲدﻫﺪ و آرﮔﻮﻣﺎن اول در واﻗﻊ آدرس و اﺳﻢ ﻓﺎﻳﻞ ﺑﻪ ﺻﻮرت ﻳﻚ رﺷﺘﻪ اﺳﺖ .ﺑﻘﻴﻪي آرﮔﻮﻣﺎنﻫﺎ را ﺑﻪ ﻫﻤﻴﻦ ﺻﻮرت ﺑﻪ ﻛﺎر ﺑﺒﺮﻳﺪ .اﻳﻦ آرﮔﻮﻣﺎنﻫﺎي اﺿﺎﻓﻪ در واﻗﻊ ﺗﻨﻈﻴﻤﺎت و وﻳﮋﮔﻲ- ﻫﺎي ﻓﺎﻳﻞ ﺑﺎز ﺷﺪه را ﻣﺸﺨﺺ ﻣﻲﻛﻨﻨﺪ .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ اﻳﻦ آرﮔﻮﻣﺎن ﻃﻮري ﻧﻮﺷﺘﻪ ﺷﺪهاﻧﺪ ﻛﻪ ﻓﺎﻳﻞ را ﺑﺮاي ﺧﻮاﻧﺪن و ﻧﻮﺷﺘﻦ آﻣﺎده ﻛﻨﻨﺪ .در ﺿﻤﻦ اﻳﻦ ﺗﻨﻈﻴﻤﺎت ﺑﻪ ﮔﻮﻧﻪاي ﻫﺴﺘﻨﺪ ﻛﻪ اﮔﺮ ﻓﺎﻳﻞ ﻣﻮﺟﻮد ﻧﺒﺎﺷﺪ ،اﻳﺠﺎد ﺑﺸﻮد و اﮔﺮ ﻣﻮﺟﻮد ﺑﺎﺷﺪ ﻓﻘﻂ ﺑﺎز ﺷﻮد ﺑﺪون اﻳﻦ ﻛﻪ اﻃﻼﻋﺎﺗﺶ از ﺑﻴﻦ ﺑﺮود. ﺑﻌﺪ از اﺟﺮاي )( CreateFileﻳﻚ ﻓﺎﻳﻞ ﺑﺎز دارﻳﻢ ﻛﻪ handle ،hanآن اﺳﺖ .ﭼﻮن ﻓﺎﻳﻞ ،ﺗﺎزه ﺑﺎز ﺷﺪه ﻣﻜﺎن ﺟﺎري آن اﺑﺘﺪاي ﻓﺎﻳﻞ اﺳﺖ )ﺑﻪ ﻋﺒﺎرت دﻳﮕﺮ ﻣﻜﺎن ﺟﺎري ﺻﻔﺮ اﺳﺖ( .ﺑﺎ ﺗﺎﺑﻊ )( WriteFileﻣﻲﺷﻮد در ﻣﻜﺎن ﺟﺎري اﻃﻼﻋﺎت را وارد ﻛﺮد .ﻓﻘﻂ ﺑﺎ ﺳﻪ ﭘﺎراﻣﺘﺮ اول اﻳﻦ ﺗﺎﺑﻊ ﻛﺎر دارﻳﻢ دو ﭘﺎراﻣﺘﺮ آﺧﺮ را ﻫﻤﻴﺸﻪ ﺑﻪ ﻫﻤﻴﻦ ﺷﻜﻞ ﺑﻨﻮﻳﺴﻴﺪ .آرﮔﻮﻣﺎن اول )( han ،WriteFileاﺳﺖ .اﻳﻦ آرﮔﻮﻣﺎن ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ ﻛﻪ )( WriteFileﺑﺎﻳﺪ در ﻛﺪام ﻓﺎﻳﻞ ِ ﺑﺎز اﻃﻼﻋﺎت را وارد ﻛﻨﺪ .آرﮔﻮﻣﺎن دوم ﻳﻚ pointerﺑﻪ اﻃﻼﻋﺎﺗﻲ اﺳﺖ ﻛﻪ ﺑﺎﻳﺪ وارد ﻓﺎﻳﻞ ﺑﺸﻮﻧﺪ .در اﻳﻦ ﺟﺎ aﺑﻪ دﺳﺘﻪاي از ﺑﺎﻳﺖﻫﺎ اﺷﺎره دارد .اﻳﻦ اﺻﻼ ﻣﻬﻢ ﻧﻴﺴﺖ ﻛﻪ اﻳﻦ ﺑﺎﻳﺖﻫﺎ ﻳﻚ
380
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ آرﮔﻮﻣﺎن ﺳﻮم ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎﻳﻲ.رﺷﺘﻪ ﺗﺸﻜﻴﻞ ﻣﻲدﻫﻨﺪ ﺗﻨﻬﺎ ﭼﻴﺰ ﻣﻬﻢ ﺻﻔﺮ و ﻳﻚﻫﺎي ﻫﺮ ﺑﺎﻳﺖ و ﺗﻌﺪاد ﺑﺎﻳﺖﻫﺎﺳﺖ .ﻛﻪ ﺑﺎﻳﺪ ﺑﻪ ﻓﺎﻳﻞ وارد ﺑﺸﻮﻧﺪ ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ ﺣﺎﻻ ﺑﺮﻧﺎﻣﻪاي ﻣﻲﻧﻮﻳﺴﻴﻢ ﻛﻪ ﻓﺎﻳﻠﻲ را ﻛﻪ در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ اﻳﺠﺎد ﺷﺪ ﺑﺎز ﻛﻨﺪ و اﻃﻼﻋﺎت ﺛﺒﺖ ﺷﺪه در ﻓﺎﻳﻞ را “ ﺗﺒﺪﻳﻞ ﺷﻮد )آوردهاﻧﺪ ﻛﻪ ﮔﺮﺑﻪايGlad to eat you” ﻃﻮري ﺗﻐﻴﻴﺮ دﻫﺪ ﻛﻪ ﻣﺘﻦ ﻧﻮﺷﺘﻪ ﺷﺪه در آن ﺑﻪ :(
glad to eat you ﻣﻮﺷﻲ را ﻣﻲﺑﻴﻨﻴﺪ و ﺑﻪ او ﻣﻲﮔﻮﻳﺪ،اﻧﮕﻴﺴﻲ زﺑﺎن
#include <conio.h> #include <windows.h> unsigned __int64 MySetFilePointer ( HANDLE han, const unsigned __int64& position, unsigned long from_where = FILE_BEGIN ) { LARGE_INTEGER large_integer; large_integer.QuadPart = position; large_integer.LowPart = SetFilePointer( han , large_integer.LowPart , &large_integer.HighPart, from_where ); return large_integer.QuadPart; } int main() { HANDLE han = CreateFile( "C:\\hello.txt" , FILE_ALL_ACCESS , FILE_SHARE_WRITE | FILE_SHARE_READ, 0 , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , 0 ); char a[] ="eat you "; unsigned len = strlen(a); MySetFilePointer(han,8);// where the action is unsigned long data_actually_wrote; WriteFile( han, a,
381
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ len, &data_actually_wrote, 0 ;) ;)(_getch }
Output (BDS 2006): empty
ﺣﺎﻻ C:\hello.txtرا )ﺑﺎ (Notepadﺑﺎز ﻛﻨﻴﺪ ﺗﺎ ﺗﻐﻴﻴﺮات را ﺑﺒﻴﻨﻴﺪ:
در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﺎﺑﻌﻲ ﺑﻪ ﻧﺎم )( MySetFilePointerﺗﻌﺮﻳﻒ ﻛﺮدهام .ﻛﺎر آن ﺗﻐﻴﻴﺮ ﻣﻜﺎن ﺟﺎري در ﻳﻚ ﻓﺎﻳﻞ ﺑﺎز اﺳﺖ ِ handle .ﻓﺎﻳﻞ ﺑﺎز ﺑﻪ ﻋﻨﻮان آرﮔﻮﻣﺎن اول داده ﻣﻲﺷﻮد .ﻧﻴﺎزي ﺑﻪ اﻳﻦ ﻛﻪ ﺑﺪاﻧﻴﻢ اﻳﻦ ﺗﺎﺑﻊ ﭼﻪ ﻃﻮر ﺗﻌﺮﻳﻒ ﺷﺪه ﻧﺪارﻳﻢ .اﻳﻦ ﺗﺎﺑﻊ را ﺑﺎ ﻫﻤﻴﻦ ﺗﻌﺮﻳﻒ در ﺑﺮﻧﺎﻣﻪﻫﺎي ﺧﻮدﺗﺎن ﺑﻪ ﻛﺎر ﺑﺒﺮﺑﺪ .در ﺣﻘﻴﻘﺖ ﭼﻮن __int64
)ﻛﻪ ﻳﻚ ﻋﺪد ﺻﺤﻴﺢ ﺑﺎ 64ﺑﺎﻳﺖ ﺣﺎﻓﻈﻪ اﺳﺖ( ﻳﻚ ﻧﻮع اﺳﺘﺎﻧﺪارد ﻧﻴﺴﺖ ،ﺗﺎﺑﻊﻫﺎي اﺳﺘﺎﻧﺪارد از آن اﺳﺘﻔﺎده ﻧﻤﻲ- ﻛﻨﻨﺪ .اﻣﺎ ﻓﺎﻳﻞﻫﺎي اﻣﺮوزي ﺧﻴﻠﻲ ﺣﺠﻴﻢ ﻫﺴﺘﻨﺪ و longﻇﺮﻓﻴﺖ ﻻزم ﺑﺮاي اﺷﺎره ﺑﻪ اﻧﺪازهي آنﻫﺎ را ﻧﺪارد .ﺑﻪ ﻧﺎﭼﺎر ﺗﺎﺑﻊ اﺳﺘﺎﻧﺪارد )( SetFilePointerﺑﺮاي اﺷﺎره ﺑﻪ ﻣﻜﺎن ﺧﺎﺻﻲ از ﻓﺎﻳﻞ ،دو ﭘﺎراﻣﺘﺮ دارد ﻛﻪ ﺗﺮﻛﻴﺐ آنﻫﺎ ﻣﻜﺎن ﺧﺎﺻﻲ از ﻓﺎﻳﻞ را ﻧﺸﺎن ﻣﻲدﻫﺪ .ﺗﺎﺑﻌﻲ ﻛﻪ ﻣﻦ ﻧﻮﺷﺘﻪام ﻛﺎر را ﺳﺎده ﻣﻲﻛﻨﺪ و ﻳﻚ ﭘﺎراﻣﺘﺮ 64ﺑﺎﻳﺘﻲ را ﺑﺮاي اﺷﺎره ﺑﻪ ﻣﻜﺎنﻫﺎي ﻳﻚ ﻓﺎﻳﻞ ﺑﻪ ﻛﺎر ﻣﻲﺑﺮد. در ﺗﺎﺑﻊ )( mainاول ﻓﺎﻳﻞ hello.txtرا ﺑﺎز ﻛﺮدهام ﻛﻪ ﻫﻤﺎن اﻃﻼﻋﺎﺗﻲ را دارد ﻛﻪ در ﺑﺮﻧﺎﻣﻪي ﻗﺒﻞ ﺑﻪ آن وارد ﻛﺮدﻳﻢ .وﻗﺘﻲ ﻓﺎﻳﻞ ﺑﺎز ﻣﻲﺷﻮد ﻣﻜﺎن ﺟﺎري ﺻﻔﺮ اﺳﺖ )ﻳﻌﻨﻲ اﺑﺘﺪاي ﻓﺎﻳﻞ اﺳﺖ( .از )( MySetFilePointerاﺳﺘﻔﺎده ﻛﺮدهام ﺗﺎ ﻣﻜﺎن ﺟﺎري را 8ﻛﻨﻢ )ﻳﻌﻨﻲ ﻗﺒﻞ از ” (“meet youﺑﻌﺪ ﺑﺎ اﺳﺘﻔﺎده از ﺗﺎﺑﻊ )( WriteFileﻣﺜﻞ ﻗﺒﻞ اﻃﻼﻋﺎت aرا )ﻳﻌﻨﻲ " "eat youرا( در ﻣﻜﺎن ﺟﺎري ﻧﻮﺷﺘﻢ. ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ اﻃﻼﻋﺎت ﺟﺪﻳﺪ اﻃﻼﻋﺎت ﻗﺒﻠﻲ را ﭘﺎك ﻣﻲﻛﻨﺪ و ﺑﻪ ﺟﺎي آنﻫﺎ ﻗﺮار ﻣﻲﮔﻴﺮد. ﺑﺮﻧﺎﻣﻪي زﻳﺮ اﻃﻼﻋﺎت را از ﻓﺎﻳﻠﻲ ﻛﻪ در ﺑﺮﻧﺎﻣﻪﻫﺎي ﻗﺒﻠﻲ ﺳﺎﺧﺘﻪ ﺷﺪ ﻣﻲﺧﻮاﻧﺪ و ﭼﺎپ ﻣﻲﻛﻨﺪ: >#include <conio.h
382
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ #include <windows.h> #include <iostream> using namespace std; unsigned __int64 MyGetFileSize(HANDLE han) { LARGE_INTEGER large_integer; large_integer.LowPart = GetFileSize(han,(LPDWORD) &large_integer.HighPart); return large_integer.QuadPart; } int main() { HANDLE han = CreateFile( "C:\\hello.txt" , FILE_ALL_ACCESS , FILE_SHARE_WRITE | FILE_SHARE_READ, 0 , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , 0 ); unsigned size = MyGetFileSize(han); char* c = new char [size+1]; unsigned long number_of_bytes_read; ReadFile( han, c, size, &number_of_bytes_read, 0 ); c[size] = 0; cout<< c; _getch(); }
Output (BDS 2006): Glad to eat you
ﻛﻪ ﺧﻮد ﻣﺎ آن را ﻧﻮﺷﺘﻪاﻳﻢMyGetFileSize() در اﻳﻦ ﺑﺮﻧﺎﻣﻪ اول ﻓﺎﻳﻞ ﺑﺎز ﻣﻲﺷﻮد و ﺑﻌﺪ ﺑﺎ اﺳﺘﻔﺎده از ﺗﺎﺑﻊ heap ﺑﺎﻳﺖ ﺣﺎﻓﻈﻪ ازsize+1 ﺑﻌﺪ ﺑﻪ اﻧﺪازهي. ذﺧﻴﺮه ﻣﻲﺷﻮدsize اﻧﺪازهي ﻓﺎﻳﻞ ﻣﺸﺨﺺ ﻣﻲﺷﻮد و در ﺗﺎﺑﻊ. ﻣﻜﺎن ﺟﺎري در ﺻﻔﺮ )اول ﻓﺎﻳﻞ( ﻗﺮار دارد. ﻣﻲﮔﻴﺮﻳﻢc درﻳﺎﻓﺖ ﻣﻲﻛﻨﻴﻢ و اﺷﺎره ﮔﺮ آن را c ﺑﺎﻳﺖ از ﻓﺎﻳﻞ )ﻳﻌﻨﻲ ﻫﻤﻪي اﻃﻼﻋﺎت ﻓﺎﻳﻞ( را در ﺣﺎﻓﻈﻪاي ﻗﺮار ﻣﻲدﻫﺪ ﻛﻪsize ﺑﻪ اﻧﺪازهReadFile() c[size] ﻳﻚ رﺷﺘﻪ ﺑﺎﺷﺪ ﻛﺎراﻛﺘﺮ آﺧﺮ آن ﺑﺎﻳﺪ ﺻﻔﺮ ﺑﺎﺷﺪ ﺑﺮاي ﻫﻤﻴﻦc ﭼﻮن ﻗﺮار اﺳﺖ.ﺑﻪ آن اﺷﺎره ﻣﻲﻛﻨﺪ
. را ﭼﺎپ ﻣﻲﻛﻨﻴﻢc را ﺻﻔﺮ ﻣﻲﻛﻨﻴﻢ و ﺑﻌﺪ
383
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﻧﺪازهي آن را ﺗﻐﻴﻴﺮ، ﻫﻨﮕﺎم ﻧﻮﺷﺘﻦ در ﻓﺎﻳﻞWriteFile() دﻳﺪﻳﻢ ﻛﻪ.ﻣﺴﺄﻟﻪي ﺑﻌﺪ ﺗﻐﻴﻴﺮ اﻧﺪازهي ﻓﺎﻳﻞ اﺳﺖ : در ﺑﺮﻧﺎﻣﻪي زﻳﺮ ﺗﺎﺑﻌﻲ ﻛﺎﻣﻞ ﺗﺮ ﺑﺮاي ﺗﻐﻴﻴﺮ اﻧﺪازهي ﻓﺎﻳﻞ ﻣﻌﺮﻓﻲ ﻛﺮدهام.ﻣﻲدﻫﺪ #include <conio.h> #include <windows.h> unsigned __int64 MySetFilePointer ( HANDLE han, const unsigned __int64& position, unsigned long from_where = FILE_BEGIN ) { LARGE_INTEGER large_integer; large_integer.QuadPart = position; large_integer.LowPart = SetFilePointer( han , large_integer.LowPart , &large_integer.HighPart, from_where ); return large_integer.QuadPart; } void MySetFileSize( HANDLE han, unsigned __int64 end_of_file, unsigned long from_where = FILE_BEGIN) { MySetFilePointer(han, end_of_file, from_where); SetEndOfFile(han); } int main() { HANDLE han = CreateFile( "C:\\hello.txt" , FILE_ALL_ACCESS , FILE_SHARE_WRITE | FILE_SHARE_READ, 0 , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , 0 ); MySetFileSize(han, 100000); _getch(); }
Output (BDS 2006): empty
384
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﺗﺎﺑﻊ )( MySetFilePointerﻫﻤﺎن اﺳﺖ ﻛﻪ در ﺑﺮﻧﺎﻣﻪﻫﺎي ﻗﺒﻞ ﻣﻌﺮﻓﻲ ﻛﺮدم .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﻨﻬﺎ در ﺗﻌﺮﻳﻒ ﺗﺎﺑﻊ )( MySetFileSizeﺑﻪ ﻛﺎر رﻓﺘﻪ .ﻛﺎر )( MySetFileSizeﺗﻐﻴﻴﺮ اﻧﺪازهي ﻳﻚ ﻓﺎﻳﻞ اﺳﺖ. در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﻓﺎﻳﻞ C:\hello.txtرا ﺑﺎز ﻛﺮدهاﻳﻢ و ﺑﻌﺪ اﻧﺪازهي آن را 100000ﺑﺎﻳﺖ ﻗﺮار دادهاﻳﻢ. ﺑﺮاي اﻳﻦ ﻛﻪ ﻣﻄﻤﺌﻦ ﺑﺎﺷﻴﺪ اﻧﺪازهي ﻓﺎﻳﻞ ﻫﻤﻴﻦ اﺳﺖ ﺑﻪ دراﻳﻮ C:ﺑﺮوﻳﺪ ،روي hello.txtﻛﻠﻴﻚ راﺳﺖ ﻛﻨﻴﺪ و Propertiesرا اﻧﺘﺨﺎب ﻛﻨﻴﺪ:
ﺗﺎ ﭘﻨﺠﺮهي زﻳﺮ ﺑﺎز ﺷﻮد:
385
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ اﮔﺮ ﻫﻨﮕﺎم ﺑﺎز ﻛﺮدن ﻳﻚ ﻓﺎﻳﻞ ﺑﺎ )( CreateFileدر آرﮔﻮﻣﺎن اول ﺑﻪ ﺟﺎي آدرس و اﺳﻢ ﻓﺎﻳﻞ ﻓﻘﻂ اﺳﻢ آن را ﺑﻨﻮﻳﺴﻴﻢ ﻓﺎﻳﻞ در ﻣﺤﻞ ﺟﺎري اﻳﺠﺎد ﻣﻲﺷﻮد ﻛﻪ ﻣﻌﻤﻮﻻ ﻫﻤﺎن ﺟﺎﻳﻲ اﺳﺖ ﻛﻪ ﻓﺎﻳﻞ .exeي ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ در آن ﻗﺮار دارد .اﻳﻦ ﺑﺮﻧﺎﻣﻪ زﻳﺮ اﺟﺮا ﻛﻨﻴﺪ: >#include <conio.h >#include <windows.h )(int main { (CreateFile ""hello.txt , FILE_ALL_ACCESS , FILE_SHARE_WRITE | FILE_SHARE_READ, 0 , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , 0 ;) ;)(_getch }
Output (BDS 2006): empty
ﺣﺎل ﺑﻪ ﻣﺤﻠﻲ ﻛﻪ ﻓﺎﻳﻞ اﺟﺮاﻳﻲ )ﻳﻌﻨﻲ ﻓﺎﻳﻞ .exeي( ﺑﺮﻧﺎﻣﻪي ﺷﻤﺎ اﻳﺠﺎد ﺷﺪه ﺑﺮوﻳﺪ و ﻓﺎﻳﻞ hello.txtرا ﭘﻴﺪا ﻛﻨﻴﺪ:
ﺑﺮاي ﺑﺴﺘﻦ ﻳﻚ ﻓﺎﻳﻞ ﺑﺎز ﺑﺎ han ِ handleﺑﺎﻳﺪ ﻧﻮﺷﺖ: ;)CloseHandle(han
386
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ .در واﻗﻊ در آﺧﺮ ﻫﻤﻪي ﺑﺮﻧﺎﻣﻪي ﻗﺒﻠﻲ ﻛﻪ ﻳﻚ ﻓﺎﻳﻞ ﺑﺎز ﺷﺪه ﺑﻬﺘﺮ اﺳﺖ ﻓﺎﻳﻞ را ﺑﺒﻨﻴﺪم ﻳﻚ ﺑﺮﻧﺎﻣﻪي ﺑﺴﻴﺎر ﺳﺎدهي وﻳﻨﺪوز را ﺑﻪ ﺷﻤﺎ ﻣﻌﺮﻓﻲ ﻣﻲﻛﻨﻢ ﺗﺎ ﺷﺮوع ﻛﺎر ﺷﻤﺎ ﺑﺎ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ وﻳﻨﺪوز،در ﭘﺎﻳﺎن : اﻳﺠﺎد ﺷﻮدproject ﻣﺴﻴﺮ زﻳﺮ را ﻃﻲ ﻛﻨﻴﺪ ﺗﺎ ﻳﻚBDS 2006 در.ﺑﺎﺷﺪ File | New | Other… | C++Builder Projects | Console Application : ﺗﻨﻈﻴﻤﺎت را ﻃﻮري ﺗﻐﻴﻴﺮ دﻫﻴﺪ ﻛﻪ ﻣﺜﻞ ﺷﻜﻞ زﻳﺮ ﺷﻮد.ﭘﻨﺤﺮهاي ﺑﺎز ﻣﻲﺷﻮد
. را ﻓﺸﺎر دﻫﻴﺪOK : ﺑﻪ وﺟﻮد آﻣﺪه ﺑﻨﻮﻳﺴﻴﺪ و اﺟﺮا ﻛﻨﻴﺪproject ﺑﺮﻧﺎﻣﻪ زﻳﺮ را در #include <windows.h> long WINAPI WindowProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hdc = GetDC(hwnd); switch(uMsg) { case WM_MOUSEMOVE: LineTo(hdc,LOWORD(lParam),HIWORD(lParam)); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } class hkNormalWindow { public: HINSTANCE LPSTR int HWND
hi; lpCmd; nCmd; hwnd;
// provided in WinMain() // provided in WinMain() // the handle of window created
387
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ //in MakeWindow() below // procedure used by window
WNDPROC Proc; char *className; WNDCLASSEX wcx; int menuID; int smallIconID;
// window class name // window class to be registered //in RegisterClass() below // ID of a menu in resource // ID of an icon in resource
hkNormalWindow(WNDPROC WndProc);// constructor gets the procedure bool RegisterClass(); // fills & registers wcx bool MakeWindow(); // creates a window using wcx bool Run(
//gets WinMain & other information HINSTANCE hinstance, // provided in WinMain() LPSTR lpCmdLine, // provided in WinMain() int nCmdShow, // provided in WinMain() int menu_id_in_resource=101, // ID of a menu
//in resource int small_icon_id_in_resource=100 // ID of an icon in //resource ); //hPrevInstance is not needed because its always 0 }; hkNormalWindow::hkNormalWindow(WNDPROC WndProc) :className("hkClass"),Proc(WndProc)//constructor {} bool hkNormalWindow::RegisterClass() { wcx.cbSize = sizeof(wcx); // size of structure wcx.style = CS_HREDRAW | CS_VREDRAW; // redraw if size changes wcx.lpfnWndProc = Proc; // points to window procedure wcx.cbClsExtra = 0; // no extra class memory wcx.cbWndExtra = 0; // no extra window memory wcx.hInstance = hi; // handle to instance wcx.hIcon =0; //default wcx.hCursor = LoadCursor(NULL,IDC_ARROW); // predefined arrow wcx.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH); // white background brush wcx.lpszMenuName = MAKEINTRESOURCE(menuID); // name of menu resource wcx.lpszClassName = className; // name of window class wcx.hIconSm =(HICON)LoadImage( // small class icon hi, MAKEINTRESOURCE(smallIconID), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR ); return RegisterClassEx(&wcx); } bool hkNormalWindow::MakeWindow()
388
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ { hwnd = CreateWindowEx(
); if (!hwnd) return false; ShowWindow(hwnd,nCmd); UpdateWindow(hwnd); return true;
WS_EX_WINDOWEDGE|WS_EX_CLIENTEDGE, className, // name of window class "Sample", // title-bar string WS_OVERLAPPEDWINDOW, // top-level window CW_USEDEFAULT, // default horizontal position CW_USEDEFAULT, // default vertical position CW_USEDEFAULT, // default width CW_USEDEFAULT, // default height (HWND) NULL, // no owner window (HMENU) NULL, // use class menu hi, // handle to application instance (LPVOID) NULL // no window-creation data
} bool hkNormalWindow::Run( HINSTANCE hinstance, LPSTR lpCmdLine, int nCmdShow, int menu_id_in_resource, int small_icon_id_in_resource ) { hi = hinstance ; lpCmd = lpCmdLine ; nCmd = nCmdShow ; menuID= menu_id_in_resource; smallIconID=small_icon_id_in_resource; if(RegisterClass()==false) return false; if(MakeWindow()==false) return false; return true; } MSG hkLoop() //message loop { MSG msg; bool bRet; while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if(bRet == -1) MessageBox(GetDesktopWindow(), "An error in receiving messages","Error",MB_OK); else { TranslateMessage(&msg); DispatchMessage(&msg);
389
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ } } ;return msg } #pragma argsused int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, )LPSTR lpCmdLine, int nCmdShow { ;)hkNormalWindow MyWindow(WindowProcedure ;)MyWindow.Run(hinstance,lpCmdLine,nCmdShow ;return hkLoop().wParam }
Output (BDS 2006):
ﻣﻬﻢ ﺗﺮﻳﻦ ﺑﺨﺶ در اﻳﻦ ﺑﺮﻧﺎﻣﻪ ﺗﺎﺑﻊ )( WindowProcedureاﺳﺖ .ﺑﺎ ﻛﻠﻴﻚ ﻛﺮدن ﻣﺎوس روي ﭘﻨﺠﺮهي ﺑﺮﻧﺎﻣﻪ ،ﺣﺮﻛﺖ ﻣﺎوس روي آن ،ﻓﺸﺎر ﻛﻠﻴﺪﻫﺎي ﺻﻔﺤﻪ ﻛﻠﻴﺪ ﻳﺎ ...ﻳﻚ ﭘﻴﺎم ) (messageﺑﻪ ﺗﺎﺑﻊ )( WindowProcedureﻓﺮﺳﺘﺎده ﻣﻲﺷﻮد .اﻳﻦ ﭘﻴﺎم ﺣﺎوي اﻃﻼﻋﺎت ﺧﺎﺻﻲ اﺳﺖ .ﻣﺜﻼ اﮔﺮ ﻣﺎوس ﺣﺮﻛﺖ ﻛﻨﺪ اﻃﻼﻋﺎت ﻣﺮﺑﻮط ﺑﻪ ﻣﻜﺎن ﻣﺎوس ﺟﺰﺋﻲ از ﭘﻴﺎم اﺳﺖ WindowProcedure() .ﺑﺎﻳﺪ ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﻧﻮع و اﻃﻼﻋﺎت ﭘﻴﺎم ﻛﺎرﻫﺎﻳﻲ را اﻧﺠﺎم ﺑﺪﻫﺪ .در اﻳﻦ ﺑﺮﻧﺎﻣﻪ وﻗﺘﻲ ﭘﻴﺎم ﺣﺮﻛﺖ ﻣﺎوس ﻣﻲرﺳﺪ، )( WindowProcedureﺧﻄﻲ از ﮔﻮﺷﻪي ﭘﻨﺠﺮه ﺑﻪ ﻣﺤﻞ ﻓﻌﻠﻲ ﻣﺎوس رﺳﻢ ﻣﻲﻛﻨﺪ.
390
www.pupuol.com
ﭘﻮﭘﻮﻝ ﻣﺮﺟﻊ ﺩﺍﻧﺸﮕﺎﻩ ﻭ ﻣﺪﺭﺳﻪ ﻛﻢ ﻛﻢ دارﻳﻢ ﺑﻪ ﺧﻂﻫﺎي ﭘﺎﻳﺎﻧﻲ اﻳﻦ ﻧﻮﺷﺘﻪ ﻧﺰدﻳﻚ ﻣﻲﺷﻮﻳﻢ .ﺗﺎ ﻧﻮﺷﺘﻪاي دﻳﮕﺮ ﻫﻤﻪي ﺷﻤﺎ ﻋﺰﻳﺰان را ﺑﻪ compilerﺗﺎن ﻣﻲﺳﭙﺎرم
.
> ا Qار
391
www.pupuol.com