सामग्री पर जाएं

कंटेनर एडेप्टर और विशेष कंटेनर

stack, queue, priority_queue, और span, string_view तथा optional जैसे आधुनिक वोकैबुलरी टाइप के बारे में जानें। समझें कि एडेप्टर आपके कोड को कब सरल बनाते हैं।

कंटेनर एडेप्टर क्या होते हैं?

कंटेनर एडेप्टर मौजूदा कंटेनर के ऊपर बने रैपर हैं, जो एक सीमित इंटरफ़ेस देते हैं। वे पूरे कंटेनर API को छिपाकर एक ख़ास एक्सेस पैटर्न लागू करवाते हैं:

- std::stack — LIFO (Last In, First Out), डिफ़ॉल्ट रूप से deque पर आधारित
- std::queue — FIFO (First In, First Out), डिफ़ॉल्ट रूप से deque पर आधारित
- std::priority_queue — मैक्स-हीप, डिफ़ॉल्ट रूप से vector पर आधारित

एडेप्टर इटरेटर नहीं देते — आप सिर्फ़ top/front एलिमेंट तक पहुँच सकते हैं। यह सीमा जानबूझकर रखी गई है: इससे कोड का उद्देश्य ज़्यादा स्पष्ट होता है और ग़लती से होने वाला दुरुपयोग रुकता है।

स्टैक और क्यू का प्रयोग

स्टैक और क्यू बुनियादी डेटा स्ट्रक्चर हैं, जिनका उपयोग पार्सिंग, BFS/DFS, undo सिस्टम और टास्क शेड्यूलिंग में होता है।

stack_queue.cpp
#include <stack>
#include <queue>
#include <string>
#include <iostream>

// Check if parentheses are balanced using a stack
bool is_balanced(const std::string& expr) {
    std::stack<char> stk;
    for (char c : expr) {
        if (c == '(' || c == '[' || c == '{') {
            stk.push(c);
        } else if (c == ')' || c == ']' || c == '}') {
            if (stk.empty()) return false;
            char top = stk.top();
            stk.pop();
            if ((c == ')' && top != '(') ||
                (c == ']' && top != '[') ||
                (c == '}' && top != '{')) {
                return false;
            }
        }
    }
    return stk.empty();
}

int main() {
    std::cout << std::boolalpha;
    std::cout << is_balanced("({[]})") << '\n';   // true
    std::cout << is_balanced("({[}])") << '\n';   // false

    // Queue — BFS-style task processing
    std::queue<std::string> tasks;
    tasks.push("compile");
    tasks.push("link");
    tasks.push("run tests");

    while (!tasks.empty()) {
        std::cout << "Processing: " << tasks.front() << '\n';
        tasks.pop();   // removes from front
    }
}

प्रायोरिटी क्यू (हीप)

std::priority_queue डिफ़ॉल्ट रूप से मैक्स-हीप है — top() सबसे बड़ा एलिमेंट लौटाता है। मिन-हीप के लिए, कंपेरेटर के रूप में std::greater का उपयोग करें।

priority_queue.cpp
#include <queue>
#include <vector>
#include <functional>
#include <iostream>
#include <string>

struct Task {
    int priority;
    std::string name;
};

int main() {
    // Max-heap (default) — largest first
    std::priority_queue<int> max_heap;
    max_heap.push(30);
    max_heap.push(10);
    max_heap.push(50);
    max_heap.push(20);

    while (!max_heap.empty()) {
        std::cout << max_heap.top() << ' ';  // 50 30 20 10
        max_heap.pop();
    }
    std::cout << '\n';

    // Min-heap — smallest first
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
    min_heap.push(30);
    min_heap.push(10);
    min_heap.push(50);
    std::cout << "Min: " << min_heap.top() << '\n';  // 10

    // Custom comparator with lambda
    auto cmp = [](const Task& a, const Task& b) {
        return a.priority < b.priority;  // higher priority first
    };
    std::priority_queue<Task, std::vector<Task>, decltype(cmp)> task_queue(cmp);
    task_queue.push({3, "low priority"});
    task_queue.push({10, "critical"});
    task_queue.push({7, "medium"});

    while (!task_queue.empty()) {
        auto& t = task_queue.top();
        std::cout << "[" << t.priority << "] " << t.name << '\n';
        task_queue.pop();
    }
}

std::span — कंटिग्यूअस डेटा का नॉन-ओनिंग व्यू (C++20)

std::span एलिमेंट के कंटिग्यूअस सीक्वेंस के ऊपर एक हल्का, नॉन-ओनिंग (स्वामित्व-रहित) व्यू है। यह (pointer, size) जोड़े पास करने के त्रुटि-प्रवण पैटर्न की जगह लेता है।

span_example.cpp
#include <span>
#include <vector>
#include <array>
#include <iostream>
#include <numeric>

// span accepts vector, array, C-array — anything contiguous
void print_stats(std::span<const int> data) {
    if (data.empty()) return;
    std::cout << "Size: " << data.size() << '\n';
    std::cout << "First: " << data.front() << '\n';
    std::cout << "Last:  " << data.back() << '\n';

    int sum = std::accumulate(data.begin(), data.end(), 0);
    std::cout << "Sum:   " << sum << '\n';

    // Subviews
    auto first3 = data.first(3);   // first 3 elements
    auto last2  = data.last(2);    // last 2 elements
    auto mid    = data.subspan(1, 3);  // 3 elements starting at index 1
}

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::array<int, 5> arr = {10, 20, 30, 40, 50};
    int c_arr[] = {100, 200, 300, 400, 500};

    print_stats(vec);     // works with vector
    print_stats(arr);     // works with array
    print_stats(c_arr);   // works with C-array
}

string_view और optional

std::string_view (C++17) किसी स्ट्रिंग का नॉन-ओनिंग, रीड-ओनली रेफ़रेंस है। यह उन फ़ंक्शन के लिए std::string को कॉपी करने या आवंटित करने की लागत से बचाता है जिन्हें सिर्फ़ पढ़ना होता है:

void greet(std::string_view name) {  // न कॉपी, न आवंटन
    std::cout << "Hello, " << name << '\n';
}
greet("world");               // स्ट्रिंग लिटरल के साथ काम करता है
greet(std::string{"world"});  // std::string के साथ काम करता है

std::optional (C++17) ऐसी वैल्यू को दर्शाता है जो मौजूद हो भी सकती है और नहीं भी — यह अनुपस्थिति दिखाने के लिए "मैजिक वैल्यू" या रॉ पॉइंटर की जगह एक टाइप-सेफ़ विकल्प है:

std::optional<int> find_index(std::span<const int> data, int target) {
    for (int i = 0; i < data.size(); ++i)
        if (data[i] == target) return i;
    return std::nullopt;  // नहीं मिला
}
auto idx = find_index(vec, 42);
if (idx) std::cout << "Found at " << *idx << '\n';
std::cout << idx.value_or(-1) << '\n';  // खाली होने पर -1
सामान्य गलती

चूँकि std::string_view और std::span नॉन-ओनिंग हैं, इसलिए अंतर्निहित डेटा नष्ट हो जाने पर वे आसानी से डैंगलिंग हो सकते हैं:

std::string_view bad() {
    std::string temp = "hello";
    return temp;  // डैंगलिंग — temp स्कोप के अंत में नष्ट हो जाता है
}

किसी लोकल वेरिएबल का string_view या span कभी न लौटाएँ। वे फ़ंक्शन के पैरामीटर के लिए सुरक्षित हैं (कॉलर का डेटा कॉल से ज़्यादा समय तक रहता है), लेकिन रिटर्न वैल्यू के रूप में ख़तरनाक हैं, जब तक आप यह गारंटी न दे सकें कि स्रोत डेटा व्यू से ज़्यादा समय तक जीवित रहेगा।

यही बात std::span पर भी लागू होती है — यह भीतर से सिर्फ़ एक पॉइंटर और एक साइज़ है, जिसमें स्वामित्व का कोई सिमेंटिक्स नहीं है।

सर्वोत्तम अभ्यास

जब आप एलिमेंट के एक्सेस पर अनुशासन लागू करना चाहते हों, तो कंटेनर एडेप्टर का उपयोग करें:

- जब सिर्फ़ LIFO एक्सेस मायने रखता हो, तो रॉ deque या vector की जगह std::stack का उपयोग करें। इससे उद्देश्य साफ़ दिखता है और ग़लती से रैंडम एक्सेस नहीं हो पाता।
- FIFO प्रोसेसिंग (टास्क क्यू, BFS) के लिए std::queue का उपयोग करें।
- शेड्यूलिंग, Dijkstra के एल्गोरिदम, या किसी भी ऐसी स्थिति के लिए std::priority_queue का उपयोग करें जहाँ आप हमेशा चरम (extreme) एलिमेंट चाहते हों।

अगर आपको इटरेशन, किसी भी बिंदु पर साइज़ की जाँच, या सीक्वेंस के बीच में एक्सेस चाहिए, तो सीधे अंतर्निहित कंटेनर का उपयोग करें।

मुख्य बातें
  • कंटेनर एडेप्टर (stack, queue, priority_queue) उपयोग में अनुशासन लागू करने के लिए इंटरफ़ेस को सीमित करते हैं
  • std::priority_queue डिफ़ॉल्ट रूप से मैक्स-हीप है — मिन-हीप के लिए std::greater का उपयोग करें
  • std::span (C++20) कंटिग्यूअस डेटा के ऊपर नॉन-ओनिंग व्यू देता है, और (pointer, size) जोड़ों की जगह लेता है
  • std::string_view (C++17) सिर्फ़-पढ़ने के उपयोग के लिए स्ट्रिंग की कॉपी से बचाता है — लेकिन डैंगलिंग रेफ़रेंस से सावधान रहें
  • std::optional (C++17) "शायद कोई वैल्यू नहीं" को बिना मैजिक सेंटिनल वैल्यू के दर्शाने का टाइप-सेफ़ तरीक़ा है
  • नॉन-ओनिंग व्यू (span, string_view) को उस डेटा से ज़्यादा समय तक कभी जीवित नहीं रहना चाहिए जिसे वे रेफ़र करते हैं

क्विज़ — अपना ज्ञान परखें

(15 XP)

1. कौन-सा कंटेनर एडेप्टर FIFO (First In, First Out) व्यवहार देता है?

2. डिफ़ॉल्ट रूप से `std::priority_queue<int>` का `top()` कौन-सा एलिमेंट लौटाता है?

3. लोकल `std::string` का `std::string_view` लौटाना ख़तरनाक क्यों है?