Scott Meyers membahas masalah ini dengan sangat baik dan menyeluruh. Item-nya # 2 dalam "Efektif C ++ Edisi Ketiga". Dua kasus khusus (1) konstanta statis lebih disukai dalam ruang lingkup kelas untuk konstanta spesifik kelas; (2) namespace atau const lingkup anonim lebih disukai daripada #define.
Eric
2
Saya lebih suka Enums. Karena itu adalah gabungan keduanya. Tidak menempati ruang kecuali Anda membuat variabel. Jika Anda hanya ingin menggunakan konstanta, enum adalah pilihan terbaik. Ini memiliki tipe keamanan di C / C ++ 11 std dan juga konstanta yang sempurna. #define adalah tipe tidak aman, const membutuhkan ruang jika kompiler tidak dapat mengoptimalkannya.
siddhusingh
1
Keputusan saya apakah akan menggunakan #defineatau static const(untuk string) didorong oleh aspek inisialisasi (tidak disebutkan melalui jawaban di bawah): jika konstanta hanya digunakan dalam unit kompilasi tertentu saja, maka saya akan pergi dengan static const, yang lain saya gunakan #define- hindari kegagalan inisialisasi urutan statis. isocpp.org/wiki/faq/ctors#static-init-order
Martin Dvorak
Jika const, constexpratau enumvariasi apa pun berfungsi dalam kasus Anda, maka pilih itu#define
Phil1970
@ MartinDvorak " hindari kegagalan inisialisasi urutan statis " Bagaimana masalah untuk konstanta?
curiousguy
Jawaban:
139
Secara pribadi, saya benci preprocessor, jadi saya akan selalu pergi dengan const.
Keuntungan utama dari a #defineadalah tidak memerlukan memori untuk disimpan dalam program Anda, karena sebenarnya hanya mengganti beberapa teks dengan nilai literal. Ini juga memiliki keuntungan karena tidak memiliki tipe, sehingga dapat digunakan untuk nilai integer apa pun tanpa menghasilkan peringatan.
Keuntungan dari " const" adalah bahwa mereka dapat dicakup, dan mereka dapat digunakan dalam situasi di mana penunjuk ke objek perlu dilewatkan.
Saya tidak tahu persis apa yang Anda maksud dengan bagian " static" itu. Jika Anda mendeklarasikan secara global, saya akan meletakkannya di namespace anonim alih-alih menggunakan static. Sebagai contoh
namespace{unsignedconst seconds_per_minute =60;};int main (int argc;char*argv[]){...}
Konstanta string secara khusus adalah salah satu yang mungkin mendapat manfaat dari menjadi #defined, setidaknya jika mereka dapat digunakan sebagai "blok bangunan" untuk konstanta string yang lebih besar. Lihat balasan saya untuk contoh.
AnT
62
The #definekeuntungan tidak menggunakan memori setiap tidak akurat. "60" pada contoh harus disimpan di suatu tempat, terlepas dari apakah itu static constatau #define. Bahkan, saya telah melihat kompiler di mana menggunakan #define menyebabkan konsumsi memori besar (hanya baca), dan const statis tidak menggunakan memori yang tidak diperlukan.
Gilad Naor
3
#Define seperti jika Anda mengetiknya, jadi pasti bukan dari memori.
Yang Terhormat
27
@theReverend Apakah nilai literal entah bagaimana dikecualikan dari konsumsi sumber daya mesin? Tidak, mereka mungkin menggunakannya dengan cara yang berbeda, mungkin itu tidak akan muncul di stack atau heap, tetapi di beberapa titik program dimuat ke memori bersama dengan semua nilai yang dikompilasi ke dalamnya.
Sqeaky
13
@ gilad-naor, Anda benar secara umum tetapi bilangan bulat kecil seperti 60 sebenarnya terkadang menjadi semacam pengecualian parsial. Beberapa set instruksi memiliki kemampuan untuk menyandikan bilangan bulat atau subset bilangan bulat langsung dalam aliran instruksi. Sebagai contoh, MIP segera ditambahkan ( cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/addi.html ). Dalam kasus seperti ini integer #defined benar-benar dapat dikatakan tidak menggunakan ruang karena dalam biner yang dikompilasinya ia menempati beberapa bit cadangan dalam instruksi yang harus tetap ada.
ahcox
242
Pro dan kontra antara #defines, consts dan (apa yang Anda lupa) enum, tergantung penggunaan:
enums:
hanya mungkin untuk nilai integer
masalah benturan enum class Xdengan cakupan / pengidentifikasi yang ditangani dengan baik, khususnya di kelas C ++ 11 enum di mana enumerasi untuk disatukan oleh ruang lingkupX::
sangat diketik, tetapi ke ukuran int cukup-masuk-atau-tidak-ditandatangani di mana Anda tidak memiliki kontrol di C ++ 03 (meskipun Anda dapat menentukan bidang bit di mana mereka harus dikemas jika enum adalah anggota struct / class / union), sedangkan C ++ 11 default inttetapi dapat secara eksplisit diatur oleh programmer
tidak dapat mengambil alamat - tidak ada karena nilai enumerasi secara efektif diganti secara inline pada titik penggunaan
pembatasan penggunaan yang lebih kuat (misalnya penambahan - template <typename T> void f(T t) { cout << ++t; }tidak akan dikompilasi, meskipun Anda dapat membungkus enum ke dalam kelas dengan konstruktor implisit, operator casting dan operator yang ditentukan pengguna)
setiap tipe konstanta diambil dari enum penutup, jadi template <typename T> void f(T)dapatkan instantiasi yang berbeda ketika melewati nilai numerik yang sama dari enum yang berbeda, yang semuanya berbeda dari f(int)instantiasi yang sebenarnya . Kode objek masing-masing fungsi bisa identik (mengabaikan offset alamat), tapi saya tidak akan mengharapkan kompiler / penghubung untuk menghilangkan salinan yang tidak perlu, meskipun Anda dapat memeriksa kompiler / penghubung Anda jika Anda peduli.
bahkan dengan typeof / decltype, tidak dapat mengharapkan numeric_limits untuk memberikan wawasan yang berguna ke dalam set nilai dan kombinasi yang bermakna (memang, kombinasi "legal" bahkan tidak dinotasikan dalam kode sumber, anggap enum { A = 1, B = 2 }- adalah A|B"legal" dari logika program perspektif?)
nama ketik enum dapat muncul di berbagai tempat di RTTI, pesan kompiler dll. - mungkin berguna, mungkin kebingungan
Anda tidak dapat menggunakan enumerasi tanpa unit terjemahan benar-benar melihat nilai, yang berarti enum di API perpustakaan membutuhkan nilai yang diekspos di header, dan makedan alat rekompilasi berbasis timestamp lainnya akan memicu kompilasi ulang klien ketika mereka diubah (buruk! )
consts:
masalah benturan dengan cakupan / pengidentifikasi yang ditangani dengan baik
kuat, tunggal, tipe yang ditentukan pengguna
Anda mungkin mencoba untuk "mengetik" #defineala #define S std::string("abc"), tetapi konstanta menghindari pembangunan berulang temporaries yang berbeda pada setiap titik penggunaan
Satu Definisi Aturan komplikasi
dapat mengambil alamat, membuat referensi const untuk mereka dll.
paling mirip dengan yang tidak constbernilai, yang meminimalkan pekerjaan dan dampak jika beralih di antara keduanya
nilai dapat ditempatkan di dalam file implementasi, memungkinkan kompilasi ulang lokal dan hanya tautan klien untuk mengambil perubahan
#defines:
Ruang lingkup "global" / lebih rentan terhadap penggunaan yang saling bertentangan, yang dapat menghasilkan masalah kompilasi yang sulit diselesaikan dan hasil run-time yang tidak terduga daripada pesan kesalahan yang waras; mengurangi ini membutuhkan:
pengidentifikasi yang panjang, tidak jelas, dan / atau terkoordinasi secara terpusat, dan akses ke mereka tidak dapat mengambil manfaat dari pencocokan secara tersirat namespace bekas / saat ini / Koenig-look-up, alias namespace dll.
sementara praktik terbaik trumping memungkinkan pengidentifikasi parameter templat menjadi huruf besar karakter tunggal (mungkin diikuti oleh angka), penggunaan pengidentifikasi lainnya tanpa huruf kecil secara konvensional disediakan untuk dan diharapkan dari definisi preprosesor (di luar perpustakaan OS dan C / C ++) header). Ini penting agar penggunaan preprosesor skala perusahaan tetap terkelola. Perpustakaan pihak ketiga dapat diharapkan untuk mematuhi. Mengamati hal ini menyiratkan migrasi konst atau enum yang ada ke / dari definisi melibatkan perubahan dalam kapitalisasi, dan karenanya memerlukan pengeditan kode sumber klien daripada kompilasi ulang "sederhana". (Secara pribadi, saya menggunakan huruf besar dari huruf pertama dari pencacahan tetapi bukan konstanta, jadi saya juga akan terpukul untuk bermigrasi di antara keduanya - mungkin waktu untuk memikirkan kembali hal itu.)
operasi waktu kompilasi yang lebih mungkin: string literal string, stringifikasi (mengambil ukurannya), concatenation menjadi pengidentifikasi
Kelemahannya adalah bahwa diberikan #define X "x"dan beberapa penggunaan klien ala "pre" X "post", jika Anda ingin atau perlu membuat X variabel runtime-berubah daripada konstan Anda memaksa pengeditan ke kode klien (bukan hanya kompilasi ulang), sedangkan transisi yang lebih mudah dari const char*atau const std::stringdiberikan mereka sudah memaksa pengguna untuk memasukkan operasi gabungan (misalnya "pre" + X + "post"untuk string)
tidak dapat digunakan sizeofsecara langsung pada literal numerik yang ditentukan
untyped (GCC tidak memperingatkan jika dibandingkan dengan unsigned)
beberapa kompiler / penghubung / rantai debugger mungkin tidak menampilkan pengidentifikasi, jadi Anda akan direduksi menjadi melihat "angka ajaib" (string, apa pun ...)
tidak dapat mengambil alamat
nilai yang diganti tidak boleh legal (atau diskrit) dalam konteks di mana #define dibuat, seperti yang dievaluasi pada setiap titik penggunaan, sehingga Anda dapat mereferensikan objek yang belum dideklarasikan, bergantung pada "implementasi" yang tidak perlu dimasukkan terlebih dahulu, buat "konstanta" seperti { 1, 2 }yang dapat digunakan untuk menginisialisasi array, atau lainnya #define MICROSECONDS *1E-6( pasti tidak merekomendasikan ini!)
beberapa hal khusus seperti __FILE__dan __LINE__dapat dimasukkan ke dalam substitusi makro
Anda dapat menguji keberadaan dan nilai dalam #ifpernyataan untuk menyertakan kode secara kondisional (lebih kuat daripada "jika" pasca-preproses karena kode tidak perlu dikompilasi jika tidak dipilih oleh preprocessor), gunakan #undef-ine, redefine dll.
teks yang diganti harus dibuka:
di unit terjemahan yang digunakan olehnya, yang berarti makro di perpustakaan untuk penggunaan klien harus ada di header, jadi makedan alat rekompilasi berbasis timestamp lainnya akan memicu kompilasi ulang klien saat diubah (buruk!)
atau pada baris perintah, di mana bahkan lebih hati-hati diperlukan untuk memastikan kode klien dikompilasi ulang (mis. Makefile atau skrip yang memasok definisi harus didaftar sebagai ketergantungan)
Pendapat pribadi saya:
Sebagai aturan umum, saya menggunakan consts dan menganggapnya sebagai opsi paling profesional untuk penggunaan umum (meskipun yang lain memiliki kesederhanaan yang menarik bagi pemrogram malas yang lama ini).
Jawaban yang luar biasa. Satu nit kecil: saya kadang-kadang menggunakan enum lokal yang tidak di header sama sekali hanya untuk kejelasan kode, seperti di mesin negara kecil dan semacamnya. Jadi mereka tidak harus berada di header, setiap saat.
kert
Pro dan kontra digabungkan, saya akan sangat suka melihat tabel perbandingan.
Unknown123
@ Tidak Diketahui123: jangan ragu untuk memposting satu - Saya tidak keberatan jika Anda merobek poin yang Anda rasa layak dari sini. Cheers
Tony Delroy
48
Jika ini adalah pertanyaan C ++ dan ia menyebutkan #definesebagai alternatif, maka ini adalah tentang konstanta "global" (yaitu file-scope), bukan tentang anggota kelas. Ketika datang ke konstanta seperti itu di C ++ static constadalah redundan. Dalam C ++ constmemiliki tautan internal secara default dan tidak ada gunanya mendeklarasikannya static. Jadi itu benar-benar tentang constvs #define.
Dan, akhirnya, di C ++ constlebih disukai. Setidaknya karena konstanta seperti itu diketik dan dicakup. Tidak hanya ada alasan untuk lebih #definelebih const, selain dari beberapa pengecualian.
Konstanta string, BTW, adalah salah satu contoh pengecualian tersebut. Dengan #definekonstanta d string seseorang dapat menggunakan fitur penggabungan waktu kompilasi dari kompiler C / C ++, seperti pada
PS Sekali lagi, untuk berjaga-jaga, ketika seseorang menyebutkan static constsebagai alternatif #define, biasanya itu berarti mereka berbicara tentang C, bukan tentang C ++. Saya ingin tahu apakah pertanyaan ini ditandai dengan benar ...
"sama sekali tidak ada alasan untuk lebih memilih #define " Atas apa? Variabel statis didefinisikan dalam file header?
curiousguy
9
#define dapat menyebabkan hasil yang tidak terduga:
#include<iostream>#define x 500#define y x +5int z = y *2;int main(){
std::cout <<"y is "<< y;
std::cout <<"\nz is "<< z;}
Menghasilkan hasil yang salah:
y is505
z is510
Namun, jika Anda mengganti ini dengan konstanta:
#include<iostream>constint x =500;constint y = x +5;int z = y *2;int main(){
std::cout <<"y is "<< y;
std::cout <<"\nz is "<< z;}
Ini menghasilkan hasil yang benar:
y is505
z is1010
Ini karena #definehanya mengganti teks. Karena melakukan ini secara serius dapat mengacaukan urutan operasi, saya akan merekomendasikan menggunakan variabel konstan sebagai gantinya.
Saya memiliki hasil yang berbeda yang tidak terduga: ymemiliki nilai 5500, gabungan sedikit-endian dari xdan 5.
Kode dengan Hammer
5
Menggunakan const statis sama seperti menggunakan variabel const lainnya dalam kode Anda. Ini berarti Anda dapat melacak dari mana pun informasi itu berasal, bukan dari #define yang hanya akan diganti dalam kode dalam proses pra-kompilasi.
Const statis diketik (memiliki tipe) dan dapat diperiksa oleh kompiler untuk validitas, redefinisi, dll.
#define dapat didefinisikan ulang, tidak ditentukan apa pun.
Biasanya Anda harus lebih suka const statis. Itu tidak memiliki kerugian. Prosesor terutama harus digunakan untuk kompilasi bersyarat (dan kadang-kadang untuk trics yang sangat kotor mungkin).
Menentukan konstanta dengan menggunakan arahan preprocessor #definetidak disarankan untuk diterapkan tidak hanya dalam C++, tetapi juga dalam C. Konstanta ini tidak akan memiliki tipe. Bahkan dalam Cdiusulkan untuk digunakan constuntuk konstanta.
Selalu lebih suka menggunakan fitur bahasa daripada beberapa alat tambahan seperti preprosesor.
ES.31: Jangan gunakan makro untuk konstanta atau "fungsi"
Macro adalah sumber utama bug. Makro tidak mematuhi lingkup dan jenis aturan yang biasa. Macro tidak menaati aturan yang biasa untuk melewati argumen. Macro memastikan bahwa pembaca manusia melihat sesuatu yang berbeda dari apa yang dilihat oleh kompiler. Makro mempersulit pembuatan alat.
Jika Anda mendefinisikan konstanta untuk dibagikan di antara semua instance kelas, gunakan konstanta statis. Jika konstanta spesifik untuk setiap instance, cukup gunakan const (tetapi perhatikan bahwa semua konstruktor dari kelas harus menginisialisasi variabel anggota const ini dalam daftar inisialisasi).
#define
ataustatic const
(untuk string) didorong oleh aspek inisialisasi (tidak disebutkan melalui jawaban di bawah): jika konstanta hanya digunakan dalam unit kompilasi tertentu saja, maka saya akan pergi denganstatic const
, yang lain saya gunakan#define
- hindari kegagalan inisialisasi urutan statis. isocpp.org/wiki/faq/ctors#static-init-orderconst
,constexpr
atauenum
variasi apa pun berfungsi dalam kasus Anda, maka pilih itu#define
Jawaban:
Secara pribadi, saya benci preprocessor, jadi saya akan selalu pergi dengan
const
.Keuntungan utama dari a
#define
adalah tidak memerlukan memori untuk disimpan dalam program Anda, karena sebenarnya hanya mengganti beberapa teks dengan nilai literal. Ini juga memiliki keuntungan karena tidak memiliki tipe, sehingga dapat digunakan untuk nilai integer apa pun tanpa menghasilkan peringatan.Keuntungan dari "
const
" adalah bahwa mereka dapat dicakup, dan mereka dapat digunakan dalam situasi di mana penunjuk ke objek perlu dilewatkan.Saya tidak tahu persis apa yang Anda maksud dengan bagian "
static
" itu. Jika Anda mendeklarasikan secara global, saya akan meletakkannya di namespace anonim alih-alih menggunakanstatic
. Sebagai contohsumber
#define
d, setidaknya jika mereka dapat digunakan sebagai "blok bangunan" untuk konstanta string yang lebih besar. Lihat balasan saya untuk contoh.#define
keuntungan tidak menggunakan memori setiap tidak akurat. "60" pada contoh harus disimpan di suatu tempat, terlepas dari apakah itustatic const
atau#define
. Bahkan, saya telah melihat kompiler di mana menggunakan #define menyebabkan konsumsi memori besar (hanya baca), dan const statis tidak menggunakan memori yang tidak diperlukan.Pro dan kontra antara
#define
s,const
s dan (apa yang Anda lupa)enum
, tergantung penggunaan:enum
s:enum class X
dengan cakupan / pengidentifikasi yang ditangani dengan baik, khususnya di kelas C ++ 11 enum di mana enumerasi untuk disatukan oleh ruang lingkupX::
int
tetapi dapat secara eksplisit diatur oleh programmertemplate <typename T> void f(T t) { cout << ++t; }
tidak akan dikompilasi, meskipun Anda dapat membungkus enum ke dalam kelas dengan konstruktor implisit, operator casting dan operator yang ditentukan pengguna)template <typename T> void f(T)
dapatkan instantiasi yang berbeda ketika melewati nilai numerik yang sama dari enum yang berbeda, yang semuanya berbeda darif(int)
instantiasi yang sebenarnya . Kode objek masing-masing fungsi bisa identik (mengabaikan offset alamat), tapi saya tidak akan mengharapkan kompiler / penghubung untuk menghilangkan salinan yang tidak perlu, meskipun Anda dapat memeriksa kompiler / penghubung Anda jika Anda peduli.enum { A = 1, B = 2 }
- adalahA|B
"legal" dari logika program perspektif?)make
dan alat rekompilasi berbasis timestamp lainnya akan memicu kompilasi ulang klien ketika mereka diubah (buruk! )const
s:#define
ala#define S std::string("abc")
, tetapi konstanta menghindari pembangunan berulang temporaries yang berbeda pada setiap titik penggunaanconst
bernilai, yang meminimalkan pekerjaan dan dampak jika beralih di antara keduanya#define
s:#define X "x"
dan beberapa penggunaan klien ala"pre" X "post"
, jika Anda ingin atau perlu membuat X variabel runtime-berubah daripada konstan Anda memaksa pengeditan ke kode klien (bukan hanya kompilasi ulang), sedangkan transisi yang lebih mudah dariconst char*
atauconst std::string
diberikan mereka sudah memaksa pengguna untuk memasukkan operasi gabungan (misalnya"pre" + X + "post"
untukstring
)sizeof
secara langsung pada literal numerik yang ditentukanunsigned
){ 1, 2 }
yang dapat digunakan untuk menginisialisasi array, atau lainnya#define MICROSECONDS *1E-6
( pasti tidak merekomendasikan ini!)__FILE__
dan__LINE__
dapat dimasukkan ke dalam substitusi makro#if
pernyataan untuk menyertakan kode secara kondisional (lebih kuat daripada "jika" pasca-preproses karena kode tidak perlu dikompilasi jika tidak dipilih oleh preprocessor), gunakan#undef
-ine, redefine dll.make
dan alat rekompilasi berbasis timestamp lainnya akan memicu kompilasi ulang klien saat diubah (buruk!)Pendapat pribadi saya:
Sebagai aturan umum, saya menggunakan
const
s dan menganggapnya sebagai opsi paling profesional untuk penggunaan umum (meskipun yang lain memiliki kesederhanaan yang menarik bagi pemrogram malas yang lama ini).sumber
Jika ini adalah pertanyaan C ++ dan ia menyebutkan
#define
sebagai alternatif, maka ini adalah tentang konstanta "global" (yaitu file-scope), bukan tentang anggota kelas. Ketika datang ke konstanta seperti itu di C ++static const
adalah redundan. Dalam C ++const
memiliki tautan internal secara default dan tidak ada gunanya mendeklarasikannyastatic
. Jadi itu benar-benar tentangconst
vs#define
.Dan, akhirnya, di C ++
const
lebih disukai. Setidaknya karena konstanta seperti itu diketik dan dicakup. Tidak hanya ada alasan untuk lebih#define
lebihconst
, selain dari beberapa pengecualian.Konstanta string, BTW, adalah salah satu contoh pengecualian tersebut. Dengan
#define
konstanta d string seseorang dapat menggunakan fitur penggabungan waktu kompilasi dari kompiler C / C ++, seperti padaPS Sekali lagi, untuk berjaga-jaga, ketika seseorang menyebutkan
static const
sebagai alternatif#define
, biasanya itu berarti mereka berbicara tentang C, bukan tentang C ++. Saya ingin tahu apakah pertanyaan ini ditandai dengan benar ...sumber
#define
dapat menyebabkan hasil yang tidak terduga:Menghasilkan hasil yang salah:
Namun, jika Anda mengganti ini dengan konstanta:
Ini menghasilkan hasil yang benar:
Ini karena
#define
hanya mengganti teks. Karena melakukan ini secara serius dapat mengacaukan urutan operasi, saya akan merekomendasikan menggunakan variabel konstan sebagai gantinya.sumber
y
memiliki nilai5500
, gabungan sedikit-endian darix
dan 5.Menggunakan const statis sama seperti menggunakan variabel const lainnya dalam kode Anda. Ini berarti Anda dapat melacak dari mana pun informasi itu berasal, bukan dari #define yang hanya akan diganti dalam kode dalam proses pra-kompilasi.
Anda mungkin ingin melihat C ++ FAQ Lite untuk pertanyaan ini: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7
sumber
Biasanya Anda harus lebih suka const statis. Itu tidak memiliki kerugian. Prosesor terutama harus digunakan untuk kompilasi bersyarat (dan kadang-kadang untuk trics yang sangat kotor mungkin).
sumber
Menentukan konstanta dengan menggunakan arahan preprocessor
#define
tidak disarankan untuk diterapkan tidak hanya dalamC++
, tetapi juga dalamC
. Konstanta ini tidak akan memiliki tipe. Bahkan dalamC
diusulkan untuk digunakanconst
untuk konstanta.sumber
Silakan lihat di sini: static const vs define
biasanya deklarasi const (perhatikan itu tidak harus statis) adalah cara untuk pergi
sumber
Selalu lebih suka menggunakan fitur bahasa daripada beberapa alat tambahan seperti preprosesor.
Dari C ++ Core Guidelines
sumber
Jika Anda mendefinisikan konstanta untuk dibagikan di antara semua instance kelas, gunakan konstanta statis. Jika konstanta spesifik untuk setiap instance, cukup gunakan const (tetapi perhatikan bahwa semua konstruktor dari kelas harus menginisialisasi variabel anggota const ini dalam daftar inisialisasi).
sumber