2010-04-12 8 views
6

मैं मेरी कक्षा में कई सदस्यों को जो const होते हैं और इसलिए केवल इसलिए की तरह initialiser सूची के माध्यम से initialised किया जा सकता है:सदस्य स्थिरांक, जबकि अभी भी ऑपरेटर समर्थन = वर्ग पर होने की अनुमति दें

class MyItemT 
{ 
public: 
    MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo) 
     : mMyPacket(aMyPacket), 
      mMyInfo(aMyInfo) 
    { 
    } 

private: 
    const MyPacketT mMyPacket; 
    const MyInfoT mMyInfo; 
}; 

मेरी कक्षा हो सकता है हमारे कुछ आंतरिक रूप से परिभाषित कंटेनर कक्षाओं (जैसे वैक्टर) में उपयोग किया जाता है, और इन कंटेनरों को कक्षा में परिभाषित operator= की आवश्यकता होती है।

बेशक

, मेरे operator= कुछ इस तरह करने की जरूरत है:

MyItemT& 
MyItemT::operator=(const MyItemT& other) 
{ 
    mMyPacket = other.mPacket; 
    mMyInfo = other.mMyInfo; 
    return *this; 
} 

जो निश्चित रूप से काम नहीं करता है क्योंकि mMyPacket और mMyInfoconst सदस्य हैं।

इन सदस्यों को गैर-const (जो मैं नहीं करना चाहता) बनाने के अलावा, इस बारे में कोई विचार है कि मैं इसे कैसे ठीक कर सकता हूं?

+8

यदि आपकी कक्षा असाइन करने योग्य है और उन सदस्यों को बदला जा सकता है, तो 'const' को हटा दें। यह बस समझ में नहीं आता है। – GManNickG

+0

@GMan - मुझे लगता है कि आप सही हैं। मैं किसी तरह के मित्र-जैसा दृष्टिकोण की तलाश में था जो 'ऑपरेटर =' को विशेष विशेषाधिकार रखने की अनुमति देता है, जो अभी भी सदस्यों को मैन्युअल रूप से ओवरराइड होने से रोकता है। लेकिन @MichaelMrozek ने क्या कहा, यह पढ़ने के बाद, यह वास्तव में बिल्कुल सुरक्षित नहीं है। – LeopardSkinPillBoxHat

उत्तर

7

यदि आपके पास असाइनमेंट ऑपरेटर है जो निर्माण समाप्त होने के बाद उन्हें बदल सकता है तो आप कॉन्स्ट की परिभाषा का उल्लंघन कर रहे हैं। यदि आपको वास्तव में आवश्यकता है, तो मुझे लगता है कि Potatoswatter की प्लेसमेंट नई विधि शायद सबसे अच्छी है, लेकिन यदि आपके पास असाइनमेंट ऑपरेटर है तो आपके चर वास्तव में नहीं हैं, क्योंकि कोई भी एक नया उदाहरण बना सकता है और अपने मानों को बदलने के लिए इसका उपयोग कर सकता है

+0

@ माइकल - सच, और एक वैध बिंदु। – LeopardSkinPillBoxHat

+1

दूसरी तरफ, कुछ भी वास्तव में उपयोगकर्ता को ऐसी बुराई करने से रोकता है। (या बस 'const_cast' का उपयोग कर!) निश्चित रूप से, दस्तावेज़ जो' ऑपरेटर = 'सदस्यों के संदर्भ को अमान्य करता है। – Potatoswatter

+1

ठीक है, 'const_cast' और' mutable' विनिर्देशक के बीच आप कॉन्स्ट चर के अवधारणा को पूरी तरह से नष्ट कर सकते हैं :)। यदि आप इसे –

2

यह एक गंदा हैक है, लेकिन आप को नष्ट करने और अपने आप को फिर से संगठित कर सकते हैं:

MyItemT& 
MyItemT::operator=(const MyItemT& other) 
{ 
    if (this == &other) return *this; // "suggested" by Herb Sutter ;v) 

    this->MyItemT::~MyItemT(); 
    try { 
     new(this) MyItemT(other); 
    } catch (...) { 
     new(this) MyItemT(); // nothrow 
     throw; 
    } 
    return *this; 
} 

संपादित करें: ऐसा न हो कि मैं अपने विश्वसनीयता को नष्ट, मैं वास्तव में इस अपने आप ऐसा नहीं करते हैं, मैं const को दूर करेंगे। हालांकि, मैं इस अभ्यास को बदलने पर बहस कर रहा हूं, क्योंकि const जहां भी संभव हो, उपयोग करने के लिए बस उपयोगी और बेहतर है।

कभी-कभी किसी ऑब्जेक्ट द्वारा प्रदत्त संसाधन और मूल्य के बीच एक अंतर होता है। जब तक संसाधन समान होता है तब तक एक सदस्य मूल्य में परिवर्तनों के माध्यम से हो सकता है, और उस पर संकलन-समय सुरक्षा प्राप्त करना अच्छा लगेगा।

संपादित करें 2: @ चार्ल्स बेली ने यह अद्भुत (और अत्यधिक महत्वपूर्ण) लिंक प्रदान किया है: http://gotw.ca/gotw/023.htm

  • किसी भी व्युत्पन्न वर्ग operator= में सेमेन्टिक्स मुश्किल हैं।
  • यह अक्षम हो सकता है क्योंकि यह असाइनमेंट ऑपरेटरों को नहीं बुलाता है जिन्हें परिभाषित किया गया है।
  • यह wonky operator& भार के (जो)
  • आदि

संपादित 3 के साथ असंगत है: बनाम "क्या मूल्य" भेद "जो संसाधन" के माध्यम से सोच रही थी, यह स्पष्ट लगता है कि operator= हमेशा चाहिए परिवर्तन मूल्य और संसाधन नहीं। संसाधन पहचानकर्ता const हो सकता है। उदाहरण में, सभी सदस्य const हैं। यदि "जानकारी" है जो "पैकेट" के अंदर संग्रहीत है, तो शायद पैकेट const और जानकारी नहीं होनी चाहिए।

तो समस्या इस उदाहरण में स्पष्ट मूल्य की कमी के रूप में असाइनमेंट के अर्थशास्त्र को इतनी अधिक नहीं लग रही है, अगर "जानकारी" वास्तव में मेटाडेटा है। यदि किसी भी वर्ग के पास MyItemT है, तो इसे एक पैकेट से दूसरे में स्विच करना चाहता है, इसे छोड़कर auto_ptr<MyItemT> का उपयोग करना चाहिए या उपरोक्त के समान ही हैक (पहचान परीक्षण अनावश्यक है लेकिन catch शेष) लागू किया गया है के बाहर।लेकिन operator= को एक अतिरिक्त विशेष सुविधा के अलावा संसाधन बाध्यकारी को नहीं बदला जाना चाहिए जो बिल्कुल किसी और के साथ हस्तक्षेप नहीं करेगा।

ध्यान दें कि यह सम्मेलन असाइनमेंट के मामले में कॉपी निर्माण को लागू करने के लिए सटर की सलाह के साथ अच्छा प्रदर्शन करता है।

MyItemT::MyItemT(MyItemT const &in) 
    : mMyPacket(in.mMyPacket) // initialize resource, const member 
    { *this = in; } // assign value, non-const, via sole assignment method 
+2

@Potatoswatter - सुझाव के लिए धन्यवाद, लेकिन मुझे लगता है कि यह सदस्यों को शुरू करने के लिए सदस्यों को गैर-कॉन्स बनाने से शायद गंदे है। – LeopardSkinPillBoxHat

+0

@ लियोपार्ड: समझा, लेकिन दूसरी ओर वास्तव में कुछ भी असुरक्षित नहीं है। एक तरह से, आपको एक अतिरिक्त गारंटी मिलती है कि कोई पुरानी स्थिति का उपयोग नहीं किया जा रहा है। – Potatoswatter

+3

यदि कॉपी कन्स्ट्रक्टर फेंकता है, तो आप बर्बाद हो सकते हैं, क्योंकि जहां तक ​​भाषा जाती है, '* यह फिर से अपरिहार्य नष्ट हो जाएगा। – UncleBens

3

सीधे अपने कंटेनरों में वस्तुओं को संग्रहीत करने के बजाय, आप पॉइंटर्स (या स्मार्ट पॉइंटर्स) स्टोर करने में सक्षम हो सकते हैं। इस तरह, आपको अपनी कक्षा के किसी भी सदस्य को म्यूटेट करने की आवश्यकता नहीं है - आप const और सभी में पारित वही ऑब्जेक्ट वापस प्राप्त करते हैं।

बेशक, ऐसा करने से शायद आपके आवेदन के मेमोरी प्रबंधन को कुछ हद तक बदल दिया जाएगा, जो कि एक अच्छा पर्याप्त कारण नहीं हो सकता है।

+0

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

+0

@LeopardSkinPillBoxHat: यहां तक ​​कि यदि कक्षा पॉइंटर्स रखती है, इसका मतलब यह नहीं है कि आपके पास आजीवन समस्याएं होंगी - कक्षा अभी भी उस डेटा को अपने आवंटन में कॉपी करेगी, यह सिर्फ इतना है कि वे ढेर से 'नया' पॉइंटर्स (या स्मार्ट पॉइंटर्स) उन गतिशील रूप से आवंटित बिट्स के लिए सीधे ऑब्जेक्ट में प्रतियां रखने के बजाय। –

1

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

तो आपको const का लाभ मिलता है ताकि आप गलती से उन परिवर्तनों को अनुमति देने में मदद कर सकें जिन्हें आप डिज़ाइन करना चाहते हैं, लेकिन आप अभी भी ऑब्जेक्ट को किसी अन्य ऑब्जेक्ट से असाइन करने की अनुमति देते हैं। उदाहरण के लिए, यह आपको एसटीएल कंटेनरों में इस वर्ग की वस्तुओं का उपयोग करने देगा।

आप इसे 'पिंपल' मुहावरे के विशेष अनुप्रयोग के रूप में देख सकते हैं।

की तर्ज पर

कुछ:

#include <algorithm> // for std::swap 

#include "boost/scoped_ptr.hpp" 

using namespace boost; 

class MyPacketT {}; 
class MyInfoT {}; 


class MyItemT 
{ 
public: 
    MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo) 
     : pMyPacket(new MyPacketT(aMyPacket)), 
      pMyInfo(new MyInfoT(aMyInfo)) 
    { 
    } 

    MyItemT(MyItemT const& other) 
     : pMyPacket(new MyPacketT(*(other.pMyPacket))), 
      pMyInfo(new MyInfoT(*(other.pMyInfo))) 
    { 

    } 

    void swap(MyItemT& other) 
    { 
     pMyPacket.swap(other.pMyPacket); 
     pMyInfo.swap(other.pMyInfo);   
    } 


    MyItemT const& operator=(MyItemT const& rhs) 
    { 
     MyItemT tmp(rhs); 

     swap(tmp); 

     return *this; 
    } 

private: 
    scoped_ptr<MyPacketT const> pMyPacket; 
    scoped_ptr<MyInfoT const> pMyInfo; 
}; 

अंत में, मैं अपने उदाहरण क्योंकि मैंने सोचा कि यह क्या ओपी इरादा की एक अधिक सामान्य प्रतिनिधित्व था scoped_ptr<> बजाय shared_ptr<> उपयोग करने के लिए बदल दिया है। हालांकि, अगर 'पुन: असाइन करने योग्य' const सदस्यों को साझा किया जा सकता है (और यह शायद सच है, ओपी चाहता है कि क्यों उन्हें ओपी उन्हें const) चाहिए, तो यह shared_ptr<> का उपयोग करने का अनुकूलन हो सकता है और प्रतिलिपि और असाइनमेंट ऑपरेशन shared_ptr<> कक्षा उन वस्तुओं के लिए चीजों का ख्याल रखती है - यदि आपके पास कोई अन्य सदस्य नहीं है जिसके लिए विशेष प्रतिलिपि या सेमिटिक्स असाइन की आवश्यकता है, तो आपकी कक्षा को अभी बहुत आसान हो गया है, और आप प्रतियां साझा करने में सक्षम होने के कारण स्मृति उपयोग का एक महत्वपूर्ण बिट भी बचा सकते हैं MyPacketT और MyInfoT ऑब्जेक्ट्स का।

1

मुझे लगता है कि आप एक विशेष const प्रॉक्सी से दूर हो सकते हैं।

template <class T> 
class Const 
{ 
public: 
    // Optimal way of returning, by value for built-in and by const& for user types 
    typedef boost::call_traits<T>::const_reference const_reference; 
    typedef boost::call_traits<T>::param_type param_type; 

    Const(): mData() {} 
    Const(param_type data): mData(data) {} 
    Const(const Const& rhs): mData(rhs.mData) {} 

    operator const_reference() const { return mData; } 

    void reset(param_type data) { mData = data; } // explicit 

private: 
    Const& operator=(const Const&); // deactivated 
    T mData; 
}; 

अब, const MyPacketT के बजाय आप Const<MyPacketT> होगा। यह नहीं कि इंटरफ़ेस केवल इसे बदलने का एक तरीका प्रदान करता है: एक स्पष्ट कॉल के माध्यम से reset पर।

मुझे लगता है कि mMyPacket.reset का कोई भी उपयोग आसानी से खोजा जा सकता है।@MSalters ने कहा कि यह मर्फी के खिलाफ सुरक्षा करता है, मच्छियाली नहीं :)

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