4-bo‘lim

Havolalar va const

Havola - obyektning ikkinchi nomi; nusxa qachon qimmat va const nima uchun deyarli hamma joyda kerak.

🕑 15 daqiqa o‘qish 📄 1 139 so‘z 👁 5 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Havola nima
  2. Nusxa qachon qimmat
  3. const havola vaqtinchalik qiymatni ham qabul qiladi
  4. Havola va ko'rsatkich
  5. Osilgan havola
  6. Konteyner elementiga havola
  7. const ning joyi muhim
  8. const to'g'riligi
  9. Xulosa

Havola (reference) - mavjud obyektning ikkinchi nomi.

Bu C da yo'q tushuncha va u C++ kodining ko'rinishini tubdan o'zgartiradi.

Havola nima #

C++
#include <print>
#include <string>
#include <vector>
#include <algorithm>
C++
int main()
{
    int son = 10;
    int& havola = son;      // havola - son ning ikkinchi nomi

    std::println("son = {}, havola = {}", son, havola);

    havola = 20;            // havola orqali o'zgartiramiz
    std::println("havola = 20 dan keyin: son = {}", son);

    son = 30;               // asl nom orqali
    std::println("son = 30 dan keyin: havola = {}", havola);

    // Ikkalasining manzili BIR XIL
    std::println("manzillar teng: {}", &son == &havola);

    return 0;
}
Natija
son = 10, havola = 10
havola = 20 dan keyin: son = 20
son = 30 dan keyin: havola = 30
manzillar teng: true
Havola - alohida obyekt emas

havola yangi xotira egallamaydi. U shunchaki son ning boshqa nomi.

Havolaning uchta qat'iy qoidasi bor:

QoidaMa'nosi
Initsializatsiya majburiyint& h; - kompilyatsiya xatosi
Boshqa obyektga o'tkazib bo'lmaydiBir marta bog'lanadi va shunday qoladi
nullptr bo'la olmaydiHar doim mavjud obyektga ishora qiladi

Uchinchi qoida muhim: havola olgan funksiya obyekt mavjudligiga ishonch hosil qilishi mumkin. Ko'rsatkichda esa har doim nullptr tekshiruvi kerak.

Nusxa qachon qimmat #

C++
struct KattaObyekt
{
    std::vector<int> malumot;
    std::string nom;

    // Nusxa olinganda xabar beradi
    KattaObyekt(std::string n, std::size_t hajm)
        : malumot(hajm, 1), nom(std::move(n)) {}

    KattaObyekt(const KattaObyekt& boshqa)
        : malumot(boshqa.malumot), nom(boshqa.nom)
    {
        std::println("  [nusxa olindi: {} element]", malumot.size());
    }
};

// Qiymat bo'yicha - NUSXA olinadi
void qiymatBoyicha(KattaObyekt obyekt)
{
    std::println("  qiymat bo'yicha: {}", obyekt.nom);
}

// Havola bo'yicha - nusxa YO'Q
void havolaBoyicha(const KattaObyekt& obyekt)
{
    std::println("  havola bo'yicha: {}", obyekt.nom);
}

int main()
{
    KattaObyekt katta{"hisobot", 100000};

    std::println("1. qiymat bo'yicha chaqiruv:");
    qiymatBoyicha(katta);

    std::println("2. havola bo'yicha chaqiruv:");
    havolaBoyicha(katta);

    return 0;
}
Natija
1. qiymat bo'yicha chaqiruv:
  [nusxa olindi: 100000 element]
  qiymat bo'yicha: hisobot
2. havola bo'yicha chaqiruv:
  havola bo'yicha: hisobot
Birinchi chaqiruvda 100 000 element ko'chirildi

Farq shunchaki tezlikda emas - xotirada ham. Qiymat bo'yicha uzatishda vaqtinchalik nusxa uchun yangi xotira ajratiladi.

Amaliy qoida:

Parametr turiQanday uzatish
int, double, char, ko'rsatkichQiymat bo'yicha
std::string, std::vector, sinflarconst&
O'zgartirish kerak bo'lsa& (havola)
Funksiya nusxani saqlab qolsaQiymat bo'yicha (12-bo'lim)

Birinchi qator ham muhim: kichik turlar uchun const& foydasiz va hatto sekinroq bo'lishi mumkin - chunki havola ham manzil orqali ishlaydi.

Amaliy chegara: obyekt 16 baytdan katta bo'lsa const& ishlating.

const havola vaqtinchalik qiymatni ham qabul qiladi #

C++
void chop(const std::string& matn)
{
    std::println("  chop: '{}'", matn);
}

std::string yasa()
{
    return "funksiyadan qaytgan";
}

int main()
{
    std::string oddiy{"oddiy o'zgaruvchi"};

    chop(oddiy);              // mavjud obyekt
    chop("to'g'ridan-to'g'ri satr");   // vaqtinchalik std::string yasaladi
    chop(yasa());             // funksiya natijasi
    chop(std::string(5, 'x'));        // vaqtinchalik obyekt

    // const BO'LMAGAN havola vaqtinchalik qiymatni QABUL QILMAYDI
    // void ozgartir(std::string& m);
    // ozgartir("satr");      // KOMPILYATSIYA XATOSI

    return 0;
}
Natija
  chop: 'oddiy o'zgaruvchi'
  chop: 'to'g'ridan-to'g'ri satr'
  chop: 'funksiyadan qaytgan'
  chop: 'xxxxx'
Nima uchun const& shunchalik keng ishlatiladi

const std::string& parametri hamma narsani qabul qiladi:

Argumentconst&& (const emas)
O'zgaruvchiHaHa
KonstantaHaYo'q
Vaqtinchalik obyektHaYo'q
Funksiya natijasiHaYo'q

Sabab mantiqiy: vaqtinchalik obyektni o'zgartirishning ma'nosi yo'q - u keyingi qatorda yo'q bo'ladi.

Shuning uchun kiritma parametrlar uchun const& standart tanlov bo'ldi.

Havola va ko'rsatkich #

C++
int main()
{
    int a = 10;
    int b = 20;

    int& havola = a;
    int* korsatkich = &a;

    std::println("boshlang'ich:  a={} b={}", a, b);

    // Ko'rsatkichni boshqa obyektga o'tkazish mumkin
    korsatkich = &b;
    *korsatkich = 99;
    std::println("korsatkich b ga: a={} b={}", a, b);

    // Havolani o'tkazib BO'LMAYDI - bu a ga yozish demakdir
    havola = b;
    std::println("havola = b:      a={} b={}", a, b);

    // Ko'rsatkich nullptr bo'la oladi
    int* bosh = nullptr;
    std::println("bosh korsatkich: {}", bosh == nullptr);

    return 0;
}
Natija
boshlang'ich:  a=10 b=20
korsatkich b ga: a=10 b=99
havola = b:      a=99 b=99
bosh korsatkich: true
havola = b; obyektni o'zgartirdi, havolani emas

Uchinchi natijaga qarang: a ning qiymati 99 bo'ldi.

havola = b yozuvi "havolani b ga bog'la" degani emas. U "havola ko'rsatayotgan obyektga b ning qiymatini yoz" degani.

Havola bir marta bog'lanadi va umrining oxirigacha o'sha obyektga ishora qiladi.

HavolaKo'rsatkich
InitsializatsiyaMajburiyIxtiyoriy
Qayta bog'lashMumkin emasMumkin
nullptrMumkin emasMumkin
ArifmetikaYo'qBor (p + 1)
Sintaksish.metod()p->metod()
Qachon ishlatiladiDeyarli har doimIxtiyoriylik kerak bo'lsa

Zamonaviy C++ da xom ko'rsatkichlar asosan ixtiyoriy bo'lishi mumkin bo'lgan joylarda qoladi. Egalik uchun esa unique_ptr ishlatiladi (10-bo'lim).

Qiymat bo'yicha va havola bo'yicha uzatish Qiymat bo'yicha katta 100 000 element ~400 KB nusxa obyekt 100 000 element YANGI ~400 KB Xotira ikki barobar, vaqt sarflandi const havola bo'yicha katta 100 000 element ~400 KB ishora obyekt o'sha obyekt Nusxa yo'q, faqat manzil uzatildi Parametrni qanday uzatish - qaror daraxti Funksiya obyektni o'zgartiradimi? ha → T& Yo'q bo'lsa: obyekt kichikmi (≤16 bayt)? ha → T · yo'q → const T& Funksiya nusxani saqlab qolsa - qiymat bo'yicha + std::move (12-bo'lim) Havolaning asosiy xavfi Havola o'zi ko'rsatayotgan obyektni ushlab turmaydi. Obyekt yo'q bo'lsa, havola osilib qoladi - va uni ishlatish aniqlanmagan xulq.
const havola - kiritma parametrlar uchun standart tanlov

Osilgan havola #

C++
// XATO: mahalliy o'zgaruvchiga havola qaytariladi
const std::string& xatoFunksiya()
{
    std::string mahalliy{"men funksiya tugagach yo'q bo'laman"};
    return mahalliy;          // mahalliy shu yerda yo'q qilinadi
}

// TO'G'RI: qiymat bo'yicha qaytarish
std::string togriFunksiya()
{
    std::string mahalliy{"men xavfsiz ko'chiriladi"};
    return mahalliy;          // ko'chiriladi, nusxa emas
}

int main()
{
    std::println("to'g'ri: {}", togriFunksiya());

    std::println("");
    std::println("xatoFunksiya() ni chaqirish - aniqlanmagan xulq.");
    std::println("Kompilyator ogohlantiradi, lekin taqiqlamaydi.");
    std::println("Bunday kodni ASan bilan ishga tushiring (19-bo'lim).");

    return 0;
}
Natija
to'g'ri: men xavfsiz ko'chiriladi

xatoFunksiya() ni chaqirish - aniqlanmagan xulq.
Kompilyator ogohlantiradi, lekin taqiqlamaydi.
Bunday kodni ASan bilan ishga tushiring (19-bo'lim).
Mahalliy obyektga havola qaytarmang

xatoFunksiya ichidagi mahalliy funksiya tugashi bilan yo'q qilinadi. Qaytgan havola esa endi mavjud bo'lmagan xotiraga ishora qiladi.

Bu osilgan havola (dangling reference) va uni ishlatish aniqlanmagan xulq-atvor.

Kompilyator buni sezadi va ogohlantiradi:

KOD
warning: reference to local variable 'mahalliy' returned

Lekin u faqat oddiy holatlarni sezadi. Havola bir necha funksiya orqali o'tsa - hech kim ogohlantirmaydi.

Qachon havola qaytarish to'g'ri:

HolatMisol
Sinf a'zosiga havolav[0], obj.nom()
Parametrga havolamax(a, b) - ikkalasi ham tashqaridan
Statik obyektga havolaKamdan-kam
Mahalliy obyektgaHech qachon

Konteyner elementiga havola #

C++
int main()
{
    std::vector<int> sonlar{10, 20, 30};

    int& birinchi = sonlar[0];
    std::println("birinchi = {}", birinchi);

    birinchi = 99;
    std::println("o'zgartirgandan keyin: {}", sonlar);

    // XAVF: vektor o'sganda xotira KO'CHIRILISHI mumkin
    std::println("");
    std::println("sig'im: {}", sonlar.capacity());

    for (int i = 0; i < 10; ++i) {
        sonlar.push_back(i);
    }

    std::println("qo'shgandan keyin sig'im: {}", sonlar.capacity());
    std::println("birinchi havola endi ISHONCHSIZ");
    std::println("(xotira boshqa joyga ko'chgan bo'lishi mumkin)");

    return 0;
}
Natija
birinchi = 10
o'zgartirgandan keyin: [99, 20, 30]

sig'im: 3
qo'shgandan keyin sig'im: 24
birinchi havola endi ISHONCHSIZ
(xotira boshqa joyga ko'chgan bo'lishi mumkin)
Iterator va havolalarning yaroqsizlanishi

push_back sig'imdan oshib ketsa, vector yangi xotira ajratadi va hamma elementni ko'chiradi.

Shundan keyin eski elementlarga olingan barcha havolalar, ko'rsatkichlar va iteratorlar yaroqsiz bo'ladi.

AmalNima yaroqsizlanadi
push_back (sig'im yetsa)Hech nima
push_back (sig'im yetmasa)Hammasi
insert, eraseO'sha joydan keyingilari
clear, resizeHammasi (yoki qismi)
reserveHammasi

Amaliy qoida:

Konteynerni o'zgartirayotganda unga olingan havolalarni saqlamang.

Agar saqlash kerak bo'lsa - indeks saqlang. Indeks ko'chirishdan omon qoladi.

Bu xato ASan bilan darhol topiladi (19-bo'lim).

const ning joyi muhim #

C++
int main()
{
    int a = 1;
    int b = 2;

    // const ko'rsatkichning nimasi o'zgarmas?
    const int* p1 = &a;        // ko'rsatilgan QIYMAT o'zgarmas
    int* const p2 = &a;        // KO'RSATKICH o'zgarmas
    const int* const p3 = &a;  // ikkalasi ham

    // *p1 = 10;               // XATO - qiymatni o'zgartirib bo'lmaydi
    p1 = &b;                   // lekin ko'rsatkichni o'zgartirish mumkin

    *p2 = 10;                  // qiymatni o'zgartirish mumkin
    // p2 = &b;                // XATO - ko'rsatkichni o'zgartirib bo'lmaydi

    std::println("a = {}", a);
    std::println("p1 endi b ga ishora qiladi: {}", *p1);
    std::println("p3 orqali: {}", *p3);

    return 0;
}
Natija
a = 10
p1 endi b ga ishora qiladi: 2
p3 orqali: 10
const ni o'ngdan chapga o'qing
YozuvO'qilishi
const int* pp - o'zgarmas int ga ko'rsatkich
int* const pp - int ga o'zgarmas ko'rsatkich
const int* const pO'zgarmas int ga o'zgarmas ko'rsatkich

Yodlash usuli: const o'zidan chapdagi narsani o'zgarmas qiladi. Chapda hech narsa bo'lmasa - o'ngdagini.

Amalda birinchi shakl eng ko'p uchraydi: const char*, const int* va h.k.

Havolalarda esa muammo yo'q - havola o'zi doim "o'zgarmas bog'lanish", shuning uchun int& const degan yozuv umuman mavjud emas.

const to'g'riligi #

C++
struct Hisob
{
    std::string egasi;
    int balans;

    // const metod - obyektni O'ZGARTIRMAYDI
    int balansOl() const
    {
        return balans;
    }

    // const emas - o'zgartiradi
    void toldir(int summa)
    {
        balans += summa;
    }
};

void korsat(const Hisob& h)
{
    // const obyektda faqat const metodlarni chaqirish mumkin
    std::println("  {} - {} so'm", h.egasi, h.balansOl());

    // h.toldir(100);      // KOMPILYATSIYA XATOSI
}

int main()
{
    Hisob hisob{"Husanboy", 500000};

    korsat(hisob);

    hisob.toldir(250000);
    korsat(hisob);

    const Hisob ozgarmas{"Malika", 1000000};
    korsat(ozgarmas);
    // ozgarmas.toldir(1);   // KOMPILYATSIYA XATOSI

    return 0;
}
Natija
  Husanboy - 500000 so'm
  Husanboy - 750000 so'm
  Malika - 1000000 so'm
const metod nima va'da qiladi

int balansOl() const yozuvidagi oxirgi const - "bu metod obyektni o'zgartirmaydi" degan va'da.

Foydasi:

FoydaIzoh
const obyektlarda chaqirish mumkinAks holda ishlatib bo'lmaydi
const& parametrlarda ishlaydiEng keng tarqalgan holat
Niyat kodda ko'rinadiO'qish oson
Kompilyator tekshiradiTasodifiy o'zgartirish rad etiladi

Amaliy qoida: metod obyektni o'zgartirmasa - doim const deb belgilang.

Buni keyinroq qo'shish qiyin: const metod faqat boshqa const metodlarni chaqira oladi, shuning uchun bitta yetishmayotgan const butun zanjirni buzadi.

Bu const to'g'riligi (const correctness) deb ataladi va u boshidan qilinishi kerak.

Amaliy topshiriq
  1. O'zgaruvchi va unga havola yarating, manzillarini solishtiring.
  2. Havola orqali qiymatni o'zgartiring va asl nom orqali tekshiring.
  3. Katta obyektni qiymat va havola bo'yicha uzatib, nusxa sonini sanang.
  4. const& parametrga vaqtinchalik obyekt bering.
  5. Const bo'lmagan havolaga vaqtinchalik obyekt berishga urinib ko'ring.
  6. havola = b; nima qilishini natija bilan tushuntiring.
  7. Mahalliy obyektga havola qaytaruvchi funksiya yozing - kompilyator nima dedi?
  8. Vektorga element qo'shib, sig'im o'zgarishini kuzating.
  9. const int*, int* const va const int* const ni sinab ko'ring.
  10. const metodli sinf yozing va const obyektda chaqiring.

Xulosa #

  • Havola - mavjud obyektning ikkinchi nomi, yangi xotira egallamaydi.
  • U majburiy initsializatsiya qilinadi va qayta bog'lanmaydi.
  • Havola nullptr bo'la olmaydi - obyekt mavjudligi kafolatlangan.
  • Katta obyektni qiymat bo'yicha uzatish butun nusxani ko'chiradi.
  • Kiritma parametrlar uchun standart tanlov - const T&.
  • const& vaqtinchalik obyektlarni ham qabul qiladi, & esa yo'q.
  • havola = b; havolani emas, obyektni o'zgartiradi.
  • Mahalliy obyektga havola qaytarish - osilgan havola va aniqlanmagan xulq.
  • Konteynerni o'zgartirish unga olingan havolalarni yaroqsiz qilishi mumkin.
  • Obyektni o'zgartirmaydigan metodni const deb belgilang.

Keyingi bo'limda funksiyalar va lambdalar bilan tanishamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.