2009-09-07 7 views
8

क्यों है निम्नलिखित कोड प्रिंट "XXY"? स्थानीय चर के पूरे कार्य के दायरे में नहीं रहना चाहिए? क्या मैं इस तरह के व्यवहार का उपयोग कर सकता हूं या भविष्य में सी ++ मानक में बदल दिया जाएगा?स्थानीय चर गुंजाइश सवाल

मैंने सोचा था कि सी ++ स्टैंडर्ड 3.3.2 के अनुसार, "एक नाम एक ब्लॉक में घोषणा की है कि ब्लॉक करने के लिए स्थानीय है। इसकी संभावित गुंजाइश घोषणा के स्थान पर शुरू होता है और इसके कथात्मक क्षेत्र के अंत में समाप्त होता है।"

#include <iostream> 
using namespace std; 

class MyClass 
{ 
public: 
    MyClass(int) { cout << "x" << endl; }; 
    ~MyClass() { cout << "x" << endl; }; 
}; 

int main(int argc,char* argv[]) 
{ 
    MyClass (12345); 
// changing it to the following will change the behavior 
//MyClass m(12345); 
    cout << "Y" << endl; 

    return 0; 
} 

प्रतिक्रियाओं के आधार पर मैं मान सकते हैं कि MyClass(12345); अभिव्यक्ति (और गुंजाइश) है। यह समझ में आता है। इसलिए मैं उम्मीद करते हैं कि निम्न कोड "xYx" हमेशा प्रिंट होगा:

MyClass (12345), cout << "Y" << endl; 

और यह इतनी प्रतिस्थापन बनाने के लिए अनुमति दी है: दायरे में रखते हुए बिना

// this much strings with explicit scope 
{ 
    boost::scoped_lock lock(my_mutex); 
    int x = some_func(); // should be protected in multi-threaded program 
} 
// mutex released here 

//  

// I can replace with the following one string: 
int x = boost::scoped_lock (my_mutex), some_func(); // still multi-thread safe 
// mutex released here 
+2

आपके प्रश्न में उत्तर है: * एक नाम घोषित किया गया ... *। कोई नाम नहीं है! – quamrana

+0

इस उदाहरण में: MyClass (12345) एक फ़ंक्शन शैली कास्ट है, घोषणा नहीं। –

+0

फिर भी, उदाहरण – artificialidiot

उत्तर

4

आपने मानक सही ढंग से उद्धृत किया है। मुझे पर जोर डालते हैं:

एक नामघोषित एक ब्लॉक में है कि ब्लॉक करने के लिए स्थानीय है। इसका संभावित दायरा घोषणा के अपने बिंदु पर शुरू होता है और इसके घोषणात्मक क्षेत्र के अंत में समाप्त होता है।

आपने वास्तव में नाम घोषित नहीं किया था। आपका लाइन

MyClass (12345); 

भी एक घोषणा शामिल नहीं है! इसमें क्या एक अभिव्यक्ति है जो MyClass का एक उदाहरण बनाता है, अभिव्यक्ति की गणना करता है (हालांकि, इस विशेष मामले में गणना करने के लिए कुछ भी नहीं है), और इसका परिणाम void पर जाता है, और वहां बनाए गए ऑब्जेक्ट को नष्ट कर देता है।

एक कम दुविधापूर्ण की तरह ध्वनि जाएगा

call_a_function(MyClass(12345)); 

आप इसे कई बार देखा था और पता है कि यह कैसे काम करता है, है ना?

+0

चश्मे "संभावित" दायरे के बारे में भी बात करते हैं। गारंटी नहीं है"। तो संकलक पहले ऑब्जेक्ट को नष्ट कर सकता है अगर इसका दायरा उपयोग नहीं किया जाता है। –

+4

"संभावित दायरे" का एक सटीक अर्थ है: यह दायरा और वह हिस्सों है जहां नाम पुन: घोषणा के कारण छिपा हुआ है। एक कार्यान्वयन पहले किसी ऑब्जेक्ट को नष्ट नहीं कर सकता है, भले ही इसका उपयोग न किया जाए (जो आरएआईआई बीटीडब्ल्यू का कुछ प्रमुख उपयोग तोड़ देगा)। – AProgrammer

8

आप वास्तव में एक वस्तु बना रहे हैं, इसलिए इसे बनाया जाने के ठीक बाद इसे नष्ट कर दिया जाता है। इसलिए आप जिस व्यवहार का अनुभव कर रहे हैं।

आप निर्मित ऑब्जेक्ट तक नहीं पहुंच सकते हैं तो संकलक इसे चारों ओर क्यों रखेगा?

16

वस्तु अपने

MyClass(12345); 

में बनाए गए एक अस्थायी वस्तु जो केवल कि अभिव्यक्ति में जिंदा है;

MyClass m(12345); 

एक वस्तु है जो पूरे ब्लॉक के लिए जीवित है।

+0

के लिए कोई नाम नहीं है * वह अभिव्यक्ति * मुख्य कार्य नहीं है? –

+1

यह मेरे लिए सही लगता है। एक और चीज अनुकूलन हो सकती है: भले ही आप दूसरी विधि का उपयोग करते हैं, फिर भी कंपाइलर इसे पहले अनुकूलित कर सकता है। – Anna

+0

@ अन्ना, दूसरे मामले में यह हमेशा xYx प्रिंट करेगा। –

5

अपने अन्य सवालों के जवाब देने के लिए। निम्नलिखित अल्पविराम ऑपरेटर का आविष्कार है।यह MyClass अस्थायी बनाता है, जिसमें इसके कन्स्ट्रक्टर को कॉल करना शामिल है। इसके बाद यह दूसरी अभिव्यक्ति cout << "Y" << endl का मूल्यांकन करता है जो वाई को प्रिंट करेगा। फिर, पूर्ण अभिव्यक्ति के अंत में, अस्थायी को नष्ट कर देगा, जो इसके विनाशक को बुलाएगा। तो आपकी उम्मीदें सही थीं।

MyClass (12345), cout << "Y" << endl; 

काम करने के लिए निम्न के लिए, आप, कोष्ठक जोड़ना चाहिए क्योंकि अल्पविराम घोषणाओं में एक पूर्वनिर्धारित अर्थ नहीं है। यह एक some_func एक int लौटने और कोई पैरामीटर नहीं लेना शुरू कर देगा और ऑब्जेक्ट को x पर असाइन करेगा। कोष्ठक का उपयोग करके, आप कहते हैं कि पूरी बात इसके बजाय एकमात्र कॉमा ऑपरेटर अभिव्यक्ति है।

int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe 

यह ध्यान दिया जाना चाहिए कि निम्नलिखित दो पंक्तियां बराबर हैं। पहले कन्स्ट्रक्टर तर्क के रूप में my_mutex का उपयोग करके एक अस्थायी अनामित वस्तु बनाते हैं, लेकिन इसके बजाय नाम के चारों ओर कोष्ठक अनावश्यक हैं। वाक्यविन्यास आपको भ्रमित मत होने दें।

boost::scoped_lock(my_mutex); 
boost::scoped_lock my_mutex; 

मैं शब्दों गुंजाइश और जीवन भर के दुरुपयोग देखा है।

  • Scope है जहां इसके नाम योग्यता के बिना एक नाम का उल्लेख कर सकते। नामों में स्कोप होते हैं, और वस्तुओं को परिभाषित करने के लिए उपयोग किए गए नाम के दायरे का उत्तराधिकारी होता है (इस प्रकार कभी-कभी मानक "स्थानीय वस्तु" कहता है)। एक अस्थायी वस्तु का कोई दायरा नहीं है, क्योंकि इसका कोई नाम नहीं है। इसी प्रकार, new द्वारा बनाई गई वस्तु का कोई दायरा नहीं है। दायरा एक संकलित समय संपत्ति है। इस शब्द को अक्सर मानक में दुरुपयोग किया जाता है, this defect report देखें, इसलिए यह वास्तविक अर्थ खोजने में काफी उलझन में है।

  • Lifetime एक रनटाइम संपत्ति है। इसका मतलब है कि जब वस्तु स्थापित की जाती है और उपयोग के लिए तैयार होती है। एक वर्ग प्रकार की वस्तु के लिए, जीवनकाल तब शुरू होता है जब निर्माता निष्पादन समाप्त करता है, और यह तब समाप्त होता है जब विनाशक निष्पादन शुरू करता है। लाइफटाइम अक्सर गुंजाइश के साथ भ्रमित होता है, हालांकि ये दो चीजें पूरी तरह से अलग हैं।

    अस्थायी जीवनकाल निश्चित रूप से परिभाषित किया गया है। उनमें से अधिकतर पूर्ण अभिव्यक्ति के मूल्यांकन के बाद आजीवन समाप्त होते हैं (जैसे, ऊपर के अल्पविराम ऑपरेटर, या असाइनमेंट अभिव्यक्ति)। अस्थायी संदर्भों के संदर्भ में बंधे जा सकते हैं जो उनके जीवनकाल को बढ़ाएंगे। अपवादों में फेंकने वाले ऑब्जेक्ट्स भी अस्थायी हैं, और उनका जीवनकाल समाप्त होता है जब उनके लिए कोई हैंडलर नहीं होता है।