2012-08-23 13 views
18

कुछ मिनट पहले this question देखने के बाद, मुझे आश्चर्य हुआ कि क्यों भाषा डिजाइनर इसे अनुमति देते हैं क्योंकि यह निजी डेटा के अप्रत्यक्ष संशोधन की अनुमति देता है। उदाहरण के रूप मेंसी ++ इस दृष्टिकोण का उपयोग करके निजी सदस्यों को संशोधित करने की अनुमति क्यों देता है?

class TestClass { 
    private: 
    int cc; 
    public: 
    TestClass(int i) : cc(i) {}; 
}; 

TestClass cc(5); 
int* pp = (int*)&cc; 
*pp = 70;    // private member has been modified 

मैंने उपरोक्त कोड का परीक्षण किया और वास्तव में निजी डेटा संशोधित किया गया है। क्या इस बारे में कोई स्पष्टीकरण है कि ऐसा क्यों होने की अनुमति है या यह भाषा में सिर्फ एक निरीक्षण है? ऐसा लगता है कि निजी डेटा सदस्यों के उपयोग को सीधे कमजोर कर दिया जाता है।

+21

सी ++ में, जिस क्षण आप इसका उपयोग करते हैं, आपने संकलक को बताया है कि आपको इसकी प्रकार की सुरक्षा और जांच की परवाह नहीं है। यह नेटवर्क सुरक्षा नहीं है; यदि आप जोर देते हैं, तो संकलक आपको अपनी खुद की सामग्री को बर्बाद कर देगा, जैसे आपका कोड करता है। – tenfour

+0

@tenfour हाँ मुझे एहसास हुआ कि मैं थोड़ा आश्चर्यचकित था कि यह संभव था। मैंने सी कास्ट को static_cast में बदल दिया और फिर संकलक ने शिकायत की। – mathematician1975

+2

आपको वास्तव में सी ++ में सी कास्ट नहीं करना चाहिए। असल में वे कंपाइलर को 'दूर जाने के बारे में बता रहे हैं, मुझे पता है कि मैं क्या कर रहा हूं, और मैं सभी जिम्मेदारी लेता हूं'। वे reinterpret_cast से भी बदतर हैं (जो भी रास्ते से काम करते थे। Static_cast काम नहीं करेगा क्योंकि आप जो भी पूछ रहे हैं उसे करने के लिए कोई अच्छी तरह से परिभाषित तरीका नहीं है) –

उत्तर

31

क्योंकि, जैसा कि बजेर्न ने इसे रखा है, सी ++ मर्फी के खिलाफ रक्षा करने के लिए डिज़ाइन किया गया है, मच्छियाली नहीं।

दूसरे शब्दों में, यह आपको दुर्घटनाओं से बचाने के लिए माना जाता है - लेकिन अगर आप इसे किसी भी काम पर जाने के लिए जाते हैं (जैसे कि कास्ट का उपयोग करना) यह आपको रोकने की कोशिश भी नहीं कर रहा है।

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

संपादित करें: प्रश्न के अनुसार @Xeo चर्चा करता है, मानक कहता है कि "सभी सार्वजनिक अभिगम नियंत्रण" के बजाय मानक "समान पहुंच नियंत्रण" क्यों है, जवाब लंबा और थोड़ा कष्टप्रद है।

आइए वापस शुरुआत करने के लिए कदम और की तरह एक struct पर विचार करें:

struct X { 
    int a; 
    int b; 
}; 

सी हमेशा इस तरह एक struct के लिए कुछ नियम था। एक यह है कि संरचना के उदाहरण में, संरचना के पते को a के पते के बराबर होना चाहिए, ताकि आप सूचक को int पर पॉइंटर पर एक पॉइंटर डालें और अच्छी तरह से परिभाषित परिणामों के साथ a तक पहुंच सकें। दूसरा यह है कि सदस्यों को स्मृति में उसी क्रम में व्यवस्थित किया जाना चाहिए क्योंकि उन्हें संरचना में परिभाषित किया गया है (हालांकि संकलक उनके बीच पैडिंग डालने के लिए स्वतंत्र है)।

सी ++ के लिए, विशेष रूप से मौजूदा सी structs के लिए बनाए रखने का इरादा था। साथ ही, एक स्पष्ट इरादा था कि यदि संकलक private (और protected) को रन-टाइम पर लागू करना चाहता था, तो ऐसा करना आसान होना चाहिए (उचित रूप से कुशलतापूर्वक)।

इसलिए, कुछ दिया की तरह:

struct Y { 
    int a; 
    int b; 
private: 
    int c; 
    int d; 
public: 
    int e; 

    // code to use `c` and `d` goes here. 
}; 

संकलक Y.a और Y.b के संबंध में सी के रूप में ही नियम बनाए रखने के लिए आवश्यक होना चाहिए। इसी समय, अगर यह रन टाइम पर पहुँच लागू करने के लिए जा रहा है, यह स्मृति में एक साथ सभी सार्वजनिक चर ले जाने के लिए इतना लेआउट होगा चाहते हो सकता है और अधिक की तरह:

struct Z { 
    int a; 
    int b; 
    int e; 
private: 
    int c; 
    int d; 
    // code to use `c` and `d` goes here. 
}; 

तब, जब उस पर बातें लागू करने है रन-टाइम, यह मूल रूप से if (offset > 3 * sizeof(int)) access_violation();

मेरे ज्ञान के लिए किसी ने कभी ऐसा नहीं किया है, और मुझे यकीन नहीं है कि शेष मानक वास्तव में इसे अनुमति देता है, लेकिन ऐसा लगता है कि कम से कम आधा- उस रेखा के साथ एक विचार के जीवाणु बनाया।

उन दोनों को लागू करने के लिए, सी ++ 98 कहा Y::a और Y::b स्मृति में इसी क्रम में होना ही था, और Y::a struct (अर्थात, सी की तरह नियम) की शुरुआत में होना ही था। लेकिन, हस्तक्षेप पहुंच विनिर्देशकों के कारण, Y::c और Y::e अब एक दूसरे के सापेक्ष क्रम में नहीं होना चाहिए था।दूसरे शब्दों में, उनके बीच एक एक्सेस विनिर्देश के बिना परिभाषित सभी लगातार चर को एक साथ समूहीकृत किया गया था, संकलक उन समूहों को पुनर्व्यवस्थित करने के लिए स्वतंत्र था (लेकिन अभी भी शुरुआत में पहले को रखना था)।

यह ठीक था जब तक कि कुछ झटका (यानी, मुझे) ने इंगित किया कि नियमों के लिखे गए तरीके में एक और छोटी समस्या थी। अगर मैंने कोड लिखा:

struct A { 
    int a; 
public: 
    int b; 
public: 
    int c; 
public: 
    int d; 
}; 

... आप स्वयं के विरोधाभास के साथ समाप्त हो गए। एक ओर, यह अभी भी आधिकारिक तौर पर एक पीओडी संरचना था, इसलिए सी-जैसे नियम लागू किए जाने थे - लेकिन चूंकि आपने सदस्यों के बीच पहुंच विनिर्देशकों (स्वीकार्य रूप से अर्थहीन) थे, इसलिए सदस्यों को पुनर्व्यवस्थित करने के लिए संकलक अनुमति भी दी गई, इस प्रकार सी-जैसे नियमों को तोड़ना उनका इरादा था।

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

+0

दरअसल, यह सख्ती से सच नहीं है। मानक इस विशिष्ट कलाकार को पूरी तरह से अच्छी तरह से परिभाषित करता है। – Puppy

+4

@DeadMG: और आप कैसे सोचते हैं कि मैंने जो भी कहा है उसके विपरीत? मैंने बहुत विशेष रूप से * नहीं कहा * परिणाम अपरिभाषित था, या कुछ भी करीब - केवल इतना है कि आपको एक कास्ट का उपयोग करना होगा। –

+0

ठीक है, यह इंगित करने के लिए थोड़ा अपमानजनक है कि मानक इस मामले में मर्फी के खिलाफ सुरक्षा करना चाहता है, जब इसका इरादा स्पष्ट रूप से कुछ भी नहीं है, न ही यह मानक के कानूनी नियमों का एक विचलन है। यहां कोई मर्फी/माचियावेली नियम नहीं है- यह एक सरल, अच्छी तरह से परिभाषित कलाकार है और यही वह है। – Puppy

0

ऐसा इसलिए है क्योंकि आप स्मृति में हेरफेर कर रहे हैं जहां आपकी कक्षा स्मृति में स्थित है। आपके मामले में यह निजी सदस्य को इस स्मृति स्थान पर संग्रहीत करने के लिए होता है ताकि आप इसे बदल सकें। ऐसा करना बहुत अच्छा विचार नहीं है क्योंकि अब आप जानते हैं कि वस्तु स्मृति में कैसे संग्रहीत की जाएगी।

14

सी के पीछे की संगतता के कारण, जहां आप एक ही काम कर सकते हैं। क्यों इस यूबी नहीं है और वास्तव में मानक द्वारा अनुमति दी है


सभी लोगों को सोच के लिए, यहाँ है:

:

पहले, TestClass एक मानक लेआउट वर्ग (§9 [class] p7) है

मानक-लेआउट वर्ग एक वर्ग है जो:

    गैर स्थैतिक डेटा सदस्य प्रकार का है 'int'
  • कोई आभासी है:
  • प्रकार गैर मानक लेआउट वर्ग (या इस तरह के प्रकार के सरणी) या संदर्भ, // ठीक करने का कोई गैर स्थैतिक डेटा सदस्य हैं फ़ंक्शंस (10.3) और वर्चुअल बेस क्लासेस (10.1), // ठीक
  • में सभी गैर-स्थिर डेटा सदस्यों के लिए समान पहुंच नियंत्रण (क्लॉज 11) है, // ठीक है, सभी गैर-स्थैतिक डेटा सदस्य (1) 'निजी'
  • में कोई गैर-मानक-लेआउट बेस कक्षाएं नहीं हैं, // ठीक है, कोई बेस क्लास
  • या तो सबसे व्युत्पन्न वर्ग में कोई गैर-स्थैतिक डेटा सदस्य नहीं है और गैर-स्थैतिक डेटा सदस्यों वाले अधिकांश बेस क्लास में, या गैर स्थैतिक डेटा सदस्यों के साथ कोई आधार वर्ग नहीं है, और // ठीक है, फिर से कोई बेस क्लास नहीं है
  • में पहले गैर-स्थैतिक डेटा सदस्य के समान प्रकार के आधार वर्ग नहीं हैं।

    एक संकेतक के लिए: // ठीक है, कोई आधार वर्ग फिर से

और उस के साथ, आप reinterpret_cast के वर्ग ने अपना पहला सदस्य के प्रकार (§9.2 [class.mem] p20) करने की अनुमति है सकते हैं एक मानक-लेआउट स्ट्रक्चर ऑब्जेक्ट, जो कि reinterpret_cast का उपयोग करके उपयुक्त रूप से परिवर्तित किया गया है, इसके आरंभिक सदस्य को इंगित करता है (या यदि वह सदस्य थोड़ा-फ़ील्ड है, फिर उस यूनिट में जिसमें वह रहता है) और इसके विपरीत।

आपके मामले में, सी-शैली (int*) डाली एक reinterpret_cast (§5.4 [expr.cast] p4) को हल करता है।

+1

यह सवाल का जवाब नहीं देता है ... यदि मानक पहले सदस्य निजी है तो मानक आपको ऐसा करने की अनुमति क्यों देगा? इसके बारे में सोचने के लिए आओ, मानक इस सुविधा का सक्रिय रूप से समर्थन क्यों करता है? : पी – tenfour

+1

@tenfour: हमेशा के रूप में सी के साथ पिछड़ा-संगतता। मुझसे मत पूछें, हालांकि उन्होंने इसे "समान पहुंच नियंत्रण" क्यों बनाया है और नहीं, "सभी सार्वजनिक पहुंच नियंत्रण" नहीं है। : एस आप सही थे, हालांकि, मैंने शीर्ष पर एक छोटा-छोटा जवाब जोड़ा। :) – Xeo

+0

@Xeo क्योंकि यह कम प्रतिबंधक है? –

1

यदि आपने int *pp = &cc.cc की कोशिश की है तो संकलक आपको एक त्रुटि देगा, तो कंपाइलर ने आपको बताया होगा कि आप किसी निजी सदस्य तक नहीं पहुंच सकते हैं।

अपने कोड में आप सीसी के पते को एक int के सूचक के रूप में पुन: परिभाषित कर रहे हैं। आपने इसे सी स्टाइल तरीका लिखा है, सी ++ शैली का तरीका int* pp = reinterpret_cast<int*>(&cc); होगा। Reinterpret_cast हमेशा एक चेतावनी है कि आप दो बिंदुओं के बीच कास्ट कर रहे हैं जो संबंधित नहीं हैं। ऐसे मामले में आपको यह सुनिश्चित करना होगा कि आप सही कर रहे हैं। आपको अंतर्निहित स्मृति (लेआउट) पता होना चाहिए। कंपाइलर आपको ऐसा करने से नहीं रोकता है, क्योंकि यदि अक्सर इसकी आवश्यकता होती है।

जब कलाकारों को आप कक्षा के बारे में सभी ज्ञान फेंक देते हैं। अब से कंपाइलर पर केवल एक int सूचक दिखता है। बेशक आप पॉइंटर पॉइंट्स मेमोरी तक पहुंच सकते हैं। आपके मामले में, आपके प्लेटफ़ॉर्म पर संकलक सीसी को टेस्ट क्लास ऑब्जेक्ट के पहले एन बाइट्स में डाल दिया गया था, इसलिए टेस्ट क्लास पॉइंटर भी सीसी सदस्य को इंगित करता है।

2

सी के साथ संगतता की अनुमति देने के लिए एक अच्छा कारण है सी ++ परत पर अतिरिक्त पहुंच सुरक्षा।

पर विचार करें:

struct S { 
#ifdef __cplusplus 
private: 
#endif // __cplusplus 
    int i, j; 
#ifdef __cplusplus 
public: 
    int get_i() const { return i; } 
    int get_j() const { return j; } 
#endif // __cplusplus 
}; 

की आवश्यकता होती है कि सी-दिखाई S और सी ++ तक - दिखाई S हो लेआउट संगत, S सी ++ ओर अधिक से अधिक उपयोग सुरक्षा होने के साथ भाषा सीमा पार किया जा सकता है । reinterpret_cast पहुंच सुरक्षा उपversण एक दुर्भाग्यपूर्ण लेकिन आवश्यक अनुशासनिक है।

एक तरफ, समान पहुंच नियंत्रण वाले सभी सदस्यों को रखने पर प्रतिबंध इसलिए है क्योंकि कार्यान्वयन को विभिन्न अभिगम नियंत्रण वाले सदस्यों के सापेक्ष सदस्यों को पुनर्व्यवस्थित करने की अनुमति है। संभवतः कुछ कार्यान्वयन ने सदस्यों को एक ही पहुंच नियंत्रण के साथ रखा, स्वच्छता के लिए; इसका उपयोग पैडिंग को कम करने के लिए भी किया जा सकता है, हालांकि मुझे ऐसा कोई कंपाइलर नहीं पता है जो ऐसा करता है।

1

reinterpret_cast का पूरा उद्देश्य (और एक सी शैली कास्ट reinterpret_cast से भी अधिक शक्तिशाली है) सुरक्षा उपायों के आसपास एक बचने का मार्ग प्रदान करना है।

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

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