Qiymat bo'yicha o'tish. Parametrlarni mos yozuvlar va qiymat bo'yicha o'tkazish. Standart sozlamalar

Demak, Faktorial(n) n sonning faktorialini hisoblash funksiyasi bo‘lsin. Keyin, 1 faktorial 1 ekanligini "bilar ekanmiz", biz quyidagi zanjirni qurishimiz mumkin:

Faktorial(4)=Omilli(3)*4

Faktorial(3)=Omilli(2)*3

Faktorial(2)=Omilli(1)*2

Ammo, agar bizda n=1 bo'lganda Faktorial funksiya 1 ni qaytarishi kerak bo'lgan terminal shart bo'lmaganida, bunday nazariy zanjir hech qachon tugamagan bo'lardi va bu Call Stack Overflow xatosi - call stack overflow bo'lishi mumkin edi. Qo'ng'iroqlar to'plami nima ekanligini va u qanday to'lib ketishi mumkinligini tushunish uchun funksiyamizning rekursiv bajarilishini ko'rib chiqamiz:

Funktsiya faktorial(n: Integer): LongInt;

Agar n=1 bo'lsa

Faktorial:=Faktorial(n-1)*n;

Oxiri;

Ko'rib turganimizdek, zanjir to'g'ri ishlashi uchun har bir keyingi funktsiya o'ziga qo'ng'iroq qilishdan oldin, barcha mahalliy o'zgaruvchilarni biror joyda saqlash kerak, shunda zanjir teskari bo'lganda, natija to'g'ri bo'ladi (hisoblangan qiymat n-1 faktorialining n ga ko'paytiriladi). Bizning holatda, har safar faktorial funktsiya o'zidan chaqirilganda, n o'zgaruvchining barcha qiymatlari saqlanishi kerak. O'zini rekursiv chaqirganda funktsiyaning mahalliy o'zgaruvchilari saqlanadigan soha chaqiruvlar stegi deb ataladi. Albatta, bu stek cheksiz emas va agar rekursiv qo'ng'iroqlar noto'g'ri tuzilgan bo'lsa, tugashi mumkin. Bizning misolimizdagi takrorlanishlarning chekliligi n=1 bo'lganda funktsiya chaqiruvi to'xtashi bilan kafolatlanadi.

Parametrlarni qiymat va mos yozuvlar bo'yicha o'tkazish

Shu paytgacha biz pastki dasturdagi qiymatni o'zgartira olmadik haqiqiy parametr(ya'ni, pastki dasturni chaqirganda ko'rsatilgan parametr) va ba'zi amaliy vazifalarda bu qulay bo'ladi. Bir vaqtning o'zida ikkita haqiqiy parametrining qiymatini o'zgartiruvchi Val protsedurasini eslaylik: birinchisi - satr o'zgaruvchisining aylantirilgan qiymati yoziladigan parametr, ikkinchisi - Code parametri, bu erda xatoliklar soni. belgi turini o'zgartirish vaqtida ishlamay qolganda joylashtiriladi. Bular. hali ham kichik dastur haqiqiy parametrlarni o'zgartirishi mumkin bo'lgan mexanizm mavjud. Bu parametrlarni o'tkazishning turli usullari tufayli mumkin. Keling, ushbu usullarni batafsil ko'rib chiqaylik.

Paskalda dasturlash

Parametrlarni qiymat bo'yicha o'tkazish

Umuman olganda, biz barcha parametrlarni o'z tartibimizga o'tkazdik. Mexanizm quyidagicha: haqiqiy parametr ko'rsatilganda uning qiymati pastki dastur joylashgan xotira maydoniga ko'chiriladi va keyin funksiya yoki protsedura o'z ishini tugatgandan so'ng, bu maydon tozalanadi. Taxminan aytganda, quyi dastur ishlayotgan vaqtda uning parametrlarining ikkita nusxasi mavjud: biri chaqiruvchi dastur doirasida, ikkinchisi esa funksiya doirasida.

Parametrlarni o'tkazishning ushbu usuli bilan pastki dasturni chaqirish uchun ko'proq vaqt talab etiladi, chunki qo'ng'iroqning o'ziga qo'shimcha ravishda barcha haqiqiy parametrlarning barcha qiymatlarini nusxalash kerak. Agar kichik dasturga katta hajmdagi ma'lumotlar uzatilsa (masalan, ko'p sonli elementlarga ega massiv), ma'lumotlarni mahalliy hududga ko'chirish uchun zarur bo'lgan vaqt sezilarli bo'lishi mumkin va bu dasturlarni ishlab chiqishda e'tiborga olinishi kerak. ularning ishlashida qiyinchiliklarni topish.

Ushbu uzatish usuli bilan haqiqiy parametrlarni pastki dastur tomonidan o'zgartirib bo'lmaydi, chunki o'zgarishlar faqat ajratilgan mahalliy hududga ta'sir qiladi, bu funktsiya yoki protsedura tugagandan so'ng chiqariladi.

Parametrlarni mos yozuvlar bo'yicha o'tkazish

Ushbu usul yordamida haqiqiy parametrlarning qiymatlari pastki dasturga ko'chirilmaydi, lekin ular joylashgan xotiradagi manzillar (o'zgaruvchilarga havolalar) uzatiladi. Bunday holda, pastki dastur allaqachon mahalliy doirada bo'lmagan qiymatlarni o'zgartiradi, shuning uchun barcha o'zgarishlar qo'ng'iroq qiluvchi dasturga ko'rinadi.

Argument havola orqali berilishi kerakligini ko'rsatish uchun uning e'lon qilinishidan oldin var kalit so'zi qo'shiladi:

GetTwoRandom protsedurasi(var n1, n2:Integer; diapazon: Integer);

n1:=tasodifiy(diapazon);

n2:=tasodifiy(diapazon); oxiri ;

var rand1, rand2: Integer;

Boshlanishi getTwoRandom(rand1,rand2,10); WriteLn(rand1); WriteLn(rand2);

Oxiri.

Ushbu misolda ikkita o'zgaruvchiga havolalar getTwoRandom protsedurasiga haqiqiy parametrlar sifatida uzatiladi: rand1 va rand2. Uchinchi haqiqiy parametr (10) qiymat bo'yicha uzatiladi. Jarayon rasmiy parametrlar yordamida yoziladi

Stringlar yordamida dasturlash usullari

Laboratoriya ishining maqsadi : C# tilidagi usullarni, belgilar ma'lumotlari bilan ishlash qoidalarini va ListBox komponentini o'rganish. Satrlar bilan ishlash dasturini yozing.

Usullari

Metod - bu dastur kodini o'z ichiga olgan sinf elementi. Usul quyidagi tuzilishga ega:

[atributlar] [belgilagichlar] tur nomi ([parametrlar])

Usul tanasi;

Atributlar - bu metodning xususiyatlari haqida kompilyatorga berilgan maxsus ko'rsatmalar. Atributlar kamdan-kam qo'llaniladi.

Kvalifikatsiyalar turli maqsadlarga xizmat qiluvchi kalit so'zlardir, masalan:

· Boshqa sinflar uchun metod mavjudligini aniqlash:

o xususiy- usul faqat shu sinfda mavjud bo'ladi

o himoyalangan- usul bolalar sinflari uchun ham mavjud bo'ladi

o ommaviy- usul ushbu sinfga kirishi mumkin bo'lgan har qanday boshqa sinf uchun mavjud bo'ladi

Sinf yaratmasdan usulning mavjudligini ko'rsatish

· Sozlash turi

Tur usul qaytaradigan natijani aniqlaydi: bu C# da mavjud bo'lgan har qanday tur, shuningdek, natija talab qilinmasa, void kalit so'zi bo'lishi mumkin.

Usul nomi - bu usulni chaqirish uchun foydalaniladigan identifikator. Xuddi shu talablar identifikatorga o'zgaruvchilar nomlariga nisbatan qo'llaniladi: u harflar, raqamlar va pastki chiziqdan iborat bo'lishi mumkin, lekin raqam bilan boshlana olmaydi.

Parametrlar - chaqirilganda usulga uzatilishi mumkin bo'lgan o'zgaruvchilar ro'yxati. Har bir parametr o'zgaruvchining turi va nomidan iborat. Parametrlar vergul bilan ajratiladi.

Usulning tanasi oddiy dastur kodidir, bundan tashqari u boshqa usullar, sinflar, nomlar boʻshliqlari va hokazolarning taʼriflarini oʻz ichiga olmaydi. Agar usul qandaydir natijani qaytarishi kerak boʻlsa, u holda return kalit soʻzi oxirida qaytish qiymati bilan birga boʻlishi kerak. . Agar natijalarni qaytarish kerak bo'lmasa, qaytish kalit so'zidan foydalanish shart emas, garchi bunga ruxsat berilgan bo'lsa ham.

Ifodani baholash usuliga misol:

umumiy ikkilik Calc(doublika a, double b, double c)

return Math.Sin(a) * Math.Cos(b);

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

qaytish k * Math.Exp(c / k);

Usulni ortiqcha yuklash

C# tili bir xil nomli, lekin parametrlari har xil bo'lgan bir nechta usullarni yaratishga imkon beradi. Kompilyator dasturni yaratishda avtomatik ravishda eng mos usulni tanlaydi. Misol uchun, siz raqamni darajaga ko'tarishning ikkita alohida usulini yozishingiz mumkin: bitta algoritm butun sonlar uchun, ikkinchisi esa haqiqiy raqamlar uchun ishlatiladi:

///

/// Butun sonlar uchun X ni Y ning darajasiga hisoblang

///

xususiy int Pow(int X, int Y)

///

/// Haqiqiy sonlar uchun X ni Y ning darajasiga hisoblang

///

xususiy juftlik (er-xotin X, ikki Y)

return Math.Exp(Y * Math.Log(Math.Abs(X)));

Aks holda (Y == 0)

Ushbu kod xuddi shu tarzda chaqiriladi, farq faqat parametrlarda - birinchi holda, kompilyator butun son parametrlari bilan Pow usulini, ikkinchisida esa haqiqiy parametrlar bilan chaqiradi:

Standart sozlamalar

C# tili, 4.0 (Visual Studio 2010) versiyasidan boshlab, ba'zi parametrlar uchun standart qiymatlarni o'rnatishga imkon beradi, shuning uchun usulni chaqirganda siz ba'zi parametrlarni o'tkazib yuborishingiz mumkin. Buning uchun usulni amalga oshirishda kerakli parametrlarga to'g'ridan-to'g'ri parametrlar ro'yxatida qiymat berilishi kerak:

private void GetData(int Number, int Majburiy emas = 5 )

Console.WriteLine("Raqam: (0)", Raqam);

Console.WriteLine("Ixtiyoriy: (0)", ixtiyoriy);

Bunday holda siz usulni quyidagicha chaqirishingiz mumkin:

GetData(10, 20);

Birinchi holda, ixtiyoriy parametr 20 ga teng bo'ladi, chunki u aniq ko'rsatilgan, ikkinchi holatda esa 5 ga teng bo'ladi, chunki u aniq belgilanmagan va kompilyator standart qiymatni oladi.

Standart parametrlar faqat parametrlar ro'yxatining o'ng tomonida o'rnatilishi mumkin, masalan, bunday usul imzosi kompilyator tomonidan qabul qilinmaydi:

xususiy void GetData(int Majburiy emas = 5 , int raqami)

Parametrlar odatdagi usulda (qo'shimcha ref va out kalit so'zlarisiz) usulga uzatilganda, usul ichidagi parametrlarga kiritilgan har qanday o'zgarishlar uning asosiy dasturdagi qiymatiga ta'sir qilmaydi. Aytaylik, bizda quyidagi usul mavjud:

shaxsiy bekor Calc (int raqami)

Ko'rinib turibdiki, usul ichida parametr sifatida berilgan Number o'zgaruvchisi o'zgartirilgan. Keling, usulni chaqirishga harakat qilaylik:

Console.WriteLine(n);

Ekranda 1 raqami paydo bo'ladi, ya'ni Calc usulida o'zgaruvchining o'zgarishiga qaramay, asosiy dasturdagi o'zgaruvchining qiymati o'zgarmagan. Buning sababi, usul chaqirilganda, a nusxa ko'chirish o'zgaruvchidan o'tgan bo'lsa, aynan shu o'zgaruvchi usul o'zgaradi. Usul tugagach, nusxalarning qiymati yo'qoladi. Parametrni uzatishning bu usuli deyiladi qiymat bo'yicha o'tish.

Usulga o'tkazilgan o'zgaruvchini o'zgartirish uchun u ref kalit so'zi bilan uzatilishi kerak - u usul imzosida ham, chaqirilganda ham bo'lishi kerak:

shaxsiy bekor Calc (ref int Number)

Console.WriteLine(n);

Bunday holda, ekranda 10 raqami paydo bo'ladi: usuldagi qiymatning o'zgarishi asosiy dasturga ham ta'sir qildi. Ushbu usul uzatish deb ataladi ma'lumotnoma orqali o'tadi, ya'ni. Bu endi uzatiladigan nusxa emas, balki xotiradagi haqiqiy o'zgaruvchiga havola.

Agar usul o'zgaruvchilardan faqat qiymatlarni qaytarish uchun foydalansa va ularda nima bo'lganiga ahamiyat bermasa, unda siz bunday o'zgaruvchilarni ishga tushira olmaysiz, balki ularni out kalit so'zi bilan uzata olmaysiz. Kompilyator o'zgaruvchining boshlang'ich qiymati muhim emasligini tushunadi va ishga tushirishning yo'qligi haqida shikoyat qilmaydi:

xususiy void Calc (out int Number)

int n; // Biz hech narsa tayinlamaymiz!

string ma'lumotlar turi

C# tili satrlarni saqlash uchun string turidan foydalanadi. Satr o'zgaruvchisini e'lon qilish (va, qoida tariqasida, darhol ishga tushirish) uchun siz quyidagi kodni yozishingiz mumkin:

string a = "Matn";

string b = "satrlar";

Siz satrlarda qo'shimcha operatsiyani bajarishingiz mumkin - bu holda bir qatorning matni boshqasining matniga qo'shiladi:

string c = a + "" + b; // Natija: string matn

String turi aslida String sinfi uchun taxallus bo‘lib, u qatorlar ustida bir qancha murakkab amallarni bajarish imkonini beradi. Masalan, IndexOf usuli satrda pastki satrni izlashi mumkin va Substring usuli belgilangan pozitsiyadan boshlab belgilangan uzunlikdagi satrning bir qismini qaytaradi:

string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int indeks = a.IndexOf("OP"); // Natija: 14 (0 dan boshlab)

string b = a.Substring(3, 5); // Natija: DEFGH

Agar siz satrga maxsus belgilar qo'shishingiz kerak bo'lsa, buni teskari chiziq bilan boshlanadigan qochish ketma-ketligi yordamida qilishingiz mumkin:

ListBox komponenti

Komponent ListBox elementlari klaviatura yoki sichqoncha yordamida tanlangan roʻyxat. Elementlar ro'yxati xususiyat tomonidan belgilanadi Elementlar. Elementlar o'ziga xos xususiyatlarga va o'z usullariga ega bo'lgan elementdir. Usullari Qo'shish, RemoveAt Va Kiritmoq elementlarni qo'shish, o'chirish va kiritish uchun ishlatiladi.

Ob'ekt Elementlar ro'yxatdagi ob'ektlarni saqlaydi. Ob'ekt har qanday sinf bo'lishi mumkin - sinf ma'lumotlari ko'rsatish uchun ToString usuli bo'yicha satr tasviriga aylantiriladi. Bizning holatda, satrlar ob'ekt sifatida ishlaydi. Biroq, Items ob'ekti ob'ekt turiga aylantirilgan ob'ektlarni saqlaganligi sababli, uni ishlatishdan oldin ularni asl turiga qaytarishingiz kerak, bizning holatda qator:

string a = (string)listBox1.Items;

Tanlangan elementning sonini aniqlash uchun xususiyatdan foydalaning Tanlangan indeks.

Men C++ da dasturlashni boshlaganimda va kitoblar va maqolalarni jadal o‘rganganimda, men doimo bir xil maslahatga duch keldim: agar funksiyada o‘zgarmasligi kerak bo‘lgan funksiyaga biron-bir ob’ektni o‘tkazishimiz kerak bo‘lsa, u har doim uzatilishi kerak. doimiyga havola orqali(PPSK), biz ibtidoiy turni yoki ularga o'xshash tuzilmani o'tkazishimiz kerak bo'lgan holatlar bundan mustasno. Chunki C++ da 10 yildan ortiq dasturlash davomida men bu maslahatga tez-tez duch kelaman (va men o'zim buni bir necha marta berganman), u menga uzoq vaqtdan beri "singib ketgan" - men avtomatik ravishda barcha dalillarni doimiyga havola qilaman. . Ammo vaqt o'tadi va bizda C++ 11 o'zining harakatchan semantikasi bilan ixtiyorimizda bo'lganiga 7 yil o'tdi, shu sababli men eski yaxshi dogmaga shubha bildiruvchi ovozlarni tobora ko'proq eshitaman. Ko'pchilik doimiyga murojaat qilish o'tmishdagi narsa va endi bu zarur deb bahslasha boshlaydi qiymat bo'yicha o'tish(PPZ). Ushbu suhbatlar ortida nima bor, shuningdek, bularning barchasidan qanday xulosalar chiqarishimiz mumkin, men ushbu maqolada muhokama qilmoqchiman.

Kitob donolik

Qaysi qoidaga amal qilishimiz kerakligini tushunish uchun men kitoblarga murojaat qilishni taklif qilaman. Kitoblar biz qabul qilishga majbur emasmiz, lekin tinglashga arziydigan ajoyib ma'lumot manbaidir. Va biz tarixdan, kelib chiqishidan boshlaymiz. Men PPSC uchun birinchi apolog kim bo'lganini bilmayman, shunchaki misol sifatida PPSC-dan foydalanish masalasida shaxsan menga eng katta ta'sir ko'rsatgan kitobni keltiraman.

Mayers

Xo'sh, bu erda bizda barcha parametrlar mos yozuvlar bo'yicha o'tkaziladigan sinf mavjud, bu sinf bilan bog'liq muammolar bormi? Afsuski, bor va bu muammo sirtda yotadi. Bizning sinfimizda 2 ta funktsional ob'ekt mavjud: birinchisi ob'ektni yaratish bosqichida qiymat oladi, ikkinchisi esa avval o'rnatilgan qiymatni o'zgartirishga imkon beradi. Bizda ikkita ob'ekt bor, lekin to'rtta funktsiya. Endi tasavvur qiling-a, bizda ikkita o'xshash ob'ekt emas, balki 3, 5, 6 bo'lishi mumkin, unda nima bo'ladi? Keyin biz qattiq kod shishishiga duch kelamiz. Shu sababli, ko'plab funktsiyalarni yaratmaslik uchun parametrlardagi havolalardan butunlay voz kechish taklifi paydo bo'ldi:

Shablon sinf egasi (ommaviy: aniq Holder(T qiymati): m_Value(ko'chirish(qiymat)) ( ) void setValue(T qiymati) (m_Value = ko'chirish(qiymat); ) const T& value() const noexcept ( m_Value; ) xususiy: T m_Value; );

Sizning e'tiboringizni darhol tortadigan birinchi afzallik shundaki, kodning sezilarli darajada kamligi. Bu const va & ning olib tashlanishi tufayli (garchi ular move ni qo'shgan bo'lsa ham) birinchi versiyaga qaraganda kamroq. Ammo bizga har doim ma'lumotnoma bo'yicha o'tish qiymat bo'yicha o'tishdan ko'ra samaraliroq ekanligi o'rgatilgan! C++ 11 dan oldin ham shunday bo'lgan va hozir ham shunday, lekin hozir ushbu kodni ko'rib chiqsak, bu erda birinchi versiyaga qaraganda ko'proq nusxa ko'chirish yo'qligini ko'ramiz, T ning harakat konstruktori bo'lishi sharti bilan. Bular. PPSC-ning o'zi PPZ-dan tezroq bo'lgan va bo'ladi, lekin kod qandaydir tarzda o'tkazilgan ma'lumotnomadan foydalanadi va ko'pincha bu argument nusxalanadi.

Biroq, bu butun hikoya emas. Birinchi variantdan farqli o'laroq, bizda faqat nusxa ko'chirish mavjud, bu erda biz harakatni ham qo'shamiz. Lekin ko'chirish arzon operatsiya, shunday emasmi? Ushbu mavzu bo'yicha, biz ko'rib chiqayotgan Mayers kitobida ham bo'lim ("29-modda") mavjud: "Ko'chirish operatsiyalari mavjud emas, arzon emas va ishlatilmaydi deb hisoblang". Asosiy g'oya sarlavhadan aniq bo'lishi kerak, lekin agar siz tafsilotlarni istasangiz, uni o'qing - men bu haqda to'xtalmayman.

Bu erda birinchi va oxirgi usullarning to'liq qiyosiy tahlilini o'tkazish o'rinli bo'lar edi, lekin men kitobdan chetga chiqishni xohlamayman, shuning uchun biz boshqa bo'limlar uchun tahlilni kechiktiramiz va bu erda biz Skottning dalillarini ko'rib chiqishni davom ettiramiz. Shunday qilib, uchinchi variant ikkinchisidan aniq qisqaroq ekanligidan tashqari, Skott PPZ ning zamonaviy kodda PPSC ga nisbatan afzalligini nimada ko'radi?

U buni shunday ko'radiki, qiymat o'tgan taqdirda, ya'ni. ba'zilari shunday chaqirishadi: Holder holder(string("me")); , PPSC bilan variant bizga nusxa ko'chirish imkonini beradi va PPZ bilan variant bizga harakatni beradi. Boshqa tomondan, agar transfer shunday bo'lsa: Holder holder(someLvalue); , keyin PPZ nusxa ko'chirish va ko'chirishni amalga oshirishi tufayli albatta yo'qotadi, PPSC versiyasida esa faqat bitta nusxa ko'chirish bo'ladi. Bular. Ma'lum bo'lishicha, PPZ, agar biz sof samaradorlikni hisobga olsak, kod miqdori va harakat semantikasini "to'liq" (&& orqali) qo'llab-quvvatlash o'rtasidagi qandaydir kelishuvdir.

Shuning uchun Skott o'z maslahatini juda ehtiyotkorlik bilan aytdi va uni juda ehtiyotkorlik bilan targ'ib qiladi. Hatto menga u xuddi bosim ostida bo‘lgandek, istaksiz ravishda bu masalani ko‘targandek tuyuldi: kitobda shu mavzuda muhokamalarni joylashtirmay qo‘ya olmadi, chunki... Bu juda keng muhokama qilindi va Skott har doim jamoaviy tajriba yig'uvchi edi. Bundan tashqari, u PPZni himoya qilish uchun juda kam dalillar keltiradi, lekin u ushbu "texnika" ni shubha ostiga qo'yadiganlarning ko'pini beradi. Biz uning qarshi argumentlarini keyingi bo'limlarda ko'rib chiqamiz, lekin bu erda biz Skottning PPPni himoya qilish uchun keltirgan argumentini qisqacha takrorlaymiz (aqliy qo'shimchalar). "agar ob'ekt harakatni qo'llab-quvvatlasa va u arzon bo'lsa"): funktsiya argumenti sifatida qiymat ifodasini o'tkazishda nusxa ko'chirishdan qochish imkonini beradi. Ammo Meyersning azobli kitobi etarli, keling, boshqa kitobga o'tamiz.

Aytgancha, agar kimdir kitobni o'qigan bo'lsa va men bu erda Mayers universal havolalar deb atagan - endi yo'naltiruvchi havolalar deb nomlanuvchi variantni qo'shmaganimdan hayron bo'lsa, buni osonlik bilan tushuntirish mumkin. Men faqat PPZ va PPSC ni ko'rib chiqyapman, chunki ... Har ikkala turga (value/lvalue) mos yozuvlar bo'yicha o'tishni qo'llab-quvvatlash uchun shablon bo'lmagan usullar uchun shablon funktsiyalarini joriy etishni yomon shakl deb bilaman. Kod boshqacha bo'lib chiqishi (endi doimiylik yo'q) va boshqa muammolarni keltirib chiqarishi haqida gapirmasa ham bo'ladi.

Josattis va kompaniya

Biz ko'rib chiqadigan oxirgi kitob "C++ shablonlari" bo'lib, u ushbu maqolada tilga olingan barcha kitoblarning eng so'nggisidir. U 2017 yil oxirida nashr etilgan (va 2018 yil kitob ichida ko'rsatilgan). Boshqa kitoblardan farqli o'laroq, bu kitob Stroustrup kabi maslahat (Mayers kabi) yoki umuman C++ emas, balki butunlay naqshlarga bag'ishlangan. Shuning uchun, bu erda shablonlarni yozish nuqtai nazaridan ijobiy / salbiy tomonlari ko'rib chiqiladi.

Butun 7-bob ushbu mavzuga bag'ishlangan bo'lib, unda "Qiymat bo'yichami yoki mos yozuvlar bo'yichami?" Deb nomlangan. Ushbu bobda mualliflar barcha uzatish usullarini barcha ijobiy va salbiy tomonlari bilan qisqacha, ammo qisqacha tasvirlab beradilar. Samaradorlik tahlili bu erda deyarli berilmagan va PPSC PPZ dan tezroq bo'lishi tabiiy deb hisoblanadi. Ammo bularning barchasi bilan, bobning oxirida mualliflar shablon funktsiyalari uchun standart PPP dan foydalanishni tavsiya qiladilar. Nega? Chunki havoladan foydalanganda shablon parametrlari to'liq ko'rsatiladi va havolasiz ular "chirigan" bo'lib, massivlar va satr literallarini qayta ishlashga foydali ta'sir ko'rsatadi. Mualliflarning fikricha, agar PPP ning ba'zi turlari uchun u samarasiz bo'lib chiqsa, siz har doim std::ref va std::cref dan foydalanishingiz mumkin. Bu qandaydir maslahat, rostini aytsam, yuqoridagi funktsiyalardan foydalanishni xohlaydigan ko'p odamlarni ko'rganmisiz?

Ular PPSC haqida nima maslahat berishadi? Ishlash juda muhim bo'lsa yoki boshqa holatlarda ular PPSC dan foydalanishni maslahat berishadi salmoqli PPP dan foydalanmaslik sabablari. Albatta, biz bu erda faqat qozon kodi haqida gapiramiz, ammo bu maslahat dasturchilarga o'n yil davomida o'rgatilgan barcha narsaga to'g'ridan-to'g'ri zid keladi. Bu shunchaki PPPni muqobil sifatida ko'rib chiqish bo'yicha maslahat emas - yo'q, bu PPSCni muqobil qilish bo'yicha maslahat.

Shu bilan bizning kitob sayohatimiz yakunlanadi, chunki... Men bu masala bo'yicha maslahatlashadigan boshqa kitoblarni bilmayman. Keling, boshqa media maydoniga o'tamiz.

Tarmoq donoligi

Chunki Biz Internet asrida yashayapmiz, unda siz faqat kitob donoligiga tayanmasligingiz kerak. Bundan tashqari, ilgari kitob yozgan ko'plab mualliflar endi shunchaki blog yozadilar va kitoblarni tashlab ketishadi. Ushbu mualliflardan biri Xerb Sutter bo'lib, u 2013 yil may oyida o'zining "GotW # 4 Solution: Class Mechanics" blogida maqola e'lon qilgan bo'lsa-da, u biz ko'rib chiqayotgan muammoga to'liq bag'ishlanmagan bo'lsa-da, hali ham unga to'g'ri keladi.

Shunday qilib, maqolaning asl nusxasida Sutter oddiygina eski donolikni takrorladi: "parametrlarni doimiyga mos ravishda o'tkazing", ammo biz endi maqolaning ushbu versiyasini ko'rmaymiz, chunki Maqolada qarama-qarshi maslahat mavjud: " Agar parametr hali ham nusxalanadi, keyin uni qiymat bo'yicha o'tkazing." Yana mashhur "agar". Nega Sutter maqolani o'zgartirdi va men bu haqda qayerdan bildim? Izohlardan. Uning maqolasiga sharhlarni o'qing, aytmoqchi, ular maqolaning o'zidan ko'ra qiziqarli va foydaliroq. To'g'ri, maqolani yozgandan so'ng, Sutter o'z fikrini o'zgartirdi va u endi bunday maslahat bermaydi. Fikrning o'zgarishini uning 2014 yilda CppCondagi nutqida topish mumkin: “Asosiylarga qaytish! Zamonaviy C++ uslubining asoslari”. Qarang, biz keyingi Internet havolasiga o'tamiz.

Va keyin bizda 21-asrning asosiy dasturlash resursi mavjud: StackOverflow. To'g'rirog'i javob, ushbu maqolani yozish paytida ijobiy reaktsiyalar soni 1700 dan oshdi. Savol tug'iladi: Nusxa ko'chirish va almashtirish idiomasi nima? , va sarlavhadan ko'rinib turibdiki, biz ko'rib chiqayotgan mavzuda unchalik emas. Lekin muallif bu savolga javobida bizni qiziqtirgan mavzuga ham to‘xtalib o‘tadi. Shuningdek, u PPZ dan foydalanishni maslahat beradi "agar argument baribir ko'chirilsa" (buning uchun ham qisqartma kiritish vaqti keldi, Xudo haqi). Va umuman olganda, bu maslahat o'zining javobi va u erda muhokama qilingan operator = doirasida juda o'rinli ko'rinadi, lekin muallif bunday maslahatni nafaqat bu alohida holatda emas, balki kengroq ma'noda berish erkinligini oladi. Bundan tashqari, u biz ilgari muhokama qilgan barcha maslahatlardan uzoqroqqa boradi va buni hatto C++ 03 kodida ham qilishga chaqiradi! Muallifni bunday xulosalar chiqarishga nima undadi?

Ko'rinishidan, javob muallifi asosiy ilhomni boshqa kitob muallifi va Boost.MPLning yarim kunlik ishlab chiquvchisi - Deyv Abrahamsning maqolasidan olgan. Maqola “Tezlikni xohlaysizmi? Qiymat bo'yicha o'ting." , va u 2009 yil avgust oyida nashr etilgan, ya'ni. C++ 11 ning qabul qilinishidan va harakat semantikasining kiritilishidan 2 yil oldin. Avvalgi holatlarda bo'lgani kabi, men o'quvchiga maqolani mustaqil ravishda o'qishni maslahat beraman, lekin men Deyv PPZ foydasiga beradigan asosiy dalillarni (aslida bitta dalil bor) keltiraman: siz PPZ dan foydalanishingiz kerak. , chunki "nusxani o'tkazib yuborish" optimallashtirish u bilan yaxshi ishlaydi (nusxa olish elision), bu PPSCda yo'q. Agar siz maqolaga sharhlarni o'qisangiz, u targ'ib qilayotgan maslahatlar universal emasligini ko'rishingiz mumkin, muallifning o'zi sharhlovchilar tanqidiga javob berganda buni tasdiqlaydi. Biroq, maqolada argument baribir nusxa ko'chirilsa, PPPdan foydalanish bo'yicha aniq maslahat (yo'riqnoma) mavjud. Aytgancha, agar kimdir qiziqsa, siz "Tezlikni xohlaysizmi?" maqolasini o'qishingiz mumkin. (Har doim) qiymatdan o'tmang." . Sarlavhadan ko'rinib turibdiki, ushbu maqola Deyvning maqolasiga javobdir, shuning uchun agar siz birinchisini o'qisangiz, buni ham o'qing!

Afsuski (baxtiga ba'zilar uchun) mashhur saytlardagi bunday maqolalar va (bundan ham ko'proq) mashhur javoblar shubhali usullardan ommaviy foydalanishga olib keladi (arzimas misol), chunki bu kamroq yozishni talab qiladi va eski dogma endi buzilmaydi - Agar sizni devorga surib qo'ysangiz, har doim "o'sha mashhur maslahat" ga murojaat qilishingiz mumkin. Endi men sizga turli xil manbalar bizga kod yozish bo'yicha tavsiyalar bilan tanishishingizni taklif qilaman.

Chunki Turli standartlar va tavsiyalar endi Internetda ham e'lon qilinganligi sababli, men ushbu bo'limni "tarmoq donoligi" deb tasniflashga qaror qildim. Shunday qilib, men bu erda ikkita manba haqida gapirmoqchiman, ularning maqsadi C++ dasturchilarining kodini aynan shu kodni yozish bo'yicha maslahatlar (yo'riqnomalar) bilan ta'minlash orqali yaxshilashdir.

Men ko'rib chiqmoqchi bo'lgan birinchi qoidalar to'plami meni ushbu maqolani olishga majbur qilgan so'nggi tomchi edi. Ushbu to'plam clang-tidy yordam dasturining bir qismidir va undan tashqarida mavjud emas. Clang bilan bog'liq bo'lgan barcha narsalar singari, bu yordam dasturi juda mashhur va CLion va Resharper C++ bilan integratsiyani allaqachon olgan (men shunday qilib uchratdim). Shunday qilib, clang-tydy PPSC orqali argumentlarni qabul qiladigan konstruktorlarda ishlaydigan modernizatsiya-value qoidasini o'z ichiga oladi. Ushbu qoida PPSC ni PPZ bilan almashtirishni taklif qiladi. Bundan tashqari, maqolani yozish paytida ushbu qoidaning tavsifida ushbu qoida haqida eslatma mavjud Xayr faqat konstruktorlar uchun ishlaydi, lekin ular (ular kimlar?) ushbu qoidani boshqa ob'ektlarga kengaytirganlarning yordamini mamnuniyat bilan qabul qiladilar. U erda tavsifda Deyvning maqolasiga havola mavjud - oyoqlar qaerdan kelgani aniq.

Nihoyat, boshqa odamlarning donoligi va obro'li fikrlarini ko'rib chiqishni yakunlash uchun men sizga C++ kodini yozish bo'yicha rasmiy ko'rsatmalarni ko'rib chiqishni taklif qilaman: C++ asosiy ko'rsatmalari, ularning asosiy muharrirlari Herb Sutter va Bjarne Stroustrup (yomon emas, to'g'rimi?). Shunday qilib, ushbu tavsiyalar quyidagi qoidani o'z ichiga oladi: "In" parametrlari uchun arzon ko'chirilgan turlarni qiymat bo'yicha va boshqalarni const ga havola qilib o'tkazing", bu eski donolikni butunlay takrorlaydi: hamma joyda PPSK va kichik ob'ektlar uchun PPP. Ushbu maslahat ko'rib chiqilishi kerak bo'lgan bir nechta alternativalarni tavsiflaydi. argumentni uzatish optimallashtirishga muhtoj bo'lsa. Ammo PPZ muqobillar ro'yxatiga kiritilmagan!

Menda e'tiborga loyiq boshqa manbalar yo'qligi sababli, men uzatishning ikkala usulini to'g'ridan-to'g'ri tahlil qilishga o'tishni taklif qilaman.

Tahlil

Oldingi matn men uchun biroz g'ayrioddiy tarzda yozilgan: men boshqalarning fikrlarini bildiraman va hatto o'zimning fikrlarimni bildirmaslikka harakat qilaman (bu yomon ekanligini bilaman). Ko'p jihatdan boshqalarning fikri va mening maqsadim ular haqida qisqacha ma'lumot berish bo'lganligi sababli, men boshqa mualliflarda topilgan ba'zi dalillarni batafsil ko'rib chiqishni keyinga qoldirdim. Ushbu bo'limda men hokimiyatga murojaat qilmayman va fikr bildirmayman; bu erda biz PPSC va PPZ ning ba'zi ob'ektiv afzalliklari va kamchiliklarini ko'rib chiqamiz, ular mening sub'ektiv idrokim bilan ta'minlanadi. Albatta, ilgari muhokama qilinganlarning ba'zilari takrorlanadi, ammo, afsuski, bu maqolaning tuzilishi.

PPP afzalligi bormi?

Shunday qilib, tarafdor va qarshi dalillarni ko'rib chiqishdan oldin, men qiymat bo'yicha o'tish bizga nima va qanday hollarda ustunlik berishini ko'rib chiqishni taklif qilaman. Aytaylik, bizda shunday sinf bor:

Class CopyMover ( ommaviy: void setByValuer(Accounter byValuer) ( m_ByValuer = std::move(byValuer); ) void setByRefer(const Accounter& byRefer) ( m_ByRefer = byRefer; ) void setByValuertAndAndBy yValuerAndNotMover = byVal uerAndNotMover; ) bekor setRvaluer (Accounter&& rvaluer) ( m_Rvaluer = std::move(rvaluer); ) );

Ushbu maqolaning maqsadlari uchun biz faqat birinchi ikkita funktsiyaga qiziqqan bo'lsak-da, men ularni kontrast sifatida ishlatish uchun to'rtta variantni kiritdim.

Accounter klassi oddiy sinf bo'lib, u necha marta nusxalangan/ko'chirilganligini hisoblaydi. Va CopyMover sinfida biz quyidagi variantlarni ko'rib chiqishga imkon beradigan funktsiyalarni amalga oshirdik:

    harakatlanuvchi argumentdan o'tdi.

    Qiymat bo'yicha o'ting, keyin esa nusxa ko'chirish argumentdan o'tdi.

Endi, agar biz ushbu funktsiyalarning har biriga qiymat o'tkazsak, masalan:

Hisobchi byRefer; Hisobchi byValuer; Buxgalter byValuerAndNotMover; CopyMover copyMover; copyMover.setByRefer(byRefer); copyMover.setByValuer(byValuer); copyMover.setByValuerAndNotMover(byValuerAndNotMover);

keyin biz quyidagi natijalarga erishamiz:

Aniq g'olib PPSC, chunki ... faqat bitta nusxani beradi, PPZ esa bitta nusxa va bitta harakatni beradi.

Endi qiymatni o'tkazishga harakat qilaylik:

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

Biz quyidagilarni olamiz:

Bu erda aniq g'olib yo'q, chunki... PPZ ham, PPSK ham bitta operatsiyaga ega, ammo PPZ harakatdan, PPSK esa nusxa ko'chirishdan foydalanganligi sababli, biz g'alabani PPZga berishimiz mumkin.

Ammo bizning tajribalarimiz shu bilan tugamaydi; keling, bilvosita qo'ng'iroqni taqlid qilish uchun quyidagi funktsiyalarni qo'shamiz (keyingi argumentlar bilan):

Void setByValuer(Accounter byValuer, CopyMover& copyMover) ( copyMover.setByValuer(std::move(byValuer)); ) void setByRefer(const Accounter& byRefer, CopyMover& copyMover) ( copyMover.setByRefer(...));

Biz ularni xuddi ularsiz ishlatganimizdek ishlatamiz, shuning uchun kodni takrorlamayman (agar kerak bo'lsa, omborga qarang). Shunday qilib, lvalue uchun natijalar quyidagicha bo'ladi:

E'tibor bering, PPSC bitta nusxada qolgan holda PPZ bilan bo'shliqni oshiradi, PPZ allaqachon 3 ta operatsiyaga ega (yana bitta harakat)!

Endi biz qiymatdan o'tamiz va quyidagi natijalarni olamiz:

Endi PPZ 2 ta harakatga ega va PPSC hali ham bitta nusxaga ega. Endi PPZni g'olib sifatida ko'rsatish mumkinmi? Yo'q, chunki agar bitta harakat kamida bitta nusxadan yomon bo'lmasligi kerak bo'lsa, biz 2 ta harakat haqida bir xil narsani ayta olmaymiz. Shuning uchun, bu misolda g'olib bo'lmaydi.

Ular menga e’tiroz bildirishlari mumkin: “Muallif, siz noxolis fikrdasiz va o‘zingizga foydali bo‘lgan narsani o‘zingizga tortasiz. Hatto 2 ta harakat nusxa ko'chirishdan arzonroq bo'ladi!” Men bu bayonotga qo'shila olmayman umuman, chunki Nusxa olishdan ko'ra tezroq harakatlanish muayyan sinfga bog'liq, ammo biz "arzon" harakatni alohida bo'limda ko'rib chiqamiz.

Bu erda biz qiziqarli narsaga to'xtaldik: biz bitta bilvosita qo'ng'iroqni qo'shdik va PPP "og'irlikda" aynan bitta operatsiyani qo'shdi. O'ylaymanki, bizda bilvosita qo'ng'iroqlar qancha ko'p bo'lsa, PPZ-dan foydalanganda shunchalik ko'p operatsiyalar amalga oshirilishini tushunish uchun MSTU diplomiga ega bo'lishingiz shart emas, PPSC uchun esa raqam o'zgarishsiz qoladi.

Yuqorida muhokama qilingan hamma narsa hech kimga vahiy bo'lishi dargumon; biz hatto tajriba ham o'tkazmagan bo'lishimiz mumkin - bu raqamlarning barchasi birinchi qarashda ko'pchilik C++ dasturchilariga ayon bo'lishi kerak. To'g'ri, bir nuqta hali ham tushuntirishga loyiqdir: nima uchun rvalue holatida PZ nusxasi (yoki boshqa harakat) yo'q, faqat bitta harakat.

Xo'sh, biz PPZ va PPSC o'rtasidagi uzatishdagi farqni birinchi qo'ldan nusxa ko'chirish va harakatlanish sonini kuzatish orqali ko'rib chiqdik. Bunday oddiy misollarda ham PPZ ning PPSC dan ustunligi, yumshoq qilib aytganda, aniq bo'lsa ham. Yo'q Shubhasiz, men hali ham bir oz egilib, quyidagi xulosaga keldim: agar biz hali ham funktsiya argumentini ko'chirmoqchi bo'lsak, unda argumentni funktsiyaga qiymat bo'yicha o'tkazish haqida o'ylash mantiqan. Nega men bu xulosaga keldim? Keyingi bo'limga muammosiz o'tish uchun.

Agar biz nusxa olsak ...

Shunday qilib, biz "agar" maqoliga o'tamiz. Biz duch kelgan argumentlarning aksariyati PPSC o'rniga PPPni universal amalga oshirishni talab qilmagan; ular faqat "agar argument baribir nusxalangan bo'lsa" buni qilishga chaqirishgan. Bu dalil bilan nima noto'g'ri ekanligini aniqlash vaqti keldi.

Men kodni qanday yozishimning qisqacha tavsifi bilan boshlamoqchiman. So'nggi paytlarda mening kodlash jarayonim TDDga o'xshab ketdi, ya'ni. har qanday sinf usulini yozish ushbu usul paydo bo'lgan test yozishdan boshlanadi. Shunga ko'ra, test yozishni boshlaganimda va testni yozgandan so'ng usulni yaratganimda, men argumentni ko'chirib olishimni hali ham bilmayman. Albatta, barcha funktsiyalar shu tarzda yaratilmaydi; ko'pincha, hatto test yozish jarayonida ham, qanday amalga oshirish bo'lishini aniq bilasiz. Ammo bu har doim ham sodir bo'lmaydi!

Kimdir menga e'tiroz bildirishi mumkinki, usul dastlab qanday yozilganligi muhim emas, biz usul shakllanganda va u erda nima sodir bo'layotgani bizga to'liq tushunarli bo'lsa (ya'ni, bizda nusxa ko'chirish yoki nusxa ko'chirish bormi) argumentni qanday o'tkazishimizni o'zgartirishimiz mumkin. emas). Men bunga qisman qo'shilaman - haqiqatan ham siz buni shunday qilishingiz mumkin, ammo bu bizni qandaydir g'alati o'yinga jalb qiladi, bu erda biz interfeyslarni faqat dastur o'zgarganligi sababli o'zgartirishimiz kerak. Bu bizni keyingi dilemmaga olib keladi.

Ma'lum bo'lishicha, biz interfeysni qanday amalga oshirilishiga qarab o'zgartiramiz (yoki hatto rejalashtiramiz). Men o'zimni OOP va dasturiy ta'minot arxitekturasining boshqa nazariy hisob-kitoblari bo'yicha mutaxassis deb hisoblamayman, ammo bunday harakatlar amalga oshirish interfeysga ta'sir qilmasligi kerak bo'lgan asosiy qoidalarga aniq zid keladi. Albatta, amalga oshirishning ba'zi tafsilotlari (ular tilning xususiyatlari bo'ladimi yoki maqsadli platforma bo'ladimi) hali ham interfeys orqali u yoki bu tarzda oqadi, ammo siz bunday narsalar sonini ko'paytirishga emas, kamaytirishga harakat qilishingiz kerak.

Xo'sh, Xudo uni barakallasin, keling, bu yo'ldan boraylik va argumentni nusxalash nuqtai nazaridan amalga oshirishda nima qilayotganimizga qarab interfeyslarni o'zgartiraylik. Aytaylik, biz ushbu usulni yozdik:

Bekor to'plamName(Ism nomi) ( m_Name = ko'chirish(nom); )

va o'zgartirishlarimizni omborga kiritdik. Vaqt o'tishi bilan bizning dasturiy mahsulotimiz yangi funksiyalarga ega bo'ldi, yangi ramkalar birlashtirildi va sinfimizdagi o'zgarishlar haqida tashqi dunyoni xabardor qilish vazifasi paydo bo'ldi. Bular. Biz usulimizga ba'zi bildirishnomalar mexanizmini qo'shamiz, u Qt signallariga o'xshash bo'lsin:

Void setName(Name name) ( m_Name = move(name); emit nameChanged(m_Name); )

Ushbu kod bilan muammo bormi? Yemoq. setName-ga har bir qo'ng'iroq uchun biz signal yuboramiz, shuning uchun signal qachon ham yuboriladi ma'nosi m_Name o'zgarmagan. Ishlash muammolaridan tashqari, bu holat setName ga qo'ng'iroq qilish uchun qandaydir tarzda yuqoridagi bildirishnomani qabul qiladigan kod tufayli cheksiz tsiklga olib kelishi mumkin. Ushbu muammolarni oldini olish uchun bunday usullar ko'pincha shunday ko'rinadi:

Void setName(Name name) ( if(name == m_Name) qaytish; m_Name = move(name); emit nameChanged(m_Name); )

Biz yuqorida tavsiflangan muammolardan xalos bo'ldik, ammo "agar biz baribir nusxa olsak ..." qoidamiz muvaffaqiyatsiz tugadi - endi argumentni so'zsiz nusxalash yo'q, endi biz uni faqat o'zgargan taqdirda nusxalaymiz! Xo'sh, endi nima qilishimiz kerak? Interfeys o'zgartirilsinmi? Yaxshi, keling, ushbu tuzatish tufayli sinf interfeysini o'zgartiraylik. Agar bizning sinfimiz ushbu usulni qandaydir mavhum interfeysdan meros qilib olgan bo'lsa-chi? Keling, u erda ham o'zgartiraylik! Amalga oshirish o'zgargani uchun juda ko'p o'zgarishlar bormi?

Yana menga e'tiroz bildirishlari mumkin, muallif, nega gugurtga pul tejashga harakat qilyapsan, bu shart o'sha yerda ishlaydi? Ha, qo'ng'iroqlarning aksariyati yolg'on bo'ladi! Bunga ishonch bormi? Qayerda? Va agar men gugurtlarni tejashga qaror qilsam, biz PPZ dan foydalanganimiz aynan shunday tejamkorlikning natijasi emasmi? Men samaradorlikni targ'ib qiluvchi "partiya chizig'ini" davom ettiryapman.

Konstruktorlar

Keling, konstruktorlar haqida qisqacha to'xtalib o'tamiz, ayniqsa ular uchun clang-tidy-da maxsus qoida mavjud bo'lib, u boshqa usullar/funksiyalar uchun hali ishlamaydi. Aytaylik, bizda shunday sinf bor:

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

Shubhasiz, parametr ko'chiriladi va clang-tidy bizga konstruktorni bunga qayta yozish yaxshi fikr bo'lishini aytadi:

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

Rostini aytsam, bu erda bahslashish men uchun qiyin - axir, biz har doim nusxa ko'chiramiz. Va ko'pincha, biz konstruktor orqali biror narsani o'tkazganimizda, biz uni nusxalaymiz. Ammo tez-tez bu har doim ham degani emas. Mana yana bir misol:

Class TimeSpan ( ommaviy: TimeSpan(DateTime start, DateTime end) ( if(start > end) invalidTimeSpan(); m_Start = move(start); m_End = move(end); ) private: DateTime m_Start; DateTime m_End; );

Bu erda biz har doim nusxa ko'chirmaymiz, faqat sanalar to'g'ri ko'rsatilganda. Albatta, aksariyat hollarda bu shunday bo'ladi. Lekin har doim emas.

Siz boshqa misol keltira olasiz, lekin bu safar kodsiz. Katta ob'ektni qabul qiladigan sinfingiz borligini tasavvur qiling. Sinf uzoq vaqtdan beri mavjud va endi uni amalga oshirishni yangilash vaqti keldi. Bizga katta ob'ektning yarmidan ko'pi kerak emasligini tushunamiz (bu yillar davomida o'sib bormoqda) va ehtimol kamroq. Qiymatdan o'tish orqali bu haqda biror narsa qila olamizmi? Yo'q, biz hech narsa qila olmaymiz, chunki nusxa hali ham yaratiladi. Ammo agar biz PPSC dan foydalansak, biz nima qilayotganimizni o'zgartirgan bo'lardik ichida dizayner. Va bu asosiy nuqta: PPSC-dan foydalanib, biz o'z funktsiyamizni (konstruktor) amalga oshirishda nima va qachon sodir bo'lishini nazorat qilamiz, lekin agar biz PPZ-dan foydalansak, biz nusxa ko'chirish ustidan hech qanday nazoratni yo'qotamiz.

Ushbu bo'limdan nimani olib tashlashingiz mumkin? "Agar biz baribir nusxa olsak ..." argumenti juda ziddiyatli, chunki Biz nimani nusxalashimizni har doim ham bilmaymiz va hatto bilsak ham, bu kelajakda davom etishiga ishonchimiz komil emas.

Ko'chirish arzon

Harakat semantikasi paydo bo'lgan paytdan boshlab u zamonaviy C++ kodining yozilishiga jiddiy ta'sir ko'rsata boshladi va vaqt o'tishi bilan bu ta'sir kuchaydi: ajablanarli emas, chunki harakat shunday arzon nusxa ko'chirish bilan solishtirganda. Lekin shundaymi? Bu harakat rostmi Har doim arzon jarrohlik? Bu biz ushbu bo'limda tushunishga harakat qilamiz.

Ikkilik katta ob'ekt

Keling, arzimas misoldan boshlaylik, deylik, bizda quyidagi sinf mavjud:

Struct Blob ( std::array ma'lumotlar; );

Oddiy blob(BDO, inglizcha BLOB), bu turli vaziyatlarda ishlatilishi mumkin. Keling, ma'lumotnoma va qiymat bo'yicha o'tish bizga qanday qimmatga tushishini ko'rib chiqaylik. Bizning BDO quyidagi kabi ishlatiladi:

Void Storage::setBlobByRef(const Blob& blob) ( m_Blob = blob; ) void Storage::setBlobByVal(Blob blob) ( m_Blob = move(blob); )

Va biz bu funktsiyalarni shunday chaqiramiz:

Const Blob blob(); saqlash; storage.setBlobByRef(blob); storage.setBlobByVal(blob);

Boshqa misollar uchun kod bu kod bilan bir xil bo'ladi, faqat turli nomlar va turlar bilan, shuning uchun qolgan misollar uchun uni bermayman - hamma narsa omborda.

O'lchovlarga o'tishdan oldin, keling, natijani taxmin qilishga harakat qilaylik. Shunday qilib, bizda Storage sinfi ob'ektida saqlamoqchi bo'lgan 4 KB std :: massiv mavjud. Avvalroq bilib olganimizdek, PPSC uchun bizda bitta nusxa, PPZ uchun esa bitta nusxa va bitta harakat bo'ladi. Massivni ko'chirishning iloji yo'qligiga asoslanib, PPZ uchun 2 nusxa, PPSC uchun bitta nusxa bo'ladi. Bular. PPSC uchun ishlashda ikki baravar ustunlikni kutishimiz mumkin.

Endi test natijalarini ko'rib chiqamiz:

Bu va keyingi barcha testlar MSVS 2017 (15.7.2) va /O2 bayrog'i yordamida bir xil mashinada o'tkazildi.

Amaliyot taxminga to'g'ri keldi - qiymat bo'yicha o'tish 2 baravar qimmatroq, chunki massiv uchun ko'chirish nusxa ko'chirishga to'liq tengdir.

Chiziq

Keling, boshqa misolni ko'rib chiqaylik, oddiy std::string . Biz nimani kutishimiz mumkin? Biz bilamizki (men buni maqolada muhokama qildim) zamonaviy ilovalar ikki turdagi satrni ajratib turadi: qisqa (taxminan 16 belgi) va uzun (qisqaroqdan uzunroq). Qisqa bo'lganlar uchun ichki bufer ishlatiladi, bu char ning oddiy C-massividir, lekin uzunlari allaqachon uyumga joylashtiriladi. Bizni qisqa satrlar qiziqtirmaydi, chunki... natija BDO bilan bir xil bo'ladi, shuning uchun uzoq chiziqlarga e'tibor qarataylik.

Shunday qilib, uzun ipga ega bo'lgan holda, uni ko'chirish juda arzon bo'lishi aniq (shunchaki ko'rsatgichni siljitish), shuning uchun siz ipni siljitish natijalarga umuman ta'sir qilmasligiga ishonishingiz mumkin va PPZ natija berishi kerak. PPSC dan yomonroq emas. Keling, buni amalda tekshirib ko'ramiz va quyidagi natijalarga erishamiz:

Biz ushbu "fenomen" ni tushuntirishga o'tamiz. Xo'sh, biz mavjud satrni allaqachon mavjud satrga ko'chirsak nima bo'ladi? Keling, arzimas bir misolni ko'rib chiqaylik:

Birinchi qator (64, "C"); ikkinchi qator (64, "N"); //... ikkinchi = birinchi;

Bizda 64 ta belgidan iborat ikkita satr bor, shuning uchun ularni yaratishda ichki bufer yetarli emas, natijada ikkala satr ham toʻpga ajratiladi. Endi biz birinchidan ikkinchisiga ko'chiramiz. Chunki bizning qator o'lchamlarimiz bir xil, birinchidan barcha ma'lumotlarni joylashtirish uchun soniyada etarli joy ajratilganligi aniq, shuning uchun ikkinchi = birinchi; banal memcpy bo'ladi, boshqa hech narsa. Ammo biroz o'zgartirilgan misolni ko'rib chiqsak:

Birinchi qator (64, "C"); ikkinchi qator = birinchi;

keyin operator= ga qo'ng'iroq bo'lmaydi, lekin nusxa ko'chirish konstruktori chaqiriladi. Chunki Biz konstruktor bilan ishlayotganimiz sababli, unda mavjud xotira yo'q. Avval tanlangan bo'lishi kerak va faqat keyin birinchi nusxa ko'chiriladi. Bular. Bu xotira ajratish va keyin memcpy. Siz va men bilganimizdek, xotirani global to'pga ajratish odatda qimmat operatsiya hisoblanadi, shuning uchun ikkinchi misoldan nusxa ko'chirish birinchisidan nusxa ko'chirishdan qimmatroq bo'ladi. Har bir yig'ma xotirani ajratish qimmatroq.

Bu bizning mavzuimizga qanday aloqasi bor? Eng to'g'ridan-to'g'ri, chunki birinchi misol PPSC bilan nima sodir bo'lishini, ikkinchisi esa PPZ bilan nima sodir bo'lishini ko'rsatadi: PPZ uchun har doim yangi qator yaratiladi, PPSC uchun esa mavjudidan qayta foydalaniladi. Siz ijro vaqtidagi farqni allaqachon ko'rgansiz, shuning uchun bu erda qo'shadigan hech narsa yo'q.

Bu erda biz yana DXShdan foydalanganda kontekstdan tashqari ishlayotganimiz va shuning uchun u taqdim eta oladigan barcha imtiyozlardan foydalana olmasligimizga duch keldik. Va agar ilgari biz nazariy kelajakdagi o'zgarishlar nuqtai nazaridan mulohaza yuritgan bo'lsak, bu erda biz unumdorlikning juda aniq nosozliklarini kuzatmoqdamiz.

Albatta, kimdir menga ip bir-biridan ajralib turishiga e'tiroz bildirishi mumkin va aksariyat turlar bunday ishlamaydi. Bunga men quyidagilarga javob bera olaman: yuqorida tavsiflangan hamma narsa elementlar to'plami uchun darhol yig'ma xotirani ajratadigan har qanday konteyner uchun to'g'ri bo'ladi. Bundan tashqari, boshqa turlarda kontekstga sezgir bo'lgan yana qanday optimallashtirishlar qo'llanilishini kim biladi?

Ushbu bo'limdan nimani olib tashlashingiz kerak? Ko'chirish haqiqatan ham arzon bo'lsa ham, nusxa ko'chirishni nusxalash + ko'chirish bilan almashtirish har doim ishlash jihatidan taqqoslanadigan natija beradi degani emas.

Kompleks turi

Va nihoyat, bir nechta ob'ektlardan iborat bo'lgan turni ko'rib chiqaylik. Bu shaxsga xos bo'lgan ma'lumotlardan iborat Person sinfi bo'lsin. Odatda bu sizning ismingiz, familiyangiz, pochta indeksingiz va boshqalar. Bularning barchasini satrlar sifatida ko'rsatishingiz mumkin va Person sinfining maydonlariga qo'ygan satrlaringiz qisqa bo'lishi mumkin deb taxmin qilishingiz mumkin. Haqiqiy hayotda qisqa iplarni o'lchash eng foydali bo'lishiga ishongan bo'lsam-da, biz to'liqroq tasvirni berish uchun har xil o'lchamdagi satrlarni ko'rib chiqamiz.

Men 10 ta maydonga ega bo'lgan odamdan ham foydalanaman, lekin buning uchun to'g'ridan-to'g'ri sinf tanasida 10 ta maydon yaratmayman. Person-ning amalga oshirilishi konteynerni chuqurlikda yashiradi - bu test parametrlarini o'zgartirishni qulayroq qiladi, agar Person haqiqiy sinf bo'lsa, u qanday ishlashidan deyarli chetga chiqmasdan. Biroq, dastur mavjud va siz har doim kodni tekshirishingiz va noto'g'ri ish qilganimni aytishingiz mumkin.

Shunday qilib, keling: 10 ta string turiga ega bo'lgan shaxs, biz ularni PPSC va PPZ yordamida saqlashga o'tkazamiz:

Ko'rib turganingizdek, bizda ishlashda katta farq bor, bu avvalgi bo'limlardan keyin o'quvchilarni ajablantirmasligi kerak. Men shuningdek, Person sinfi etarlicha "haqiqiy" ekanligiga ishonaman, shuning uchun bunday natijalar mavhum sifatida rad etilmaydi.

Aytgancha, men ushbu maqolani tayyorlayotganimda, men yana bir misol tayyorladim: bir nechta std::function obyektlaridan foydalanadigan sinf. Mening fikrimga ko'ra, u PPSC ning PPZga nisbatan ishlashida ham ustunlikni ko'rsatishi kerak edi, ammo buning aksi chiqdi! Lekin men bu misolni natijalarni yoqtirmaganim uchun emas, balki bunday natijalar nima uchun olinganligini tushunishga vaqtim yo'qligi uchun keltirmayapman. Shunga qaramay, omborda kod (Printerlar), testlar mavjud - agar kimdir buni aniqlamoqchi bo'lsa, tadqiqot natijalari haqida eshitishdan xursand bo'laman. Men bu misolga keyinroq qaytishni rejalashtirmoqdaman va agar mendan oldin hech kim bu natijalarni e'lon qilmasa, men ularni alohida maqolada ko'rib chiqaman.

Natijalar

Shunday qilib, biz qiymat bo'yicha o'tish va doimiyga murojaat qilishning turli ijobiy va salbiy tomonlarini ko'rib chiqdik. Biz ba'zi misollarni ko'rib chiqdik va ushbu misollarda ikkala usulning ishlashini ko'rib chiqdik. Albatta, ushbu maqola to'liq emas va to'liq emas, lekin mening fikrimcha, u qaysi usuldan foydalanish yaxshiroq ekanligi haqida mustaqil va asosli qaror qabul qilish uchun etarli ma'lumotni o'z ichiga oladi. Kimdir e'tiroz bildirishi mumkin: "nega bitta usuldan foydalanish kerak, keling, vazifadan boshlaylik!" Men ushbu tezisga umuman qo'shilsam ham, bu vaziyatda men bunga qo'shilmayman. Men tilda argumentlarni o'tkazishning faqat bitta usuli bo'lishi mumkinligiga ishonaman, sukut bo'yicha ishlatiladi.

Standart nimani anglatadi? Bu shuni anglatadiki, men funktsiyani yozganimda, men argumentni qanday topshirishim kerakligi haqida o'ylamayman, men faqat "standart" dan foydalanaman. C++ tili juda murakkab til bo'lib, ko'pchilik undan qochishadi. И по моему мнению, сложность вызвана не столько сложностью языковых конструкций, которые есть в языке (типичный программист может с ними никогда не столкнуться), сколько тем, что язык заставляет очень много думать: освободил ли я память, не дорого ли использовать здесь эту функцию va h.k.

Ko'pgina dasturchilar (C, C++ va boshqalar) 2011 yildan keyin paydo bo'la boshlagan C++ tiliga ishonmaydilar va qo'rqishadi. Til murakkablashib borayotgani, endi unda faqat “guruslar” yozishi mumkinligi haqida ko‘p tanqidlarni eshitganman. Shaxsan men bunday emas deb hisoblayman - aksincha, qo'mita tilni yangi boshlanuvchilar uchun qulayroq qilish uchun ko'p vaqt ajratadi va dasturchilar tilning xususiyatlari haqida kamroq o'ylashlari kerak. Axir, agar biz til bilan kurashishimiz shart bo'lmasa, unda vazifa haqida o'ylash uchun vaqtimiz bor. Ushbu soddalashtirishlarga aqlli ko'rsatkichlar, lambda funktsiyalari va tilda paydo bo'lgan yana ko'p narsalar kiradi. Shu bilan birga, men endi ko'proq o'qishimiz kerakligini inkor etmayman, lekin o'qishning nimasi yomon? Yoki boshqa mashhur tillarda o'rganish kerak bo'lgan o'zgarishlar yuz bermayaptimi?

Bundan tashqari, men bunga javoban: “Siz o'ylashni xohlamaysizmi? Keyin PHP da yozing." Men bunday odamlarga javob berishni ham xohlamayman. Men o'yin haqiqatidan bir misol keltiraman: Starcraftning birinchi qismida, binoda yangi ishchi yaratilganida, u minerallarni (yoki gazni) qazib olishni boshlashi uchun uni qo'lda u erga yuborish kerak edi. Bundan tashqari, minerallarning har bir to'plami o'z chegarasiga ega edi, unga erishilganda ishchilarning ko'payishi foydasiz edi va ular hatto bir-biriga aralashib, ishlab chiqarishni yomonlashtirishi mumkin edi. Bu Starcraft 2-da o'zgartirildi: ishchilar avtomatik ravishda minerallarni (yoki gazni) qazib olishni boshlaydilar va bu ayni paytda qancha ishchi qazib olishini va bu konning chegarasi qancha ekanligini ko'rsatadi. Bu o'yinchining baza bilan o'zaro munosabatini sezilarli darajada soddalashtirdi va unga o'yinning muhim jihatlariga e'tibor qaratishga imkon berdi: bazani qurish, qo'shinlarni to'plash va dushmanni yo'q qilish. Bu shunchaki ajoyib yangilik bo'lib tuyulishi mumkin, lekin nima Internetda boshlangan! Odamlar (ular kimlar?) o'yin "buzildi" va "ular Starcraftni o'ldirishdi" deb baqira boshladilar. Shubhasiz, bunday xabarlar faqat qandaydir "elita" klubda bo'lishni yaxshi ko'radigan "maxfiy bilimlar saqlovchilari" va "yuqori APM ustalari" dan kelishi mumkin edi.

Shunday qilib, bizning mavzuimizga qaytadigan bo'lsak, kodni qanday yozish haqida o'ylashim kerak bo'lsa, darhol muammoni hal qilish haqida o'ylashim kerak. Qaysi usulni qo'llashim kerakligi haqida o'ylash - PPSC yoki PPZ - muammoni hal qilish uchun menga bir zarra ham yaqinlashtirmaydi, shuning uchun men bunday narsalar haqida o'ylashdan bosh tortaman va bitta variantni tanlayman: doimiyga murojaat qilish. Nega? Chunki men umumiy holatlarda PPP uchun hech qanday afzalliklarni ko'rmayapman va alohida holatlar alohida ko'rib chiqilishi kerak.

Bu alohida holat, shunchaki, PPSC qandaydir tarzda to'siq bo'lib qolayotganini va PPZ ga uzatishni o'zgartirish orqali biz ishlashning muhim o'sishiga erishamiz, men foydalanishda ikkilanmayman. PPZ. Ammo sukut bo'yicha men PPSC-dan oddiy funktsiyalarda ham, konstruktorlarda ham foydalanaman. Va agar iloji bo'lsa, men ushbu aniq usulni imkon qadar targ'ib qilaman. Nega? Chunki menimcha, PPPni ilgari surish amaliyoti, dasturchilarning asosiy qismi unchalik bilimga ega emasligi (yo printsipial jihatdan, yoki shunchaki, hali ishlarga kirishib ulgurmagan) va ular shunchaki maslahatlarga amal qilishlari sababli yomon deb o'ylayman. Bundan tashqari, agar bir-biriga qarama-qarshi bo'lgan bir nechta maslahatlar bo'lsa, ular soddaroq bo'lganini tanlashadi va bu kodda pessimizmga olib keladi, chunki kimdir biror joyda eshitgan. Ha, bu kishi uning haqligini isbotlash uchun Ibrohimning maqolasiga havola ham berishi mumkin. Va keyin siz o'tirasiz, kodni o'qing va o'ylab ko'ring: parametr bu erda qiymat bo'yicha berilganmi, chunki buni yozgan dasturchi Java'dan kelgan, shunchaki ko'plab "aqlli" maqolalarni o'qiganmi yoki haqiqatan ham kerakmi? texnik tavsif?

PPSC ni o'qish ancha oson: odam C++ ning "yaxshi shaklini" aniq biladi va biz davom etamiz - nigoh uzoqqa cho'zilmaydi. PPSC dan foydalanish amaliyoti C++ dasturchilariga yillar davomida o‘rgatilgan, undan voz kechishning sababi nimada? Bu meni boshqa xulosaga olib keladi: agar metod interfeysi PPP dan foydalansa, nega bunday bo'lganligi haqida izoh ham bo'lishi kerak. Boshqa hollarda, PPSC qo'llanilishi kerak. Albatta, istisno turlari mavjud, lekin men ularni bu erda eslatib o'tmayman, chunki ular nazarda tutilgan: string_view , initializer_list , turli iteratorlar va boshqalar. Ammo bu istisnolar, ularning ro'yxati loyihada qanday turlardan foydalanilganiga qarab kengayishi mumkin. Ammo mohiyat C++ 98 dan beri o'zgarmaydi: sukut bo'yicha biz har doim PPCS dan foydalanamiz.

std::string uchun kichik satrlarda katta ehtimol bilan farq bo'lmaydi, bu haqda keyinroq gaplashamiz.

"Ballarni joylashtirish" haqidagi g'ayrioddiy izoh uchun oldindan uzr so'rayman, lekin biz sizni qandaydir tarzda maqolaga jalb qilishimiz kerak)) O'z navbatida, referat hali ham kutganingizga mos kelishini ta'minlashga harakat qilaman.

Biz nima haqida gaplashayotganimizni qisqacha

Buni hamma biladi, lekin boshida men 1C da usul parametrlarini qanday o'tkazish mumkinligini eslatib o'taman. Ular "malumot bo'yicha" yoki "qiymat bo'yicha" o'tkazilishi mumkin. Birinchi holda, biz usulga qo'ng'iroq nuqtasida bo'lgani kabi bir xil qiymatni, ikkinchisida esa uning nusxasini o'tkazamiz.

Odatiy bo'lib, 1C da argumentlar mos yozuvlar bo'yicha uzatiladi va usul ichidagi parametrga o'zgartirishlar usul tashqarisidan ko'rinadi. Bu erda savolni qo'shimcha tushunish "parametrni o'zgartirish" so'zini aniq tushunganingizga bog'liq. Shunday qilib, bu qayta tayinlashni anglatadi va boshqa hech narsa emas. Bundan tashqari, topshiriq yashirin bo'lishi mumkin, masalan, chiqish parametrida biror narsani qaytaradigan platforma usulini chaqirish.

Ammo agar biz parametrimiz mos yozuvlar orqali o'tkazilishini istamasak, u holda parametrdan oldin kalit so'zni belgilashimiz mumkin. Ma'nosi

Protsedura ByValue(Value Parameter) Parametr = 2; EndProcedure parametri = 1; ByValue (Parametr); Hisobot (Parametr); // 1 chop etadi

Hamma narsa va'da qilinganidek ishlaydi - parametr qiymatini o'zgartirish (aniqrog'i "almashtirish") usuldan tashqaridagi qiymatni o'zgartirmaydi.

Xo'sh, nima hazil?

Parametrlar sifatida ibtidoiy turlarni (satrlar, raqamlar, sanalar va boshqalar) emas, balki ob'ektlar sifatida o'tkazishni boshlaganimizda, qiziqarli daqiqalar boshlanadi. Bu erda ob'ektning "sayoz" va "chuqur" nusxalari kabi tushunchalar, shuningdek, ko'rsatkichlar (C++ terminlarida emas, balki mavhum tutqichlar sifatida) o'ynaydi.

Ob'ektni (masalan, qiymatlar jadvalini) mos yozuvlar bo'yicha o'tkazayotganda, biz ko'rsatgich qiymatining o'zini (ma'lum bir tutqich) o'tkazamiz, bu ob'ektni platforma xotirasida "ushlab turadi". Qiymat bo'yicha o'tkazilganda, platforma ushbu ko'rsatgichning nusxasini yaratadi.

Boshqacha qilib aytadigan bo'lsak, agar ob'ektni mos yozuvlar bo'yicha o'tkazib, biz parametrga "Array" qiymatini beradigan bo'lsak, u holda biz qo'ng'iroq nuqtasida massivni olamiz. Malumot orqali o'tgan qiymatning qayta tayinlanishi qo'ng'iroq joyidan ko'rinadi.

Protsedura ProcessValue(Parameter) Parametr = Yangi massiv; EndProcedure Table = New ValueTable; ProcessValue (jadval); Hisobot (ValueType(Jadval)); // massivni chiqaradi

Agar ob'ektni qiymat bo'yicha o'tkazsak, u holda qo'ng'iroq nuqtasida bizning qiymat jadvalimiz yo'qolmaydi.

Ob'ekt tarkibi va holati

Qiymat bo'yicha o'tishda butun ob'ekt emas, balki faqat uning ko'rsatkichi nusxalanadi. Ob'ekt namunasi bir xil bo'lib qoladi. Ob'ektni mos yozuvlar yoki qiymat bo'yicha qanday o'tkazishingiz muhim emas - qiymatlar jadvalini tozalash jadvalning o'zini tozalaydi. Bu tozalash hamma joyda ko'rinadi, chunki ... faqat bitta ob'ekt bor edi va u usulga qanday aniq o'tganligi muhim emas edi.

Protsedura ProcessValue(Parameter) Parameter.Clear(); EndProcedure Table = New ValueTable; Table.Add(); ProcessValue (jadval); Hisobot(Table.Quantity()); // 0 chiqadi

Ob'ektlarni usullarga o'tkazishda platforma ko'rsatkichlar bilan ishlaydi (shartli, C++ dan to'g'ridan-to'g'ri analoglar emas). Agar ob'ekt havola orqali uzatilsa, u holda ob'ekt joylashgan 1C virtual mashinasining xotira katakchasi boshqa ob'ekt tomonidan yozilishi mumkin. Agar ob'ekt qiymat bo'yicha uzatilsa, u holda ko'rsatkich ko'chiriladi va ob'ektning ustiga yozish xotira joyini asl ob'ekt bilan qayta yozishga olib kelmaydi.

Shu bilan birga, har qanday o'zgarish davlat ob'ekt (tozalash, xususiyatlarni qo'shish va hokazo) ob'ektning o'zini o'zgartiradi va ob'ekt qanday va qayerga o'tkazilganiga umuman aloqasi yo'q. Ob'ekt namunasining holati o'zgargan; unga bir nechta "mos yozuvlar" va "qiymatlar" bo'lishi mumkin, ammo namuna har doim bir xil. Ob'ektni metodga o'tkazish orqali biz butun ob'ektning nusxasini yaratmaymiz.

Va bu har doim to'g'ri, bundan tashqari ...

Mijoz va server o'zaro ta'siri

Platforma server qo'ng'iroqlarini juda shaffof tarzda amalga oshiradi. Biz shunchaki usulni chaqiramiz va kaput ostida platforma usulning barcha parametrlarini ketma-ketlashtiradi (satrga aylanadi), ularni serverga uzatadi va keyin chiqish parametrlarini mijozga qaytaradi, u erda ular seriyadan chiqariladi va shunday yashaydi. agar ular hech qachon biron bir serverga kirmagan bo'lsa.

Ma'lumki, barcha platforma ob'ektlari ketma-ketlashtirilmaydi. Bu erda cheklov kuchayadi: barcha ob'ektlarni mijozdan server usuliga o'tkazib bo'lmaydi. Agar siz seriyali bo'lmagan ob'ektdan o'tsangiz, platforma yomon so'zlardan foydalanishni boshlaydi.

  • Dasturchining niyatlari haqida aniq bayonot. Usul imzosiga qarab, qaysi parametrlar kiritilganligini va qaysi biri chiqishini aniq ayta olasiz. Ushbu kodni o'qish va saqlash osonroq
  • Serverdagi "mos yozuvlar bo'yicha" parametridagi o'zgarish mijozning qo'ng'iroq nuqtasida ko'rinishi uchun, p. Platformaning o'zi maqolaning boshida tasvirlangan xatti-harakatni ta'minlash uchun mijozga havola orqali serverga uzatilgan parametrlarni qaytarib beradi. Parametrni qaytarish kerak bo'lmasa, tirbandlik paydo bo'ladi. Ma'lumotlar almashinuvini optimallashtirish uchun chiqishda qiymatlari bizga kerak bo'lmagan parametrlar Value so'zi bilan belgilanishi kerak.

Bu erda ikkinchi nuqta e'tiborga loyiqdir. Trafikni optimallashtirish uchun, agar parametr Value so'zi bilan belgilangan bo'lsa, platforma parametr qiymatini mijozga qaytarmaydi. Bularning barchasi ajoyib, ammo bu qiziqarli ta'sirga olib keladi.

Yuqorida aytib o'tganimdek, ob'ekt serverga o'tkazilganda, serializatsiya sodir bo'ladi, ya'ni. ob'ektning "chuqur" nusxasi amalga oshiriladi. Va agar so'z bo'lsa Ma'nosi ob'ekt serverdan mijozga qaytib ketmaydi. Biz ushbu ikkita faktni qo'shamiz va quyidagilarni olamiz:

&OnServerProcedureByLink(Parameter) Parameter.Clear(); EndProcedure &OnServerProcedureByValue(Qiymat parametri) Parameter.Clear(); EndProcedure &OnClient Procedure ByValueClient(Value Parameter) Parameter.Clear(); EndProcedure &OnClient Procedure CheckValue() List1= Yangi ListValues; List1.Add("salom"); List2 = List1.Copy(); List3 = List1.Copy(); // ob'ekt to'liq nusxalanadi, // serverga o'tkaziladi, keyin qaytariladi. // ro'yxatni tozalash chaqiruv nuqtasida ko'rinadi ByRef(List1); // ob'ekt to'liq nusxalanadi, // serverga o'tkaziladi. Qaytib kelmaydi. // Ro'yxatni tozalash ByValue(List2) ga qo'ng'iroq qilish nuqtasida KO'RINMAYDI; // faqat ob'ekt ko'rsatkichi nusxalanadi // ro'yxatni tozalash ByValueClient(List3) ga qo'ng'iroq qilish nuqtasida ko'rinadi; Hisobot (Ro‘yxat1.Miqdor()); Hisobot (Ro'yxat2.Miqdor()); Hisobot (Ro'yxat3.Miqdor()); Jarayonning oxiri

Xulosa

Muxtasar qilib aytganda, uni quyidagicha umumlashtirish mumkin:

  • Malumot orqali o'tish ob'ektni butunlay boshqa ob'ekt bilan "ustiga yozish" imkonini beradi
  • Qiymat bo'yicha o'tish ob'ektni "ustiga yozish" imkonini bermaydi, lekin ob'ektning ichki holatidagi o'zgarishlar ko'rinadi, chunki biz bir xil ob'ekt misoli bilan ishlayapmiz
  • Server qo'ng'irog'ini amalga oshirishda ob'ektning TURLI misollari bilan ish bajariladi, chunki Chuqur nusxasi amalga oshirildi. Kalit so'z Ma'nosi server nusxasini mijoz nusxasiga ko'chirishni oldini oladi va serverdagi ob'ektning ichki holatini o'zgartirish mijozda shunga o'xshash o'zgarishlarga olib kelmaydi.

Umid qilamanki, ushbu oddiy qoidalar ro'yxati sizga "qiymat bo'yicha" va "ma'lumotnoma bo'yicha" parametrlarni o'tkazish bo'yicha hamkasblar bilan nizolarni hal qilishni osonlashtiradi.