के उचित उपयोग क्या हैं:
static_castdynamic_castconst_castreinterpret_cast- सी-स्टाइल कास्ट
(type)value - फंक्शन-स्टाइल कास्ट
type(value)
कोई कैसे तय करता है कि किन विशिष्ट मामलों में उपयोग करना है?
के उचित उपयोग क्या हैं:
static_castdynamic_castconst_castreinterpret_cast(type)valuetype(value)कोई कैसे तय करता है कि किन विशिष्ट मामलों में उपयोग करना है?
जवाबों:
static_castपहली कास्ट है जिसे आपको उपयोग करने का प्रयास करना चाहिए। यह (जैसे प्रकार के बीच निहित रूपांतरणों को करता है intकरने के लिए float, या सूचक को void*), और यह भी स्पष्ट रूपांतरण कार्यों (या छुपा हुआ वाले) कह सकते हैं। कई मामलों में, स्पष्ट रूप से बताते हुए static_castआवश्यक नहीं है, लेकिन यह ध्यान रखना महत्वपूर्ण है कि T(something)वाक्यविन्यास के बराबर है (T)somethingऔर इससे बचा जाना चाहिए (बाद में उस पर अधिक)। एक T(something, something_else), सुरक्षित है तथापि, और निर्माता कॉल करने के लिए गारंटी।
static_castवंशानुगत पदानुक्रम के माध्यम से भी डाली जा सकती है। ऊपर की ओर (बेस क्लास की ओर) कास्टिंग करते समय यह अनावश्यक है, लेकिन जब नीचे की ओर कास्टिंग की जाती है तो इसका उपयोग तब तक किया जा सकता है जब तक यह virtualविरासत के माध्यम से नहीं डाली जाती है । हालाँकि, यह जाँच नहीं करता है, और यह static_castएक प्रकार के पदानुक्रम को नीचे करने के लिए अपरिभाषित व्यवहार है जो वास्तव में ऑब्जेक्ट का प्रकार नहीं है।
const_castconstएक चर को हटाने या जोड़ने के लिए इस्तेमाल किया जा सकता है ; कोई अन्य C ++ कास्ट इसे हटाने में सक्षम है (नहीं भी reinterpret_cast)। यह ध्यान रखना महत्वपूर्ण है कि constमूल चर होने पर पूर्व मान को संशोधित करना केवल अपरिभाषित है const; यदि आप इसका उपयोग constकिसी ऐसी चीज़ का संदर्भ लेने के लिए करते हैं const, जिसके साथ घोषित नहीं किया गया था , तो यह सुरक्षित है। यह उपयोगी हो सकता है जब constउदाहरण के लिए, के आधार पर सदस्य कार्यों को ओवरलोड करना । इसका उपयोग constकिसी ऑब्जेक्ट को जोड़ने के लिए भी किया जा सकता है , जैसे कि एक सदस्य फ़ंक्शन को अधिभार देना।
const_castयह भी इसी तरह से काम करता है volatile, हालांकि यह कम आम है।
dynamic_castबहुरूपता से निपटने के लिए विशेष रूप से उपयोग किया जाता है। आप किसी पॉलीमॉर्फ़िक प्रकार को किसी अन्य वर्ग प्रकार के लिए एक पॉइंटर या संदर्भ दे सकते हैं (एक पॉलीमॉर्फिक प्रकार में कम से कम एक आभासी फ़ंक्शन है, घोषित या विरासत में मिला है)। आप इसे केवल नीचे की ओर कास्टिंग से अधिक के लिए उपयोग कर सकते हैं - आप बग़ल में या यहां तक कि दूसरी श्रृंखला भी डाल सकते हैं। dynamic_castवांछित वस्तु की तलाश और इसे वापस यदि संभव हो जाएगा। यदि ऐसा नहीं हो सकता है, तो यह nullptrएक पॉइंटर के मामले में वापस आ जाएगा , या std::bad_castएक संदर्भ के मामले में फेंक देगा ।
dynamic_castहालांकि कुछ सीमाएं हैं। यदि वंशानुक्रम पदानुक्रम (तथाकथित 'खूंखार हीरा') में एक ही प्रकार की कई वस्तुएं हैं तो यह काम नहीं करता है और आप virtualवंशानुक्रम का उपयोग नहीं कर रहे हैं । यह केवल सार्वजनिक विरासत के माध्यम से भी जा सकता है - यह हमेशा protectedया privateविरासत के माध्यम से यात्रा करने में विफल रहेगा । यह शायद ही कभी एक मुद्दा है, हालांकि, विरासत के ऐसे रूप दुर्लभ हैं।
reinterpret_castसबसे खतरनाक कास्ट है, और इसे बहुत कम इस्तेमाल किया जाना चाहिए। यह एक प्रकार को सीधे दूसरे में बदल देता है - जैसे कि एक पॉइंटर से दूसरे में मूल्य डालना, या एक पॉइंटर को स्टोर करना int, या अन्य सभी प्रकार की गंदी चीजों को। मोटे तौर पर, आपके पास एकमात्र गारंटी reinterpret_castयह है कि आम तौर पर यदि आप परिणाम को मूल प्रकार में वापस लाते हैं, तो आपको सटीक समान मूल्य मिलेगा (लेकिन यह नहीं कि मध्यवर्ती प्रकार मूल प्रकार से छोटा है)। ऐसे कई रूपांतरण हैं, जो reinterpret_castनहीं कर सकते हैं। यह विशेष रूप से अजीब रूपांतरण और बिट जोड़तोड़ के लिए उपयोग किया जाता है, जैसे कि कच्चे डेटा स्ट्रीम को वास्तविक डेटा में बदलना, या डेटा को संरेखित करने के लिए पॉइंटर के कम बिट्स में डेटा संग्रहीत करना।
सी-शैली डाली और समारोह शैली डाली का उपयोग कर डाले हैं (type)objectया type(object), क्रमशः, और कार्यात्मक रूप से बराबर कर रहे हैं। उन्हें निम्नलिखित में से पहले के रूप में परिभाषित किया गया है जो सफल होता है:
const_caststatic_cast (हालांकि उपयोग प्रतिबंधों की अनदेखी)static_cast (ऊपर देखें), फिर const_castreinterpret_castreinterpret_cast, फिर const_castइसलिए इसे कुछ उदाहरणों में अन्य जातियों के प्रतिस्थापन के रूप में इस्तेमाल किया जा सकता है, लेकिन एक में विकसित करने की क्षमता के कारण बेहद खतरनाक हो सकता है reinterpret_cast, और बाद में पसंद किया जाना चाहिए जब स्पष्ट कास्टिंग की आवश्यकता होती है, जब तक कि आप सुनिश्चित न हों कि static_castवह सफल reinterpret_castहोगा या विफल होगा । फिर भी, लंबे, अधिक स्पष्ट विकल्प पर विचार करें।
सी-स्टाइल कास्ट्स भी प्रदर्शन करते समय अभिगम नियंत्रण की उपेक्षा करते हैं static_cast, जिसका अर्थ है कि उनके पास एक ऑपरेशन करने की क्षमता है जो कोई अन्य कलाकार नहीं कर सकता है। यह ज्यादातर एक कीचड़ है, हालांकि, और मेरे दिमाग में सी-स्टाइल कास्ट से बचने का सिर्फ एक और कारण है।
const(नहीं भी reinterpret_cast)" ... वास्तव में? किस बारे में reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
reinterpret_castअक्सर एपीआई का सेट अपारदर्शी डेटा प्रकारों के साथ काम करते समय पसंद का हथियार होता है
dynamic_castएक विरासत पदानुक्रम के भीतर संकेत / संदर्भ परिवर्तित करने के लिए उपयोग करें ।
static_castसाधारण प्रकार के रूपांतरणों के लिए उपयोग करें ।
reinterpret_castबिट पैटर्न के निम्न-स्तरीय पुनर्व्याख्या के लिए उपयोग करें । अत्यधिक सावधानी के साथ उपयोग करें।
const_castदूर कास्टिंग के लिए उपयोग करें const/volatile। इससे बचें जब तक कि आप एक कास्ट-गलत एपीआई का उपयोग करके अटक नहीं जाते हैं।
(ऊपर बहुत सैद्धांतिक और वैचारिक व्याख्या दी गई है)
नीचे में से कुछ हैं व्यावहारिक उदाहरण है जब मैं इस्तेमाल किया static_cast , dynamic_cast , const_cast , reinterpret_cast ।
(स्पष्टीकरण को समझने के लिए इसे भी संदर्भित करता है: 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);
.....
}
डायनामिक_कास्ट:
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)?
static_castकेवल परिभाषित रूपांतरणों के साथ प्रकारों के बीच काम करता है, वंशानुक्रम द्वारा दृश्य संबंध या / से void *। बाकी सभी चीजों के लिए, अन्य जातियां हैं। reinterpret castकिसी भी char *प्रकार के किसी भी ऑब्जेक्ट के प्रतिनिधित्व को पढ़ने की अनुमति देने की अनुमति है - और केवल उन मामलों में से एक जहां कीवर्ड उपयोगी है, न कि कार्यान्वयन के एक बड़े पैमाने पर जनरेटर- / अपरिभाषित व्यवहार। लेकिन इसे 'सामान्य' रूपांतरण नहीं माना जाता है, इसलिए (आमतौर पर) बहुत रूढ़िवादी द्वारा इसकी अनुमति नहीं दी जाती है static_cast।
यदि आप बहुत कम इंटर्ल्स जानते हैं तो यह मदद कर सकता है ...
static_cast
static_castउनके लिए उपयोग करें।Aलिए कहते हैं B, तो कंस्ट्रक्टर को पारम कहते हैं। वैकल्पिक रूप से, एक रूपांतरण ऑपरेटर (यानी ) हो सकता है । यदि इस तरह का कोई निर्माणकर्ता नहीं है, या आपके पास रूपांतरण ऑपरेटर नहीं है, तो आपको संकलन समय त्रुटि मिलती है।static_castBAAA::operator B()BAA*करने के लिए B*हमेशा सफल होता है ए और बी वंशानुगत पदानुक्रम (या शून्य) में हैं अन्यथा आप संकलन त्रुटि मिलती है।A&है B&।dynamic_cast
(Base*)करने के लिए (Derived*)विफल हो सकता है अगर सूचक व्युत्पन्न प्रकार की वास्तव में नहीं है।A*लिए B*, यदि कास्ट अमान्य है, तो डायनेमिक_कास्ट nullptr वापस आ जाएगा।A&करने के लिए B&करता है, तो डाली अमान्य है तो dynamic_cast bad_cast अपवाद फेंक देते हैं।const_cast
set<T>कि केवल अपने तत्वों को कॉन्स्टेंट के रूप में देता है ताकि यह सुनिश्चित हो सके कि आप इसकी कुंजी को नहीं बदलते हैं। हालाँकि यदि आपका इरादा ऑब्जेक्ट के गैर-प्रमुख सदस्यों को संशोधित करना है तो यह ठीक होना चाहिए। आप कब्ज को दूर करने के लिए const_cast का उपयोग कर सकते हैं।T& SomeClass::foo()साथ ही लागू करना चाहते हैं const T& SomeClass::foo() const। कोड दोहराव से बचने के लिए, आप दूसरे से एक फ़ंक्शन का मान वापस करने के लिए const_cast लागू कर सकते हैं।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.आप यूबी प्राप्त करते हैं जिसके परिणामस्वरूप यदि आप भाग्यशाली हैं तो रनगेट पर सेगफॉल्ट हो सकता है। 2. डायनामिक कास्ट का उपयोग क्रॉस कास्टिंग में भी किया जा सकता है। 3. कास्ट कास्ट कुछ मामलों में यूबी में परिणाम कर सकते हैं। mutableतार्किक विवशता को लागू करने के लिए उपयोग करना एक बेहतर विकल्प हो सकता है।
mutable, जो कास्टिंग के साथ आते हैं आदि
क्या यह आपके प्रश्न का उत्तर देता है?
मैंने कभी उपयोग नहीं किया है reinterpret_cast, और आश्चर्य है कि क्या ऐसे मामले में चल रहा है जिसे इसकी आवश्यकता नहीं है, यह खराब डिजाइन की गंध नहीं है। मेरे द्वारा काम किए जाने वाले कोड आधार में dynamic_castबहुत उपयोग किया जाता है। इसके साथ अंतर static_castयह है कि एक dynamic_castरनटाइम चेकिंग होती है जो (सुरक्षित) या (अधिक ओवरहेड) हो सकती है जो आप चाहते हैं (देखें msdn )।
reinterpret_castएक सरणी से डेटा के टुकड़े निकालने के लिए उपयोग करता हूं । उदाहरण के लिए अगर मेरे पास एक char*बफ़र है जिसमें पैक्ड बाइनरी डेटा भरा हुआ है, जिसे मुझे अलग-अलग प्रकार के अलग-अलग प्राथमिकताओं के माध्यम से स्थानांतरित करने और प्राप्त करने की आवश्यकता है। कुछ इस तरह: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, इसके लिए बहुत सारे उपयोग नहीं हैं।
reinterpret_castएक कारण के लिए कभी देखा है। मैंने कच्चे ऑब्जेक्ट डेटा को डेटाबेस में "ब्लॉब" डेटाटाइप पर संग्रहीत किया है, तब जब डेटा डेटाबेस से पुनर्प्राप्त reinterpret_castकिया जाता है , तो इस कच्चे डेटा को ऑब्जेक्ट में बदलने के लिए उपयोग किया जाता है।
अब तक के अन्य उत्तरों के अलावा, यहां स्पष्ट उदाहरण static_castहै कि पर्याप्त नहीं है जहां reinterpret_castइसकी आवश्यकता है। मान लीजिए कि एक फ़ंक्शन है जो आउटपुट पैरामीटर में विभिन्न वर्गों की वस्तुओं (जो एक सामान्य आधार वर्ग को साझा नहीं करता है) की ओर संकेत देता है। ऐसे फ़ंक्शन का एक वास्तविक उदाहरण है CoCreateInstance()(अंतिम पैरामीटर देखें, जो वास्तव में है void**)। मान लें कि आप इस फ़ंक्शन से ऑब्जेक्ट के विशेष वर्ग का अनुरोध करते हैं, तो आप पहले से ही सूचक के लिए प्रकार जानते हैं (जो आप अक्सर कॉम ऑब्जेक्ट्स के लिए करते हैं)। इस मामले में आप में अपने सूचक को सूचक डाली नहीं कर सकते हैं void**के साथ static_castआप की जरूरत है: reinterpret_cast<void**>(&yourPointer)।
कोड में:
#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) );
हालाँकि, static_castसरल पॉइंटर्स (पॉइंटर्स टू पॉइंटर्स) के लिए काम करता है, इसलिए उपरोक्त कोड reinterpret_castको निम्नलिखित तरीके से (अतिरिक्त चर की कीमत पर) से बचने के लिए फिर से लिखा जा सकता है :
#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)इसके बजाय यह कुछ काम नहीं करेगा static_cast<void**>(&pNetFwPolicy2)?
हालांकि अन्य जवाबों ने सी ++ जातियों के बीच सभी अंतरों का अच्छी तरह से वर्णन किया है, मैं एक लघु नोट जोड़ना चाहूंगा कि आपको सी-स्टाइल कलाकारों (Type) varऔर का उपयोग क्यों नहीं करना चाहिए Type(var)।
C ++ शुरुआती के लिए C- शैली की जातियां C ++ जातियों (static_cast <>), डायनामिक_कास्ट <> (), const_cast <> (), reinterpret_cast <> () पर सुपरसेट ऑपरेशन की तरह दिखती हैं और कोई भी व्यक्ति उन्हें C ++ जातियों के ऊपर पसंद कर सकता है। । वास्तव में सी-स्टाइल कास्ट सुपरसेट और लिखने के लिए छोटा है।
सी-शैली के कलाकारों की मुख्य समस्या यह है कि वे डेवलपर को कलाकारों के वास्तविक इरादे को छिपाते हैं। C- शैली की जातियां लगभग सभी प्रकार की कास्टिंग कर सकती हैं, जो आमतौर पर static_cast <> () और डायनामिक_कास्ट <() द्वारा की जाती हैं जैसे कि const_cast <> () के लिए संभावित खतरनाक जातियाँ, जहाँ const modifier को हटाया जा सकता है ताकि const चर संशोधित किया जा सकता है और reinterpret_cast <> () भी संकेत करने के लिए पूर्णांक मान पुन: व्याख्या कर सकते हैं।
यहाँ नमूना है।
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 + शैली वाले C- शैली वाले कलाकारों को प्राथमिकता देना चाहिए।
यहाँ Bjarne Stroustrup (C ++ के लेखक) की पुस्तक C ++ प्रोग्रामिंग लैंग्वेज 4th संस्करण का एक छोटा उद्धरण है - पृष्ठ 302।
यह सी-स्टाइल कास्ट नामित रूपांतरण ऑपरेटरों की तुलना में कहीं अधिक खतरनाक है क्योंकि संकेतन एक बड़े कार्यक्रम में स्पॉट करने के लिए कठिन है और प्रोग्रामर द्वारा जिस तरह का रूपांतरण करना है वह स्पष्ट नहीं है।
समझने के लिए, आइए नीचे कोड स्निपेट पर विचार करें:
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;
}
केवल पंक्ति (4) त्रुटि के बिना संकलित होती है। केवल रीइंटरटेनमेंट_कास्ट का उपयोग पॉइंटर को ऑब्जेक्ट से पॉइंटर को किसी असंबंधित ऑब्जेक्ट प्रकार में बदलने के लिए किया जा सकता है।
इस पर ध्यान दिया जाना है: डायनामिक_कास्ट रन-टाइम में विफल हो जाएगा, हालांकि अधिकांश कंपाइलरों पर यह संकलित करने में भी विफल हो जाएगा क्योंकि पॉइंटर को डाले जाने की संरचना में कोई वर्चुअल फ़ंक्शन नहीं हैं, जिसका अर्थ है कि डायनेमिक_कास्ट केवल पॉलीफ़ोरफ़िक क्लास पॉइंटर्स के साथ काम करेगा ।
C ++ कास्ट का उपयोग कब करें :
static_castबनाम dynamic_castबनाम reinterpret_castआंतरिक एक डाउनकास्ट / upcast पर देखें
इस उत्तर में, मैं इन तीन तंत्रों की तुलना एक ठोस अपकास्ट / डाउनकास्ट उदाहरण पर करता हूं और विश्लेषण करता हूं कि अंतर्निहित बिंदुओं / मेमोरी / असेंबली का क्या होता है, इसकी ठोस समझ देने के लिए कि वे कैसे तुलना करते हैं।
मेरा मानना है कि इससे उन जातियों के अलग होने का एक अच्छा अंतर्ज्ञान होगा:
static_cast: एक पता रनटाइम (कम रनटाइम प्रभाव) पर ऑफसेट होता है और कोई सुरक्षा जांच नहीं करता है कि डाउनकास्ट सही है।
dyanamic_cast: एक ही पता रनटाइम पर ऑफसेट करता है static_cast, लेकिन यह भी और एक महंगी सुरक्षा जांच है कि एक डाउनकास्ट RTTI का उपयोग करके सही है।
यह सुरक्षा जांच आपको क्वेरी करने की अनुमति देती है यदि एक बेस क्लास पॉइंटर रनटाइम में दिए गए प्रकार का nullptrहोता है , जिसमें वापसी की जाँच करके एक अमान्य डाउनकास्ट इंगित करता है।
इसलिए, यदि आपका कोड उसके लिए जाँच करने में सक्षम नहीं है nullptrऔर एक वैध गैर-गर्भपात की कार्रवाई कर रहा है, तो आपको केवल static_castगतिशील कलाकारों के बजाय उपयोग करना चाहिए ।
यदि एक गर्भपात एकमात्र क्रिया है जो आपका कोड ले सकता है, तो शायद आप केवल dynamic_castडिबग बिल्ड ( -NDEBUG) में सक्षम करना चाहते हैं , और static_castअन्यथा उपयोग करें , जैसे कि यहां किया जाता है , अपने तेज़ रन को धीमा नहीं करने के लिए।
reinterpret_cast: रनटाइम पर कुछ भी नहीं करता है, पता भी ऑफसेट नहीं है। सूचक को सही प्रकार की ओर इंगित करना चाहिए, यहां तक कि एक आधार वर्ग भी काम नहीं करता है। आप आम तौर पर यह तब तक नहीं चाहते जब तक कि कच्ची बाइट धाराएँ शामिल न हों।
निम्नलिखित कोड उदाहरण पर विचार करें:
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;
}
इसके साथ संकलित करें, चलाएं और जुदा करें:
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
जहां setarchहै अक्षम ASLR के लिए इस्तेमाल किया यह आसान रन तुलना करने के लिए बनाने के लिए।
संभव उत्पादन:
&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
अब, जैसा कि उल्लेख किया गया है: https://en.wikipedia.org/wiki/Virtual_method_table वर्चुअल तरीके से कॉल करने के लिए कुशलता से समर्थन करने के लिए, स्मृति डेटा संरचना Dको कुछ इस तरह देखना पड़ता है:
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
मुख्य तथ्य यह है कि मेमोरी डेटा संरचना Dमें आंतरिक मेमोरी शामिल होती B1है जो B2आंतरिक रूप से और उस के साथ संगत होती है ।
इसलिए हम महत्वपूर्ण निष्कर्ष पर पहुँचते हैं:
अपस्टॉक या डाउनकास्ट को केवल संकलक समय पर ज्ञात मूल्य द्वारा सूचक मान को स्थानांतरित करना होगा
इस प्रकार, जब Dबेस प्रकार सरणी में पास हो जाता है, तो टाइप कास्ट वास्तव में गणना करता है कि ऑफसेट और कुछ ऐसा दिखता है जो B2स्मृति में बिल्कुल मान्य जैसा दिखता है :
b2s[1] = &d;
सिवाय इसके कि Dइसके बजाय इसके लिए व्यवहार्य है B2, और इसलिए सभी आभासी कॉल पारदर्शी रूप से काम करते हैं।
अब, हम आखिरकार टाइपिंग कास्टिंग और हमारे ठोस उदाहरण के विश्लेषण के लिए वापस आ सकते हैं।
स्टडआउट आउटपुट से हम देखते हैं:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
इसलिए, static_castवहां किए गए निहितार्थ ने D0x7fffffc930 पर पूर्ण डेटा संरचना से ऑफसेट की गणना ठीक उसी B2तरह की है जो 0x7fffffc940 पर है। हम यह भी अनुमान लगाते हैं कि 0x7fffffc930 और 0x7fffffffc940 के बीच जो निहित है, वह संभवतः B1डेटा और वाइब्रेट हो सकता है ।
फिर, डाउनकास्ट अनुभागों पर, अब यह समझना आसान है कि अमान्य कैसे विफल होते हैं और क्यों:
static_cast<D*>(b2s[0]) 0x7fffffffc910: कंपाइलर केवल 0x10 पर संकलित समय बाइट्स में कोशिश करने और एक B2से युक्त होने के लिए ऊपर चला गयाD
लेकिन क्योंकि b2s[0]यह नहीं था D, यह अब एक अपरिभाषित स्मृति क्षेत्र की ओर इशारा करता है।
Disassembly है:
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)
इसलिए हम देखते हैं कि जीसीसी करता है:
Dजो मौजूद नहीं हैdynamic_cast<D*>(b2s[0]) 0: C ++ ने वास्तव में पाया कि कास्ट अमान्य था और वापस आ गया nullptr!
ऐसा कोई तरीका नहीं है जिससे संकलन समय पर किया जा सके, और हम इस बात की पुष्टि करेंगे:
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)
पहले एक NULL चैक होता है, और यह NULL लौटता है यदि वें einput NULL है।
अन्यथा, यह आरडीएक्स, आरएसआई और आरडीआई और कॉल में कुछ तर्क देता है __dynamic_cast।
मेरे पास अब इसे और अधिक विश्लेषण करने का धैर्य नहीं है, लेकिन जैसा कि अन्य ने कहा, इसके लिए काम करने का एकमात्र तरीका __dynamic_castकुछ अतिरिक्त आरटीटीआई-इन-मेमोरी डेटा संरचनाओं का उपयोग करना है जो वर्ग पदानुक्रम का प्रतिनिधित्व करते हैं।
इसलिए इसे B2उस तालिका के लिए प्रविष्टि से शुरू करना चाहिए , फिर इस वर्ग पदानुक्रम तक चलना चाहिए जब तक कि यह पता नहीं चलता कि Dटाइप टाइपकास्ट के लिए व्यवहार्य है b2s[0]।
यही कारण है कि पुनर्व्याख्या कास्ट संभावित रूप से महंगा है! यहाँ एक उदाहरण है जहाँ एक जटिल प्रक्रिया में एक में एक लाइनर पैच को परिवर्तित dynamic_castकरने से static_castरनटाइम में 33% की कमी आई है! ।
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940यह सिर्फ हमें आँख बंद करके विश्वास करता है: हमने कहा कि एक Dपते पर है b2s[1], और संकलक कोई ऑफसेट गणना नहीं करता है।
लेकिन यह गलत है, क्योंकि डी वास्तव में 0x7fffffc930 पर है, जो 0x7fffffffc940 पर है वह डी के अंदर बी 2 जैसी संरचना है! तो कचरा पहुंच जाता है।
हम इस भयावह -O0सभा से इसकी पुष्टि कर सकते हैं जो मूल्य को चारों ओर ले जाती है:
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)संबंधित सवाल:
Ubuntu 18.04 am6464, GCC 7.4.0 पर परीक्षण किया गया।