BjΓΆrn Straustrup Jawaban Top 5 C ++ Pertanyaan dengan Stack Overflow

Untuk mengantisipasi dimulainya kursus "Pengembang C ++" menyiapkan terjemahan materi yang menarik.





Marielle Frank dan Sonny Lee, penulis kursus Learn C ++ on Codecademy, baru-baru ini mendapat kesempatan untuk mewawancarai Dr. BjΓΆrn Straustrup, pencipta C ++.

Sebagai bagian dari wawancara ini, ia menjawab pertanyaan-pertanyaan C ++ yang mendapat suara terbanyak di Stack Overflow. Meskipun semua wawancara layak dibaca, Codecademy dengan murah hati mengizinkan kami untuk membagikan sebagian darinya.

Jika Anda pernah bertanya-tanya apakah ada jawaban yang sangat komprehensif tentang Stack Overflow, maka di sini ada sesuatu yang paling dekat dengan ini yang bisa Anda dapatkan dengan pasti (meskipun kami mengharapkan seseorang tidak setuju).

Mengapa memproses array yang diurutkan lebih cepat daripada memproses array yang tidak disortir?



Catatan: Pertanyaan ini adalah nomor 1 yang menerima suara terbanyak di Stack Overflow sepanjang masa.


Kedengarannya seperti pertanyaan dari sebuah wawancara. Itu benar? Bagaimana kamu tahu itu? Sangat buruk untuk menjawab pertanyaan tentang efisiensi tanpa melakukan pengukuran awal, jadi penting untuk mengetahui bagaimana mengukurnya.
Jadi, saya memeriksa vektor sejuta bilangan bulat dan mendapatkan:

    32995 
          125944 

     18610 
          133304 

     17942 
          107858 


Saya menjalankan ini beberapa kali untuk memastikan. Ya, fenomena ini nyata. Kode utama saya adalah:

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    ");
}


Setidaknya, fenomena ini nyata dengan penyusun ini, pustaka standar, dan pengaturan pengoptimal. Implementasi yang berbeda dapat memberikan (dan memberikan) jawaban yang berbeda. Bahkan, seseorang melakukan penelitian yang lebih sistematis (pencarian cepat di Internet akan membantu Anda menemukannya), dan sebagian besar implementasi menunjukkan efek ini.

Salah satu alasannya adalah prediksi cabang: operasi utama dalam algoritma penyortiran adalah "jika (v [i] <pivot]) ..." atau yang setara. Untuk urutan yang diurutkan, tes ini selalu benar, sedangkan untuk urutan acak, cabang yang dipilih berubah secara acak.

Alasan lain adalah ketika vektor sudah diurutkan, kita tidak perlu memindahkan elemen ke posisi yang benar. Efek dari detail kecil ini memberikan faktor sekitar lima atau enam, yang kami amati.

Quicksort (dan memilah secara umum) adalah studi kompleks yang telah menarik beberapa pemikir besar ilmu komputer. Fungsi penyortiran yang baik adalah hasil dari pemilihan algoritma yang baik dan perhatian terhadap kinerja peralatan selama implementasinya.
Jika Anda ingin menulis kode yang efisien, Anda harus mempertimbangkan arsitektur mesin.
β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
Maaf atas intervensi. Hanya pengingat bahwa podcast Stack Overflow kembali! Mampir dan dengarkan wawancara dengan CEO baru kami.

Apa yang dimaksud dengan -> operator di C ++?




Ini pertanyaan jebakan lama. Di C ++ tidak ada operator ->. Pertimbangkan yang berikut ini:

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


Ini, tentu saja, terlihat seperti ada beberapa jenis operator -> dan dengan deklarasi p dan m yang sesuai, Anda bahkan dapat mengkompilasi dan menjalankan ini:

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


Ini sebenarnya berarti: lihat apakah p-- lebih besar dari m (sebagaimana adanya), dan kemudian bandingkan hasilnya (benar) dengan 0. Ya, benar! = 0, jadi hasilnya salah dan f () tidak dipanggil. Dengan kata lain:

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


Tolong jangan menghabiskan terlalu banyak waktu untuk pertanyaan seperti itu. Mereka populer untuk pemula yang membingungkan bahkan sebelum penemuan C ++.

Panduan dan daftar buku C ++ terbaik



Sayangnya, tidak ada daftar kanonik buku C ++. Ini, pada prinsipnya, tidak mungkin. Tidak semua orang membutuhkan informasi yang sama, tidak semua orang memiliki pengalaman yang sama, dan praktik terbaik C ++ terus berkembang.

Saya melakukan sedikit riset di Internet dan menemukan serangkaian rekomendasi yang luar biasa. Banyak yang sudah ketinggalan zaman, dan ada yang benar-benar buruk. Seorang pemula yang mencari buku bagus tanpa bimbingan akan sangat bingung!

Anda benar-benar membutuhkan buku, karena metode yang membuat C ++ efektif tidak mudah ditemukan di banyak blog tentang topik tertentu, dan tentu saja blog juga menderita kesalahan, usang, dan penjelasan yang buruk. Seringkali mereka juga fokus pada topik baru lanjutan dan mengabaikan prinsip-prinsip dasar.

Saya merekomendasikanPemrograman: Prinsip dan Praktek menggunakan C ++ (edisi ke-2) untuk orang-orang yang baru mulai mempelajari cara memprogram, dan Tur C ++ (edisi ke-2) untuk orang-orang yang sudah menjadi programmer dan yang perlu mengenal C ++ modern. Orang-orang dengan latar belakang matematika yang kuat dapat mulai dengan Menemukan Modern C ++: Kursus Intensif untuk Ilmuwan, Insinyur, dan Pemrogram Peter Gottschling.

Setelah Anda mulai menggunakan C ++ secara nyata, Anda akan memerlukan seperangkat pedoman untuk membedakan antara apa yang dapat dilakukan dan apa yang merupakan praktik yang baik. Untuk ini, saya merekomendasikan Pedoman Inti C ++ di GitHub.

Untuk penjelasan singkat yang baik tentang fitur bahasa individu dan fungsi perpustakaan standar, saya sarankanwww.cppreference.com .

# 4 Apa perbedaan antara variabel pointer dan variabel referensi dalam C ++?



Untuk mempelajari lebih lanjut tentang tautan dan petunjuk, periksa Pelajari C ++ .

Keduanya direpresentasikan dalam memori sebagai alamat mesin. Perbedaannya adalah dalam penggunaannya.
Untuk menginisialisasi pointer, Anda memberikannya alamat objek:

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


Untuk membaca dan menulis melalui pointer, kami menggunakan operator dereference (*):

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


Saat kami menetapkan satu pointer ke pointer lainnya, keduanya akan menunjuk ke objek yang sama:

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


Perhatikan bahwa pointer dapat menunjuk ke objek yang berbeda selama siklus hidupnya. Inilah perbedaan utama dari tautan. Tautan dilampirkan ke objek saat dibuat dan tidak dapat dikonversi menjadi tautan ke yang lain.

Untuk referensi, dereferencing adalah implisit. Anda menginisialisasi tautan dengan objek, dan tautan mendapatkan alamatnya.

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


Operator baru mengembalikan pointer, jadi saya harus melakukan dereferensi sebelum penugasan, menggunakannya untuk menginisialisasi tautan.

Untuk membaca dan menulis melalui tautan, kami cukup menggunakan nama tautan (tanpa dereferencing eksplisit):

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


Saat kami menetapkan satu tautan ke tautan lain, nilai yang ditentukan akan disalin:

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


Baik referensi dan petunjuk sering digunakan sebagai argumen untuk suatu fungsi:

void f(int* p)

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

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

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


Mungkin ada pointer nullptr, jadi kita harus memeriksa untuk melihat apakah itu menunjuk ke sesuatu. Tentang tautan, Anda awalnya dapat berasumsi bahwa itu merujuk pada sesuatu.

# 5 Bagaimana cara mengulang kata string?




Gunakan stringstream, tetapi bagaimana Anda mendefinisikan "kata"? Pikirkan: "Mary punya domba kecil." Kata terakhir adalah "domba" atau "domba." Jika tidak ada tanda baca, ini mudah:

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';


atau hanya:

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


Secara default, operator >> melewatkan spasi. Jika kita membutuhkan set pembatas yang berubah-ubah, segalanya menjadi sedikit membingungkan:

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 adalah operasi yang memberitahukan apakah karakternya adalah pembatas, dan saya mengembalikan "" (string kosong) untuk menunjukkan bahwa tidak ada kata untuk kembali.

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';


Jika Anda memiliki pustaka C ++ 20 Range, Anda tidak perlu menulis sesuatu seperti itu, tetapi Anda bisa menggunakan split_view.

Bjarn Straustrup adalah mitra teknis dan direktur pelaksana Morgan Stanley New York dan profesor tamu di Universitas Columbia. Dia juga pencipta C ++.

Untuk informasi lebih lanjut tentang C ++ 20, lihat: isocpp.org .


Itu saja. Sampai jumpa di lapangan !

All Articles