Inline (суулгасан) функцууд. Функцийн хэт ачаалал

Функцийн хэт ачаалал

Үйлдлийн хэт ачаалал (оператор, функц, журам)- програмчлалд - хэд хэдэн хүрээнд нэгэн зэрэг орших боломжоос бүрддэг полиморфизмыг хэрэгжүүлэх арга замуудын нэг. янз бүрийн сонголтуудижил нэртэй боловч хамаарах параметрийн төрлөөрөө ялгаатай үйлдлүүд (мэдэгдэл, функц эсвэл процедур).

Нэр томьёо

"Хэт ачаалал" гэсэн нэр томъёо нь 1990-ээд оны эхний хагаст програмчлалын хэл дээрх номнуудын орос орчуулгад гарч ирсэн англи хэлний "хэт ачаалал" гэсэн ул мөр юм. Магадгүй энэ нь хамгийн их биш юм хамгийн сайн сонголтОрчуулга нь орос хэл дээрх "хэт ачаалал" гэсэн үг нь шинээр санал болгож буй үгнээс эрс өөр өөрийн гэсэн утгатай байдаг тул энэ нь үндэслэж, нэлээд өргөн тархсан байна. ЗХУ-ын үеийн хэвлэлүүдэд үүнтэй төстэй механизмуудыг орос хэлээр "дахин тодорхойлох" эсвэл "үйл ажиллагааг дахин тодорхойлох" гэж нэрлэдэг байсан боловч энэ сонголт нь маргаангүй юм: англи хэл дээрх "давхих", "хэт ачаалал", "хэт ачаалал" гэсэн орчуулгад зөрүү, төөрөгдөл үүсдэг. дахин тодорхойлох".

Гадаад төрх байдлын шалтгаанууд

Ихэнх эртний програмчлалын хэлүүдэд ижил нэртэй нэгээс илүү үйлдлийг нэгэн зэрэг ашиглах боломжгүй гэсэн хязгаарлалт байдаг. Үүний дагуу програмын өгөгдсөн цэг дээр харагдах бүх функц, процедур өөр өөр нэртэй байх ёстой. Програмчлалын хэлний нэг хэсэг болох функц, процедур, операторуудын нэр, тэмдэглэгээг програмист өөрийн функц, процедур, операторуудыг нэрлэхэд ашиглах боломжгүй. Зарим тохиолдолд программист өөрийн программын объектыг аль хэдийн байгаа өөр нэг объектын нэрээр үүсгэж болох боловч дараа нь шинээр үүсгэсэн объект нь өмнөх объекттой "давхцаж", хоёр сонголтыг нэгэн зэрэг ашиглах боломжгүй болдог.

Энэ байдал нь нэлээд нийтлэг тохиолдлуудад тохиромжгүй байдаг.

  • Заримдаа програмистын үүсгэсэн өгөгдлийн төрлүүдэд тухайн хэл дээр байгаатай ижил утгатай үйлдлүүдийг тайлбарлах, хэрэглэх шаардлага гардаг. Сонгодог жишээ бол комплекс тоотой ажиллах номын сан юм. Тэд энгийн тоон төрлүүдийн нэгэн адил арифметик үйлдлүүдийг дэмждэг бөгөөд эдгээрийг үүсгэх нь зүйн хэрэг юм. энэ төрлийн"нэмэх", "хасах", "үржүүлэх", "хуваах" үйлдлүүд нь бусад тоон төрлүүдийн адил үйлдлийн тэмдгээр тэмдэглэнэ. Хэл дээр тодорхойлсон элементүүдийг ашиглахыг хориглосон нь ComplexPlusComplex, IntegerPlusComplex, ComplexMinusFloat гэх мэт нэртэй олон функцийг бий болгоход хүргэдэг.
  • Операндуудад ижил утгатай үйлдлүүдийг хэрэглэх үед янз бүрийн төрөл, тэдгээрийг өөрөөр нэрлэх ёстой. Өргөдөл гаргах боломжгүй янз бүрийн төрөлижил нэртэй функцууд нь ижил зүйлд өөр өөр нэр зохион бүтээх хэрэгцээнд хүргэдэг бөгөөд энэ нь төөрөгдөл үүсгэж, алдаа гаргахад хүргэдэг. Жишээлбэл, сонгодог Си хэлэнд тооны модулийг олох стандарт номын сангийн функцийн хоёр хувилбар байдаг: abs() ба fabs() - эхнийх нь бүхэл аргумент, хоёр дахь нь бодит аргументад зориулагдсан. Энэ нөхцөл байдал нь C төрлийн сул шалгалттай хослуулан олоход хэцүү алдаад хүргэж болзошгүй: хэрвээ програмист х нь бодит хувьсагч болох abs(x) тооцоололд бичвэл зарим хөрвүүлэгчид анхааруулгагүйгээр код үүсгэдэг. бутархай хэсгүүдийг хаях замаар х-г бүхэл тоо болгон хувиргаж, үүссэн бүхэл тооноос модулийг тооцоол!

Асуудлыг хэсэгчлэн объектын програмчлалын тусламжтайгаар шийддэг - шинэ өгөгдлийн төрлүүдийг анги гэж зарлах үед тэдгээр дээрх үйлдлүүд нь ижил нэртэй ангийн аргуудыг багтаасан ангийн арга хэлбэрээр албан ёсоор албан ёсоор хэрэгжих боломжтой байдаг (учир нь өөр өөр ангиллын аргууд заавал байх албагүй. өөр өөр нэрс), гэхдээ нэгдүгээрт, янз бүрийн төрлийн утгууд дээр ажиллах ийм загвар нь тохиромжгүй, хоёрдугаарт, шинэ оператор үүсгэх асуудлыг шийдэж чадахгүй.

Операторын хэт ачаалал нь өөрөө зүгээр л "синтаксийн элсэн чихэр" боловч энэ нь хөгжүүлэгчдэд илүү байгалийн аргаар программчлах боломжийг олгож, захиалгат төрлүүдийг суулгасан шиг ажиллах боломжийг олгодог тул ашигтай байж болох юм. Хэрэв бид асуудалд илүү ерөнхий байр сууринаас хандвал хэлийг өргөжүүлэх, түүнийг шинэ үйлдлүүд, синтаксийн бүтцээр баяжуулах (мөн үйлдлүүдийг хэт ачаалах нь объект, макро гэх мэт ийм хэрэгслүүдийн нэг юм) гэдгийг харж болно. , функциональ, хаалт) нь үүнийг аль хэдийн метал хэлээр эргүүлэх нь тодорхой даалгаварт чиглэсэн хэлийг дүрслэх хэрэгсэл юм. Үүний тусламжтайгаар тодорхой ажил бүрийн хувьд хамгийн тохиромжтой хэлний өргөтгөлийг бий болгох боломжтой бөгөөд энэ нь түүний шийдлийг хамгийн энгийн, ойлгомжтой, энгийн хэлбэрээр тайлбарлах боломжийг олгоно. Жишээлбэл, хэт ачаалах үйлдлүүдийн програмд: нарийн төвөгтэй математик төрлүүдийн номын сан (вектор, матриц) үүсгэх, тэдгээртэй хийсэн үйлдлүүдийг байгалийн, "математик" хэлбэрээр дүрслэх нь "вектор үйлдлийн хэл" -ийг бий болгодог. Тооцоолол нь далд байдаг бөгөөд техник дээр бус асуудлын мөн чанарт анхаарлаа хандуулж, вектор ба матрицын үйлдлээр асуудлын шийдлийг дүрслэх боломжтой. Эдгээр шалтгааны улмаас ийм хэрэгслийг нэгэн цагт Алгол-68 хэлэнд оруулсан байдаг.

Хэт ачааллын механизм

Хэрэгжилт

Операторын хэт ачаалал нь хэлэнд харилцан уялдаатай хоёр шинж чанарыг нэвтрүүлэх явдал юм: ижил нэртэй хэд хэдэн процедур эсвэл функцийг ижил хүрээнд тунхаглах чадвар, үйл ажиллагааны өөрийн хэрэгжилтийг тайлбарлах чадвар (өөрөөр хэлбэл үйл ажиллагааны шинж тэмдэг, ихэвчлэн infix тэмдэглэгээгээр, операндуудын хооронд бичигдсэн). Үндсэндээ тэдгээрийг хэрэгжүүлэх нь маш энгийн:

  • Ижил нэртэй хэд хэдэн үйлдлийг бий болгохын тулд хэл дээр дүрмийг нэвтрүүлэхэд хангалттай бөгөөд үүний дагуу үйлдлийг (процедур, функц эсвэл оператор) хөрвүүлэгч зөвхөн нэрээр нь (тэмдэглэгээ) хүлээн зөвшөөрдөг. мөн тэдгээрийн параметрийн төрлөөр. Тэгэхээр i-г бүхэл тоо гэж зарласан abs(i), х-г бодит гэж зарласан abs(x) нь хоёр өөр үйлдэл юм. Үндсэндээ ийм тайлбар өгөхөд ямар ч бэрхшээл байхгүй.
  • Тодорхойлох, дахин тодорхойлох үйлдлүүдийг идэвхжүүлэхийн тулд хэлэнд тохирох синтаксийн бүтцийг нэвтрүүлэх шаардлагатай. Маш олон сонголт байж болох ч үнэн хэрэгтээ тэдгээр нь бие биенээсээ ялгаатай биш тул маягтыг оруулах нь хангалттай гэдгийг санах нь зүйтэй.<операнд1> <знакОперации> <операнд2>» нь үндсэндээ « функцийг дуудахтай төстэй.<знакОперации>(<операнд1>,<операнд2>)". Программист операторуудын зан төлөвийг функц хэлбэрээр дүрслэхийг зөвшөөрөхөд хангалттай бөгөөд тайлбарын асуудал шийдэгдэнэ.

Сонголт ба асуудал

Түвшинд хэт ачаалал өгөх журам, функцууд нийтлэг санааДүрмээр бол хэрэгжүүлэх, ойлгоход хэцүү биш юм. Гэсэн хэдий ч үүнд анхаарч үзэх ёстой зарим "нуруу" байдаг. Операторын хэт ачааллыг зөвшөөрөх нь тухайн хэл дээр ажиллаж байгаа хэл хэрэгжүүлэгч болон програмист хоёуланд нь илүү их асуудал үүсгэдэг.

Таних асуудал

Процедур, функцийг хэт ачаалах боломжийг олгодог хэлний орчуулагч хөгжүүлэгчийн тулгардаг хамгийн эхний асуулт бол ижил нэртэй процедуруудын дотроос тухайн зүйлд хэрэглэхийг хэрхэн сонгох вэ гэдэг асуулт юм. тодорхой тохиолдол? Хэрэв энэ дуудлагад ашигласан бодит параметрүүдийн төрлүүдтэй яг таарч байгаа албан ёсны параметрүүдийн төрлүүд байгаа бол бүх зүйл хэвийн байна. Гэсэн хэдий ч бараг бүх хэл дээр хөрвүүлэгч нь тодорхой нөхцөл байдалд автоматаар төрлийн аюулгүй хөрвүүлэлтийг хийдэг гэж үзвэл төрлүүдийг ашиглахад тодорхой хэмжээний эрх чөлөө байдаг. Жишээлбэл, бодит болон бүхэл тооны аргументууд дээр арифметик үйлдлүүд хийхдээ бүхэл тоо нь ихэвчлэн автоматаар бодит төрөлд хувирдаг бөгөөд үр дүн нь бодит байдаг. Нэмэх функцийн хоёр хувилбар байна гэж бодъё:

int нэмэх(int a1, int a2); хөвөх нэмэх(хөвөгч a1, хөвөх a2);

Хөрвүүлэгч нь x нь хөвөгч, i нь int байх y = add(x, i) илэрхийллийг хэрхэн зохицуулах ёстой вэ? Яг тохирох зүйл байхгүй нь ойлгомжтой. Хоёр сонголт байна: y=add_int((int)x,i) , эсвэл y=add_flt(x, (float)i) (энд add_int болон add_float нэр нь функцийн эхний болон хоёр дахь хувилбарыг тус тус илэрхийлнэ) .

Асуулт гарч ирнэ: хөрвүүлэгч нь хэт ачаалалтай функцуудыг ашиглахыг зөвшөөрөх ёстой юу, хэрэв тийм бол ашигласан хувилбарыг ямар үндэслэлээр сонгох вэ? Ялангуяа дээрх жишээн дээр орчуулагч сонгохдоо y хувьсагчийн төрлийг харгалзан үзэх ёстой юу? Дээрх нөхцөл байдал нь хамгийн энгийн, илүү төвөгтэй тохиолдлууд байж болох бөгөөд энэ нь зөвхөн суулгасан төрлүүдийг хэлний дүрмийн дагуу хөрвүүлэхээс гадна програмистын зарласан ангиудыг хөрвүүлэх боломжтой гэдгийг тэмдэглэх нь зүйтэй. , хэрэв тэд ураг төрлийн харилцаатай бол бие биендээ цутгаж болно. Энэ асуудлыг шийдэх хоёр арга бий:

  • Буруу танихыг огт хоригло. Тодорхой хос төрөл бүрийн хувьд хэт ачаалалтай процедур эсвэл үйлдлийн яг тохирсон хувилбар байхыг шаардана. Хэрэв ийм сонголт байхгүй бол хөрвүүлэгч алдаа гаргах ёстой. Энэ тохиолдолд програмист нь бодит параметрүүдийг хүссэн төрлүүдэд шилжүүлэхийн тулд тодорхой хөрвүүлэлт хийх ёстой. Энэ арга нь C++ гэх мэт хэлнүүдэд тохиромжгүй бөгөөд энэ нь суулгасан болон хэт ачаалалтай операторуудын зан төлөвт мэдэгдэхүйц ялгаа үүсгэдэг (ердийн тоонуудад арифметик үйлдлүүдийг ашиглаж болно) учир төрлүүдтэй харьцахад хангалттай эрх чөлөө олгодог. бодолгүйгээр, гэхдээ бусад төрлүүдэд - зөвхөн тодорхой хөрвүүлэлтээр) эсвэл үйл ажиллагааны асар олон тооны сонголтууд гарч ирдэг.
  • "Хамгийн ойр тохирохыг" сонгох тодорхой дүрмийг бий болго. Ихэвчлэн энэ хувилбарт хөрвүүлэгч нь дуудлагыг нь зөвхөн аюулгүй (алдагдалгүй мэдээлэл) төрлийн хөрвүүлэлтээр эх сурвалжаас авах боломжтой хувилбаруудыг сонгодог бөгөөд хэрэв тэдгээр нь хэд хэдэн байгаа бол аль хувилбарт цөөн шаардлагатай байгааг харгалзан сонгож болно. ийм хөрвүүлэлтүүд. Хэрэв үр дүн нь нэгээс олон боломжийг үлдээвэл хөрвүүлэгч алдаа гаргаж, програмистаас хувилбарыг тодорхой зааж өгөхийг шаарддаг.

Үйл ажиллагааны хэт ачаалалтай холбоотой онцгой анхаарах зүйлс

Процедур, функцээс ялгаатай нь програмчлалын хэлний инфиксийн үйлдлүүд нь тэдгээрийн функциональ байдалд ихээхэн нөлөөлдөг хоёр нэмэлт шинж чанартай байдаг: тэргүүлэх ач холбогдол ба нэгдмэл байдал нь операторуудын "гинжин" бичлэг хийх боломжтой (a + b * -ийг хэрхэн ойлгох вэ) холбоотой байдаг. в: (a + b )*c эсвэл a+(b*c) шиг ? a-b+c илэрхийлэл нь (a-b)+c эсвэл a-(b+c) ?).

Хэлэнд суулгасан үйлдлүүд нь үргэлж уламжлалт давуу эрх, холбоог урьдчилан тодорхойлсон байдаг. Асуулт гарч ирнэ: эдгээр үйлдлүүдийн дахин тодорхойлсон хувилбарууд, эсвэл программистын бүтээсэн шинэ үйлдлүүд ямар тэргүүлэх чиглэл, холбоо байх вэ? Тодруулга шаардлагатай байж болох бусад нарийн мэдрэмжүүд байдаг. Жишээлбэл, Си хэлэнд нэмэх, бууруулах операторууд ++ ба -- - угтвар, дараах хоёр хэлбэр байдаг бөгөөд тэдгээр нь өөр өөрөөр ажилладаг. Ийм операторуудын хэт ачаалалтай хувилбарууд хэрхэн ажиллах ёстой вэ?

Өөр өөр хэлүүд эдгээр асуудлыг янз бүрийн аргаар шийддэг. Иймээс C++ хэл дээр операторуудын хэт ачаалалтай хувилбаруудын давуу байдал ба холбоо нь тухайн хэл дээр тодорхойлсонтой ижил хэвээр хадгалагддаг; Тусгай гарын үсгийг ашиглан нэмэгдүүлэх, бууруулах операторуудын угтвар болон дараах хэлбэрийг тусад нь хэт ачаалах боломжтой.

Тиймээс int нь гарын үсгийн ялгааг бий болгоход хэрэглэгддэг

Шинэ үйл ажиллагааны зарлал

Шинэ үйл ажиллагаа зарлахтай холбоотой нөхцөл байдал бүр ч төвөгтэй. Ийм тунхаглалыг хэлээр оруулах нь тийм ч хэцүү биш боловч түүнийг хэрэгжүүлэхэд ихээхэн бэрхшээлтэй тулгардаг. Шинэ үйл ажиллагаа зарлах нь үнэндээ шинэ үйл ажиллагааг бий болгож байна түлхүүр үгПрограмчлалын хэл нь текст дэх үйлдлүүд нь дүрмээр бол бусад жетонуудтай тусгаарлахгүйгээр дагаж мөрддөг тул төвөгтэй байдаг. Тэд гарч ирэхэд лексик анализаторын зохион байгуулалтад нэмэлт бэрхшээлүүд гарч ирдэг. Жишээлбэл, хэрэв хэлэнд "+" ба нэгдмэл "-" (тэмдэг солих) үйлдлүүд байгаа бол a+-b илэрхийллийг + (-b) гэж зөв тайлбарлаж болно, гэхдээ шинэ үйлдэл +- програмд ​​зарласан бол тэр даруй тодорхой бус байдал үүсдэг, учир нь ижил илэрхийллийг аль хэдийн a (+-) b гэж задлан шинжилж болно. Хэлний боловсруулагч, хэрэгжүүлэгч нь иймэрхүү асуудлуудыг ямар нэгэн байдлаар шийдвэрлэх ёстой. Сонголтууд дахин өөр байж болно: бүх шинэ үйлдлүүд нэг тэмдэгттэй байхыг шаардаж, ямар нэгэн зөрчил гарсан тохиолдолд үйлдлийн "хамгийн урт" хувилбарыг сонгоно (өөрөөр хэлбэл, дараагийн тэмдэгтүүдийн уншсан тэмдэгт хүртэл) Орчуулагч ямар ч үйлдэлтэй таарч байвал түүнийг үргэлжлүүлэн уншдаг), орчуулгын явцад зөрчилдөөнийг илрүүлж, маргаантай тохиолдлуудад алдаа гаргахыг хичээгээрэй ... Шинэ үйлдлийг зарлах боломжийг олгодог хэлүүд эдгээр асуудлыг шийддэг.

Шинэ үйл ажиллагааны хувьд нэгдэл, тэргүүлэх чиглэлийг тодорхойлох асуудал бас байдгийг мартаж болохгүй. Хэлний стандарт үйлдлийн хэлбэрээр бэлэн шийдэл байхаа больсон бөгөөд ихэвчлэн та эдгээр параметрүүдийг хэлний дүрмээр тохируулах хэрэгтэй. Жишээлбэл, бүх шинэ үйлдлүүдийг зүүн-холбоотой болгож, тэдгээрт ижил, тогтмол, тэргүүлэх ач холбогдол өгөх эсвэл аль алиныг нь зааж өгөх арга хэрэгслийг хэлэнд нэвтрүүлэх.

Хэт ачаалал ба полиморф хувьсагч

Хэт ачаалалтай операторууд, функцууд болон процедуруудыг хувьсагч бүр нь урьдчилан зарласан төрөлтэй хэлээр ашиглах үед хэр төвөгтэй байсан ч тухайн тохиолдол бүрт хэт ачаалалтай операторын аль хувилбарыг ашиглахыг хөрвүүлэгч өөрөө шийднэ. . Энэ нь хөрвүүлсэн хэлнүүдийн хувьд операторын хэт ачааллыг ашиглах нь гүйцэтгэлийн доройтолд хүргэдэггүй гэсэн үг юм - ямар ч тохиолдолд програмын объект кодонд сайн тодорхойлсон ажиллагаа эсвэл функцийн дуудлага байдаг. Хэл дэх полиморф хувьсагч, өөрөөр хэлбэл өөр өөр цаг үед өөр өөр төрлийн утгыг агуулж болох хувьсагчдыг ашиглах боломжтой бол нөхцөл байдал өөр байна.

Кодыг орчуулах үед хэт ачаалалтай үйлдлийн утгын төрөл тодорхойгүй тул хөрвүүлэгч сонгох боломжгүй. хүссэн сонголтурьдчилан. Энэ тохиолдолд объектын кодонд фрагмент оруулах шаардлагатай бөгөөд энэ үйлдлийг гүйцэтгэхийн өмнө аргумент дахь утгуудын төрлийг тодорхойлж, энэ төрлийн төрлүүдэд тохирох хувилбарыг динамикаар сонгох болно. Түүгээр ч зогсохгүй ийм тодорхойлолтыг үйлдлийг гүйцэтгэх бүрт хийх ёстой, учир нь хоёр дахь удаагаа дуудагдсан ижил кодыг өөр өөрөөр гүйцэтгэх боломжтой.

Тиймээс операторын хэт ачааллыг полиморф хувьсагчтай хослуулан ашиглах нь аль кодыг дуудахыг динамикаар тодорхойлох зайлшгүй шаардлагатай болгодог.

Шүүмжлэл

Хэт ачааллыг ашиглах нь бүх шинжээчдийн хувьд ашиг тус гэж тооцогддоггүй. Хэрэв функц болон процедурын хэт ачаалал нь ерөнхийдөө дургүйцэх зүйлгүй бол (хэсэгчлэн энэ нь "оператор"-ын ердийн асуудалд хүргэдэггүй, зарим талаараа үүнийг буруугаар ашиглах сонирхол багатай учраас) операторын хэт ачаалал нь зарчмын хувьд, мөн тусгай хэлний хэрэгжилтэд, олон програмчлалын онолч, практикчдийн нэлээд хатуу шүүмжлэлд өртөж байна.

Шүүмжлэгчид дээр дурьдсан таних, давуу эрх, нэгдлийн асуудал нь хэт ачаалалтай операторуудтай харьцах нь шаардлагагүй эсвэл байгалийн бус болгодог гэдгийг онцолж байна.

  • Таних. Хэрэв хэл нь таних хатуу дүрэмтэй бол программист ямар төрлийн хослолуудад хэт ачаалал ихтэй үйлдлүүд байгааг санаж, тэдгээрт операндуудыг гараар дамжуулах шаардлагатай болдог. Хэрэв хэл нь "ойролцоогоор" таних боломжийг олгодог бол зарим нэг төвөгтэй нөхцөл байдалд програмистын бодож байсан үйлдлийн яг хувилбарыг гүйцэтгэнэ гэдэгт хэзээ ч итгэлтэй байж чадахгүй.
  • Тэргүүлэх, нэгдмэл байх. Хэрэв тэдгээрийг хатуу тодорхойлсон бол энэ нь тохиромжгүй бөгөөд тухайн сэдэвт хамааралгүй байж болно (жишээлбэл, олонлогтой үйлдлийн хувьд тэргүүлэх чиглэлүүд нь арифметикээс ялгаатай). Хэрэв тэдгээрийг програмист тохируулж чадвал энэ нь алдааны нэмэлт эх үүсвэр болно (хэрэв нэг үйлдлийн өөр өөр хувилбарууд нь өөр өөр тэргүүлэх чиглэлтэй эсвэл бүр нэгдмэл шинж чанартай байдаг бол).

Өөрийнхөө үйл ажиллагааг ашиглахад тав тухтай байдал нь програмын хяналтыг муутгахаас хэр их хүндрэл учруулж болох вэ гэдэг нь тодорхой хариултгүй асуулт юм.

Хэлний хэрэгжилтийн үүднээс авч үзвэл ижил асуудал нь орчуулагчдын нарийн төвөгтэй байдал, тэдний үр ашиг, найдвартай байдлыг бууруулахад хүргэдэг. Мөн полиморф хувьсагчтай хамт хэт ачааллыг ашиглах нь эмхэтгэлийн явцад хатуу кодлогдсон үйлдлийг дуудахаас илүү удаан бөгөөд объектын кодыг оновчтой болгох боломж багатай байдаг. Төрөл бүрийн хэл дээр хэт ачааллыг хэрэгжүүлэх онцлог шинж чанарууд нь тусдаа шүүмжлэлд өртдөг. Тиймээс C++ хэл дээр шүүмжлэлийн объект нь хэт ачаалалтай функцүүдийн нэрсийн дотоод дүрслэлийн талаархи тохиролцооны дутагдал байж болох бөгөөд энэ нь өөр өөр C++ хөрвүүлэгчдийн эмхэтгэсэн номын сангийн түвшинд үл нийцэх байдлыг үүсгэдэг.

Зарим шүүмжлэгчид хэт ачаалалтай үйл ажиллагааны эсрэг ярьж байна, үндэслэн ерөнхий зарчимхөгжлийн онол програм хангамжболон бодит үйлдвэрлэлийн практик.

  • Wirth эсвэл Hoare зэрэг хэлийг бий болгоход "пуритан" хандлагыг дэмжигчид операторын хэт ачааллыг амархан арилгах боломжтой учраас эсэргүүцдэг. Тэдний үзэж байгаагаар ийм хэрэгсэл нь зөвхөн хэл, орчуулагчийг хүндрүүлдэг бөгөөд энэ хүндрэлтэй тохирох зүйлийг өгөхгүй нэмэлт функцууд. Тэдний үзэж байгаагаар хэлийг даалгаварт чиглэсэн өргөтгөл бий болгох санаа нь зөвхөн сонирхол татахуйц харагдаж байна. Бодит байдал дээр хэлний өргөтгөлийн хэрэгслийг ашиглах нь програмыг зөвхөн энэ өргөтгөлийг боловсруулсан зохиогчид нь ойлгомжтой болгодог. Хөтөлбөр нь бусад програмистуудад ойлгох, дүн шинжилгээ хийхэд илүү хэцүү болж, засвар үйлчилгээ хийх, өөрчлөх, багийг хөгжүүлэхэд илүү хэцүү болгодог.
  • Хэт ачааллыг ашиглах боломж нь ихэвчлэн өдөөн хатгасан үүрэг гүйцэтгэдэг: програмистууд үүнийг аль болох ашиглаж эхэлдэг бөгөөд үүний үр дүнд програмыг хялбарчлах, хялбаршуулах хэрэгсэл нь түүний хүндрэл, төөрөгдлийн шалтгаан болдог.
  • Ачаалал ихтэй операторууд төрлөөсөө хамааран тэднээс хүлээгдэж буй зүйлийг яг хийхгүй байж болно. Жишээлбэл, a + b нь ихэвчлэн (гэхдээ үргэлж биш) b + a -тай ижил утгатай боловч "нэг" + "хоёр" нь + оператор хэт ачаалалтай байдаг хэл дээрх "хоёр" + "нэг" -ээс ялгаатай байдаг. мөр холбох.
  • Операторын хэт ачаалал нь програмын хэсгүүдийг контекстэд илүү мэдрэмтгий болгодог. Илэрхийлэлд хамаарах операндуудын төрлийг мэдэхгүй бол хэт ачаалалтай оператор ашигладаг бол илэрхийлэл юу хийхийг ойлгох боломжгүй юм. Жишээ нь, C++ програмд ​​мэдэгдэл<< может означать и побитовый сдвиг, и вывод в поток. Выражение a << 1 возвращает результат побитового сдвига значения a на один бит влево, если a - целая переменная, но если a является выходным потоком , то же выражение выведет в этот поток строку «1» .

Ангилал

Дараах нь зарим програмчлалын хэлийг операторын хэт ачааллыг зөвшөөрдөг эсэх, операторууд урьдчилан тодорхойлсон багцаар хязгаарлагдаж байгаа эсэхээс хамааран ангилдаг.

Үйл ажиллагаа Хэт ачаалал байхгүй Хэт ачаалал байна
Хязгаарлагдмал үйлдлийн багц
  • Зорилго-C
  • Python
Шинэ үйл ажиллагааг тодорхойлох боломжтой
  • PostgreSQL
  • бас үзнэ үү

    Викимедиа сан. 2010 он.

    Бусад толь бичгүүдээс "Функцийн хэт ачаалал" гэж юу болохыг харна уу:

      - (операторууд, функцууд, процедурууд) програмчлалын үйл ажиллагааны хэд хэдэн өөр хувилбаруудын (оператор, функц эсвэл ... ... Википедиа) нэг хүрээнд нэгэн зэрэг орших боломжийг бүрдүүлдэг полиморфизмыг хэрэгжүүлэх арга замуудын нэг.

Функцийн хэт ачаалал гэдэг нь ижил нэртэй боловч өөр өөр параметртэй хэд хэдэн функцийг (хоёр ба түүнээс дээш) тодорхойлох явдал юм. Хэт ачаалалтай функцүүдийн параметрийн багц нь дараалал, тоо, төрлөөр ялгаатай байж болно. Тиймээс ижил төстэй үйлдлүүдийг гүйцэтгэдэг боловч өөр програмын логиктой функцүүдийн нэрийг давтахаас зайлсхийхийн тулд функцийг хэт ачаалах шаардлагатай болдог. Жишээлбэл, тэгш өнцөгтийн талбайг тооцоолох areaRectangle() функцийг авч үзье.

Float areaRectangle(float, float) //а(см) ба b(см) хоёр параметр бүхий тэгш өнцөгтийн талбайг тооцоолох функц ( a * b буцаана; // тэгш өнцөгтийн талуудын уртыг үржүүлж буцаана. гарсан бүтээгдэхүүн)

Тэгэхээр энэ нь хоёр хөвөгч төрлийн параметртэй функц бөгөөд функцэд дамжуулсан аргументууд нь сантиметрээр байх ёстой, хөвөх төрлийн буцах утга нь мөн сантиметрээр байна.

Бидний анхны өгөгдөл (тэгш өнцөгт талууд) метр, сантиметрээр өгөгдсөн гэж бодъё, жишээлбэл: a = 2м 35 см; b = 1м 86 см Энэ тохиолдолд дөрвөн параметртэй функцийг ашиглах нь тохиромжтой байх болно. Өөрөөр хэлбэл тэгш өнцөгтийн талуудын урт бүрийг метр ба сантиметр гэсэн хоёр параметрээр функцэд дамжуулдаг.

Float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // a(m) a(см) 4 параметр бүхий тэгш өнцөгтийн талбайг тооцоолох функц); b(m) b(см) (буцах (a_m * 100 + a_sm) * (b_m * 100 + b_sm); )

Функцийн биед метрээр дамжуулсан утгуудыг (a_m ба b_m) сантиметр болгон хувиргаж, a_sm b_sm утгууд дээр нэмсэний дараа бид нийлбэрийг үржүүлж, тэгш өнцөгтийн талбайг авна. см-ээр Мэдээжийн хэрэг, анхны өгөгдлийг сантиметр болгон хувиргаж, эхний функцийг ашиглах боломжтой байсан, гэхдээ одоо энэ тухай биш юм.

Одоо хамгийн чухал зүйл бол бид хоёр функцтэй, өөр өөр гарын үсэгтэй, гэхдээ ижил нэртэй (хэт ачаалалтай функцүүд). Гарын үсэг нь функцийн нэрийг түүний параметрүүдтэй хослуулсан хослол юм. Эдгээр функцийг хэрхэн дуудах вэ? Хэт ачаалалтай функцуудыг дуудах нь ердийн функцуудыг дуудахаас ялгаатай биш юм, жишээлбэл:

AreaRectangle(32, 43); // a(см) ба b(см) талбайRectangle(4, 43, 2, 12) гэсэн хоёр параметр бүхий тэгш өнцөгтийн талбайг тооцоолох функц дуудагдах болно; // a(m) a(см) 4 параметр бүхий тэгш өнцөгтийн талбайг тооцоолох функц дуудагдах болно; б(м) б(см)

Таны харж байгаагаар хөрвүүлэгч нь зөвхөн хэт ачаалалтай функцүүдийн гарын үсгийг шинжлэх замаар хүссэн функцийг бие даан сонгох болно. Функцийн хэт ачааллыг алгасаж, өөр нэртэй функцийг зүгээр л зарлаж болох бөгөөд энэ нь үүргээ сайн гүйцэтгэх болно. Гэхдээ танд хоёроос илүү ийм функц хэрэгтэй бол юу болохыг төсөөлөөд үз дээ, жишээ нь 10. Мөн тус бүрдээ утга учиртай нэр гаргаж ирэх хэрэгтэй бөгөөд санахад хамгийн хэцүү зүйл бол тэдгээрийг санах явдал юм. Тийм ч учраас мэдээжийн хэрэг шаардлагагүй бол функцуудыг хэт ачаалах нь илүү хялбар бөгөөд илүү дээр юм. Програмын эх кодыг доор харуулав.

#include "stdafx.h" #include << "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

// код Код::Блокууд

// Dev-C++ код

#оруулна namespace std ашиглах; // хэт ачаалалтай функцүүдийн прототипүүд float areaRectangle(float a, float b); хөвөх талбайТэгш өнцөгт(хөвөх a_m, хөвөх a_sm, хөвөх b_m, хөвөх b_sm); int main() ( cout<< "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

Хөтөлбөрийн үр дүнг Зураг 1-т үзүүлэв.



Си хэл дээрх функцийн хэт ачаалалд хэрхэн хүрэх вэ? (10)

Си хэл дээр функцийг хэт ачаалахад хүрэх арга бий юу? Би хэт ачаалалтай байж болох энгийн функцуудыг хайж байна

foo (int a) foo (char b) foo (float c , int d)

Шууд зам байхгүй гэж би бодож байна; Хэрэв байгаа бол би үүнийг шийдвэрлэх арга замыг хайж байна.

Доорх код нь функцийн хэт ачааллыг ойлгоход тусална гэж найдаж байна

#оруулна #оруулна int fun(int a, ...); int main(int argc, char *argv)( fun(1,10); fun(2,"cquestionbank"); буцах 0; ) int fun(int a, ...)( va_list vl; va_start(vl,a) ); if(a==1) printf("%d",va_arg(vl,int)); else printf("\n%s",va_arg(vl,char *)); )

Үгүй ээ, чадахгүй гэсэн үг.

Та va_arg функцийг зарлаж болно

void my_func(char* формат, ...);

Гэхдээ та printf() гэх мэт эхний аргумент дахь хувьсагчийн тоо болон тэдгээрийн төрлүүдийн талаарх зарим мэдээллийг дамжуулах шаардлагатай болно.

Тийм ээ, дуртай.

Энд та жишээ хэлье:

void printA(int a)( printf("Hello world from printA: %d\n",a); ) void printB(const char *buff)( printf("Hello world from printB: %s\n",buff) ; ) #define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 #define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define _Num_ARGS_(... ) __VA_ARG_N(__VA_ARGS__) #NUM_ARGS(...)-г тодорхойлно if(NUM_ARGS(args) #define print(x , args ...) \ CHECK_ARGS_MIN_LIMIT(1) printf("алдаа");fflush(stdout); \ CHECK_ARGS_MAX_LIMIT(4) printf("алдаа");fflush(stdout) ; \ (( \ хэрэв (__бүтээсэн_төрлийн_нийцтэй_p (төрөл (x), int)) \ printA(x, ##args); \ else \ printB (x,##args); \ )) int main(int argc, char* * argv) ( int a=0; хэвлэх(a); хэвлэх("сайн уу"); буцах (EXIT_SUCCESS); )

Энэ нь printA болон printB-ээс 0 болон hello гаргана.

Хэрэв таны хөрвүүлэгч нь gcc бөгөөд та шинэ хэт ачаалал нэмэх болгондоо гараар шинэчлэлт хийхээс татгалзахгүй бол та макро масс хийж, дууддаг хүмүүсийн үзэл бодлоос хүссэн үр дүнг авах боломжтой, бичих нь тийм ч таатай биш ... гэхдээ Энэ боломжтой

__builtin_types_compatible_p-г хараад дараа нь үүнтэй төстэй зүйл хийх макро тодорхойлоход ашиглана уу.

#define foo(a) \ ((__бүтээсэн_төрөл_нийцтэй_p(int, a)?foo(a):(__бүтээсэн_төрөл_нийцтэй_p(хөвөгч, а)?foo(a):)

гэхдээ тийм ээ, муухай, зүгээр л үгүй

ЗАСАХ: C1X нь дараах төрлийн илэрхийлэлд дэмжлэг үзүүлэх болно.

#define cbrt(X) _Generic((X), long double: cbrtl, \ default: cbrt, \ float: cbrtf)(X)

Өмнө дурьдсанчлан, таны хэлж байгаа утгаараа хэт ачааллыг C дэмждэггүй. Асуудлыг шийдвэрлэх ердийн хэлц үг бол функцэд тэмдэглэгдсэн нэгдлийг авах явдал юм. Үүнийг бүтцийн параметрийг ашиглан хэрэгжүүлдэг бөгөөд бүтэц нь өөрөө enum гэх мэт зарим төрлийн төрлийн заагч болон өөр өөр утгын төрлүүдийн нэгдэлээс бүрддэг. Жишээ:

#оруулна typedef enum ( T_INT, T_FLOAT, T_CHAR, ) my_type; typedef бүтэц ( my_type төрөл; union ( int a; float b; char c; ) my_union; ) my_struct; хүчингүй болгох багц_хэт ачаалал (миний_бүтэц *ямар ч байсан) (шилжүүлэлт (ямар ч байсан->төрөл) ( тохиолдол T_INT: ямар ч байсан->my_union.a = 1; завсарлага; тохиолдол T_FLOAT: юу ч байсан->my_union.b = 2.0; завсарлага; тохиолдол T_CHAR: юу ч байсан-> my_union.c = "3"; ) ) void printf_overload (my_struct *ямар ч байсан) (шилж (ямар ч байсан->төрөл) ( case T_INT: printf("%d\n", ямар ч хамаагүй->my_union.a); завсарлага; case T_FLOAT : printf("%f\n", юу ч байсан->my_union.b); завсарлага; тохиолдол T_CHAR: printf("%c\n", юу ч байсан->my_union.c); завсарлага; ) ) int main (int argc, char* argv) ( my_struct s; s.type=T_INT; set_overload(&s); printf_overload(&s); s.type=T_FLOAT; set_overload(&s); printf_overload(&s); s.type=T_CHAR; set_overload(&s) ; printf_overload(&s); )

Та зүгээр л C++-г ашиглаж, үүнээс гадна бусад бүх C++ функцуудыг ашиглаж болохгүй гэж үү?

Хэрэв одоог хүртэл хатуу чанга С байгаагүй бол би оронд нь вариадик функцуудыг санал болгож байна.

Дараахь арга нь үүнтэй төстэй юм a2800276, гэхдээ зарим C99 макротой:

// бидэнд `size_t` #include хэрэгтэй // sum_arg_types тоолохыг хүлээн авах аргументийн төрлүүд ( SUM_LONG, SUM_ULONG, SUM_DOUBLE ); // аргумент барих бүтэц struct sum_arg ( enum sum_arg_types type; union ( урт_урт; тэмдэггүй урт урт; давхар_давхар; ) утга; ); // массивын хэмжээг тодорхойлох #define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY))) // бидний функц ингэж нэрлэгдэх болно #define sum(...) _sum( count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__)) // `struct sum_arg`-ын массив үүсгэх #define sum_args(...) ((struct sum_arg )( __VA_ARGS__ )) //f анхдагч нийлбэрүүдийг үүсгэх (УТГА) ( SUM_LONG, ( .as_long = (VALUE) ) ) #define sum_ulong(VALUE) ( SUM_ULONG, ( .as_ulong = (VALUE) ) ) #define sum_double(VALUE) ( SUM_DOUBLE, ( .a_ALUEuble) ) // манай полиморф функц long double _sum(size_t count, struct sum_arg * args) ( long double value = 0; for(size_t i = 0; i)< count; ++i) { switch(args[i].type) { case SUM_LONG: value += args[i].value.as_long; break; case SUM_ULONG: value += args[i].value.as_ulong; break; case SUM_DOUBLE: value += args[i].value.as_double; break; } } return value; } // let"s see if it works #include int main() ( тэмдэггүй урт foo = -1; урт давхар утга = нийлбэр(нийлбэр_урт(42), нийлбэр_улонг(foo), нийлбэр_давхар(1e10)); printf("%Le\n", утга); буцах 0; )

Одоогийн байдлаар _Generic асуултын _Generic-ээс хойш стандарт C (өргөтгөл байхгүй) үр дүнтэй байна хүлээн авсан C11-д _Generic _Generic үгийг нэмсний ачаар хэт ачаалах функцуудыг (операторуудаас илүү) дэмждэг. (4.9 хувилбараас хойш GCC-д дэмжигдсэн)

(Асуултанд үзүүлсэн шиг хэт ачаалал нь үнэхээр "барьсан" биш, гэхдээ иймэрхүү ажилладаг зүйлийг устгахад хялбар байдаг.)

Generic нь sizeof болон _Alignof-тай нэг гэр бүлийн эмхэтгэлийн цагийн оператор юм. Үүнийг стандартын 6.5.1.1-д тайлбарласан болно. Үүнд хоёр үндсэн параметр шаардлагатай: илэрхийлэл (үүнийг ажиллах үед үнэлэхгүй) болон төрөл/илэрхийллийн холбоодын жагсаалт нь шилжүүлэгч блоктой төстэй юм. _Generic нь илэрхийллийн ерөнхий төрлийг аваад дараа нь түүн рүү "шилждэг" жагсаалтаас эцсийн үр дүнгийн илэрхийллийг төрөлд нь сонгоно:

Ерөнхий(1, хөвөх: 2.0, char *: "2", int: 2, анхдагч: get_two_object());

Дээрх илэрхийлэл нь 2 гэж үнэлэгддэг - удирдлагын илэрхийллийн төрөл нь int тул int-тэй холбоотой илэрхийллийг утга болгон сонгоно. Эдгээрийн аль нь ч ажиллах үед үлдэхгүй. (Өгөгдмөл заалт нь заавал байх ёстой: хэрэв та үүнийг заагаагүй бөгөөд төрөл нь таарахгүй бол эмхэтгэлд алдаа гарна.)

Функцийг хэт ачаалахад ашигтай арга бол үүнийг C-ийн урьдчилсан процессор оруулж, хяналтын макро руу дамжуулсан аргументуудын төрлөөс хамааран үр дүнгийн илэрхийллийг сонгох боломжтой байдаг. Тиймээс (С стандартын жишээ):

#define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \)(X)

Энэ макро нь аргументийн төрлийг макро руу дамжуулж, тохирох хэрэгжүүлэх функцийг сонгоод дараа нь анхны макрог тухайн функц руу шилжүүлэх замаар хэт ачаалалтай cbrt үйлдлийг хэрэгжүүлдэг.

Тиймээс, таны анхны жишээг хэрэгжүүлэхийн тулд бид үүнийг хийж болно:

Foo_int (int a) foo_char (char b) foo_float_int (float c , int d) #define foo(_1, ...) _Ерөнхий((_1), \ int: foo_int, \ char: foo_char, \ float: _Generic(( FIRST(__VA_ARGS__,)), \int: foo_float_int))(_1, __VA_ARGS__) #FIRST(A, ...) A-г тодорхойлох

Энэ тохиолдолд бид өгөгдмөл утгыг ашиглаж болох юм: гуравдахь тохиолдолд холбох, гэхдээ энэ нь зарчмыг олон аргумент болгон хэрхэн өргөжүүлэхийг харуулахгүй. Эцсийн үр дүнд та аргументынхаа төрлөөс санаа зовохгүйгээр foo(...) кодыг ашиглаж болно.

Илүү их аргументуудыг хэт ачаалах эсвэл тоог өөрчлөх функц зэрэг илүү төвөгтэй нөхцөл байдлын хувьд та статик диспетчерийн бүтцийг автоматаар үүсгэхийн тулд хэрэгслийн макро ашиглаж болно:

хүчингүй хэвлэх_ii(int a, int b) ( printf("int, int\n"); ) хүчингүй хэвлэх_di(давхар a, int b) ( printf("давхар, int\n"); ) хүчингүй хэвлэх_iii(int a, int b, int c) ( printf("int, int, int\n"); ) void print_default(void) ( printf("үл мэдэгдэх аргументууд\n"); ) #define print(...) ХЭТ АЧАЛАЛ(хэвлэх, (__VA_ARGS__), \ (print_ii, (int, int)), \ (print_di, (давхар, int)), \ (print_iii, (int, int, int)) \) #ХЭТ АЧАЛАЛТ_ARG_TYPES (int, double) #тодорхойлох ХЭТ АЧААЛАХ_ФУНКЦИУД (хэвлэх) #include "activate-overloads.h" int main(void) ( print(44, 47); // "int, int" print(4.4, 47); // "double, int" хэвлэнэ. (1, 2, 3); // "int, int, int" print(""); // "үл мэдэгдэх аргумент" хэвлэнэ)

(хэрэгжилтийг энд). Хэд хэдэн хүчин чармайлт гаргаснаар та хэт ачааллын дэмжлэгтэй хэлтэй нэлээд төстэй харагдахын тулд зуухны хавтанг багасгаж чадна.

Хажуугаар нь хэт ачаалах боломжтой байсан тоо хэмжээ C99 дэх аргументууд (төрөл гэхээсээ илүү).

С-г хэрхэн үнэлэх нь таныг хөдөлгөж чадна гэдгийг анхаарна уу. Жишээлбэл, хэрэв та түүн рүү үгийн үсэг дамжуулахыг оролдвол foo_int-г сонгох бөгөөд хэрэв та хэт ачааллыг мөрийн литералуудыг дэмжихийг хүсвэл foo_int хэрэгтэй болно. Гэсэн хэдий ч ерөнхийдөө дажгүй.

Леушенкогийн хариулт үнэхээр гайхалтай: зөвхөн foo жишээ нь GCC-тэй хөрвүүлдэггүй бөгөөд энэ нь foo(7) дээр бүтэлгүйтэж, АНХНЫ макро болон бодит функцийн дуудлагад ((_1, __VA_ARGS__) , нэмэлт таслалтай үлддэг. Мөн, foo(double) гэх мэт нэмэлт ачаалал өгөхийг хүсвэл бид асуудалтай тулгардаг.

Тиймээс би хоосон хэт ачааллыг (foo(void) - зарим асуудал үүсгэсэн ...) зөвшөөрөх зэрэг энэ асуултанд илүү дэлгэрэнгүй хариулахаар шийдсэн.

Одоо байгаа санаа нь: өөр өөр макронд нэгээс олон ерөнхий зүйлийг тодорхойлж, аргументуудын тоогоор зөвийг нь сонго!

Энэ хариулт дээр үндэслэн аргументуудын тоо маш энгийн:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) #define CONCAT(X, Y) CONCAT_(X, Y) # CONCAT_(X, Y) X ## Y-г тодорхойлох

Сайн байна, бид SELECT_1 эсвэл SELECT_2 (эсвэл хэрэв танд хүсвэл/хэрэгтэй бол илүү олон аргумент) сонгож байгаа тул бидэнд тохирох тодорхойлолтууд л хэрэгтэй байна:

#define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char: foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double : _Ерөнхий((_2), \ int: foo_double_int \) \)

Нэгдүгээрт, хоосон макро дуудлага (foo()) токен үүсгэсэн хэвээр байгаа ч хоосон байна. Тиймээс макро хоосон гэж нэрлэсэн ч тоолох макро нь үнэндээ 0-ийн оронд 1-ийг буцаана. Хэрэв бид __VA_ARGS__-ийн дараа __VA_ARGS__ таслалтай байвал бид энэ асуудлыг "амархан" засч чадна. нөхцөлтэйгээр, жагсаалт хоосон эсэхээс хамаарч:

#NARG(...) ARG4_(__VA_ARGS__ ТАСАЛ(__VA_ARGS__) 4, 3, 2, 1, 0)-г тодорхойлох

Энэ харавхялбар, гэхдээ COMMA макро нь нэлээд хүнд; Аз болоход, энэ сэдвийг Йенс Густедтийн блогт аль хэдийн оруулсан байна (баярлалаа, Женс). Гол заль мэх нь хаалтанд оруулахгүй л бол функцийн макронууд тэлэхгүй, цаашдын тайлбарыг Jens блогоос харна уу... Бид өөрсдийн хэрэгцээнд зориулан макрог бага зэрэг өөрчлөх хэрэгтэй (Би богино нэр, аргументуудыг товчилно) .

#define ARGN(...) ARGN_(__VA_ARGS__) #define ARGN_(_0, _1, _2, _3, N, ...) N #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0 ) #тодорхойлох SET_COMMA(...) , #define COMMA(...) SELECT_COMMA(\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_COMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA (S_ETVA_COMSG)MA \) #СОНГОХ_ТАГАЛТ(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #тодорхойлно тодорхойлох COMMA_0000 , #тодорхойлох COMMA_0001 #тодорхойлох COMMA_0010 , // ... (бусад бүх таслалтай) #ТАСАЛ_1111-г тодорхойлох,

Одоо бид зүгээр ...

Нэг блок дахь бүрэн код:

/* * demo.c * * Үүсгэсэн огноо: 2017-09-14 * Зохиогч: sboehler */ #include void foo_void(void) ( puts("void"); ) void foo_int(int c) ( printf("int: %d\n", c); ) void foo_char(char c) ( printf("char: %c) \n", c); ) void foo_double(давхар c) ( printf("давхар: %.2f\n", c); ) void foo_double_int(давхар c, int d) ( printf("давхар: %.2f, int: %d\n", c, d); ) #define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) # тодорхойлох CONCAT(X, Y) CONCAT_(X, Y) #define CONCAT_(X, Y) X ## Y #define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char : foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double: _Generic((_2), \ int: foo_double_int \) \) #define ARGN(...) ARGN_( __VA_ARGS__) #define ARGN_(_0, _1, _2, N, ...) N #define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0) #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_C OMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \) #SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) SELECT_COMMA_define #COMMA_de COMMA_ ## _0 ## _1 ## _2 ## _3 COMMA_1001 , # COMMA_1010-ыг тодорхойлно , # COMA_1011-ийг тодорхойлно , # COMMA_1100 -ийг тодорхойлно , # COMMA_1101 -ийг тодорхойлно , # COMMA_1101 -ийг тодорхойл , # COMMA_101 -ийг тодорхойлно , #1inef_t a COMMA1 , COMMA 1-г тодорхойлно. (foo(); foo(7); foo(10.12); foo(12.10, 7); foo((char)"s"); буцаах 0; )

Тэмдэглэл: Лекцээр C++ программ дахь шугаман болон хэт ачаалалтай функцүүдийн тухай ойлголт, тунхаглал, хэрэглээ, орлуулах, функцийг хэт ачаалах механизм, хэт ачаалал эсвэл функцийг орлуулах замаар програмын үр ашгийг дээшлүүлэх зөвлөмжийг авч үзнэ.

Лекцийн зорилго: доторлогоотой (суулгасан) функцууд болон функцийн хэт ачааллыг сурах, C ++ хэл дээр функцийн хэт ачааллыг ашиглан програмыг хэрхэн хөгжүүлэх талаар сурах.

Inline функцууд

Функцийг дуудах, түүнд утгыг дамжуулах, утгыг буцаах - эдгээр үйлдлүүд нь CPU-ийн маш их цаг зарцуулдаг. Ихэвчлэн функцийг тодорхойлохдоо хөрвүүлэгч өөрийн мэдэгдлийг хадгалахын тулд санах ойд зөвхөн нэг блок нүдийг хадгалдаг. Функц дуудагдсаны дараа програмын удирдлага эдгээр операторууд руу шилждэг бөгөөд функцээс буцаж ирэхэд функц дуудагдсаны дараах мөрөнд програмын гүйцэтгэлийг үргэлжлүүлнэ.

Дахин дахин дуудлага хийх болгонд програм нь дуудлага тус бүрд хуулбар үүсгэхгүйгээр ижил тушаалуудыг боловсруулах болно.

Функцийн мэдэгдлүүдийг агуулсан санах ойн хэсэг рүү үсрэх бүр нь програмын гүйцэтгэлийг удаашруулдаг. Хэрэв функц нь жижиг бол хөрвүүлэгчид функцийн кодыг дуудлагын сайт дээр шууд програм руу оруулахыг зааварчилснаар олон дуудлага хийх цагийг хэмнэж болно. Ийм функцийг нэрлэдэг орлуулсан. Энэ тохиолдолд үр ашгийн тухай ярих нь юуны түрүүнд програмын гүйцэтгэлийн хурдыг илтгэнэ.

Inline эсвэл Inline функцууднь функцийн нэг инстанц руу удирдлагыг шилжүүлэхийн оронд хөрвүүлэгч шууд дуудлагын сайт дээр кодыг нь оруулдаг функцууд юм.

Хэрэв функц нь шугаманд байгаа бол хөрвүүлэгч нь санах ойд өгөгдсөн функцийг үүсгэхгүй, харин түүний мөрүүдийг дуудлагын газар дахь програмын код руу шууд хуулдаг. Энэ нь функцийн дуудлагын оронд тохирох блокуудыг программд бичихтэй тэнцэнэ. Тиймээс тодорхойлогч шугамандгэж нэрлэгддэг функцийг тодорхойлдог дотоод холболт, энэ нь хөрвүүлэгч функцийг дуудахын оронд кодын командуудыг орлуулдагт оршино. Функцийн бие нь хэд хэдэн хэллэгээс бүрдэх тохиолдолд шугамын функцуудыг ашигладаг.

Командуудыг програмаас хассан тул энэ арга нь програмын гүйцэтгэлийн хурдыг нэмэгдүүлэх боломжийг олгодог. микропроцессорАргумент дамжуулах, функцийг дуудах шаардлагатай.

Жишээлбэл:

/*функц нь координат(x1,y1) цэгээс координаттай (x2,y2) цэг хүртэлх зайг буцаана*/ inline float Line(float x1,float y1,float x2, float y2) ( return sqrt(pow(x1-x2) ,2)+pow(y1-y2,2)); )

Гэсэн хэдий ч, доторлогооны функцуудыг ашиглах нь үргэлж эерэг үр дүнд хүргэдэггүй гэдгийг тэмдэглэх нь зүйтэй. Хэрэв ийм функцийг програмын кодонд хэд хэдэн удаа дуудсан бол in эмхэтгэх хугацааЭнэ функцийн аль болох олон хуулбарыг дуудаж байгаа бол программд оруулах болно. Програмын кодын хэмжээ мэдэгдэхүйц нэмэгдэх бөгөөд үүний үр дүнд програмын гүйцэтгэлийн үр ашгийг цаг тухайд нь нэмэгдүүлэхгүй байж магадгүй юм.

Жишээ 1.

#include "stdafx.h" #include namespace std ашиглах; inline int Cube(int x); int _tmain(int argc, _TCHAR* argv)( int x=2; хөвөх у=3; давхар z=4; cout<

Дотор тодорхойлогчтой функцийг ердийн шугамын бус функц гэж үзэх шалтгааныг бид жагсаав.

  • inline функц нь рекурсив;
  • Дуудлага нь тодорхойлолтын өмнө тавигдсан функцууд;
  • илэрхийлэлд нэгээс олон удаа дуудагддаг функцууд;
  • гогцоо, унтраалга, болон агуулсан функцууд үсрэх операторууд;
  • орлуулахыг зөвшөөрөхөд хэтэрхий том функцууд.

Орлуулахад тавих хязгаарлалт нь ихэнхдээ хэрэгжилтээс хамаардаг. Тодорхойлогчтой функцийн хувьд бол шугамандхөрвүүлэгч нь дуудлагыг байрлуулсан контекстээс шалтгаалан орлуулалтыг хийж чадахгүй, дараа нь функц нь статик гэж тооцогддог бөгөөд гарч ирдэг. анхааруулах мессеж.

Inline функцүүдийн өөр нэг онцлог нь эдгээр функцийг дууддаг програмын бүх хэсгийг дахин хөрвүүлэхгүйгээр тэдгээрийг өөрчлөх боломжгүй юм.

Функцийн хэт ачаалал

Хөтөлбөрт функцийг тодорхойлохдоо функцээс буцаж ирсэн утгын төрөл, параметрийн тоо, тэдгээрийн төрлийг зааж өгөх шаардлагатай. Хэрэв C++ хэл дээр хоёр бүхэл тоон утга дээр ажилладаг add_values ​​гэж нэрлэгддэг функц байсан бөгөөд програм гурван бүхэл утгыг дамжуулахын тулд ижил төстэй функцийг ашиглах шаардлагатай байсан бол өөр нэртэй функц үүсгэх шаардлагатай болно. Жишээлбэл, хоёр_утга нэмэх ба гурван_утга нэмэх. Үүний нэгэн адил, хэрэв та хөвөх утгуудтай ажиллахын тулд ижил төстэй функцийг ашиглахыг хүсвэл өөр нэртэй өөр функц хэрэгтэй болно. Функцийн давхардлаас зайлсхийхийн тулд C++ нь ижил нэртэй олон функцийг тодорхойлох боломжийг олгодог. Эмхэтгэлийн явцад C++ нь функц тус бүрийн ашигладаг аргументуудын тоог харгалзан үзээд дараа нь яг шаардлагатай функцийг дууддаг. Хөрвүүлэгчид олон функцээс сонголт өгөхийг хэт ачаалал гэнэ.

Функцийн хэт ачаалалгэдэг нь ижил нэртэй боловч өөр өөр параметртэй хэд хэдэн функц үүсгэх явдал юм. Өөр өөр параметрүүд нь өөр байх ёстой гэсэн үг юм аргументуудын тоочиг үүрэг ба/эсвэл тэдгээрийн төрөл. Өөрөөр хэлбэл, функцийг хэт ачаалах нь ижил нэртэй, буцах төрөлтэй олон функцийг тодорхойлох боломжийг олгодог.

Функцийн хэт ачаалал гэж бас нэрлэдэг функциональ полиморфизм. "Поли" гэдэг нь маш их утгатай, "морф" - хэлбэр, өөрөөр хэлбэл полиморф функц нь олон янзын хэлбэрээр ялгагддаг функц юм.

Функцийн полиморфизм гэдэг нь өөр өөр утгатай функцийн хэд хэдэн хэт ачаалалтай хувилбарууд програмд ​​байх явдал гэж ойлгогддог. Параметрийн тоо эсвэл төрлийг өөрчилснөөр хоёр ба түүнээс дээш функцэд ижил нэр өгч болно. Энэ тохиолдолд төөрөгдөл гарахгүй, учир нь хүссэн функц нь ашигласан параметрүүдийн давхцлаар тодорхойлогддог. Энэ нь функц бүрт тусдаа нэр үүсгэхгүйгээр бүхэл тоо, хөвөгч эсвэл бусад төрлийн утгуудтай ажиллах функцийг үүсгэх боломжийг олгоно.

Тиймээс ашиглалтын ачаар хэт ачаалалтай функцууд, дамжуулж буй хувьсагчийн төрөлд тохирох програмын зөв функцийг дуудах талаар санаа зовох хэрэггүй. Хэт ачаалалтай функцийг дуудах үед хөрвүүлэгч функцийн аль хувилбарыг ашиглахыг автоматаар тодорхойлно.

Жишээлбэл, дараах програм нь add_values ​​нэртэй функцийг хэт ачаалдаг. Эхний функцийн тодорхойлолт нь int төрлийн хоёр утгыг нэмдэг. Хоёрдахь функцийн тодорхойлолт нь int төрлийн гурван утгыг нэмдэг. Эмхэтгэлийн явцад C++ нь ашиглах функцийг зөв тодорхойлдог.

#include "stdafx.h" #include namespace std ашиглах; int нэмэх_утгууд(int a,int b); int нэмэх_утгууд (int a, int b, int c); int _tmain(int argc, _TCHAR* argv)( cout<< "200+801=" << add_values(200,801) << "\n"; cout << "100+201+700=" << add_values(100,201,700) << "\n"; system("pause"); return 0; } int add_values(int a,int b) { return(a + b); } int add_values (int a, int b, int c) { return(a + b + c); }

Тиймээс програм нь add_values ​​нэртэй хоёр функцийг тодорхойлдог. Эхний функц нь хоёр утгыг нэмдэг бол хоёр дахь нь ижил int төрлийн гурван утгыг нэмнэ. C++ хөрвүүлэгч нь програмын өгсөн сонголтууд дээр үндэслэн аль функцийг ашиглахаа тодорхойлдог.

Функцийн хэт ачааллыг ашиглах

Хэт ачааллыг ашиглах хамгийн түгээмэл тохиолдлуудын нэг бол янз бүрийн параметрийн дагуу тодорхой үр дүнг гаргах функцийг ашиглах явдал юм. Жишээлбэл, таны программ долоо хоногийн тухайн өдрийг буцаадаг долоо хоногийн_өдөр нэртэй функцтэй байна гэж бодъё (Ням гарагт 0, Даваа гаригт 1, Бямба гаригт ... , 6). Програм нь Жулиан өдрийг параметр болгон өгсөн эсвэл өдөр, сар, жилээр өгсөн бол долоо хоногийн өдрийг зөв буцаахын тулд энэ функцийг хэт ачаалж болно.

долоо хоногийн_өдөр(int julian_day) ( // операторууд ) int долоо хоногийн_өдөр (int сар, өдөр, жил) ( // операторууд )

Ашиглаж байна хэт ачаалалтай функцуудхэд хэдэн алдаа ихэвчлэн гардаг. Жишээлбэл, хэрэв функцууд нь зөвхөн буцах төрлөөр ялгаатай боловч аргументийн төрлөөр ялгаатай бол ийм функцууд ижил нэртэй байж болохгүй. Дараах хэт ачааллын сонголт бас буруу байна:

int функцийн нэр (int аргументийн нэр); int функцийн нэр (int аргументийн нэр); /* нэрийн хэт ачаалал: аргументууд ижил тоо, ижил төрлийн */

Функцийг хэт ачаалахын ашиг тус:

  • функцийн хэт ачаалал сайжирна уншигдах чадвархөтөлбөрүүд;
  • C++ функцийг хэт ачаалах нь програмуудад ижил нэртэй олон функцийг тодорхойлох боломжийг олгодог;
  • хэт ачаалалтай функцуудижил төрлийн буцаах утгууд, гэхдээ параметрийн тоо, төрлөөр ялгаатай байж болно;
  • Функцийг хэт ачаалах нь програмистуудаас зөвхөн нэг функцийн нэрийг санахыг шаардах замаар даалгаврыг хялбаршуулдаг боловч дараа нь параметрийн аль хослол нь аль функцтэй тохирч байгааг мэдэх ёстой.

Жишээ 2.

/*Хэт ачаалал ихтэй функцууд нь ижил нэртэй боловч өөр параметрийн жагсаалт болон буцах утгуудтай*/ #include "stdafx.h" #include namespace std ашиглах; int дундаж(int эхний_тоо, int хоёр дахь_тоо, int гурав дахь_тоо); int дундаж(int эхний_тоо, int хоёр дахь_тоо); int _tmain(int argc, _TCHAR* argv)(// үндсэн функц int тоо_A = 5, тоо_B = 3, тоо_C = 10; cout<< "Целочисленное среднее чисел " << number_A << " и "; cout << number_B << " равно "; cout << average(number_A, number_B) << ".\n\n"; cout << "Целочисленное среднее чисел " << number_A << ", "; cout << number_B << " и " << number_C << " равно "; cout << average(number_A, number_B, number_C) << ".\n"; system("PAUSE"); return 0; }// конец главной функции /*функция для вычисления целочисленного среднего значения 3-х целых чисел*/ int average(int first_number, int second_number, int third_number) { return((first_number + second_number + third_number)/3); } // конец функции /*функция для вычисления целочисленного среднего значения 2-х целых чисел*/ int average(int first_number, int second_number) { return((first_number + second_number)/2); } // конец функции



Ачааж байна...
Топ