81 / 90

C++11 Features

C++11 yangiliklari va imkoniyatlari

Rasmiy ravishda ISO/IEC 14882:2011 nomi bilan tanilgan C++ 11 2011 yilda chiqarilgan va C++ dasturlash tili evolyutsiyasida muhim bosqich boʻlgan.

U C++ ni yanada kuchliroq qiladigan ko'plab yangi xususiyatlar va yaxshilanishlarni taqdim etdi.

C++ 11 da kiritilgan xususiyatlar

Mana C++ 11 da kiritilgan eng muhim xususiyatlar:

  • auto kalit so'zi
  • Diapazonga asoslangan for sikli
  • Lambda ifodalari
  • Aqlli ko'rsatkichlar
  • constexpr kalit so'zi
  • nullptr kalit so'zi
  • Tip xususiyatlari
  • Ipni qo'llab-quvvatlash
  • Konstruktorni topshirish
  • O'chirilgan va standart funksiyalar

auto kalit so'zini o'rganishdan boshlaylik.

C++ avtomatik kalit so'z

auto kalit so'zi C++ 11 ga o'zgaruvchining turini o'z initsializatoridan avtomatik ravishda chiqarish imkonini beradi.

example.cpp
#include <iostream>
using namespace std;

int main() {

    // x int sifatida chiqariladi
    auto x = 42;
    
    // pi ikki barobar qilib chiqariladi
    auto pi = 3.1415926535;  

    cout << "x: " << x << endl;
    cout << "pi: " << pi << endl;

    return 0;
}

Chiqish

kod
x: 42
pi: 3.14159

Bu erda kompilyator x va pi o'zgaruvchilarining turini ularning initsializatoridan chiqarib tashlashga qodir, shuning uchun ularning turini aniq aytib o'tishimiz shart emas. Biroq, auto dan foydalanilganda, o'zgaruvchi har doim ishga tushirilishi kerak.

example.cpp
auto x; // xato: har doim ishga tushirilishi kerak

[!NOTE] auto - bu C++ tilidagi turning o'zi emas, balki turni to'ldiruvchisidir. Shunday qilib, uni sizeof yoki typeid kabi translatsiyalarda yoki operatorlarda ishlatib bo'lmaydi.

Looplar uchun diapazon

Range based for loops in C++ executes a loop for a range.

Sintaksis

example.cpp
for (range_initialization : range_container) {
    // sikl operatorlari
}

Bu yerga,

  • range_initialization - takrorlanuvchi o'zgaruvchini yaratadi.
  • range_container - takrorlash uchun konteyner.

Masalan,

example.cpp
vector<int> numbers = {1, 2, 3};

// raqamlar vektori bo'ylab aylanish
for (int num : numbers) {

    // vektor elementini chop etish
    cout << num << endl;
}

[!NOTE] range-based-loops should be preferred over normal for loops whenever possible to avoid out of bound errors.

1-misol: Loop uchun asoslangan C++ diapazoni

example.cpp
#include <iostream>
#include <vector>
using namespace std;

int main() {

    // butun sonlar vektorini yarating
    vector<int> numbers = {1, 2, 3, 4, 5};

    // diapazonga vosita for loopdan foydalanish
    // vektor orqali takrorlash
    for (const auto& num : numbers) {
        cout << num << " ";
    }

    return 0;
}

Chiqish

kod
1 2 3 4 5

Bu erda biz num takrorlanuvchi o'zgaruvchini e'lon qildik va range based for loop. yordamida konteyner raqamlarini takrorladik.

Lambda ifodalari

Lambda ifodalari qisqacha anonim funksiyalarni yaratishga imkon beradi. Ular, ayniqsa, kichik funksiyalarni boshqa funksiyalarga argument sifatida o'tkazish kerak bo'lganda foydalidir.

Sintaksis

example.cpp
[capture_clause] (parameter_list) -> return_type {
    // lambda tanasi: bajariladigan kod
}

Bu yerga,

  • capture_clause - lambda funksiyasida atrofdagi doiradan qaysi o'zgaruvchilarga kirish mumkinligini belgilaydi.
  • parametr_ro'yxati - funksiya qabul qiladigan parametrlarni belgilaydi.
  • -> return_type - funksiyaning qaytish turini e'lon qiladi.

[!NOTE] Turlarni avtomatik chegirib tashlash uchun return_type ni o'tkazib yuborishimiz mumkin, agar har xil turdagi bir nechta mumkin bo'lgan qaytarish qiymatlari bo'lmasa.

Batafsil ma’lumot olish uchun C++ Lambda qo‘llanmamizga tashrif buyuring.

2-misol: C++ Lambda ifodasi

example.cpp
#include <iostream>
using namespace std;

int main() {

    // "qo'shish" deb nomlangan lambda funksiyasini aniqlang
    // bu ikkita butun sonni oladi va ularning yig'indisini qaytaradi
    auto add = [] (int a, int b) {
        return a + b;
    };

    // 3 va 4 argumentlar bilan lambdani chaqiring
    int result = add(3, 4);

    // natijani chop eting.
    cout << "Result: " << result << endl;

    return 0;
}

Chiqish

kod
Result: 7

Bu yerda biz add() lambda funksiyasini yaratdik, u ikkita a va b argumentlarini oladi va ularning summasini qaytaradi.

Aqlli ko'rsatkichlar

C++ 11 xotirani avtomatik boshqaradigan va xotira oqishini oldini olishga yordam beruvchi aqlli ko‘rsatkichlarni taqdim etdi. Asosan, aqlli ko'rsatkich amaldan tashqariga chiqqanda o'zi boshqaradigan xotirani avtomatik ravishda chiqaradi.

Aqlli ko'rsatkichlar ikki xil:

  • Noyob ko'rsatkichlar o'zlari ko'rsatgan ob'ektlarning eksklyuziv mulkiga ega.
  • Birgalikda ko'rsatkichlar bir nechta umumiy ko'rsatkichlarga bitta ob'ektga egalik qilish imkonini beradi.

Sintaksis

example.cpp
// noyob ko'rsatkich
std::unique_ptr<data_type> unique_pointer = std::make_unique<data_type>(args...);

// umumiy ko'rsatkich
std::shared_ptr<Type> shared_pointer = std::make_shared<data_type>(args...);

[!NOTE] Aqlli ko'rsatkichlardan foydalanish uchun <memory> sarlavha faylini import qilishimiz kerak.

3-misol: C++ Smart Pointer

example.cpp
#include <iostream>
#include <memory>
using namespace std;

int main() {

    // qiymati 42 bo'lgan butun songa umumiy ko'rsatkich yarating
    shared_ptr<int> shared_pointer = make_shared<int>(42);

    // qiymati 3.14 bo'lgan dublga noyob ko'rsatkich yarating
    unique_ptr<double> unique_pointer = make_unique<double>(3.14);

    // shared_ptr tomonidan ko'rsatilgan qiymatni chop eting
    cout << "shared_pointer: " << *shared_pointer << endl;

    // unique_ptr tomonidan ko'rsatilgan qiymatni chop eting
    cout << "unique_pointer: " << *unique_pointer << endl;

    return 0;
}

Chiqish

kod
shared_pointer: 42
unique_pointer: 3.14

Semantikani ko'chirish

Ko'chirish semantikasi ob'ektga tegishli resurslarni nusxalash o'rniga boshqa ob'ektga ko'chirish imkonini beradi. Bu chuqur nusxalardan qochib ishlashni optimallashtiradi.

Biz harakat semantikasini amalga oshirish uchun move() funksiyasidan foydalanamiz. Masalan,

example.cpp
#include <iostream>
#include <vector>
using namespace std;

int main() {

    // butun son vektorini yarating
    vector<int> source = {1, 2, 3};

    // manba tarkibini boshqa vektorga o'tkazing
    vector<int> destination = move(source);

    // maqsad vektorini chop eting
    cout << "Destination Vector Contents: ";
    for (const int num : destination) {
        cout << num << "  ";
    }
    
    // maqsad vektorining o'lchamini chop eting
    cout << "\nDestination Vector Size: " << destination.size();

    return 0;
}

Chiqish

kod
Destination Vector Contents: 1  2  3  
Destination Vector Size: 3

Bu erda biz move() funksiyasidan tarkibni manbadan manzilga ko'chirish uchun foydalandik.

example.cpp
vector<int> destination = move(source);

C++ constexpr Kalit so'z

constexpr kalit so'zi o'zgaruvchi yoki funksiyani kompilyatsiya vaqtida baholash mumkinligini belgilash imkonini beradi. Masalan,

example.cpp
constexpr int square(int x) {
    return x * x;
}

// kompilyatsiya vaqtida hisoblangan
int result = square(5);

Bu erda biz constexpr kalit so'zidan square() funksiyasi bilan foydalandik. Natijada, square(5) ishlash vaqtida emas, balki kompilyatsiya vaqtida baholanishi mumkin.

Bu kodning ishlashini oshirishi mumkin va ifodalar kompilyatsiya vaqtida ma'lum bo'lgan qiymat bilan ishga tushirilishini ta'minlaydi.

Null Pointer

nullptr ning kiritilishi null ko'rsatkichlar uchun NULL dan foydalanishga xavfsizroq alternativani taqdim etadi.

example.cpp
int* ptr = nullptr;

[!NOTE] nullptr NULL xavfsizligini ta'minlaganligi sababli har doim nullptr ni NULL dan afzal ko'ring.

Turi xususiyatlari

C++ tilida tip belgilari kompilyatsiya vaqtida turlar haqida ma’lumot to‘plash uchun ishlatiladigan shablonlar guruhi bo‘lib, shablonlarni metadasturlash uchun kuchli vosita hisoblanadi.

Bu shuni anglatadiki, dasturingiz ishga tushishidan oldin kompilyator siz foydalanayotgan turlar haqida, masalan, tur butun son bo'ladimi, uni nusxalash mumkinmi yoki u ma'lum funksiyaga ega sinfmi yoki yo'qligini tushunishi mumkin.

Ular standart andozalar kutubxonasining (STL) bir qismidir va <type_traits> sarlavha fayliga kiritilgan.

Tur belgilari class templates yoki function templates yordamida amalga oshiriladi. Mana, funksiya shablonining namuna kodi.

example.cpp
template <typename T>
void process(T value) {

    if (std::is_pointer<T>::value) {
        // ko'rsatkichlarni boshqarish
    }
    else if (std::is_integral<T>::value) {
        // butun sonlarni boshqarish
    }
}

Mavzuni qo'llab-quvvatlash

C++ 11 mavzularni yaratish va boshqarish imkonini beruvchi standartlashtirilgan ish zarrachalari kutubxonasini qo'shdi.

example.cpp
#include <iostream>
#include <thread>

using namespace std;

void sayHello(){
  cout << "Hello, from the spawned thread\n";
}

int main(){
    // std::thread obyektini yarating
    std::thread t(sayHello);

    std::cout << "Hello, from the main thread\n";

    // ip o'z ishini yakunlashini kuting
    t.join();

}

Chiqish:

kod
Hello, from the main thread
Hello, from the spawned thread

[!NOTE] Chiqish bajarilishga bog'liq, shuning uchun uni har safar ishga tushirganingizda bir xil natijaga erisha olmaysiz.

Har bir ipning boshlang'ich funksiyasi bo'lishi kerak, bu erda yangi ipning bajarilishi boshlanadi.

Shunday qilib, biz sayHello funksiyasini o'tkazish orqali std::thread t ob'ektini yaratdik, biz ip bajarilishini xohlaymiz.

Endi dasturda 2 ta ip mavjud - biri asosiy funksiyani bajaradi, ikkinchisi sayHello funksiyasini bajaradi.

Konstruktorlarni topshirish

C++ 11 da konstruktor xuddi shu sinfdagi boshqa konstruktorni chaqirishi mumkin. Masalan,

example.cpp
#include <iostream>
using namespace std;

class Complex {
    int img;
    int real;

public:

    // №1 konstruktor (maqsadli konstruktor)
    Complex(int i, int r) : img(i), real(r) {}

    // №2 konstruktor (konstruktorni topshirish)
    // maqsadli konstruktorga argument sifatida ikkita nolni o'tkazing
    Complex() : Complex(0, 0) {
        cout << "Delegating constructor" << endl;
        cout << "img = " << img << endl;
        cout << "real  = " << real << endl;
    }
};

int main() {

    // Kompleks sinfining namunasini yarating
    // vakolat beruvchi konstruktor yordamida
    Complex obj; 

    return 0;
}

Chiqish

kod
Delegating constructor
img = 0
real = 0

Bu erda vakolat beruvchi konstruktor maqsadli konstruktorni chaqiradi.

O'chirilgan va standart funksiyalar

  1. O'chirilgan funksiya

Ular ataylab o'chirilgan deb belgilangan a'zo funksiyalari. Bu shuni anglatadiki, funksiyalardan foydalanib bo'lmaydi va ularni chaqirishga har qanday urinish kompilyatsiya xatosiga olib keladi.

Funksiyalarni o'chirish sinfingizdagi muayyan operatsiyalarni oldini olishni istasangiz foydali bo'ladi.

example.cpp
class My_Class {
public:

    // standart konstruktorni o'chirib tashlang
    My_Class() = delete;

    // a'zo funksiyasini o'chirish
    void do_something() = delete; 
};
  1. Standart funksiya

Ular kompilyatordan ma'lum a'zo funksiyalari uchun standart ilovalarni yaratishni aniq so'rash uchun ishlatiladi.

Bu, ayniqsa, kompilyator tomonidan yaratilgan versiyalardan foydalanmoqchi bo'lganingizda foydali bo'ladi va shu bilan birga boshqa funksiyalar uchun shaxsiy xatti-harakatlarni ta'minlaydi.

example.cpp
class Another_Class {
public:

    // kompilyator tomonidan yaratilgan standart konstruktordan foydalaning
    Another_Class() = default;

    // maxsus konstruktorni aniqlang
    Another_Class(int x) : value(x) {} 

private:
    int value;
};

4-misol: C++ o'chirilgan va standart funksiyalar

example.cpp
class Example {
public:

    // sukut bo'yicha standart konstruktor
    Example() = default;
    
    // o'chirilgan nusxa konstruktori
    Example(const Example&) = delete; 
};

int main() {

    // ruxsat berilgan: standart konstruktor ishlatiladi
    Example obj1; 
    
    // xato: nusxa ko'chirish konstruktori o'chirildi
    // Misol obj2 = obj1;

    return 0;
}

Bu yerga,

  • Biz standart konstruktor yordamida Example sinfining ob'ektini yaratishimiz mumkin.
  • Biz Example ob'ektining nusxasini yarata olmaymiz, chunki nusxa ko'chirish konstruktori o'chirilgan.