Di C ++ , apakah ada perbedaan antara:
struct Foo { ... };
dan:
typedef struct { ... } Foo;
Di C ++ , apakah ada perbedaan antara:
struct Foo { ... };
dan:
typedef struct { ... } Foo;
Jawaban:
Dalam C ++, hanya ada sedikit perbedaan. Ini peninggalan dari C, di mana itu membuat perbedaan.
Standar bahasa C ( C89 §3.1.2.3 , C99 §6.2.3 , dan C11 §6.2.3 ) mengamanatkan ruang nama yang terpisah untuk berbagai kategori pengidentifikasi, termasuk pengidentifikasi tag (untuk struct
/ union
/ enum
) dan pengidentifikasi biasa (untuk typedef
dan pengidentifikasi lainnya) .
Jika Anda baru saja mengatakan:
struct Foo { ... };
Foo x;
Anda akan mendapatkan kesalahan kompiler, karena Foo
hanya didefinisikan dalam tag namespace.
Anda harus mendeklarasikannya sebagai:
struct Foo x;
Kapan pun Anda ingin merujuk ke Foo
, Anda harus selalu menyebutnya a struct Foo
. Ini menjadi menjengkelkan dengan cepat, sehingga Anda dapat menambahkan typedef
:
struct Foo { ... };
typedef struct Foo Foo;
Sekarang struct Foo
(dalam tag namespace) dan hanya polos Foo
(dalam namespace pengidentifikasi biasa) keduanya merujuk pada hal yang sama, dan Anda dapat dengan bebas mendeklarasikan objek jenis Foo
tanpa struct
kata kunci.
Konstruk:
typedef struct Foo { ... } Foo;
hanyalah singkatan untuk deklarasi dan typedef
.
Akhirnya,
typedef struct { ... } Foo;
mendeklarasikan struktur anonim dan membuat typedef
untuk itu. Jadi, dengan konstruk ini, ia tidak memiliki nama di namespace tag, hanya nama di namespace typedef. Ini berarti juga tidak dapat dideklarasikan ke depan. Jika Anda ingin membuat deklarasi maju, Anda harus memberikannya nama di tag namespace .
Dalam C ++, semua struct
/ union
/ enum
/ class
deklarasi bertindak seperti mereka implisit typedef
'ed, selama nama tersebut tidak disembunyikan oleh deklarasi lain dengan nama yang sama. Lihat jawaban Michael Burr untuk detail selengkapnya.
Dalam artikel DDJ ini , Dan Saks menjelaskan satu area kecil di mana bug dapat menjalar jika Anda tidak mengetikkan struct Anda (dan kelas!):
Jika mau, Anda dapat membayangkan bahwa C ++ menghasilkan typedef untuk setiap nama tag, seperti
typedef class string string;
Sayangnya, ini tidak sepenuhnya akurat. Saya berharap sesederhana itu, tetapi tidak. C ++ tidak dapat menghasilkan typedefs seperti untuk struct, unions, atau enums tanpa memperkenalkan ketidakcocokan dengan C.
Misalnya, misalkan program C mendeklarasikan fungsi dan status yang dinamai struct:
int status(); struct status;
Sekali lagi, ini mungkin praktik yang buruk, tetapi C. Dalam program ini, status (dengan sendirinya) mengacu pada fungsi; status struct mengacu pada jenisnya.
Jika C ++ memang secara otomatis menghasilkan typedefs untuk tag, maka ketika Anda mengkompilasi program ini sebagai C ++, kompiler akan menghasilkan:
typedef struct status status;
Sayangnya, nama jenis ini akan bertentangan dengan nama fungsi, dan program tidak mau dikompilasi. Itu sebabnya C ++ tidak bisa begitu saja menghasilkan typedef untuk setiap tag.
Dalam C ++, tag bertindak seperti nama typedef, kecuali bahwa suatu program dapat mendeklarasikan objek, fungsi, atau enumerator dengan nama yang sama dan cakupan yang sama dengan tag. Dalam hal itu, objek, fungsi, atau nama enumerator menyembunyikan nama tag. Program dapat merujuk ke nama tag hanya dengan menggunakan kelas kata kunci, struct, union, atau enum (yang sesuai) di depan nama tag. Nama tipe yang terdiri dari salah satu kata kunci ini diikuti oleh tag adalah penspesifikasi tipe-terperinci. Misalnya, status struct dan bulan enum adalah penentu tipe yang diuraikan.
Dengan demikian, program C yang mengandung keduanya:
int status(); struct status;
berperilaku sama ketika dikompilasi sebagai C ++. Status nama sendiri mengacu pada fungsi. Program ini dapat merujuk ke tipe hanya dengan menggunakan status struct specifier tipe-elaborated.
Jadi bagaimana ini memungkinkan bug untuk menyusup ke dalam program? Pertimbangkan program di Listing 1 . Program ini mendefinisikan kelas foo dengan konstruktor default, dan operator konversi yang mengubah objek foo menjadi char const *. Ekspresi
p = foo();
pada dasarnya harus membuat objek foo dan menerapkan operator konversi. Pernyataan output selanjutnya
cout << p << '\n';
harus menampilkan kelas foo, tetapi tidak. Ini menampilkan fungsi foo.
Hasil mengejutkan ini terjadi karena program menyertakan header lib.h yang ditunjukkan pada Listing 2 . Header ini mendefinisikan fungsi yang juga bernama foo. Nama fungsi foo menyembunyikan nama kelas foo, jadi referensi ke foo pada utamanya merujuk ke fungsi, bukan kelas. main dapat merujuk ke kelas hanya dengan menggunakan specifier tipe-elaborated, seperti pada
p = class foo();
Cara untuk menghindari kebingungan seperti itu di seluruh program adalah dengan menambahkan typedef berikut untuk nama kelas foo:
typedef class foo foo;
segera sebelum atau setelah definisi kelas. Typedef ini menyebabkan konflik antara nama jenis foo dan nama fungsi foo (dari perpustakaan) yang akan memicu kesalahan waktu kompilasi.
Saya tahu tidak ada orang yang benar-benar menulis typedef ini sebagai hal yang biasa. Itu membutuhkan banyak disiplin. Karena insiden kesalahan seperti yang ada di Listing 1 mungkin cukup kecil, Anda banyak yang tidak pernah mengalami masalah ini. Tetapi jika kesalahan dalam perangkat lunak Anda dapat menyebabkan cedera tubuh, maka Anda harus menulis typedef tidak peduli seberapa kecil kesalahannya.
Saya tidak bisa membayangkan mengapa ada orang yang ingin menyembunyikan nama kelas dengan fungsi atau nama objek dalam lingkup yang sama dengan kelas. Aturan persembunyian di C adalah kesalahan, dan mereka seharusnya tidak diperluas ke kelas di C ++. Memang, Anda dapat memperbaiki kesalahan, tetapi membutuhkan disiplin pemrograman ekstra dan upaya yang seharusnya tidak perlu.
Listing 1
dan Listing 2
tautan rusak. Silahkan lihat.
Satu lagi perbedaan penting: typedef
s tidak dapat dideklarasikan ke depan. Jadi untuk typedef
opsi Anda harus #include
file yang berisi typedef
, artinya segala sesuatu yang #include
Anda .h
miliki juga termasuk file itu secara langsung membutuhkannya atau tidak, dan seterusnya. Ini pasti dapat memengaruhi waktu pembangunan Anda pada proyek yang lebih besar.
Tanpa itu typedef
, dalam beberapa kasus Anda hanya dapat menambahkan deklarasi maju struct Foo;
di bagian atas .h
file Anda , dan hanya #include
definisi struct dalam .cpp
file Anda .
Ada adalah perbedaan, tapi halus. Lihatlah seperti ini: struct Foo
memperkenalkan tipe baru. Yang kedua membuat alias bernama Foo (dan bukan tipe baru) untuk struct
tipe yang tidak disebutkan namanya .
7.1.3 Penspesifikasi typedef
1 [...]
Sebuah nama yang dideklarasikan dengan penspesifikasi typedef menjadi nama typedef. Dalam lingkup deklarasi, nama typedef secara sintaksis setara dengan kata kunci dan nama tipe yang terkait dengan pengidentifikasi dengan cara yang dijelaskan dalam Klausa 8. Nama typedef dengan demikian merupakan sinonim untuk jenis lain. Nama typedef tidak memperkenalkan tipe baru seperti halnya deklarasi kelas (9.1) atau deklarasi enum.
8 Jika deklarasi typedef mendefinisikan kelas tanpa nama (atau enum), nama typedef pertama yang dideklarasikan oleh deklarasi menjadi tipe kelas (atau tipe enum) digunakan untuk menunjukkan tipe kelas (atau tipe enum) hanya untuk keperluan hubungan saja ( 3.5). [Contoh:
typedef struct { } *ps, S; // S is the class name for linkage purposes
Jadi, typedef selalu digunakan sebagai pengganti / sinonim untuk jenis lain.
Anda tidak dapat menggunakan deklarasi maju dengan struct typedef.
Struct itu sendiri adalah jenis anonim, sehingga Anda tidak memiliki nama sebenarnya untuk meneruskan menyatakan.
typedef struct{
int one;
int two;
}myStruct;
Deklarasi maju seperti ini tidak akan berfungsi:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
myStruct
tinggal di namespace tag dan typedef_ed myStruct
tinggal di namespace normal tempat pengidentifikasi lain seperti nama fungsi, nama variabel lokal tinggal. Jadi tidak boleh ada konflik .. Saya bisa menunjukkan kode saya jika Anda ragu ada kesalahan di dalamnya.
typedef
, meneruskan deklarasi dengan typedef
nama ed, tidak merujuk pada struktur yang tidak disebutkan namanya. Sebaliknya deklarasi maju menyatakan struktur tidak lengkap dengan tag myStruct
. Juga, tanpa melihat definisi typedef
, prototipe fungsi menggunakan typedef
nama ed tidak sah. Jadi kita harus memasukkan seluruh typedef setiap kali kita perlu menggunakan myStruct
untuk menunjukkan suatu tipe. Perbaiki saya jika saya salah paham. Terima kasih.
Perbedaan penting antara 'typedef struct' dan 'struct' dalam C ++ adalah inisialisasi anggota inline di 'typedef structs' tidak akan berfungsi.
// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;
// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };
x
diinisialisasi. Lihat tes di IDE online Coliru (saya menginisialisasi ke 42 sehingga lebih jelas daripada dengan nol bahwa tugas telah benar-benar terjadi).
Tidak ada perbedaan dalam C ++, tapi saya percaya pada C akan memungkinkan Anda untuk mendeklarasikan instance dari struct Foo tanpa secara eksplisit melakukan:
struct Foo bar;
Struct adalah membuat tipe data. Typedef adalah untuk menetapkan nama panggilan untuk tipe data.