static_cast
Fonksiyonun C stili veya basit fonksiyon stili dökümlere tercih edilmesi gerektiğini duydum . Bu doğru mu? Neden?
static_cast
Fonksiyonun C stili veya basit fonksiyon stili dökümlere tercih edilmesi gerektiğini duydum . Bu doğru mu? Neden?
Yanıtlar:
Temel nedeni klasik C atmalarını dediğimiz arasında hiçbir ayrım yapmak olmasıdır static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
, ve dynamic_cast<>()
. Bu dört şey tamamen farklı.
A static_cast<>()
genellikle güvenlidir. Dilde geçerli bir dönüşüm veya bunu mümkün kılan uygun bir kurucu var. Biraz riskli olan tek zaman, miras alınan bir sınıfa atıldığınız zamandır; nesnenin, dilin dışında (nesnedeki bir bayrak gibi) olduğunu iddia ettiğiniz alt öğenin olduğundan emin olmalısınız. dynamic_cast<>()
Sonuç kontrol edildiğinde (işaretçi) veya olası bir istisna dikkate alındığı sürece ( A ) güvenlidir.
Öte yandan A reinterpret_cast<>()
(veya a const_cast<>()
) her zaman tehlikelidir. Derleyiciye şunları söyleyin: "güven bana: Bunun bir gibi foo
görünmediğini biliyorum (değişebilir gibi görünmüyor), ama öyle".
İlk sorun, büyük bir kod parçasına bakmadan ve tüm kuralları bilmeden C-tarzı bir oyuncuda hangisinin gerçekleşeceğini söylemek neredeyse imkansızdır.
Bunları varsayalım:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Şimdi, bu ikisi aynı şekilde derleniyor:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
Ancak, neredeyse aynı kodu görelim:
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
Gördüğünüz gibi, dahil olan tüm sınıflar hakkında çok şey bilmeden iki durumu birbirinden ayırt etmenin kolay bir yolu yoktur.
İkinci sorun, C tipi kalıpların yerini bulmak için çok zor olmasıdır. Karmaşık ifadelerde C tarzı dökümleri görmek çok zor olabilir. Tam gelişmiş bir C ++ derleyici ön ucu olmadan C stili dökümleri (örneğin bir arama aracı) bulmak için otomatik bir araç yazmak neredeyse imkansızdır. Öte yandan, "static_cast <" veya "reinterpret_cast <" için arama yapmak kolaydır.
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
Bu, sadece C tarzı dökümler daha tehlikeli olmakla kalmaz, aynı zamanda doğru olduklarından emin olmak için hepsini bulmak çok daha zordur.
static_cast
Kalıtım hiyerarşisini düşürmek için kullanmamalısınız dynamic_cast
. Bu null işaretçiyi veya geçerli bir işaretçiyi döndürür.
static_cast
bu durumda uyarıları gösterdim . dynamic_cast
daha güvenli olabilir, ancak her zaman en iyi seçenek değildir. Bazen bir işaretçinin derleyiciye opak olarak belirli bir alt tipini gösterdiğini ve a'nın daha hızlı olduğunu bilirsinizstatic_cast
. En azından bazı ortamlarda, dynamic_cast
isteğe bağlı derleyici desteği ve çalışma zamanı maliyeti (RTTI'yi etkinleştirme) gerektirir ve bunu kendiniz yapabileceğiniz birkaç denetim için etkinleştirmek istemeyebilirsiniz. C ++ 'ın RTTI soruna sadece bir olası çözümdür.
static_cast
. C eşdeğer reinterpret_cast
olan *(destination_type *)&
, yani, nesnenin adresi alarak farklı bir türü için bir işaretçi bu adresi döküm ve ardından dereferencing. Cı bu yapının davranışını tanımlayan için karakter tipleri veya belirli yapı tiplerinin durum dışında, genel olarak C ile tanımlanmamış bir davranış ile sonuçlanır
int
(ve int
tek başına) için neden tek fayda olarak static_cast<int>
vs. kullanmanın (int)
sınıf değişkenleri ve göstergeleri ile olduğu görülmektedir. Bu konuyla ilgili ayrıntılı bilgi isteyin.
int
dynamic_cast
geçerli değildir, ancak diğer tüm nedenler geçerlidir. Örneğin: diyelim diyelim v
olarak bildirilen bir işlevdir parametresidir float
, ardından (int)v
ise static_cast<int>(v)
. Eğer parametreyi değiştirmek Ama eğer float*
, (int)v
sessizce olur reinterpret_cast<int>(v)
ise static_cast<int>(v)
yasadışı ve doğru olarak derleyici tarafından tespit edilmesi.
Bir pragmatik ipucu: Projeyi derlemeyi planlıyorsanız, kaynak kodunuzdaki static_cast anahtar sözcüğünü kolayca arayabilirsiniz.
int
parametreye sahip bir işlev bildirimi .
Kısacası :
static_cast<>()
size derleme zamanı kontrol yeteneği verir, C-Style cast yapmaz.static_cast<>()
C ++ kaynak kodu içinde herhangi bir yerde kolayca tespit edilebilir; aksine, C_Style dökümünü tespit etmek daha zordur.- Niyetler C ++ dökümleri kullanılarak çok daha iyi aktarılır.
Daha Fazla Açıklama :
Statik yayın uyumlu türler arasında dönüşüm gerçekleştirir . C tarzı oyuncuya benzer, ancak daha kısıtlayıcıdır. Örneğin, C stili kadro bir tamsayı işaretçisinin bir karakteri göstermesini sağlar.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
Bu, 1 baytlık ayrılmış belleğe işaret eden 4 baytlık bir işaretçi ile sonuçlandığından, bu işaretçiye yazmak bir çalışma zamanı hatasına neden olur veya bitişikteki bazı belleğin üzerine yazar.
*p = 5; // run-time error: stack corruption
C stili dökümün aksine, statik döküm derleyicinin işaretçi ve nokta veri türlerinin uyumlu olup olmadığını kontrol etmesine izin verir, bu da programcının derleme sırasında bu yanlış işaretçi atamasını yakalamasını sağlar.
int *q = static_cast<int*>(&c); // compile-time error
Daha fazla bilgi için:
static_cast <> ve C stili döküm
ile Düzenli yayın ile static_cast ve dynamic_cast arasındaki fark nedir
static_cast<>()
Daha okunabilir olduğuna katılmıyorum . Yani, bazen öyledir, ama çoğu zaman - özellikle temel tamsayı türlerinde - sadece korkunç ve gereksiz bir şekilde ayrıntılıdır. Örneğin: Bu, 32 bitlik bir kelimenin baytlarını değiştiren bir işlevdir. static_cast<uint##>()
Yayınları kullanarak okumak neredeyse imkansızdır , ancak (uint##)
yayınları kullanarak anlaşılması oldukça kolaydır . Kodun resmi: imgur.com/NoHbGve
always
. (ancak çoğu zaman evet) c stili dökümünün çok daha okunabilir olduğu durumlar vardır. Bu c tarzı döküm hala canlı ve c ++ imho tekme nedenlerinden biri. :) Bu arada çok güzel bir örnek oldu
(uint32_t)(uint8_t)
en düşük değerlerin yanı sıra baytların sıfırlandığını başarmak için zincirleme ( ) iki dönüşüm kullanır . Bunun için bitsel ve ( 0xFF &
) vardır. Döküm kullanımı niyeti gizliyor.
Soru, sadece static_cast veya C stili döküm kullanmaktan daha büyük çünkü C stili dökümler kullanılırken farklı şeyler oluyor. C ++ döküm operatörleri bu işlemleri daha açık hale getirmeyi amaçlamaktadır.
Yüzeyde static_cast ve C tarzı dökümler aynı şeye benziyor, örneğin bir değeri diğerine aktarırken:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Her ikisi de tamsayı değerini iki katına çıkarır. Ancak işaretçilerle çalışırken işler daha karmaşık hale gelir. bazı örnekler:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
Bu örnekte (1) belki Tamam olabilir, çünkü A ile gösterilen nesne gerçekten B'nin bir örneğidir. Fakat kodda bu noktada bilmiyorsanız, aslında neyi gösterir? (2) belki de tam olarak yasaldır (sadece tamsayının bir baytına bakmak istiyorsunuz), ancak bu da bir hata olabilir, bu durumda bir hata güzel olabilir, (3). C ++ döküm işleçleri, mümkün olduğunda derleme zamanı veya çalışma zamanı hataları sağlayarak koddaki bu sorunları ortaya çıkarmayı amaçlamaktadır.
Yani, katı "değer dökümü" için static_cast kullanabilirsiniz. İşaretçilerin çalışma zamanı polimorfik dökümü istiyorsanız dynamic_cast kullanın. Türleri gerçekten unutmak istiyorsanız, reintrepret_cast kullanabilirsiniz. Ve sadece const pencereden dışarı atmak için const_cast var.
Sadece kodu daha açık hale getiriyorlar, böylece ne yaptığınızı biliyorsunuz gibi görünüyor.
static_cast
yanlışlıkla yapamayacağınız anlamına gelir const_cast
veya reinterpret_cast
bu iyi bir şeydir.
Etkili C ++ Girişine bakın
Ne kadar güvenlik sağlamak istediğinizle ilgilidir.
Yazarken (bar) foo
( reinterpret_cast<bar> foo
bir tür dönüştürme operatörü sağlamadıysanız buna eşdeğerdir ) derleyiciye tür güvenliğini yok saymasını ve söylendiği gibi yapmasını söylersiniz.
Yazdığınızda static_cast<bar> foo
, derleyiciden en azından tür dönüşümünün anlamlı olup olmadığını kontrol etmesini ve integral türler için bazı dönüşüm kodu eklemesini istersiniz.
DÜZENLEME 2014-02-26
Bu cevabı 5 yıldan fazla bir süre önce yazdım ve yanlış anladım. (Bkz. Yorumlar.) Ama yine de upvotes alır!
static_cast<bar>(foo)
parantez ile. Aynı reinterpret_cast<bar>(foo)
.
C Style kodları bir kod bloğunda kaçırmak kolaydır. C ++ tarzı dökümler sadece daha iyi uygulama değildir; çok daha fazla esneklik sunarlar.
reinterpret_cast, işaretçi türü dönüşümleri için integral sağlar, ancak yanlış kullanıldığında güvensiz olabilir.
static_cast, numaralandırmalardan girişlere veya girişlerden yüzerlere veya türden emin olduğunuz herhangi bir veri türü gibi sayısal türler için iyi bir dönüşüm sunar. Herhangi bir çalışma süresi kontrolü gerçekleştirmez.
dynamic_cast ise bu kontrolleri belirsiz atamaları veya dönüşümleri işaretleyerek gerçekleştirir. Sadece işaretçiler ve referanslar üzerinde çalışır ve ek yüke neden olur.
Birkaç tane daha var ama bunlar karşınıza çıkacak en önemli olanlar.
static_cast, işaretçileri sınıflara yönlendirmenin yanı sıra, sınıflarda açıkça tanımlanan dönüşümleri gerçekleştirmek ve temel türler arasında standart dönüşümler gerçekleştirmek için de kullanılabilir:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
olsa da, ne zaman (int)d
çok daha kısa ve okunabilir? (Yani temel tiplerde, nesne işaretçileri değil.)
(int)d
olduğunda kimse yazsın int{d}
ki? Yapıcı, ya da eğer işlevsellik gibi ()
, sözdizimi karmaşık ifadelerde kabus gibi bir parantez labirentine dönüşecek kadar hızlı değildir. Bu durumda, bunun int i{d}
yerine olurdu int i = (int)d
. Çok daha iyi IMO. Bununla birlikte, bir ifadede geçici bir şeye ihtiyacım olduğunda, static_cast
yapıcı dökümleri kullanıyorum ve hiç kullanmadım , sanmıyorum. Sadece (C)casts
aceleyle hata ayıklama yazarken kullanıyorum cout
...