මේවායේ නිසි භාවිතයන් මොනවාද:
static_cast
dynamic_cast
const_cast
reinterpret_cast
- සී විලාසිතාවේ වාත්තු
(type)value
- ක්රියාකාරී ශෛලියේ වාත්තු කිරීම
type(value)
කුමන විශේෂිත අවස්ථා වලදී භාවිතා කළ යුතුද යන්න යමෙකු තීරණය කරන්නේ කෙසේද?
මේවායේ නිසි භාවිතයන් මොනවාද:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(type)value
type(value)
කුමන විශේෂිත අවස්ථා වලදී භාවිතා කළ යුතුද යන්න යමෙකු තීරණය කරන්නේ කෙසේද?
Answers:
static_cast
ඔබ භාවිතා කිරීමට උත්සාහ කළ යුතු පළමු කාස්ට් වේ. එය වර්ග (වැනි අතර ගම්ය පරිවර්ථනයන් වැනි දේවල් int
කිරීමට float
, හෝ පෙන්නුම් කරන්නක් void*
), සහ එය ද පැහැදිලි පරිවර්තනය කාර්යයන් (හෝ ගම්ය අය) කතා කළ හැකිය. බොහෝ අවස්ථාවන්හීදී, පැහැදිලිව ප්රකාශ static_cast
කිරීම අවශ්ය නොවේ, නමුත් T(something)
වාක්ය ඛණ්ඩය සමාන වන (T)something
අතර එය වළක්වා ගත යුතුය (පසුව වැඩි විස්තර). T(something, something_else)
කෙසේ වෙතත්, A ආරක්ෂිත වන අතර, ඉදිකිරීම්කරු ඇමතීමට සහතික වේ.
static_cast
උරුම ධූරාවලිය හරහා ද දැමිය හැකිය. ඉහළට (මූලික පංතියක් දෙසට) වාත්තු කිරීමේදී එය අනවශ්යය, නමුත් පහළට වාත්තු කිරීමේදී එය virtual
උරුමය හරහා නොයන තාක් කල් එය භාවිතා කළ හැකිය . කෙසේ වෙතත්, එය පරික්ෂා කිරීම සිදු නොකරන static_cast
අතර, ධූරාවලියක් ඇත්ත වශයෙන්ම වස්තුවේ වර්ගය නොවන වර්ගයකට පහත හෙලීම නිර්වචනය නොකළ හැසිරීමකි .
const_cast
const
විචල්යයක් ඉවත් කිරීමට හෝ එකතු කිරීමට භාවිතා කළ හැකිය ; වෙනත් 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_cast
static_cast
(ප්රවේශ සීමාවන් නොසලකා හැරියද)static_cast
(ඉහත බලන්න), පසුව const_cast
reinterpret_cast
reinterpret_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
. ඔබ වැරදි-වැරදි API භාවිතා කරමින් හිර වී ඇත්නම් මිස මෙය වළක්වා ගන්න.
(න්යායාත්මක හා සංකල්පීය පැහැදිලි කිරීම් රාශියක් ඉහත දක්වා ඇත)
පහත කිහිපයකි ප්රායෝගික උදාහරණ මම භාවිතා කරන විට 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)
නැවත අර්ථකථනය_ විකාශනය:
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_cast
ඇමතුම් B
ගේ ඉදිකිරීමටත් පසුකර A
param ලෙස. විකල්පයක් ලෙස, A
පරිවර්තන ක්රියාකරුවෙකු සිටිය හැකිය (එනම් A::operator B()
). B
එවැනි ඉදිකිරීම්කරුවෙකු නොමැති නම් හෝ A
පරිවර්තන ක්රියාකරුවෙකු නොමැති නම්, ඔබට සම්පාදක කාල දෝෂයක් ලැබේ.A*
කිරීම B*
සැමවිටම සාර්ථක වේ.A&
වේ B&
.ගතික_ විකාශනය
(Base*)
කිරීමට (Derived*)
අවධානය යොමුකළ ව්යුත්පන්න වර්ගයේ ඇත්තටම නොවේ නම් අසමත් විය හැක.A*
කිරීමට B*
, වාත්තු වලංගු නොවන නම් dynamic_cast nullptr හැරී යනු ඇත.A&
කිරීමට B&
කාස්ට් වලංගු නොවන නම් dynamic_cast bad_cast හැර විසි කරනු ඇත.const_cast
set<T>
නැවත යෙදීමයි, එමඟින් එහි මූලද්රව්යයන් කොන්ස් ලෙස පමණක් ලබා දෙන අතර ඔබ එහි යතුර වෙනස් නොකරන බවට වග බලා ගන්න. කෙසේ වෙතත් ඔබේ අභිප්රාය වස්තුවෙහි ප්රධාන නොවන සාමාජිකයන් වෙනස් කිරීම නම් එය හරි විය යුතුය. ස්ථායිතාව ඉවත් කිරීමට ඔබට const_cast භාවිතා කළ හැකිය.T& SomeClass::foo()
මෙන්ම const T& SomeClass::foo() const
. කේත අනුපිටපත් වළක්වා ගැනීම සඳහා, එක් ශ්රිතයක අගය තවත් ක්රියාවකින් ආපසු ලබා දීම සඳහා ඔබට const_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**
). මෙම ශ්රිතයෙන් ඔබ විශේෂිත වස්තු වර්ගයක් ඉල්ලයි යැයි සිතමු, එබැවින් ඔබ දර්ශකය සඳහා වර්ගය කල්තියා දන්නවා (ඔබ බොහෝ විට COM වස්තු සඳහා කරන). මෙම අවස්ථාවේ දී ඔබ ඔබගේ අවධානය යොමුකළ පෙන්නුම් කරන්නක් ප්රකාශ කළ නොහැකි 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)
නැද්ද?
වෙනත් පිළිතුරු අවසරයෙන් C ++ නීති ක්රීයාත්මක වන අතර සියලු වෙනස්කම් විස්තර අතර, මම ඔබ C ආකාරයේ නීති ක්රීයාත්මක වන භාවිතා කළ යුතු නෑ ඇයි කෙටි සටහනක් එකතු කිරීමට කැමැත්තෙමි (Type) var
හා Type(var)
.
සී ++ ආරම්භකයින් සඳහා සී-ස්ටයිල් කැස්ට් යනු සී ++ කාස්ට් (ස්ථිතික_කාස්ට් <> (), ඩයිනමික්_කාස්ට් <> (), කොන්ස්ට්_කාස්ට් <> (), රීටෙන්ටර්ප්රෙස්ට්_කාස්ට් <> () ට වඩා සුපර්සෙට් මෙහෙයුමක් සේ පෙනේ, යමෙකුට සී ++ කාස්ට් වලට වඩා කැමති විය හැකිය. . ඇත්ත වශයෙන්ම සී-ස්ටයිල් කාස්ට් යනු සුපර්සෙට් සහ ලිවීමට කෙටි ය.
සී-ස්ටයිල් කැස්ට් වල ඇති ප්රධාන ගැටළුව නම්, ඔවුන් සංවර්ධකයින්ගේ සැබෑ අභිප්රාය සැඟවීමයි. ස්ථිතික_කාස්ට් <> () සහ ඩයිනමික්_කාස්ට් <> () විසින් සිදු කරන ලද සාමාන්යයෙන් ආරක්ෂිත වාත්තු සිට const_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 ++ කැස්ට් භාෂාවට එකතු කිරීමට ප්රධාන හේතුව වූයේ සංවර්ධකයෙකුට ඔහුගේ අභිප්රාය පැහැදිලි කර ගැනීමට ඉඩ දීමයි - ඔහු එම වාත්තු කිරීමට යන්නේ ඇයි. සී ++ හි පරිපූර්ණ ලෙස වලංගු වන සී-ස්ටයිල් කැස්ට් භාවිතා කිරීමෙන් ඔබ ඔබේ කේතය අඩු කියවිය හැකි සහ විශේෂයෙන් ඔබේ කේතය නිර්මාණය නොකළ වෙනත් සංවර්ධකයින් සඳහා දෝෂ සහිත වේ. එබැවින් ඔබේ කේතය වඩාත් කියවිය හැකි සහ පැහැදිළි කිරීමට නම් ඔබ සැමවිටම සී-ශෛලියේ කාස්ට් වලට වඩා සී ++ කැස්ට් වලට වැඩි කැමැත්තක් දැක්විය යුතුය.
මෙන්න Bjarne Stroustrup's (C ++ හි කතුවරයා) පොතේ C ++ ක්රමලේඛන භාෂාව 4 වන සංස්කරණය - 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) පමණක් දෝෂයකින් තොරව සම්පාදනය කරයි. කිසියම් වස්තුවකට දර්ශකයක් සම්බන්ධයක් නැති ඕනෑම වස්තු වර්ගයක් බවට පරිවර්තනය කිරීමට භාවිතා කළ හැක්කේ reinterpret_cast පමණි .
මෙය සැලකිල්ලට ගත යුතු එක් කරුණක් නම්: ගතික_ විකාශය ක්රියාත්මක වන වේලාවේදී අසමත් වනු ඇත, කෙසේ වෙතත් බොහෝ සම්පාදකයින් මත එය සම්පාදනය කිරීමට අසමත් වනු ඇත, මන්ද යත්, දර්ශකයේ ව්යුහය තුළ අථත්ය කාර්යයන් නොමැති හෙයින්, ගතික_කාස්ට් ක්රියා කරන්නේ බහුමාමක පන්ති දර්ශකයන් සමඟ පමණි .
C ++ වාත්තු භාවිතා කළ යුත්තේ කවදාද :
static_cast
එදිරිව dynamic_cast
එදිරිව reinterpret_cast
අභ්යන්තරිකයන් පහත් / ඉහළ මට්ටමේ දර්ශනයක්
මෙම පිළිතුරේ දී, මට අවශ්ය වන්නේ මෙම යාන්ත්රණ තුන කොන්ක්රීට් උඩු යටිකුරු / පහත් උදාහරණයකින් සංසන්දනය කර ඒවා සංසන්දනය කරන ආකාරය පිළිබඳ ස්ථිර අවබෝධයක් ලබා දීම සඳහා යටි පොයින්ටර්ස් / මතකය / එකලස් කිරීම සඳහා කුමක් සිදුවේද යන්න විශ්ලේෂණය කිරීමයි.
මෙම කුලයන් වෙනස් වන්නේ කෙසේද යන්න පිළිබඳව මෙය හොඳ අවබෝධයක් ලබා දෙනු ඇතැයි මම විශ්වාස කරමි:
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;
මේ සඳහා vtable ඇති බව හැර D
ඒ වෙනුවට අතර B2
, එබැවින් සියලු අථත්ය ඇමතුම් විනිවිදභාවයෙන් ක්රියා කරයි.
දැන්, අපට අවසාන වශයෙන් යතුරු ලියනය කිරීම සහ අපගේ සංයුක්ත උදාහරණය විශ්ලේෂණය කිරීම වෙත ආපසු යා හැකිය.
Stdout නිමැවුමෙන් අපට පෙනෙන්නේ:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
එම නිසා, එහි static_cast
සිදු කර ඇති ව්යංගයෙන් D
0x7fffffffc930 හි සම්පූර්ණ දත්ත ව්යුහයේ සිට 0x7fffffffc940 හි ඇති ඕෆ්සෙට් එක නිවැරදිව ගණනය කර B2
ඇත. 0x7fffffffc930 සහ 0x7fffffffc940 අතර ඇති දේ B1
දත්ත සහ vtable විය හැකි බව අපි අනුමාන කරමු .
පසුව, පහත් කොට ඇති කොටස් වල, අවලංගු ඒවා අසමත් වන්නේ කෙසේද සහ ඇයි යන්න තේරුම් ගැනීම දැන් පහසුය:
static_cast<D*>(b2s[0]) 0x7fffffffc910
: සම්පාදකයා ටයිම් බයිට් 0x10 කින් ඉහළට ගොස් උත්සාහ කර බැලීමට උත්සාහ B2
කරයිD
නමුත් නිසා b2s[0]
එය නොවූD
, එය දැන් නිර්වචනය නොකළ මතක කලාපයකට යොමු කරයි.
විසුරුවා හැරීම:
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)
එබැවින් GCC විසින් කරන බව අපට පෙනේ:
D
නොපවතින ස්ථානයට ළඟා වීමට 0x10 එයින් අඩු කරන්න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 නම් එය NULL ලබා දෙයි.
එසේ නොමැති නම්, එය RDX, RSI සහ RDI සහ ඇමතුම් වල යම් තර්ක විතර්ක කරයි __dynamic_cast
.
මෙය තවදුරටත් විශ්ලේෂණය කිරීමට මට ඉවසීමක් නැත, නමුත් අනෙක් අය පැවසූ පරිදි, මෙය ක්රියාත්මක වීමට ඇති එකම ක්රමය __dynamic_cast
පන්ති ධූරාවලිය නියෝජනය කරන අතිරේක RTTI මතකයේ දත්ත ව්යුහයන් වෙත ප්රවේශ වීමයි.
එම නිසා එය B2
එම වගුව සඳහා වන ප්රවේශයෙන් ආරම්භ විය යුතු අතර, ඒ සඳහා වන විචල්යතාවය සොයා ගන්නා තෙක් මෙම පන්ති ධූරාවලියෙහි ගමන් කරන්නD
සිට typecast b2s[0]
.
නැවත අර්ථ නිරූපණය කිරීම මිල අධික විය හැක්කේ මේ නිසා ය! මෙතන ඇති පරිවර්තනය එක් නෞකාවක් ලප එහිදී උදාහරණයක් dynamic_cast
වෙත static_cast
සංකීර්ණ ව්යාපෘතිය 33% කින් ධාවන අඩු! .
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
මේ තැනැත්තා අපව අන්ධ ලෙස විශ්වාස කරයි: අපි කිව්වේ D
ලිපිනයක් ඇති බවයිb2s[1]
, සම්පාදකයා ඕෆ්සෙට් ගණනය කිරීම් නොකරන බවත්ය.
නමුත් මෙය වැරදියි, D ඇත්ත වශයෙන්ම 0x7fffffffc930 හි ඇති නිසා, 0x7fffffffc940 හි ඇති දෙය D තුළ ඇති B2 වැනි ව්යුහයයි! එබැවින් කුණු කූඩයට ප්රවේශ වේ.
-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)
අදාළ ප්රශ්න:
උබුන්ටු 18.04 amd64, GCC 7.4.0 හි පරීක්ෂා කරන ලදී.