2010-04-01 16 views
11

में सार्वजनिक गुण होने के कारण आपके पास सी ++ कक्षा में गुण हैं, जैसा कि आपके पास सी # कक्षा है।सी ++ कक्षा

मैं गेटर और सेटर विधियों को नहीं चाहता हूं।

+2

यहां एक समान प्रश्न देखें: http://stackoverflow.com/questions/2017623/forward-unbreakable-accessor-class-templates-c। हमने सी ++ सुविधाओं का उपयोग करके गुणों को लागू करने के कुछ तरीकों को फेंक दिया। –

+0

प्लेटफ़ॉर्म-विशिष्ट, या प्लेटफ़ॉर्म-स्वतंत्र C++? विंडोज़ में, वीसी ++ शिकायतकर्ता के पास गैर मानक एक्सटेंशन हैं जो इसका समर्थन करते हैं। नीचे मेरा जवाब देखें। –

उत्तर

11

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

#include <iostream> 

template<typename T> 
class Accessor { 
public: 
    explicit Accessor(const T& data) : value(data) {} 

    Accessor& operator=(const T& data) { value = data; return *this; } 
    Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; } 
    operator T() const { return value; } 
    operator T&() { return value; } 

private: 
    Accessor(const Accessor&); 


    T value; 

}; 

struct Point { 
    Point(int a = 0, int b = 0) : x(a), y(b) {} 
    Accessor<int> x; 
    Accessor<int> y; 
}; 

int main() { 
    Point p; 
    p.x = 10; 
    p.y = 20; 
    p.x++; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 15; 
    std::cout << p.x << "," << p.y << std::endl; 

    return 0; 
} 

हम operator= ओवरलोड एक समारोह-कॉल की तरह वाक्य रचना के बजाय हमेशा की तरह काम वाक्य रचना बनाए रखने के लिए। हम कास्ट ऑपरेटर का उपयोग "गेटटर" के रूप में करते हैं। main() में दूसरी तरह के असाइनमेंट की अनुमति देने के लिए हमें operator= के दूसरे संस्करण की आवश्यकता है।

अब आप एक्सेसर के कन्स्ट्रक्टर फ़ंक्शन पॉइंटर्स या बेहतर - फ़ैक्टरों को जोड़ सकते हैं - गेटर्स/सेटर्स के रूप में कॉल करने के लिए किसी भी तरह से आपको सही लगता है। ,

#include <iostream> 
#include <functional> 
#include <cmath> 

template<typename T> 
class MySetter { 
public: 
    bool operator()(const T& data) 
    { 
     return (data <= 20 ? true : false); 
    } 
}; 

template<typename T> 
class MyGetter { 
public: 
    T operator()(const T& data) 
    { 
     return round(data, 2); 
    } 

private: 
    double cint(double x) { 
     double dummy; 
     if (modf(x,&dummy) >= 0.5) { 
      return (x >= 0 ? ceil(x) : floor(x)); 
     } else { 
      return (x < 0 ? ceil(x) : floor(x)); 
     } 
    } 

    double round(double r, int places) { 
     double off = pow(10.0L, places); 
     return cint(r*off)/off; 
    } 
}; 

template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>> 
class Accessor { 
public: 
    explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {} 

    Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; } 
    Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; } 
    operator T() const { value = getter(value); return value;} 
    operator T&() { value = getter(value); return value; } 

private: 
    Accessor(const Accessor&); 

    T value; 

    G getter; 
    S setter; 

}; 

struct Point { 
    Point(double a = 0, double b = 0) : x(a), y(b) {} 
    Accessor<double> x; 
    Accessor<double> y; 
}; 

int main() { 
    Point p; 
    p.x = 10.712; 
    p.y = 20.3456; 
    p.x+=1; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 15.6426; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 25.85426; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 19.8425; 
    p.y+=1; 
    std::cout << p.x << "," << p.y << std::endl; 

    return 0; 
} 

हालांकि अंतिम पंक्ति को दर्शाता है के रूप में यह एक बग है: निम्न उदाहरण नया मान सेट करने के लिए समझौते को व्यक्त करने के सेटर समारोह वापसी bool मान लिया गया है, और यह तरीका है पर गेटर बस इसे संशोधित कर सकते हैं। एक टी & लौटने वाले कास्ट ऑपरेटर उपयोगकर्ताओं को सेटर को बाईपास करने की अनुमति देता है, क्योंकि इससे उन्हें निजी मूल्य तक पहुंच मिलती है। इस बग को हल करने का एक तरीका उन सभी ऑपरेटरों को लागू करना है जिन्हें आप अपने एक्सेसर को प्रदान करना चाहते हैं।उदाहरण के लिए, निम्न कोड में मैं + = ऑपरेटर इस्तेमाल किया, और के बाद से मैं डाली ऑपरेटर लौटने संदर्भ हटा दिया मैं लागू करने के लिए किया था एक operator+=:

#include <iostream> 
#include <functional> 
#include <cmath> 

template<typename T> 
class MySetter { 
public: 
    bool operator()(const T& data) const { 
     return (data <= 20 ? true : false); 
    } 
}; 

template<typename T> 
class MyGetter { 
public: 
    T operator() (const T& data) const { 
     return round(data, 2); 
    } 

private: 
    double cint(double x) const { 
     double dummy; 
     if (modf(x,&dummy) >= 0.5) { 
      return (x >= 0 ? ceil(x) : floor(x)); 
     } else { 
      return (x < 0 ? ceil(x) : floor(x)); 
     } 
    } 

    double round(double r, int places) const { 
     double off = pow(10.0L, places); 
     return cint(r*off)/off; 
    } 
}; 

template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>> 
class Accessor { 
private: 
public: 
    explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {} 

    Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; } 
    Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; } 
    operator T() const { return getter(value);} 

    Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; } 

private: 
    Accessor(const Accessor&); 

    T value; 

    G getter; 
    S setter; 

}; 

struct Point { 
    Point(double a = 0, double b = 0) : x(a), y(b) {} 
    Accessor<double> x; 
    Accessor<double> y; 
}; 

int main() { 
    Point p; 
    p.x = 10.712; 
    p.y = 20.3456; 
    p.x+=1; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 15.6426; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 25.85426; 
    std::cout << p.x << "," << p.y << std::endl; 

    p.x = p.y = 19.8425; 
    p.y+=1; 
    std::cout << p.x << "," << p.y << std::endl; 

    return 0; 
} 

आप सभी ऑपरेटरों आप जा रहे हैं लागू करता है करना होगा उपयोग करने के लिए।

+0

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

+0

यह ध्यान में रखते हुए कि आप केवल उन सभी ऑपरेटरों को टेम्पलेट कक्षा में परिभाषित करते हैं, मुझे नहीं लगता कि यह भयानक है। एसटीएल या बूस्ट कोड मेरी राय में इस (और आमतौर पर भी बोल रहा है :)) की तुलना में भयानक तुलना करता है। लेकिन अन्य विकल्प भी हैं। किसी को किसी की परिस्थितियों पर विचार करना चाहिए और तदनुसार अपनी पसंद करना चाहिए। :) – conio

1

आप नहीं करते हैं। सी ++ सी # करता है जैसे गुणों का समर्थन नहीं करता है। यदि आप सेट/प्राप्त करने के लिए कोड चलाने के लिए चाहते हैं, तो यह एक विधि होनी चाहिए।

+0

गुण वास्तव में सी # में विधियों को भी चलाते हैं। संकलक इसे आप से छुपाता है। –

7

इस तरह की व्यवहार के लिए, मैं एक टेम्पलेट मेटा-एक्सेसर का उपयोग करता हूं। यहाँ पॉड प्रकारों के लिए एक अत्यधिक सरल एक है:

template<class T> 
struct accessor { 

    explicit accessor(const T& data) : value(data) {} 
    T operator()() const { return value; } 
    T& operator()() { return value; } 
    void operator()(const T& data) { value = data; } 

private: 

    accessor(const accessor&); 
    accessor& operator=(const accessor&); 
    T value; 

}; 

विशिष्ट उपयोग इस तरह है:

struct point { 
    point(int a = 0, int b = 0) : x(a), y(b) {} 
    accessor<int> x; 
    accessor<int> y; 
}; 

point p; 
p.x(10); 
p.y(20); 
p.x()++; 
std::cout << p.x(); 

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

संपादित करें: यदि आप कोष्ठक का उपयोग नहीं करना चाहते हैं, तो आप हमेशा operator=() और एक निहित कास्ट ऑपरेटर परिभाषित कर सकते हैं। यहां एक ऐसा संस्करण है जो मूलभूत "सामान हुआ" कॉलबैक समर्थन जोड़ने के दौरान करता है:

आगे संपादित करें: ठीक है, पूरी तरह से याद किया गया है कि किसी ने पहले से ही मेरे कोड का एक संशोधित संस्करण बनाया है। आह।

+0

जब आप ऑपरेटर परिभाषित कर सकते थे तो आप क्लंकी फ़ैक्टर शैली का उपयोग क्यों कर रहे हैं? –

+0

@ बेन: पूर्णता के लिए अंतर जोड़ा गया। यह मेरे दिमाग में फिसल गया कि कैसे बकवास सी # में दिखता है। –

1

गुण सी ++ प्रतिशत समर्थन नहीं करता, लेकिन आप उन्हें लागू कर सकते हैं:
1) टेम्पलेट्स
2) भाषा विस्तार करके का उपयोग कर और कस्टम कोड पूर्वप्रक्रमक

या तो दृष्टिकोण लेखन आसान नहीं होगा करके, लेकिन यह किया जा सकता है।

+0

सी ++ ओवरलोडिंग ऑपरेटर का समर्थन करता है =, इसलिए कोई एक्सटेंशन आवश्यक नहीं है। –

+0

@ बेन वोगेट यह इस बात पर निर्भर करता है कि आप वास्तव में क्या कार्यान्वित करना चाहते हैं, और आप कितनी संपत्तियां चाहते हैं। बड़ी संख्या में कोड के साथ, एक कीवर्ड या दो पेश करना और कोड प्रीप्रोसेसर लिखना बेहतर विचार होगा - यह कोड को और अधिक पठनीय बना देगा। – SigTerm

3

यहां एक पीओसी कार्यान्वयन है जो मैंने कुछ समय पहले किया था, अच्छी तरह से काम करता है सिवाय इसके कि आपको इसके लिए कन्स्ट्रक्टर में कुछ अच्छा और सुचारू रूप से काम करने की आवश्यकता है।

http://www.codef00.com/code/Property.h

यहां उदाहरण के उपयोग है:

    :

    #include <iostream> 
    #include "Property.h" 
    
    
    class TestClass { 
    public: 
        // make sure to initialize the properties with pointers to the object 
        // which owns the property 
        TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) { 
        } 
    
    private: 
        int getProp1() const { 
         return m_Prop1; 
        } 
    
        void setProp1(int value) { 
         m_Prop1 = value; 
        } 
    
        int getProp2() const { 
         return 1234; 
        } 
    
        void setProp3(double value) { 
         m_Prop3 = value; 
        } 
    
        int m_Prop1; 
        double m_Prop3; 
    
    public: 
        PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1; 
        PropertyRO<int, TestClass, &TestClass::getProp2> prop2; 
        PropertyWO<double, TestClass, &TestClass::setProp3> prop3; 
    }; 
    

    और इस वर्ग के कुछ उपयोग ...

    int main() { 
        unsigned int a; 
        TestClass t; 
        t.prop1 = 10; 
        a = t.prop1; 
        t.prop3 = 5; 
        a = t.prop2; 
        std::cout << a << std::endl; 
        return 0; 
    } 
    

    इस दृष्टिकोण के साथ दो खीज कर रहे हैं

  1. आपको जी की आवश्यकता है संपत्ति को पॉइंटर अपनी खुद की कक्षा में ले जाएं।
  2. वाक्य रचना एक संपत्ति घोषित करने के लिए एक बिट वर्बोज़ है, लेकिन मुझे यकीन है कि मैं साफ कर सकते हैं कि कुछ मैक्रो के साथ एक सा
+0

एक छोटी सी अजीब, हाँ, लेकिन साथ 'this', एक अच्छा एक है क्योंकि यह आसानी गुण जो उदाहरण के आधार पर उन तक पहुँचने और टाइप जानकारी कर सकते हैं के साथ चाल खेलने की अनुमति देता एक्सेसर पंजीकरण की विचार। –

1

आप सका हो और सेट तरीकों डेटा सदस्यों के लिए इसी तरह के नाम है प्रदान करते हैं :

class Example 
{ 
    private: 
    unsigned int x_; 
    double d_; 
    std::string s_s; 
    public: 
    unsigned int x(void) const 
    { return x_;} 

    void x(unsigned int new_value) 
    { x_ = new_value;} 

    double d(void) const 
    { return d_;} 
    void d(double new_value) 
    { d_ = new_value;} 

    const std::string& s(void) const 
    { return s_;} 
    void s(const std::string& new_value) 
    { s_ = new_value;} 
}; 

इस करीब आता है, के रूप में उपयोग की आवश्यकता है '()' प्रत्येक सदस्य के लिए, यह गुण कि माइक्रोसॉफ्ट बोली प्रदान की सटीक कार्यक्षमता को पूरा नहीं करता।

गुणों के लिए निकटतम मिलान डेटा सदस्यों को सार्वजनिक रूप से घोषित करना है।

+0

गलत, ऑपरेटर ओवरलोडिंग द्वारा = आप सटीक वाक्यविन्यास (उपयोगकर्ता के लिए) और किसी भी भाषा की क्षमताओं को प्राप्त कर सकते हैं जो आपके लिए गुण प्रदान करता है। –

3

यदि आपको परवाह नहीं है कि आपका सी ++ कोड माइक्रोसॉफ्ट विजुअल सी ++ कंपाइलर के अलावा किसी अन्य चीज़ के साथ संकलित नहीं होगा, तो आप कुछ कंपाइलर के गैर-मानक एक्सटेंशन का उपयोग कर सकते हैं।

उदाहरण के लिए, निम्न कोड MyProperty नामक सी # जैसी संपत्ति बनाएगा।

struct MyType 
{ 
    // This function pair may be private (for clean encapsulation) 
    int get_number() const { return m_number; } 
    void set_number(int number) { m_number = number; } 

    __declspec(property(get=get_number, put=set_number)) int MyProperty; 
private: 
    int m_number: 
} 

int main() 
{ 
    MyType m; 
    m.MyProperty = 100; 
    return m.MyProperty; 
} 

इस माइक्रोसॉफ्ट-विशिष्ट भाषा एक्सटेंशन पर अधिक जानकारी here उपलब्ध है।

संबंधित मुद्दे