'++' ve 'typedef struct' arasındaki fark C ++?


Yanıtlar:


1202

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 ( typedefve 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;

Fooyalnı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 Foove structanahtar 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 typedefiç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/ classbildirimleri 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.


53
Söylediğiniz doğru olsa da, AFAIK, 'typedef struct {...} Foo;' adsız bir yapı için bir takma ad oluşturur.
dirkgently

25
İyi yakala, "typedef struct Foo {...} Foo;" arasında ince bir fark var. ve "typedef struct {...} Foo;".
Adam Rosenfield

8
C'de, yapı etiketleri, birleşim etiketleri ve numaralandırma etiketleri, yukarıda belirtildiği gibi iki tanesini kullanarak (yapı ve birleşim) yerine bir ad alanını paylaşır; typedef adları için başvurulan ad alanı gerçekten ayrıdır. Bu, her iki 'sendika x {...};' ve 'struct x {...};' tek bir kapsamda.
Jonathan Leffler

10
Oldukça tipik olmayan bir şey dışında, söz konusu iki kod parçası arasındaki başka bir fark, Foo'nun ilk örnekte bir yapıcı tanımlayabildiği, ancak ikincisinde değil (anonim sınıflar yapıcıları veya yıkıcıları tanımlayamadığı için) .
Steve Jessop

1
@Lazer: Orada olan ince farklar, ancak Adam araçlarının bir typedef olmadan değişkenleri bildirmek için 'Tür var' kullanabileceği (o söylemeye devam ediyor gibi).
Fred Nurk

226

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.


11
"Class foo ()" komutunu denemeniz ve başarısız olması durumunda: ISO C ++ uygulamasında "class foo ()" geçersiz bir yapıdır (makale standardizasyondan önce '97 olarak yazılmıştır). "Typedef class foo foo;" daha sonra "foo ();" diyebilirsiniz. (çünkü o zaman typedef-name, işlevin adından sözlüksel olarak daha yakındır). Sözdizimsel olarak, T () 'de T, basit bir tür belirteci olmalıdır. ayrıntılı tür belirticilerine izin verilmez. Yine de bu elbette iyi bir cevap.
Johannes Schaub - litb

Listing 1ve Listing 2bağlantılar koptu. Bir bak.
Prasoon Saurav

3
Sınıflar ve işlevler için farklı adlandırma kuralları kullanırsanız, ek typedefs eklemenize gerek kalmadan adlandırma çakışmasından da kaçınabilirsiniz.
zstewart

64

Bir diğer önemli fark: typedefileri iletilemez. Yani için typedefseçenek size gereken #includedosya içeren typedefher şeyi, yani #includesenin s .hda doğrudan veya gerekli ve benzeri olmadığını bu dosyayı içerir. İnşaat sürelerinizi büyük projelerde kesinlikle etkileyebilir.

Olmadan, typedefbazı durumlarda dosyanızın struct Foo;üst kısmına bir ileri bildirimi .hve sadece dosyanızdaki #includeyapı tanımı ekleyebilirsiniz .cpp.


Yapının tanımını ortaya çıkarmak inşa süresini neden etkiler? Derleyici, Foo * nextFoo; ?
Zengin

3
Onun gerçekten ekstra kontrol değil, sadece derleyici ile ilgilenmesi gereken daha fazla kod. İçerdiği zincirde bir yerde typedef ile karşılaşan her cpp dosyası için, typedef'i derler. Daha büyük projelerde, typedef'i içeren .h dosyası, önceden derlenmiş başlıklar çok yardımcı olsa da, yüzlerce kez kolayca derlenebilir. Bir ileri bildirim kullanarak kurtulmak mümkün ise, o zaman tam yapı belirtimi içeren .h'nin dahil edilmesini sadece gerçekten önemseyen koda sınırlamak daha kolaydır ve bu nedenle karşılık gelen içerme dosyası daha az derlenir.
Joe

lütfen önceki yorumcuyu @ (gönderinin sahibi dışında). Neredeyse cevabını özledim. Yine de bilgilendirdiğin için teşekkürler.
Zengin

Bu aynı yapıyı birkaç kez aynı adla yazabileceğiniz C11'de artık geçerli değil.
michaelmeyer

32

Orada olan bir fark, ama ince. Şuna şu şekilde bakın: struct Fooyeni 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.


10

İ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

İşlev prototipi için ikinci hatayı almıyorum. Neden "yeniden tanımlama; farklı temel türler" diyor? derleyicinin myStruct'ın tanımının neye benzediğini bilmesine gerek yok, değil mi? Kod parçasıyla (typedef olanı veya ileri bildirim olanından) bağımsız olarak, myStruct bir yapı türünü belirtir, değil mi?
Zengin

@ Zengin Bir isim çatışması olduğundan şikayet ediyor. "MyStruct adlı bir yapı arayın" diyen ileri bir bildiri var ve sonra isimsiz bir yapıyı "myStruct" olarak yeniden adlandıran typedef var.
Yochai Timmer

Yani hem typedef hem de forward bildirimini aynı dosyaya mı koyuyorsunuz? Yaptım ve gcc iyi derledi. myStruct, isimsiz yapı olarak doğru yorumlanır. Etiket myStruct, etiket ad alanında ve typedef_ed myStructnormal 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.
Zengin

@Rich GCC aynı hatayı veriyor, metin biraz değişiyor: gcc.godbolt.org/…
Yochai Timmer

Ben sadece ed adıyla typedefileriye yönelik bir beyanınız typedefolduğ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, typedefed adını kullanan işlev prototipi yasal değildir. Bu nedenle, myStructbir türü belirtmek için kullanmamız gerektiğinde tüm typedef'i dahil etmeliyiz . Seni yanlış anladıysam düzelt. Teşekkürler.
Zengin

0

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; };

3
Doğru değil. Her iki durumda xda 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).
Colin D Bennett

Aslında, Visual Studio 2013'te test ettim ve başlatılmadı. Bu, üretim kodunda karşılaştığımız bir sorundu. Tüm derleyiciler farklıdır ve yalnızca belirli kriterleri karşılamaları gerekir.
user2796283

-3

C ++ 'da bir fark yoktur, ancak C'ye açıkça yapmadan yapı Foo örneklerini bildirmenize izin vereceğine inanıyorum:

struct Foo bar;

3
@ Dirkgently cevabı bak --- orada olan bir fark, ama zekice.
Keith Pinson

-3

Yapısal bir veri türü oluşturmaktır. Typedef, veri türü için bir takma ad ayarlamaktır.


12
Soruyu anlamadınız.
Kış
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.