2012-06-08 15 views
13

पर विचार करें:क्या मुझे std :: एक move_ptr को एक चालक कन्स्ट्रक्टर में ले जाना चाहिए?

#include <cstdlib> 
#include <memory> 
#include <string> 
#include <vector> 
#include <algorithm> 
#include <iterator> 
using namespace std; 

class Gizmo 
{ 
public: 
    Gizmo() : foo_(shared_ptr<string>(new string("bar"))) {}; 
    Gizmo(Gizmo&& rhs); // Implemented Below 

private: 
    shared_ptr<string> foo_; 
}; 

/* 
// doesn't use std::move 
Gizmo::Gizmo(Gizmo&& rhs) 
: foo_(rhs.foo_) 
{ 
} 
*/ 


// Does use std::move 
Gizmo::Gizmo(Gizmo&& rhs) 
: foo_(std::move(rhs.foo_)) 
{ 
} 

int main() 
{ 
    typedef vector<Gizmo> Gizmos; 
    Gizmos gizmos; 
    generate_n(back_inserter(gizmos), 10000, []() -> Gizmo 
    { 
     Gizmo ret; 
     return ret; 
    }); 

    random_shuffle(gizmos.begin(), gizmos.end()); 

} 

उपरोक्त कोड में, वहाँ Gizmo::Gizmo(Gizmo&&) के दो संस्करण हैं - एक std::move का उपयोग करता है वास्तव में कदमshared_ptr, और अन्य बस प्रतियां shared_ptr करने के लिए।

दोनों संस्करण सतह पर काम करने लगते हैं। एक अंतर (केवल अंतर जो मैं देख सकता हूं) गैर-move संस्करण में है shared_ptr की संदर्भ संख्या अस्थायी रूप से बढ़ी है, लेकिन केवल संक्षेप में।

मैं सामान्य रूप से आगे बढ़ता हूं और moveshared_ptr, लेकिन केवल मेरे कोड में स्पष्ट और सुसंगत होने के लिए। क्या मैं यहां पर विचार कर रहा हूं? क्या मुझे किसी अन्य तकनीकी कारण के लिए दूसरे पर एक संस्करण पसंद करना चाहिए?

+2

एक चालक कन्स्ट्रक्टर में स्थानांतरित करना कम से कम अर्थात् संगत है ... – ildjarn

+1

आप स्ट्रिंग को shared_ptr में क्यों रखते हैं? एक सदस्य-चर के रूप में एक साझा_ptr अक्सर खराब डिजाइन का संकेत है। –

+1

एक चालक कन्स्ट्रक्टर में स्थानांतरित करना संकलक स्वचालित रूप से जेनरेट करेगा। –

उत्तर

16

यहां पर मुख्य मुद्दा shared_ptr में अतिरिक्त परमाणु वृद्धि और कमी के कारण छोटे प्रदर्शन अंतर नहीं है, लेकिन ऑपरेशन के अर्थशास्त्र असंगत हैं जब तक आप कोई कदम नहीं उठाते।

हालांकि धारणा यह है कि shared_ptr की संदर्भ संख्या केवल अस्थायी होगी, भाषा में ऐसी कोई गारंटी नहीं है। जिस स्रोत वस्तु से आप आगे बढ़ रहे हैं वह अस्थायी हो सकता है, लेकिन इसमें अधिक जीवनकाल भी हो सकता है। यह एक नामित ऐसा वैरिएबल rvalue-संदर्भ के लिए casted किया गया है हो सकता है (माना std::move(var)) है, जो मामला नहीं shared_ptr से ले जाकर में आप अभी भी बनाए रखने के स्रोत के साथ स्वामित्व साझा है, और अगर गंतव्य shared_ptr में एक छोटा सा दायरा है तो बिंदु वस्तु का जीवनकाल अनिवार्य रूप से बढ़ाया जाएगा।

+0

मुझे आश्चर्य है कि चाल का उपयोग करते समय, हमें किस हद तक खुद को सोचना चाहिए, "यह कदम एक प्रतिलिपि में गिरावट हो सकती है"? जाहिर है हम एक मनमाने ढंग से 'MoveConstructible' प्रकार' टी' के लिए टेम्पलेट कोड लिखते समय करते हैं, क्योंकि वास्तव में इसे एक चालक कन्स्ट्रक्टर नहीं होना चाहिए, अकेले स्रोत को संशोधित करने दें। इसके अलावा, यह गरीब QoI है यदि किसी स्थानांतरित वस्तु से संसाधन अनावश्यक रूप से होते हैं, इसलिए यदि 'Gizmo' के पास एक चालक कन्स्ट्रक्टर है तो यह एक अच्छा होना चाहिए। लेकिन मुझे लगता है कि यह सम्मेलन और कोडिंग शैली का विषय है कि जब हम नहीं होते हैं तो हम कितने क्रॉस हैं। –

+0

+1, यही वह है जो मैं जेम्स के जवाब पर अपनी टिप्पणी में प्राप्त कर रहा था। अच्छे से कहा। – ildjarn

+0

एक और उदाहरण के लिए, एक चाल असाइनमेंट का वैध कार्यान्वयन 'स्वैप' को कॉल करना है। स्थानांतरित वस्तु से स्थिति अनिर्दिष्ट है, और इसलिए विशेष रूप से स्थानांतरित करने की स्थिति की स्थिति होने की अनुमति है। लेकिन आप थोड़ा उलझन में होंगे कि * स्थानांतरित * ऑब्जेक्ट के संसाधन अनिश्चित काल तक लटकते हैं। –

11

move का उपयोग बेहतर है: इसे प्रतिलिपि से अधिक कुशल होना चाहिए क्योंकि इसे अतिरिक्त परमाणु वृद्धि और संदर्भ गणना की कमी की आवश्यकता नहीं है।

+0

अर्थात् मतभेद भी हैं: क्या कोई व्यक्ति अपने आंतरिक साझा राज्य को जीवित रखने के लिए 'Gizmo' से स्थानांतरित होने की उम्मीद करेगा? व्यक्तिगत रूप से मुझे यह आश्चर्यजनक लगेगा, और एक साझा वस्तु से सभी साझा राज्य को रिलीज़ करने की उम्मीद होगी। – ildjarn

+1

@ildjarn: मैं सहमत हूं, हालांकि यह प्रोग्राम की शुद्धता को किसी भी तरह से प्रभावित नहीं करेगा: वस्तु से स्थानांतरित हो जाएगा अभी भी विनाशकारी और असाइन करने योग्य होगा। –

13

मैंने जेम्स मैकनेलिस के जवाब को उजागर किया। मैं उनके जवाब के बारे में एक टिप्पणी करना चाहता हूं लेकिन मेरी टिप्पणी टिप्पणी प्रारूप में फिट नहीं होगी। तो मैं इसे यहाँ डाल रहा हूँ।

shared_ptr को प्रतिलिपि बनाने के प्रदर्शन प्रभाव को मापने का एक मजेदार तरीका यह है कि vector<shared_ptr<T>> जैसे कुछ का उपयोग करें या उन्हें पूरा समय कॉपी करें या कॉपी करें। अधिकांश कंपाइलरों के पास भाषा मोड निर्दिष्ट करके सैमसंगिक्स को चालू/बंद करने का एक तरीका होता है (उदा। -dd = C++ 03 या -std = C++ 11)।

यहाँ कोड मैं सिर्फ -O3 में परीक्षण किया है:

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

int main() 
{ 
    std::vector<std::shared_ptr<int> > v(10000, std::shared_ptr<int>(new int(3))); 
    typedef std::chrono::high_resolution_clock Clock; 
    typedef Clock::time_point time_point; 
    typedef std::chrono::duration<double, std::micro> us; 
    time_point t0 = Clock::now(); 
    v.erase(v.begin()); 
    time_point t1 = Clock::now(); 
    std::cout << us(t1-t0).count() << "\u00B5s\n"; 
} 

बजना का उपयोग करते हुए/libC++ और -std में = C++ 03 इस बाहर मेरे लिए प्रिंट: करने के लिए

195.368µs 

स्विचिंग - std = C++ 11 मुझे मिलता है:

16.422µs 

आपका माइलेज भिन्न हो सकता है।

+2

+1: बस एक अवलोकन। जब मैंने इसे ऊपर 'मेरी' Gizmo' कक्षा का उपयोग करने के लिए अनुकूलित किया, तो 'move' और non-'move' संस्करण के समय लगभग समान थे। यह एमएसवीसी 10 पर था, 'std :: chrono' के बजाय 'boost :: chrono' का उपयोग करके, संकलित x64 रिलीज। –

+0

@ जॉन डीबलिंग: दिलचस्प। यदि आप समझते हैं कि हमारे परिणामों में ऐसी असमानता क्यों है, तो मुझे इसके बारे में सुनना अच्छा लगेगा। कोशिश करने की एक बात: अपने चालक कन्स्ट्रक्टर पर कोई ध्यान दें। मुझे नहीं पता कि MSVC10 यह लागू करता है या नहीं। मुझे आश्चर्य होगा अगर यह विचार किया गया कि कितना देर से अस्वीकार आया। और मैं वास्तव में वेक्टर :: मिटा सदस्य के लिए एक अंतर बनाने की उम्मीद नहीं करता। लेकिन फिर भी, यह पहली बात है जिसे मैं कोशिश करूंगा। मैं 2.8 गीगाहर्ट्ज इंटेल कोर i5 (64 बिट्स के लिए संकलित) पर चल रहा हूं। क्या आपके परिणाम कुछ सौ माइक्रोसॉन्ड, या कुछ दस माइक्रोसॉन्ड के क्रम पर थे? –

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