2009-12-25 9 views
5
class mystring { 
friend ostream& operator<<(ostream &out, const mystring ss) { 
     out << ss.s; 
     return out; 
    } 
private: 
    string s; 
public: 
    mystring(const char ss[]) { 
     cout << "constructing mystring : " << ss << endl; 
     s = ss; 
    } 
}; 

void outputStringByRef(const mystring &ss) { 
cout << "outputString(const string&) " << ss << endl; 
} 

void outputStringByVal(const mystring ss) { 
cout << "outputString(const string) " << ss << endl; 
} 

int main(void) { 
    outputStringByRef("string by reference"); 
    outputStringByVal("string by value"); 
    outputStringByRef(mystring("string by reference explict call mystring consructor")); 
    outputStringByVal(mystring("string by value explict call mystring constructor")); 
} ///:~ 

उपर्युक्त उदाहरण को ध्यान में रखते हुए, हम पास-दर-संदर्भ चर को संशोधित नहीं कर सके, न ही हम पास-दर-मूल्य चर को संशोधित कर सकते हैं। प्रत्येक विधियों का आउटपुट समान है। चूंकि इन दोनों के बीच कोई अंतर नहीं है विधि, सी ++ दोनों विधियों का समर्थन क्यों करते हैं?एफ (कॉन्स स्ट्रिंग और) और एफ (कॉन्स स्ट्रिंग) के बीच कोई अंतर?

धन्यवाद।

+2

संबंधित http://stackoverflow.com/questions/1567138/const-t-arg-vs-t-arg/1567186#1567186 –

उत्तर

19

दोनों के बीच एक अंतर है। पर विचार करें निम्नलिखित:

#include <iostream> 
#include <string> 
using std::string; 

string g_value; 

void callback() { 
    g_value = "blue"; 
} 

void ProcessStringByRef(const string &s) { 
    callback(); 
    std::cout << s << "\n"; 
} 

void ProcessStringByValue(const string s) { 
    callback(); 
    std::cout << s << "\n"; 
} 

int main() { 
    g_value = "red"; 
    ProcessStringByValue(g_value); 
    g_value = "red"; 
    ProcessStringByRef(g_value); 
} 

आउटपुट:

red 
blue 

सिर्फ इसलिए कि एक संदर्भ में एक समारोह के अंदर स्थिरांक है, इसका मतलब यह नहीं है कि referand (अन्य संदर्भ के माध्यम से बदला नहीं जा सकता से एक की स्थिति ऑब्जेक्ट में कई संदर्भ या पॉइंटर्स होने पर "एलियासिंग" कहा जाता है)। इस प्रकार संदर्भ के मामले में, एक कॉन्स्टेंस संदर्भ पास करने और एक कॉन्स वैल्यू गुजरने के बीच एक अलग है, कॉल के बाद ऑब्जेक्ट बदल सकता है। मूल्य के मामले में, कैली की एक निजी प्रति है, जो नहीं बदलेगी।

चूंकि वे अलग-अलग चीजें करते हैं, सी ++ आपको वह चुनने देता है जो आप चाहते हैं।

किसी भी तरह के प्रदर्शन के परिणाम हैं - जब आप मूल्य से गुजरते हैं, तो एक प्रतिलिपि बनाना होता है, जिसका खर्च होता है। लेकिन संकलक तब जानता है कि केवल आपके फ़ंक्शन में उस प्रतिलिपि के संदर्भ हो सकते हैं, जो अन्य अनुकूलन की अनुमति दे सकता है। ProcessStringByRef callback() वापस लौटने तक प्रिंटिंग के लिए स्ट्रिंग की सामग्री लोड नहीं कर सकता है। ProcessStringByValue कर सकता है, अगर संकलक ऐसा करने लगता है तो तेज़ है।

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

1

outputStringByRef के साथ आपको यह सुनिश्चित करना होगा कि जब आप outputStringByRef के लिए जीवित और अपरिवर्तित रहने के लिए संदर्भ को पारित कर रहे हैं, तो इसकी आवश्यकता है। outputStringByVal के साथ आप जिस चर को पारित कर सकते हैं वह मर सकता है या दायरे से बाहर निकल सकता है और उस प्रतिलिपि की प्रतिलिपि अभी भी ठीक है।

8

f(const string&)const संदर्भ द्वारा स्ट्रिंग लेता है संदर्भ: f संदर्भ द्वारा पारित स्ट्रिंग ऑब्जेक्ट पर सीधे चल रहा है: इसमें कोई प्रति शामिल नहीं है। const हालांकि मूल ऑब्जेक्ट में संशोधन रोकता है।

f(const string) एक स्ट्रिंग मान लेता है, जिसका अर्थ है f मूल स्ट्रिंग की एक प्रति दी गई है। यहां तक ​​कि यदि आप const ड्रॉप करते हैं, तो मूल्य से गुजरते समय, f रिटर्न पर स्ट्रिंग में कोई भी संशोधन खो जाता है।

मुझे नहीं पता कि "सी ++ दोनों तरीकों का समर्थन क्यों करते हैं?" यह लागू होने वाले सामान्य अधिभार नियम हैं।

+0

'सी ++ दोनों तरीकों का समर्थन क्यों करते हैं, क्योंकि पहले, मैंने नहीं देखा इन दो कार्यों के बीच का अंतर। मैंने सोचा कि दोनों एक जैसे हैं। इसके बाद असेंबली जी ++ और एमएसवीसी उत्पन्न होकर, स्थिर के समान पते को पार करके यह वही तरीका लगता है। – Jichao

3

अपने वस्तु एक mutable सदस्य है, जो एक स्थिरांक संदर्भ

6

f(string s) मूल्य द्वारा पारित स्ट्रिंग है, दूसरे शब्दों में यह एक कॉपी बन जाती और स्ट्रिंग के मूल्य के साथ यह initializes साथ भी संशोधित किया जा सकता हो सकता है आप उत्तीर्ण करना। प्रतिलिपि में कोई भी परिवर्तन, फ़ंक्शन को कॉल करने के लिए पारित मूल स्ट्रिंग पर प्रचारित नहीं किया जाएगा। f(const string s) कॉन्स अनावश्यक है क्योंकि आप मूल मूल्य को बदल नहीं सकते हैं।

f(const string& s) में स्ट्रिंग की प्रतिलिपि नहीं बनाई गई है लेकिन आप इसका संदर्भ देते हैं। यह आमतौर पर तब किया जाता है जब आपके पास बड़ी वस्तु होती है ताकि "पास-बाय-वैल्यू" ओवरहेड उत्पन्न कर सके (यही कारण है कि सी ++ दोनों विधियों का समर्थन करता है)। संदर्भ द्वारा पास करने का अर्थ है कि आप अपने द्वारा पारित "बड़ी" ऑब्जेक्ट का मान बदल सकते हैं, लेकिन कॉन्स्ट विनिर्देशक के कारण, आप इसे संशोधित नहीं कर सकते हैं। यह एक तरह का "संरक्षण" है।

+1

'कॉन्स्ट स्ट्रिंग एस' की आवश्यकता हो सकती है यदि यह इरादा – philsquared

+0

का इरादा बताता है 'स्ट्रिंग एस' और' कॉन्स्ट स्ट्रिंग एस 'के बीच इरादे में क्या अंतर है? –

+0

मार्टिन बी: ​​यह फ़ंक्शन के लिए आंतरिक विवरण है और जो भी उस फ़ंक्शन को लिखने में मदद कर सकता है, लेकिन हस्ताक्षर संगत हैं (आप 'void f (int const) कर सकते हैं; typedef void (* FP) (int); एफपी पी = & f; ') और कॉलर्स के लिए इसका कोई महत्व नहीं है। –

1

फ़ंक्शन के भीतर स्ट्रिंग को संशोधित करने में सक्षम होने के संदर्भ में (लगभग) कोई अंतर नहीं है। हालांकि पारित होने के मामले में एक बड़ा अंतर है। const mystring &ss ओवरलोड स्ट्रिंग के लिए एक कॉन्स्ट संदर्भ लेता है। हालांकि यह इसे संशोधित नहीं कर सकता है, यह वही स्मृति संबोधित किया जा रहा है। यदि स्ट्रिंग लंबा है तो यह एक बड़ा कारक हो सकता है (माना जाता है कि copy-on-write का उपयोग करके स्ट्रिंग लागू नहीं किया गया है)। const mystring ss फॉर्म स्ट्रिंग की प्रति बना रहा है, इसलिए अलग-अलग मेमोरी को संबोधित किया जाएगा।

असल const mystring &ss रूप स्ट्रिंग को बदल सकता है, अगर एक const_cast<mystring&> इस्तेमाल किया गया था - हालांकि मैं यहाँ कि सिफारिश नहीं होगा।

1

कल्पना कीजिए कि आप एक वस्तु की प्रतिलिपि नहीं बनाई जा सकती है, प्रिंट करना चाहते हैं:

Thread th; 
// ... 
cout << th; // print out informations... 

संदर्भ संस्करण धागा कॉपी नहीं होगा, लेकिन th का पता लेने के लिए और यह करने के लिए एक उपनाम का निर्माण करेगा।दूसरा संस्करण मूल्य से गुजरते समय धागे की प्रतिलिपि बनाने का प्रयास करेगा - लेकिन प्रतिलिपि ऐसी वस्तु के लिए समझदार नहीं हो सकती है (तब एक अतिरिक्त धागा होगा?)।

यह आप के बारे में सोच में मदद मिल सकती एक वस्तु क्लोनिंग के रूप में एक वस्तु की नकल। सी ++ में उपरोक्त th की प्रतिलिपि का मतलब केवल जावा में एक ही ऑब्जेक्ट के लिए एक और हैंडल नहीं है - इसका मतलब है कि निहित वस्तु को स्पष्ट रूप से क्लोन करना है, और इसकी पूरी प्रतिलिपि है। तो सवाल यह है कि "जावा Object.clone दोनों का समर्थन क्यों करता है और संदर्भों की प्रतिलिपि बनाता है?" - दोनों के अलग-अलग उद्देश्य हैं।

और फिर भी प्रदर्शन की बात है, आखिरकार। संसाधन भूखे ऑब्जेक्ट्स के लिए आप हर बार कुछ पास करते समय कॉपी नहीं करना चाहते हैं।