Soru Static_cast, dynamic_cast, const_cast ve reinterpret_cast ne zaman kullanılmalıdır?


Doğru kullanımları nelerdir?

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C stili döküm (type)value
  • İşlev stili döküm type(value)

Hangi özel durumlarda hangi kararın kullanılacağına nasıl karar verilir?


2052
2017-12-01 20:11


Menşei


Belki burada iyi bir referans:Static_cast, reinterpret_cast, const_cast ve dynamic_cast arasındaki farkları yeni bir C ++ programcıya nasıl açıklarsınız?. - Nan Xiao
Farklı türdeki kalıpları kullanmanın bazı yararlı somut örnekleri için, benzer bir sorudaki ilk cevabı kontrol edebilirsiniz. bu diğer konu. - TeaMonkie


Cevaplar:


static_cast kullanmaya teşebbüs ettiğin ilk oyuncu. Türler arasında örtük dönüşümler gibi şeyler yapar int için floatveya işaretçi void*) ve ayrıca açık dönüştürme işlevlerini (veya örtülü olanları) de çağırabilir. Birçok durumda, açıkça belirten static_cast gerekli değil, ancak not etmek önemlidir T(something) sözdizimi eşdeğerdir (T)something ve kaçınılmalıdır (daha sonra bu konuda daha fazla). bir T(something, something_else) Ancak, güvenlidir ve kurucuyu aramayı garanti eder.

static_cast Ayrıca kalıtım hiyerarşileri aracılığıyla da yayınlayabilir. Yukarı doğru (bir ana sınıfa doğru) dökülürken gereksizdir, ancak aşağıya doğru dökülürken, bu işlemin yapılmadığı sürece kullanılabilir. virtual miras. Ancak, kontrol etmiyor ve tanımlanmamış bir davranış static_cast Bir hiyerarşiyi, aslında nesnenin türü olmayan bir türe indirir.


const_cast kaldırmak veya eklemek için kullanılabilir const bir değişkene; başka hiçbir C ++ kalıbı kaldıramaz (hatta reinterpret_cast). Önceden değiştirilmenin önemli olduğunu belirtmek önemlidir const orijinal değişken ise değer sadece tanımlanmamıştır const; onu kullanmak için kullanırsanız const bildirilmemiş bir şeye referans olarak const, güvenli. Bu, üye fonksiyonlarını aşırı yüklerken faydalı olabilir. const, Örneğin. Eklemek için de kullanılabilir const Bir üye fonksiyon aşırı yüklenmesini çağırmak gibi bir nesneye.

const_cast aynı şekilde çalışır volatileAncak bu daha az yaygındır.


dynamic_cast neredeyse polimorfizm işlemek için kullanılır. Herhangi bir sınıf türüne herhangi bir polimorfik türden bir işaretçi veya referans atayabilirsiniz (bir polimorfik tür, en az bir sanal işleve sahip, bildirilmiş veya devralınmış bir türe sahipse). Sadece aşağı doğru döküm yapmaktan daha fazlası için kullanabilirsiniz - yana ya da başka bir zincir oluşturabilirsin. dynamic_cast İstenen nesneyi arar ve mümkünse iade eder. Yapamazsa, geri dönecek nullptr işaretçi veya atma durumunda std::bad_cast referans durumunda.

dynamic_cast Yine de bazı sınırlamalar vardır. Miras hiyerarşisinde aynı türden çok sayıda nesne (sözde 'korkulan elmas') varsa ve çalışmıyorsanız işe yaramıyor. virtual miras. Aynı zamanda sadece kamu mirasından geçebilir - her zaman seyahat etmeyi başaramaz protected veya private miras. Ne var ki, bu tür kalıtım biçimleri nadirdir, ancak bu nadiren bir konudur.


reinterpret_cast En tehlikeli dökümdür ve çok kullanışlıdır. Bir türü doğrudan diğerine çevirir - örneğin bir işaretçiden diğerine değer atmak veya bir işaretçiyi bir intya da diğer her türlü kötü şeyler. Büyük ölçüde, aldığınız tek garanti reinterpret_cast Bu, sonucu orijinal türüne geri döndürürseniz, normalde aynı değeri alırsınız (ancak değil ara türü orijinal tipten daha küçük ise). Çok sayıda dönüşüm var. reinterpret_cast de yapamazsın. Öncelikle özellikle garip dönüşümler ve bit manipülasyonları için kullanılır, örneğin ham bir veri akışını gerçek verilere dönüştürmek veya verileri hizalanmış bir işaretçinin düşük bitlerine kaydetmek gibi.


C stili döküm ve işlev stili döküm kullanarak atıyor (type)object veya type(object), sırasıyla. C stili bir döküm, aşağıdakilerden başarılı olan ilklerden biri olarak tanımlanır:

  • const_cast
  • static_cast (erişim kısıtlamalarını göz ardı etmemekle birlikte)
  • static_cast (yukarıya bakın), sonra const_cast
  • reinterpret_cast
  • reinterpret_cast, sonra const_cast

Bu nedenle, bazı durumlarda diğer dökümler için bir yedek olarak kullanılabilir, ancak içine devredilebilme yeteneği nedeniyle çok tehlikeli olabilir. reinterpret_castve emin değilseniz, ikincisi açıkça döküm gerektiğinde tercih edilmelidir static_cast başarılı olacak veya reinterpret_cast başaramayacak. O zaman bile, daha uzun ve daha açık bir seçenek düşünün.

C stili dökümler, bir static_castBaşka bir oyuncuyu kullanamayacak bir operasyonu gerçekleştirebilecekleri anlamına gelir. Yine de, bu çoğunlukla bir klüdedir ve aklımda, C-tarzı kadrolardan kaçınmak için başka bir sebep vardır.


2211
2017-12-01 20:22



dynamic_cast sadece polimorfik tipler içindir. türetilmiş bir sınıfa atarken sadece onu kullanmanız gerekir. Özellikle dynamic_cast'in functinoality özelliğine ihtiyacınız olmadıkça static_cast kesinlikle ilk seçenektir. Genel olarak mucizevi gümüş mermi "tip kontrol etme" değil. - jalf
Mükemmel cevap! Hızlı bir açıklama: static_cast, türetilmiş bir Kaynağınız olması ve Base * 'e dönüştürmeniz durumunda hiyerarşiyi oluşturmak için gerekli olabilir. Çünkü çift işaretçiler / referanslar otomatik olarak hiyerarşiyi oluşturmaz. İki dakika önce böylesine (açıkçası değil) durumla karşılaştım. ;-) - bartgol
* "başka C ++ kalıbı kaldıramaz const (bile değil reinterpret_cast) "... gerçekten? Ne hakkında reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))? - Mehrdad
Yukarıda eksik olan önemli bir detayın, dynamic_cast'in statik veya reinterpret_cast ile karşılaştırıldığında bir çalışma süresi performans cezasına sahip olduğunu düşünüyorum. Bu önemlidir, örn. gerçek zamanlı yazılım. - jfritz42
Söz etmeye değer olabilir reinterpret_cast API'nin bir dizi opak veri türü ile uğraşırken genellikle tercih edilen silahtır - camelCase


kullanım dynamic_cast Bir kalıtım hiyerarşisinde işaretçileri / referansları dönüştürmek için.

kullanım static_cast sıradan yazım dönüşümleri için.

kullanım reinterpret_cast Bit modellerinin düşük seviyeli yeniden yorumlanması için. Aşırı dikkatle kullanın.

kullanım const_cast uzaklaştırmak için const/volatile. Const yanlış bir API kullanarak takılmadıkça bunu yapmayın.


284
2018-01-21 04:53





(Yukarıda bir çok teorik ve kavramsal açıklama verilmiştir) 

Aşağıda bazı pratik örnekler kullandım static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Ayrıca açıklamayı anlamak için buna da değinir: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

152
2017-12-11 02:05



Diğer cevapların bazılarının teorisi iyidir, ama yine de kafa karıştırıcıdır, bu örnekleri diğer cevapları okuduktan sonra görmek onların gerçekten anlamlı olmasını sağlar. Bu örneklerden yoksun, ben hala emin değildim, ama onlarla, şimdi diğer cevapların ne anlama geldiğinden eminim. - Solx
Reinterpret_cast'in son kullanımı hakkında: bu kullanımla aynı değil static_cast<char*>(&val) ? - Lorenzo Belli
@LorenzoBelli Tabii ki değil. Onu denedin mi? İkincisi geçerli C ++ değildir ve derlemeyi engeller. static_cast yalnızca tanımlı dönüşümlere, miras yoluyla görünür ilişkilere veya bunlardan türetilen türler arasında çalışır void *. Diğer her şey için başka oyuncular var. reinterpret cast herhangi birine char * Herhangi bir nesnenin temsilinin okunmasına izin verilmesine izin verilir - ve bu anahtar kelimenin yararlı olduğu tek durumdan biri, uygulama / tanımlanmamış davranışın yaygın bir üreteci değil. Ama bu 'normal' dönüşüm olarak kabul edilmez, bu nedenle (genellikle) çok muhafazakar izin verilmez static_cast. - underscore_d
Veritabanları gibi sistem yazılımı ile çalışırken yeniden yazmanız çok yaygındır. Çoğu durumda kendi sayfa yöneticinizi yazıyorsunuz, bu da sayfadaki veri türü hakkında hiçbir fikre sahip değil ve sadece bir boşluk işaretçisi döndürüyor. Yeniden yorumlama yapmak ve onu istedikleri gibi çıkarmak için daha yüksek seviyelere kadar. - Sohaib


İçinde biraz bilgi varsa bile yardımcı olabilir.

static_cast

  • C ++ derleyicisi, float-int gibi scaler türlerini nasıl dönüştüreceğini zaten biliyor. Onlar için static_cast kullan.
  • B tipi A dönüştürülürken B böyle yapıcı yoksa Genel olarak, static_cast o zaman derleme zamanı hatası olsun o A. geçen B'nin yapıcı çağırır.
  • Dan yayınla A* için B* A ve B devralma hiyerarşisinde (veya geçersizse) her zaman başarılı olur, aksi halde derleme hatası alırsınız.
  • Yakaladım: Temel göstergeyi türetilmiş işaretçiye yazarsanız ancak gerçek nesne türetilmemişse yapamaz hata al. Kötü bir işaretçi alırsınız ve türetilmiş işaretçinin üyelerine erişmeye çalıştığınız anda çalışma zamanında segfault alırsınız.
  • Aynı şey için de geçerli A& için B&.
  • Yakaladım: Base veya tersi Türetilmiş Cast yeni bir kopyasını yaratır! C # / Java'dan gelen insanlar için, yukarıdakilerin çoğu büyük bir sürpriz olabilir.

dynamic_cast

  • dynamic_cast, yayın geçerli olup olmadığını anlamak için çalışma zamanı bilgisini kullanır. Örneğin, (Base*) için (Derived*)İşaretçi türetilmiş türden değilse başarısız olabilir.
  • Bu, dynamic_cast'in static_cast ile karşılaştırıldığında çok pahalı olduğu anlamına gelir!
  • İçin A* için B*Eğer cast geçersizse, dynamic_cast nullptr değerini döndürür.
  • İçin A& için B& cast geçersizse, dynamic_cast bad_cast istisnasını atar.
  • Diğer oyunculardan farklı olarak, çalışma zamanı yükü var.

const_cast

  • Static_cast const olmayan bir const yapabilmesine rağmen, başka bir yoldan gidemez. Const_cast iki yol da yapabilir.
  • Bunun kullanışlı olduğu bir örnek, set<T> anahtarını değiştirmediğinizden emin olmak için öğelerini yalnızca const olarak döndürür. Ancak amacınız, nesnenin anahtar olmayan üyelerini değiştirmekse, o zaman tamam olmalıdır. Sabitliği kaldırmak için const_cast'i kullanabilirsiniz.
  • Başka bir örnek uygulamak istediğiniz zaman T& foo() Hem de const T& foo(). Kod çoğaltmasını önlemek için, bir işlevden diğerine değer döndürmek üzere const_cast'i uygulayabilirsiniz.

reinterpret_cast

  • Bu temelde, bu baytları bu bellek konumunda almayı ve verilen nesne olarak düşünmeyi söylüyor.
  • Örneğin, floattaki bitlerin nasıl göründüğünü görmek için 4 baytlık bir float'ı 4 baytlık int'ye yükleyebilirsiniz.
  • Açıkçası, veri türü için doğru değilse, segfault alabilirsiniz.
  • Bu yayın için çalışma zamanı başı yok.

52
2017-12-01 20:20



Hakkında son nokta const_cast yanlış: sadece dynamic_cast aslında çalışma zamanı yükü vardır. Siparişi değiştirmiş olabilirsiniz ve onu harekete geçirip tekrar hatırlamayı unutmuş gibisiniz. - rubenvb
Senin doğruların! Sabit. - ShitalShah


does bu sorunu cevapla?

Hiç kullanmadım reinterpret_castve buna ihtiyaç duyan bir vakaya koşmanın kötü bir tasarım kokusu olmadığını düşünün. Kod tabanında çalışıyorum dynamic_cast çok kullanılır. İle fark static_cast bu bir dynamic_cast çalışma zamanını (daha güvenli) veya (daha fazla yükü) istediğinizi kontrol edebilir (bkz. msdn).


11
2018-05-31 14:16



Bir amaç için reintrepret_cast kullanıyorum - çiftleri bir çiftden (platformumda uzun süre aynı boyutta) alıyorum. - Joshua
reinterpret_cast gereklidir, ör. COM nesnelerle çalışmak için. CoCreateInstance (), void ** (son parametre) çıkış parametresine sahiptir, burada işaretçinizi örn. "INetFwPolicy2 * pNetFwPolicy2". Bunu yapmak için, reinterpret_cast <void **> (& pNetFwPolicy2) gibi bir şey yazmanız gerekir. - Serge Rogatch


Şimdiye kadarki diğer cevaplara ek olarak, burada açık olmayan bir örnek var. static_cast yeterli değil ki reinterpret_cast gereklidir. Bir çıkış parametresinde, farklı sınıfların (ortak bir temel sınıfı paylaşmayan) nesneler için işaretçi döndüren bir işlev olduğunu varsayalım. Böyle bir işlevin gerçek bir örneği CoCreateInstance() (aslında son parametreye bakın) void**). Bu işlevin belirli bir nesne sınıfını talep ettiğinizi varsayalım, böylece işaretçinin türünü önceden biliyorsunuz (COM nesneleri için sık sık yaptığınız). Bu durumda işaretçinize işaretçi atayamazsınız. void** ile static_cast: İhtiyacınız var reinterpret_cast<void**>(&yourPointer).

Kodda:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

Ancak, static_cast basit işaretçiler için çalışır (işaretçiler için işaretçiler değil), bu nedenle yukarıdaki kod, önlemek için yeniden yazılabilir reinterpret_cast (ek bir değişkenin bir bedeli ile) aşağıdaki şekilde:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9