सी ++ स्थिर आभासी सदस्य?


140

क्या C ++ में एक सदस्य फ़ंक्शन होना संभव है जो दोनों है staticऔर virtual? जाहिर है, ऐसा करने का एक सीधा तरीका नहीं है (static virtual member(); एक संकलन त्रुटि है), लेकिन क्या कम से कम एक ही प्रभाव को प्राप्त करने का एक तरीका है?

अर्थात:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

यह GetTypeInformation()एक उदाहरण ( object->GetTypeInformation()) और एक वर्ग पर दोनों का उपयोग करने के लिए समझ में आता है (SomeObject::GetTypeInformation() ) , जो टेम्पलेट्स के लिए तुलना और महत्वपूर्ण के लिए उपयोगी हो सकता है।

केवल उन तरीकों के बारे में सोच सकता हूं जिनमें दो कार्य / एक फ़ंक्शन और एक स्थिर, प्रति वर्ग लिखना या मैक्रोज़ का उपयोग करना शामिल है।

कोई और उपाय?


12
बस एक पक्ष टिप्पणी: स्थिर तरीके किसी भी उदाहरण पर निष्पादित नहीं होते हैं, इसका क्या अर्थ है कि उनके पास इस सूचक को निहित नहीं है। यह कहा जा रहा है, constएक विधि हस्ताक्षर में निहित thisसूचक को स्थिर के रूप में चिह्नित करता है और स्थैतिक तरीकों पर लागू नहीं किया जा सकता है क्योंकि उनके पास निहित पैरामीटर की कमी है।
डेविड रॉड्रिग्ज - ड्रिबेक्स

2
@cvb: मैं गंभीरता से आपके उदाहरण को कोड के साथ बदलने पर पुनर्विचार करूंगा जिसमें प्रतिबिंब शामिल नहीं है। जिस तरह से अब आप दो अलग-अलग (यद्यपि संबंधित) मुद्दों को हल करने की तरह हैं। हां, और मुझे पता है कि यह पूछने पर आपको साढ़े 5 साल हो गए हैं।
ईनपोक्लुम

यहाँ एक विशेषता की आवश्यकता है जो संकलक की जाँच के लिए है कि पदानुक्रम में प्रत्येक वस्तु एक विशिष्ट इंटरफ़ेस (जहाँ एक या अधिक विधि स्थिर है) को लागू करती है। मूल रूप से, स्थिर विधि के लिए एक शुद्ध आभासी जांच जब से तुम स्थिर विधि जोड़ने के लिए भूल जाते हैं, तो संकलक, भावना का एक बहुत बनाता है चाहिए बाहर त्रुटि। वर्चुअल यहाँ कीवर्ड नहीं है, यह अधिक सार है जो इस विशिष्ट मामले को छोड़कर, C ++ में समानार्थी शब्द की तरह होता है। दुर्भाग्य से, आप इसे वर्तमान में C ++ के साथ नहीं कर सकते।
xryl669

जवाबों:


75

नहीं, ऐसा करने का कोई तरीका नहीं है, जब आप बुलाएंगे तो क्या होगा Object::GetTypeInformation()? यह पता नहीं चल सकता है कि किस वर्ग के संस्करण को कॉल करना है क्योंकि इसके साथ कोई ऑब्जेक्ट जुड़ा नहीं है।

आपको इसे ठीक से काम करने के लिए एक गैर-स्थिर वर्चुअल फ़ंक्शन बनाना होगा; यदि आप किसी ऑब्जेक्ट के बिना एक विशिष्ट व्युत्पन्न वर्ग के संस्करण को गैर-वस्तुतः कॉल करने में सक्षम होना चाहते हैं, तो आपको एक दूसरा निरर्थक स्टैटिक नॉन-वर्चुअल संस्करण भी उपलब्ध कराना होगा।


8
यदि आप एक एकल के रूप में स्थिर वर्ग (या कक्षाओं के स्थिर सदस्य) के बारे में सोचते हैं, तो सब कुछ स्पष्ट हो जाता है - आपके मामले में बस वस्तु :: GetTypeInformation को बुलाया जाना चाहिए - उसी तरह जैसे आधार वर्ग के उदाहरण पर नियमित आभासी विधि को कॉल करना । (बेशक, अगर C ++ ने वर्चुअल स्टैटिक तरीकों का समर्थन किया है)
Spook

13
यह पूरी तरह से विश्वसनीय तर्क है। यदि आप किसी ऑब्जेक्ट के बजाय कक्षा का उपयोग करते हैं, तो यह स्वाभाविक रूप से वर्चुअल-प्रेषण करने के बजाय, उस वर्ग के संस्करण का उपयोग करेगा। वहां कुछ भी नया नहीं है।
डेडुप्लिकेटर

54

कई लोग कहते हैं कि यह संभव नहीं है, मैं एक कदम आगे जाऊंगा और कहूंगा कि यह सार्थक नहीं है।

एक स्थिर सदस्य एक ऐसी चीज है जो किसी भी उदाहरण से संबंधित नहीं है, केवल कक्षा के लिए।

एक आभासी सदस्य एक ऐसी चीज है जो किसी भी वर्ग से सीधे संबंधित नहीं है, केवल एक उदाहरण के लिए।

तो एक स्थिर आभासी सदस्य कुछ ऐसा होगा जो किसी भी उदाहरण या किसी भी वर्ग से संबंधित नहीं है।


42
यह उन भाषाओं में पूरी तरह से सार्थक है जहां कक्षाएं प्रथम श्रेणी के मूल्य हैं - उदाहरण के लिए डेल्फी में वह है, और "स्थिर आभासी" विधियां भी हैं।
पावेल मिनाएव

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

7
मुझे यह भी लगता है कि स्थिर आभासी सार्थक हैं। इंटरफ़ेस कक्षाओं को परिभाषित करना और स्थिर तरीकों को शामिल करना संभव होगा जिन्हें व्युत्पन्न वर्ग में लागू किया जाना है।
bkausbk

34
यह एक static virtualविधि के लिए इतना सार्थक नहीं है , लेकिन एक इंटरफ़ेस में एक static शुद्ध virtual विधि बहुत सार्थक है।
ब्रेट कुह्न

4
यह पूरी तरह से सार्थक है एक static const string MyClassSillyAdditionalName
ईनपोकलम

23

मैं दूसरे दिन इस समस्या में भाग गया: मेरे पास कुछ स्टैटिक विधियों से भरी कक्षाएं थीं, लेकिन मैं विरासत और आभासी तरीकों का उपयोग करना चाहता था और कोड पुनरावृत्ति को कम करना चाहता था। मेरा समाधान था:

स्थैतिक तरीकों का उपयोग करने के बजाय, आभासी विधियों के साथ एक सिंगलटन का उपयोग करें।

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

व्यवहार में, एक सिंगलटन का उपयोग करना स्टैटिक विधियों का उपयोग करने जैसा है, सिवाय इसके कि आप विरासत और आभासी तरीकों का लाभ उठा सकते हैं।


यह मेरे प्रदर्शन पर खर्च करने जा रहा है - जब तक कि कंपाइलर निश्चित नहीं हो सकता है कि: 1. यह वास्तव में एक सिंगलटन है और 2. इसमें से कुछ भी नहीं मिलता है, मुझे नहीं लगता कि यह सभी ओवरहेड को दूर कर सकता है।
einpoklum

अगर इस तरह का प्रदर्शन आपको चिंतित करता है तो C # शायद आपके लिए गलत भाषा है।
नैट सीके

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

15

हो सकता!

लेकिन वास्तव में क्या संभव है, चलो नीचे संकीर्ण करें। स्थिर कॉल "SomeDerivedClass :: myfunction ()" और पॉलीमॉर्फ़िक कॉल "bas_class_ointerter-> myfunction ()" के माध्यम से एक ही फ़ंक्शन को कॉल करने में सक्षम होने के लिए आवश्यक कोड के दोहराव के कारण लोग अक्सर किसी प्रकार का "स्थिर आभासी फ़ंक्शन" चाहते हैं। इस तरह की कार्यक्षमता की अनुमति के लिए "कानूनी" विधि फ़ंक्शन परिभाषाओं का दोहराव है:

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

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

इसलिए, हम व्युत्पन्न वर्ग के अनुसार getTypeInformation () की केवल एक परिभाषा प्रदान करना चाहते हैं और यह स्पष्ट है कि इसकी एक परिभाषा होनी चाहिए स्थैतिक कीकार्य करें क्योंकि "SomeDerivedClass :: getTypeInformation ()" को कॉल करना संभव नहीं है अगर getTypeInformation () वर्चुअल है। हम सूचक से आधार वर्ग तक व्युत्पन्न वर्ग के स्थिर कार्य को कैसे कह सकते हैं? यह व्यवहार्य के साथ संभव नहीं है क्योंकि व्यवहार्य भंडार केवल आभासी कार्यों को इंगित करते हैं और चूंकि हमने आभासी कार्यों का उपयोग नहीं करने का फैसला किया है, इसलिए हम अपने लाभ के लिए व्यवहार्यता को संशोधित नहीं कर सकते हैं। फिर, पॉइंटर से बेस क्लास के लिए व्युत्पन्न वर्ग के लिए स्थिर फ़ंक्शन तक पहुंचने में सक्षम होने के लिए हमें किसी प्रकार के ऑब्जेक्ट को उसके बेस क्लास के भीतर स्टोर करना होगा। एक दृष्टिकोण बेस क्लास टेम्प्लेट को "उत्सुकता से आवर्ती टेम्पलेट पैटर्न" का उपयोग करके बनाना है, लेकिन यह यहां उचित नहीं है और हम "टाइप इरेज़र" नामक तकनीक का उपयोग करेंगे:

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

अब हम बेस क्लास "ऑब्जेक्ट" के भीतर एक वस्तु का प्रकार एक चर "कीपर" के साथ स्टोर कर सकते हैं:

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

एक व्युत्पन्न वर्ग कीपर में निर्माण के दौरान आरंभीकृत किया जाना चाहिए:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

चलो जोड़तोड़ चीनी:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

अब वंशजों की घोषणा इस तरह दिखती है:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

उपयोग:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

लाभ:

  1. कोड का कम दोहराव (लेकिन हमें हर कंस्ट्रक्टर में OVERRIDE_STATIC_FUNCTIONS को कॉल करना होगा)

नुकसान:

  1. हर रचनाकार में OVERRIDE_STATIC_FUNCTIONS
  2. स्मृति और प्रदर्शन ओवरहेड
  3. जटिलता बढ़ गई

खुले मामले:

1) स्थैतिक और आभासी कार्यों के लिए अलग-अलग नाम हैं यहां अस्पष्टता कैसे हल करें?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) प्रत्येक निर्माता के अंदर OVERRIDE_STATIC_FUNCTIONS को कैसे कहें?


प्रयास के लिए +1, हालांकि मुझे यकीन नहीं है कि यह आभासी विधियों के साथ एक सिंगलटन के लिए कार्यक्षमता को सौंपने से अधिक सुरुचिपूर्ण है।
einpoklum

1
@einpoklum, मैं ऐसी स्थिति के बारे में सोच सकता हूं जब यह बेहतर हो सकता है। मान लीजिए हमारे पास बहुत सारे क्लाइंट कोड हैं जो पहले से ही स्टैटिक मेथड को कॉल करते हैं। आभासी तरीकों के साथ एक सिंगलटन के लिए स्थिर तरीकों से स्विच करने पर क्लाइंट कोड में बदलाव की आवश्यकता होगी जबकि ऊपर प्रस्तुत समाधान गैर-आक्रामक है।
Alsk

"foo :: getTypeInformation" और "TypeKeeperImpl :: getTypeFformation" के लिए "वर्चुअल" कीवर्ड की आवश्यकता नहीं है।
बारटोलो-ओट्री

12

हालांकि Alsk ने पहले से ही एक विस्तृत जवाब दे दिया है, मैं एक विकल्प जोड़ना चाहूंगा, क्योंकि मुझे लगता है कि उसका बढ़ा हुआ कार्यान्वयन अधूरा है।

हम एक सार आधार वर्ग के साथ शुरू करते हैं, जो सभी प्रकार के ऑब्जेक्ट के लिए इंटरफ़ेस प्रदान करता है:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

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

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

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

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

आइए परीक्षण के लिए कुछ कोड जोड़ें:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

परिशिष्ट (जनवरी 12, 2019):

GetClassNameStatic () फ़ंक्शन का उपयोग करने के बजाय, आप क्लास नाम को एक स्थिर सदस्य, यहां तक ​​कि "इनलाइन" के रूप में भी परिभाषित कर सकते हैं, जो IIRC C ++ 11 के बाद से काम करता है (सभी संशोधकों द्वारा डरा नहीं :))

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

11

हो सकता है। दो कार्य करें: स्थिर और आभासी

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

4
साथ ही, स्थैतिक विधियाँ संकुचित नहीं हो सकती हैं। यह सिर्फ समझ में नहीं आता है, वे किस उदाहरण को म्यूट करने के लिए नहीं जा रहे हैं?
डेविड रॉड्रिग्ज - ड्रिबेक्स

1
यह ज्यादातर सिर्फ कोड दोहराव है। यह विचार उपवर्गों के लिए है कि उन्हें केवल स्थैतिक कांस्टेबल सदस्य की आवश्यकता है, न कि कोड एक्सेस करने की।
ईनपोकलम

8

नहीं, यह संभव नहीं है, क्योंकि स्थैतिक सदस्य कार्यों में एक thisसंकेतक की कमी होती है । और स्थिर सदस्य (कार्य और चर दोनों) वास्तव में प्रति वर्ग सदस्य नहीं हैं। वे बस द्वारा आह्वान किया जा करने के लिए होता हैClassName::member , और क्लास एक्सेस स्पेसर्स का पालन करते हैं। उनके भंडारण को कक्षा के बाहर कहीं परिभाषित किया गया है; हर बार जब आप क्लास की एक वस्तु को स्टोर करते हैं तो स्टोरेज नहीं बनता है। कक्षा के सदस्यों के संकेत शब्दार्थ और वाक्यविन्यास में विशेष हैं। स्थैतिक सदस्य के लिए एक सूचक सभी संबंध में एक सामान्य सूचक है।

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


1
केवल गैर-स्थिर कार्यों के लिए एक this सूचक की आवश्यकता होती है । स्थिर कार्य एक उदाहरण के लिए विशिष्ट नहीं हैं, और इसकी आवश्यकता नहीं होगी। इसलिए - यह एक कारण नहीं है कि आभासी स्थिर सदस्य असंभव हैं।
einpoklum

7

ठीक है, काफी देर से जवाब लेकिन यह उत्सुकता से आवर्ती टेम्पलेट पैटर्न का उपयोग करना संभव है। इस विकिपीडिया लेख में आपके लिए आवश्यक जानकारी है और स्थिर बहुरूपता के तहत उदाहरण भी है जो आपसे पूछा जाता है।


3

मुझे लगता है कि आप जो करने की कोशिश कर रहे हैं वह टेम्प्लेट के माध्यम से किया जा सकता है। मैं यहाँ लाइनों के बीच पढ़ने की कोशिश कर रहा हूँ। आप जो करने की कोशिश कर रहे हैं वह कुछ कोड से एक विधि को कॉल करने के लिए है, जहां यह एक व्युत्पन्न संस्करण को कॉल करता है, लेकिन कॉल करने वाला निर्दिष्ट नहीं करता है कि कौन सा वर्ग है। उदाहरण:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

आप बार को निर्दिष्ट किए बिना M के बार संस्करण को कॉल करने का प्रयास () करना चाहते हैं। जिस तरह से आप स्टेटिक्स के लिए एक टेम्पलेट का उपयोग करते हैं। इसलिए इसे ऐसे बदलें:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}

1
यदि आप अपने कोड 4 स्थानों को इंडेंट करते हैं तो आप इसे स्वचालित रूप से स्वरूपित कर सकते हैं। वैकल्पिक रूप से मेरा मानना ​​है कि आप एक ही उद्देश्य इनलाइन को प्राप्त करने के लिए बैक टिक का उपयोग कर सकते हैं।
चोलिडा

1
यह स्पष्ट है कि मैं चूक गया। धन्यवाद। फिर भी, जघन सदस्य अजीब हैं।
23

M () स्थिर कार्य नहीं है। इसे T :: M कैसे कहा जाता है?
DDukDDak99

3

नहीं, स्टेटिक मेंबर फंक्शन वर्चुअल नहीं हो सकता है। वर्सट्री वर्चुअल कॉन्सेप्ट को vptr की मदद से रन टाइम पर हल किया जाता है, और vptr एक class.due का नॉन स्टैटिक मेंबर होता है। स्टैटिक मेंबर फंक्शन vptr को एक्सेप्ट नहीं कर सकता इसलिए स्टैटिक मेंबर कर सकता है। 'आभासी मत बनो।


2
केवल उदाहरण-विशिष्ट आभासी तरीकों के लिए अस्थिरता की आवश्यकता होती है। आपके पास एक स्थिर - एक-प्रति-वर्ग - व्यवहार्य हो सकता है। और यदि आप उदाहरणों के बारे में जानना चाहते हैं, तो उदाहरण के दृष्टांत से कक्षा सांख्यिकी के लिए भी इंगित करें।
19

2

यह संभव नहीं है, लेकिन यह सिर्फ एक चूक है। यह ऐसा कुछ नहीं है जो "समझदारी नहीं करता है" जैसा कि बहुत सारे लोग दावा करते हैं। स्पष्ट होने के लिए, मैं कुछ इस तरह की बात कर रहा हूं:

struct Base {
  static virtual void sayMyName() {
    cout << "Base\n";
  }
};

struct Derived : public Base {
  static void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
  Derived::sayMyName(); // Also would work.
}

यह 100% कुछ है जिसे लागू किया जा सकता है (यह सिर्फ नहीं है), और मैं कुछ ऐसा तर्क दूंगा जो उपयोगी है।

इस बात पर विचार करें कि सामान्य वर्चुअल फ़ंक्शन कैसे काम करते हैं। एस निकालें staticऔर कुछ अन्य सामानों में जोड़ें और हमारे पास हैं:

struct Base {
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
}

यह ठीक काम करता है और मूल रूप से क्या होता है संकलक दो टेबल बनाता है, जिसे वीटीएबल्स कहा जाता है, और इस तरह के आभासी कार्यों के लिए सूचकांक प्रदान करता है

enum Base_Virtual_Functions {
  sayMyName = 0;
  foo = 1;
};

using VTable = void*[];

const VTable Base_VTable = {
  &Base::sayMyName,
  &Base::foo
};

const VTable Derived_VTable = {
  &Derived::sayMyName,
  &Base::foo
};

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

struct Base {
  VTable* vtable;
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  VTable* vtable;
  void sayMyName() override {
    cout << "Derived\n";
  }
};

जब आप कॉल करते हैं तब वास्तव में क्या होता है b->sayMyName()? मूल रूप से यह:

b->vtable[Base_Virtual_Functions::sayMyName](b);

(पहला पैरामीटर बन जाता है this।)

ठीक है, तो यह स्थिर आभासी कार्यों के साथ कैसे काम करेगा? वैसे स्थैतिक और गैर-स्थैतिक सदस्य कार्यों में क्या अंतर है? अंतर केवल इतना है कि बाद वाले को एक thisसूचक मिलता है ।

हम स्थैतिक आभासी कार्यों के साथ बिल्कुल वैसा ही कर सकते हैं - बस thisपॉइंटर को हटा दें ।

b->vtable[Base_Virtual_Functions::sayMyName]();

यह तब दोनों सिंटैक्स का समर्थन कर सकता है:

b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".

इसलिए सभी नैय्यर्स को नजरअंदाज करें। यह करता है मेकअप भावना। यह तब क्यों समर्थित नहीं है? मुझे लगता है कि यह इसलिए है क्योंकि इसका बहुत कम लाभ है और थोड़ा भ्रमित भी हो सकता है।

एक सामान्य आभासी फ़ंक्शन पर केवल तकनीकी लाभ यह है कि आपको फ़ंक्शन को पास thisकरने की आवश्यकता नहीं है, लेकिन मुझे नहीं लगता है कि प्रदर्शन के लिए कोई औसत दर्जे का अंतर होगा।

इसका मतलब है कि आपके पास मामलों के लिए एक अलग स्थिर और गैर-स्थिर फ़ंक्शन नहीं है जब आपके पास एक उदाहरण है, और जब आपके पास कोई उदाहरण नहीं है, लेकिन यह भी भ्रमित हो सकता है कि यह केवल "वर्चुअल" है जब आप उपयोग करते हैं उदाहरण कॉल।


0

नहीं, यह संभव नहीं है, क्योंकि स्थिर सदस्य संकलन के समय बाध्य होते हैं, जबकि आभासी सदस्य रनटाइम पर बाध्य होते हैं।


0

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

इसलिए, यदि आप स्वयं को ऐसा चाहते हैं, तो आप अपने समाधान को पुनः प्राप्त करना चाह सकते हैं। फिर भी, मैं स्थैतिक तरीकों की इच्छा को समझता हूं और फिर उन्हें गतिशील रूप से एक आवृत्ति के आधार पर कॉल करता हूं। मुझे लगता है कि विज़िटर पैटर्न आपको वही दे सकता है जो आप चाहते हैं। यह आपको वही देता है जो आप चाहते हैं। यह थोड़ा अतिरिक्त कोड है, लेकिन यह अन्य आगंतुकों के लिए उपयोगी हो सकता है।

देखें: http://en.wikipedia.org/wiki/Visitor_pattern पृष्ठभूमि के लिए

struct ObjectVisitor;

struct Object
{
     struct TypeInformation;

     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v);
};

struct SomeObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

struct AnotherObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

फिर प्रत्येक ठोस वस्तु के लिए:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&.
}
void AnotherObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // Here *this is a const AnotherObject& at compile time.
}

और फिर आधार आगंतुक को परिभाषित करें:

struct ObjectVisitor {
    virtual ~ObjectVisitor() {}
    virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like.
    virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like.
    // More virtual void visit() methods for each Object class.
};

तब ठोस आगंतुक जो उपयुक्त स्थिर फ़ंक्शन का चयन करता है:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) {
        result = SomeObject::GetTypeInformation();
    }
    virtual void visit(const AnotherObject& o) {
        result = AnotherObject::GetTypeInformation();
    }
    // Again, an implementation for each concrete Object.
};

अंत में, इसका उपयोग करें:

void printInfo(Object& o) {
    ObjectVisitorGetTypeInfo getTypeInfo;
    Object::TypeInformation info = o.accept(getTypeInfo).result;
    std::cout << info << std::endl;
}

टिप्पणियाँ:

  • लगातार व्यायाम के रूप में छोड़ दिया।
  • आपने स्थैतिक से एक संदर्भ लौटाया। जब तक आपके पास एक सिंगलटन नहीं है, यह संदिग्ध है।

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

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) { doVisit(o); }
    virtual void visit(const AnotherObject& o) { doVisit(o); }
    // Again, an implementation for each concrete Object.

  private:
    template <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};

आभासी स्थिर विधियाँ, वे अस्तित्व में थीं, उदाहरण के लिए किसी भी चीज़ पर निर्भर नहीं होंगी - लेकिन उदाहरण के लिए उन्हें आह्वान करने के लिए इसके प्रकार को जानना होगा। यह एक संकलक द्वारा काम किया जा सकता है (उदाहरण के लिए कुछ प्रति-वर्ग एकल डेटा संरचना का उपयोग करके वर्चुअल स्टैटिक विधियों और सदस्यों के संकेत के साथ।) यह निश्चित रूप से शब्दों में विरोधाभास नहीं है।
einpoklum

शब्दों में विरोधाभास है या नहीं, यह शब्दार्थ का सवाल है। कोई C ++ की कल्पना कर सकता है कि एक उदाहरण (उदाहरण के Foo foo; ... foo::bar();बजाय Foo::bar();) से स्टैटिक्स को अनुमति दे । यह विपरीत नहीं है decltype(foo)::bar();लेकिन फिर से वैधानिक रूप से बाध्य होगा। विज़िटर का दृष्टिकोण इस व्यवहार को प्राप्त करने के लिए एक उचित तरीका लगता है, बिना स्थैतिक विधि को वर्चुअल कास्ट विधि बनाने के बिना।
बेन

0

सी ++ के साथ आप crt विधि के साथ स्थैतिक विरासत का उपयोग कर सकते हैं। उदाहरण के लिए, यह व्यापक रूप से विंडो टेम्पलेट atl & wtl पर उपयोग किया जाता है।

Https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern देखें

सरल होने के लिए, आपके पास एक वर्ग है जो स्वयं से वर्ग मायक्लास की तरह प्रेरित है: सार्वजनिक myancestor। इस बिंदु से myancestor वर्ग अब आपके स्थैतिक T :: YourImpl फ़ंक्शन को कॉल कर सकता है।


-1

शायद आप नीचे मेरे समाधान की कोशिश कर सकते हैं:

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

public:
    virtual void MyVirtualFun(void) = 0;
    static void  MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); }
private:
    static Base* mSelf;
};

Base::mSelf = NULL;

Base::Base(void) {
    mSelf = this;
}

Base::~Base(void) {
    // please never delete mSelf or reset the Value of mSelf in any deconstructors
}

class DerivedClass : public Base {
public:
    DerivedClass(void) : Base() {}
    ~DerivedClass(void){}

public:
    virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}

हाँ, मुझे पता है, 4 साल। उन लोगों के लिए -score की व्याख्या करना जो उस विवरण में कोड को पढ़ना नहीं चाहते हैं। Base::mSelfकिसी भी व्युत्पन्न वर्ग के सबसे हालिया निर्मित उदाहरण को संदर्भित करता है, भले ही वह उदाहरण नष्ट हो गया हो । इसलिए class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */वह नहीं है जो वह चाहता है।
जेसी चिशोल्म

-3

जैसा कि दूसरों ने कहा है, जानकारी के 2 महत्वपूर्ण टुकड़े हैं:

  1. thisस्थिर फ़ंक्शन कॉल करते समय और कोई संकेतक नहीं होता है
  2. thisसंरचना जहां आभासी तालिका, या thunk, को देखने के लिए जो क्रम विधि कॉल करने के लिए उपयोग किया जाता करने के लिए सूचक अंक।

एक स्थिर फ़ंक्शन संकलन समय पर निर्धारित किया जाता है।

मैंने कक्षा में C ++ स्थिर सदस्यों में यह कोड उदाहरण दिखाया ; यह दर्शाता है कि आप एक अशक्त सूचक को दिए गए स्थैतिक विधि को कॉल कर सकते हैं:

struct Foo
{
    static int boo() { return 2; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Foo* pFoo = NULL;
    int b = pFoo->boo(); // b will now have the value 2
    return 0;
}

6
तकनीकी रूप से, यह अपरिभाषित व्यवहार है। आप किसी भी कारण से अशक्त सूचक को हटा नहीं सकते। केवल एक चीज जो आप एक नल पॉइंटर के साथ कर सकते हैं वह है) दूसरे पॉइंटर को इसके लिए असाइन करें और बी) दूसरे पॉइंटर से इसकी तुलना करें।
कीथबी ३०'०

1
इसके अलावा, आप केवल यह तुलना कर सकते हैं समानता के लिए । (या किसी अन्य सूचक, नहीं आदेश IE के साथ inequality_ p < null, p >= nullआदि सभी में अच्छी तरह से अपरिभाषित कर रहे हैं।
पावेल Minaev

1
@KeithB - ​​पूर्णता के लिए आप एक नल सूचक पर डिलीट को सुरक्षित रूप से कॉल कर सकते हैं।
स्टीव रो डे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.