हम एक std :: वेक्टर <AbstractClass> की घोषणा क्यों नहीं कर सकते?


88

C # में विकसित होने में कुछ समय बिताने के बाद, मैंने देखा कि यदि आप एक इंटरफ़ेस के रूप में इसका उपयोग करने के उद्देश्य से एक अमूर्त वर्ग की घोषणा करते हैं, तो आप इस अमूर्त वर्ग के एक सदिश को बच्चों की कक्षाओं के उदाहरणों को संग्रहीत करने के लिए तुरंत नहीं कर सकते।

#pragma once
#include <iostream>
#include <vector>

using namespace std;

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

class FunnyContainer
{
private:
    std::vector <IFunnyInterface> funnyItems;
};

सार वर्ग के वेक्टर की घोषणा करने वाली लाइन MS VS2005 में इस त्रुटि का कारण बनती है:

error C2259: 'IFunnyInterface' : cannot instantiate abstract class

मुझे एक स्पष्ट हल दिखाई देता है, जिसे निम्नलिखित के साथ IFunnyInterface को बदलना है:

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};

क्या यह स्वीकार्य वर्कअराउंड C ++ वार है? यदि नहीं, तो क्या कोई तृतीय पक्ष पुस्तकालय जैसा बढ़ावा है जो मुझे इसके आसपास जाने में मदद कर सकता है?

इसे पढ़ने के लिए आपका धन्यवाद !

एंथोनी

जवाबों:


127

आप अमूर्त कक्षाओं को तुरंत नहीं कर सकते हैं, इस प्रकार सार कक्षाओं का एक वेक्टर काम नहीं कर सकता है।

हालांकि आप अमूर्त कक्षाओं के लिए संकेत के एक वेक्टर का उपयोग कर सकते हैं:

std::vector<IFunnyInterface*> ifVec;

यह आपको वास्तव में बहुरूपी व्यवहार का उपयोग करने की भी अनुमति देता है - भले ही वर्ग अमूर्त नहीं था, मूल्य द्वारा भंडारण वस्तु स्लाइसिंग की समस्या को जन्म देगा ।


5
या आप std :: वेक्टर <std :: tr1 :: shared_ptr <IFunnyInterface>> का उपयोग कर सकते हैं, यदि आप मैन्युअल रूप से ऑब्जेक्ट जीवनकाल से निपटना नहीं चाहते हैं।
सर्गेई टेप्लाकोव

4
या इससे भी बेहतर, बढ़ावा :: ptr_vector <>।
Roel

7
या अब, std :: वेक्टर <std :: unique_ptr <IFunnyInterface >>।
कज़ ड्रैगन

21

आप अमूर्त वर्ग के प्रकार का वेक्टर नहीं बना सकते क्योंकि आप एक सार वर्ग के उदाहरण नहीं बना सकते हैं, और C ++ मानक लाइब्रेरी कंटेनर जैसे std :: वेक्टर स्टोर मान (उदाहरण)। यदि आप ऐसा करना चाहते हैं, तो आपको अमूर्त वर्ग प्रकार के लिए सदिश बिंदु बनाने होंगे।

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

आपको महसूस करना चाहिए कि C ++ और C # में बहुत कम समानता है। यदि आप C ++ सीखने के इच्छुक हैं, तो आपको इसे स्क्रैच से शुरू करने के बारे में सोचना चाहिए, और कोएनिग और मू द्वारा एक अच्छा समर्पित C ++ ट्यूटोरियल जैसे Accelerated C ++ को पढ़ना चाहिए ।


पोस्ट का जवाब देने के अलावा एक किताब की सिफारिश करने के लिए धन्यवाद!
ब्लूट्रिन

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

6

इस मामले में हम इस कोड का भी उपयोग नहीं कर सकते हैं:

std::vector <IFunnyInterface*> funnyItems;

या

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;

क्योंकि FunnyImpl और IFunnyInterface के बीच कोई IS संबंध नहीं है और निजी विरासत के कारण FunnyImpl और IFunnyInterface के बीच कोई अंतर्निहित रूपांतरण नहीं है।

आपको अपना कोड इस प्रकार अपडेट करना चाहिए:

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: public IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

1
ज्यादातर लोगों को लगता है कि मुझे निजी विरासत मिली है :) लेकिन चलो ओपी को और अधिक भ्रमित नहीं करते :)
Roel

1
हां। विशेष रूप से विषय स्टार्टर के वाक्यांश के बाद: "सी # में विकसित होने में कुछ समय बिताया है" (जहां कोई निजी विरासत नहीं है)।
सर्गेई टेप्लाकोव

6

पारंपरिक विकल्प vectorबिंदुओं का उपयोग करना है, जैसे पहले से ही उल्लेख किया गया है।

जो लोग सराहना करते हैं, उनके लिए Boostएक बहुत ही दिलचस्प पुस्तकालय आता है: Pointer Containersजो कार्य के लिए पूरी तरह से अनुकूल है और आपको संकेत से जुड़ी विभिन्न समस्याओं से मुक्त करता है:

  • आजीवन प्रबंधन
  • पुनरावृत्तियों की डबल dereferencing

ध्यान दें कि यह vectorप्रदर्शन और इंटरफ़ेस दोनों के मामले में स्मार्ट पॉइंटर्स की तुलना में काफी बेहतर है ।

अब, एक तीसरा विकल्प है, जो आपके पदानुक्रम को बदलना है। उपयोगकर्ता के बेहतर इन्सुलेशन के लिए, मैंने निम्न पैटर्न का उपयोग कई बार देखा है:

class IClass;

class MyClass
{
public:
  typedef enum { Var1, Var2 } Type;

  explicit MyClass(Type type);

  int foo();
  int bar();

private:
  IClass* m_impl;
};

struct IClass
{
  virtual ~IClass();

  virtual int foo();
  virtual int bar();
};

class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };

यह काफी सीधा है, और Pimplएक Strategyपैटर्न द्वारा समृद्ध मुहावरे की भिन्नता है ।

यह काम करता है, निश्चित रूप से, केवल उस मामले में जहां आप "सही" वस्तुओं को सीधे हेरफेर करने की इच्छा नहीं रखते हैं, और गहरी-प्रतिलिपि शामिल करते हैं। तो यह वह नहीं हो सकता है जो आप चाहते हैं।


1
बूस्ट संदर्भ और डिज़ाइन पैटर्न के लिए धन्यवाद
ब्लूट्रिन

2

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

आप सुझाए गए अन्य के रूप में एक पॉइंटर का उपयोग कर सकते हैं।


1

std :: वेक्टर अपने प्रकार को सम्‍मिलित करने के लिए मेमोरी आवंटित करने का प्रयास करेगा। यदि आपकी कक्षा विशुद्ध रूप से आभासी है, तो वेक्टर को उस वर्ग के आकार का पता नहीं चल सकता है जिसे उसे आवंटित करना होगा।

मुझे लगता है कि अपने वर्कअराउंड के साथ, आप एक संकलन करने में सक्षम होंगे vector<IFunnyInterface>लेकिन आप इसके अंदर FunnyImpl में हेरफेर नहीं कर पाएंगे। उदाहरण के लिए यदि IFunnyInterface (सार वर्ग) आकार 20 का है (मैं वास्तव में नहीं जानता) और FunnyImpl 30 आकार का है क्योंकि इसमें अधिक सदस्य और कोड हैं, तो आप 30 को 20 के अपने वेक्टर में फिट करने की कोशिश करेंगे।

समाधान "नए" के साथ हीप पर मेमोरी आवंटित करने और पॉइंटर्स को स्टोर करने का होगा vector<IFunnyInterface*>


मैंने सोचा था कि यह उत्तर था, लेकिन gf उत्तर और ऑब्जेक्ट स्लाइसिंग के लिए देखें, यह वास्तव में समझाता है कि कंटेनर के भीतर क्या होगा
ब्लूट्रिन

इस उत्तर में बताया गया है कि क्या होगा लेकिन 'स्लाइसिंग' शब्द का उपयोग किए बिना, इसलिए यह उत्तर सही है। पीटीआरएस के वेक्टर का उपयोग करते समय, कोई स्लाइसिंग नहीं होगी। यह पहली जगह में ptrs का उपयोग करने का पूरा बिंदु है।
Roel

-2

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


2
यह मूल कारण नहीं है, और यह "दुख सीमा" नहीं है।

कृपया बताएं कि आपको क्यों लगता है कि यह कोई सीमा नहीं है? क्षमता रखना अच्छा होगा। और प्रोग्रामर पर कुछ ओवरहेड होता है जब वह कंटेनर में पॉइंटर्स लगाने के लिए मजबूर होता है, और एक विलोपन के बारे में चिंता करता है। मैं इस बात से सहमत हूं कि एक ही कंटेनर में विभिन्न आकारों की वस्तुएँ होने से प्रदर्शन ख़राब हो जाएगा।
डेविड ग्रुज़मैन

वर्चुअल फ़ंक्शन आपके पास मौजूद ऑब्जेक्ट के प्रकार के आधार पर प्रेषण करते हैं। कंस्ट्रक्टरों का पूरा मुद्दा यह है कि अभी तक कोई वस्तु नहीं है । इस कारण से संबंधित है कि आपके पास स्थैतिक आभासी कार्य क्यों नहीं हो सकते हैं: कोई वस्तु भी नहीं।
MSalters

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