Үнэ цэнээр дамжуулна. Параметрүүдийг лавлагаа болон утгаар дамжуулах. Өгөгдмөл тохиргоо

Тэгэхээр, n тооны факториалыг тооцоолох функц (n)-г авч үзье. Дараа нь хүчин зүйл 1 нь 1 гэдгийг "мэддэг" тохиолдолд бид дараах хэлхээг байгуулж болно.

Факториал(4)=Хүчин зүйл(3)*4

Факториал(3)=Хүчин зүйл(2)*3

Факториал(2)=Хүчин зүйл(1)*2

Гэхдээ хэрэв бидэнд n=1 үед Факториал функц 1-ийг буцаана гэсэн терминалын нөхцөл байхгүй байсан бол ийм онолын гинж хэзээ ч дуусахгүй байсан бөгөөд энэ нь Call Stack Overflow алдаа байж болох юм - call stack overflow. Дуудлагын стек гэж юу болох, хэрхэн хальж болохыг ойлгохын тулд функцийнхээ рекурсив хэрэгжилтийг харцгаая.

Функцийн факториал(n: Бүхэл тоо): LongInt;

Хэрэв n=1 бол

Факториал:=Факториал(n-1)*n;

Төгсгөл;

Бидний харж байгаагаар гинж зөв ажиллахын тулд дараагийн функц бүрийг өөртөө дуудахын өмнө бүх орон нутгийн хувьсагчдыг хаа нэгтээ хадгалах шаардлагатай бөгөөд ингэснээр гинжийг эргүүлэхэд үр дүн нь зөв болно (тооцоолсон утга n-1-ийн факториалыг n-ээр үржүүлнэ). Манай тохиолдолд хүчин зүйлийн функцийг өөрөө дуудах бүрт n хувьсагчийн бүх утгыг хадгалах ёстой. Өөрийгөө рекурсив байдлаар дуудах үед функцийн локал хувьсагчдыг хадгалах хэсгийг дуудлагын стек гэнэ. Мэдээжийн хэрэг, энэ стек нь хязгааргүй биш бөгөөд хэрэв рекурсив дуудлагууд буруу хийгдсэн бол дуусч болно. Манай жишээний давталтын хязгаарлагдмал байдал нь n=1 үед функцийн дуудлага зогссоноор баталгааждаг.

Параметрүүдийг утга болон лавлагаагаар дамжуулах

Одоог хүртэл бид дэд програмын утгыг өөрчилж чадаагүй бодит параметр(жишээ нь, дэд програмыг дуудах үед заасан параметр), мөн зарим хэрэглэгдэхүүнд энэ нь тохиромжтой байх болно. Бодит хоёр параметрийнх нь утгыг нэг дор өөрчилдөг Val процедурыг санацгаая: эхнийх нь мөрийн хувьсагчийн хөрвүүлсэн утгыг бичих параметр, хоёр дахь нь Code параметр, алдааны тоо. Төрөл хувиргах явцад алдаа гарсан тохиолдолд тэмдэгтийг байрлуулна. Тэдгээр. Дэд програм нь бодит параметрүүдийг өөрчлөх механизм байсаар байна. Энэ нь параметрүүдийг дамжуулах янз бүрийн аргуудын ачаар боломжтой юм. Эдгээр аргуудыг нарийвчлан авч үзье.

Паскаль хэл дээр програмчлал

Параметрүүдийг утгаар дамжуулах

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

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

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

Параметрүүдийг лавлагаагаар дамжуулах

Энэ аргын тусламжтайгаар бодит параметрүүдийн утгыг дэд программд хуулахгүй, харин санах ой дахь хаягуудыг (хувьсагчийн холбоос) шилжүүлдэг. Энэ тохиолдолд дэд програм нь орон нутгийн хүрээнд байхгүй утгуудыг аль хэдийн өөрчилдөг тул бүх өөрчлөлтүүд дуудлагын програмд ​​харагдах болно.

Аргументыг лавлагаагаар дамжуулах ёстойг харуулахын тулд мэдэгдлийн өмнө var гэсэн түлхүүр үгийг нэмсэн:

Процедур getTwoRandom(var n1, n2: Бүхэл тоо; муж: Бүхэл тоо);

n1:=санамсаргүй(муж);

n2:=санамсаргүй(муж); Төгсгөл ;

var rand1, rand2: Бүхэл тоо;

Эхлэх getTwoRandom(rand1,rand2,10); WriteLn(rand1); WriteLn(rand2);

Төгсгөл.

Энэ жишээнд хоёр хувьсагчийн ишлэлийг getTwoRandom процедурт бодит параметр болгон шилжүүлсэн: rand1 ба rand2. Гурав дахь бодит параметрийг (10) утгаар дамжуулна. Процедур нь албан ёсны параметрүүдийг ашиглан бичдэг

Мөр ашиглан програмчлалын аргууд

Лабораторийн ажлын зорилго : C# хэл дээрх аргууд, тэмдэгтийн өгөгдөлтэй ажиллах дүрэм, ListBox бүрэлдэхүүн хэсэгт суралцах. Мөртэй ажиллах програм бичнэ үү.

Арга зүй

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

[шинж чанарууд] [тодорхойлогч] төрлийн нэр ([параметр])

Аргын бие;

Аттрибутууд нь аргын шинж чанарын талаар хөрвүүлэгчид өгөх тусгай заавар юм. Шинж чанаруудыг бараг ашигладаггүй.

Сонгогч нь өөр өөр зорилготой түлхүүр үгс юм, жишээлбэл:

· Бусад ангиудад ашиглах боломжтой эсэхийг тодорхойлох:

о хувийн– аргыг зөвхөн энэ ангид ашиглах боломжтой

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

о олон нийтийн– энэ ангид хандах боломжтой бусад ангиуд энэ аргыг ашиглах боломжтой

Анги үүсгэхгүйгээр аргын бэлэн байдлыг харуулж байна

· Тохиргооны төрөл

Төрөл нь аргын буцаах үр дүнг тодорхойлдог: энэ нь C# хэл дээр байгаа ямар ч төрөл байж болно, мөн үр дүн шаардлагагүй бол void түлхүүр үг байж болно.

Аргын нэр нь аргыг дуудахад ашиглагдах танигч юм. Хувьсагчийн нэрийн хувьд тодорхойлогчдод ижил шаардлага тавигдана: энэ нь үсэг, тоо, доогуур зураасаас бүрдэх боловч тоогоор эхэлж болохгүй.

Параметрүүд нь дуудах үед арга руу дамжуулж болох хувьсагчдын жагсаалт юм. Параметр бүр нь хувьсагчийн төрөл, нэрээс бүрдэнэ. Параметрүүдийг таслалаар тусгаарлана.

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

Илэрхийлэлийг үнэлэх аргын жишээ:

нийтийн давхар Calc(давхар a, давхар b, давхар c)

буцаах Math.Sin(a) * Math.Cos(b);

double k = Math.Tan(a * b);

буцах k * Math.Exp(c / k);

Аргын хэт ачаалал

C# хэл нь ижил нэртэй боловч өөр параметртэй олон аргыг үүсгэх боломжийг олгодог. Хөрвүүлэгч программыг бүтээхдээ хамгийн тохиромжтой аргыг автоматаар сонгоно. Жишээлбэл, та тоог өсгөх хоёр аргыг бичиж болно: нэг алгоритмыг бүхэл тоонд, нөгөөг нь бодит тоонд ашиглах болно:

///

/// Бүхэл тоонуудын хувьд X-ийг Y-ийн зэрэглэлээр тооцоол

///

private int Pow(int X, int Y)

///

/// Бодит тоонуудын хувьд X-ийг Y-ийн зэрэглэлээр тооцоол

///

хувийн давхар Поу(давхар X, давхар Y)

буцаах Math.Exp(Y * Math.Log(Math.Abs(X)));

Хэрэв өөр бол (Y == 0)

Энэ кодыг ижил аргаар дууддаг бөгөөд цорын ганц ялгаа нь параметрүүдэд байдаг - эхний тохиолдолд хөрвүүлэгч нь Pow аргыг бүхэл тоон параметрүүдээр, хоёр дахь нь бодит параметрүүдээр дуудна.

Өгөгдмөл тохиргоо

C# хэл нь 4.0 (Visual Studio 2010) хувилбараас эхлэн зарим параметрийн анхдагч утгыг тохируулах боломжийг олгодог бөгөөд ингэснээр аргыг дуудахдаа зарим параметрүүдийг орхиж болно. Үүнийг хийхийн тулд аргыг хэрэгжүүлэхдээ шаардлагатай параметрүүдийг параметрийн жагсаалтад шууд утгаар нь өгөх ёстой.

private void GetData(int Number, int Нэмэлт = 5 )

Console.WriteLine("Тоо: (0)", Тоо);

Console.WriteLine("Заавал биш: (0)", Нэмэлт);

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

GetData(10, 20);

Эхний тохиолдолд Нэмэлт параметр нь тодорхой заасан тул 20-той тэнцүү байх ба хоёр дахь тохиолдолд 5-тай тэнцүү байх болно, учир нь Энэ нь тодорхой заагаагүй бөгөөд хөрвүүлэгч нь анхдагч утгыг авдаг.

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

хувийн хүчингүй GetData(int Нэмэлт = 5 , int дугаар)

Параметрүүдийг ердийн аргаар (нэмэлт ref болон гадагшлах түлхүүр үггүйгээр) арга руу шилжүүлэх үед аргын доторх параметрийн өөрчлөлт нь үндсэн програм дахь түүний утгад нөлөөлөхгүй. Бидэнд дараах арга байна гэж бодъё.

хувийн хүчингүй Калк(int тоо)

Аргын дотор параметр болгон дамжуулсан Number хувьсагч өөрчлөгдсөнийг харж болно. Энэ аргыг дуудаж үзье:

Console.WriteLine(n);

Дэлгэц дээр 1 гэсэн тоо гарч ирэх бөгөөд өөрөөр хэлбэл Calc аргын хувьсагч өөрчлөгдсөн ч үндсэн программ дахь хувьсагчийн утга өөрчлөгдөөгүй байна. Энэ нь аргыг дуудах үед а хуулбарлахдамжуулсан хувьсагч, арга нь энэ хувьсагчаар өөрчлөгддөг. Арга дуусах үед хуулбаруудын үнэ цэнэ алдагдана. Параметр дамжуулах энэ аргыг нэрлэдэг үнэ цэнээр дамжих.

Арга руу шилжүүлсэн хувьсагчийг өөрчлөхийн тулд үүнийг ref түлхүүр үгээр дамжуулах ёстой - энэ нь аргын гарын үсэг болон дуудагдах үед хоёуланд нь байх ёстой:

хувийн хүчингүй кальц(ref int дугаар)

Console.WriteLine(n);

Энэ тохиолдолд дэлгэцэн дээр 10 дугаар гарч ирнэ: аргын утгын өөрчлөлт нь үндсэн програмд ​​мөн нөлөөлсөн. Энэ аргыг шилжүүлэх гэж нэрлэдэг лавлагаагаар дамжуулж байна, өөрөөр хэлбэл Энэ нь дамжуулагдах хуулбар байхаа больсон, харин санах ой дахь бодит хувьсагчийн лавлагаа юм.

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

хувийн хүчингүй Тооцоолол(out int дугаар)

int n; // Бид юу ч томилдоггүй!

мөр өгөгдлийн төрөл

C# хэл нь мөрүүдийг хадгалахдаа мөрийн төрлийг ашигладаг. Мөр хувьсагчийг зарлах (мөн дүрмээр бол шууд эхлүүлэх) тулд та дараах кодыг бичиж болно.

string a = "Текст";

string b = "мөр";

Та мөрөнд нэмэх үйлдлийг хийж болно - энэ тохиолдолд нэг мөрийн текст нөгөө мөрийн текстэд нэмэгдэх болно.

string c = a + "" + b; // Үр дүн: Мөр текст

Мөрний төрөл нь үнэндээ String классын нэр бөгөөд энэ нь мөрүүд дээр хэд хэдэн илүү төвөгтэй үйлдлүүдийг гүйцэтгэх боломжийг олгодог. Жишээлбэл, IndexOf арга нь мөр доторх дэд мөрийг хайж болох ба Substring арга нь заасан байрлалаас эхлэн заасан урттай мөрийн хэсгийг буцаана.

string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int индекс = a.IndexOf("OP"); // Үр дүн: 14 (0-ээс тоолох)

string b = a.Дэд мөр(3, 5); // Үр дүн: DEFGH

Хэрэв та мөрөнд тусгай тэмдэгт нэмэх шаардлагатай бол урвуу зураасаар эхэлсэн escape дарааллыг ашиглан үүнийг хийж болно:

ListBox бүрэлдэхүүн хэсэг

Бүрэлдэхүүн хэсэг Жагсаалтын хайрцагнь гар эсвэл хулганаар элементүүдийг сонгосон жагсаалт юм. Элементүүдийн жагсаалтыг өмчөөр зааж өгсөн болно Эд зүйлс. Эд зүйлс нь өөрийн гэсэн шинж чанартай, өөрийн гэсэн арга барилтай элемент юм. Арга зүй Нэмэх, RemoveAtТэгээд Оруулахэлементүүдийг нэмэх, устгах, оруулахад ашигладаг.

Объект Эд зүйлсжагсаалтад байгаа объектуудыг хадгалдаг. Объект нь ямар ч анги байж болно - ангийн өгөгдлийг ToString аргаар дэлгэцийн дүрслэл болгон хувиргадаг. Манай тохиолдолд мөр нь объектын үүрэг гүйцэтгэх болно. Гэсэн хэдий ч, Items объект нь төрөл объект руу шилжүүлсэн объектуудыг хадгалдаг тул үүнийг ашиглахын өмнө та тэдгээрийг анхны төрөл рүү нь буцаах хэрэгтэй, бидний тохиолдолд мөр:

string a = (string)listBox1.Items;

Сонгосон элементийн тоог тодорхойлохын тулд шинж чанарыг ашиглана уу Сонгосон индекс.

Би C++ хэл дээр програмчлал хийж, ном, нийтлэлүүдийг эрчимтэй судалж байхдаа би үргэлж ижил зөвлөгөөтэй тулгардаг: хэрэв бид функц нь өөрчлөгдөх ёсгүй функц руу ямар нэг объектыг дамжуулах шаардлагатай бол түүнийг үргэлж дамжуулж байх ёстой. Тогтмол тоогоор(PPSK), бид анхдагч төрөл эсвэл тэдгээрийн хэмжээтэй ижил төстэй бүтцийг дамжуулах шаардлагатай тохиолдлуудаас бусад тохиолдолд. Учир нь C++ хэл дээр 10 гаруй жил программчлахдаа би энэ зөвлөгөөг маш олон удаа олж мэдсэн (мөн би өөрөө үүнийг нэгээс олон удаа өгсөн), энэ нь надад удаан хугацаанд шингэсэн байдаг - би бүх аргументыг автоматаар тогтмол дамжуулдаг. . Гэвч цаг хугацаа өнгөрч, бид C++ 11-ийг нүүлгэн шилжүүлэх семантиктай болгосноос хойш 7 жил өнгөрч байгаа бөгөөд үүнтэй холбоотойгоор би хуучин сайн сургаалыг эргэлзэж буй дуу хоолойг улам олон сонсож байна. Тогтмол тоогоор дамжуулж байгаа нь өнгөрсөн зүйл бөгөөд одоо зайлшгүй шаардлагатай гэж олон хүн маргаж эхэлж байна үнэ цэнээр дамжих(PPZ). Эдгээр ярианы ард юу байгаа, мөн энэ бүхнээс бид ямар дүгнэлт хийж болох талаар би энэ нийтлэлд хэлэлцэхийг хүсч байна.

Номын мэргэн ухаан

Бид ямар дүрмийг баримтлах ёстойг ойлгохын тулд ном уншихыг санал болгож байна. Ном бол бидний хүлээн зөвшөөрөх үүрэг хүлээдэггүй, гэхдээ сонсох нь зүйтэй мэдээллийн маш сайн эх сурвалж юм. Тэгээд бид түүх, гарал үүслээр нь эхлэх болно. PPSC-ийн анхны уучлал гуйсан хүн хэн байсныг би олж мэдэхгүй, би PPSC-ийг ашиглах асуудалд надад хамгийн их нөлөө үзүүлсэн номыг жишээ болгон өгөх болно.

Майерс

За, энд бүх параметрүүдийг лавлагаагаар дамжуулдаг анги байна, энэ ангид ямар нэгэн асуудал байна уу? Харамсалтай нь байгаа бөгөөд энэ асуудал өнгөн талдаа оршдог. Манай ангид 2 функциональ нэгж байдаг: эхнийх нь объект үүсгэх үе шатанд утгыг авдаг, хоёр дахь нь өмнө нь тохируулсан утгыг өөрчлөх боломжийг олгодог. Бид хоёр байгууллагатай ч дөрвөн чиг үүрэгтэй. Одоо бид 2 ижил төстэй зүйл биш, харин 3, 5, 6 байж болно гэж төсөөлөөд үз дээ, тэгвэл яах вэ? Дараа нь бид ноцтой кодтой тулгарах болно. Тиймээс, олон тооны функц үүсгэхгүйн тулд параметрүүдийн холбоосыг бүхэлд нь орхих санал гарч ирэв.

Загвар анги Эзэмшигч ( нийтийн: тодорхой Эзэмшигч(T утга): m_Value(шилжүүлэх(утга)) ( ) хүчингүй болсон багцValue(T утга) (m_Value = зөөх(утга); ) const T& value() const noexcept ( m_Value буцаана; ) хувийн: T m_Value; );

Таны анхаарлыг шууд татдаг эхний давуу тал бол код нь мэдэгдэхүйц бага байх явдал юм. Энэ нь эхний хувилбараас ч бага байгаа нь const болон &-г хассан тул (хэдийгээр тэд move нэмсэн). Гэхдээ бид лавлагаагаар өнгөрөх нь үнэ цэнээр дамжихаас илүү бүтээмжтэй гэдгийг үргэлж сургасаар ирсэн! Энэ нь C++ 11-ээс өмнө ийм байсан, одоо ч байгаа, гэхдээ одоо бид энэ кодыг харвал энд эхний хувилбараас илүү хуулбарлах зүйл байхгүй болохыг харах болно. Т нь хөдөлгөгч үүсгэгчтэй байх нөхцөлд. Тэдгээр. PPSC нь өөрөө PPZ-ээс хурдан байсан бөгөөд байх ч код нь дамжуулсан лавлагааг ашигладаг бөгөөд энэ аргументыг ихэвчлэн хуулж авдаг.

Гэсэн хэдий ч энэ бол бүхэл бүтэн түүх биш юм. Эхний сонголтоос ялгаатай нь бид зөвхөн хуулбарлах боломжтой бол энд бид бас хөдөлгөөнийг нэмдэг. Гэхдээ нүүх нь хямдхан хагалгаа шүү дээ? Энэ сэдвээр бидний авч үзэж буй Майерсийн номонд мөн бүлэг байдаг ("Зүйл 29") нь: "Хөдөлгөөний үйл ажиллагаа байхгүй, хямд биш, ашиглагдаагүй гэж бодъё." Гол санаа нь гарчигнаас тодорхой байх ёстой, гэхдээ хэрэв та дэлгэрэнгүй мэдээлэл авахыг хүсвэл үүнийг заавал уншаарай - би энэ талаар ярихгүй.

Энд эхний болон сүүлчийн аргуудын бүрэн харьцуулсан дүн шинжилгээ хийх нь зүйтэй болов уу, гэхдээ би номноос хазайхыг хүсэхгүй байгаа тул бусад хэсгүүдийн дүн шинжилгээг хойшлуулж, энд Скоттын аргументуудыг үргэлжлүүлэн авч үзэх болно. Гурав дахь хувилбар нь хоёр дахь хувилбараасаа богино байхаас гадна орчин үеийн кодоор PPSC-ээс PPZ-ийн давуу талыг Скотт юу гэж үзэж байна вэ?

Тэр үүнийг rvalue дамжуулсан тохиолдолд, i.e. зарим нь ингэж дууддаг: Holder holder(string("me")); , PPSC-тэй сонголт нь хуулбарлах боломжийг бидэнд өгөх бөгөөд PPZ-тэй сонголт нь бидэнд хөдөлгөөнийг өгөх болно. Нөгөө талаас, хэрэв шилжүүлэг ийм байвал: Holder holder(someLvalue); , дараа нь PPZ нь хуулбарлах, зөөх хоёуланг нь гүйцэтгэх тул алдагдах нь гарцаагүй, харин PPSC-тэй хувилбарт зөвхөн нэг хуулбарлах болно. Тэдгээр. Хэрэв бид цэвэр үр ашгийг авч үзвэл PPZ нь кодын хэмжээ болон хөдөлгөөний семантикийн "бүрэн" (&& -аар) дэмжлэгийн хооронд ямар нэгэн буулт хийх явдал юм.

Тийм ч учраас Скотт зөвлөгөөгөө маш болгоомжтой хэлж, маш болгоомжтой сурталчилж байна. Тэр ч байтугай дарамтанд байгаа юм шиг дурамжхан авчирсан юм шиг надад санагдсан: тэр номонд энэ сэдвээр хэлэлцүүлэг оруулахгүй байхын аргагүй байсан, учир нь ... Энэ нь нэлээд өргөн хүрээнд яригдаж байсан бөгөөд Скотт үргэлж хамтын туршлагыг цуглуулдаг байсан. Нэмж дурдахад тэрээр PPZ-ийг хамгаалахын тулд маш цөөхөн аргумент өгдөг боловч энэ "техник" гэж нэрлэдэг хүмүүст маш их эргэлзээ төрүүлдэг. Бид түүний эсрэг аргументуудыг дараагийн хэсгүүдэд авч үзэх болно, гэхдээ энд бид Скоттын PPP-ийг хамгаалж буй аргументыг товчхон давтах болно (оюун санааны нэмэлт "хэрэв объект хөдөлгөөнийг дэмждэг бөгөөд энэ нь хямд байвал"): функцийн аргумент болгон үнэ цэнийн илэрхийлэл дамжуулах үед хуулбарлахаас зайлсхийх боломжийг танд олгоно. Гэхдээ Мейерсийн ном хангалттай тарчлаан, өөр ном руу шилжье.

Дашрамд хэлэхэд, хэрэв хэн нэгэн энэ номыг уншсан бөгөөд Майерсын бүх нийтийн лавлагаа гэж нэрлэсэн одоо дамжуулагч лавлагаа гэж нэрлэгддэг хувилбарыг энд оруулаагүйд гайхаж байвал үүнийг хялбархан тайлбарлаж болно. Би зөвхөн PPZ болон PPSC-ийг л авч үзэж байна, учир нь... Зөвхөн хоёр төрлийн (value/lvalue) лавлагаагаар дамжуулалтыг дэмжих үүднээс загвар биш аргуудын загвар функцуудыг нэвтрүүлэх нь буруу хэлбэр гэж би үзэж байна. Код нь өөр болж хувирдаг (тогтмол байдал байхгүй) бусад асуудлуудыг дагуулдаг гэдгийг энд дурдахгүй.

Жосаттис ба компани

Бидний үзэх сүүлчийн ном бол "C++ Templates" бөгөөд энэ нийтлэлд дурдсан бүх номнуудаас хамгийн сүүлийнх нь юм. Энэ нь 2017 оны сүүлээр хэвлэгдсэн (мөн 2018 оныг номонд оруулсан болно). Бусад номноос ялгаатай нь энэ ном нь бүхэлдээ хэв маягт зориулагдсан болохоос зөвлөгөө (Майерс гэх мэт) эсвэл ерөнхийдөө Строуструп гэх мэт C++ биш юм. Тиймээс энд загвар бичих талаас нь давуу болон сул талуудыг авч үзсэн болно.

Энэ сэдэвт бүхэл бүтэн 7-р бүлэг зориулагдсан бөгөөд "Үнэ цэнээр үү, лавлагаагаар уу?" гэсэн уран яруу гарчигтай. Энэ бүлэгт зохиогчид бүх дамжуулалтын аргуудыг давуу болон сул талуудын хамт маш товч боловч товч тайлбарлав. Үр дүнтэй байдлын дүн шинжилгээг энд бараг өгөөгүй бөгөөд PPSC нь PPZ-ээс илүү хурдан байх болно гэж үздэг. Гэхдээ энэ бүхний хувьд бүлгийн төгсгөлд зохиогчид загвар функцүүдэд анхдагч PPP ашиглахыг зөвлөж байна. Яагаад? Учир нь холбоосыг ашигласнаар загварын параметрүүд бүрэн харагдах бөгөөд холбоосгүй бол тэдгээр нь "муудсан" бөгөөд энэ нь массив болон стринг литерал боловсруулахад сайнаар нөлөөлдөг. Хэрэв зарим төрлийн PPP-ийн хувьд энэ нь үр дүнгүй бол та std::ref болон std::cref ашиглаж болно гэж зохиогчид үзэж байна. Энэ бол зарим зөвлөгөө юм, үнэнийг хэлэхэд та дээрх функцуудыг ашиглахыг хүсч буй олон хүмүүсийг харсан уу?

PPSC-ийн талаар тэд юу зөвлөх вэ? Гүйцэтгэл чухал эсвэл бусад тохиолдолд тэд PPSC ашиглахыг зөвлөж байна жинтэй PPP ашиглахгүй байх шалтгаан. Мэдээжийн хэрэг, бид энд зөвхөн стандартын кодын тухай ярьж байна, гэхдээ энэ зөвлөгөө нь програмистуудын арван жилийн турш заасан бүх зүйлтэй шууд зөрчилдөж байна. Энэ бол зөвхөн PPP-ийг өөр хувилбар болгон авч үзэх зөвлөгөө биш, үгүй, энэ бол PPSC-ийг өөр хувилбар болгох зөвлөгөө юм.

Үүгээр бидний номын аялал өндөрлөж байна, учир нь... Энэ асуудлаар зөвлөлдөх өөр ном байхгүй гэдгийг би мэднэ. Өөр медиа орон зай руу шилжье.

Сүлжээний мэргэн ухаан

Учир нь Бид интернетийн эрин зуунд амьдарч байгаа тул та зөвхөн номын мэргэн ухаанд найдах ёсгүй. Түүгээр ч барахгүй ном бичдэг байсан олон зохиолчид одоо зүгээр л блог бичиж, номоо орхисон байна. Эдгээр зохиолчдын нэг нь Херб Саттер бөгөөд 2013 оны 5-р сард өөрийн блогтоо "GotW #4 Шийдэл: Ангийн механик" нийтлэлийг нийтэлсэн бөгөөд энэ нь бидний хөндөж буй асуудалд бүрэн зориулагдаагүй ч одоог хүртэл хөндөгдсөөр байна.

Тиймээс, нийтлэлийн анхны хувилбарт Саттер "параметрүүдийг тогтмол үзүүлэлтээр дамжуулдаг" гэсэн хуучин мэргэн ухааныг давтсан боловч бид нийтлэлийн энэ хувилбарыг цаашид харахгүй. Нийтлэл нь эсрэг талын зөвлөгөөг агуулдаг: " Хэрэвпараметрийг хуулсан хэвээр байх бөгөөд дараа нь утгаараа дамжуулна." Дахиад л алдартай "хэрэв". Саттер яагаад нийтлэлийг өөрчилсөн бэ, би үүнийг яаж мэдсэн бэ? Сэтгэгдэлээс. Түүний нийтлэлийн сэтгэгдлийг уншина уу, дашрамд хэлэхэд тэдгээр нь нийтлэлээс илүү сонирхолтой бөгөөд ашигтай байдаг. Үнэн бол энэ нийтлэлийг бичсэний дараа Саттер бодлоо өөрчилсөн бөгөөд тэрээр ийм зөвлөгөө өгөхөө больсон. Үзэл бодлын өөрчлөлтийг түүний 2014 онд CppCon дээр хэлсэн үгнээс харж болно: “Үндсэн зүйл рүү буцах! Орчин үеийн C++ загварын үндсэн зүйлс". Үзээрэй, бид дараагийн интернет холбоос руу шилжих болно.

Дараа нь бид 21-р зууны програмчлалын гол нөөц бол StackOverflow юм. Өөрөөр хэлбэл, энэ нийтлэлийг бичиж байх үед эерэг хариу урвалын тоо 1700-аас давсан хариулт юм. Асуулт нь: Хуулбар, солих хэлц гэж юу вэ? , мөн гарчигнаас нь үзэхэд бидний үзэж буй сэдвийн талаар тийм ч сайн биш байна. Гэхдээ зохиолч энэ асуултын хариултдаа бидний сонирхдог сэдвийг бас хөндсөн. Тэрээр мөн "хэрэв аргументыг ямар ч байсан хуулбарлах болно" гэж PPZ ашиглахыг зөвлөж байна (Бурханаас үүнийг товчлох цаг нь болсон). Ерөнхийдөө энэ зөвлөгөө нь түүний хариулт болон операторын хүрээнд маш тохиромжтой мэт санагдаж байна, гэхдээ зохиогч зөвхөн энэ тохиолдолд биш, харин илүү өргөн хүрээнд ийм зөвлөгөө өгөх эрх чөлөөг авдаг. Түүгээр ч барахгүй тэрээр бидний өмнө нь хэлэлцсэн бүх зөвлөмжөөс илүү урагшилж, үүнийг C++ 03 код дээр ч хийхийг уриалж байна! Зохиогчийг ийм дүгнэлт хийхэд юу нөлөөлсөн бэ?

Хариултыг зохиогч өөр нэг номын зохиолч, Boost.MPL-ийн хагас цагийн хөгжүүлэгч Дэйв Абрахамсын нийтлэлээс гол санаа авсан бололтой. Нийтлэлийг “Хурдмаар байна уу? Үнэ цэнээр дамжуулаарай." , мөн энэ нь 2009 оны 8-р сард хэвлэгдсэн, i.e. C++ 11-ийг баталж, шилжих семантикийг нэвтрүүлэхээс 2 жилийн өмнө. Өмнөх тохиолдлуудын нэгэн адил би уншигчдад нийтлэлийг бие даан уншихыг зөвлөж байна, гэхдээ би Дэйвийн PPZ-ийг дэмжсэн гол аргументуудыг (үнэндээ ганц л аргумент) өгөх болно: та PPZ-ийг ашиглах хэрэгтэй. , учир нь "хуулбарыг алгасах" оновчлол нь үүнтэй сайн ажилладаг ( copy elision), энэ нь PPSC-д байхгүй. Хэрэв та нийтлэлийн сэтгэгдлийг уншвал түүний сурталчилж буй зөвлөгөө нь бүх нийтийнх биш гэдгийг харж болно, үүнийг зохиогч өөрөө тайлбарлагчдын шүүмжлэлд хариулахдаа баталж байна. Гэсэн хэдий ч, уг нийтлэлд аргументыг хуулбарлах тохиолдолд PPP-ийг ашиглах тодорхой зөвлөгөө (удирдамж) агуулж байна. Энэ дашрамд сонирхох хүн байвал “Хурдмаар байна уу? Битгий (үргэлж) үнэ цэнээ бүү алдаарай." . Гарчигнаас нь харахад энэ нийтлэл нь Дэйвийн нийтлэлийн хариу тул та эхнийхийг уншсан бол үүнийг бас уншаарай!

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

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

Миний авч үзэхийг хүсч буй хамгийн эхний дүрэм бол намайг энэ нийтлэлийг авахад хүргэсэн сүүлчийн дарс байсан. Энэ багц нь clang-tidy хэрэгслийн нэг хэсэг бөгөөд үүнээс гадна байхгүй. Дуу чимээтэй холбоотой бүх зүйлийн нэгэн адил энэ хэрэгсэл нь маш алдартай бөгөөд CLion болон Resharper C++-тэй аль хэдийн интеграцчлагдсан (би тэгж л таарсан). Тиймээс, clang-tydy нь PPSC-ээр дамжуулан аргументуудыг хүлээн авдаг констракторууд дээр ажилладаг орчин үеийн утгыг нэвтрүүлэх дүрмийг агуулдаг. Энэ дүрэм нь PPSC-ийг PPZ-ээр солихыг санал болгож байна. Түүнээс гадна, нийтлэл бичих үед энэ дүрмийн тайлбар нь энэ дүрэм гэсэн тайлбарыг агуулна Баяртайзөвхөн бүтээгчид л ажилладаг, гэхдээ тэд (тэд хэн бэ?) энэ дүрмийг бусад аж ахуйн нэгжүүдэд түгээсэн хүмүүсийн тусламжийг дуртайяа хүлээн авах болно. Тэнд, тайлбар дээр Дэйвийн нийтлэлийн холбоос байдаг - хөл нь хаанаас ирсэн нь тодорхой байна.

Эцэст нь, бусад хүмүүсийн мэргэн ухаан, эрх мэдэл бүхий үзэл бодлыг тоймлон дүгнэхийн тулд би танд C++ код бичих албан ёсны удирдамжийг үзэхийг санал болгож байна: C++ үндсэн удирдамж, гол редактор нь Херб Саттер, Бжарне Строуструп (муу биш, тийм ээ?). Тиймээс, эдгээр зөвлөмжүүд нь дараах дүрмийг агуулна: ""-д" параметрийн хувьд хямд хуулбарласан төрлүүдийг үнэ цэнээр нь, бусад нь const-ийн лавлагаагаар дамжуулна" гэсэн хуучин мэргэн ухааныг бүрэн давтаж байна: PPSK хаа сайгүй, жижиг объектуудад PPP. Энэхүү зөвлөмж нь авч үзэх хэд хэдэн хувилбаруудыг тоймлон харуулав. аргумент дамжуулалтыг оновчтой болгох шаардлагатай тохиолдолд. Гэхдээ PPZ нь хувилбаруудын жагсаалтад ороогүй болно!

Надад анхаарал татахуйц өөр эх сурвалж байхгүй тул дамжуулах хоёр аргын шууд шинжилгээнд шилжихийг санал болгож байна.

Шинжилгээ

Өмнөх бичвэр бүхэлдээ миний хувьд ер бусын байдлаар бичигдсэн: Би бусдын санаа бодлыг танилцуулж, тэр ч байтугай өөрийн бодлоо илэрхийлэхгүй байхыг хичээдэг (энэ нь муу болж байгааг би мэднэ). Бусдын санаа бодол, миний зорилго бол тэдгээрийн талаар товч тоймлох явдал байсан тул би бусад зохиолчдоос олж мэдсэн зарим аргументуудыг нарийвчлан авч үзэхээ хойшлуулав. Энэ хэсэгт би эрх баригчдад хандаж, санал өгөхгүй; энд бид PPSC болон PPZ-ийн зарим объектив давуу болон сул талуудыг авч үзэх болно, энэ нь миний субъектив ойлголтоор амтлагдах болно. Мэдээжийн хэрэг, өмнө нь хэлэлцсэн зарим зүйлийг давтах болно, гэхдээ харамсалтай нь энэ нийтлэлийн бүтэц ийм байна.

PPP давуу талтай юу?

Тиймээс, дэмжсэн болон эсэргүүцсэн аргументуудыг авч үзэхээсээ өмнө үнэ цэнийг давах нь бидэнд ямар давуу талтай, ямар тохиолдолд авч үзэхийг санал болгож байна. Бидэнд ийм анги байна гэж бодъё:

Class CopyMover ( public: void setByValuer(Accounter byValuer) ( m_ByValuer = std::move(byValuer); ) void setByRefer(const Accounter& byRefer) ( m_ByRefer = byRefer; ) void setByValuertAmVoid (NotByValuerAm) yValuerAndNotMover = byVal uerAndNotMover; ) хүчингүй setRvaluer (Accounter&& rvaluer) ( m_Rvaluer = std::move(rvaluer; ) );

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

Нягтлан бодогчийн анги нь хэдэн удаа хуулсан/шилжсэнийг тооцдог энгийн анги юм. CopyMover ангид бид дараах сонголтуудыг авч үзэх боломжийг олгодог функцуудыг хэрэгжүүлсэн.

    хөдөлж байнааргументыг давсан.

    Утгаар дамжуулж, дараа нь хуулбарлахаргументыг давсан.

Одоо, хэрэв бид эдгээр функц бүрт утгыг дамжуулбал, жишээ нь:

Нягтлан бодогч byRefer; Accounter byValuer; Accounter byValuerAndNotMover; CopyMover copyMover; copyMover.setByRefer(byRefer); copyMover.setByValuer(byValuer); copyMover.setByValuerAndNotMover(byValuerAndNotMover);

Дараа нь бид дараах үр дүнг авна.

Илт ялагч бол PPSC, учир нь... зөвхөн нэг хувийг өгдөг бол PPZ нь нэг хуулбар, нэг шилжилтийг өгдөг.

Одоо утгыг дамжуулахыг оролдъё:

CopyMover copyMover; copyMover.setByRefer(Accounter()); copyMover.setByValuer(Accounter()); copyMover.setByValuerAndNotMover(Accounter()); copyMover.setRvaluer(Accounter());

Бид дараахь зүйлийг авна.

Энд тодорхой ялагч байхгүй, учир нь... PPZ болон PPSK хоёулаа тус бүр нэг ажиллагаатай боловч PPZ нь хөдөлгөөнийг, PPSK нь хуулбарлах аргыг ашигладаг тул бид ялалтыг PPZ-д өгч чадна.

Гэхдээ бидний туршилтууд үүгээр дуусахгүй; шууд бус дуудлагыг дуурайхын тулд дараах функцуудыг нэмж оруулцгаая (дараагийн аргумент дамжуулалттай):

хүчингүй болгох setByValuer(Accounter byValuer, CopyMover& copyMover) ( copyMover.setByValuer(std::move(byValuer)); ) void setByRefer(const Accounter& byRefer, CopyMover & copyMover) ( copyMover.setByRefer(...));

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

PPSC нь PPZ-ийн зөрүүг нэмэгдүүлж, нэг хуулбараар үлдэж байгааг анхаарна уу, харин PPZ нь аль хэдийн 3 үйлдэлтэй (дахин нэг хөдөлгөөн) байна!

Одоо бид утгыг давж, дараах үр дүнг авна.

Одоо PPZ нь 2 хөдөлгөөнтэй, PPSC нь нэг хуулбартай хэвээр байна. Одоо PPZ-ийг ялагчаар нэр дэвшүүлэх боломжтой юу? Үгүй, учир нь Хэрэв нэг нүүдэл ядаж нэг хуулбараас дордохгүй байвал бид 2 нүүдлийн талаар ижил зүйлийг хэлж чадахгүй. Тиймээс энэ жишээнд ялагч байхгүй болно.

Тэд намайг эсэргүүцэж магадгүй: "Зохиогч, та өрөөсгөл бодолтой байна, та өөрт ашигтай зүйлийг татаж байна. 2 нүүдэл ч гэсэн хуулбарлахаас хямд байх болно!" Би энэ мэдэгдэлтэй санал нийлэхгүй байна Бүгдээрээ, учир нь Хуулахаас хэр хурдан хөдөлж байгаа нь тухайн ангиас шалтгаална, гэхдээ бид "хямдхан" шилжихийг тусдаа хэсэгт авч үзэх болно.

Энд бид нэг сонирхолтой зүйлийг хөндсөн: бид нэг шууд бус дуудлага нэмсэн бөгөөд PPP нь "жин"-д яг нэг үйлдлийг нэмсэн. Шууд бус дуудлага их байх тусам PPZ ашиглах үед илүү их ажиллагаа хийгдэх болно, харин PPSC-ийн хувьд энэ тоо өөрчлөгдөхгүй гэдгийг ойлгохын тулд та MSTU-ийн дипломтой байх шаардлагагүй гэж бодож байна.

Дээр дурдсан бүх зүйл хэнд ч илчлэх магадлал багатай байсан, бид туршилт хийгээгүй байж магадгүй - эдгээр бүх тоо нь C++ програмистуудын дийлэнх нь анх харахад ойлгомжтой байх ёстой. Үнэн бол нэг зүйлийг тодруулах шаардлагатай хэвээр байна: яагаад rvalue-ийн хувьд PZ-д хуулбар (эсвэл өөр нүүдэл) байдаггүй, харин зөвхөн нэг нүүдэл байдаг.

За, бид PPZ болон PPSC-ийн дамжуулалтын ялгааг өөрийн биеэр хуулбар, шилжилтийн тоог ажиглаж үзсэн. Хэдийгээр ийм энгийн жишээн дээр ч гэсэн PPZ-ийн PPSC-ээс давуу тал нь ойлгомжтой юм. ҮгүйМэдээжийн хэрэг, би одоо ч гэсэн бага зэрэг дүр эсгэн дараах дүгнэлтийг хийж байна: хэрэв бид функцийн аргументыг хуулбарлах гэж байгаа бол аргументыг утгаараа функц руу шилжүүлэх талаар бодох нь зүйтэй болов уу. Би яагаад ийм дүгнэлт хийсэн бэ? Дараагийн хэсэг рүү жигд шилжихийн тулд.

Хэрэв бид хуулбарлавал ...

Тиймээс бид "хэрэв" гэсэн зүйр үгэнд хүрнэ. Бидний тулгарсан аргументуудын ихэнх нь PPSC-ийн оронд PPP-ийг бүх нийтээр хэрэгжүүлэхийг уриалаагүй бөгөөд тэд зөвхөн "хэрэв аргументыг хуулбарласан бол" үүнийг хийхийг уриалсан. Энэ мэтгэлцээнд юу буруу байгааг олж мэдэх цаг болжээ.

Би кодыг хэрхэн бичих талаар бага зэрэг тайлбарлаж эхэлмээр байна. Сүүлийн үед миний кодлох процесс улам бүр TDD шиг болж байна, i.e. Ямар ч ангийн аргыг бичих нь энэ арга байгаа тестийг бичихээс эхэлдэг. Үүний дагуу тест бичиж эхлэх, тест бичсэнийхээ дараа аргыг бий болгохдоо би аргументыг хуулах эсэхээ мэдэхгүй хэвээр байна. Мэдээжийн хэрэг, бүх функцууд ийм байдлаар бүтээгддэггүй, ихэнхдээ тест бичих явцад ч гэсэн та ямар төрлийн хэрэгжилтийг яг таг мэддэг. Гэхдээ энэ нь үргэлж тохиолддоггүй!

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

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

За, Бурхан түүнийг ивээг, энэ замаар явж, аргументыг хуулбарлах тал дээр хэрэгжүүлэхдээ юу хийж байгаагаас хамааран интерфейсүүдийг өөрчилье. Бид энэ аргыг бичсэн гэж үзье:

Хүчингүй багцНэр(Нэр) ( m_Name = шилжих(нэр); )

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

Хүчингүй багцНэр(Нэр) ( m_Name = зөөх(нэр); ялгаруулах нэрӨөрчлөгдсөн(m_Name); )

Энэ кодтой холбоотой асуудал байна уу? Идэх. setName руу залгах болгонд бид дохио илгээдэг тул дохио хэзээ ч илгээгдэх болно утга учир m_Нэр өөрчлөгдөөгүй. Гүйцэтгэлийн асуудлаас гадна энэ нөхцөл байдал нь ямар нэгэн байдлаар setName руу залгахаар ирсэн дээрх мэдэгдлийг хүлээн авсан кодын улмаас хязгааргүй давталт үүсгэдэг. Эдгээр бүх асуудлаас зайлсхийхийн тулд ийм аргууд ихэвчлэн иймэрхүү харагддаг.

Хүчингүй багцНэр(Нэр) ( if(нэр == m_Name) буцах; m_Name = зөөх(нэр); ялгаруулах нэрӨөрчлөгдсөн(m_Name); )

Бид дээр дурдсан асуудлуудаас салсан боловч одоо бидний "хэрэв бид хуулж авбал ..." дүрэм бүтэлгүйтсэн - аргументыг болзолгүй хуулбарлахаа больсон, одоо бид үүнийг зөвхөн өөрчлөгдсөн тохиолдолд л хуулах болно! Тэгэхээр бид одоо яах ёстой вэ? Интерфэйсийг өөрчлөх үү? За, энэ засварын улмаас ангийн интерфейсийг өөрчилье. Хэрэв манай анги энэ аргыг хийсвэр интерфейсээс өвлөн авсан бол яах вэ? Тэнд бас өөрчилье! Хэрэгжилт өөрчлөгдсөн учраас өөрчлөлт их байна уу?

Дахиад л намайг эсэргүүцэж магадгүй, зохиолч аа, энэ нөхцөл бүрдчихээд байхад чи яагаад шүдэнзний мөнгө хэмнэх гээд байгаа юм бэ? Тийм ээ, ихэнх дуудлага худал байх болно! Үүнд итгэх итгэл байна уу? Хаана? Хэрэв би шүдэнзээ хэмнэхээр шийдсэн бол бид PPZ ашигласан нь яг ийм хэмнэлтийн үр дагавар биш гэж үү? Үр ашгийг эрхэмлэдэг “намын шугам”-ыг л үргэлжлүүлж байна.

Барилгачид

Бүтээгчдийг товчхон авч үзье, ялангуяа тэдний хувьд clang-tidy-д зориулсан тусгай дүрэм байдаг бөгөөд энэ нь бусад арга/функцид хараахан тохирохгүй байна. Бидэнд ийм анги байна гэж бодъё:

Class JustClass ( public: JustClass(const string & justString): m_JustString(justString) ( ) private: string m_JustString; );

Мэдээжийн хэрэг, параметрийг хуулсан бөгөөд clang-tidy нь бүтээгчийг үүн дээр дахин бичих нь зүйтэй гэдгийг хэлэх болно:

JustClass(string justString): m_JustString(move(justString)) ( )

Үнэнийг хэлэхэд, энд маргах нь надад хэцүү байдаг - эцэст нь бид үргэлж хуулбарладаг. Ихэнхдээ бид ямар нэг зүйлийг бүтээгчээр дамжуулахдаа үүнийг хуулж авдаг. Гэхдээ ихэнхдээ энэ нь үргэлж гэсэн үг биш юм. Энд өөр нэг жишээ байна:

Class TimeSpan ( public: TimeSpan(DateTime start, DateTime end) ( if(star > end) throw InvalidTimeSpan(); m_Start = move(start); m_End = move( end); ) private: DateTime m_Start; DateTime m_End; );

Энд бид үргэлж хуулж авдаггүй, зөвхөн огноог зөв оруулсан тохиолдолд л хуулдаг. Мэдээжийн хэрэг, ихэнх тохиолдолд ийм байх болно. Гэхдээ дандаа биш.

Та өөр жишээ өгч болно, гэхдээ энэ удаад кодгүй. Та том объектыг хүлээн авдаг ангитай гэж төсөөлөөд үз дээ. Анги нь удаан хугацааны турш оршин тогтнож байсан бөгөөд одоо түүний хэрэгжилтийг шинэчлэх цаг болжээ. Бидэнд том байгууламжийн талаас илүүгүй нь (энэ нь олон жилийн туршид өссөн), магадгүй бүр бага байх ёстой гэдгийг ойлгож байна. Бид үнэ цэнээр дамжуулснаар энэ талаар ямар нэг зүйл хийж чадах уу? Үгүй, бид юу ч хийх боломжгүй, учир нь хуулбар үүсгэгдсэн хэвээр байх болно. Гэхдээ хэрэв бид PPSC ашигласан бол бид хийдэг зүйлээ өөрчлөх байсан дотордизайнер. Энэ бол гол зүйл юм: PPSC-ийг ашиглан бид функцээ (барилга бүтээгч) хэрэгжүүлэхэд юу, хэзээ болж байгааг хянадаг, гэхдээ хэрэв бид PPZ ашигладаг бол хуулбарлах хяналтаа алддаг.

Та энэ хэсгээс юу авч болох вэ? “Ямар ч байсан хуулж авбал...” гэсэн маргаан маш маргаантай байгаа учраас Бид юу хуулахаа тэр бүр мэддэггүй бөгөөд мэдсэн ч энэ нь ирээдүйд үргэлжлэх болно гэдэгт итгэлгүй байдаг.

Нүүх нь хямд

Хөдөлгөөний семантик үүссэн цагаасаа л орчин үеийн C++ кодыг бичихэд ноцтой нөлөөлж эхэлсэн бөгөөд цаг хугацаа өнгөрөх тусам энэ нөлөө улам бүр нэмэгдсээр байна: энэ нь гайхах зүйл биш юм, учир нь хөдөлгөөн маш их байдаг. хямдхуулбарлахтай харьцуулахад. Гэхдээ тийм үү? Хөдөлгөөн гэж үнэн үү Үргэлжхямд мэс засал? Үүнийг бид энэ хэсэгт ойлгохыг хичээх болно.

Хоёртын том объект

Өчүүхэн жишээнээс эхэлье, бидэнд дараах анги байна гэж бодъё.

Struct Blob ( std::array өгөгдөл; );

Энгийн бөмбөг(BDO, Англи хэлний BLOB), янз бүрийн нөхцөл байдалд ашиглаж болно. Лавлагаа болон үнэ цэнээр дамжуулж өгөхөд ямар зардал гарахыг харцгаая. Манай BDO-г дараах байдлаар ашиглах болно.

Хүчингүй Хадгалах::setBlobByRef(const Blob& blob) ( m_Blob = blob; ) void Storage::setBlobByVal(Blob blob) ( m_Blob = move(blob); )

Мөн бид эдгээр функцуудыг дараах байдлаар дуудна.

Const Blob blob(); хадгалах; storage.setBlobByRef(blob); storage.setBlobByVal(blob);

Бусад жишээнүүдийн код нь зөвхөн өөр өөр нэр, төрлөөр үүнтэй ижил байх тул үлдсэн жишээнүүдэд би үүнийг өгөхгүй - бүх зүйл хадгалах газарт байгаа.

Хэмжилт рүү шилжихээсээ өмнө үр дүнг урьдчилан таамаглахыг хичээцгээе. Тиймээс бидэнд Storage ангиллын объектод хадгалахыг хүссэн 4 KB std:: массив байна. Өмнө нь мэдэж байсанчлан, PPSC-ийн хувьд бид нэг хувьтай, харин PPZ-ийн хувьд нэг хуулбар, нэг нүүдэлтэй байх болно. Массивыг зөөх боломжгүйг үндэслэн PPZ-ийн хувьд 2 хувь, PPSC-ийн хувьд нэг хувь байх болно. Тэдгээр. Бид PPSC-ийн гүйцэтгэлийн хувьд хоёр дахин давуу талыг хүлээж болно.

Одоо туршилтын үр дүнг харцгаая:

Энэ болон дараагийн бүх туршилтыг MSVS 2017 (15.7.2) болон /O2 туг ашиглан нэг машин дээр хийсэн.

Дадлага нь таамаглалтай давхцаж байна - утгыг дамжуулах нь 2 дахин илүү үнэтэй байдаг, учир нь массивын хувьд шилжих нь хуулбарлахтай бүрэн тэнцүү юм.

Шугам

Өөр нэг жишээг харцгаая, ердийн std::string . Бид юу хүлээж болох вэ? Орчин үеийн хэрэглүүрүүд нь богино (ойролцоогоор 16 тэмдэгт) ба урт (богинооос урт) гэсэн хоёр төрлийн мөрийг ялгаж салгадаг гэдгийг бид мэднэ (би үүнийг нийтлэлд хэлэлцсэн). Богино буферийн хувьд ердийн C-массив болох char-ийн дотоод буфер ашиглагддаг боловч урт нь аль хэдийн нуруулдан дээр тавигдах болно. Бид богино мөрийг сонирхдоггүй, учир нь... Үр дүн нь BDO-той адил байх тул урт мөрүүд дээр анхаарлаа хандуулцгаая.

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

Бид энэ "үзэгдэл" -ийг тайлбарлах болно. Тэгэхээр бид одоо байгаа мөрийг аль хэдийн байгаа мөр рүү хуулах үед юу болох вэ? Өчүүхэн жишээг харцгаая:

Эхлээд мөр(64, "C"); мөр секунд(64, "N"); //... хоёр дахь = эхний;

Бидэнд 64 тэмдэгт бүхий хоёр мөр байгаа тул тэдгээрийг үүсгэх үед дотоод буфер хангалтгүй тул хоёр мөрийг нуруулдан дээр хуваарилдаг. Одоо бид нэгээс хоёрдугаарт хуулж байна. Учир нь бидний эгнээний хэмжээ ижил байна, эхнийхээс бүх өгөгдлийг багтаахын тулд хоёрдугаарт хангалттай зай хуваарилсан нь ойлгомжтой, тэгэхээр хоёрдугаарт = нэгдүгээрт; улиг болсон memcpy байх болно, өөр юу ч биш. Гэхдээ бид бага зэрэг өөрчлөгдсөн жишээг харвал:

Эхлээд мөр(64, "C"); хоёр дахь мөр = эхний;

дараа нь оператор= руу дуудлага хийхээ больсон ч хуулбар үүсгэгчийг дуудах болно. Учир нь Бид бүтээгчтэй харьцаж байгаа тул түүнд санах ой байхгүй. Энэ нь эхлээд сонгогдсон байх ёстой бөгөөд зөвхөн дараа нь эхлээд хуулна. Тэдгээр. Энэ бол санах ойн хуваарилалт, дараа нь memcpy . Глобал овоо дээр санах ойг хуваарилах нь ихэвчлэн үнэтэй ажиллагаа байдаг тул хоёр дахь жишээнээс хуулбарлах нь эхнийхээс илүү үнэтэй байх болно гэдгийг та бид мэднэ. Нэг овоолгын санах ойн хуваарилалт илүү үнэтэй.

Энэ нь бидний сэдэвтэй ямар холбоотой вэ? Хамгийн шууд нь, учир нь эхний жишээ нь PPSC-д яг юу тохиолдохыг, хоёр дахь жишээ нь PPZ-д юу тохиолдохыг харуулдаг: PPZ-ийн хувьд шинэ мөр үргэлж үүсгэгддэг бол PPSC-ийн хувьд одоо байгаа мөрийг дахин ашигладаг. Та гүйцэтгэлийн хугацааны ялгааг аль хэдийн харсан тул энд нэмэх зүйл алга.

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

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

Та энэ хэсгээс юу авах ёстой вэ? Хөдөлгөөн нь үнэхээр хямд байсан ч хуулбарлах+шилжүүлэхээр солих нь гүйцэтгэлийн хувьд үргэлж ижил төстэй үр дүнг өгнө гэсэн үг биш юм.

Нарийн төвөгтэй төрөл

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

Би мөн 10 талбартай Person-г ашиглах болно, гэхдээ үүний тулд би ангийн үндсэн хэсэгт 10 талбар үүсгэхгүй. Person-ийн хэрэгжилт нь савыг гүнд нь нуудаг - энэ нь хэрвээ Person жинхэнэ анги байсан бол хэрхэн ажиллахаас бараг хазайхгүйгээр туршилтын параметрүүдийг өөрчлөхөд илүү тохиромжтой болгодог. Гэсэн хэдий ч, хэрэгжилт бэлэн байгаа бөгөөд та үргэлж кодыг шалгаж, би буруу зүйл хийсэн эсэхийг хэлж болно.

Ингээд явцгаая: PPSC болон PPZ ашиглан Storage руу шилжүүлдэг string төрлийн 10 талбартай хүн:

Таны харж байгаагаар бид гүйцэтгэлийн хувьд асар их зөрүүтэй байгаа нь өмнөх хэсгүүдийн дараа уншигчдад гайхах зүйл биш юм. Би бас Person анги нь хангалттай "бодит" бөгөөд ийм үр дүнг хийсвэр гэж үзэхгүй гэдэгт би итгэдэг.

Дашрамд хэлэхэд, би энэ нийтлэлийг бэлдэж байхдаа өөр нэг жишээ бэлдсэн: хэд хэдэн std::function объект ашигладаг анги. Миний бодлоор энэ нь PPSC-ийн гүйцэтгэлд PPZ-ээс давуу талыг харуулах ёстой байсан ч энэ нь яг эсрэгээрээ болсон! Гэхдээ үр дүн нь надад таалагдаагүйдээ биш, харин яагаад ийм үр дүнд хүрснийг ойлгох цаг байхгүй байсан учраас би энэ жишээг энд дурдаагүй. Гэсэн хэдий ч хадгалах санд код (хэвлэгч), тестүүд байдаг - хэрэв хэн нэгэн үүнийг олж мэдэхийг хүсвэл судалгааны үр дүнг сонсоход таатай байх болно. Би энэ жишээнд дараа нь эргэж орохоор төлөвлөж байгаа бөгөөд хэрэв надаас өмнө хэн ч эдгээр үр дүнг нийтлэхгүй бол би тэдгээрийг тусдаа нийтлэлд авч үзэх болно.

Үр дүн

Тиймээс бид үнэ цэнээр дамжуулж, тогтмол тоогоор дамжихын давуу болон сул талуудыг авч үзсэн. Бид зарим жишээг авч үзээд эдгээр жишээн дээрх хоёр аргын гүйцэтгэлийг харлаа. Мэдээжийн хэрэг, энэ нийтлэл нь бүрэн дүүрэн байж чадахгүй, гэхдээ миний бодлоор энэ нь аль аргыг ашиглах нь дээр вэ гэсэн бие даасан, мэдээлэлтэй шийдвэр гаргахад хангалттай мэдээлэл агуулдаг. Хэн нэгэн эсэргүүцэж магадгүй: "Яагаад нэг аргыг ашиглах хэрэгтэй вэ, даалгавраас эхэлье!" Би энэ дипломын ажилтай ерөнхийдөө санал нийлж байгаа ч энэ нөхцөл байдалд санал нийлэхгүй байна. Хэл дээр аргумент дамжуулах цорын ганц арга зам байж болно гэдэгт би итгэдэг. үүнийг анхдагч байдлаар ашигладаг.

Өгөгдмөл гэдэг нь юу гэсэн үг вэ? Энэ нь би функц бичихдээ аргументыг хэрхэн дамжуулах талаар боддоггүй, зүгээр л "default"-ыг ашигладаг гэсэн үг юм. C++ хэл бол олон хүмүүсээс зайлсхийдэг нэлээд төвөгтэй хэл юм. Миний бодлоор нарийн төвөгтэй байдал нь тухайн хэлэнд байдаг хэлний бүтцийн нарийн төвөгтэй байдлаас (ердийн программист хэзээ ч тааралддаггүй) биш, харин хэл нь таныг маш их бодогдуулдагтай холбоотой юм: би чөлөөлөгдсөн үү? санах ойг нэмэгдүүлэх, энэ функцийг энд ашиглах нь үнэтэй юу? гэх мэт.

Олон програмистууд (C, C++ болон бусад) 2011 оноос хойш гарч эхэлсэн C++-д үл итгэн, эмээдэг. Хэл нь төвөгтэй болж байна, одоо зөвхөн “гуру” л бичдэг болсон гэх мэт шүүмжлэлийг би маш их сонссон. Би хувьдаа энэ нь тийм биш гэдэгт би итгэдэг - эсрэгээр, хороо нь эхлэгчдэд хэлийг илүү ээлтэй болгоход маш их цаг зарцуулдаг бөгөөд ингэснээр програмистууд хэлний онцлог шинж чанаруудын талаар бага бодох хэрэгтэй болдог. Эцсийн эцэст, хэрэв бид хэлтэй тэмцэх шаардлагагүй бол бид даалгаврын талаар бодох цагтай болно. Эдгээр хялбаршуулалтууд нь ухаалаг заагч, ламбда функцууд болон хэл дээр гарч ирсэн бусад зүйлсийг багтаасан болно. Үүний зэрэгцээ, одоо бид илүү их суралцах шаардлагатай байгааг үгүйсгэхгүй, гэхдээ сурахад юу нь буруу вэ? Эсвэл сурах шаардлагатай бусад алдартай хэлэнд ямар ч өөрчлөлт гарахгүй байна уу?

Цаашилбал, хариуд нь “Чи бодохыг хүсэхгүй байна уу? Дараа нь PHP дээр бичээрэй." Би ийм хүмүүст хариу хэлмээргүй байна. Би тоглоомын бодит байдлаас жишээ хэлье: Starcraft-ын эхний хэсэгт барилгад шинэ ажилчин бий болоход ашигт малтмал (эсвэл хий) олборлож эхлэхийн тулд түүнийг гараар илгээх шаардлагатай байв. Түүгээр ч зогсохгүй, ашигт малтмалын багц бүр хязгаартай байсан бөгөөд үүнд хүрэхэд ажилчдын өсөлт ашиггүй болж, бие биедээ саад болж, үйлдвэрлэлийг улам дордуулж болно. Үүнийг Starcraft 2-т өөрчилсөн: ажилчид автоматаар ашигт малтмал (эсвэл хий) олборлож эхэлдэг бөгөөд энэ нь одоогоор хэдэн ажилчин олборлож байгаа, энэ ордын хязгаар хэр байгааг харуулж байна. Энэ нь тоглогчийн баазтай харилцах харилцааг ихээхэн хялбарчилж, түүнд тоглоомын илүү чухал талуудад анхаарлаа төвлөрүүлэх боломжийг олгосон: бааз барих, цэрэг цуглуулах, дайсныг устгах. Энэ бол зүгээр л гайхалтай шинэлэг юм шиг санагдаж байна, гэхдээ Интернет дээр юу эхэлсэн бэ! Хүмүүс (тэд хэн бэ?) тоглоомыг "бүхэж байна", "тэд Starcraft алсан" гэж хашгирч эхлэв. Ийм мессежийг зөвхөн "элит" клубт байх дуртай "нууц мэдлэгийг сахигчид" болон "өндөр APM"-аас авах боломжтой.

Тиймээс, бидний сэдэв рүү буцаж ирэхэд би код бичих талаар бага бодох тусам яаралтай асуудлыг шийдэх талаар бодох хэрэгтэй болно. PPSC эсвэл PPZ гэсэн аль аргыг ашиглах вэ гэж бодох нь асуудлыг шийдэхэд нэг ч удаа ойртдоггүй тул би ийм зүйлийн талаар бодохоос татгалзаж, нэг хувилбарыг сонгоорой: тогтмол тоонд шилжүүлэх. Яагаад? Учир нь би ерөнхий тохиолдолд PPP-ийн давуу талыг олж харахгүй байгаа бөгөөд онцгой тохиолдлуудыг тусад нь авч үзэх шаардлагатай.

Энэ бол онцгой тохиолдол юм, зүгээр л ямар нэг аргаар PPSC нь гацаа болж хувирч байгааг анзаарсан бөгөөд дамжуулалтыг PPZ руу өөрчилснөөр бид гүйцэтгэлийн чухал өсөлтийг авах болно, би ашиглахаас эргэлзэхгүй байна. PPZ. Гэхдээ анхдагч байдлаар би PPSC-ийг ердийн функцууд болон бүтээгчид ашиглах болно. Боломжтой бол би энэ аргыг аль болох сурталчлах болно. Яагаад? Яагаад гэвэл програмистуудын арслангийн хувь нь тийм ч сайн мэдлэггүй (зарчмын хувьд, эсвэл зүгээр л юмны савлуур руу орж амжаагүй), зүгээр л зөвлөгөөг дагаж мөрддөг учраас PPP-ийг сурталчлах нь харгис үйлдэл гэж би боддог. Дээрээс нь хэд хэдэн зөрчилдөөнтэй зөвлөгөө байгаа бол тэд илүү энгийнийг нь сонгодог бөгөөд энэ нь хэн нэгэн хаа нэгтээ ямар нэг зүйл сонссоноос болж кодын гутранги байдалд хүргэдэг. Тийм ээ, энэ хүн Абрахамын нийтлэлийн холбоосыг өгч, түүний зөв гэдгийг батлах боломжтой. Дараа нь та суугаад кодыг уншаад бодоорой: энэ параметрийг бичсэн программист Java-ээс ирсэн, зүгээр л олон "ухаалаг" нийтлэл уншсан учраас энд параметрийг утгаар дамжуулж байна уу, эсвэл үнэхээр хэрэгтэй байна уу? техникийн тодорхойлолт?

PPSC-ийг уншихад илүү хялбар байдаг: тэр хүн C++-ийн "сайн хэлбэрийг" сайн мэддэг бөгөөд бид цаашаа явна - харц удаан үргэлжлэхгүй. PPSC ашиглах дадлыг C++ програмистуудад олон жилийн турш зааж өгсөн, үүнийг орхих шалтгаан юу вэ? Энэ нь намайг өөр нэг дүгнэлтэд хүргэж байна: хэрэв аргын интерфейс нь PPP ашигладаг бол яагаад ийм болсон талаар тайлбар байх ёстой. Бусад тохиолдолд PPSC-ийг хэрэглэх ёстой. Мэдээжийн хэрэг, үл хамаарах төрлүүд байдаг, гэхдээ би тэдгээрийг энд дурдаагүй, учир нь тэдгээр нь: string_view , initializer_list , төрөл бүрийн давталт гэх мэт. Гэхдээ эдгээр нь үл хамаарах зүйлүүд бөгөөд жагсаалт нь төсөлд ямар төрлүүдийг ашиглахаас хамаарч өргөжиж болно. Гэхдээ мөн чанар нь C++ 98-аас хойш өөрчлөгдөөгүй хэвээр байна: анхдагч байдлаар бид үргэлж PPCS ашигладаг.

std::string-ийн хувьд жижиг мөрт ялгаа байхгүй байх магадлалтай, бид энэ тухай дараа ярих болно.

"Цоо тавих" тухай дүр эсгэсэн тайлбарт би урьдчилан хүлцэл өчье, гэхдээ бид таныг ямар нэгэн байдлаар нийтлэлд татан оруулах хэрэгтэй)) Миний хувьд хийсвэр нь таны хүлээлтийг хангаж байгаа эсэхийг шалгахыг хичээх болно.

Товчхондоо бид юу ярьж байна

Хүн бүр үүнийг аль хэдийн мэддэг, гэхдээ эхэнд би аргын параметрүүдийг 1С-д хэрхэн дамжуулж болохыг сануулах болно. Тэдгээрийг "лавлагаа" эсвэл "үнэ цэнээр" дамжуулж болно. Эхний тохиолдолд бид дуудлага хийх цэгтэй ижил утгыг, хоёрдугаарт, хуулбарыг нь арга руу шилжүүлдэг.

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

Гэхдээ хэрэв бид параметрээ лавлагаагаар дамжуулахыг хүсэхгүй байгаа бол параметрийн өмнө түлхүүр үг зааж өгч болно. Утга

Procedure ByValue(Value Parameter) Параметр = 2; EndProcedure Параметр = 1; Үнээр(Үзүүлэлт); Тайлан (параметр); // 1 хэвлэнэ

Бүх зүйл амласан ёсоороо ажилладаг - параметрийн утгыг өөрчлөх (эсвэл "орлуулах") нь аргын гаднах утгыг өөрчлөхгүй.

За, ямар онигоо вэ?

Бид анхдагч төрлүүдийг (мөр, тоо, огноо гэх мэт) параметрүүдийг биш харин объект болгон дамжуулж эхлэхэд сонирхолтой мөчүүд эхэлдэг. Энд объектын "гүехэн", "гүн" хуулбарууд, мөн заагч (C++ хэлээр биш, харин хийсвэр бариулууд) гэх мэт ойлголтууд гарч ирдэг.

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

Өөрөөр хэлбэл, объектыг лавлагаагаар дамжуулж, параметрт "Массив" гэсэн утгыг оноож өгвөл дуудлагын цэг дээр бид массив хүлээн авах болно. Лавлагаагаар дамжуулсан утгыг дахин хуваарилах нь дуудлагын байршлаас харагдана.

Procedure ProcessValue(Parameter) Параметр = Шинэ массив; EndProcedure Table = Шинэ утгын хүснэгт; ProcessValue(Хүснэгт); Тайлан(ValueType(Хүснэгт)); // Массив гаргана

Хэрэв бид объектыг утгаараа дамжуулвал бидний утгын хүснэгт алдагдахгүй.

Объектын агуулга ба төлөв

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

Procedure ProcessValue(Parameter) Parameter.Clear(); EndProcedure Table = Шинэ утгын хүснэгт; Хүснэгт.Нэмэх(); ProcessValue(Хүснэгт); Тайлан(Хүснэгт.Тоо хэмжээ()); // 0 гаргана

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

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

Энэ нь үргэлж үнэн байдаг, зөвхөн ...

Үйлчлүүлэгч-серверийн харилцан үйлчлэл

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

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

  • Програмистын зорилгын талаар тодорхой мэдэгдэл. Аргын гарын үсгийг харснаар аль параметрийг оруулах, аль нь гаралт болохыг тодорхой хэлж чадна. Энэ кодыг унших, хадгалахад хялбар байдаг
  • Сервер дээрх "лавлагаагаар" параметрийн өөрчлөлтийг үйлчлүүлэгчийн дуудлагын цэг дээр харагдахын тулд pӨгүүллийн эхэнд тайлбарласан зан төлөвийг хангахын тулд платформ нь өөрөө сервер рүү линкээр дамжуулсан параметрүүдийг заавал буцааж өгөх болно. Хэрэв параметрийг буцаах шаардлагагүй бол ачаалал ихсэх болно. Өгөгдлийн солилцоог оновчтой болгохын тулд гаралтанд утга нь хэрэггүй параметрүүдийг Value гэсэн үгээр тэмдэглэх хэрэгтэй.

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

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

&OnServerProcedureByLink(Параметр) Параметр.Clear(); EndProcedure &OnServerProcedureByValue(Value Parameter) Parameter.Clear(); EndProcedure &OnClient Procedure ByValueClient(Value Parameter) Parameter.Clear(); EndProcedure &OnClient Procedure CheckValue() List1= Шинэ ЖагсаалтынҮнэлгээ; List1.Add("сайн уу"); Жагсаалт2 = Жагсаалт1.Хуулбар(); Жагсаалт3 = Жагсаалт1.Хуулбар(); // объектыг бүрэн хуулж, // сервер рүү шилжүүлж, дараа нь буцаана. // жагсаалтыг арилгах нь дуудлагын цэг дээр харагдаж байна ByRef(List1); // объектыг бүрэн хуулсан, // сервер рүү шилжүүлсэн. Энэ нь эргэж ирэхгүй. // Жагсаалтыг арилгах нь ByValue(List2)-г дуудах үед ҮЗЭХГҮЙ; // зөвхөн объект заагчийг хуулсан // жагсаалтыг арилгах нь ByValueClient(List3) руу залгах цэг дээр харагдана; Тайлан(Жагсаалт1.Тоо хэмжээ()); Тайлан(Жагсаалт2.Тоо хэмжээ()); Тайлан(Жагсаалт3.Тоо хэмжээ()); Процедурын төгсгөл

Дүгнэлт

Товчхондоо үүнийг дараах байдлаар дүгнэж болно.

  • Лавлагаагаар дамжуулснаар огт өөр объекттой объектыг "дарж бичих" боломжийг олгодог
  • Утгаар дамжуулснаар объектыг "дарж бичих" боломжийг олгодоггүй боловч объектын дотоод төлөвийн өөрчлөлт харагдах болно, учир нь Бид ижил объектын жишээтэй ажиллаж байна
  • Серверийн дуудлага хийх үед тухайн объектын ӨӨР НЭГДСЭН тохиолдлуудтай ажил хийгддэг, учир нь Гүнзгий хуулбарыг гүйцэтгэсэн. Түлхүүр үг Утгасерверийн инстанцыг үйлчлүүлэгчийн инстанц руу буцаан хуулахаас урьдчилан сэргийлэх бөгөөд сервер дээрх объектын дотоод төлөвийг өөрчлөх нь үйлчлүүлэгчид ижил төстэй өөрчлөлт оруулахгүй.

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