Bagaimana cara beralih ke C ++ 11?

35

Saya sudah pemrograman dalam C ++ untuk sementara waktu sekarang, tetapi sebagian besar hal berpusat di sekitar fitur tingkat rendah C ++. Maksud saya sebagian besar bekerja dengan pointer dan array mentah. Saya pikir perilaku ini dikenal sebagai menggunakan C ++ sebagai C dengan kelas. Meskipun demikian, saya baru mencoba C baru-baru ini untuk pertama kalinya. Saya terkejut bagaimana bahasa seperti C # dan Java menyembunyikan detail-detail ini di kelas perpustakaan standar yang nyaman seperti Kamus dan Daftar.

Saya sadar bahwa pustaka standar C ++ memiliki banyak wadah seperti vektor, peta dan string juga dan C ++ 11 hanya menambah ini dengan memiliki std :: array dan rentang loop.

Bagaimana cara terbaik saya belajar memanfaatkan fitur-fitur bahasa modern ini dan mana yang cocok untuk saat-saat mana? Benarkah rekayasa perangkat lunak dalam C ++ saat ini sebagian besar bebas dari manajemen memori manual?

Terakhir, kompiler mana yang harus saya gunakan untuk memaksimalkan standar baru? Visual Studio memiliki alat debugging yang sangat baik, tetapi bahkan VS2012 tampaknya memiliki dukungan C ++ 11 yang mengerikan.

Overv
sumber
2
Saya akan mengatakan memanggil dukungan C ++ 11 VS2012 "mengerikan" agak berlebihan, tapi tentu saja bisa lebih baik (daftar inisialisasi yang hilang sangat mengganggu untuk tes / kode mainan). Tetapi perhatikan bahwa mereka mengumumkan bahwa mereka akan mengirimkan pembaruan kompiler secara terpisah dari sisa VS, jadi saya kira kita bisa berharap untuk beberapa fitur C ++ 11 di VS2012 selama 2013.
Martin Ba
3
Pada awalnya saya pikir menyarankan untuk belajar C ++ 11 akan aneh, tetapi melihat bahwa Anda masih terjebak di tanah C-With-Classes ... Satu dekade yang lalu saya membaca Koenig / Moo's Accelerated C ++ . Pada saat saya sebenarnya sudah melakukan template meta-programming (saya hanya membacanya untuk review), tetapi masih terasa seperti wahyu. (Saya menggunakannya sebagai dasar untuk mengajar C ++ sejak itu.) Berasal dari C With Classes , buku ini dapat menunjukkan kepada Anda bahasa yang sama sekali baru yang tidak Anda ketahui miliki. Ini hanya 250 halaman, dan Anda kemudian dapat dengan cepat beralih ke sesuatu yang spesifik C ++ 11, tetapi IMO itu adalah langkah yang berharga.
sbi
4
g++ -std=c++11
fredoverflow

Jawaban:

50

Pertama, beberapa aturan praktis:

  • Gunakan std::unique_ptrsebagai smart pointer yang tidak ada overhead. Anda tidak perlu repot dengan pointer mentah terlalu sering. std::shared_ptrjuga tidak perlu dalam banyak kasus. Keinginan untuk memiliki kepemilikan bersama seringkali mengkhianati kurangnya pemikiran tentang kepemilikan.

  • Gunakan std::arrayuntuk array panjang statis dan std::vectoruntuk dinamis.

  • Gunakan algoritma generik secara luas, khususnya:

    • <algorithm>
    • <numeric>
    • <iterator>
    • <functional>
  • Gunakan autodan di decltype()mana pun mereka mendapat manfaat keterbacaan. Khususnya, ketika Anda ingin mendeklarasikan sesuatu, tetapi dari tipe yang tidak Anda pedulikan seperti iterator atau tipe templat yang kompleks, gunakan auto. Ketika Anda ingin mendeklarasikan sesuatu dalam hal jenis hal lain, gunakan decltype().

  • Jadikan hal-hal menjadi aman ketika Anda bisa. Ketika Anda memiliki pernyataan yang menerapkan invarian pada hal tertentu, logika itu dapat dipusatkan dalam suatu tipe. Dan ini tidak selalu membuat overhead runtime. Itu juga harus pergi tanpa mengatakan bahwa gips C-style ( (T)x) harus dihindari untuk gips C ++-style yang lebih eksplisit (dan dapat dicari!) (Misalnya, static_cast).

  • Terakhir, ketahuilah bagaimana aturan tiga:

    • Destructor
    • Salin konstruktor
    • Operator penugasan

    Sudah menjadi aturan lima dengan penambahan move constructor dan move assignment operator. Dan pahami rvalue referensi secara umum dan cara menghindari penyalinan.

C ++ adalah bahasa yang kompleks, sehingga sulit untuk menggambarkan cara terbaik untuk menggunakan semua itu. Namun praktik pengembangan C ++ yang baik belum berubah secara mendasar dengan C ++ 11. Anda masih harus lebih memilih wadah yang dikelola memori daripada manajemen memori manual — pointer pintar membuatnya mudah untuk melakukan ini secara efisien.

Saya akan mengatakan bahwa C ++ modern memang sebagian besar bebas dari manajemen memori manual - keuntungan dari model memori C ++ adalah bahwa itu deterministik , bukan manual. Alokasi yang dapat diprediksi menjadikan kinerja yang lebih dapat diprediksi.

Adapun kompiler, G ++ dan Dentang keduanya kompetitif dalam hal fitur C ++ 11, dan dengan cepat mengejar kekurangan mereka. Saya tidak menggunakan Visual Studio, jadi saya tidak bisa berbicara atau menentangnya.

Akhirnya, catatan tentang std::for_each: hindari secara umum.

transform, accumulate, Dan erase- remove_ifbaik tua fungsional map, folddan filter. Tetapi for_eachlebih umum, dan karena itu kurang bermakna — itu tidak mengungkapkan maksud apa pun selain perulangan. Selain itu, ini digunakan dalam situasi yang sama dengan range-based for, dan secara sintaksis lebih berat, bahkan ketika digunakan point-free. Mempertimbangkan:

for (const auto i : container)
    std::cout << i << '\n';

std::for_each(container.begin(), container.end(), [](int i) {
    std::cout << i << '\n';
});

for (const auto i : container)
    frobnicate(i);

std::for_each(container.begin(), container.end(), frobnicate);
Jon Purdy
sumber
6
Apakah ada beberapa prinsip yang mengikat pada aturan praktis ini? Tampaknya seperti daftar saran yang bagus, tapi ... Sebagai orang luar yang datang ke pertanyaan ini melalui Google, bagaimana jawaban Anda membantu saya mengambil C ++ 11 dengan cara berprinsip, dan menggunakannya tanpa membungkus diri saya di sekitar poros C ++ ?
Robert Harvey
3
+1 untuk daftar, tetapi saya memiliki nitpick kecil: ketika (benar) peringatan terhadap std::for_eachsaya akan mengharapkan rentang berdasarkan untuk loop sebagai pengganti yang lebih baik daripada biasa for.
Fabio Fracassi
@FabioFracassi: Ups. Saya menulis dengan jelas untuk kontras std::for_each, bukan dengan berbasis rentang . Saya telah menghapusnya untuk menghindari kebingungan.
Jon Purdy
1
Saya akan memperbarui jika bukan karena std::for_each(). Ketika Anda memiliki lambda, itu tentu lebih baik daripada forloop tradisional . Dengan forloop berbasis rentang yang mungkin tidak terjadi, tetapi Anda tidak menulis " forloop berbasis rentang ".
sbi
@ sbi: Dalam pikiran saya, istilah " forloop" termasuk " forloop berbasis rentang ". Saya telah mengedit dengan lebih banyak penjelasan dan contoh untuk menjelaskan, terima kasih.
Jon Purdy
12

Sebagai titik awal:

  • Hentikan penggunaan char*untuk string. Gunakan std::stringatau std::wstringdan tonton saja kode Anda menjadi lebih pendek, lebih mudah dibaca, dan lebih aman
  • Berhentilah menggunakan array gaya-C (hal-hal yang dideklarasikan dengan [ ]) dan gunakan std::vectoratau kelas kontainer lain yang sesuai. Hal-hal baik tentang itu std::vectoradalah bahwa ia tahu panjangnya sendiri, itu membersihkan kontennya ketika keluar dari ruang lingkup, mudah untuk beralih, dan itu membuat dirinya lebih besar ketika Anda menambahkan lebih banyak item. Tetapi ada koleksi lain yang mungkin bekerja lebih baik untuk keadaan Anda.
  • Gunakan std::unique_ptr- dan pelajari std::movesegera. Karena hal ini dapat menyebabkan beberapa objek yang tidak dapat ditiru, kemalasan kadang-kadang dapat mengirim Anda ke arah std::shared_ptr- dan Anda mungkin memiliki beberapa kasus penggunaan asli std::shared_ptrjuga
  • Gunakan autosaat mendeklarasikan iterator dan tipe yang bergantung pada deklarasi sebelumnya (mis. Sebelumnya Anda mendeklarasikan vektor sesuatu, sekarang Anda mendeklarasikan sesuatu, gunakan auto)
  • Gunakan algoritma dan for_eachlebih dari "mentah untuk" kapan saja Anda bisa karena itu membuat orang lain dari membaca loop Anda dengan hati-hati untuk menyimpulkan bahwa Anda sebenarnya mengulangi seluruh koleksi dll. Jika kompiler Anda mendukung "rentang untuk" maka gunakan itu for_each. Pelajari panggilan algoritma sepele seperti iota, generate, accumulate, find_ifdan sebagainya.
  • Gunakan lambdas - mereka adalah cara mudah untuk meningkatkan algoritma. Mereka juga membuka pintu untuk lebih banyak lagi.

Jangan terlalu sibuk dengan apa yang digunakan kompiler. Kurangnya "mengerikan, mengerikan" dukungan C ++ 11 di VS2012 adalah bahwa tidak ada template variadic (ya benar, Anda baru saja akan menggunakan template variadic) dan {}initializer tidak ada di sana. Saya ingin itu juga, tetapi saya hampir tidak akan berhenti menggunakan alat pengembangan yang berguna di atasnya.

Hal kedua yang harus dilakukan, setelah merangkul std::, adalah mulai berpikir RAII. Kapan saja Anda miliki

  • memulai aksi
  • serangkaian tindakan dengan sesuatu yang Anda dapatkan dari memulai aksi
  • tindakan pembersihan yang perlu terjadi bahkan dalam kasus pengecualian

Maka yang Anda miliki adalah konstruktor, sejumlah fungsi anggota, dan destruktor. Tulis kelas yang menangani itu untuk Anda. Anda bahkan mungkin tidak perlu menulis ctor dan dtor. Menempatkan shared_ptrsebagai variabel anggota kelas adalah contoh RAII - Anda tidak menulis kode manajemen memori, tetapi ketika instance Anda keluar dari ruang lingkup, hal-hal yang benar akan terjadi. Perluas pemikiran itu untuk mencakup hal-hal seperti menutup file, melepaskan pegangan, kunci dll dan kode hanya akan menjadi lebih sederhana dan lebih kecil (sambil menghilangkan kebocoran) di depan mata Anda.

Jika Anda merasa benar-benar percaya diri, pembersihan printfmendukung cout, menyingkirkan macro ( #definebarang-barang), dan mulai mempelajari beberapa "idiom canggih" seperti PIMPL. Saya memiliki seluruh kursus tentang ini di Pluralsight yang mungkin dapat Anda tonton menggunakan uji coba gratis mereka.

Kate Gregory
sumber
2
Saya suka bagaimana Anda menyindir dia tidak menggunakan template variadic dalam waktu dekat, tapi saya pikir inisialisasi seragam yang hilang adalah sesuatu yang sangat penting untuk pemrograman sehari-hari.
sbi
Tidak bisa menunggu daftar penginisialisasi ... menunggu untuk mengetahui kapan kami akan mendapatkannya ...
Kate Gregory
Kekurangan penting lainnya dalam VS2012 adalah "rvalue Reference v3", yaitu konstruktor pemindahan yang otomatis dan pemindahan pemindahan.
Mr.C64
3

Bagaimana cara terbaik saya belajar memanfaatkan fitur-fitur bahasa modern ini dan mana yang cocok untuk saat-saat mana?

Dengan pemrograman. Pengalaman adalah cara terbaik untuk belajar.

C ++ 11 memiliki banyak fitur baru (otomatis, nilai, pointer pintar baru - hanya untuk beberapa nama). Awal yang terbaik adalah mulai menggunakannya, dan bacalah kapan pun Anda bisa, dan kapan pun Anda menemukan artikel yang menarik.

Benarkah rekayasa perangkat lunak dalam C ++ saat ini sebagian besar bebas dari manajemen memori manual?

Itu tergantung apa yang perlu Anda lakukan. Sebagian besar aplikasi dapat lolos dengan pointer cerdas, dan melupakan manajemen memori. Masih ada aplikasi yang tidak dapat lolos dengan mudah (misalnya jika mereka membutuhkan penempatan baru, atau pengalokasi memori khusus untuk alasan apa pun).

Jika Anda perlu menggunakan Qt, Anda harus menggunakan aturan mereka untuk manajemen memori.

kompiler mana yang harus saya gunakan untuk memaksimalkan standar baru?

Apa pun yang Anda miliki yang mendukung standar terbaru:

tetapi tidak ada kompiler yang mendukung semua fitur.

BЈовић
sumber
-7

Universitas saya masih menggunakan C ++ untuk mengajar. Saya telah memprogram dengan C ++ selama 5 tahun dan sekarang saya adalah mahasiswa pascasarjana. Tentu saja, sekarang saya menggunakan banyak Java, Ruby dll. Saya sangat menyarankan Anda jangan terlalu terburu-buru tentang fitur-fitur dalam bahasa seperti Java. Dalam pengalaman dan pendapat saya, setelah fitur tingkat rendah C ++. Anda harus melihat ke topik seperti pemrograman umum dengan C / C ++ seperti membuat kelas template, fungsi template, operator ditulis ulang, metode virtual, pointer pintar. Untuk Bagian Desain Berorientasi Objek, ada banyak fitur yang dimiliki C ++ dan Java tidak, seperti multi-inheritance. Warisan sangat kuat tetapi juga berbahaya. Level implementasi desain berorientasi objek dalam C ++ juga merupakan topik yang bagus. Komunikasi antar proses, utas, juga penting dalam C ++.

Untuk kompiler dan debugger. Saya tahu visual studio itu luar biasa. Tapi saya sangat menyarankan Anda mempelajari GDB, Valgrind, dan Diam, dan jagokan alat ini. Microsoft memang luar biasa, tetapi itu melakukan terlalu banyak hal untuk Anda. Sebagai siswa, Anda benar-benar perlu mempelajari hal-hal yang Microsoft lakukan juga terhadap Anda. Untuk kompiler, G ++ bagus dari GNU.

Lagi pula, setelah bertahun-tahun, saya benar-benar merasa, hal yang paling penting adalah fitur tingkat rendah seperti array mentah. Vektor sebenarnya hanya array dinamis. Ini adalah rekomendasi saya, sesuatu yang mungkin terlalu subjektif, hanya mengambil apa yang Anda anggap benar.

Sen Han
sumber
6
Bagaimana ini menjawab pertanyaan? Pertanyaannya bukan tentang belajar C ++ secara umum, tetapi beralih ke C ++ 11.
Roc Martí