Yanıtlar:
C ++ 'da, sadece ince bir fark vardır. Fark yaratan C'nin bir gerilemesi.
C dili standardı ( C89 §3.1.2.3 , C99 §6.2.3 ve C11 §6.2.3 ), etiket tanımlayıcıları ( struct
/ union
/ için enum
) ve normal tanımlayıcılar ( typedef
ve diğer tanımlayıcılar) dahil olmak üzere farklı tanımlayıcı kategorileri için ayrı ad alanları zorunlu kılar .
Az önce söylediyseniz:
struct Foo { ... };
Foo x;
Foo
yalnızca etiket ad alanında tanımlandığı için bir derleyici hatası alırsınız .
Bunu şöyle beyan etmeniz gerekir:
struct Foo x;
Ne zaman başvurmak istersen Foo
, onu her zaman aramak zorunda kalırsın struct Foo
. Bu can sıkıcı olur, böylece şunları ekleyebilirsiniz typedef
:
struct Foo { ... };
typedef struct Foo Foo;
Şimdi struct Foo
(etiket ad alanında) ve sadece düz Foo
(sıradan tanımlayıcı ad alanında) her ikisi de aynı şeyi ifade eder Foo
ve struct
anahtar kelime olmadan türdeki nesneleri serbestçe bildirebilirsiniz .
Yapı:
typedef struct Foo { ... } Foo;
sadece bildirinin kısaltmasıdır ve typedef
.
En sonunda,
typedef struct { ... } Foo;
anonim bir yapı beyan eder ve bunun typedef
için bir a oluşturur . Bu nedenle, bu yapı ile etiket ad alanında bir ad yoktur, yalnızca typedef ad alanında bir ad vardır. Bu, aynı zamanda ileri bildirilemeyeceği anlamına gelir. Bir ileri bildirimde bulunmak istiyorsanız, buna etiket ad alanında bir ad vermeniz gerekir .
C ++ 'da, tüm struct
/ union
/ enum
/ class
bildirimleri typedef
, ad aynı ada sahip başka bir bildirimle gizlenmediği sürece örtük olarak düzenlenmiş gibi davranır . Tüm ayrıntılar için Michael Burr'un cevabına bakınız.
Gelen bu DDJ makalesinde Dan Saks size yapılar typedef yoksa böcek yoluyla geçebilirim bir küçük alanı açıklar (ve sınıflar!):
İsterseniz, C ++ 'ın her etiket adı için bir typedef oluşturduğunu düşünebilirsiniz.
typedef class string string;
Ne yazık ki, bu tamamen doğru değil. Keşke bu kadar basit olsaydı, ama değil. C ++, C ile uyumsuzluklar getirmeden yapılar, birlikler veya numaralandırmalar için bu tür tanımlamalar oluşturamaz.
Örneğin, bir C programının hem function hem de status adlı bir yapı bildirdiğini varsayalım:
int status(); struct status;
Yine, bu kötü bir uygulama olabilir, ancak C'dir. Bu programda durum (kendi başına) işlevi ifade eder; struct status türünü ifade eder.
C ++ etiketler için otomatik olarak typedefs oluşturduysa, bu programı C ++ olarak derlediğinizde, derleyici aşağıdakileri oluşturur:
typedef struct status status;
Ne yazık ki, bu tür ad işlev adıyla çakışır ve program derlenmez. Bu yüzden C ++ her etiket için bir typedef oluşturamaz.
C ++ 'da, etiketler tıpkı typedef adları gibi davranır, ancak bir program bir etiketle aynı ada ve kapsama sahip bir nesne, işlev veya numaralandırıcı bildirebilir. Bu durumda, nesne, işlev veya numaralandırıcı adı etiket adını gizler. Program, etiket adına yalnızca etiket adının önünde class, struct, union veya enum (uygunsa) anahtar sözcüğünü kullanarak başvurabilir. Bu anahtar kelimelerden birini ve ardından etiketi içeren bir tür adı, ayrıntılı bir tür belirticidir. Örneğin, yapı durumu ve numaralandırma ayı ayrıntılı tür belirticileridir.
Böylece, her ikisini de içeren bir C programı:
int status(); struct status;
C ++ ile derlendiğinde aynı davranır. Yalnızca ad durumu işleve karşılık gelir. Program, yalnızca ayrıntılı tür belirteci yapı durumunu kullanarak türe başvurabilir.
Peki bu, hataların programlara girmesine nasıl izin verir? Liste 1'deki programı düşünün . Bu program, varsayılan bir kurucu ile bir sınıf foo ve bir foo nesnesini char const * 'a dönüştüren bir dönüştürme operatörü tanımlar. İfade
p = foo();
genel olarak bir foo nesnesi oluşturmalı ve dönüştürme operatörünü uygulamalıdır. Sonraki çıktı bildirimi
cout << p << '\n';
foo sınıfını görüntülemelidir, ama göstermez. Foo işlevini görüntüler.
Bu şaşırtıcı sonuç, program Liste 2'de gösterilen lib.h üstbilgisini içerdiği için oluşur . Bu başlık, foo olarak da adlandırılan bir işlevi tanımlar. Foo işlev adı, foo sınıf adını gizler, bu nedenle main içindeki foo'ya yapılan başvuru sınıfı değil, işlevi ifade eder. main, sınıfa, yalnızca aşağıdaki gibi ayrıntılı bir tür belirteci kullanarak başvurabilir
p = class foo();
Program boyunca bu karışıklığı önlemenin yolu, foo sınıf adı için aşağıdaki typedef'i eklemektir:
typedef class foo foo;
sınıf tanımından hemen önce veya sonra. Bu typedef, tip adı foo ile foo (kitaplıktan) işlev adı arasında bir derleme zamanı hatası tetikleyecek çakışmaya neden olur.
Tabii bu tip tanımları yazan hiç kimseyi bilmiyorum. Çok fazla disiplin gerektirir. Liste 1'dekiler gibi hataların görülme olasılığı oldukça küçük olduğundan, birçoğunuz bu sorundan asla kaçmazsınız. Ancak yazılımınızdaki bir hata bedensel yaralanmaya neden olabilirse, hata ne kadar olası olursa olsun typedefs'i yazmalısınız.
Neden hiç kimse sınıf ile aynı kapsamda bir işlev veya nesne adı ile bir sınıf adını gizlemek isteyebilirsiniz düşünemiyorum. C'deki gizleme kuralları bir hataydı ve C ++ 'daki sınıflara genişletilmemeliydi. Aslında, hatayı düzeltebilirsiniz, ancak gerekli olmayan ekstra programlama disiplini ve çaba gerektirir.
Listing 1
ve Listing 2
bağlantılar koptu. Bir bak.
Bir diğer önemli fark: typedef
ileri iletilemez. Yani için typedef
seçenek size gereken #include
dosya içeren typedef
her şeyi, yani #include
senin s .h
da doğrudan veya gerekli ve benzeri olmadığını bu dosyayı içerir. İnşaat sürelerinizi büyük projelerde kesinlikle etkileyebilir.
Olmadan, typedef
bazı durumlarda dosyanızın struct Foo;
üst kısmına bir ileri bildirimi .h
ve sadece dosyanızdaki #include
yapı tanımı ekleyebilirsiniz .cpp
.
Orada olan bir fark, ama ince. Şuna şu şekilde bakın: struct Foo
yeni bir tür sunar. İkincisi, adlandırılmamış bir tür için Foo (yeni bir tür değil) adlı bir takma ad oluşturur struct
.
7.1.3 typedef belirteci
1 [...]
Typedef belirtici ile bildirilen bir ad, typedef-name olur. Bildirimi kapsamında, bir typedef-name sözdizimsel olarak bir anahtar kelimeye eşdeğerdir ve 8. maddede açıklanan şekilde tanımlayıcıyla ilişkilendirilen türü adlandırır. Bir typedef-adı, başka bir türle eşanlamlıdır. Typedef-name , sınıf bildirimi (9.1) veya numaralandırma bildirimi gibi yeni bir tür sunmaz.
8 typedef bildirimi adsız bir sınıf (veya enum) tanımlarsa, bildirim tarafından bu sınıf türü (veya enum türü) olarak bildirilen ilk typedef adı, yalnızca bağlantı amacıyla sınıf türünü (veya enum türünü) belirtmek için kullanılır ( 3.5). [ Misal:
typedef struct { } *ps, S; // S is the class name for linkage purposes
Yani, bir typedef her zaman başka bir tür için yer tutucu / eşanlamlı olarak kullanılır.
İleri bildirimini typedef struct ile kullanamazsınız.
Yapının kendisi anonim bir tiptir, bu nedenle bildirmek için gerçek bir adınız yoktur.
typedef struct{
int one;
int two;
}myStruct;
Böyle bir ileri bildirim işe yaramaz:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
myStruct
, etiket ad alanında ve typedef_ed myStruct
normal ad alanında, işlev adı, yerel değişken adları gibi diğer tanımlayıcıların yaşadığı yerlerde yaşar. Bu nedenle, herhangi bir çakışma olmamalıdır .. İçinde herhangi bir hata olduğundan şüphe ederseniz, kodumu gösterebilirim.
typedef
ileriye yönelik bir beyanınız typedef
olduğunda isimsiz yapıya atıfta bulunmadığını anlıyorum . Bunun yerine, ileri bildirim etiketli tamamlanmamış bir yapı bildirir myStruct
. Ayrıca, tanımını görmeden typedef
, typedef
ed adını kullanan işlev prototipi yasal değildir. Bu nedenle, myStruct
bir türü belirtmek için kullanmamız gerektiğinde tüm typedef'i dahil etmeliyiz . Seni yanlış anladıysam düzelt. Teşekkürler.
C ++ 'daki' typedef struct 'ile' struct 'arasındaki önemli bir fark,' typedef structs 'içindeki satır içi üye başlatmanın işe yaramayacağıdır.
// 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
da başlatılır. Coliru çevrimiçi IDE'deki testi görün (42'ye başlattım, böylece atamanın gerçekten gerçekleştiğini sıfırdan daha açık).
C ++ 'da bir fark yoktur, ancak C'ye açıkça yapmadan yapı Foo örneklerini bildirmenize izin vereceğine inanıyorum:
struct Foo bar;