Aşağıdakilerin uygun kullanımları nelerdir:
static_castdynamic_castconst_castreinterpret_cast- C tarzı döküm
(type)value - İşlev tarzı döküm
type(value)
Hangi özel durumlarda hangisinin kullanılacağına nasıl karar verilir?
Aşağıdakilerin uygun kullanımları nelerdir:
static_castdynamic_castconst_castreinterpret_cast(type)valuetype(value)Hangi özel durumlarda hangisinin kullanılacağına nasıl karar verilir?
Yanıtlar:
static_castkullanmaya çalışmanız gereken ilk oyuncu. Bu örtük (örneğin türleri arasındaki dönüşümler gibi şeyler yapar intiçin floatveya işaretçi void*) ve aynı zamanda açık dönüştürme işlevleri (veya örtülü olanlar) çağırabilir. Çoğu durumda, açıkça belirtmek static_castgerekli değildir, ancak T(something)sözdiziminin eşdeğer olduğunu (T)somethingve bundan kaçınılması gerektiğini belirtmek önemlidir (daha sonra daha fazla). T(something, something_else)Ancak A güvenlidir ve kurucuyu çağırması garanti edilir.
static_castkalıtım hiyerarşileri yoluyla da kullanılabilir. Yukarı doğru (bir temel sınıfa doğru) döküm yaparken gereksizdir, ancak aşağı doğru döküm yaparken, virtualkalıtımdan geçmediği sürece kullanılabilir . Ancak denetleme yapmaz ve static_castbir hiyerarşiyi aslında nesnenin türü olmayan bir türe indirgemek tanımsız bir davranıştır .
const_castconstbir değişkeni kaldırmak veya eklemek için kullanılabilir ; başka hiçbir C ++ dökümü onu kaldıramaz (hatta değil reinterpret_cast). Önceden bir constdeğerin değiştirilmesinin yalnızca orijinal değişken ise tanımsız olduğunu belirtmek önemlidir const; constbildirilmemiş bir şeye referans almak için kullanırsanız const, güvenlidir. Bu, örneğin üye fonksiyonlarını aşırı yüklerken faydalı olabilir const. constBir üye fonksiyonunun aşırı yüklenmesi gibi bir nesneye eklemek için de kullanılabilir .
const_castvolatiledaha az yaygın olsa da benzer şekilde çalışır .
dynamic_castsadece polimorfizmi işlemek için kullanılır. Bir işaretçiyi veya herhangi bir polimorfik türe referansı başka herhangi bir sınıf türüne atayabilirsiniz (polimorfik bir türün bildirilen veya devralınan en az bir sanal işlevi vardır). Sadece aşağı doğru dökmekten daha fazlası için kullanabilirsiniz - yanlara veya hatta başka bir zincire kadar dökebilirsiniz. dynamic_castİstenen nesneyi ararlar ve mümkünse bunu dönecektir. Eğer yapamazsa, nullptrbir işaretçi durumunda geri dönecek ya da std::bad_castbaşvuru durumunda atılacaktır .
dynamic_castolsa da, bazı sınırlamaları vardır. Kalıtım hiyerarşisinde ('korkunç elmas' olarak adlandırılan) aynı türde birden fazla nesne varsa ve miras kullanmıyorsanız işe yaramaz virtual. Ayrıca sadece kamusal mirastan geçebilir - her zaman seyahat etmek protectedveya privatemiras almak başarısız olur . Bununla birlikte, bu tür bir kalıtım biçimi nadir olduğu için bu nadiren bir konudur.
reinterpret_casten tehlikeli dökümdür ve çok az kullanılmalıdır. Bir türü doğrudan diğerine dönüştürür - örneğin değeri bir işaretçiden diğerine çevirmek veya bir işaretçiyi bir intveya başka türlü kötü şeylerde saklamak gibi . Büyük ölçüde, sadece birlikte olsun garanti reinterpret_castorijinal türüne sonucu geri dökme normalde bile, tam olarak aynı değere alacak (ancak değil orta tip orijinal tip küçükse). Yapamayan birkaç dönüşüm reinterpret_castde var. Öncelikle, ham veri akışını gerçek verilere dönüştürmek veya hizalanmış verilere işaretçinin düşük bitlerinde veri depolamak gibi özellikle garip dönüşümler ve bit manipülasyonları için kullanılır.
C stili döküm ve işlev stili döküm , sırasıyla (type)objectveya kullanan dökümlerdir type(object)ve işlevsel olarak eşdeğerdir. Bunlar, aşağıdakilerden başarılı olan aşağıdakilerden birincisi olarak tanımlanır:
const_caststatic_cast (erişim kısıtlamalarına uymamakla birlikte)static_cast (yukarıya bakın), sonra const_castreinterpret_castreinterpret_cast, sonra const_castBu nedenle, bazı durumlarda diğer dökümlerin yerine kullanılabilir, ancak a'ya dönüşme yeteneği nedeniyle son derece tehlikeli olabilir reinterpret_castve ikincisi, açık döküm gerektiğinde, static_castbaşarılı reinterpret_castolacağınızdan veya başarısız olacağınızdan emin olmadan tercih edilmelidir. . O zaman bile, daha uzun, daha açık seçeneği düşünün.
C tarzı dökümler static_cast, a yaparken erişim kontrolünü de yok sayar , bu da başka hiçbir dökümün yapamayacağı bir işlem gerçekleştirme yeteneğine sahip oldukları anlamına gelir. Bu çoğunlukla bir çamurdur ve bence C tarzı dökümlerden kaçınmanın başka bir nedeni.
const(hatta değil reinterpret_cast) kaldıramaz " ... gerçekten? Ne olmuş reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
reinterpret_castBir API'nin opak veri türleri kümesi ile uğraşırken bunun genellikle tercih edilen silah olduğunu belirtmek gerekebilir
dynamic_castKalıtım hiyerarşisinde işaretçileri / referansları dönüştürmek için kullanın .
static_castNormal tür dönüşümler için kullanın .
reinterpret_castBit örüntülerinin düşük seviye yeniden yorumlanması için kullanın . Çok dikkatli kullanın.
const_castDöküm yapmak için kullanın const/volatile. Bir const-false API kullanarak sıkışıp kalmadıkça bunu önlemek.
(Yukarıda birçok teorik ve kavramsal açıklama verilmiştir)
Aşağıda static_cast , dynamic_cast , const_cast , reinterpret_cast kullandığım bazı pratik örnekler var .
(Ayrıca açıklamayı anlamak için bunu ifade eder: 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);
}
static_cast<char*>(&val)mi?
static_castyalnızca tanımlanmış dönüşümleri olan türler arasında, kalıtım yoluyla veya / ile görünür ilişki arasında çalışır void *. Diğer her şey için başka oyuncular var. reinterpret castherhangi bir char *türün herhangi bir nesnenin temsilinin okunmasına izin vermesine izin verilir - ve bu anahtar kelimenin yararlı olduğu tek durumlardan biri, uygulama / tanımsız davranışların yaygın bir üreticisi değil. Ancak bu 'normal' bir dönüşüm olarak kabul edilmez, bu nedenle (genellikle) çok muhafazakarlar tarafından izin verilmez static_cast.
Biraz içsel olanı biliyorsanız yardımcı olabilir ...
static_cast
static_castOnlar için kullanın .Aistediğinizde B, static_castçağrının Byapıcısını Aparam olarak iletir. Alternatif olarak, Abir dönüştürme operatörüne (yani A::operator B()) sahip olabilir. Eğer Bböyle yapıcı yok, ya Abir dönüşüm operatörü yoktur, o zaman derleme zamanı hatası alırsınız.A*için B*A ve B kalıtım hiyerarşisi (veya boşluk) ise her zaman aksi takdirde derleme hatası alıyorum başarır.A&de geçerli B&.dynamic_cast
(Base*)için (Derived*)işaretçi aslında türetilmiş bir tür değilse başarısız olabilir.A*için B*döküm ise, geçersiz sonra dynamic_cast nullptr dönecektir.A&için B&dökme geçersizse sonra dynamic_cast bad_cast özel durum oluşturur.const_cast
set<T>, anahtarını değiştirmediğinizden emin olmak için öğelerini yalnızca const olarak döndüren bir kapsayıcıdan yinelenmektir . Ancak amacınız nesnenin anahtar olmayan üyelerini değiştirmekse, o zaman sorun olmamalıdır. Sabitliği kaldırmak için const_cast kullanabilirsiniz.T& SomeClass::foo()yanı sıra const T& SomeClass::foo() const. Kod çoğaltmasını önlemek için, bir işlevin değerini diğerinden döndürmek için const_cast uygulayabilirsiniz.reinterpret_cast
If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.Eğer şanslıysanız çalışma zamanında bir segfault neden olabilir UB olsun. 2. Dinamik dökümler çapraz dökümde de kullanılabilir. 3. Sabit oyuncular bazı durumlarda UB ile sonuçlanabilir. Kullanımı mutablemantıksal sabitliği uygulamak için daha iyi bir seçim olabilir.
mutable, çapraz döküm
Does bu sorunuza cevap?
Hiç kullanmadım reinterpret_castve ihtiyaç duyan bir davaya girmenin kötü bir tasarım kokusu olmadığını merak ediyorum. Üzerinde çalıştığım kod tabanında dynamic_castçok kullanılıyor. Aradaki fark static_cast, istediğiniz dynamic_cast(daha güvenli) veya daha fazla (daha fazla yük) olabilecek bir çalışma zamanı denetiminin yapılmasıdır ( msdn'ye bakın ).
reinterpret_castbir dizi veri parçaları ayıklamak için kullanın . Örneğin char*, hareket etmem ve farklı türlerde bireysel ilkelleri almam gereken paketlenmiş ikili verilerle dolu büyük bir arabelleğim varsa . Böyle bir şey:template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
reinterpret_cast, bunun için çok fazla kullanım yok.
reinterpret_castbir sebepten dolayı kullanıldığını gördüm . Ben bir veritabanında bir "blob" veri tipinde saklanan ham nesne verileri gördüm, sonra veri veritabanından alındığında, reinterpret_castbu ham verileri nesneye dönüştürmek için kullanılır.
Şimdiye kadar olan diğer cevaplara ek olarak, burada static_castyeterli olmadığı reinterpret_castve ihtiyaç duyulmadığı açık olmayan bir örnek . Bir çıkış parametresinde farklı sınıftaki nesnelere (ortak bir temel sınıfı paylaşmayan) işaretçiler döndüren bir işlev olduğunu varsayalım. Bu işlevin gerçek bir örneği ( CoCreateInstance()aslında son parametreye bakın void**). Bu işlevden belirli bir nesne sınıfı istediğinizi varsayalım, böylece işaretçinin türünü önceden bilirsiniz (genellikle COM nesneleri için yaparsınız). Bu durumda işaretçiyi işaretçinize void**şununla atamazsınız static_cast: ihtiyacı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) );
Bununla birlikte, static_castbasit işaretçiler (işaretleyicilere işaretçiler için değil) için çalışır, bu nedenle yukarıdaki kod reinterpret_cast(ekstra değişkenin bir fiyatında) aşağıdaki şekilde önlemek için yeniden yazılabilir :
#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);
&static_cast<void*>(pNetFwPolicy2)Bunun yerine böyle bir şey olmaz static_cast<void**>(&pNetFwPolicy2)mı?
Diğer cevaplar güzel C ++ atmalarını arasındaki tüm farklılıkları tarif ederken, ben C tarzı yayınları kullanmamalısınız neden kısa bir not eklemek istiyorum (Type) varve Type(var).
C ++ yeni başlayanlar için C tarzı dökümler, C ++ yayınları (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) üzerinde üst küme işlemi gibi görünür ve birisi bunları C ++ yayınları üzerinde tercih edebilir . Aslında C tarzı döküm süper set ve yazmak için daha kısa.
C tarzı dökümlerin temel sorunu geliştiricinin oyuncu kadrosunun gerçek niyetini gizlemesidir. C stili dökümler, static_cast <> () ve dynamic_cast <> () tarafından yapılan normalde güvenli dökümlerden const değişkeninin kaldırılabileceği const_cast <> () gibi potansiyel olarak tehlikeli dökümlere kadar neredeyse tüm döküm türlerini yapabilir değiştirilebilir ve tamsayı değerlerini işaretçilerle yeniden yorumlayabilen reinterpret_cast <> ().
İşte örnek.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
C ++ dillerinin dile eklenmesinin ana nedeni, bir geliştiricinin niyetlerini netleştirmesine izin vermekti - neden bu kadroyu yapacak. C ++ 'da mükemmel şekilde geçerli olan C tarzı dökümler kullanarak, kodunuzu daha az okunabilir hale getirir ve özellikle kodunuzu oluşturmayan diğer geliştiriciler için daha fazla hataya açık hale gelirsiniz. Bu nedenle, kodunuzu daha okunabilir ve açık hale getirmek için her zaman C stili yayınlar yerine C ++ yayınlarını tercih etmelisiniz.
İşte Bjarne Stroustrup'un (C ++ yazarı) kitabı C ++ Programlama Dili 4. baskıdan kısa bir alıntı - sayfa 302.
Bu C tarzı döküm, adlandırılan dönüşüm operatörlerinden çok daha tehlikelidir, çünkü gösterimi büyük bir programda tespit etmek daha zordur ve programcı tarafından amaçlanan dönüşüm türü açık değildir.
Anlamak için aşağıdaki kod snippet'ini ele alalım:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Yalnızca satır (4) hatasız derlenir. Yalnızca reinterpret_cast , bir işaretçiyi bir nesneye işaretçiyle ilgisiz herhangi bir nesne türüne dönüştürmek için kullanılabilir.
Dikkat edilmesi gereken noktalardan biri şudur: dynamic_cast çalışma zamanında başarısız olur, ancak çoğu derleyicide de derleme başarısız olur çünkü imlecin yapısında sanal işlevler yoktur, yani dynamic_cast sadece polimorfik sınıf işaretçileriyle çalışır .
C ++ cast ne zaman kullanılır :
static_castvs dynamic_castvs reinterpret_castinternals downcast / upcast görünüm
Bu cevapta, somut bir inişli çıkışlı / inişli çıkış örneğinde bu üç mekanizmayı karşılaştırmak ve nasıl karşılaştırıldığına dair somut bir anlayış sağlamak için altta yatan işaretlere / bellek / düzeneğe ne olduğunu analiz etmek istiyorum.
Bunun, bu yayınların nasıl farklı olduğu konusunda iyi bir sezgi vereceğine inanıyorum:
static_cast: çalışma zamanında bir adres ofseti yapar (düşük çalışma zamanı etkisi) ve bir downcast'in doğru olduğunu kontrol etmez.
dyanamic_cast: çalışma zamanında aynı adres ofsetini yapar, aynı static_castzamanda ve RTTI kullanarak bir downcast'in doğru olup olmadığını pahalı bir güvenlik kontrolü yapar.
Bu güvenlik denetimi, bir temel sınıf işaretçisinin çalışma zamanında belirli bir türde olup nullptrolmadığını, geri dönüşünü geçersiz bir downcast işaret ettiğini kontrol ederek sorgulamanızı sağlar .
Bu nedenle, kodunuz bunu kontrol edemiyor nullptrve geçerli bir iptal edilmedi eylemi gerçekleştiremiyorsa, yalnızca static_castdinamik döküm yerine kullanmanız gerekir .
Bir iptal işlemi kodunuzun gerçekleştirebileceği tek işlemse, belki de yalnızca dynamic_casthata ayıklama yapılarını ( -NDEBUG) etkinleştirmek ve hızlı çalışmalarınızı yavaşlatmamak için static_castörneğin burada yapıldığı gibi başka şekilde kullanmak istersiniz .
reinterpret_cast: çalışma zamanında hiçbir şey yapmaz, adres ofseti bile. İşaretçi, temel sınıfın bile çalışmadığından tam olarak doğru türün üzerine gelmelidir. Ham bayt akışları sürece genellikle bunu istemezsiniz.
Aşağıdaki kod örneğini düşünün:
main.cpp
#include <iostream>
struct B1 {
B1(int int_in_b1) : int_in_b1(int_in_b1) {}
virtual ~B1() {}
void f0() {}
virtual int f1() { return 1; }
int int_in_b1;
};
struct B2 {
B2(int int_in_b2) : int_in_b2(int_in_b2) {}
virtual ~B2() {}
virtual int f2() { return 2; }
int int_in_b2;
};
struct D : public B1, public B2 {
D(int int_in_b1, int int_in_b2, int int_in_d)
: B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
void d() {}
int f2() { return 3; }
int int_in_d;
};
int main() {
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1, 2, 3};
// The memory layout must support the virtual method call use case.
b2s[0] = &b2;
// An upcast is an implicit static_cast<>().
b2s[1] = &d;
std::cout << "&d " << &d << std::endl;
std::cout << "b2s[0] " << b2s[0] << std::endl;
std::cout << "b2s[1] " << b2s[1] << std::endl;
std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;
// Now for some downcasts.
// Cannot be done implicitly
// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
// dp = (b2s[0]);
// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp = static_cast<D*>(b2s[0]);
std::cout << "static_cast<D*>(b2s[0]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = static_cast<D*>(b2s[1]);
std::cout << "static_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Segfault because dp is nullptr.
dp = dynamic_cast<D*>(b2s[0]);
std::cout << "dynamic_cast<D*>(b2s[0]) " << dp << std::endl;
//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = dynamic_cast<D*>(b2s[1]);
std::cout << "dynamic_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Undefined behaviour to an unrelated memory address because this
// did not calculate the offset to get from B2* to D*.
dp = reinterpret_cast<D*>(b2s[1]);
std::cout << "reinterpret_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}
Aşağıdakilerle derleyin, çalıştırın ve sökün:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out
nerede çalıştırmayı karşılaştırmayı kolaylaştırmak için ASLR'yi devre dışı bırakmaksetarch için kullanılır .
Olası çıktı:
&d 0x7fffffffc930
b2s[0] 0x7fffffffc920
b2s[1] 0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0]) 0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d 1
static_cast<D*>(b2s[1]) 0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d 3
dynamic_cast<D*>(b2s[0]) 0
dynamic_cast<D*>(b2s[1]) 0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767
Şimdi, sanal yöntem çağrılarını verimli bir şekilde desteklemek için https://en.wikipedia.org/wiki/Virtual_method_table adresinde belirtildiği gibi , bellek veri yapısı aşağıdaki Dgibi görünmelidir:
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
D:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
Anahtar gerçek şu ki, içerisindeki bellek veri yapısı D, içerideki B1ve B2dahili olanla uyumlu olan bellek yapısını içerir .
Bu nedenle kritik sonuca ulaşıyoruz:
upcast veya downcast yalnızca işaretçi değerini derleme zamanında bilinen bir değere kaydırmalıdır.
Bu şekilde, Dtemel tür dizisine geçtiğinde, cast türü aslında bu dengeyi hesaplar ve tam olarak B2bellekte geçerli gibi görünen bir şeyi işaret eder :
b2s[1] = &d;
Dbunun yerine bunun için vtable olması B2ve bu nedenle tüm sanal çağrılar şeffaf bir şekilde çalışır.
Şimdi nihayet tip dökümüne ve somut örneğimizin analizine geri dönebiliriz.
Stdout çıktısından şunu görüyoruz:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
Bu nedenle, static_castorada yapılan örtük D, 0x7fffffffc930'daki tam veri yapısından 0x7fffffffc940'takine B2benzer bir şekilde ofseti doğru bir şekilde hesapladı . Ayrıca, 0x7fffffffc930 ve 0x7fffffffc940 arasında yatan şeyin muhtemelen B1veri ve vtable olduğunu da çıkarıyoruz.
Sonra, downcast bölümlerinde, geçersiz olanların nasıl başarısız olduğunu ve nedenini anlamak artık kolay:
static_cast<D*>(b2s[0]) 0x7fffffffc910: derleyici sadece denemek ve B2içeren bir denemek için derleme zamanı bayt 0x10 gittiD
Ancak b2s[0]bir a olmadığı için D, şimdi tanımlanmamış bir hafıza bölgesine işaret ediyor.
Sökme:
49 dp = static_cast<D*>(b2s[0]);
0x0000000000000fc8 <+414>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fcc <+418>: 48 85 c0 test %rax,%rax
0x0000000000000fcf <+421>: 74 0a je 0xfdb <main()+433>
0x0000000000000fd1 <+423>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fd5 <+427>: 48 83 e8 10 sub $0x10,%rax
0x0000000000000fd9 <+431>: eb 05 jmp 0xfe0 <main()+438>
0x0000000000000fdb <+433>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000000fe0 <+438>: 48 89 45 98 mov %rax,-0x68(%rbp)
yani GCC'nin şunları yaptığını görüyoruz:
Dyok kidynamic_cast<D*>(b2s[0]) 0: C ++ aslında kadronun geçersiz olduğunu ve geri döndüğünü buldu nullptr!
Bunun derleme zamanında yapılmasının bir yolu yoktur ve bunu sökme işleminden doğrulayacağız:
59 dp = dynamic_cast<D*>(b2s[0]);
0x00000000000010ec <+706>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x00000000000010f0 <+710>: 48 85 c0 test %rax,%rax
0x00000000000010f3 <+713>: 74 1d je 0x1112 <main()+744>
0x00000000000010f5 <+715>: b9 10 00 00 00 mov $0x10,%ecx
0x00000000000010fa <+720>: 48 8d 15 f7 0b 20 00 lea 0x200bf7(%rip),%rdx # 0x201cf8 <_ZTI1D>
0x0000000000001101 <+727>: 48 8d 35 28 0c 20 00 lea 0x200c28(%rip),%rsi # 0x201d30 <_ZTI2B2>
0x0000000000001108 <+734>: 48 89 c7 mov %rax,%rdi
0x000000000000110b <+737>: e8 c0 fb ff ff callq 0xcd0 <__dynamic_cast@plt>
0x0000000000001110 <+742>: eb 05 jmp 0x1117 <main()+749>
0x0000000000001112 <+744>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000001117 <+749>: 48 89 45 98 mov %rax,-0x68(%rbp)
İlk olarak bir NULL kontrolü vardır ve çıkış NULL ise NULL döndürür.
Aksi takdirde, RDX, RSI ve RDI ve çağrılarda bazı argümanlar kurar __dynamic_cast.
Bunu daha fazla analiz etmek için sabrım yok, ancak diğerlerinin söylediği gibi, bunun çalışmasının tek yolu __dynamic_cast, sınıf hiyerarşisini temsil eden bazı ekstra RTTI bellek içi veri yapılarına erişmektir.
Bu nedenle B2, bu tablonun girişinden başlamalı , sonra bu tür hiyerarşisinde bir tür Dtahmini için vtable'ı bulana kadar yürümelidir b2s[0].
Bu yüzden dökümün yeniden yorumlanması potansiyel olarak pahalıdır! İşte bir dönüştürücü bir astar yama örnek dynamic_casta static_castkarmaşık bir projede% 33 çalışma zamanı düşük! .
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940bu sadece bize körü körüne inanıyor: bir Dadres olduğunu söyledik b2s[1]ve derleyici ofset hesaplama yapmıyor.
Ancak bu yanlıştır, çünkü D aslında 0x7fffffffc930'dadır, 0x7fffffffc940'ta olan şey D içindeki B2 benzeri yapıdır! Böylece çöp kutusuna erişilir.
Bunu -O0, değeri sadece hareket ettiren korkunç montajdan doğrulayabiliriz:
70 dp = reinterpret_cast<D*>(b2s[1]);
0x00000000000011fa <+976>: 48 8b 45 d8 mov -0x28(%rbp),%rax
0x00000000000011fe <+980>: 48 89 45 98 mov %rax,-0x68(%rbp)İlgili sorular:
Ubuntu 18.04 amd64, GCC 7.4.0 üzerinde test edilmiştir.