typedefs ve #defines


32

Hepimiz kesinlikle kullanmış typedeflar ve #definebir kez ya da başka bu. Bugün onlarla çalışırken bir şey üzerinde düşünmeye başladım.

intVeri türünü başka bir adla kullanmak için aşağıdaki 2 durumu göz önünde bulundurun :

typedef int MYINTEGER

ve

#define MYINTEGER int

Yukarıdaki durum gibi, birçok durumda da #define kullanarak bir şeyi başarabiliriz ve aynı şeyi typedef kullanarak da yapabiliriz, ancak aynı şekilde yaptığımız yöntemler oldukça farklı olabilir. #define ayrıca bir typedef öğesinin yapamadığı MACRO eylemlerini de yapabilir.

Bunları kullanmanın temel nedeni farklı olsa da, çalışmaları ne kadar farklı? Her ikisi de ne zaman kullanılabilir? Ayrıca, biri hangi durumlarda diğerinden daha hızlı olduğu garantilidir? (örneğin, #define önişlemci yönergesidir, bu nedenle her şey derleme veya çalışma süresinden daha erken yapılır).


8
#Define'ı ne için kullandıklarını kullanın. Kod yazarken bilmediğiniz özelliklere dayalı koşullu derleme (OS / Compiler gibi). Aksi takdirde dil yapılarını kullanın.
Martin York

Yanıtlar:


67

A typedef, özellikle bir makroya ihtiyaç duymanızın garip bir nedeni olmadıkça, genellikle tercih edilir.

Makrolar, kodun anlamsallığına ciddi şiddet uygulayabilen metinsel ikame yapar. Örneğin, verilen:

#define MYINTEGER int

yasal olarak yazabilirsin:

short MYINTEGER x = 42;

çünkü short MYINTEGERgenişler short int.

Öte yandan, typedef ile:

typedef int MYINTEGER:

ad MYINTEGER, tür için başka bir addır int, "int" anahtar sözcüğünün yerine geçen bir metin yerine geçer.

İşler daha karmaşık tiplerle daha da kötüleşiyor. Örneğin, bu verilen:

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

a, bVe ctüm noktalar şunlardır, ancak dbir olan charson satırı genişler, çünkü:

char* c, d;

hangi eşdeğer

char *c;
char d;

(İşaretçi türleri için typedef'ler genellikle iyi bir fikir değildir, ancak bu, noktayı gösterir.)

Başka bir garip dava:

#define DWORD long
DWORD double x;     /* Huh? */

1
İşaretçiler tekrar suçlanacak! :) Bu tür makroları kullanmanın akıllıca olmadığı işaretçiler dışında başka bir örnek var mı?
c0da

2
Asla bir makrosunu yerine kullanmayacağımdan eminim typedef, ama aşağıdakileri yapan bir şey yapan bir makro oluşturmanın doğru yolu argümanları kullanmaktır #define CHAR_PTR(x) char *x. Bu en azından yanlış kullanılırsa derleyicinin kıvrılmasına neden olur.
Blrfl

1
Unutma:const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
Jon Purdy

3
Aynı zamanda hata mesajlarını anlaşılmaz hale getirdiğini de tanımlar
Thomas Bonini,

Makroları suiistimal eden acıya bir örnek (makrolar ve yazım tanımları ile doğrudan ilişkili olmasa da): github.com/Keith-S-Thompson/42
Keith Thompson

15

Bugüne kadar makrolarla ilgili en büyük sorun, kapsam dışı olmalarıdır. Bu tek başına typedef kullanımını garanti eder. Ayrıca, anlamsal olarak daha açıktır. Kodunuzu okuyan bir kişi bir tanım gördüğünde, onu okuyana ve tüm makrosu anlayana kadar ne hakkında olduğunu bilemez. Bir typedef, okuyucuya bir tip adının tanımlanacağını söyler. (C ++ hakkında konuştuğumu söylemeliyim. C için typedef kapsamından emin değilim ama sanırım benzer.)


Ancak soruda verilen örnekte gösterdiğim gibi, typedef ve #define aynı sayıda kelimeyi kullanır! Ayrıca, bir makrodaki bu 3 kelime, kelimeler aynı olduğu için, ancak sadece yeniden düzenlendikleri için herhangi bir karışıklığa neden olmaz. :)
c0da

2
Evet, ama bu kısalıkla ilgili değil. Bir makro, yazım tanımlamanın yanı sıra zillion başka şeyler de yapabilir. Ama şahsen ben kapsamın en önemli olduğunu düşünüyorum.
Tamás Szelei,

2
@ c0da - typedef'ler için makro kullanmamalı, typedef'ler için typedef kullanmalısınız. Makronun gerçek etkisi, çeşitli tepkilerle gösterildiği gibi, çok farklı olabilir.
Joris Timmermans

15

Kaynak kod esas olarak diğer geliştiriciler için yazılmıştır. Bilgisayarlar derlenmiş bir versiyonunu kullanırlar.

Bu bakış açısında olmayan typedefbir anlam taşıyor #define.


6

Keith Thompson'ın cevabı çok iyidir ve Tamás Szelei ile birlikte, kapsam belirleme konusundaki ek hususları ihtiyacınız olan tüm arka planı sağlamalıdır.

Her zaman çaresizliğin en son çare olan makroları düşünmelisin. Sadece makrolarla yapabileceğiniz bazı şeyler var. O zaman bile, gerçekten yapmak istiyorsan uzun ve sıkı düşünmelisin. Dikkatlice kırılmış makroların neden olabileceği hata ayıklamadaki ağrı miktarı oldukça fazladır ve önceden işlenmiş dosyalara bakmanızı sağlar. C ++ 'da sorunun kapsamı hakkında bir fikir edinmek için sadece bir kez yapmaya değer - önceden işlenmiş dosyanın boyutu bir göz açıcı olabilir.


5

Daha önce verilen kapsam ve metin değiştirme hakkındaki tüm geçerli açıklamalara ek olarak, #define ayrıca typedef! Yani fonksiyon göstergeleri söz konusu olduğunda.

typedef void(*fptr_t)(void);

Bu, türün fptr_tbir işlevine işaretçi olan bir tür bildirir void func(void).

Bu türü bir makro ile bildiremezsiniz. #define fptr_t void(*)(void)Açıkçası işe yaramayacak. Yapıcılarımızın #define fptr_t(name) void(*name)(void)olmadığı, C dilinde gerçekten bir anlam ifade etmeyen, belirsiz bir şey yazmak zorunda kalacaksınız .

Dizi işaretçileri de #define ile bildirilemez: typedef int(*arr_ptr)[10];


C, bahsetmeye değer tür güvenliği için dil desteğine sahip olmamakla birlikte, typedef ve #define'in uyumlu olmadığı başka bir durum, şüpheli tür dönüşümleri yaptığınız zamandır. Derleyici ve / veya astatik analiz aracı, typedef kullanıyorsanız bu tür dönüşümler için uyarı verebilir.


1
Gibi bile iyi işlev ve dizi işaretçileri kombinasyonları olan char *(*(*foo())[])();( fooişaretçiyi geri işlevleri için işaretçiler bir dizi için bir işaretçi dönen bir fonksiyonudur char).
John Bode

4

Aracı, işi en az güç veren ve en çok uyarı veren olanı kullanın. #define önişlemcide değerlendirilir, çoğunlukla orada kendi başınızasınız. typedef, derleyici tarafından değerlendirilir. Çekler verilir ve typedef, adından da anlaşılacağı gibi, yalnızca türleri tanımlayabilir. Bu nedenle, örneğinizde kesinlikle typedef komutunu kullanın.


2

Tanımlamaya karşı normal argümanların ötesinde, bu işlevi Makrolar kullanarak nasıl yazardınız?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

Bu açık bir şekilde önemsiz bir örnektir, ancak bu işlev, ilaveyi uygulayan herhangi bir türün ileriye dönük herhangi bir sırasını toplayabilir *. Bunun gibi kullanılan tipik tanımlamalar Genel Programlamanın önemli bir unsurudur .

* Bunu buraya yeni yazdım, okuyucunun bir alıştırması olarak derlemeye bırakıyorum :-)

DÜZENLE:

Bu cevap çok fazla kafa karışıklığı yaratıyor gibi görünüyor, bu yüzden daha fazla çizmeme izin verin. Bir STL vektörünün tanımına bakacak olsaydınız, aşağıdakine benzer bir şey görürsünüz:

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

Standart kaplar içinde typedeflerin kullanılması, bu türlere atıfta bulunmak için genel bir işleve (yukarıda oluşturduğum gibi) izin verir. "Topla" işlevi, kabın ( std::vector<int>) üzerinde tutulan türde değil, kabın ( int) üzerinde belirtilir. Typedef olmadan o dahili tipe atıf yapmak mümkün olmazdı.

Bu nedenle, typedef'ler Modern C ++ 'ın merkezindedir ve bu Makrolarda mümkün değildir.


Soru makrolar vs typedefdeğil, satır içi işlevler değil makrolar hakkında sordu .
Ben Voigt

Tabi ve bu sadece typedefs ile mümkündür ... value_type budur.
Chris Pitman

Bu bir typedef değil, şablon programlama. Bir işlev veya işlev, ne kadar genel olursa olsun, bir tür değildir.

@ Lundin Daha fazla ayrıntı ekledim: Sum işlevi yalnızca standart kapsayıcılar, üzerinde gösterildikleri türleri ortaya çıkarmak için typedef'leri kullandığı için mümkündür. Ben am değil Sum Typedef olduğunu söyleyerek. Ben std :: vector <T> :: value_type bir typedef olduğunu söylüyorum . Doğrulamak için herhangi bir standart kütüphane uygulamasının kaynağına bakmaktan çekinmeyin.
Chris Pitman

Yeterince adil. Belki sadece ikinci snippet'i göndermek daha iyi olurdu.

1

typedefC ++ felsefesine uyar: derleme zamanında olası tüm kontroller / iddialar. #definederleyiciye çok fazla anlamsal anlam katan bir önişlemci numarasıdır. Derleme performansı hakkında kod doğruluğundan daha fazla endişe etmemelisiniz.

Yeni bir tür oluşturduğunuzda, programınızın etki alanının manipüle ettiği yeni bir "şey" tanımlarsınız. Böylece, bu "şeyi" işlevleri ve sınıfları oluşturmak için kullanabilir ve derleyiciyi statik kontroller yapmak için yararınız için kullanabilirsiniz. Her neyse, C ++ C ile uyumlu olduğundan, intuyarı oluşturmayan int tabanlı türler arasında çok fazla örtük dönüşüm vardır . Yani bu durumda statik kontrollerin tam gücünü alamıyorsunuz. Bununla birlikte, örtük dönüşümler bulmak için yerine typedefbir enumkullanabilirsiniz. Örneğin: Eğer bıraktıysanız typedef int Age;, o zaman değiştirebileceğiniz bir enum Age { };ve aralarında örtük dönüşümler tarafından hataları her türlü alacak Ageve int.

Başka bir şey: içinde typedefolabilir namespace.


1

Yazım tanımları yerine tanımları kullanmak, başka bir önemli yönüyle ve yazım özellikleri kavramında rahatsız edicidir. Her biri kendi özel tanımlarını tanımlayan farklı sınıfları (standart kapları düşünün) düşünün. Tipik tanımlara bakarak genel kodu yazabilirsiniz. Bunun örnekleri arasında genel konteyner gereksinimleri (c ++ standard 23.2.1 [container.requirements.general]) gibi

X::value_type
X::reference
X::difference_type
X::size_type

Tüm bunlar, makro ile birlikte genel bir şekilde ifade edilemez çünkü kapsamda değildir.


1

Bunun hata ayıklayıcınızdaki etkilerini unutmayın. Bazı hata ayıklayıcılar # tanımları çok iyi ele almazlar. Kullandığınız hata ayıklayıcıda her ikisiyle de oynayın. Unutmayın, okumaktan daha çok zaman harcayacağınızı unutmayın.


-2

"#define" sonra yazdıkların yerine geçecek, typedef yazacak. Bu nedenle, özel tipte sahip olmak istiyorsanız - typedef kullanın. Makrolara ihtiyacınız varsa - tanımını kullanın.


Sadece oy vermeyi bırakan ve hatta yorum yazmak için zahmet etmeyen insanları seviyorum.
Dainius
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.