يجيب Björn Straustrup على أعلى 5 أسئلة C ++ مع تجاوز سعة المكدس

تحسبًا لبدء الدورة ، أعد "مطور C ++" ترجمة لمواد مثيرة للاهتمام.





حصلت كل من Marielle Frank و Sonny Lee ، مؤلفو دورة Learn C ++ على Codecademy ، على فرصة مقابلة د. Björn Straustrup ، مبتكر C ++.

كجزء من هذه المقابلة ، أجاب على أسئلة C ++ التي حصلت على أكبر عدد من الأصوات في Stack Overflow. على الرغم من أن جميع المقابلات تستحق القراءة ، فقد سمحت لنا Codecademy بسخاء بمشاركة جزء منها.

إذا تساءلت يومًا عما إذا كانت هناك إجابات شاملة نهائية على Stack Overflow ، فهناك شيء قريب من هذا يمكنك أن تتأكد منه (على الرغم من أننا نتوقع أن يختلف أحد).

لماذا تتم معالجة مصفوفة مرتبة بشكل أسرع من معالجة مصفوفة غير مصنفة؟



ملاحظة: هذا السؤال هو الرقم 1 الذي حصل على أكبر عدد من الأصوات على Stack Overflow في كل الأوقات.


يبدو وكأنه سؤال من مقابلة. انها حقيقة؟ كبف عرفت ذلك؟ من السيء الإجابة على الأسئلة المتعلقة بالكفاءة دون أخذ قياسات أولية ، لذا من المهم معرفة كيفية قياسها.
لذا ، راجعت متجه مليون عدد صحيح وحصلت على:

    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] <pivot]) ..." أو ما يعادلها. بالنسبة للتسلسل المصنف ، يكون هذا الاختبار دائمًا صحيحًا ، بينما بالنسبة للتسلسل العشوائي ، يتغير الفرع المحدد بشكل عشوائي.

سبب آخر هو أنه عندما يتم فرز المتجه بالفعل ، لا نحتاج أبدًا إلى تحريك العناصر إلى الموضع الصحيح. يعطي تأثير هذه التفاصيل الصغيرة عاملًا تقريبًا خمسة أو ستة ، لاحظنا.

Quicksort (والفرز بشكل عام) هي دراسة معقدة اجتذبت بعض أعظم العقول في علوم الكمبيوتر. وظيفة الفرز الجيدة هي نتيجة اختيار خوارزمية جيدة واهتمام بأداء المعدات أثناء تنفيذها.
إذا كنت تريد كتابة رمز فعال ، فأنت بحاجة إلى مراعاة بنية الجهاز.
————————————————————————————— -
آسف للتدخل. مجرد تذكير بأن البودكاست Stack Overflow قد عاد! اذهب واستمع إلى مقابلة مع الرئيس التنفيذي الجديد.

ما هو العامل -> في لغة C ++؟




هذا سؤال خدعة قديم. في C ++ لا يوجد عامل ->. خذ بعين الاعتبار ما يلي:

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


هذا ، بالطبع ، يبدو أن هناك نوعًا من عامل التشغيل -> ومع إعلان مناسب p و m ، يمكنك حتى تجميع وتشغيل هذا:

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


هذا يعني في الواقع: انظر ما إذا كانت p-- أكبر من m (كما هي) ، ثم قارن النتيجة (true) مع 0. حسنًا ، true! = 0 ، وبالتالي فإن النتيجة خاطئة ولا يتم استدعاء f (). بعبارات أخرى:

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


من فضلك لا تنفق الكثير من الوقت على مثل هذه الأسئلة. كانت شائعة في إرباك المبتدئين حتى قبل اختراع C ++.

أفضل دليل وقائمة كتب C ++



للأسف ، لا توجد قائمة أساسية لكتب C ++. هذا ، من حيث المبدأ ، لا يمكن أن يكون. لا يحتاج كل شخص إلى نفس المعلومات ، ولا يمتلك كل شخص نفس التجربة ، وتتطور أفضل ممارسات C ++ باستمرار.

لقد أجريت القليل من البحث على الإنترنت ووجدت مجموعة مذهلة من التوصيات. كان العديد منها عتيقًا بشكل خطير ، والبعض الآخر ببساطة سيئ. سوف يكون الخلط بين المبتدئين الذين يبحثون عن كتاب جيد بدون توجيه!

أنت حقًا بحاجة إلى كتاب لأن الطرق التي تجعل C ++ فعالة ليس من السهل العثور عليها في العديد من المدونات حول مواضيع محددة ، وبالطبع فإن المدونات تعاني أيضًا من أخطاء وتقادم وتفسيرات ضعيفة. غالبًا ما يركزون أيضًا على مواضيع جديدة متقدمة ويتجاهلون المبادئ الأساسية.

أوصيالبرمجة: مبادئ وممارسات استخدام C ++ (الإصدار الثاني) للأشخاص الذين بدأوا للتو في تعلم كيفية البرمجة ، وجولة C ++ (الإصدار الثاني) للأشخاص المبرمجين بالفعل والذين يحتاجون إلى التعرف على C ++ الحديث. يمكن للأشخاص الذين لديهم خلفية رياضية قوية أن يبدأوا باكتشاف Modern C ++: دورة مكثفة للعلماء والمهندسين والمبرمجين Peter Gottschling.

بمجرد البدء في استخدام C ++ بشكل حقيقي ، ستحتاج إلى مجموعة من الإرشادات للتمييز بين ما يمكن القيام به وما هو الممارسة الجيدة. لهذا ، أوصي بإرشادات C ++ الأساسية على GitHub.

للحصول على شرح موجز جيد لميزات اللغة الفردية ووظائف المكتبة القياسية ، أوصيwww.cppreference.com .

# 4. ما هي الاختلافات بين متغير المؤشر ومتغير مرجعي في C ++؟



لمعرفة المزيد حول الروابط والمؤشرات ، راجع Learn 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)


لاحظ أن المؤشر يمكن أن يشير إلى كائنات مختلفة خلال دورة حياته. هذا هو الفرق الرئيسي من الروابط. يتم إرفاق الرابط بالكائن عند إنشائه ولا يمكن تحويله إلى ارتباط بأخر.

بالنسبة للمراجع ، يعد إلغاء الإشارة ضمنيًا. تقوم بتهيئة الرابط بالكائن ، ويحصل الرابط على عنوانه.

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 ، فلن تحتاج إلى كتابة شيء من هذا القبيل ، ولكن يمكنك استخدام Split_view.

Bjarn Straustrup هو شريك تقني ومدير إداري لشركة Morgan Stanley New York وأستاذ زائر في جامعة كولومبيا. وهو أيضًا مبتكر لغة C ++.

لمزيد من المعلومات حول C ++ 20 ، راجع: isocpp.org .


هذا كل شئ. نراكم في الدورة !

All Articles