2012-01-26 13 views
9

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

#include <iostream> 
#include <string> 
#include <vector> 

class Vehicle 
{ 
public: 
    virtual std::string compareTo(Vehicle* v) = 0; 
}; 

class Bicycle : public Vehicle 
{ 
public: 
    Bicycle() { color_ = "red"; } 
    std::string compareTo(Vehicle* v) { return "We're different vehicles."; } 
    std::string compareTo(Bicycle* b) { return color_.compare(b->color_) ? "We're different bicycles." : "We're the same bicycle."; } 

private: 
    std::string color_; 
}; 

class Car : public Vehicle 
{ 
public: 
    Car() { style_ = "sedan"; } 
    std::string compareTo(Vehicle* v) { return "We're different vehicles."; } 
    std::string compareTo(Car* c) { return style_.compare(c->style_) ? "We're different cars." : "We're the same car."; } 

private: 
    std::string style_; 
}; 

int main() 
{ 
    Vehicle* compareFrom = new Bicycle(); 

    std::vector<Vehicle*> compareTos; 
    compareTos.push_back(new Bicycle()); 
    compareTos.push_back(new Car()); 

    std::vector<Vehicle*>::iterator it; 
    for (it = compareTos.begin(); it != compareTos.end(); ++it) 
    std::cout << compareFrom->compareTo(*it) << std::endl; 

    return 0; 
} 

वर्तमान में, निर्गम (आप here देख सकते हैं जो) कहते हैं, "हम अलग-अलग वाहनों रहे हैं"। मुझे पता है कि यह हो रहा है क्योंकि मैं सार आधार सूचक का उपयोग कर रहा हूँ। समस्या यह है कि इसे कैसे ठीक किया जाए!

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

+0

मेरा बुरा, ध्यान दें कि string.compare() स्ट्रिंग मैच के दौरान 0 लौटाता है। –

+0

आप गतिशील कास्टिंग में खुद को 'फेंक' क्यों मानते हैं? – Macke

+0

@ मैके मैंने जो पढ़ा है, उससे "उचित" ऑब्जेक्ट उन्मुख डिजाइन में गतिशील कास्टिंग आवश्यक नहीं होना चाहिए। चूंकि मैं इस डिजाइन को जमीन से कर रहा हूं, मैं इसे सही करना चाहता था! – aardvarkk

उत्तर

11

आप Multiple Dispatch (यानी एक से अधिक चर पर आधारित गतिशील रूप से कॉल करने के लिए कौन सा फ़ंक्शन चुनते हैं, न केवल 'यह')। ऐसा इसलिए है क्योंकि आपको किसी प्रकार का निरीक्षण करने की आवश्यकता है, अन्यथा कंपाइलर प्रकारों पर एक स्थिर विश्लेषण करेगा और कॉल करने के लिए कौन सा फ़ंक्शन कॉल करेगा (वाहन में आभासी वाला)।

इसके आसपास कोई रास्ता नहीं है। dynamic_cast आपका मित्र यहां है, लेकिन आप प्रदर्शन (या अन्य) कारणों के लिए अपनी खुद की आरटीटीआई प्रणाली रोल करना चाहते हैं। (विकिपीडिया लेख किसी तरह से पता चलता है ..)

std::string Bicycle::compareTo(Vehicle* v) { 
    if (Bicycle* b = dynamic_cast<Bicycle*>(v)) { 
     return compareTo(b); 
    } else { 
     return "We're different vehicles."; 
    } 
} 

Loki C++ library में इस पैटर्न जो अगर आप कई प्रकार के होते है कि तुलना करने की जरूरत है मदद कर सकता है के एक कार्यान्वयन है।

एकाधिक प्रेषण सी ++ में भाषा द्वारा समर्थित नहीं है, न ही अधिकांश मुख्यधारा भाषाओं में। हालांकि इसे C++ 11 में जोड़ने का एक प्रस्ताव था, हालांकि, यह question और Bjarne's paper देखें। मुझे लगता है कि इसे अस्वीकार कर दिया गया था क्योंकि (ज्ञात और अज्ञात) गतिशील लिंकिंग के साथ समस्याएं, जो सी ++ मानक दुख से कुछ भी नहीं जानता है।

+0

एकाधिक प्रेषण के साथ मैंने जो समस्या देखी है वह यह है कि आपको लगता है कि तुलना करने के लिए आपको हर दूसरे प्रकार के लिए एक फ़ंक्शन लागू करना होगा। उदाहरण के लिए, यदि मेरे पास 100 प्रकार के वाहन थे, तो प्रत्येक वाहन उप-वर्ग को किसी अन्य के खिलाफ तुलना करने में सक्षम होना चाहिए। ऐसा लगता है कि यदि आप किसी को लागू करने के लिए * भूल जाते हैं, तो आप संभावित अनंत लूप में भाग लेते हैं। यह गतिशील दृष्टिकोण उस अच्छी तरह से टालता है, मैंने सोचा कि शायद कुछ सुपर-चालाक था जो मैं गायब था। – aardvarkk

+0

@Aardvarkk: ठीक है, सुपर-चालाक चीज़ सी ++ द्वारा समर्थित नहीं है, इसलिए आपको इसके आसपास काम करना होगा। लोकी की हेडर फाइल थकाऊ चीजों को स्वचालित करके मदद करती है। – Macke

+0

@Aardvarkk: इसके अलावा, यदि आपके पास 100 प्रकार के वाहन वर्ग हैं जिन्हें कस्टम तुलना कोड की आवश्यकता है, तो आपको शायद अपने डिज़ाइन पर पुनर्विचार करना होगा। ;-) हालांकि, टकराव का पता लगाने की आवश्यकता वाले 10 अलग-अलग ज्यामिति प्रकार असामान्य नहीं हैं, और फिर आपको प्रत्येक संस्करण में कुशल कोड होने की आवश्यकता है। – Macke

0

यदि आपके पास अपने कंपाइलर में आरटीटीआई सक्षम है, तो आप टाइपिड() ऑपरेटर का उपयोग करने में सक्षम हो सकते हैं, लेकिन इसके लिए आपके वर्गों को पॉलिमॉर्फिक होना आवश्यक होगा।

5

आपके कोड में बड़ी समस्या है कि यह आसानी से एक्स्टेंसिबल नहीं है (open/closed principle का उल्लंघन करता है)। हालांकि आप बेस क्लास विधि की तुलना में प्रतिनिधि को प्रतिनिधि बना सकते हैं।

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

यह मजबूत और विस्तृत बनाने के लिए,

  1. आधार विधि शुद्ध आभासी
  2. आधार विधि के लिए एक कार्यान्वयन प्रदान करते हैं (हाँ, यह काम करता है! यहां तक ​​कि अगर यह शुद्ध आभासी है) कि वस्तुओं 'तुलना प्रकार
  3. व्युत्पन्न कक्षाओं में, समान श्रेणी के परीक्षण के लिए बेस क्लास 'कार्यान्वयन का उपयोग करें, फिर वास्तविक तर्क जांच करें।क्योंकि हम पहले से सुनिश्चित किया है प्रकार एक ही है, इस प्रकार डाली असफल कभी नहीं होगा है कि
#include <iostream> 
#include <iomanip> 
#include <string> 
#include <typeinfo> 

struct vehicle { 
    virtual bool compare_to(vehicle const& other) const = 0; 
}; 

bool vehicle::compare_to(vehicle const& other) const { 
    return typeid(*this) == typeid(other); 
} 

struct car : vehicle { 
    std::string color; 

    car(std::string const& color) : color(color) { } 

    bool compare_to(vehicle const& other) const { 
     bool result = vehicle::compare_to(other); 
     return result and (color == static_cast<car const&>(other).color); 
    } 
}; 

struct bike : vehicle { 
    int spokes; 

    bike(int spokes) : spokes(spokes) { } 

    bool compare_to(vehicle const& other) const { 
     bool result = vehicle::compare_to(other); 
     return result and (spokes == static_cast<bike const&>(other).spokes); 
    } 
}; 

int main() { 
    car c1("blue"); 
    car c2("red"); 
    bike b1(42); 

    std::cout << std::boolalpha; 
    std::cout << c1.compare_to(c2) << "\n" 
       << c1.compare_to(b1) << "\n" 
       << c1.compare_to(c1) << "\n"; 
} 

उपरोक्त कोड, static_cast सुरक्षित है।

ध्यान दें कि typeid का उपयोग यहां पूरी तरह से वैध है। यह भी बहुत अक्षम नहीं होना चाहिए क्योंकि चलने के लिए कोई गहरा प्रकार पदानुक्रम नहीं है। लेकिन यदि आप इसे और अधिक कुशल बनाना चाहते हैं तो आप एक सरल स्वयं तंत्र को कार्यान्वित कर सकते हैं जो बेस क्लास में एक स्थिर तालिका का उपयोग करता है ताकि प्रत्येक निर्मित उदाहरण को टाइप-अनन्य नंबर पहचानकर्ता (उदाहरण के लिए std::map<vehicle*, type_id>), जहां type_id एक सादा पुराना enum है) एक साधारण लुकअप

... या वास्तव में dynamic_cast का उपयोग करें।

+0

आपके पूर्ण उत्तर के लिए धन्यवाद! लगता है कि आरटीटीआई यहां जरूरी है ... – aardvarkk

+0

@AndyT कृपया वाक्यविन्यास को "सही" न करें, यह पहले से ही सही था। 'और' और 'या' '&&' और '||' के लिए वैध वैकल्पिक प्रीप्रोकैसिंग टोकन हैं, भले ही माइक्रोसॉफ्ट अलग-अलग नाटक करता हो। –

+0

@ कोनराड रुडॉल्फ: क्षमा करें, पता नहीं था –

4

मैं सामान्य रूप से बेस क्लास में 'दयालु' सदस्य का उपयोग करके इसे कार्यान्वित करता हूं। मैं इस में कुछ फायदे हैं लगता है:

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

तरह प्रकार निम्नलिखित दिखाई देगा:

enum Kind { 
     HUMAN_POWERED = (0x1 << 0), 
     MOTOR_POWERED = (0x1 << 1), 
     BICYCLE  = (0x1 << 2) | HUMAN_POWERED, 
     UNICYCLE  = (0x1 << 3) | HUMAN_POWERED, 
     CAR   = (0x1 << 4) | MOTOR_POWERED 
    }; 

अब यह जांच करने के लिए है कि कार एक साइकिल नहीं है संभव है, लेकिन यह भी अगर दो प्रकार MOTOR_POWERED या नहीं कर रहे हैं!

bool areSameClass (Vehicle const & lhs, Vehicle const & rhs) 
{ 
    return (lhs->getKind() & rhs->getKind()) & (HUMAN_POWERED | MOTOR_POWERED); 
} 
संबंधित मुद्दे