Apakah ada pandangan apakah menggunakan #define untuk mendefinisikan baris kode lengkap untuk menyederhanakan pengkodean adalah praktik pemrograman yang baik atau buruk? Misalnya, jika saya perlu mencetak banyak kata bersama, saya akan merasa kesal mengetik
<< " " <<
Untuk menyisipkan spasi antara kata dalam pernyataan cout. Saya hanya bisa melakukannya
#define pSpace << " " <<
dan ketik
cout << word1 pSpace word2 << endl;
Bagi saya ini tidak menambah atau mengurangi dari kejelasan kode dan membuat mengetik sedikit lebih mudah. Ada kasus lain yang bisa saya pikirkan di mana mengetik akan lebih mudah, biasanya untuk debugging.
Ada pemikiran tentang ini?
EDIT: Terima kasih atas semua jawaban hebatnya! Pertanyaan ini baru saja datang kepada saya setelah melakukan banyak pengetikan berulang, tetapi saya tidak pernah berpikir akan ada makro lain yang kurang membingungkan untuk digunakan. Bagi mereka yang tidak ingin membaca semua jawaban, alternatif terbaik adalah menggunakan makro IDE Anda untuk mengurangi pengetikan berulang.
sumber
Jawaban:
Menulis kode itu mudah. Membaca kode itu sulit.
Anda menulis kode sekali. Itu hidup selama bertahun-tahun, orang membacanya seratus kali.
Optimalkan kode untuk membaca, bukan untuk menulis.
sumber
Secara pribadi, saya benci itu. Ada sejumlah alasan mengapa saya tidak menyarankan orang dari teknik ini:
Pada waktu kompilasi, perubahan kode Anda yang sebenarnya mungkin signifikan. Orang berikutnya datang dan bahkan menyertakan braket penutup di #define atau panggilan fungsi. Apa yang ditulis pada titik kode tertentu jauh dari apa yang akan ada di sana setelah pra-pemrosesan.
Itu tidak bisa dibaca. Mungkin jelas bagi Anda .. untuk saat ini .. jika hanya definisi yang satu ini. Jika itu menjadi kebiasaan, Anda akan segera berakhir dengan puluhan #define dan mulai kehilangan jejak. Tapi yang terburuk, tidak ada orang lain yang bisa mengerti apa
word1 pSpace word2
sebenarnya arti (tanpa melihat #define).Mungkin menjadi masalah bagi alat eksternal. Katakanlah Anda entah bagaimana berakhir dengan #define yang mencakup braket penutup, tetapi tidak ada braket pembuka. Semuanya mungkin berfungsi dengan baik, tetapi editor dan alat lain mungkin melihat sesuatu yang
function(withSomeCoolDefine;
agak aneh (yaitu, mereka akan melaporkan kesalahan dan yang lainnya). (Contoh serupa: panggilan fungsi di dalam define - apakah alat analisis Anda dapat menemukan panggilan ini?)Perawatan menjadi lebih sulit. Anda memiliki semua yang didefinisikan sebagai tambahan untuk masalah yang biasa dibawa oleh pemeliharaan itu. Selain dengan poin di atas, dukungan alat untuk refactoring juga dapat terpengaruh secara negatif.
sumber
Pikiran utama saya tentang ini, adalah bahwa saya tidak pernah menggunakan "mempermudah pengetikan" sebagai aturan saat menulis kode.
Aturan utama saya saat menulis kode adalah membuatnya mudah dibaca. Alasan di balik ini hanyalah bahwa kode dibaca urutan besarnya lebih banyak dari yang tertulis. Dengan demikian, waktu Anda kehilangan menulisnya dengan hati-hati, tertib, ditata dengan benar sebenarnya diinvestasikan untuk membuat bacaan lebih lanjut, dan memahami lebih cepat.
Dengan demikian, #define yang Anda gunakan cukup merusak cara berganti-ganti
<<
dan yang lainnya . Ini melanggar aturan yang paling tidak mengejutkan, dan bukan IMHO hal yang baik.sumber
Pertanyaan ini memberikan contoh yang jelas tentang bagaimana Anda dapat menggunakan makro dengan buruk. Untuk melihat contoh lain (dan dihibur) lihat pertanyaan ini .
Karena itu, saya akan memberikan contoh dunia nyata dari apa yang saya anggap sebagai penggabungan makro yang baik.
Contoh pertama muncul di CppUnit , yang merupakan kerangka pengujian unit. Seperti halnya kerangka kerja pengujian standar lainnya, Anda membuat kelas pengujian dan kemudian Anda harus menentukan metode apa saja yang harus dijalankan sebagai bagian dari pengujian.
Seperti yang Anda lihat, kelas memiliki blok makro sebagai elemen pertama. Jika saya menambahkan metode baru
testSubtraction
, sudah jelas apa yang perlu Anda lakukan untuk memasukkannya dalam uji coba.Blok makro ini berkembang menjadi seperti ini:
Mana yang Anda sukai untuk dibaca dan dipelihara?
Contoh lain adalah dalam kerangka kerja Microsoft MFC, di mana Anda memetakan fungsi untuk pesan:
Jadi, apa saja hal yang membedakan "Makro Baik" dari jenis jahat yang mengerikan?
Mereka melakukan tugas yang tidak dapat disederhanakan dengan cara lain. Menulis makro untuk menentukan maksimum antara dua elemen adalah salah, karena Anda dapat mencapai hal yang sama menggunakan metode templat. Tetapi ada beberapa tugas kompleks (misalnya, memetakan kode pesan ke fungsi anggota) yang bahasa C ++ tidak ditangani dengan elegan.
Mereka memiliki penggunaan formal yang sangat ketat. Dalam kedua contoh ini blok makro diumumkan dengan memulai dan mengakhiri makro, dan makro antar hanya akan pernah muncul di dalam blok ini. Anda memiliki C ++ normal, Anda permisi sebentar dengan makro, dan kemudian Anda kembali normal lagi. Dalam contoh "makro jahat", makro tersebar di seluruh kode dan pembaca yang malang tidak memiliki cara untuk mengetahui kapan aturan C ++ berlaku dan kapan mereka tidak.
sumber
Dengan segala cara, akan lebih baik jika Anda menyetel editor teks / IDE favorit Anda untuk menyisipkan cuplikan kode yang Anda rasa membosankan untuk mengetik ulang berulang kali. Dan lebih baik istilah "sopan" untuk perbandingan. Sebenarnya, saya tidak bisa memikirkan kasus serupa ketika preprocessing beats macro editor. Yah, mungkin satu - ketika dengan beberapa alasan misterius dan tidak bahagia Anda terus-menerus menggunakan berbagai alat untuk pengkodean. Tapi itu bukan pembenaran :)
Ini juga bisa menjadi solusi yang lebih baik untuk skenario yang lebih kompleks, ketika preprocessing teks dapat melakukan hal yang jauh lebih tidak dapat dibaca dan rumit (pikirkan tentang input parametrized).
sumber
<< " " <<
.Yang lain sudah menjelaskan mengapa Anda tidak harus melakukannya. Contoh Anda jelas tidak layak diimplementasikan dengan makro. Tapi, ada banyak kasus di mana Anda harus menggunakan makro demi keterbacaan.
Contoh terkenal dari aplikasi bijak dari teknik semacam itu adalah proyek Dentang : lihat bagaimana
.def
file digunakan di sana. Dengan makro dan#include
Anda dapat memberikan definisi tunggal, kadang-kadang seluruhnya deklaratif untuk koleksi hal serupa yang akan dibuka menjadi tipe deklarasi,case
pernyataan di mana pun sesuai, penginisialisasi default, dll. Ini meningkatkan pemeliharaan secara signifikan: Anda tidak akan pernah lupa menambahkan barucase
pernyataan di mana-mana saat Anda menambahkan yang baruenum
, misalnya.Jadi, seperti halnya alat canggih lainnya, Anda harus menggunakan preprosesor C dengan hati-hati. Tidak ada aturan umum dalam seni pemrograman, seperti "Anda tidak boleh menggunakan ini" atau "Anda harus selalu menggunakannya". Semua aturan hanyalah pedoman.
sumber
Tidak pernah tepat untuk menggunakan #define seperti itu. Dalam kasus Anda, Anda bisa melakukan ini:
sumber
Tidak.
Untuk makro yang dimaksudkan untuk digunakan dalam kode, pedoman yang baik untuk menguji kelayakan adalah mengelilingi ekspansi dengan tanda kurung (untuk ekspresi) atau kurung kurawal (untuk kode) dan lihat apakah masih akan dikompilasi:
Makro yang digunakan dalam deklarasi (seperti contoh dalam jawaban Andrew Shepherd) dapat lolos dengan seperangkat aturan yang lebih longgar selama mereka tidak mengganggu konteks sekitarnya (misalnya beralih antara
public
danprivate
).sumber
Ini hal yang wajar untuk dilakukan dalam program "C" murni.
Tidak perlu dan membingungkan dalam program C ++.
Ada banyak cara untuk menghindari pengetikan kode berulang-ulang di C ++. Dari menggunakan fasilitas yang disediakan oleh IDE Anda (meskipun dengan "
%s/ pspace /<< " " <</g
" sederhana, akan menghemat sebanyak mengetik dan masih menghasilkan kode yang dapat dibaca standar). Anda bisa mendefinisikan metode pribadi untuk mengimplementasikan ini atau untuk kasus yang lebih kompleks, template C ++ akan menjadi lebih bersih dan sederhana.sumber
Dalam C ++ ini dapat diatasi dengan overloading operator. Atau bahkan sesuatu yang sederhana seperti fungsi variadik:
lineWithSpaces(word1, word2, word3, ..., wordn)
keduanya sederhana dan menghemat waktu Anda mengetikpSpaces
lagi.Jadi, sementara dalam kasus Anda mungkin tidak tampak seperti masalah besar, ada solusi yang lebih sederhana dan lebih kuat.
Secara umum, ada beberapa kasus di mana menggunakan makro secara signifikan lebih pendek tanpa menimbulkan kebingungan, dan sebagian besar ada solusi yang cukup singkat menggunakan fitur bahasa aktual (makro lebih dari sekadar penggantian string).
sumber
Ya, itu sangat buruk. Saya bahkan melihat orang melakukan ini:
untuk menyimpan pengetikan (apa yang ingin Anda capai).
Kode semacam itu hanya dimiliki di tempat-tempat seperti ini .
sumber
Makro itu jahat dan harus digunakan hanya ketika Anda benar-benar harus. Ada beberapa kasus di mana makro berlaku (terutama debugging). Tetapi dalam C ++ dalam banyak kasus Anda dapat menggunakan fungsi inline sebagai gantinya.
sumber
goto
, untuk semua sistem makro yang mungkin, dll.Tidak, Anda tidak diizinkan menggunakan makro untuk menyimpan pengetikan .
Namun Anda diizinkan, bahkan diharuskan menggunakannya untuk memisahkan bagian kode yang tidak berubah dari yang diubah, dan mengurangi redundansi. Untuk yang terakhir Anda harus memikirkan alternatif dan memilih makro hanya jika alat yang lebih baik tidak berfungsi. (Untuk praktik makro cukup di akhir baris, jadi memiliki makro berarti pilihan terakhir ...)
Untuk mengurangi mengetik, sebagian besar editor memiliki makro, bahkan cuplikan kode yang cerdas.
sumber
switch
/ etc, Anda bisa bertaruh saya akan menggunakan makro untuk menghindari menjadi gila - dan meningkatkan keterbacaan. Menggunakan makro untuk menyimpan pengetikan pada kata kunci, kontrol aliran, dan sejenisnya itu bodoh - tetapi untuk mengatakan tidak ada yang bisa menggunakannya untuk menyimpan penekanan tombol dalam konteks yang valid juga sama.