এর সঠিক ব্যবহারগুলি কী:
static_cast
dynamic_cast
const_cast
reinterpret_cast
- সি স্টাইলের castালাই
(type)value
- ফাংশন-স্টাইলের castালাই
type(value)
কোনটি নির্দিষ্ট ক্ষেত্রে কোনটি ব্যবহার করবেন তা কীভাবে সিদ্ধান্ত নেওয়া যায়?
এর সঠিক ব্যবহারগুলি কী:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(type)value
type(value)
কোনটি নির্দিষ্ট ক্ষেত্রে কোনটি ব্যবহার করবেন তা কীভাবে সিদ্ধান্ত নেওয়া যায়?
উত্তর:
static_cast
আপনার প্রথম ব্যবহারের চেষ্টা করা উচিত cast এটা তোলে (যেমন ধরনের মধ্যে অন্তর্নিহিত ধর্মান্তর ভালো জিনিস আছে int
থেকে float
, অথবা পয়েন্টার void*
), এবং এটি স্পষ্ট রূপান্তর ফাংশন (অথবা অন্তর্নিহিত বেশী) কল করতে পারেন। অনেক ক্ষেত্রে সুস্পষ্টভাবে বক্তব্য দেওয়া static_cast
প্রয়োজন হয় না, তবে এটি লক্ষ করা গুরুত্বপূর্ণ যে T(something)
বাক্য গঠনটি সমতুল্য (T)something
এবং এড়ানো উচিত (এরপরে আরও)। এ T(something, something_else)
তবে নিরাপদ এবং কনস্ট্রাক্টরকে কল করার গ্যারান্টিযুক্ত।
static_cast
উত্তরাধিকারের শ্রেণিবিন্যাসের মাধ্যমেও নিক্ষিপ্ত হতে পারে। উপরের দিকে (বেস শ্রেণীর দিকে) কাস্ট করার সময় এটি অপ্রয়োজনীয়, তবে নীচের দিকে কাস্টিং করার সময় এটি যতক্ষণ না virtual
উত্তরাধিকারের মধ্যে দিয়ে যায় ততক্ষণ এটি ব্যবহার করা যেতে পারে । এটি যাচাই করে না, তবে এটি static_cast
কোনও ধরণের শ্রেণিবদ্ধতাকে নিখুঁতভাবে নিচে আটকানো আচরণ যা আসলে বস্তুর ধরণ নয়।
const_cast
const
একটি পরিবর্তনশীল অপসারণ বা যুক্ত করতে ব্যবহার করা যেতে পারে ; অন্য কোনও সি ++ কাস্ট এটি মুছে ফেলতে সক্ষম নয় (এমনকি নয় reinterpret_cast
)। এটি লক্ষ্য করা গুরুত্বপূর্ণ যে পূর্বের const
মানটি সংশোধন করা কেবলমাত্র মূল ভেরিয়েবলের ক্ষেত্রেই অপরিজ্ঞাত const
; আপনি যদি এটির const
সাথে ঘোষিত হয়নি এমন কোনও রেফারেন্স বন্ধ করতে ব্যবহার করেন তবে const
এটি নিরাপদ। const
উদাহরণস্বরূপ, ভিত্তিতে সদস্য ফাংশনগুলি ওভারলোড করার সময় এটি কার্যকর হতে পারে । এটি const
কোনও অবজেক্টে যোগ করার জন্য যেমন কোনও সদস্য ফাংশন ওভারলোডকে কল করতেও ব্যবহার করা যেতে পারে ।
const_cast
এটি একইভাবে কাজ করে volatile
, যদিও এটি কম সাধারণ।
dynamic_cast
পলিমারফিজম পরিচালনা করার জন্য একচেটিয়াভাবে ব্যবহৃত হয়। আপনি কোনও পলিমারফিক ধরণের কোনও অন্য শ্রেণীর ধরণের কাছে একটি পয়েন্টার বা রেফারেন্স নিক্ষেপ করতে পারেন (একটি পলিমারফিক ধরণের কমপক্ষে একটি ভার্চুয়াল ফাংশন রয়েছে, ঘোষিত বা উত্তরাধিকারসূত্রে প্রাপ্ত)। আপনি এটি কেবল নীচের দিকে castালাইয়ের চেয়ে আরও বেশি কিছুতে ব্যবহার করতে পারেন - আপনি পাশাপাশি বা অন্য কোনও চেইনও কাস্ট করতে পারেন। dynamic_cast
পছন্দসই বস্তুর খুঁজে বার করা এবং তা ফেরত সম্ভব হলে হবে না। যদি এটি না পারে তবে এটি nullptr
কোনও পয়েন্টারের ক্ষেত্রে ফিরে আসবে , বা std::bad_cast
কোনও রেফারেন্সের ক্ষেত্রে নিক্ষেপ করবে।
dynamic_cast
যদিও কিছু সীমাবদ্ধতা রয়েছে। উত্তরাধিকারের শ্রেণিবিন্যাসে (তথাকথিত 'ভয়ঙ্কর হীরা') একই ধরণের একাধিক অবজেক্ট রয়েছে এবং আপনি virtual
উত্তরাধিকার ব্যবহার করছেন না তবে এটি কাজ করে না । এটি কেবল সর্বজনীন উত্তরাধিকারের মধ্য দিয়ে যেতে পারে - এটি সর্বদা ভ্রমণ protected
বা private
উত্তরাধিকারের পথে ভ্রমণ করতে ব্যর্থ হবে । এটি খুব কমই একটি সমস্যা, যদিও উত্তরাধিকারের এই ধরণেরগুলি বিরল।
reinterpret_cast
এটি সবচেয়ে বিপজ্জনক castালাই এবং এটি খুব অল্প পরিমাণে ব্যবহার করা উচিত। এটি এক প্রকারকে সরাসরি অন্যটিতে রূপান্তরিত করে - যেমন একটি পয়েন্টার থেকে অন্য পয়েন্টারে মান ingালাই করা বা একটি পয়েন্টারটিকে একটি int
বা অন্য সমস্ত বাজে জিনিসগুলিতে সংরক্ষণ করা। মূলত শুধু তোমার সাথে পেতে গ্যারান্টি reinterpret_cast
যে স্বাভাবিকভাবে যদি আপনি আসল ধরনের ফলাফলের পিছনে ফেলে, আপনি সঠিক একই মান পাবেন (কিন্তু না যদি অন্তর্বর্তী টাইপ মূল টাইপ চেয়ে ছোট)। এমন অনেকগুলি রূপান্তর রয়েছে যা reinterpret_cast
নাও করতে পারে। এটি প্রাথমিকভাবে বিশেষত অদ্ভুত রূপান্তর এবং বিট ম্যানিপুলেশনের জন্য ব্যবহৃত হয়, যেমন কোনও কাঁচা ডেটা স্ট্রিমকে আসল ডেটাতে পরিণত করা, বা পয়েন্টারের নীচে বিভক্ত ডেটাতে ডেটা সংরক্ষণ করা।
সি স্টাইলের castালাই এবং ফাংশন-স্টাইলের cast ালাই যথাক্রমে ব্যবহার করে (type)object
বা type(object)
যথাযথভাবে সমান হয় cas এগুলি নিম্নলিখিতটির প্রথম হিসাবে সংজ্ঞাযুক্ত যা সফল হয়:
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
। আপনি যদি কোনও কনস্ট-ভুল এপিআই ব্যবহার না করে থাকেন তবে এড়িয়ে চলুন।
(উপরে অনেক তাত্ত্বিক এবং ধারণাগত ব্যাখ্যা দেওয়া হয়েছে)
নীচে কিছু বাস্তব উদাহরণ আমি যখন ব্যবহৃত static_cast , dynamic_cast , const_cast , reinterpret_cast ।
(ব্যাখ্যাটি বোঝার জন্য এটিও রেফার করে: http://www.cplusplus.com/doc/tutorial/typecasting/ )
স্ট্যাটিক_কাস্ট:
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( ... )*/{
// ...
}
}
কনস্ট_কাস্ট:
// *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
হিসাবে পাস করছেন। বিকল্পভাবে, A
একটি রূপান্তর অপারেটর (যেমন A::operator B()
) থাকতে পারে could যদি B
এই জাতীয় নির্মাতা না থাকে বা A
কোনও রূপান্তর অপারেটর না থাকে তবে আপনি সংকলনের সময় ত্রুটি পান।A*
হতে কাস্ট করুন B*
অন্যথায় আপনি সংকলন ত্রুটি পান।A&
থেকে B&
।dynamic_cast
(Base*)
থেকে (Derived*)
ব্যর্থ হতে পারে যদি পয়েন্টার উদ্ভূত ধরনের আসলে নয়।A*
জন্য B*
, যদি কাস্টটি অবৈধ হয় তবে ডায়নামিক_কাস্ট নালপ্ট্র ফিরে আসবে।A&
করতে B&
যদি ঢালাই অবৈধ তারপর dynamic_cast bad_cast ব্যতিক্রম নিক্ষেপ করা হবে।const_cast
set<T>
যা কেবলমাত্র তার উপাদানগুলি পরিবর্তন করে না তা নিশ্চিত করার জন্য এটির উপাদানগুলি প্রতিস্থাপন করে returns তবে যদি আপনার অভিপ্রায় অবজেক্টের নন-কী সদস্যদের পরিবর্তন করতে হয় তবে তা ঠিক হওয়া উচিত should স্থিরতা অপসারণ করতে আপনি কনস্টেইকাস্ট ব্যবহার করতে পারেন।T& SomeClass::foo()
সেইসাথে const T& SomeClass::foo() const
। কোড সদৃশতা এড়ানোর জন্য, আপনি অন্য থেকে একটি ফাংশনের মান ফেরত দিতে কনস্ট_কাস্ট প্রয়োগ করতে পারেন।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.
হ'ল : 1. আপনি ইউবি পাবেন যা ভাগ্যবান হলে রানটাইমের সময় সেগফল্টের ফলস্বরূপ। ২. গতিশীল কাস্টগুলি ক্রস কাস্টিংয়েও ব্যবহার করা যেতে পারে। ৩. কনস্ট কাস্টের কিছু ক্ষেত্রে ইউবি হতে পারে। mutable
যৌক্তিক দৃ const়তা প্রয়োগ করার জন্য ব্যবহার করা আরও ভাল পছন্দ হতে পারে।
mutable
ক্রস কাস্টিং ইত্যাদির সাথে যে সমস্ত জটিলতা দেখা দেয় সেগুলি দিয়ে অভিভূত করতে চাইনি
না এই আপনার প্রশ্নের উত্তর দিতে?
আমি কখনই ব্যবহার করিনি reinterpret_cast
, এবং ভাবছি যে এটির প্রয়োজনে এমন কোনও কেস খারাপ ডিজাইনের গন্ধ নয় into আমি যে কোড বেসে কাজ করি তাতে dynamic_cast
প্রচুর ব্যবহৃত হয়। পার্থক্য static_cast
হ'ল একটি dynamic_cast
রানটাইম চেক যা কোনও (নিরাপদ) বা না হতে পারে (আরও ওভারহেড) আপনি যা চান তা হতে পারে ( এমএসডিএন দেখুন )।
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)
?
অন্য উত্তরগুলি সি ++ কাস্টের মধ্যে সমস্ত পার্থক্য সুন্দরভাবে বর্ণনা করার সময়, আমি কেন একটি ছোট নোট যুক্ত করতে চাই যে আপনি কেন সি-স্টাইলের কাস্ট ব্যবহার করবেন না (Type) var
এবং Type(var)
।
সি ++ নতুনদের জন্য সি স্টাইলের বর্ণগুলি দেখতে দেখতে সি ++ জ্যাকেট (স্ট্যাটিক_কাস্ট <> ()), ডায়নামিক_কাস্ট <> (), কনস্ট_কাস্ট <> (), পুনরায় ব্যাখ্যা_কাস্ট <> ()) এবং কেউ তাদের সি ++ বর্ণের চেয়ে পছন্দ করতে পারে । আসলে সি স্টাইলের 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.
ভাষায় সি ++ ক্যাস্ট যুক্ত হওয়ার মূল কারণটি হ'ল কোনও বিকাশকারীকে তার উদ্দেশ্যগুলি স্পষ্ট করার অনুমতি দেওয়া - তিনি কেন এই কাস্ট করতে চলেছেন। সি স্টাইলের ক্যাসেটগুলি ব্যবহার করে যা সি ++ এ পুরোপুরি বৈধ, আপনি আপনার কোডটি কম পঠনযোগ্য এবং বিশেষত অন্য বিকাশকারীদের যারা আপনার কোড তৈরি করেন নি তাদের পক্ষে আরও ত্রুটি প্রবণ করে তুলছেন। সুতরাং আপনার কোডটি আরও পঠনযোগ্য এবং স্পষ্ট করে তোলার জন্য আপনাকে সর্বদা সি স্টাইলের বর্ণের চেয়ে সি ++ ক্যাসেট পছন্দ করা উচিত।
এখানে বার্জন স্ট্রোস্ট্রপের (সি ++ এর লেখক) বইটি সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ 4 র্থ সংস্করণ - পৃষ্ঠা 302 থেকে একটি সংক্ষিপ্ত উদ্ধৃতি দেওয়া হয়েছে।
নামী রূপান্তর অপারেটরদের তুলনায় এই সি স্টাইলের castালাই বিপজ্জনক, কারণ একটি বড় প্রোগ্রামে স্বরলিপিটি পাওয়া শক্ত এবং প্রোগ্রামার দ্বারা রুপান্তরিত ধরণের রূপান্তরটি স্পষ্ট নয়।
বুঝতে, আসুন কোড স্নিপেট নীচে বিবেচনা করুন:
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) ত্রুটি ছাড়াই সংকলন করে। কেবল পুনরায় সংজ্ঞা_কাস্ট ব্যবহার করা যেতে পারে কোনও পয়েন্টারকে পয়েন্টারকে কোনও পয়েন্টারকে কোনও সম্পর্কযুক্ত অবজেক্টের ধরণের রূপান্তর করতে।
এটি লক্ষ্য করার মতো একটি হ'ল: ডায়নামিক_কাস্ট রান-টাইমে ব্যর্থ হবে, তবে বেশিরভাগ সংকলকগুলিতে এটি সংকলন করতে ব্যর্থ হবে কারণ পয়েন্টারের কাস্টিংটিতে কোনও ভার্চুয়াল ফাংশন নেই, ডায়নামিক_কাস্ট কেবল পলিমারফিক ক্লাস পয়েন্টারগুলির সাথে কাজ করবে ।
সি ++ কাস্ট কখন ব্যবহার করবেন :
static_cast
বনাম dynamic_cast
বনাম reinterpret_cast
অভ্যন্তরীণ একটি ডাউনকাস্ট / আপকাস্টে দেখুন
এই উত্তরে আমি এই তিনটি প্রক্রিয়াটিকে একটি কংক্রিট আপকাস্ট / ডাউনকাস্ট উদাহরণের সাথে তুলনা করতে চাই এবং বিশ্লেষণ করতে চাই যে অন্তর্নিহিত পয়েন্টার / মেমরি / অ্যাসেমব্লিকে কীভাবে তারা তুলনা করে তার একটি দৃ understanding় ধারণা দেয়।
আমি বিশ্বাস করি যে এই ক্যাসেটগুলি কীভাবে আলাদা সে সম্পর্কে এটি একটি ভাল অনুভূতি দেবে:
static_cast
: রানটাইম এ অফসেট করে একটি ঠিকানা করে (কম রানটাইম প্রভাব) এবং কোনও সুরক্ষা চেক করে না যে ডাউনস্ট্রাকটি সঠিক কিনা।
dyanamic_cast
: রানটাইমের সময় একই ঠিকানাটি অফসেট করে static_cast
তবে এটি এবং একটি ব্যয়বহুল সুরক্ষা পরীক্ষা করে দেখুন যে আরটিটিআই ব্যবহার করে একটি ডাউনকাস্ট সঠিক কিনা correct
এই সুরক্ষা চেক আপনাকে কোনও বেইস ক্লাস পয়েন্টার রানটাইমে প্রদত্ত ধরণের কিনা তা জিজ্ঞাসা করার অনুমতি দেয় 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.wikedia.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
পরিবর্তে এই একটির পরিবর্তে এর জন্য vtable রয়েছে B2
এবং তাই সমস্ত ভার্চুয়াল কল স্বচ্ছতার সাথে কাজ করে।
এখন, আমরা শেষ পর্যন্ত টাইপ করা কাস্টিং এবং আমাদের দৃ concrete় উদাহরণের বিশ্লেষণে ফিরে যেতে পারি।
স্টাডআউট আউটপুট থেকে আমরা দেখতে পাই:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
অতএব, static_cast
সেখানে সম্পন্ন D
ইমপ্লিট 0x7fffffffc930 এ পূর্ণ ডেটা স্ট্রাকচার থেকে 0x7fffffffc940 এর B2
মতো একটিতে অফসেটটি সঠিকভাবে গণনা করেছে did আমরা এটিও নির্ধারণ করি যে 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)
সুতরাং আমরা দেখতে পাই যে জিসিসি তা করে:
D
যা বিদ্যমান নেইdynamic_cast<D*>(b2s[0]) 0
: সি ++ আসলে পাওয়া গেছে যে কাস্টটি অবৈধ এবং ফিরে এসেছে 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)
প্রথমে একটি নুল চেক আছে, এবং এটি যদি এনইউএল হয় তবে এটি নুল ফেরত দেয়।
অন্যথায়, এটি আরডিএক্স, আরএসআই এবং আরডিআই এবং কলগুলিতে কিছু যুক্তি সেট আপ করে __dynamic_cast
।
এটিকে আরও বিশ্লেষণ করার মতো ধৈর্য আমার এখন নেই, তবে অন্যরা যেমন বলেছে, এটির কাজ করার একমাত্র উপায় হ'ল __dynamic_cast
কিছু অতিরিক্ত আরটিটিআই-তে মেমরির ডেটা স্ট্রাকচারগুলি অ্যাক্সেস করা যা শ্রেণি শ্রেণিবিন্যাসের প্রতিনিধিত্ব করে।
সুতরাং এটি অবশ্যই সেই B2
টেবিলটির প্রবেশ থেকে শুরু হওয়া উচিত , তারপরে এই শ্রেণীর শ্রেণিবিন্যাসটি হাঁটা পর্যন্ত এটি কোনও D
টাইপকাস্টের ভিটিবেল না পাওয়া পর্যন্ত b2s[0]
।
এই কারণেই পুনরায় ব্যাখ্যা কাস্ট সম্ভাব্য ব্যয়বহুল! এখানে একটি উদাহরণ যেখানে এক মাছ ধরার নৌকা প্যাচ একটি রূপান্তর dynamic_cast
একটি থেকে static_cast
একটি জটিল প্রকল্পে 33% দ্বারা রানটাইম লঘু! ।
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
এটি কেবল আমাদের অন্ধভাবে বিশ্বাস করে: আমরা বলেছিলাম একটি D
ঠিকানা আছে b2s[1]
, এবং সংকলকটি অফসেট গণনা করে না।
তবে এটি ভুল, কারণ ডি আসলে 0x7fffffffc930 এ, 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)
সম্পর্কিত প্রশ্নাবলী:
উবুন্টু 18.04 amd64, জিসিসি 7.4.0 এ পরীক্ষিত।