2009-08-01 6 views
31

चूंकि सी ++ में interface जावा और सी # की सुविधा नहीं है, सी ++ कक्षाओं में इंटरफेस अनुकरण करने का पसंदीदा तरीका क्या है? मेरा अनुमान अमूर्त वर्गों की एकाधिक विरासत होगी। मेमोरी ओवरहेड/प्रदर्शन के संदर्भ में क्या प्रभाव हैं? क्या ऐसे नकली इंटरफेस के लिए कोई नामकरण सम्मेलन है, जैसे कि SerializableInterface?मैं सी ++ में इंटरफेस कैसे अनुकरण कर सकता हूं?

+9

इंटरफ़ेस लाता है। यह एक नकारात्मक शब्द की तरह लगता है। सी ++ में इंटरफेस की कमी नहीं है (कक्षा एक इंटरफ़ेस है) इसमें केवल कीवर्ड इंटरफ़ेस की कमी है क्योंकि यह स्टंट नहीं किया गया है। –

+3

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

+2

संभावित डुप्लिकेट [आप सी ++ में इंटरफ़ेस कैसे घोषित करते हैं?] (Http://stackoverflow.com/questions/318064/how-do-you-declare-an-interface-in-c) –

उत्तर

29

चूंकि सी ++ में सी # और जावा के विपरीत कई विरासत हैं, हां आप अमूर्त कक्षाओं की एक श्रृंखला बना सकते हैं।

सम्मेलन के लिए, यह आपके ऊपर है; हालांकि, मैं एक मैं

class IStringNotifier 
{ 
public: 
    virtual void sendMessage(std::string &strMessage) = 0; 
    virtual ~IStringNotifier() { } 
}; 

प्रदर्शन सी # और जावा के बीच तुलना के संदर्भ में चिंता की बात नहीं है के साथ वर्ग के नाम पूर्व में होना अच्छा लगता है। असल में आपके पास वर्चुअल विधियों के साथ किसी भी तरह की विरासत की तरह ही आपके कार्यों या vtable के लिए लुकअप टेबल रखने का ओवरहेड होगा।

+5

और अमूर्त वर्ग बनाएं विनाशक आभासी यदि आप अमूर्त वर्ग के लिए सूचक का उपयोग कर वस्तु को हटाना चाहते हैं। –

+0

@ जिम हुआंग: सहमत स्पष्ट रूप से जोड़ा गया। –

+9

@ माइकल हारून सफान: डाउनवोट के लिए धन्यवाद, शायद बेहतर होगा अगर आपने जवाब देकर प्रश्नों का फैसला किया हो, और कोडिंग शैली पर आपकी प्राथमिकता न हो? हंगेरियन नोटेशन मैन के लिए –

3

सी ++ में इंटरफेस कक्षाएं हैं जिनमें केवल शुद्ध वर्चुअल फ़ंक्शन हैं। जैसे :

class ISerializable 
{ 
public: 
    virtual ~ISerializable() = 0; 
    virtual void serialize(stream& target) = 0; 
}; 

यह एक नकली इंटरफ़ेस यह जावा में लोगों की तरह एक अंतरफलक है, लेकिन कमियां नहीं होता नहीं है,।

उदा। आप नकारात्मक परिणामों के बिना तरीकों और सदस्य जोड़ सकते हैं:

class ISerializable 
{ 
public: 
    virtual ~ISerializable() = 0; 
    virtual void serialize(stream& target) = 0; 
protected: 
    void serialize_atomic(int i, stream& t); 
    bool serialized; 
}; 
नामकरण सम्मेलनों के लिए

... कोई वास्तविक नामकरण सम्मेलनों C++ भाषा में परिभाषित कर रहे हैं। तो अपने पर्यावरण में से एक का चयन करें।

ओवरहेड 1 स्थैतिक तालिका है और व्युत्पन्न कक्षाओं में जिनके पास वर्चुअल फ़ंक्शंस नहीं है, स्थिर तालिका में एक सूचक है।

+1

मुझे नहीं लगता कि आपके पास आभासी रचनाकार हो सकते हैं। हालांकि, आप वर्चुअल विनाशक हो सकते हैं। – jkeys

+0

@ हुक किया गया, टाइपिंग त्रुटि तय की गई। – Christopher

+1

मुझे विनाशक शुद्ध वर्चुअल होने का कोई कारण नहीं दिखता है, केवल सादा वर्चुअल पर्याप्त होगा। डीटीओआर घोषित करना पर्याप्त नहीं है, इसे परिभाषित किया जाना है। शुद्ध आभासी dtor के मामले में इसे कक्षा परिभाषा के बाहर परिभाषित किया जाना चाहिए, जैसे: ISerializable :: ~ ISerializable() {} क्योंकि C++ व्याकरण शुद्ध वर्चुअल विनिर्देशक और कक्षा के सदस्य फ़ंक्शन दोनों की अनुमति नहीं देता है परिभाषा। –

1

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

हालांकि, अगर आप खाली बेस कक्षा अनुकूलन की तरह कुछ करते हैं, आपको लगता है कि कम से कम कर सकते हैं:

 
struct A 
{ 
    void func1() = 0; 
}; 

struct B: A 
{ 
    void func2() = 0; 
}; 

struct C: B 
{ 
    int i; 
}; 

सी के आकार दो शब्द हो जाएगा।

7

"मेमोरी ओवरहेड/प्रदर्शन के संदर्भ में क्या प्रभाव हैं?"

आमतौर पर वर्चुअल कॉल का उपयोग करने वालों को छोड़कर कोई भी नहीं, हालांकि प्रदर्शन के संदर्भ में मानक द्वारा कुछ भी गारंटी नहीं दी जाती है।

मेमोरी ओवरहेड पर, "खाली बेस क्लास" ऑप्टिमाइज़ेशन स्पष्ट रूप से कंपाइलर को लेआउट संरचनाओं को अनुमति देता है जैसे कि बेस क्लास जोड़ना जिसमें कोई डेटा सदस्य आपकी ऑब्जेक्ट्स का आकार नहीं बढ़ाता है। मुझे लगता है कि आपको एक कंपाइलर से निपटने की संभावना नहीं है जो ऐसा नहीं करता है, लेकिन मैं गलत हो सकता हूं।

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

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

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

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

उदाहरण कोड:

#include <iostream> 

// A is an interface 
struct A { 
    virtual ~A() {}; 
    virtual int a(int) = 0; 
}; 

// B is an interface 
struct B { 
    virtual ~B() {}; 
    virtual int b(int) = 0; 
}; 

// C has no interfaces, but does have a virtual member function 
struct C { 
    ~C() {} 
    int c; 
    virtual int getc(int) { return c; } 
}; 

// D has one interface 
struct D : public A { 
    ~D() {} 
    int d; 
    int a(int) { return d; } 
}; 

// E has two interfaces 
struct E : public A, public B{ 
    ~E() {} 
    int e; 
    int a(int) { return e; } 
    int b(int) { return e; } 
}; 

int main() { 
    E e; D d; C c; 
    std::cout << "A : " << sizeof(A) << "\n"; 
    std::cout << "B : " << sizeof(B) << "\n"; 
    std::cout << "C : " << sizeof(C) << "\n"; 
    std::cout << "D : " << sizeof(D) << "\n"; 
    std::cout << "E : " << sizeof(E) << "\n"; 
} 

आउटपुट (एक 32bit मंच पर जीसीसी): कुछ भी याद आ रही

A : 4 
B : 4 
C : 8 
D : 8 
E : 12 
5

नहीं है वास्तव में 'अनुकरण' कुछ भी रूप में यह है कि सी ++ नहीं है की कोई जरूरत नहीं है कि जावा इंटरफेस के साथ कर सकते हैं।

एक सी ++ पॉइंटर व्यू से, जावा interface और class के बीच एक "कृत्रिम" विच्छेदन बनाता है। एक interface बस सभी जिसका तरीकों सार कर रहे हैं और जो किसी भी डेटा सदस्यों को शामिल नहीं कर सकते हैं की एक class है।

जावा इस प्रतिबंध को बनाता है क्योंकि यह अनियंत्रित एकाधिक विरासत की अनुमति नहीं देता है, लेकिन यह class से implement एकाधिक इंटरफेस की अनुमति देता है।

सी ++ में, classclass और interfaceclass है। extends सार्वजनिक विरासत द्वारा हासिल की है और implements भी सार्वजनिक विरासत द्वारा हासिल की है।

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

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

1

रास्ता MSVC 2008 तक __interface कीवर्ड है।

A Visual C++ interface can be defined as follows: 

- Can inherit from zero or more base 
    interfaces. 
- Cannot inherit from a base class. 
- Can only contain public, pure virtual 
    methods. 
- Cannot contain constructors, 
    destructors, or operators. 
- Cannot contain static methods. 
- Cannot contain data members; 
    properties are allowed. 

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

+0

मुझे लगता है कि यह सी ++ करने का COM/DCOM तरीका है। –

+0

आपको ऐसा क्यों लगता है? –

+0

केवल इसलिए कि यह एकमात्र ऐसा स्थान है जहां मैंने इसे देखा है ;-), COM इंटरफेस के साथ काम करते समय। –

1

सी ++ में हम सादे व्यवहार से आगे जा सकते हैं-जावा & सह के कम इंटरफेस। हम एनवीआई पैटर्न के साथ स्पष्ट अनुबंध (जैसे अनुबंध द्वारा डिजाइन) जोड़ सकते हैं।

struct Contract1 : noncopyable 
{ 
    virtual ~Contract1(); 
    Res f(Param p) { 
     assert(f_precondition(p) && "C1::f precondition failed"); 
     const Res r = do_f(p); 
     assert(f_postcondition(p,r) && "C1::f postcondition failed"); 
     return r; 
    } 
private: 
    virtual Res do_f(Param p) = 0; 
}; 

struct Concrete : virtual Contract1, virtual Contract2 
{ 
    ... 
}; 
0

आपके द्वारा पूछे जाने वाले इंटरफेस को लागू करने का कोई अच्छा तरीका नहीं है। एक दृष्टिकोण के साथ समस्या जैसे पूरी तरह से अमूर्त ISerializable बेस क्लास इस तरह से निहित है कि सी ++ एकाधिक विरासत लागू करता है। निम्नलिखित पर विचार करें:

class Base 
{ 
}; 
class ISerializable 
{ 
    public: 
    virtual string toSerial() = 0; 
    virtual void fromSerial(const string& s) = 0; 
}; 

class Subclass : public Base, public ISerializable 
{ 
}; 

void someFunc(fstream& out, const ISerializable& o) 
{ 
    out << o.toSerial(); 
} 

जाहिर आशय समारोह toSerial() उन है कि यह आधार वर्ग से विरासत सहित उपवर्ग के सदस्यों के सभी को क्रमानुसार करने के लिए है। समस्या यह है कि बेस से ISerializable से कोई रास्ता नहीं है। आप रेखांकन यह देख सकते हैं यदि आप निम्नलिखित निष्पादित करें:

void fn(Base& b) 
{ 
    cout << (void*)&b << endl; 
} 
void fn(ISerializable& i) 
{ 
    cout << (void*)&i << endl; 
} 

void someFunc(Subclass& s) 
{ 
    fn(s); 
    fn(s); 
} 

पहली कॉल द्वारा मूल्य उत्पादन दूसरी कॉल द्वारा मूल्य उत्पादन के रूप में ही नहीं है। हालांकि दोनों मामलों में एस का संदर्भ पारित किया गया है, संकलक उचित आधार वर्ग प्रकार से मेल खाने के लिए पारित पते को समायोजित करता है।