2012-02-22 17 views
9

इस बिंदु पर, कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर जोड़ी लिखना अच्छी तरह परिभाषित है; एक त्वरित खोज आपको इन्हें ठीक से कोड करने के तरीके पर बहुत सारी हिट का नेतृत्व करेगी।सी ++ 11 में कॉपी/मूव/ऑपरेटर = त्रिकोणीय लिखने का "सही" तरीका क्या है?

अब चालक कन्स्ट्रक्टर मिश्रण में प्रवेश कर चुका है, क्या कोई नया "सर्वश्रेष्ठ" तरीका है?

+1

एक अच्छा [SO सवाल] था (http://stackoverflow.com/questions/9322174/move-assignment-operator-and-if-this-rhs) जिसे आप देखना चाहते हैं। –

+1

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

+0

यह बहुत व्यापक क्यों है? कॉपी कन्स्ट्रक्टर + असाइनमेंट ऑपरेटर के लिए आम तौर पर स्वीकार्य पैटर्न है, क्यों नहीं कन्स्ट्रक्टर के लिए भी? – moswald

उत्तर

12

अधिमानतः, वे सिर्फ = default; हो जाएगा, के बाद से सदस्य प्रकार std::unique_ptr की तरह, संसाधन प्रबंधन के प्रकार है कि आप से कदम विवरण को छिपाना का होना चाहिए। केवल "निम्न स्तर" प्रकार के कार्यान्वयनकर्ताओं से निपटने के साथ परेशान होना चाहिए।

याद रखें कि यदि आप बाहरी (आपके ऑब्जेक्ट) संसाधन को धारण कर रहे हैं तो आपको केवल हिल अर्थशास्त्र से परेशान करने की आवश्यकता है। यह "फ्लैट" प्रकारों के लिए पूरी तरह बेकार है।

+1

यह एक वैध बिंदु है। मुझे लगता है कि मैं "यदि आप अपना खुद का लिखने जा रहे थे" के साथ योग्यता प्राप्त कर सकते थे, लेकिन आपका उत्तर उन लोगों के लिए बहुत सही है जो महसूस नहीं करते कि उन्हें ऐसा करने की आवश्यकता नहीं है। हालांकि, मेरे मामले में, मैं एक बाहरी संसाधन धारण कर रहा हूं, जो इस सवाल को जन्म देता है। :) – moswald

+1

मामूली रिफैक्टरिंग शायद उस मुद्दे को खत्म कर देगी, हालांकि। मैं इसके बारे में सोचूंगा। मुझे नहीं पता कि अंतिम परिणाम सरल होगा या नहीं। – moswald

+0

दुर्भाग्यवश, सभी कंपाइलर डिफ़ॉल्ट चाल कन्स्ट्रक्टर/असाइनमेंट ऑपरेटर उत्पन्न नहीं करते हैं। वीसी -2010 वीसी2011 के बारे में निश्चित नहीं है। – lapk

2

यही वह है जो मैंने साथ आया है, लेकिन मुझे नहीं पता कि वहां एक और इष्टतम समाधान है या नहीं।

class MyClass 
{ 
    void Swap(MyClass &other) 
    { 
     std::swap(other.member, member); 
    } 

public: 
    MyClass() 
     : member() 
    { 
    } 

    MyClass(const MyClass &other) 
     : member(other.member) 
    { 
    } 

    MyClass(MyClass &&other) 
     : member(std::move(other.member)) 
    { 
    } 

    MyClass &operator=(MyClass other) 
    { 
     other.Swap(*this); 
     return *this; 
    } 

private: 
    int member; 
}; 
+0

आप स्वैप निजी क्यों बनायेंगे? – ronag

+0

@ronag: मेरी असली दुनिया की कक्षा में, इसके लिए कोई वास्तविक आवश्यकता नहीं है (अभी तक)। मैं एक एपीआई का पर्दाफाश नहीं करना चाहूंगा कि कोई बाद में आ सकता है और संभवतः गलत तरीके से उपयोग कर सकता है (हालांकि मुझे नहीं पता कि वे स्वैप के रूप में सरल कुछ कैसे दुरुपयोग कर सकते हैं)। – moswald

+4

यदि 'मायक्लास' वास्तव में इन अर्थशास्त्रों को माना जाता है, तो शायद यह संभवतः सबसे खराब (सबसे खराब प्रदर्शन करने वाला) तरीका है जो विशेष सदस्यों को लिखना संभव है और फिर भी इसे सही माना जाता है। क्षमा करें, इतना प्रत्यक्ष होने के लिए, लेकिन मैंने सोचा कि आपको पता होना चाहिए। –

5

सबसे अच्छा तरीका यह है कि संकलक उन्हें सभी उत्पन्न करने दें। यह सी ++ 03 में भी सबसे अच्छा तरीका था और यदि आप ऐसा करने में कामयाब रहे हैं तो आप C++ 03 कक्षाएं स्वचालित रूप से "चाल-सक्षम" बन जाते हैं जब आप C++ 11 पर माइग्रेट करते हैं।

अधिकांश संसाधन प्रबंधन मुद्दों को केवल गैर-प्रतिलिपि बनाने वाले और एकल संसाधन प्रबंधन वर्गों के विनाशकों को लिखकर हल किया जा सकता है और फिर इनका उपयोग करके समग्र कक्षाएं बना सकते हैं, साथ ही स्मार्ट पॉइंटर्स (उदाहरण के लिए std::unique_ptr) और कंटेनर कक्षाएं अमीर वस्तुओं का निर्माण करने के लिए ।

4

बजना/libc का उपयोग ++:

#include <chrono> 
#include <iostream> 
#include <vector> 

#if SLOW_DOWN 

class MyClass 
{ 
    void Swap(MyClass &other) 
    { 
     std::swap(other.member, member); 
    } 

public: 
    MyClass() 
     : member() 
    { 
    } 

    MyClass(const MyClass &other) 
     : member(other.member) 
    { 
    } 

    MyClass(MyClass &&other) 
     : member(std::move(other.member)) 
    { 
    } 

    MyClass &operator=(MyClass other) 
    { 
     other.Swap(*this); 
     return *this; 
    } 

private: 
    int member; 
}; 

#else 

class MyClass 
{ 
public: 
    MyClass() 
     : member() 
    { 
    } 

private: 
    int member; 
}; 

#endif 

int main() 
{ 
    typedef std::chrono::high_resolution_clock Clock; 
    typedef std::chrono::duration<float, std::milli> ms; 
    auto t0 = Clock::now(); 
    for (int k = 0; k < 100; ++k) 
    { 
     std::vector<MyClass> v; 
     for (int i = 0; i < 1000000; ++i) 
      v.push_back(MyClass()); 
    } 
    auto t1 = Clock::now(); 
    std::cout << ms(t1-t0).count() << " ms\n"; 
} 

$ clang++ -stdlib=libc++ -std=c++11 -O3 -DSLOW_DOWN test.cpp 
$ a.out 
519.736 ms 
$ a.out 
517.036 ms 
$ a.out 
524.443 ms 

$ clang++ -stdlib=libc++ -std=c++11 -O3 test.cpp 
$ a.out 
463.968 ms 
$ a.out 
458.702 ms 
$ a.out 
464.441 ms 

यह इस परीक्षण पर लगभग 12% की गति अंतर तरह दिखता है।

स्पष्टीकरण: इन परिभाषाओं में से एक में एक छोटी प्रतिलिपि निर्माता और कॉपी असाइनमेंट ऑपरेटर है। दूसरा नहीं है। सी ++ 11 में "ट्रिविअल" का वास्तविक अर्थ है। इसका मतलब है कि कार्यान्वयन को आपकी कक्षा की प्रतिलिपि बनाने के लिए memcpy का उपयोग करने की अनुमति है। या अपनी कक्षा के बड़े सरणी भी कॉपी करने के लिए। तो यदि आप कर सकते हैं तो अपने विशेष सदस्यों को तुच्छ बनाना सबसे अच्छा है। इसका मतलब है कि संकलक उन्हें परिभाषित करने दें। यद्यपि आप चाहें तो भी उन्हें = default के साथ घोषित कर सकते हैं।

+0

हॉवर्ड, 'SLOW_DOWN' केवल एक बहुत ही अक्षम संस्करण है। यह कंपाइलर जेनरेट कन्स्ट्रक्टर इत्यादि की तुलना के बारे में भी नहीं है ... ideone.com पर दो कोड यहां दिए गए हैं: [पहला एक] (http://ideone.com/WQVXK) ओपी के संस्करण 'टी एंड ऑपरेटर (टी)' का उपयोग करता है, [ दूसरा] (http://ideone.com/SXgVN) स्पष्ट रूप से परिभाषित 'टी एंड (टी एंड&) 'और' टी एंड ऑपरेटर = (टी एंड कॉम) 'का उपयोग करता है। 'के बीच उत्पादन की तुलना करें -----------' ... – lapk

+1

@AzzA: मैं बस एक मुझे लगता है कि बेहतर है करने के लिए ओ पी के समाधान की तुलना कर रहा हूँ। न कुछ ज्यादा, न कुछ कम। –

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