यदि किसी वस्तु का प्रकार C ++ में एक विशेष उपवर्ग है तो मैं कैसे जांचूं?


82

मैं का उपयोग करने की तर्ज पर सोच रहा था, typeid()लेकिन मुझे नहीं पता कि कैसे पूछें कि क्या प्रकार एक अन्य वर्ग का उपवर्ग है (जो, वैसे, सार है)


मुझे आश्चर्य है कि अगर किसी वस्तु का प्रकार C ++ में संकलन-समय पर एक विशेष उपवर्ग है , तो यह जांचने का एक तरीका है क्योंकि std::is_base_ofवांछित के रूप में काम नहीं करेगा। : 3
कैसरकत्जे

जवाबों:


38

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

मैं मान रहा हूं कि आपके पास इस तरह की स्थिति है:

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

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

यदि यह आपके पास है, तो इस तरह से कुछ करने की कोशिश करें:

class Base
{
  virtual void bar() = 0;
};

class A : public Base
{
  void bar() {/* do X */}
};

class B : public Base
{
  void bar() {/* do Y */}
};

void foo(Base *p)
{
  p->bar();
}

संपादित करें: चूंकि इस उत्तर के बारे में बहस इतने सालों के बाद भी जारी है, मैंने सोचा कि मुझे कुछ संदर्भों में फेंक देना चाहिए। यदि आपके पास एक बेस क्लास का पॉइंटर या संदर्भ है, और आपके कोड को ऑब्जेक्ट की व्युत्पन्न क्लास को जानना है, तो यह लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन करता है । अंकल बॉब इसे " ऑब्जेक्ट ओरिएंटेड डिज़ाइन के लिए एक" एंथेमा कहते हैं।


20
+1। मुझे लगता है कि इसके लिए सही नाम "बताओ, मत पूछो" है। मूल रूप से, हमेशा बहुरूपता का पक्ष लेते हैं (किसी वस्तु को क्या करना है, किसी मामले पर कार्यान्वयन को ध्यान में रखते हुए) यदि आप किस प्रकार की वस्तु के साथ काम कर रहे हैं, यह पता लगाने के लिए ASK।
तेंदुएस्किनपिलबॉक्सहैट

63
हाँ - यह सब अच्छा है - लेकिन आदमी प्रकार को हल करने के बारे में जानना चाहता था
JohnIdol

7
@ डिमा, और अगर कोई व्यक्ति केवल सीखने के उद्देश्यों के लिए वाक्यविन्यास जानना चाहता है (मान लें कि वे जावा में लिखी गई पुस्तक के माध्यम से जा रहे हैं, तो डिजाइन की खामियों के बारे में, और उन्हें सी ++ में अनुवाद करने की आवश्यकता है)?
पैचवर्क

8
@ डिमा एवर ने बाहरी पुस्तकालय के साथ काम किया जो सुपरक्लास को परिभाषित करता है? कृपया अपना उत्तर लागू करने का प्रयास करें।
टॉम ज़ातो -

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

125

 

class Base
{
  public: virtual ~Base() {}
};

class D1: public Base {};

class D2: public Base {};

int main(int argc,char* argv[]);
{
  D1   d1;
  D2   d2;

  Base*  x = (argc > 2)?&d1:&d2;

  if (dynamic_cast<D2*>(x) == nullptr)
  {
    std::cout << "NOT A D2" << std::endl;
  }
  if (dynamic_cast<D1*>(x) == nullptr)
  {
    std::cout << "NOT A D1" << std::endl;
  }
}

1
क्या आपको वास्तव में dynamic_cast<>यहाँ की आवश्यकता है? static_cast<>पर्याप्त नहीं होगा ?
krlmlr

15
@krlmlr आप xसंकलन के समय का प्रकार बता सकते हैं ? अगर ऐसा है तो static_cast<>()काम करेंगे। यदि आप xरनटाइम तक का प्रकार नहीं बता सकते हैं तो आपको जरूरत हैdynamic_cast<>()
मार्टिन यॉर्क

धन्यवाद। मैं ज्यादातर CRTP में डाउनकास्ट का उपयोग कर रहा हूं, मैं अन्य उपयोग के मामलों के बारे में भूलता रहता हूं ;-)
krlmlr

अच्छा जवाब, लेकिन यहाँ ध्यान देने योग्य बात है। टर्नेरी सशर्त ऑपरेटर को अपने दूसरे और तीसरे ऑपरेटर के लिए एक ही प्रकार की आवश्यकता होती है। इसलिए आइडीके इस तरह से किसी के लिए भी काम कर सकता है, इसके बजाय अगर / का उपयोग करें। शायद यह अतीत में काम किया? किसी भी तरह।
निकोस

@ निकोस, यह काम करता है क्योंकि: 1. सी ++ को टेनेरी के मामलों को एक ही प्रकार की आवश्यकता नहीं होती है, 2. वे व्युत्पन्न वर्ग पॉइंटर का प्रकार हैं, और व्युत्पन्न वर्ग पॉइंटर इम्प्लांटिटी कास्ट को आधार बनाते हैं।
13:14

30

आप इसे कर सकते हैं dynamic_cast(कम से कम बहुरूपी प्रकार के लिए)।

वास्तव में, दूसरे विचार पर - आप यह नहीं बता सकते हैं कि क्या यह विशिष्ट रूप से एक विशेष प्रकार है dynamic_cast- लेकिन आप यह बता सकते हैं कि क्या यह उस प्रकार का है या उसका कोई उपवर्ग है।

template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
  return dynamic_cast<const DstType*>(src) != nullptr;
}

जब एक उपवर्ग एक बहुरूपिक प्रकार नहीं है?
OJFord

6
@ ऑलीफ़ोर्ड: जब कोई वर्चुअल फ़ंक्शंस न हों।
आकर्षित हॉल

अलग ढंग से कहा, जब std::is_polymorphic_v<T>है false
Xeverous

7

नीचे दिया गया कोड इसे करने के 3 अलग-अलग तरीकों को प्रदर्शित करता है:

  • आभासी समारोह
  • टाइप करें
  • डायनामिक_कास्ट
#include <iostream>
#include <typeinfo>
#include <typeindex>

enum class Type {Base, A, B};

class Base {
public:
    virtual ~Base() = default;
    virtual Type type() const {
        return Type::Base;
    }
};

class A : public Base {
    Type type() const override {
        return Type::A;
    }
};

class B : public Base {
    Type type() const override {
        return Type::B;
    }
};

int main()
{
    const char *typemsg;
    A a;
    B b;
    Base *base = &a;             // = &b;    !!!!!!!!!!!!!!!!!
    Base &bbb = *base;

    // below you can replace    base    with  &bbb    and get the same results

    // USING virtual function
    // ======================
    // classes need to be in your control
    switch(base->type()) {
    case Type::A:
        typemsg = "type A";
        break;
    case Type::B:
        typemsg = "type B";
        break;
    default:
        typemsg = "unknown";
    }
    std::cout << typemsg << std::endl;

    // USING typeid
    // ======================
    // needs RTTI. under gcc, avoid -fno-rtti
    std::type_index ti(typeid(*base));
    if (ti == std::type_index(typeid(A))) {
        typemsg = "type A";
    } else if (ti == std::type_index(typeid(B))) {
        typemsg = "type B";
    } else {
        typemsg = "unknown";
    }
    std::cout << typemsg << std::endl;

    // USING dynamic_cast
    // ======================
    // needs RTTI. under gcc, avoid -fno-rtti
    if (dynamic_cast</*const*/ A*>(base)) {
        typemsg = "type A";
    } else if (dynamic_cast</*const*/ B*>(base)) {
        typemsg = "type B";
    } else {
        typemsg = "unknown";
    }
    std::cout << typemsg << std::endl;
}

इस कार्यक्रम के ऊपर प्रिंट:

type A
type A
type A

6

dynamic_castयह निर्धारित कर सकता है कि क्या प्रकार में वंशानुक्रम पदानुक्रम में कहीं भी लक्ष्य प्रकार शामिल है (हाँ, यह एक अल्पज्ञात विशेषता है कि यदि Bविरासत से Aऔर C, यह A*सीधे में बदल सकता है C*)। typeid()वस्तु का सटीक प्रकार निर्धारित कर सकते हैं। हालांकि, इन दोनों को बेहद संयम से इस्तेमाल किया जाना चाहिए। जैसा कि पहले ही उल्लेख किया गया है, आपको हमेशा गतिशील प्रकार की पहचान से बचना चाहिए, क्योंकि यह एक डिजाइन दोष को इंगित करता है। (यह भी, यदि आप जानते हैं कि ऑब्जेक्ट लक्ष्य प्रकार के बारे में सुनिश्चित करने के लिए है, तो आप इसके साथ डाउनकास्ट कर सकते हैं static_cast। बूस्ट ऑफर करता है polymorphic_downcastकि डीबग मोड के साथ dynamic_castऔर assertडीबग मोड में, और रिलीज मोड में यह सिर्फ एक का उपयोग करेगा static_cast)।


4

मैं असहमत हूं कि आपको कभी भी C ++ में ऑब्जेक्ट के प्रकार की जांच नहीं करनी चाहिए। यदि आप इससे बच सकते हैं, तो मैं सहमत हूं कि आपको चाहिए। यह कहना कि आपको किसी भी परिस्थिति में ऐसा नहीं करना चाहिए, हालांकि बहुत दूर जा रहा है। आप इसे कई भाषाओं में कर सकते हैं, और यह आपके जीवन को बहुत आसान बना सकता है। उदाहरण के लिए, हावर्ड पिंसले ने हमें दिखाया कि कैसे C # पर उनकी पोस्ट में।

मैं क्यूटी फ्रेमवर्क के साथ बहुत काम करता हूं। सामान्य तौर पर, मैं मॉडल करता हूं कि वे चीजों को करने के बाद क्या करते हैं (कम से कम जब उनके ढांचे में काम करते हैं)। QObject क्लास सभी Qt ऑब्जेक्ट्स का बेस क्लास है। उस वर्ग के पास एक त्वरित उपवर्ग चेक के रूप में फ़ंक्शन WWgetgetType () और isWindowType () है। तो क्यों अपने स्वयं के व्युत्पन्न वर्गों की जांच करने में सक्षम नहीं है, जो प्रकृति में तुलनीय है? इन कुछ अन्य पदों में से एक QObject स्पिन ऑफ है:

class MyQObject : public QObject
{
public:
    MyQObject( QObject *parent = 0 ) : QObject( parent ){}
    ~MyQObject(){}

    static bool isThisType( const QObject *qObj )
    { return ( dynamic_cast<const MyQObject*>(qObj) != NULL ); }
};

और फिर जब आप एक QObject के लिए एक पॉइंटर के चारों ओर से गुजर रहे हैं, तो आप जाँच कर सकते हैं कि क्या यह स्थिर वर्ग फ़ंक्शन को कॉल करके आपके व्युत्पन्न वर्ग को इंगित करता है:

if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";

4

मुझे नहीं पता कि क्या मैं आपकी समस्या को सही ढंग से समझता हूं, इसलिए मुझे इसे अपने शब्दों में आराम करने दें ...

समस्या: वर्गों को देखते हुए Bऔर D, यह निर्धारित करें कि Dक्या B(या इसके विपरीत) का उपवर्ग है ?

समाधान: कुछ टेम्पलेट जादू का उपयोग करें! ठीक है, गंभीरता से आपको LOKI पर एक नज़र डालने की ज़रूरत है, जो कि एक उत्कृष्ट टेम्पलेट मेटा-प्रोग्रामिंग लाइब्रेरी है जिसे f C C लेखक आंद्रेई अलेक्जेंड्रेस्कु द्वारा निर्मित किया गया है।

अधिक विशेष रूप से, LOKI डाउनलोड करें और TypeManip.hअपने स्रोत कोड में हेडर शामिल करें फिर SuperSubclassनिम्नानुसार वर्ग टेम्पलेट का उपयोग करें :

if(SuperSubClass<B,D>::value)
{
...
}

प्रलेखन के अनुसार, SuperSubClass<B,D>::valueसच होगा यदि Bसार्वजनिक आधार है D, या यदि Bऔर Dउसी प्रकार के उपनाम हैं।

यानी या तो Dएक उपवर्ग है Bया Dजैसा है B

आशा है कि ये आपकी मदद करेगा।

संपादित करें:

कृपया SuperSubClass<B,D>::valueकुछ विधियों के विपरीत संकलन समय पर होने वाले मूल्यांकन पर ध्यान दें dynamic_cast, इसलिए रनटाइम पर इस प्रणाली का उपयोग करने के लिए कोई जुर्माना नहीं है।


3
#include <stdio.h>
#include <iostream.h>

class Base
{
  public: virtual ~Base() {}

  template<typename T>
  bool isA() {
    return (dynamic_cast<T*>(this) != NULL);
  }
};

class D1: public Base {};
class D2: public Base {};
class D22: public D2 {};

int main(int argc,char* argv[]);
{
  D1*   d1  = new D1();
  D2*   d2  = new D2();
  D22*  d22 = new D22();

  Base*  x = d22;

  if( x->isA<D22>() )
  {
    std::cout << "IS A D22" << std::endl;
  }
  if( x->isA<D2>() )
  {
    std::cout << "IS A D2" << std::endl;
  }
  if( x->isA<D1>() )
  {
    std::cout << "IS A D1" << std::endl;
  }
  if(x->isA<Base>() )
  {
    std::cout << "IS A Base" << std::endl;
  }
}

परिणाम:

IS A D22
IS A D2
IS A Base

2

मैं का उपयोग करने की तर्ज पर सोच रहा था typeid()...

ठीक है, हाँ, यह तुलना करके किया जा सकता है typeid().name():। यदि हम पहले से ही वर्णित स्थिति लेते हैं, जहां:

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

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

एक संभावित कार्यान्वयन foo(Base *p)होगा:

#include <typeinfo>

void foo(Base *p)
{
    if(typeid(*p) == typeid(A))
    {
        // the pointer is pointing to the derived class A
    }  
    else if (typeid(*p).name() == typeid(B).name()) 
    {
        // the pointer is pointing to the derived class B
    }
}

1
आप टाइपिड () नाम () और टाइपिड () का कॉमरेडसन क्यों मिलाते हैं? हमेशा टाइपिड () की तुलना क्यों नहीं की जाती है?
सिलीकोमेन्सर

1

जब तक आप RTTI का उपयोग नहीं करते हैं, आप इसे केवल टेम्पलेट्स का उपयोग करते हुए संकलन समय पर कर सकते हैं।

यह आपको टाइपिड फ़ंक्शन का उपयोग करने देता है जो कि एक type_info संरचना के लिए एक संकेतक प्राप्त करेगा जिसमें प्रकार के बारे में जानकारी होती है।

विकिपीडिया पर इसे पढ़ें


इस संदर्भ में आरटीटीआई का उल्लेख करने के लिए वोट दिया गया, जिसे बाकी सभी ने सिर्फ अनदेखा किया।
मैनुएल श्नाइड 3r

1

सी # में आप बस कह सकते हैं:

if (myObj is Car) {

}

8
मैंने पोस्टर को संपादित करने से पहले उनके सवाल का जवाब दिया और उनकी भाषा पसंद का संकेत दिया।
हावर्ड पिंसले

1
मैं upvoting कर रहा हूं, यह उत्तर की गलती नहीं है कि ओपी ने अपने अनुरोध को निर्दिष्ट किया।
टॉम ज़ातो -

0

आप इसे टेम्प्लेट्स (या SFINAE (सबस्टीट्यूशन फेल्योर इज़ नॉट ए एरर)) के साथ कर सकते हैं। उदाहरण:

#include <iostream>

class base
{
public:
    virtual ~base() = default;
};

template <
    class type,
    class = decltype(
        static_cast<base*>(static_cast<type*>(0))
    )
>
bool check(type)
{
    return true;
}

bool check(...)
{
    return false;
}

class child : public base
{
public:
    virtual ~child() = default;
};

class grandchild : public child {};

int main()
{
    std::cout << std::boolalpha;

    std::cout << "base:       " << check(base())       << '\n';
    std::cout << "child:      " << check(child())      << '\n';
    std::cout << "grandchild: " << check(grandchild()) << '\n';
    std::cout << "int:        " << check(int())        << '\n';

    std::cout << std::flush;
}

आउटपुट:

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