Apa tujuan dari menggunakan kawat gigi (yaitu {}) untuk satu baris jika atau loop?


321

Saya membaca beberapa catatan kuliah dosen C ++ saya dan dia menulis yang berikut:

  1. Gunakan Indentasi // OK
  2. Jangan pernah mengandalkan prioritas operator - Selalu gunakan tanda kurung // OK
  3. Selalu gunakan blok {} - bahkan untuk satu baris // tidak OK , kenapa ???
  4. Objek Const di sisi kiri perbandingan // OK
  5. Gunakan unsigned untuk variabel yang>> 0 // trik yang bagus
  6. Setel Pointer ke NULL setelah penghapusan - Proteksi penghapusan ganda // tidak buruk

Teknik ke-3 tidak jelas bagi saya: apa yang akan saya dapatkan dengan menempatkan satu baris dalam { ... }?

Misalnya, ambil kode aneh ini:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

dan ganti dengan:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;

Apa manfaat menggunakan versi 1?


256
Keterbacaan dan pemeliharaan. Tidak jelas dengan segera apa blok pernyataan 'j ++', dan menambahkan kode setelah itu tidak akan dikaitkan dengan pernyataan if.
Hutan Dan Pohon

27
Saya selalu disuruh menggunakan kurung kurawal {} untuk baris ini karena beberapa alasan. Itu membuat kode lebih jelas untuk dibaca. Juga orang lain dalam waktu enam bulan mungkin perlu mengedit kode Anda sehingga kejelasan itu penting dan dengan kawat gigi ada kesalahan cenderung terjadi. Tidak ada yang secara teknis lebih benar tentang hal itu, ini lebih dari sekadar praktik yang baik. Ingatlah bahwa suatu proyek mungkin memiliki ribuan dan ribuan baris kode untuk dijelajahi oleh orang baru!
RossC

30
Saya tidak setuju dengan 6, karena akan menyembunyikan penghapusan ganda dan berpotensi menyembunyikan kesalahan logika.
Luchian Grigore

28
# 5 mungkin rumit - menganggap lingkaran ini: for (unsigned i = 100; i >= 0; --i).
Archie

36
Btw, (i % 2 == 0)bertentangan (2). Anda mengandalkan prioritas operator, dan artinya tentu saja ((i % 2) == 0)bukan (i % (2 == 0)). Saya akan mengklasifikasikan aturan 2 sebagai "sentimen yang valid tetapi 'selalu' salah".
Steve Jessop

Jawaban:


509

Mari kita coba juga memodifikasi iketika kita menambah j:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
        i++;

Oh tidak! Berasal dari Python, ini terlihat ok, tetapi kenyataannya tidak, karena ini setara dengan:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

Tentu saja, ini adalah kesalahan konyol, tetapi yang bahkan bisa dilakukan oleh programmer berpengalaman.

Alasan lain yang sangat bagus ditunjukkan dalam jawaban ta.speot.is .

Yang ketiga yang bisa saya pikirkan adalah bersarang if:

if (cond1)
   if (cond2) 
      doSomething();

Sekarang, anggap Anda sekarang ingin doSomethingElse()ketika cond1tidak terpenuhi (fitur baru). Begitu:

if (cond1)
   if (cond2) 
      doSomething();
else
   doSomethingElse();

yang jelas salah, karena elsebergaul dengan batin if.


Sunting: Karena ini mendapat perhatian, saya akan mengklarifikasi pandangan saya. Pertanyaan yang saya jawab adalah:

Apa manfaat menggunakan versi 1?

Yang telah saya jelaskan. Ada beberapa manfaatnya. Tapi, IMO, aturan "selalu" tidak selalu berlaku. Jadi saya tidak sepenuhnya mendukung

Selalu gunakan blok {} - bahkan untuk satu baris // tidak OK, kenapa ???

Saya tidak mengatakan selalu menggunakan {}blok. Jika itu kondisi & perilaku yang cukup sederhana, jangan. Jika Anda mencurigai seseorang akan datang kemudian & mengubah kode Anda untuk menambah fungsionalitas, lakukan.


5
@Science_Fiction: Benar, tetapi jika Anda menambahkan i++ sebelumnya j++ , maka kedua variabel akan tetap berada di ruang lingkup ketika mereka digunakan.
Mike Seymour

26
Ini terdengar sangat masuk akal, tetapi mengabaikan fakta bahwa editor melakukan lekukan, bukan Anda, dan itu akan membuat inden dengan i++;cara yang segera menunjukkan bahwa itu bukan bagian dari loop. (Di masa lalu, ini mungkin argumen yang masuk akal, dan saya telah melihat masalah seperti itu. Sekitar 20 tahun yang lalu. Tidak sejak itu.)
James Kanze

43
@Ames: itu bukan "fakta", meskipun, itu alur kerja Anda. Dan alur kerja banyak orang, tetapi tidak semua orang. Saya tidak berpikir itu selalu kesalahan untuk memperlakukan sumber C ++ sebagai file teks biasa, daripada output editor WYSIWYG (vi / emacs / Visual Studio) yang memberlakukan aturan pemformatan. Jadi aturan ini adalah editor-agnostik di luar apa yang Anda butuhkan, tetapi tidak melampaui apa yang sebenarnya digunakan orang untuk mengedit C ++. Karenanya "defensif".
Steve Jessop

31
@JamesKanze Apakah Anda benar-benar mengandalkan asumsi bahwa setiap orang selalu bekerja dalam IDE yang kuat? CI terakhir yang ditulis ada di Nano. Bahkan mengingat itu, salah satu hal pertama yang saya cenderung mematikan dalam sebuah IDE adalah auto-indentation - karena IDE cenderung menghalangi alur kerja non-linear saya , mencoba untuk memperbaiki 'kesalahan' saya berdasarkan informasi yang tidak lengkap . IDE tidak terlalu bagus dalam mengindentifikasi secara otomatis aliran alami setiap programmer. Para programmer yang menggunakan fitur-fitur tersebut cenderung untuk menggabungkan gaya mereka ke IDE mereka, yang baik jika Anda hanya menggunakan satu IDE tetapi tidak banyak jika Anda bekerja di banyak.
Rushyo

10
"Ini adalah kesalahan konyol, tetapi yang bahkan bisa dilakukan oleh programmer yang berpengalaman." - Seperti yang saya katakan dalam jawaban saya, saya tidak percaya itu. Saya pikir ini adalah kasus yang sepenuhnya dibuat-buat yang tidak menimbulkan masalah dalam kenyataan.
Konrad Rudolph

324

Sangat mudah untuk secara tidak sengaja mengubah aliran kontrol dengan komentar jika Anda tidak menggunakan {dan }. Sebagai contoh:

if (condition)
  do_something();
else
  do_something_else();

must_always_do_this();

Jika Anda berkomentar do_something_else()dengan komentar satu baris, Anda akan berakhir dengan ini:

if (condition)
  do_something();
else
  //do_something_else();

must_always_do_this();

Itu mengkompilasi, tetapi must_always_do_this()tidak selalu disebut.

Kami memiliki masalah ini di basis kode kami, tempat seseorang masuk untuk menonaktifkan beberapa fungsi dengan sangat cepat sebelum dirilis. Untungnya kami menangkapnya dalam ulasan kode.


3
Astaga !! itu didefinisikan perilaku yang must_always_do_this();akan dieksekusi jika Anda berkomentar // do_something_else ();
Mr.Anubis

1
@Supr, seperti yang pertama kali ditulis, dia mengatakan sulit untuk memutuskan aliran yang benar jika Anda menggunakan kawat gigi, dan kemudian memberikan contoh betapa mudahnya memecahnya tanpa kode dikurung dengan benar
SeanC

20
Saya mengalami hal ini beberapa hari yang lalu. if(debug) \n //print(info);. Pada dasarnya mengeluarkan seluruh perpustakaan.
Kevin

4
Fortunately we caught it in code review.Aduh! Kedengarannya salah. Fortunately we caught it in unit tests.akan jauh lebih baik!
BЈовић

4
@ BЈовић Tapi bagaimana jika kode itu dalam unit test Pikiran mengejutkan. (Hanya bercanda, ini adalah aplikasi warisan. Tidak ada tes unit.)
ta.speot.is

59

Saya memiliki keraguan terhadap kompetensi dosen. Mempertimbangkan poinnya:

  1. baik
  2. Adakah yang benar-benar menulis (atau ingin membaca) (b*b) - ((4*a)*c)? Beberapa prioritas sudah jelas (atau seharusnya), dan kurung tambahan hanya menambah kebingungan. (Di sisi lain, Anda _harus_ menggunakan tanda kurung dalam kasus yang kurang jelas, bahkan jika Anda tahu bahwa mereka tidak diperlukan.)
  3. Semacam. Ada dua konvensi penyebaran luas untuk memformat kondisional dan loop:
    if (cond) {
        kode;
    }
    
    dan:
    jika (cond)
    {
        kode;
    }
    
    Yang pertama, saya setuju dengannya. Pembukaannya {tidak terlalu terlihat, jadi sebaiknya Anda menganggapnya selalu ada. Namun, pada bagian kedua, saya (dan sebagian besar orang yang bekerja sama dengan saya) tidak memiliki masalah dengan menghilangkan kawat gigi untuk satu pernyataan. (Asalkan, tentu saja, bahwa lekukan itu sistematis dan Anda menggunakan gaya ini secara konsisten. (Dan banyak programmer yang sangat bagus, menulis kode yang sangat mudah dibaca, menghilangkan kawat gigi bahkan ketika memformat cara pertama.)
  4. TIDAK . Hal-hal seperti if ( NULL == ptr )itu cukup jelek untuk menghalangi keterbacaan. Tulis perbandingan secara intuitif. (Yang dalam banyak kasus menghasilkan konstanta di sebelah kanan.) 4 Nya adalah nasihat yang buruk; apa pun yang membuat kode itu tidak alami membuatnya kurang mudah dibaca.
  5. TIDAK . Apa pun kecuali intdicadangkan untuk kasus khusus. Untuk programmer C dan C ++ yang berpengalaman, gunakan unsignedoperator sinyal bit. C ++ tidak memiliki tipe kardinal nyata (atau tipe subrange efektif lainnya); unsignedtidak berfungsi untuk nilai numerik, karena aturan promosi. Nilai numerik yang tidak memungkinkan operasi aritmatika masuk akal, seperti nomor seri, mungkin bisa terjadi unsigned. Saya membantahnya, karena mengirimkan pesan yang salah: operasi bitwise juga tidak masuk akal. Aturan dasarnya adalah tipe integral adalah int_unless_, ada alasan signifikan untuk menggunakan tipe lain.
  6. TIDAK . Melakukan ini secara sistematis menyesatkan, dan sebenarnya tidak melindungi terhadap apa pun. Dalam kode OO yang ketat, delete this;seringkali merupakan kasus yang paling sering (dan Anda tidak dapat mengatur thiske NULL), dan sebaliknya, sebagian deletebesar dalam destruktor, sehingga Anda tidak dapat mengakses pointer nanti. Dan mengaturnya agar NULLtidak melakukan apa-apa tentang pointer lain yang beredar. Mengatur pointer secara sistematis untuk NULLmemberikan rasa aman palsu, dan tidak benar-benar membelikan Anda apa pun.

Lihatlah kode di salah satu referensi umum. Stroustrup melanggar setiap aturan yang Anda berikan kecuali yang pertama, misalnya.

Saya sarankan Anda mencari dosen lain. Orang yang benar-benar tahu apa yang dia bicarakan.


13
Angka 4 mungkin jelek namun ada tujuan untuk itu. Ia mencoba mencegah if (ptr = NULL). Saya rasa saya tidak pernah menggunakan delete this, apakah ini lebih umum daripada yang saya lihat? Saya tidak cenderung berpikir menetapkan pointer ke NULL setelah digunakan adalah hal yang buruk untuk dilakukan selain YMMV. Mungkin hanya saya, tetapi sebagian besar pedomannya tidak terlalu buruk.
Firedragon

16
@Firedragon: Kebanyakan kompiler akan memperingatkan tentang if (ptr = NULL)kecuali Anda menuliskannya sebagai if ((ptr = NULL)). Harus setuju dengan James Kanze bahwa keburukan memiliki yang NULLpertama membuatnya menjadi TIDAK yang pasti bagi saya.
Leo

34
@JamesKanze: Saya harus mengatakan bahwa saya tidak setuju dengan sebagian besar dari apa yang telah Anda nyatakan di sini - meskipun saya menghargai dan menghargai argumen Anda karena telah sampai pada mereka. Untuk programmer C dan C ++ yang berpengalaman, penggunaan sinyal yang tidak ditandatangani menggigit operator. - Saya tidak setuju sama sekali: Penggunaan operator bit menandakan penggunaan operator bit. Bagi saya, penggunaan unsignedmengindikasikan aspirasi pada bagian programmer bahwa variabel hanya mewakili angka positif. Pencampuran dengan nomor yang ditandatangani biasanya akan menyebabkan peringatan kompiler yang mungkin apa yang diinginkan dosen.
Komponen 10

18
Untuk programmer C dan C ++ yang berpengalaman, penggunaan sinyal yang tidak ditandatangani menggigit operator Atau tidak. size_tsiapa saja?
ta.speot.is

16
@ James Kanze, pertimbangkan tujuannya. Anda membandingkan kode yang dihasilkan oleh programmer yang berpengalaman dengan contoh instruksional. Aturan-aturan ini disediakan oleh dosen karena itu adalah jenis kesalahan yang dia lihat dilakukan oleh mahasiswanya. Dengan pengalaman, para siswa dapat bersantai atau mengabaikan hal-hal yang absolut ini.
Joshua Shane Liberman

46

Semua jawaban lain mempertahankan aturan dosen Anda 3.

Izinkan saya mengatakan bahwa saya setuju dengan Anda: aturannya berlebihan dan saya tidak akan menyarankannya. Memang benar bahwa secara teoritis mencegah kesalahan jika Anda selalu menambahkan kurung keriting. Di sisi lain, saya tidak pernah mengalami masalah ini dalam kehidupan nyata : bertentangan dengan apa yang dinyatakan oleh jawaban lain, saya tidak pernah lupa menambahkan tanda kurung keriting begitu diperlukan. Jika Anda menggunakan indentasi yang tepat, segera menjadi jelas bahwa Anda perlu menambahkan kurung keriting sekali lebih dari satu pernyataan yang indentasi.

Jawaban oleh "Komponen 10" sebenarnya menyoroti satu-satunya kasus yang mungkin di mana ini benar-benar dapat menyebabkan kesalahan. Tetapi di sisi lain, mengganti kode melalui ekspresi reguler selalu membutuhkan perhatian besar.

Sekarang mari kita lihat sisi lain dari medali: adakah kerugian untuk selalu menggunakan kurung keriting? Jawaban lain cukup mengabaikan hal ini. Tapi ada adalah kerugian: tidak memakan banyak ruang layar vertikal, dan ini pada gilirannya dapat membuat kode Anda terbaca karena itu berarti Anda harus gulir lebih dari yang diperlukan.

Pertimbangkan fungsi dengan banyak klausa penjaga di awal (dan ya, berikut ini adalah kode C ++ yang buruk tetapi dalam bahasa lain ini akan menjadi situasi yang cukup umum):

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
    {
        throw null_ptr_error("a");
    }
    if (b == nullptr)
    {
        throw null_ptr_error("b");
    }
    if (a == b)
    {
        throw logic_error("Cannot do method on identical objects");
    }
    if (not a->precondition_met())
    {
        throw logic_error("Precondition for a not met");
    }

    a->do_something_with(b);
}

Ini adalah kode yang mengerikan, dan saya sangat berpendapat bahwa yang berikut ini jauh lebih mudah dibaca:

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
        throw null_ptr_error("a");
    if (b == nullptr)
        throw null_ptr_error("b");
    if (a == b)
        throw logic_error("Cannot do method on identical objects");
    if (not a->precondition_met())
        throw logic_error("Precondition for a not met");

    a->do_something_with(b);
}

Demikian pula, loop bersarang pendek manfaat dari menghilangkan kurung keriting:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
        for (auto j = 0; j < a.h(); ++j)
            c(i, j) = a(i, j) + b(i, j);

    return c;
}

Dibandingkan dengan:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
    {
        for (auto j = 0; j < a.h(); ++j)
        {
            c(i, j) = a(i, j) + b(i, j);
        }
    }

    return c;
}

Kode pertama adalah ringkas; kode kedua membengkak.

Dan ya, ini dapat dikurangi sampai batas tertentu dengan menempatkan brace pembuka pada baris sebelumnya. Tapi itu masih kurang dibaca daripada kode tanpa tanda kurung keriting.

Singkatnya: jangan menulis kode yang tidak perlu yang memakan ruang layar.


26
Jika Anda tidak percaya pada penulisan kode yang menghabiskan ruang layar secara tidak perlu, maka Anda tidak perlu menempatkan brace pembuka di barisnya sendiri. Saya mungkin sekarang harus merunduk dan lari dari pembalasan suci GNU, tetapi serius - Anda ingin kode Anda menjadi kompak secara vertikal, atau tidak. Dan jika Anda melakukannya, jangan lakukan hal-hal yang dirancang hanya untuk membuat kode Anda kurang kompak secara vertikal. Tapi seperti yang Anda katakan, setelah tetap bahwa, Anda masih juga ingin menghapus kawat gigi berlebihan. Atau mungkin hanya menulis if (a == nullptr) { throw null_ptr_error("a"); }sebagai satu baris.
Steve Jessop

6
@ Steve Sebagai soal fakta, saya tidak menaruh penjepit pembukaan pada baris sebelumnya, untuk alasan Anda menyatakan. Saya menggunakan gaya lain di sini untuk memperjelas betapa ekstrim perbedaan itu.
Konrad Rudolph

9
+1 Saya sepenuhnya setuju bahwa contoh pertama Anda jauh lebih mudah dibaca tanpa kawat gigi. Pada contoh kedua, gaya pengkodean pribadi saya adalah dengan menggunakan kawat gigi di luar untuk-loop dan bukan di dalam. Saya tidak setuju dengan @SteveJessop karena harus menjadi salah satu ekstrim atau yang lain tentang kode kompak vertikal. Saya menghilangkan kawat gigi ekstra dengan one-liner untuk mengurangi ruang vertikal, tetapi saya memang memasang kawat gigi pembuka pada baris baru karena saya merasa lebih mudah untuk melihat ruang lingkup ketika kawat gigi dilapisi. Tujuannya adalah keterbacaan, dan terkadang itu berarti menggunakan lebih banyak ruang vertikal, di lain waktu itu berarti menggunakan lebih sedikit.
Travesty3

22
"Saya belum pernah mengalami masalah ini di kehidupan nyata": beruntung Anda. Hal-hal seperti ini tidak hanya membakar Anda, mereka memberi Anda luka bakar tingkat ketiga 90% (dan itu hanya beberapa lapis manajemen yang menuntut perbaikan di malam hari).
Richard

9
@Richard, saya tidak membelinya. Seperti yang telah saya jelaskan dalam obrolan, meskipun kesalahan ini seharusnya terjadi (yang saya rasa tidak mungkin) itu sepele untuk diperbaiki setelah Anda melihat jejak stack karena jelas di mana kesalahan tersebut hanya dengan melihat kode. Klaim Anda yang berlebihan benar-benar tidak berdasar.
Konrad Rudolph

40

Basis kode yang saya kerjakan tersebar dengan kode oleh orang-orang dengan keengganan patologis terhadap kawat gigi, dan bagi orang-orang yang datang kemudian, itu benar-benar dapat membuat perbedaan untuk pemeliharaan.

Contoh bermasalah yang paling sering saya temui adalah ini:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
    this_looks_like_a_then-statement_but_isn't;

Jadi ketika saya datang dan ingin menambahkan pernyataan saat itu, saya dapat dengan mudah berakhir dengan ini jika saya tidak hati-hati:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
    this_looks_like_a_then-statement_but_isn't;
    i_want_this_to_be_a_then-statement_but_it's_not;
}

Mengingat bahwa diperlukan ~ 1 detik untuk menambahkan kawat gigi dan dapat menghemat setidaknya beberapa menit debugging yang membingungkan, mengapa Anda tidak akan menggunakan opsi ambiguitas yang dikurangi? Sepertinya ekonomi palsu bagi saya.


16
Bukankah masalah dalam contoh ini di lekukan yang tidak tepat dan garis terlalu panjang daripada kawat gigi?
Honza Brabec

7
Ya, tetapi mengikuti pedoman desain / pengkodean yang hanya 'aman' dengan asumsi bahwa orang juga mengikuti pedoman lain (seperti tidak memiliki garis yang terlalu panjang) tampaknya meminta masalah. Jika kawat gigi sudah ada sejak awal, tidak mungkin berakhir dengan blok if yang salah dalam situasi ini.
selai

16
Bagaimana menambahkan kawat gigi (membuatnya if(really long...editor){ do_foo;}membantu Anda menghindari kasus ini? Sepertinya masalahnya masih akan sama. Secara pribadi saya lebih suka menghindari kawat gigi ketika tidak perlu, namun itu tidak ada hubungannya dengan waktu yang dibutuhkan untuk menulisnya tetapi mengurangi keterbacaan karena dua baris tambahan dalam kode
Grizzly

1
Poin bagus - saya berasumsi bahwa menegakkan penggunaan kawat gigi juga akan menyebabkan mereka ditempatkan di tempat yang masuk akal, tetapi tentu saja seseorang yang bertekad untuk membuat hal-hal yang sulit dapat menempatkan mereka sejalan seperti pada contoh Anda. Saya membayangkan kebanyakan orang tidak akan melakukannya.
jam

2
Hal pertama dan terakhir yang saya lakukan ketika menyentuh file adalah menekan tombol format otomatis. Ini menghilangkan sebagian besar masalah ini.
Jonathan Allen

20

2c saya:

Gunakan Indentasi

Jelas sekali

Jangan pernah mengandalkan prioritas operator - Selalu gunakan tanda kurung

Saya tidak akan menggunakan kata "tidak pernah dan" selalu ", tetapi secara umum saya melihat aturan ini berguna. Dalam beberapa bahasa (Lisp, Smalltalk) ini bukan masalah.

Selalu gunakan blok {} - bahkan untuk satu baris

Saya tidak pernah melakukan itu dan tidak pernah memiliki masalah tunggal, tetapi saya bisa melihat bagaimana itu bisa baik bagi siswa, khususnya. jika mereka mempelajari Python sebelumnya.

Objek Const di sebelah kiri perbandingan

Kondisi yoda? Tidak, kumohon. Itu menyakitkan keterbacaan. Cukup gunakan level peringatan maksimum ketika Anda mengkompilasi kode Anda.

Gunakan unsigned untuk variabel yang>> 0

BAIK. Cukup lucu, saya pernah mendengar Stroustrup tidak setuju.

Setel Pointer ke NULL setelah penghapusan - Proteksi penghapusan ganda

Saran yang buruk! Tidak pernah memiliki pointer yang menunjuk ke objek yang dihapus atau tidak ada.


5
+1 hanya untuk poin terakhir saja. Pointer mentah tidak memiliki bisnis yang memiliki memori.
Konrad Rudolph

2
Sehubungan dengan menggunakan unsigned: tidak hanya Stroustrup, tetapi K&R (dalam C), Herb Sutter dan (saya pikir) Scott Meyers. Bahkan, saya belum pernah mendengar orang yang benar-benar memahami aturan C ++ berdebat untuk menggunakan unsigned.
James Kanze

2
@JamesKanze Bahkan, pada kesempatan yang sama saya mendengar pendapat Stroustrup (konferensi Boston pada 2008), Herb Sutter ada di sana dan tidak setuju dengan Bjarne di tempat.
Nemanja Trifunovic

3
Hanya untuk menyelesaikan " unsignedrusak", salah satu masalahnya adalah ketika C ++ membandingkan jenis yang ditandatangani dan tidak ditandatangani yang berukuran sama, itu dikonversi ke yang tidak ditandatangani sebelum melakukan perbandingan. Yang menghasilkan perubahan nilai. Mengubah ke yang sudah ditandatangani tidak selalu jauh lebih baik; perbandingan harus benar-benar terjadi "seolah-olah" kedua nilai dikonversi ke tipe yang lebih besar yang dapat mewakili semua nilai dalam kedua jenis.
James Kanze

1
@SteveJessop Saya pikir Anda harus menerimanya dalam konteks fungsi yang kembali unsigned. Saya yakin dia tidak memiliki masalah dengan exp(double)mengembalikan nilai lebih dari MAX_INT:-). Tetapi sekali lagi, masalah sebenarnya adalah konversi implisit. int i = exp( 1e6 );benar-benar valid C ++. Stroustrup sebenarnya mengusulkan penghentian konversi implisit lossy pada satu titik, tetapi komite tidak tertarik. (Sebuah pertanyaan menarik: apakah unsigned-> intdianggap lossy. Saya akan mempertimbangkan keduanya unsigned-> intdan int-> unsignedlossy. Yang mana akan membuat unsignedOK
James Kanze

18

itu lebih intuitif dan mudah dimengerti. Itu membuat maksudnya jelas.

Dan itu memastikan bahwa kode tidak rusak ketika pengguna baru mungkin secara tidak sadar melewatkannya {, }sambil menambahkan pernyataan kode baru.


Makes the intent clear+1, ini mungkin alasan paling ringkas dan akurat.
Qix - MONICA DISALAHKAN

13

Untuk menambah saran yang sangat masuk akal di atas, satu contoh yang saya temui saat refactoring beberapa kode di mana ini menjadi kritis adalah sebagai berikut: Saya mengubah basis kode yang sangat besar untuk beralih dari satu API ke yang lain. API pertama memiliki panggilan untuk menetapkan Id Perusahaan sebagai berikut:

setCompIds( const std::string& compId, const std::string& compSubId );

sedangkan penggantian membutuhkan dua panggilan:

setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );

Saya mulai mengubah ini menggunakan ekspresi reguler yang sangat sukses. Kami juga melewati kode melalui astyle , yang membuatnya sangat mudah dibaca. Kemudian, sebagian dari proses peninjauan, saya menemukan bahwa dalam beberapa kondisi bersyarat itu mengubah ini:

if ( condition )
   setCompIds( compId, compSubId );

Untuk ini:

if ( condition )
   setCompId( compId );
setCompSubId( compSubId );

yang jelas bukan apa yang dibutuhkan. Saya harus kembali ke awal melakukan ini lagi dengan memperlakukan penggantian sebagai sepenuhnya dalam satu blok dan kemudian secara manual mengubah apa pun yang akhirnya tampak konyol (setidaknya itu tidak akan salah.)

Saya perhatikan bahwa astyle sekarang memiliki opsi --add-bracketsyang memungkinkan Anda untuk menambahkan tanda kurung di mana tidak ada tanda kurung dan saya sangat merekomendasikan ini jika Anda pernah berada di posisi yang sama dengan saya.


5
Saya pernah melihat beberapa dokumentasi yang memiliki koin indah "Microsoftligent". Ya, dimungkinkan untuk membuat kesalahan signifikan dengan pencarian dan penggantian global. Itu hanya berarti bahwa pencarian dan penggantian global harus digunakan secara cerdas, bukan secara elektronik.
Pete Becker

4
Saya tahu ini bukan post-mortem untuk saya lakukan, tetapi jika Anda akan melakukan penggantian teks pada kode sumber maka Anda harus melakukannya sesuai dengan aturan yang sama yang akan Anda gunakan untuk jenis penggantian teks yang dibuat dengan baik dalam bahasa: macro. Anda seharusnya tidak menulis makro #define FOO() func1(); \ func2(); (dengan linebreak setelah garis miring terbalik), hal yang sama berlaku untuk pencarian dan penggantian. Yang mengatakan, saya telah melihat "selalu menggunakan kawat gigi" maju sebagai aturan gaya justru karena itu menyelamatkan Anda dari membungkus semua makro multi-pernyataan Anda do .. while(0). Tapi saya tidak setuju.
Steve Jessop

2
Btw, itu "mapan" dalam arti bahwa knotweed Jepang mapan: Saya tidak mengatakan kita harus pergi keluar dari cara kita untuk menggunakan makro dan penggantian teks, tapi saya mengatakan bahwa ketika kita melakukan hal seperti itu hal, kita harus melakukannya dengan cara yang bekerja, daripada melakukan sesuatu yang hanya bekerja jika aturan gaya tertentu berhasil dikenakan pada seluruh basis kode :-)
Steve Jessop

1
@SteveJessop One juga bisa berdebat untuk kawat gigi dan ikat pinggang. Jika Anda harus menggunakan makro seperti itu (dan kami melakukannya, sebelum C ++ dan inline), maka Anda mungkin harus bertujuan agar mereka bekerja sebanyak mungkin fungsi, menggunakan do { ... } while(0)trik jika perlu (dan banyak tanda kurung tambahan. Tapi itu masih tidak akan tidak akan menghentikan Anda dari menggunakan kawat gigi di mana-mana, jika itu adalah gaya rumah (FWIW: Saya telah bekerja di tempat-tempat dengan gaya rumah yang berbeda-beda, mencakup semua gaya yang dibahas di sini. Saya tidak pernah menemukan ada yang menjadi masalah serius.)
James Kanze

1
Dan saya rasa, semakin banyak gaya yang Anda gunakan dengan semakin cermat Anda membaca dan mengedit kode. Jadi, bahkan jika Anda memiliki preferensi apa yang paling mudah dibaca, Anda masih akan berhasil membaca yang lain. Saya bekerja di sebuah perusahaan di mana komponen yang berbeda ditulis dalam "gaya rumah" yang berbeda oleh tim yang berbeda, dan solusi yang tepat adalah mengeluh tentang hal itu di ruang makan siang tanpa efek yang besar, tidak mencoba untuk menciptakan gaya global :-)
Steve Jessop

8

Saya menggunakan di {}mana - mana kecuali beberapa kasus yang jelas. Satu baris adalah salah satu kasus:

if(condition) return; // OK

if(condition) // 
   return;    // and this is not a one-liner 

Mungkin menyakitkan Anda ketika Anda menambahkan beberapa metode sebelum kembali. Lekukan menunjukkan bahwa pengembalian sedang dieksekusi ketika kondisi terpenuhi, tetapi itu akan selalu kembali.

Contoh lain dalam C # dengan menggunakan statment

using (D d = new D())  // OK
using (C c = new C(d))
{
    c.UseLimitedResource();
}

yang setara dengan

using (D d = new D())
{
    using (C c = new C(d))
    {
        c.UseLimitedResource();
    }
}

1
Cukup gunakan koma dalam usingpernyataan dan Anda tidak perlu :)
Ry-

1
@minitech Itu tidak berfungsi di sini - Anda hanya dapat menggunakan koma ketika jenisnya sama, bukan untuk jenis yang tidak sama. Cara Lukas melakukan ini adalah cara kanonik, IDE bahkan memformatnya secara berbeda (perhatikan kurangnya lekukan otomatis yang kedua using).
Konrad Rudolph

8

Contoh paling relevan yang dapat saya pikirkan:

if(someCondition)
   if(someOtherCondition)
      DoSomething();
else
   DoSomethingElse();

Dengan yang mana ifakan elsedipasangkan? Lekukan menyiratkan bahwa bagian luar ifmendapatkan else, tetapi itu tidak benar-benar bagaimana kompiler akan melihatnya; bagian dalam if akan mendapatkan bagian luar else, dan bagian luarif tidak. Anda harus tahu itu (atau melihatnya berperilaku seperti itu dalam mode debugging) untuk mencari tahu dengan memeriksa mengapa kode ini mungkin gagal sesuai harapan Anda. Semakin membingungkan jika Anda tahu Python; dalam hal ini Anda tahu bahwa lekukan mendefinisikan blok kode, jadi Anda akan mengharapkannya untuk mengevaluasi sesuai dengan lekukan. C #, bagaimanapun, tidak memberikan flip terbang tentang spasi.

Sekarang, yang mengatakan, saya tidak terlalu setuju dengan aturan "selalu gunakan tanda kurung" di wajahnya. Itu membuat kode sangat vertikal berisik, mengurangi kemampuan untuk membacanya dengan cepat. Jika pernyataannya adalah:

if(someCondition)
   DoSomething();

... maka harus ditulis seperti ini. Pernyataan "selalu menggunakan tanda kurung" terdengar seperti "selalu mengelilingi operasi matematika dengan tanda kurung". Itu akan mengubah pernyataan yang sangat sederhana a * b + c / dmenjadi ((a * b) + (c / d)), memperkenalkan kemungkinan kehilangan orang dekat (kutukan banyak pengkode), dan untuk apa? Urutan operasi dikenal dan ditegakkan dengan baik, sehingga tanda kurung berlebihan. Anda hanya menggunakan tanda kurung untuk menjalankan urutan operasi yang berbeda dari yang biasanya diterapkan: a * (b+c) / dmisalnya. Kawat gigi blok serupa; menggunakannya untuk menentukan apa yang ingin Anda lakukan dalam kasus di mana ia berbeda dari default, dan tidak "jelas" (subyektif, tetapi biasanya cukup masuk akal).


3
@AlexBrown ... yang persis maksud saya. Aturan seperti yang tercantum dalam OP adalah "selalu menggunakan tanda kurung, bahkan untuk baris tunggal", yang saya tidak setuju dengan alasan yang saya nyatakan. Kurung akan membantu dengan contoh kode pertama, karena kode tidak akan berperilaku seperti itu indentasi; Anda harus menggunakan tanda kurung untuk memasangkan elsedengan yang pertama, ifbukan yang kedua. Silakan hapus downvote.
KeithS

5

Melihat melalui jawaban tidak ada yang secara eksplisit menyatakan jenis praktik yang saya biasakan, menceritakan kisah kode Anda:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

Menjadi:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0) j++;
}

Menempatkan j++di baris yang sama dengan jika harus memberi sinyal kepada orang lain, "Saya hanya ingin blok ini terus bertambah j" . Dari coursethis hanya bermanfaat jika garis adalah sebagai sederhana mungkin, karena menempatkan breakpoint di sini, seperti peri menyebutkan, tidak akan menjadi sangat berguna.

Sebenarnya saya baru saja menjalankan bagian dari Twitter Storm API yang memiliki 'semacam' kode ini di java, di sini adalah potongan kode dari kode eksekusi, di halaman 43 dari tayangan slide ini :

...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...

Blok loop for memiliki dua hal di dalamnya, jadi saya tidak akan inline kode itu. Yaitu tidak pernah :

int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;

Ini mengerikan dan saya bahkan tidak tahu apakah itu berfungsi (sebagaimana dimaksud); jangan lakukan ini . Baris dan kawat gigi baru membantu membedakan bagian kode yang terpisah tetapi terkait, dengan cara yang sama seperti koma atau semi-kolon dalam prosa. Blok di atas sama buruknya dengan kalimat yang sangat panjang dengan beberapa klausa dan beberapa pernyataan lain yang tidak pernah putus atau jeda untuk membedakan bagian yang terpisah.

Jika Anda benar-benar ingin mengirim telegraf ke orang lain, itu hanya pekerjaan satu jalur saja menggunakan operator atau ?:formulir ternary :

for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;

Tapi ini sedang dalam kode-golf, dan saya pikir ini bukan latihan yang bagus (Tidak jelas bagi saya apakah saya harus meletakkan j ++ di satu sisi :atau tidak). NB Saya belum menjalankan operator ternary di C ++ sebelumnya, saya tidak tahu apakah ini berfungsi, tetapi memang ada .

Pendeknya:

Bayangkan bagaimana pembaca Anda (yaitu orang yang mempertahankan kode) menafsirkan cerita Anda (kode) Buat sejelas mungkin bagi mereka. Jika Anda tahu kode pemula / siswa mempertahankan ini, mungkin bahkan meninggalkan sebanyak {}mungkin, supaya mereka tidak bingung.


1
(1) Menempatkan pernyataan pada baris yang sama membuatnya kurang mudah dibaca, tidak lebih. Terutama berpikir sederhana seperti kenaikan mudah diabaikan. Jangan menempatkan mereka di baris baru. (2) Tentu saja Anda dapat menempatkan forloop Anda pada satu baris, mengapa ini tidak berhasil? Ini bekerja karena alasan yang sama bahwa Anda dapat menghilangkan kawat gigi; baris baru tidak signifikan dalam C ++. (3) Contoh operator kondisional Anda, selain mengerikan, tidak valid C ++.
Konrad Rudolph

@KonradRudolph terima kasih, saya agak berkarat di C ++. Saya tidak pernah mengatakan (1) lebih mudah dibaca, tetapi itu menandakan bahwa potongan kode itu dimaksudkan untuk online satu baris. (2) Komentar saya adalah lebih dari itu saya tidak akan bisa membacanya dan tahu itu berhasil, baik sama sekali atau sebagaimana dimaksud; ini adalah contoh dari apa yang tidak boleh dilakukan karena alasan ini. (3) Terima kasih, saya tidak menulis C ++ dalam waktu yang lama. Saya akan memperbaikinya sekarang.
Pureferret

2
Juga menempatkan lebih dari satu ekspresi dalam satu baris membuat lebih sulit untuk men-debug kode. Bagaimana Anda menempatkan breakpoint pada ekspresi ke-2 di baris itu?
Piotr Perak

5

Karena ketika Anda memiliki dua pernyataan tanpa {}, mudah untuk melewatkan masalah. Anggap kode itu seperti ini.

int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();

if ((err = prepare_hash(hash, &hash_result))) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
    goto fail;
    goto fail;
if ((err = hash_finish(hash)) != 0)
    goto fail;

error = do_important_stuff_with(hash);

fail:
hash_free(hash);
return error;

Terlihat baik. Masalahnya sangat mudah untuk dilewatkan, terutama ketika fungsi yang mengandung kode jauh lebih besar. Masalahnya adalah yang goto faildijalankan tanpa syarat. Anda dapat dengan mudah membayangkan betapa frustrasinya ini (membuat Anda bertanya mengapa yang terakhir hash_updateselalu gagal, setelah semuanya hash_updateberfungsi dengan baik).

Namun, itu tidak berarti saya menambahkan di {}mana-mana (menurut saya, melihat di {}mana - mana itu mengganggu). Meskipun dapat menyebabkan masalah, itu tidak pernah terjadi pada proyek saya sendiri, karena gaya pengkodean pribadi saya melarang persyaratan tanpa {}ketika mereka tidak berada pada baris yang sama (ya, saya setuju bahwa gaya pengkodean saya tidak konvensional, tapi saya suka, dan saya gunakan gaya kode proyek saat berkontribusi ke proyek lain). Ini membuat kode berikut baik-baik saja.

if (something) goto fail;

Tapi bukan yang berikut.

if (something)
    goto fail;

Persis. Hanya saja, jangan letakkan indentasi + baris baru (yang sama sekali tidak perlu), dan Anda benar-benar menghindari masalah ini sehingga setiap orang selalu begitu cepat untuk memunculkannya.
Alexander - Kembalikan Monica

4

Itu membuat kode Anda lebih mudah dibaca dengan mendefinisikan dengan jelas ruang lingkup loop dan blok bersyarat Anda. Ini juga menyelamatkan Anda dari kesalahan yang tidak disengaja.


4

wrt 6: Lebih aman karena menghapus pointer nol adalah larangan. Jadi, jika Anda secara tidak sengaja melewati jalur itu dua kali, Anda tidak akan menyebabkan korupsi memori menjadi membebaskan memori yang bebas atau telah dialokasikan untuk sesuatu yang lain.

Ini adalah sebagian besar masalah dengan objek ruang lingkup file statis dan lajang yang tidak memiliki masa hidup yang sangat jelas dan telah diketahui dapat diciptakan kembali setelah mereka dihancurkan.

Dalam kebanyakan kasus, Anda dapat menghindari kebutuhan ini dengan menggunakan auto_ptrs


6
Jika Anda melewati jalur itu dua kali, Anda mengalami kesalahan pemrograman. Menetapkan pointer ke nol untuk membuat kesalahan ini kurang berbahaya tidak menyelesaikan masalah yang mendasarinya.
Pete Becker

1
Setuju, tetapi saya telah melihat ini direkomendasikan sebelumnya, dan saya percaya ini ada dalam beberapa standar pemrograman profesional. Saya lebih banyak mengomentari mengapa profesor poster itu datang dengan itu, daripada ketika itu ada gunanya
Tom Tanner

2
Menindaklanjuti apa yang dikatakan Pete Becker: itu tidak menyelesaikan masalah yang mendasarinya, tetapi mungkin menutupi itu. (Ada kasus di mana Anda akan menetapkan pointer ke NULLsetelah menghapusnya. Jika NULLada nilai yang benar untuk pointer dalam keadaan seperti itu; misalnya pointer menunjuk ke nilai cache, dan NULLmenunjukkan cache yang tidak valid. Tetapi ketika Anda melihat seseorang menetapkan pengaturan sebuah penunjuk NULLsebagai baris terakhir dalam destruktor, Anda bertanya-tanya apakah dia tahu C ++.)
James Kanze

4

Saya suka jawaban Luchian yang diterima, sebenarnya saya belajar dengan cara yang sulit bahwa dia benar, jadi saya selalu menggunakan kawat gigi, bahkan untuk blok baris tunggal. Namun, secara pribadi saya membuat pengecualian saat menulis filter, seperti Anda dalam contoh Anda. Ini:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

terlihat berantakan bagiku. Ini memisahkan for for dan pernyataan if menjadi tindakan yang terpisah, ketika maksud Anda sebenarnya adalah tindakan tunggal: untuk menghitung semua bilangan bulat yang dapat dibagi dengan 2. Dalam bahasa yang lebih ekspresif, ini dapat dituliskan seperti:

j = [1..100].filter(_%2 == 0).Count

Dalam bahasa yang tidak memiliki penutupan, filter tidak dapat diekspresikan dalam satu pernyataan, tetapi harus berupa for for diikuti oleh pernyataan if. Namun, itu masih satu tindakan dalam pikiran programmer, dan saya percaya itu harus tercermin dalam kode, seperti:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
  if (i % 2 == 0)
{
    j++;
}

7
Saya suka bagaimana semua orang berhasil mengabaikan for (int i = 0; i < 100; i += 2);, demi melanjutkan argumen tentang indentasi ;-) Mungkin ada bunfight terpisah yang bisa kita miliki, bagaimana "terbaik" untuk mengekspresikan logika "untuk masing-masing idalam rentang tertentu dengan properti tertentu" di C ++ tanpa loop, menggunakan beberapa kombinasi mimpi buruk dari algoritma standar, filter_iteratordan / atau counting_iterator.
Steve Jessop

1
Juga, jika kita memiliki itu maka kita bisa tidak setuju tentang bagaimana membuat indentasi pernyataan tunggal besar yang dihasilkan.
Steve Jessop

2
@ Seve, Ini hanya contoh saja. Ada banyak kegunaan pola yang sah. Jelas jika Anda ingin menghitung angka dari 1 hingga 100 yang dapat dibagi 2, yang harus Anda lakukan adalah 100/2.
MikeFHay

2
Tentu, saya tahu, itu sebabnya saya mengambil abstrak "untuk masing-masing idalam kisaran tertentu dengan properti tertentu". Hanya saja biasanya pada SO, orang-orang sangat cepat mengabaikan pertanyaan yang sebenarnya dalam mendukung pendekatan yang sama sekali berbeda dengan contoh yang diberikan. Tapi indentasi itu penting , jadi kita tidak ;-)
Steve Jessop

4

Salah satu opsi untuk membantu mencegah kesalahan yang telah dijelaskan di atas adalah untuk menjelaskan apa yang Anda inginkan terjadi ketika Anda tidak menggunakan kawat gigi. Itu membuatnya lebih sulit untuk tidak melihat kesalahan ketika Anda mencoba mengubah kode.

if (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong

5
Opsi kedua akan menghasilkan kesalahan kompilasi, karena elsetidak terkait dengan if.
Luchian Grigore

Satu masalah yang tidak begitu terlihat dengan inline adalah bahwa sebagian besar IDE secara default mengubahnya ke gaya indentasi ketika menggunakan utilitas autoformat mereka.
Honza Brabec

@Honza: itu masalah politik yang sangat berat. Jika kita bekerja sama pada basis kode, kita harus menggunakan gaya indentasi yang sama hingga setiap detail terakhir, atau kita berdua harus setuju untuk tidak mengubah kode yang ada secara otomatis "hanya karena". Jika yang pertama maka gaya yang disetujui masih bisa memasukkan ini, tetapi Anda harus mengkonfigurasi IDE untuk menghormatinya atau tidak menggunakan autoformat. Menyetujui bahwa format umum adalah "apa pun untuk autoformats IDE saya" adalah sangat baik jika kita semua menggunakan IDE yang sama selamanya, tidak begitu baik sebaliknya.
Steve Jessop

3

Jika Anda seorang kompiler, itu tidak ada bedanya. Keduanya sama.

Tetapi untuk programmer, yang pertama lebih jelas, mudah dibaca dan lebih rentan kesalahan.


2
Selain membuka {di jalurnya sendiri, toh.
Rob

3

Contoh lain dari menambahkan kurung kurawal. Pernah saya mencari bug dan menemukan kode seperti itu:

void SomeSimpleEventHandler()
{
    SomeStatementAtTheBeginningNumber1;
    if (conditionX) SomeRegularStatement;
    SomeStatementAtTheBeginningNumber2;
    SomeStatementAtTheBeginningNumber3;
    if (!SomeConditionIsMet()) return;
    OtherwiseSomeAdditionalStatement1;
    OtherwiseSomeAdditionalStatement2;
    OtherwiseSomeAdditionalStatement3;
}

Jika Anda membaca metode baris demi baris, Anda akan melihat bahwa ada kondisi dalam metode yang kembali jika itu tidak benar. Tetapi sebenarnya ini terlihat seperti 100 penangan event sederhana lainnya yang menetapkan beberapa variabel berdasarkan beberapa kondisi. Dan suatu hari Fast Coder masuk dan menambahkan pernyataan pengaturan variabel tambahan di akhir metode:

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

Akibatnya, SetAnotherVariableUnconditionnaly dieksekusi ketika SomeConditionIsMet (), tetapi orang cepat tidak menyadarinya karena semua garis hampir sama ukurannya dan bahkan ketika kondisi pengembaliannya indentasi secara vertikal, itu tidak terlalu terlihat.

Jika pengembalian bersyarat diformat seperti ini:

if (!SomeConditionIsMet())
{
    return;
}

itu jauh terlihat dan Fast Coder akan menemukannya dalam sekejap.


Jika fast coder Anda tidak dapat diganggu untuk menemukan returnpernyataan yang disorot sintaksis dalam tubuh fungsi sebelum menambahkan sesuatu ke dalamnya, Anda tidak boleh membiarkan fast coder mendekati kode Anda. Anda tidak akan menghentikan orang seperti itu untuk mengganggu kode Anda dengan memasukkan kawat gigi.
cmaster - mengembalikan monica

@ cmaster Dia tidak bekerja dengan kita lagi. Pokoknya, penyorotan sintaks memang bagus, tetapi ingat bahwa ada orang yang tidak melihat dengan jelas (saya bahkan melihat tulisan dari seorang programmer buta tahun lalu).
Artemix

2

Saya menganggap yang pertama menjadi jelas kemudian kedua. Ini memberi perasaan menutup instruksi, dengan sedikit kode baik-baik saja ketika kode menjadi kompleks {...}banyak membantu bahkan jika itu endifataubegin...end

//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}


//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

1

Yang terbaik adalah mengatur pointer ke NULL ketika Anda selesai menggunakannya.

Berikut ini contohnya:

Kelas A melakukan hal berikut:

  1. Mengalokasikan satu blok memori
  2. Kemudian beberapa waktu kemudian, ini menghapus blok memori ini tetapi tidak mengatur pointer ke NULL

Kelas B melakukan hal berikut

  1. Mengalokasikan memori (dan dalam hal ini terjadi diberikan blok memori yang sama yang telah dihapus oleh kelas A.)

Pada titik ini baik Kelas A dan Kelas B memiliki pointer yang menunjuk ke blok memori yang sama, sejauh Kelas A yang bersangkutan blok memori ini tidak ada karena sudah selesai dengan itu.

Pertimbangkan masalah berikut:

Bagaimana jika ada kesalahan logika di Kelas A yang membuatnya menulis ke memori yang sekarang menjadi milik Kelas B?

Dalam contoh khusus ini, Anda tidak akan mendapatkan kesalahan pengecualian akses yang buruk karena alamat memori legal, sementara itu kelas A sekarang secara efektif merusak data kelas B.

Kelas B pada akhirnya bisa crash jika menemui nilai yang tidak terduga dan ketika itu crash, kemungkinan besar, Anda akan menghabiskan waktu yang cukup lama berburu bug ini di kelas B saat masalahnya ada di kelas A.

Jika Anda telah mengatur pointer memori yang dihapus ke NULL, Anda akan mendapatkan kesalahan pengecualian segera setelah kesalahan logika di Kelas A mencoba menulis ke pointer NULL.

Jika Anda khawatir tentang kesalahan logika dengan penghapusan ganda ketika pointer NULL untuk kedua kalinya, kemudian tambahkan menegaskan untuk ini.

Juga: Jika Anda akan memilih, harap jelaskan.


2
Jika ada kesalahan logika, itu harus diperbaiki, bukan menutupi itu.
uınbɐɥs

@Barmar, OP mengatakan ... 6. Tetapkan Pointer ke NULL setelah penghapusan - Perlindungan penghapusan ganda // tidak buruk. Beberapa orang menjawab tidak menyetelnya ke Null dan saya mengatakan mengapa itu harus disetel ke NULL, bagian 6. apa. Jawaban saya untuk menyetel NULL tidak sesuai dengan 6?
John

1
@Shaquin, Dan bagaimana Anda mengusulkan dalam menemukan kesalahan logika ini di tempat pertama? Setelah Anda mengatur variabel pointer ke NULL setelah memori dihapus. Upaya apa pun untuk mereferensikan pointer NULL akan macet ke debugger di jalur upaya ilegal Anda dilakukan. Anda dapat melacak kembali dan melihat di mana kesalahan logika itu dan memperbaiki masalahnya. Jika Anda tidak menetapkan variabel pointer ke NULL setelah Anda menghapus memori, upaya ilegal Anda untuk menulis memori yang dihapus ini karena kesalahan logika UNAWARE mungkin berhasil dan karenanya tidak mogok pada saat itu. Itu tidak menutupi itu.
John

1

Selalu memiliki kurung kurawal adalah aturan yang sangat sederhana dan kuat. Namun kode tersebut mungkin terlihat tidak bagus ketika ada banyak kawat gigi. Jika aturan memungkinkan untuk menghilangkan kurung kurawal maka harus ada aturan gaya yang lebih rinci dan alat yang lebih canggih. Kalau tidak, itu dapat dengan mudah mengakibatkan kode kacau dan membingungkan (tidak elegan). Oleh karena itu mencari aturan gaya tunggal yang terpisah dari panduan gaya lainnya dan alat yang digunakan kemungkinan tidak membuahkan hasil. Saya hanya akan membawa beberapa detail penting tentang aturan # 3 yang bahkan belum disebutkan dalam jawaban lain.

Detail menarik pertama adalah bahwa sebagian besar pendukung aturan itu setuju untuk melanggarnya else. Dengan kata lain mereka tidak menuntut kode yang ditinjau:

// pedantic rule #3
if ( command == Eat )
{
    eat();
}
else
{
    if ( command == Sleep )
    {
        sleep();
    }
    else
    {
        if ( command == Drink )
        {
            drink();
        }
        else
        {
            complain_about_unknown_command();
        }
    }
}

Sebaliknya, jika mereka melihatnya mereka bahkan mungkin menyarankan untuk menulisnya seperti itu:

// not fully conforming to rule #3
if ( command == Eat )
{
    eat();
}
else if ( command == Sleep )
{
    sleep();
}
else if ( command == Drink )
{
    drink();
}
else
{
   complain_about_unknown_command();
}

Itu secara teknis melanggar aturan itu karena tidak ada tanda kurung antara elsedan if. Dualitas aturan tersebut muncul ketika mencoba menerapkannya ke basis kode secara otomatis dengan alat yang tidak ada artinya. Memang, mengapa harus berdebat, biarkan saja alat untuk menerapkan gaya secara otomatis.

Detail kedua (yang juga sering dilupakan oleh pendukung aturan itu) adalah bahwa kesalahan yang mungkin terjadi tidak pernah hanya karena pelanggaran aturan # 3 itu. Sebenarnya itu hampir selalu melibatkan pelanggaran aturan # 1 juga (yang tidak ada yang membantah). Sekali lagi dari sudut pandang alat otomatis, tidak sulit untuk membuat alat yang langsung mengeluh ketika aturan # 1 dilanggar dan sebagian besar kesalahan dapat ditangkap tepat waktu.

Detail ketiga (yang sering dilupakan oleh penentang aturan itu) adalah sifat pernyataan kosong yang membingungkan yang diwakili oleh satu titik koma. Sebagian besar pengembang dengan beberapa pengalaman menjadi bingung cepat atau lambat oleh satu titik koma yang salah tempat atau dengan pernyataan kosong yang ditulis menggunakan titik koma tunggal. Dua kawat gigi keriting bukannya titik koma tunggal secara visual lebih mudah dikenali.


1

Saya harus mengakui bahwa tidak selalu digunakan {}untuk satu baris, tetapi ini adalah praktik yang baik.

  • Katakanlah Anda menulis kode tanpa tanda kurung yang terlihat seperti ini:

    untuk (int i = 0; i <100; ++ i) untuk (int j = 0; j <100; ++ j) DoSingleStuff ();

Dan setelah beberapa waktu Anda ingin menambahkan dalam jlingkaran beberapa hal lain dan Anda hanya melakukannya dengan menyelaraskan dan lupa untuk menambahkan tanda kurung.

  • Kesepakatan memori lebih cepat. Katakanlah Anda memiliki ruang lingkup besar dan membuat array besar di dalamnya (tanpa newbegitu mereka berada di tumpukan). Array tersebut dikeluarkan dari memori setelah Anda meninggalkan ruang lingkup. Tetapi ada kemungkinan bahwa Anda menggunakan array itu di satu tempat dan akan berada di tumpukan untuk sementara waktu dan menjadi semacam sampah. Karena stack memiliki ukuran terbatas dan sangat kecil, maka dimungkinkan untuk melebihi ukuran stack. Jadi dalam beberapa kasus lebih baik menulis {}untuk mencegah hal itu. CATATAN ini bukan untuk jalur tunggal tetapi untuk situasi seperti ini:

    if (...) {// SomeStuff ... {// kita tidak punya if, while, etc. // SomeOtherStuff} // SomeMoreStuff}

  • Cara ketiga untuk menggunakannya mirip dengan yang kedua. Hanya saja tidak membuat stack cleaner tetapi untuk membuka beberapa fungsi. Jika Anda menggunakan mutexfungsi yang panjang biasanya lebih baik mengunci dan membuka kunci sebelum mengakses data dan setelah selesai membaca / menulis itu. CATATAN cara ini digunakan jika Anda memiliki beberapa milik Anda sendiri classatau structdengan constructordan destructoruntuk mengunci memori.

  • Apa yang lebih:

    if (...) if (...) SomeStuff (); lain SomeOtherStuff (); // pergi ke yang kedua jika, tetapi alligment menunjukkan itu pada yang pertama ...

Semua Dalam Semua, saya tidak bisa mengatakan, apa cara terbaik untuk selalu menggunakan {}untuk satu baris tetapi tidak ada yang buruk untuk melakukan itu.

EDIT PENTING Jika Anda menulis kompilasi kode kurung untuk satu baris tidak melakukan apa-apa, tetapi jika kode Anda akan ditafsirkan itu memperlambat kode untuk sangat sedikit. Sangat sedikit.


1

Ada sejumlah cara yang memungkinkan untuk menulis pernyataan kontrol; kombinasi tertentu dari mereka dapat hidup berdampingan tanpa mengganggu keterbacaan, tetapi kombinasi lainnya akan menyebabkan masalah. Gaya

if (condition)
  statement;

akan hidup berdampingan dengan nyaman dengan beberapa cara lain menulis pernyataan kontrol, tetapi tidak begitu baik dengan yang lain. Jika pernyataan yang dikontrol multi-garis ditulis sebagai:

if (condition)
{
  statement;
  statement;
}

maka akan jelas secara visual ifpernyataan mana yang mengendalikan satu baris dan mana yang mengendalikan banyak baris. Namun, jika ifpernyataan multi-baris ditulis sebagai:

if (condition) {
  statement;
  statement;
}

maka kemungkinan seseorang mencoba untuk memperluas ifkonstruksi pernyataan tunggal tanpa menambahkan kawat gigi yang diperlukan mungkin jauh lebih tinggi.

Pernyataan baris-pernyataan-ke-berikutnya ifjuga mungkin bermasalah jika basis kode menggunakan signifikan formulir

if (condition) statement;

Preferensi saya sendiri adalah bahwa memiliki pernyataan pada jalurnya sendiri umumnya meningkatkan keterbacaan kecuali dalam kasus di mana ada banyak ifpernyataan dengan blok kontrol yang sama, misalnya

if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.

dalam hal ini saya biasanya akan mendahului dan mengikuti kelompok ifpernyataan seperti itu dengan garis kosong untuk memisahkan mereka secara visual dari kode lain. Memiliki serangkaian pernyataan yang semuanya dimulai dengan iflekukan yang sama kemudian akan memberikan indikasi visual yang jelas bahwa ada sesuatu yang tidak biasa.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.