C ++ में किसी ऑब्जेक्ट का प्रकार ढूँढना


147

मेरे पास एक वर्ग ए और एक अन्य वर्ग है जो इसे विरासत में मिला है, बी। मैं एक फ़ंक्शन को ओवरराइड कर रहा हूं जो एक प्रकार की एक वस्तु को एक पैरामीटर के रूप में स्वीकार करता है, इसलिए मुझे एक ए को स्वीकार करना होगा। हालांकि, मुझे बाद में केवल बी के पास फ़ंक्शन कॉल करना पड़ता है, इसलिए मैं गलत लौटना चाहता हूं और आगे नहीं बढ़ना है यदि पारित वस्तु टाइप बी की नहीं है।

यह पता लगाने का सबसे अच्छा तरीका क्या है कि मेरे कार्य के लिए पारित वस्तु किस प्रकार की है?

जवाबों:


162

डायनामिक_कास्ट को ट्रिक करना चाहिए

TYPE& dynamic_cast<TYPE&> (object);
TYPE* dynamic_cast<TYPE*> (object);

dynamic_castकीवर्ड एक से दूसरे सूचक या संदर्भ प्रकार से एक गृहीत डाले, एक क्रम की जांच प्रदर्शन कर डाली की वैधता सुनिश्चित करने के लिए।

यदि आप सूचक को उस प्रकार के लिए कास्ट करने का प्रयास करते हैं जो एक प्रकार की वास्तविक वस्तु नहीं है, तो कलाकारों का परिणाम NULL होगा। यदि आप एक प्रकार के संदर्भ के लिए कास्ट करने का प्रयास करते हैं जो वास्तविक वस्तु का प्रकार नहीं है, तो कलाकार एक bad_castअपवाद फेंक देगा ।

सुनिश्चित करें कि डायनामिक_कास्ट काम करने के लिए बेस क्लास में कम से कम एक वर्चुअल फ़ंक्शन है।

विकिपीडिया विषय रन-टाइम प्रकार की जानकारी

RTTI केवल उन वर्गों के लिए उपलब्ध है जो बहुरूपी हैं, जिसका अर्थ है कि उनके पास कम से कम एक आभासी तरीका है। व्यवहार में, यह एक सीमा नहीं है क्योंकि आधार कक्षाओं में एक वर्चुअल डिस्ट्रक्टर होना चाहिए ताकि व्युत्पन्न वर्ग की वस्तुओं को एक बेस पॉइंटर से हटाए जाने पर उचित सफाई का प्रदर्शन करने की अनुमति मिल सके।


1
डायनेमिक_कास्ट काम करने के लिए बेस क्लास में वर्चुअल फंक्शन होने का क्या मतलब है? यह मुझे महत्वपूर्ण लगता है, कि मैं सिर्फ अनुमान लगाऊंगा।
GiCo

3
ओके पाया गया: रन-टाइम टाइप इंफॉर्मेशन (RTTI) केवल उन वर्गों के लिए उपलब्ध है जो पॉलीमॉर्फिक हैं, जिसका अर्थ है कि उनके पास कम से कम एक वर्चुअल तरीका है। डायनामिक_कास्ट और टाइपिड को RTTI की आवश्यकता होती है।
जिओ

नहीं dynamic_castफेंकता है अगर इसकी परिवर्तनीय नहीं है? क्या थ्रो पैदा किए बिना इसे करने का कोई तरीका है?
jww

A* aptr = dynamic_cast<A*>(ptr);// ऐसा नहीं माना जाता है
मेहदी करमोसली

क्या यह उन POD के लिए काम करता है जिन्हें एक में डाला गया है uint8_t*? यही है, क्या मैं यह जांच सकता हूं कि uint32_t* x = dynamic_cast<uint32_t*>(p)कहां pहै uint8_t*? (मैं धूर्त उल्लंघनों के लिए एक परीक्षण खोजने की कोशिश कर रहा हूं)।
jww

157

डायनामिक कास्ट समस्या के आपके वर्णन के लिए सबसे अच्छा है, लेकिन मैं सिर्फ यह जोड़ना चाहता हूं कि आप निम्न प्रकार के साथ पा सकते हैं:

#include <typeinfo>

...
string s = typeid(YourClass).name()

4
अच्छा है यदि आप वास्तव में नहीं जानते कि आपकी वस्तु क्या है। स्वीकृत उत्तर आपको लगता है।
निकालें

4
@xus हाँ। यह std हेडर का हिस्सा है
Amey Jah

8
मैं नहीं देखता कि कैसे। आईडी नाम उपयोगी होने के लिए आवश्यक नहीं हैं और कार्यान्वयन को परिभाषित किया गया है।
जूता

3
यहां सबसे दिलचस्प: एक ही वर्ग के उदाहरणों के नाम बराबर नहीं हैं। हालाँकि टाइपिड को खुद को एक ही वर्ग के उदाहरणों के बराबर तुलना करना पड़ता है, देखें stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo

1
ध्यान दें कि जीसीएस नाम का जादुई रिटर्न देता है 11MyClass। अनमंगल करने के लिए आप ABI एक्सटेंशन लाइब्रेरी का उपयोग कर सकते हैं cxxabi.h। यह आपको देता है abi::__cxa_demangleजो आपको असली नाम देगा
डेविड जी

27

इसे आरटीटीआई कहा जाता है , लेकिन आप लगभग निश्चित रूप से अपने डिजाइन पर पुनर्विचार करना चाहते हैं, क्योंकि प्रकार खोजने और इसके आधार पर विशेष चीजें करने से आपका कोड अधिक भंगुर हो जाता है।


4
सच। दुर्भाग्य से मैं एक मौजूदा परियोजना पर काम कर रहा हूँ, इसलिए मैं वास्तव में डिज़ाइन बदलने के लिए नहीं जा सकता, या कक्षा ए में कुछ भी
lemnisca

11

बस पूरा होने के लिए, मैं Robocide का निर्माण करूँगा और यह इंगित करूँगा कि typeidबिना नाम का उपयोग किए (अकेले) उपयोग किया जा सकता है:

#include <typeinfo>
#include <iostream>

using namespace std;

class A {
public:
    virtual ~A() = default; // We're not polymorphic unless we
                            // have a virtual function.
};
class B : public A { } ;
class C : public A { } ;

int
main(int argc, char* argv[])
{
    B b;
    A& a = b;

    cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
    cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
    cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
    cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
    cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
}

आउटपुट:

a is B: true
a is C: false
b is B: true
b is A: false
b is C: false

9

संभवतः आपकी वस्तुओं को एक आईडी "टैग" में एम्बेड करें और इसका उपयोग कक्षा ए की वस्तुओं और कक्षा बी की वस्तुओं के बीच अंतर करने के लिए करें।

यह हालांकि डिजाइन में एक दोष दिखाता है। आदर्श रूप से B में वे विधियाँ जो A के पास नहीं है, A का भाग होना चाहिए लेकिन खाली छोड़ दिया जाना चाहिए, और B उन्हें अधिलेखित कर देता है। यह वर्ग-विशिष्ट कोड के साथ दूर है और OOP की भावना में अधिक है।



4

क्योंकि आपकी कक्षा बहुरूपी नहीं है। प्रयत्न:

struct BaseClas { int base; virtual ~BaseClas(){} };
class Derived1 : public BaseClas { int derived1; };

अब BaseClasबहुरूपी है। मैंने कक्षा को संरचना में बदल दिया क्योंकि एक संरचना के सदस्य डिफ़ॉल्ट रूप से सार्वजनिक होते हैं।


3

आपका विवरण थोड़ा भ्रमित करने वाला है।

आम तौर पर बोलना, हालांकि कुछ सी ++ कार्यान्वयन में इसके लिए तंत्र हैं, आप प्रकार के बारे में पूछने वाले नहीं हैं। इसके बजाय, आप ए पर पॉइंटर पर एक डायनेमिक_कास्ट करने वाले हैं। यह क्या करेगा कि रनटाइम पर, पॉइंटर से ए की वास्तविक सामग्री की जाँच की जाएगी। यदि आपके पास एक बी है, तो आप अपने पॉइंटर को बी को प्राप्त करेंगे। अन्यथा, आपको एक अपवाद या अशक्त मिलेगा।


1
यह ध्यान दिया जाना चाहिए कि आप केवल एक अपवाद प्राप्त करेंगे यदि आप एक संदर्भ कास्ट करते हैं जो विफल रहता है। यानी डायनेमिक_कास्ट <टी एंड> (टी)। असफल सूचक कलाकार NULL को वापस करते हैं। यानी डायनेमिक_कास्ट <टी *> (टी)
अल्फ़ाज़ुलु

हाँ, मुझे यह स्पष्ट करना चाहिए था कि बेहतर है। धन्यवाद। काश, सी-टाइप में एक ऐसा शब्द होता जो उप-मूल्य के बजाय उप-संदर्भ होता है।
उड़ी

3

जैसा कि दूसरों ने संकेत दिया है कि आप डायनेमिक_का उपयोग कर सकते हैं। लेकिन आमतौर पर आप जिस प्रकार की व्युत्पन्न कक्षा पर काम कर रहे हैं उसका पता लगाने के लिए डायनेमिक_का उपयोग करना खराब डिज़ाइन को इंगित करता है। यदि आप एक फ़ंक्शन को ओवरराइड कर रहे हैं जो पैरामीटर के रूप में ए का पॉइंटर लेता है तो यह कक्षा ए के तरीकों / डेटा के साथ काम करने में सक्षम होना चाहिए और क्लास बी के डेटा पर निर्भर नहीं होना चाहिए, यदि आप ओवरराइडिंग के बजाय अपने मामले में। सुनिश्चित करें कि आप जो विधि लिख रहे हैं वह केवल कक्षा बी के साथ काम करेगी, फिर आपको कक्षा बी में एक नई विधि लिखनी चाहिए।


1

ओवरलोड कार्यों का उपयोग करें। डायनामिक_कास्ट या यहां तक ​​कि RTTI समर्थन की आवश्यकता नहीं है:

class A {};
class B : public A {};

class Foo {
public:
    void Bar(A& a) {
        // do something
    }
    void Bar(B& b) {
        Bar(static_cast<A&>(b));
        // do B specific stuff
    }
};

मूल प्रश्न का अधिकार: "मैं बाद में केवल बी के पास कार्य करता हूं" - ऐसे मामले में ओवरलोडिंग कैसे काम करेगी?
मार्सिन गिल

जब आप बार को ए के साथ कहते हैं, तो कोई बी सामान नहीं होता है। जब आप बार को B के साथ बुलाते हैं, तो B पर मौजूद विधियों को बुलाया जा सकता है। क्या आप मूल प्रश्न पढ़ते हैं? बार उसका "मैं एक फ़ंक्शन को ओवरराइड कर रहा हूं जो एक पैरामीटर के रूप में ए के ऑब्जेक्ट को स्वीकार करता है"
jmucchiello

7
यह गतिशील बहुरूपता के साथ काम नहीं करता है, जो मुझे संदेह है कि प्रश्नकर्ता उपयोग कर रहा है। C ++ पैरामीटर के रनटाइम वर्ग के आधार पर एक अधिभार का चयन नहीं कर सकता है, केवल संकलन-समय प्रकार के आधार पर।
स्टीव जेसोप

1

यदि आप बूस्ट लाइब्रेरी तक पहुंच सकते हैं, तो हो सकता है type_id_with_cvr () फ़ंक्शन वह हो जो आपको चाहिए, जो कॉन्स्टेबल , वाष्पशील और && संशोधक को हटाए बिना डेटा प्रकार प्रदान कर सकता है । यहाँ C ++ 11 में एक सरल उदाहरण दिया गया है:

#include <iostream>
#include <boost/type_index.hpp>

int a;
int& ff() 
{
    return a;
}

int main() {
    ff() = 10;
    using boost::typeindex::type_id_with_cvr;
    std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
    std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
    std::cout << typeid(ff()).name() << std::endl;
}

आशा है कि यह उपयोगी है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.