Neden (int) x yerine static_cast <int> (x) kullanalım?


667

static_castFonksiyonun C stili veya basit fonksiyon stili dökümlere tercih edilmesi gerektiğini duydum . Bu doğru mu? Neden?


36
Şerefin itiraz, sordu ve cevapladı .
Graeme Perrow

25
Kabul etmiyorum, bu diğer soru C ++ 'da yayınlar arasındaki farkları tanımlamakla ilgiliydi. Bu soru, biraz farklı olan static_cast'in gerçek kullanışlılığıyla ilgilidir.
Vincent Robert

2
İki soruyu kesinlikle birleştirebiliriz, ancak bu iş parçacığından korumak zorunda olduğumuz şey, şu anda yalnızca diğer iş parçacığında tek satırlık bir cevapta belirtilen, oy kullanmadan C tarzı döküm üzerinde işlevleri kullanmanın avantajıdır. .
Tommy Herbert

6
Bu soru int gibi "yerleşik" türlerle, bu soru sınıf türleriyle ilgilidir. Bu, ayrı bir açıklamayı hak edecek kadar önemli bir fark gibi görünüyor.

9
static_cast aslında bir işleç değil, işleçtir.
ThomasMcLeod

Yanıtlar:


633

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 foogö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.


30
static_castKalı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.
David Thornley

41
@David Thornley: Katılıyorum, genellikle. Sanırım static_castbu durumda uyarıları gösterdim . dynamic_castdaha 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_castisteğ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.
Euro Micelli

18
C yayınları ile ilgili talebiniz yanlış. Tüm C yayınları değer dönüşümleri olup kabaca C ++ ile karşılaştırılabilir static_cast. C eşdeğer reinterpret_castolan *(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
R .. GitHub DUR ICE YARDIMCI

11
Güzel cevabınız gönderinin gövdesine hitap ediyor. "Neden static_cast <int> (x) yerine (int) x kullanın" başlığına bir cevap arıyordu. Yani, tip int(ve inttek 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.
chux - Monica

31
@chux, int dynamic_castgeçerli değildir, ancak diğer tüm nedenler geçerlidir. Örneğin: diyelim diyelim volarak bildirilen bir işlevdir parametresidir float, ardından (int)vise static_cast<int>(v). Eğer parametreyi değiştirmek Ama eğer float*, (int)vsessizce olur reinterpret_cast<int>(v)ise static_cast<int>(v)yasadışı ve doğru olarak derleyici tarafından tespit edilmesi.
Euro Micelli

115

Bir pragmatik ipucu: Projeyi derlemeyi planlıyorsanız, kaynak kodunuzdaki static_cast anahtar sözcüğünü kolayca arayabilirsiniz.


3
"(int)" gibi köşeli parantezleri kullanarak arama yapabilirsiniz ancak iyi yanıt ve C ++ stili döküm kullanmak için geçerli bir neden.
Mike

4
@Yanlış pozitifler bulacak - tek bir intparametreye sahip bir işlev bildirimi .
Nathan Osman

1
Bu yanlış negatifler verebilir: Eğer tek yazar olmadığınız bir kod tabanı arıyorsanız, başkalarının bazı nedenlerden dolayı getirmiş olabileceği C tarzı dökümler bulamazsınız.
Ruslan

7
Bunu yapmak projeyi düzenlemek için nasıl yardımcı olur?
Bilow

Büyük olasılıkla doğru olanı olduğu için static_cast aramazsınız. Reinterpret_cast, const_cast ve hatta dynamic_cast'i ararken, yeniden tasarlanabilecek yerleri göstereceği için static_cast'e filtre uygulamak istiyorsunuz. C-cast hep birlikte karışıyor ve size döküm sebebi vermiyor.
Dragan

78

Kısacası :

  1. static_cast<>() size derleme zamanı kontrol yeteneği verir, C-Style cast yapmaz.
  2. static_cast<>()C ++ kaynak kodu içinde herhangi bir yerde kolayca tespit edilebilir; aksine, C_Style dökümünü tespit etmek daha zordur.
  3. 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


21
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
Todd Lehman

3
@ToddLehman: Teşekkür ederim, ama ben de söylemedim 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
Rika

8
Bu görüntüdeki @ToddLehman kodu, (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.
Öö Tiib

28

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.


26

static_castyanlışlıkla yapamayacağınız anlamına gelir const_castveya reinterpret_castbu iyi bir şeydir.


4
C stili kadroya göre (oldukça küçük olsa da) ek avantajlar daha fazla göze çarpması (potansiyel olarak kötü bir şey yapmanın çirkin görünmesi gerekir) ve daha grep-mümkün.
Michael Burr

4
grep-yeteneği her zaman bir artı, benim kitabımda.
Branan

7
  1. Kodların grep veya benzeri araçları kullanarak kodunuzda kolayca bulunmasını sağlar.
  2. Ne tür bir döküm yaptığınızı ve derleyicinin onu uygulamadaki yardımını açık hale getirir. Yalnızca sabitliği kaldırmak istiyorsanız, başka tür dönüşümler yapmanıza izin vermeyecek const_cast kullanabilirsiniz.
  3. Yayınlar doğası gereği çirkin - bir programcı olarak, derleyicinin kodunuzu normal olarak nasıl işleyeceğini geçersiz kılıyorsunuz. Derleyiciye "Senden daha iyi biliyorum" diyorsun. Durum böyle olunca, bir oyuncu kadrosunun gerçekleştirilmesinin orta derecede acı verici bir şey olması ve kodunuza yapışmaları gerektiğinden mantıklıdır, çünkü olası bir sorun kaynağıdır.

Etkili C ++ Girişine bakın


Bu sınıflar için tamamen katılıyorum ama P ++ türleri için C ++ tarzı döküm kullanarak herhangi bir anlam ifade ediyor mu?
Zachary Kraus

Sanırım. Her 3 neden de POD'lar için geçerlidir ve sınıflar ve POD'lar için ayrı kurallardan ziyade tek bir kurala sahip olmak yararlıdır.
JohnMcG

İlginç, POD türleri için gelecekteki kodda atmalarını nasıl yapacağımı değiştirmek zorunda kalabilirsiniz.
Zachary Kraus

7

Ne kadar güvenlik sağlamak istediğinizle ilgilidir.

Yazarken (bar) foo( reinterpret_cast<bar> foobir 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!


8
(bar) foo, reinterpret_cast <bar> (foo) ile eşdeğer değildir. "(TYPE) ifade" için kurallar, reinterpret_cast içerebilen, kullanılacak uygun C ++ stilini seçeceğidir.
Richard Corden

İyi bir nokta. Euro Micelli bu soruya kesin cevap verdi.
Pitarou

1
Ayrıca, static_cast<bar>(foo)parantez ile. Aynı reinterpret_cast<bar>(foo).
LF

6

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.


4

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

4
Neden birisi sana yazma 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.)
Todd Lehman

@ gd1 - Neden herkes okunabilirliğin üstüne tutarlılık koysun ki? (aslında yarı ciddi)
Todd Lehman

2
@ToddLehman: Ben, sadece sizin için bir şekilde özel oldukları için belirli türler için bir istisna yapmanın benim için bir anlamı yok ve ben de okunabilirlik fikrinize katılmıyorum. Daha kısa, daha fazla okunabilir anlamına gelmez, başka bir yorumda yayınladığınız görüntüden gördüğüm gibi.
gd1

2
static_cast, çok özel bir tür dönüştürme yapmak için açık ve bilinçli bir karardır. Dolayısıyla niyet netliğine katkıda bulunur. Kod inceleme, hata veya yükseltme alıştırmasında dönüşümler için kaynak dosyaları aramak için bir işaretçi olarak da çok kullanışlıdır.
Ocak'ta Persixty

1
@ToddLehman kontrpuan: Neden bu kadar çok okunabilir (int)dolduğ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_castyapıcı dökümleri kullanıyorum ve hiç kullanmadım , sanmıyorum. Sadece (C)castsaceleyle hata ayıklama yazarken kullanıyorum cout...
underscore_d
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.