स्टैक ओवरफ्लो के साथ Björn Straustrup उत्तर टॉप 5 C ++ प्रश्न

पाठ्यक्रम की शुरुआत की प्रत्याशा में "सी ++ डेवलपर" ने दिलचस्प सामग्री का अनुवाद तैयार किया।





कोडेकडेमी पर लर्न सी ++ कोर्स के लेखक मारिएल फ्रैंक और सन्नी ली, को हाल ही में सी ++ के निर्माता डॉ। ब्योर्न स्ट्रैन्जअप का साक्षात्कार करने का अवसर मिला।

इस साक्षात्कार के भाग के रूप में, उन्होंने C ++ प्रश्नों के उत्तर दिए, जिन्हें स्टैक ओवरफ्लो पर सबसे अधिक वोट मिले। हालांकि सभी साक्षात्कार पढ़ने लायक हैं, लेकिन कोडेकेडमी ने उदारतापूर्वक हमें इसका हिस्सा साझा करने की अनुमति दी।

यदि आपने कभी सोचा है कि स्टैक ओवरफ्लो पर निश्चित रूप से व्यापक उत्तर हैं, तो यहां इस के लिए कुछ निकटतम है जिसे आप सुनिश्चित कर सकते हैं (हालांकि हम किसी से असहमत होने की उम्मीद करते हैं)।

क्यों एक सॉर्ट किए गए सरणी को अनसेंडेड सरणी को संसाधित करने की तुलना में तेज़ी से संसाधित कर रहा है?



नोट: यह सवाल नंबर 1 है जिसे सभी समय के स्टैक ओवरफ्लो पर सबसे अधिक वोट मिले।


एक साक्षात्कार से एक प्रश्न की तरह लगता है। यह सच है? आप कैसे जानते हैं? प्रारंभिक माप लेने के बिना दक्षता के बारे में सवालों का जवाब देना बुरा है, इसलिए यह जानना महत्वपूर्ण है कि उन्हें कैसे मापें।
इसलिए, मैंने एक मिलियन पूर्णांक के वेक्टर पर जाँच की और प्राप्त किया:

    32995 
          125944 

     18610 
          133304 

     17942 
          107858 


मैंने यह सुनिश्चित करने के लिए कुछ समय चलाया। हां, यह घटना वास्तविक है। मेरा मुख्य कोड था:

void run(vector<int>& v, const string& label)
{
    auto t0 = system_clock::now();
    sort(v.begin(), v.end());
    auto t1 = system_clock::now();
    cout << label 
         << duration_cast<microseconds>(t1 — t0).count() 
         << " milliseconds\n";
}

void tst()
{
    vector<int> v(1'000'000);
    iota(v.begin(), v.end(), 0);
    run(v, "already sorted ");
    std::shuffle(v.begin(), v.end(), std::mt19937{ std::random_device{}() });
    run(v, "shuffled    ");
}


कम से कम, यह घटना इस संकलक, मानक पुस्तकालय और अनुकूलक सेटिंग्स के साथ वास्तविक है। विभिन्न कार्यान्वयन अलग-अलग उत्तर दे सकते हैं (और दे सकते हैं)। वास्तव में, किसी ने अधिक व्यवस्थित अध्ययन किया (इंटरनेट पर एक त्वरित खोज आपको इसे खोजने में मदद करेगी), और अधिकांश कार्यान्वयन इस प्रभाव को दिखाते हैं।

कारणों में से एक शाखा भविष्यवाणी है: छंटाई एल्गोरिथ्म में महत्वपूर्ण संचालन "यदि (v [i] <धुरी]) ..." या समतुल्य है। क्रमबद्ध अनुक्रम के लिए, यह परीक्षण हमेशा सत्य होता है, जबकि यादृच्छिक अनुक्रम के लिए, चयनित शाखा यादृच्छिक रूप से बदलती है।

एक और कारण यह है कि जब वेक्टर पहले से ही सॉर्ट किया गया है, तो हमें तत्वों को सही स्थिति में स्थानांतरित करने की आवश्यकता नहीं है। इन छोटे विवरणों का प्रभाव लगभग पांच या छह का एक कारक देता है, जिसे हमने देखा।

क्विकॉर्ट (सामान्य रूप से छंटनी) एक जटिल अध्ययन है जिसने कंप्यूटर विज्ञान के कुछ महानतम दिमागों को आकर्षित किया है। एक अच्छा सॉर्टिंग फ़ंक्शन इसके कार्यान्वयन के दौरान एक अच्छा एल्गोरिथ्म और उपकरण के प्रदर्शन पर ध्यान देने का परिणाम है।
यदि आप कुशल कोड लिखना चाहते हैं, तो आपको मशीन की वास्तुकला को ध्यान में रखना होगा।
——————————————————————————————–
हस्तक्षेप के लिए क्षमा करें। बस एक अनुस्मारक है कि स्टैक ओवरफ्लो पॉडकास्ट वापस आ गया है! हमारे नए सीईओ के साथ एक साक्षात्कार छोड़ें और सुनें।

C ++ में ऑपरेटर> क्या है?




यह एक पुराना ट्रिक प्रश्न है। C ++ में कोई ऑपरेटर नहीं है ->। निम्नलिखित को धयान मे रखते हुए:

if (p-->m == 0) f(p);


यह, निश्चित रूप से, ऐसा लगता है कि किसी प्रकार का ऑपरेटर है -> और एक उपयुक्त घोषणा पी और एम के साथ, आप इसे संकलित और चला भी सकते हैं:

int p = 2;
int m = 0;
if (p-->m == 0) f(p);


इसका वास्तव में अर्थ है: देखें कि क्या p-- m से अधिक है (जिस तरह से है), और फिर परिणाम की (सत्य) तुलना करें (खैर), सच! = 0, इसलिए परिणाम गलत है और f () नहीं कहा जाता है। दूसरे शब्दों में:

if ((p--) > m == 0) f(p);


कृपया ऐसे प्रश्नों पर ज्यादा समय न दें। सी ++ के आविष्कार से पहले ही वे भ्रमित करने वाले शुरुआती लोगों के लिए लोकप्रिय थे।

सबसे अच्छी मार्गदर्शिका और C ++ पुस्तकों की सूची



दुर्भाग्य से, C ++ पुस्तकों की कोई विहित सूची नहीं है। यह, सिद्धांत रूप में, नहीं हो सकता। सभी को समान जानकारी की आवश्यकता नहीं है, सभी को समान अनुभव नहीं है, और C ++ सर्वोत्तम अभ्यास लगातार विकसित हो रहे हैं।

मैंने इंटरनेट पर थोड़ा शोध किया और सिफारिशों का एक अद्भुत सेट पाया। कई गंभीर रूप से पुराने थे, और कुछ बस खराब थे। मार्गदर्शन के बिना एक अच्छी किताब की तलाश में एक शुरुआत करने वाले को बहुत भ्रम होगा!

आपको वास्तव में एक पुस्तक की आवश्यकता है क्योंकि C ++ को प्रभावी बनाने वाले तरीकों को विशिष्ट विषयों पर कई ब्लॉगों पर खोजना आसान नहीं है, और निश्चित रूप से ब्लॉग भी त्रुटियों, अप्रचलन और खराब स्पष्टीकरण से ग्रस्त हैं। अक्सर वे उन्नत नए विषयों पर भी ध्यान केंद्रित करते हैं और बुनियादी सिद्धांतों की अनदेखी करते हैं।

मैं अपनी सलाह देता हूंप्रोग्रामिंग: लोगों के लिए C ++ (2 वें संस्करण) का उपयोग करने के सिद्धांत और अभ्यास, बस सीखना शुरू करने के लिए कि कैसे प्रोग्राम करना है, और C ++ टूर (2 डी संस्करण) उन लोगों के लिए जो पहले से ही प्रोग्रामर हैं और जिन्हें आधुनिक C ++ से परिचित होने की आवश्यकता है। एक मजबूत गणितीय पृष्ठभूमि वाले लोग डिस्कवरिंग मॉडर्न C ++ के साथ शुरू कर सकते हैं : वैज्ञानिकों, इंजीनियरों और प्रोग्रामर पीटर गॉटश्लिंग के लिए एक गहन पाठ्यक्रम

एक बार जब आप वास्तविक के लिए C ++ का उपयोग करना शुरू कर देंगे, तो आपको यह समझने के लिए दिशा निर्देशों के एक सेट की आवश्यकता होगी कि क्या किया जा सकता है और अच्छा अभ्यास क्या है। इसके लिए, मैं GitHub पर C ++ कोर दिशानिर्देशों की सिफारिश करता हूं

मानक पुस्तकालय की व्यक्तिगत भाषा सुविधाओं और कार्यों की एक अच्छी संक्षिप्त व्याख्या के लिए, मैं सलाह देता हूंwww.cppreference.com

# 4। C ++ में पॉइंटर चर और संदर्भ चर के बीच अंतर क्या हैं?



लिंक और संकेत के बारे में अधिक जानने के लिए, C ++ जानें

दोनों को मशीन के पते के रूप में मेमोरी में दर्शाया गया है। अंतर उनके उपयोग में है।
सूचक को इनिशियलाइज़ करने के लिए, आप उसे ऑब्जेक्ट का पता देते हैं:

int x = 7;
int* p1 = &x;
int* p2 = new int{9};


एक पॉइंटर के माध्यम से पढ़ने और लिखने के लिए, हम डीपरेशन ऑपरेटर (*) का उपयोग करते हैं:

*p1 = 9;       // write through p1
int y = *p2;   // read through p2


जब हम एक पॉइंटर को दूसरे को असाइन करते हैं, तो वे दोनों एक ही ऑब्जेक्ट को इंगित करेंगे:

p1 = p2;       //  p1  p2    int   9
*p2 = 99;      //  99  p2
int z = *p1;   //   p1, z  99 (  9)


ध्यान दें कि एक पॉइंटर अपने जीवन चक्र के दौरान विभिन्न वस्तुओं को इंगित कर सकता है। यह लिंक से मुख्य अंतर है। जब यह बनाया जाता है तो लिंक ऑब्जेक्ट से जुड़ा होता है और इसे दूसरे के लिंक में नहीं बदला जा सकता है।

संदर्भों के लिए, dereferencing निहित है। आप ऑब्जेक्ट के साथ लिंक को इनिशियलाइज़ करते हैं, और लिंक को उसका पता मिलता है।

int x = 7;
int& r1 = x;
int& r2 = *new int{9};


नया ऑपरेटर एक पॉइंटर लौटाता है, इसलिए मुझे असाइनमेंट से पहले इसे स्थगित करना पड़ा, इसका उपयोग लिंक को इनिशियलाइज़ करने के लिए किया गया।

एक लिंक के माध्यम से पढ़ने और लिखने के लिए, हम बस लिंक नाम का उपयोग करते हैं (बिना स्पष्ट डेरेफेरिंग के):

r1 = 9;        //   r1
int y = r2;    //   r2


जब हम एक लिंक दूसरे को देते हैं, तो निर्दिष्ट मूल्य की प्रतिलिपि बनाई जाएगी:

r1 = r2;       //  p1  p2     9
r1 = 99;       //  99  r1
int z = r2;    //   r2, z  9 (  99)


दोनों संदर्भ और संकेत अक्सर एक समारोह के लिए तर्क के रूप में उपयोग किए जाते हैं:

void f(int* p)

{
    if (p == nullptr) return;
    // ...
}

void g(int& r)
{
    // ...
}

int x = 7;
f(&x);
g(x);


एक सूचक हो सकता है nullptr, इसलिए हमें यह देखने के लिए जांचना चाहिए कि क्या यह कुछ भी इंगित करता है। लिंक के बारे में, आप शुरू में मान सकते हैं कि यह किसी चीज़ को संदर्भित करता है।

# 5। स्ट्रिंग शब्दों पर पुनरावृति कैसे करें?




उपयोग करें stringstream, लेकिन आप "शब्द" को कैसे परिभाषित करते हैं? सोचो: "मैरी के पास थोड़ा सा मेमना था।" अंतिम शब्द "भेड़ का बच्चा" या "भेड़ का बच्चा" है। यदि कोई विराम चिह्न नहीं है, तो यह आसान है:

vector<string> split(const string& s)
{
    stringstream ss(s);
    vector<string> words;
    for (string w; ss>>w; ) words.push_back(w);
    return words;
}

auto words = split("here is a simple example");   // five words
for (auto& w : words) cout << w << '\n';


या केवल:

for (auto& w : split("here is a simple example")) cout << w << '\n';


डिफ़ॉल्ट रूप से, >> ऑपरेटर रिक्त स्थान छोड़ देता है। यदि हमें सीमांकक के मनमाने सेट की जरूरत है, तो चीजें थोड़ी और भ्रामक हो जाती हैं:

template<typename Delim>
string get_word(istream& ss, Delim d)
{
    string word;
    for (char ch; ss.get(ch); )    //  
        if (!d(ch)) {
            word.push_back(ch);
            break;
        }
    for (char ch; ss.get(ch); )    //  
        if (!d(ch))
            word.push_back(ch);
        else
            break;
    return word;
}


d एक ऑपरेशन है जो यह बताता है कि चरित्र एक सीमांकक है, और मैं यह बताने के लिए "" (खाली स्ट्रिंग) वापस लौटा हूं कि वापस लौटने के लिए कोई शब्द नहीं था।

vector<string> split(const string& s, const string& delim)
{
    stringstream ss(s);
    auto del = [&](char ch) { for (auto x : delim) if (x == ch) return true; return false; };

    vector<string> words;
    for (string w; (w = get_word(ss, del))!= ""; ) words.push_back(w);
    return words;
}

auto words = split("Now! Here is something different; or is it? ", "!.,;? ");
for (auto& w : words) cout << w << '\n';


यदि आपके पास C ++ 20 रेंज लाइब्रेरी है, तो आपको ऐसा कुछ लिखने की आवश्यकता नहीं है, लेकिन आप विभाजन_ का उपयोग कर सकते हैं।

Bjarn Straustrup एक तकनीकी भागीदार और मॉर्गन स्टेनली न्यूयॉर्क के प्रबंध निदेशक और कोलंबिया विश्वविद्यालय में एक विजिटिंग प्रोफेसर हैं। वह C ++ के निर्माता भी हैं।

C ++ 20 के बारे में अधिक जानकारी के लिए: isocpp.org देखें


बस इतना ही। जैसा कि आप देख राह पर !

All Articles