Kita semua pasti telah menggunakan typedef
dan #define
s satu waktu atau yang lain. Hari ini ketika bekerja dengan mereka, saya mulai merenungkan sesuatu.
Pertimbangkan 2 situasi di bawah ini untuk menggunakan int
tipe data dengan nama lain:
typedef int MYINTEGER
dan
#define MYINTEGER int
Seperti situasi di atas, kita dapat, dalam banyak situasi, menyelesaikan dengan sangat baik menggunakan #define, dan juga melakukan hal yang sama dengan menggunakan typedef, walaupun cara kita melakukan hal yang sama mungkin sangat berbeda. #define juga dapat melakukan tindakan MACRO yang tidak bisa dilakukan typedef.
Meskipun alasan dasar untuk menggunakannya adalah berbeda, seberapa berbeda kerjanya? Kapan sebaiknya satu lebih disukai dari yang lain ketika keduanya dapat digunakan? Juga, apakah satu dijamin lebih cepat daripada yang lain dalam situasi apa? (misalnya, #define adalah arahan preprosesor, jadi semuanya dilakukan jauh lebih awal daripada saat kompilasi atau runtime).
sumber
Jawaban:
A
typedef
umumnya lebih disukai kecuali ada beberapa alasan aneh bahwa Anda secara khusus membutuhkan makro.macro melakukan substitusi teks, yang dapat melakukan kekerasan yang cukup besar pada semantik kode. Misalnya, diberikan:
Anda dapat secara legal menulis:
karena
short MYINTEGER
mengembang keshort int
.Di sisi lain, dengan typedef:
nama
MYINTEGER
adalah nama lain untuk jenisint
, bukan pengganti teks untuk kata kunci "int".Hal-hal menjadi lebih buruk dengan tipe yang lebih rumit. Misalnya, diberikan ini:
a
,,b
danc
semuanya adalah pointer, tetapid
achar
, karena baris terakhir diperluas ke:yang setara dengan
(Typedef untuk tipe pointer biasanya bukan ide yang baik, tetapi ini menggambarkan intinya.)
Kasus aneh lain:
sumber
typedef
, tapi cara yang tepat untuk membangun sebuah makro yang melakukan sesuatu dengan apa pun berikut adalah dengan menggunakan argumen:#define CHAR_PTR(x) char *x
. Ini setidaknya akan menyebabkan kompiler untuk mengambil jika digunakan secara tidak benar.const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
Sejauh ini, masalah terbesar dengan makro adalah bahwa mereka tidak dicakup. Itu saja menjamin penggunaan typedef. Juga, ini lebih jelas secara semantik. Ketika seseorang yang membaca kode Anda melihat sebuah definisi, ia tidak akan tahu tentang apa itu sampai ia membacanya dan memahami keseluruhan makro. Typedef memberi tahu pembaca bahwa nama tipe akan ditentukan. (Saya harus menyebutkan bahwa saya berbicara tentang C ++. Saya tidak yakin tentang scoping typedef untuk C, tapi saya kira itu mirip).
sumber
Kode sumber terutama ditulis untuk sesama pengembang. Komputer menggunakan versi kompilasi itu.
Dalam perspektif ini
typedef
membawa makna yang#define
tidak.sumber
Jawaban Keith Thompson sangat baik dan bersama dengan poin tambahan Tamás Szelei tentang pelingkupan harus memberikan semua latar belakang yang Anda butuhkan.
Anda harus selalu menganggap makro sebagai pilihan terakhir bagi mereka yang putus asa. Ada beberapa hal tertentu yang hanya dapat Anda lakukan dengan makro. Meski begitu, Anda harus berpikir panjang dan keras jika Anda benar-benar ingin melakukannya. Jumlah rasa sakit dalam debugging yang dapat disebabkan oleh makro yang rusak cukup besar, dan Anda harus mencari melalui file yang telah diproses. Sebaiknya lakukan itu sekali saja, untuk mengetahui lingkup masalah dalam C ++ - hanya ukuran file yang sudah diproses mungkin menjadi pembuka mata.
sumber
Selain semua komentar yang valid tentang ruang lingkup dan penggantian teks yang sudah diberikan, #define juga tidak sepenuhnya kompatibel dengan typedef! Yaitu ketika datang ke kasus pointer fungsi.
Ini menyatakan tipe
fptr_t
yang merupakan penunjuk ke fungsi tipevoid func(void)
.Anda tidak dapat mendeklarasikan tipe ini dengan makro.
#define fptr_t void(*)(void)
jelas tidak akan bekerja. Anda harus menulis sesuatu yang tidak jelas#define fptr_t(name) void(*name)(void)
, yang benar-benar tidak masuk akal dalam bahasa C, di mana kami tidak memiliki konstruktor.Array pointer tidak dapat dideklarasikan dengan #define:
typedef int(*arr_ptr)[10];
Dan sementara C tidak memiliki dukungan bahasa untuk keamanan jenis yang layak disebutkan, kasus lain di mana typedef dan #define tidak kompatibel adalah ketika Anda melakukan konversi jenis yang mencurigakan. Kompilator dan / atau alat analisis astatic mungkin dapat memberikan peringatan untuk konversi tersebut jika Anda menggunakan typedef.
sumber
char *(*(*foo())[])();
(foo
adalah fungsi yang mengembalikan pointer ke array pointer ke fungsi yang mengembalikan pointer kechar
).Gunakan alat dengan daya paling sedikit yang menyelesaikan pekerjaan, dan yang paling banyak peringatan. #define dievaluasi di preprocessor, Anda sebagian besar sendirian di sana. typedef dievaluasi oleh kompiler. Cek diberikan dan typedef hanya bisa mendefinisikan tipe, seperti namanya. Jadi, dalam contoh Anda pasti pergi untuk typedef.
sumber
Di luar argumen normal terhadap define, bagaimana Anda menulis fungsi ini menggunakan Macro?
Ini jelas merupakan contoh sepele, tetapi fungsi itu dapat menjumlahkan setiap urutan maju maju dari jenis yang mengimplementasikan penambahan *. Typedef yang digunakan seperti ini adalah elemen penting dari Generic Programming .
* Saya baru saja menulis ini di sini, saya meninggalkan kompilasi sebagai latihan untuk pembaca :-)
EDIT:
Jawaban ini tampaknya menyebabkan banyak kebingungan, jadi izinkan saya menjelaskannya lebih lanjut. Jika Anda melihat ke dalam definisi vektor STL, Anda akan melihat sesuatu yang mirip dengan yang berikut:
Penggunaan typedef dalam wadah standar memungkinkan fungsi generik (seperti yang saya buat di atas) untuk referensi jenis ini. Fungsi "Jumlah" templated pada jenis wadah (
std::vector<int>
), bukan pada jenis yang disimpan di dalam wadah (int
). Tanpa typedef, tidak mungkin untuk merujuk pada tipe internal itu.Oleh karena itu, typedef sangat penting bagi Modern C ++ dan ini tidak dimungkinkan dengan makro.
sumber
typedef
, bukan makro vs fungsi sebaris.typedef
cocok dengan filosofi C ++: semua kemungkinan pemeriksaan / konfirmasi dalam waktu kompilasi.#define
hanyalah trik preprosesor yang menyembunyikan banyak semantik ke kompiler. Anda tidak perlu khawatir tentang kinerja kompilasi lebih dari kebenaran kode.Saat Anda membuat tipe baru, Anda mendefinisikan "hal" baru yang dimanipulasi domain program Anda. Jadi Anda dapat menggunakan "hal" ini untuk membuat fungsi dan kelas, dan Anda dapat menggunakan kompiler untuk keuntungan Anda untuk melakukan pemeriksaan statis. Bagaimanapun, karena C ++ kompatibel dengan C, ada banyak konversi implisit antara
int
dan tipe berbasis int yang tidak menghasilkan peringatan. Jadi Anda tidak mendapatkan kekuatan penuh dari pemeriksaan statis dalam kasus ini. Namun, Anda dapat menggantitypedef
dengan denganenum
untuk menemukan konversi tersirat. Misalnya: Jika sudahtypedef int Age;
, maka Anda dapat mengganti denganenum Age { };
dan Anda akan mendapatkan semua jenis kesalahan dengan konversi tersirat antaraAge
danint
.Hal lain:
typedef
bisa di dalam anamespace
.sumber
Menggunakan mendefinisikan bukan typedefs bermasalah di bawah aspek penting lainnya, dan yaitu konsep ciri-ciri tipe. Pertimbangkan kelas yang berbeda (pikirkan wadah standar) yang semuanya menentukan jenis spesifik mereka. Anda dapat menulis kode generik dengan merujuk pada typedefs. Contohnya termasuk persyaratan wadah umum (c ++ standar 23.2.1 [container.requirements.general]) seperti
Semua ini tidak dapat diekspresikan secara umum dengan makro karena tidak dicakup.
sumber
Jangan lupa implikasi ini pada debugger Anda. Beberapa debugger tidak menangani #define dengan baik. Bermain-main dengan keduanya di debugger yang Anda gunakan. Ingat, Anda akan menghabiskan lebih banyak waktu untuk membacanya daripada menulisnya.
sumber
"#define" akan menggantikan apa yang Anda tulis setelah itu, typedef akan membuat jenis. Jadi jika Anda ingin memiliki tipe custome - gunakan typedef. Jika Anda membutuhkan makro - gunakan define.
sumber