क्या यह एक बुरा विचार है कि एक वर्ग विधि है जो कक्षा चर को पारित कर दिया गया है?


14

यहाँ मेरा मतलब है:

class MyClass {
    int arr1[100];
    int arr2[100];
    int len = 100;

    void add(int* x1, int* x2, int size) {
        for (int i = 0; i < size; i++) {
            x1[i] += x2[i];
        }
    }
};

int main() {
    MyClass myInstance;

    // Fill the arrays...

    myInstance.add(myInstance.arr1, myInstance.arr2, myInstance.len);
}

addपहले से ही सभी चर का उपयोग कर सकते हैं जो इसकी आवश्यकता है, क्योंकि यह एक क्लास विधि है, इसलिए यह एक बुरा विचार है? क्या ऐसे कारण हैं कि मुझे ऐसा करना चाहिए या नहीं?


13
यदि आप ऐसा करने का मन कर रहे हैं, तो यह एक बुरे डिजाइन पर इशारा कर रहा है। वर्ग के गुण शायद कहीं और होते हैं।
बिट्सफ्लॉजिक डिक्

11
स्निपेट बहुत छोटा है। मिश्रित आकार का यह स्तर इस आकार के स्निपेट में नहीं होता है। आप डिजाइन के विचारों पर कुछ अच्छे पाठ के साथ भी इसे ठीक कर सकते हैं।
यहोशू

16
मुझे ईमानदारी से समझ में नहीं आता है कि आप ऐसा क्यों करेंगे। आपको क्या लाभ होता है? क्यों न केवल एक नो-आर्ग addपद्धति है जो सीधे अपने इंटर्नल पर संचालित होती है? आखिर क्यों?
इयान केम्प

2
@IanKemp या एक addविधि है जो तर्क लेती है लेकिन एक वर्ग के हिस्से के रूप में मौजूद नहीं है। एक साथ दो सरणियों को जोड़ने के लिए बस एक शुद्ध कार्य।
केविन

क्या जोड़ने की विधि हमेशा arr1 और arr2 जोड़ती है, या इसका उपयोग किसी भी चीज़ में कुछ भी जोड़ने के लिए किया जा सकता है?
user253751

जवाबों:


33

कक्षा के साथ कुछ चीजें हो सकती हैं जो मैं अलग तरीके से करूंगा, लेकिन प्रत्यक्ष प्रश्न का उत्तर देने के लिए मेरा उत्तर होगा

हाँ, यह एक बुरा विचार है

इसका मुख्य कारण यह है कि आपके पास addफ़ंक्शन पर पारित होने का कोई नियंत्रण नहीं है । निश्चित रूप से आपको आशा है कि यह सदस्य सरणियों में से एक है, लेकिन क्या होगा यदि कोई व्यक्ति किसी भिन्न सरणी में गुजरता है जिसका आकार 100 से अधिक है, या आप 100 से अधिक की लंबाई में गुजरते हैं?

क्या होता है कि आपने बफर ओवररन की संभावना पैदा की है। और वह बुरी चीज है।

कुछ और (मेरे लिए) स्पष्ट सवालों के जवाब देने के लिए:

  1. आप C ++ के साथ C शैली सरणियाँ मिला रहे हैं। मैं कोई C ++ गुरु नहीं हूं, लेकिन मुझे पता है कि C ++ में सरणियों को संभालने के बेहतर (सुरक्षित) तरीके हैं

  2. यदि कक्षा में पहले से ही सदस्य चर हैं, तो आपको उन्हें पास करने की आवश्यकता क्यों है? यह वास्तु संबंधी प्रश्न अधिक है।

अधिक सी ++ अनुभव वाले अन्य लोग (मैंने 10 या 15 साल पहले इसका उपयोग बंद कर दिया था) मुद्दों को समझाने के अधिक स्पष्ट तरीके हो सकते हैं, और संभवतः अधिक मुद्दों के साथ भी आएंगे।


वास्तव में, vector<int>अधिक उपयुक्त होगा; लंबाई तब बेकार होगी। वैकल्पिक रूप से const int len, चूंकि चर लंबाई सरणी C ++ मानक का हिस्सा नहीं है (हालांकि कुछ संकलक इसका समर्थन करते हैं, क्योंकि यह C में एक वैकल्पिक विशेषता है)।
क्रिस्टोफ़

5
@Christophe लेखक निश्चित आकार की सरणी का उपयोग कर रहा था, जिसे बदल दिया जाना चाहिए, जिसके साथ std::arrayइसे क्षय नहीं किया जाता है, जब इसे मापदंडों पर पास किया जाता है, तो इसका आकार प्राप्त करने के लिए एक अधिक सुविधाजनक तरीका है, प्रतिलिपि योग्य है और वैकल्पिक सीमा जाँच के साथ उपयोग किया जाता है, जबकि कोई ओवरहेड नहीं होता है सी शैली arrays।
घन

1
@ सार्वजनिक: जब भी मैं कोड को एक मनमाने ढंग से तय आकार (जैसे 100) के साथ घोषित करने के लिए देखता हूं, तो मुझे पूरा यकीन है कि लेखक एक शुरुआती है जिसे इस बकवास के निहितार्थ के बारे में कोई पता नहीं है, और उसकी सिफारिश करने के लिए एक अच्छा विचार है। एक चर आकार के साथ इस डिजाइन को किसी चीज़ में बदलना।
डॉक्टर ब्राउन

सरणियाँ ठीक हैं।
ऑर्बिट में हल्कापन दौड़

... उन लोगों के लिए जो यह नहीं समझते कि मेरा क्या मतलब है, ज़ीरो वन इन्फिनिटी नियम
डॉक्टर ब्राउन

48

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

  • आपकी कक्षा का उपयोग करने वाले किसी भी कोड को यह जानना होगा कि lenयह सरणी की लंबाई है, और इसे लगातार उपयोग करें। यह कम से कम ज्ञान के सिद्धांत के खिलाफ जाता है । कक्षा के आंतरिक विवरणों पर इस तरह की निर्भरता अत्यंत त्रुटि-प्रवण और जोखिम भरी है।
  • यह वर्ग के विकास को बहुत मुश्किल बना देगा (जैसे यदि एक दिन, आप विरासत सरणियों को और lenअधिक आधुनिक में बदलना चाहते हैं std::vector<int>), क्योंकि इसके लिए आपको अपनी कक्षा का उपयोग करके सभी कोड भी बदलने की आवश्यकता होगी।
  • MyClassनियमों का सम्मान किए बिना कुछ सार्वजनिक चर को भ्रष्ट करके कोड का कोई भी हिस्सा आपकी वस्तुओं पर कहर बरपा सकता है (तथाकथित वर्ग हमलावर )
  • अंत में, विधि वास्तव में वर्ग से स्वतंत्र है, क्योंकि यह केवल मापदंडों के साथ काम करती है और किसी अन्य वर्ग तत्व पर निर्भर नहीं होती है। इस तरह की विधि बहुत अच्छी तरह से कक्षा के बाहर एक स्वतंत्र कार्य हो सकती है। या कम से कम एक स्थिर विधि।

आपको अपना कोड रिफलेक्टर करना चाहिए, और:

  • अपना वर्ग चर बनाएं privateया protected, जब तक कि ऐसा न करने का कोई अच्छा कारण न हो।
  • वर्ग पर किए जाने वाले कार्यों के रूप में अपनी सार्वजनिक विधियों को डिज़ाइन करें (जैसे:) object.action(additional parameters for the action)
  • यदि इस रीफ़ैक्टरिंग के बाद, आपके पास अभी भी कुछ वर्ग विधियाँ होंगी, जिन्हें कक्षा चर के साथ बुलाया जाना चाहिए, तो वे सत्यापित करने के बाद उन्हें संरक्षित या निजी बनाएं कि वे सार्वजनिक विधियों का समर्थन करने वाले उपयोगिता कार्य हैं।

7

जब इस डिजाइन का आशय यह है कि आप डेटा के लिए इस विधि का पुन: उपयोग करना चाहते हैं जो इस वर्ग उदाहरण से नहीं आती है, तो आप इसे एक staticविधि के रूप में घोषित करना चाह सकते हैं ।

एक स्थिर विधि किसी भी वर्ग के कण-प्रसार उदाहरण से संबंधित नहीं है। बल्कि यह स्वयं कक्षा की एक विधि है। चूँकि स्थिर विधियाँ वर्ग के किसी विशेष उदाहरण के संदर्भ में नहीं चलाई जाती हैं, वे केवल सदस्य चर का उपयोग कर सकती हैं, जिन्हें स्थैतिक भी घोषित किया जाता है। यह देखते हुए कि आपका तरीका addघोषित किए गए किसी भी सदस्य-चर को संदर्भित नहीं करता है MyClass, यह स्थिर घोषित होने के लिए एक अच्छा उम्मीदवार है।

हालाँकि, अन्य उत्तरों द्वारा बताई गई सुरक्षा समस्याएँ मान्य हैं: यदि दोनों सरणियाँ कम से कम lenलंबी हैं तो आप जाँच नहीं कर रहे हैं । यदि आप आधुनिक और मजबूत C ++ लिखना चाहते हैं, तो आपको सरणियों का उपयोग करने से बचना चाहिए। std::vectorजब भी संभव हो कक्षा का उपयोग करें । नियमित सरणियों के विपरीत, वैक्टर अपने स्वयं के आकार को जानते हैं। तो आप अपने आप को उनके आकार का ट्रैक रखने की जरूरत नहीं है। उनके तरीकों में से अधिकांश (सभी नहीं!) स्वचालित बाउंड चेकिंग भी करते हैं, जो गारंटी देता है कि जब आप अंत पढ़ते हैं या लिखते हैं तो आपको एक अपवाद मिलता है। रेगुलर एरे एक्सेस एक्सेस बाउंड चेकिंग नहीं करता है, जिसके परिणामस्वरूप सेगफॉल्ट सबसे अच्छा होता है और इससे शोषणकारी बफर अतिप्रवाह भेद्यता खराब हो सकती है।


1

एक कक्षा में एक विधि आदर्श रूप से वर्ग के अंदर समझाया डेटा में हेरफेर करती है। आपके उदाहरण में किसी भी पैरामीटर को जोड़ने की विधि का कोई कारण नहीं है, बस वर्ग के गुणों का उपयोग करें।


0

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

बेशक, अनुबंध कुछ प्रकार के उपनामों को अपरिभाषित छोड़ सकता है, भले ही भाषा इसका समर्थन करती हो।

प्रमुख उदाहरण:

  • स्व काम। यह एक होना चाहिए, संभवतः महंगा, नो-ऑप। इसके लिए सामान्य मामले को कम मत कीजिए।
    दूसरी ओर स्व-चालन-असाइनमेंट, जबकि यह आपके चेहरे में विस्फोट नहीं होना चाहिए, एक नहीं सेशन होना चाहिए।
  • एक कंटेनर में एक तत्व की एक प्रतिलिपि को कंटेनर में सम्मिलित करना। कुछ इस तरह v.push_back(v[2]);
  • std::copy() स्रोत के अंदर गंतव्य शुरू नहीं होता है।

लेकिन आपके उदाहरण में अलग समस्याएं हैं:

  • सदस्य-समारोह अपने किसी भी निजीकरण का उपयोग नहीं करता है, न ही संदर्भित करता है this। यह एक फ्री यूटिलिटी-फंक्शन की तरह काम करता है जो कि गलत जगह पर है।
  • आपकी कक्षा किसी भी प्रकार के अमूर्त को प्रस्तुत करने का प्रयास नहीं करती है । इसके अनुरूप, यह अपनी पारी को आगे बढ़ाने की कोशिश नहीं करता है , और न ही किसी भी हमलावर को बनाए रखता है । यह मनमाने तत्वों का गूंगा बैग है।

संक्षेप में: हां, यह उदाहरण बुरा है। लेकिन इसलिए नहीं कि सदस्य-फ़ंक्शन पास किए गए सदस्य-ऑब्जेक्ट हो जाते हैं।


0

विशेष रूप से ऐसा करना कई अच्छे कारणों के लिए एक बुरा विचार है, लेकिन मुझे यकीन नहीं है कि ये सभी आपके प्रश्न का अभिन्न अंग हैं:

  • कक्षा चर (वास्तविक चर, स्थिरांक नहीं) होने से बचने के लिए कुछ है यदि संभव हो तो, और इस पर एक गंभीर प्रतिबिंब को ट्रिगर करना चाहिए कि क्या आपको इसकी बिल्कुल आवश्यकता है।
  • सभी के लिए पूरी तरह से खुले इन वर्ग चर के लिए लिखने का उपयोग छोड़कर लगभग सार्वभौमिक रूप से खराब है।
  • आपकी कक्षा पद्धति आपकी कक्षा से असंबंधित है। यह वास्तव में एक फ़ंक्शन है जो एक सरणी के मूल्यों को दूसरे सरणी के मूल्यों में जोड़ता है। इस तरह के फ़ंक्शन को एक भाषा में एक फ़ंक्शन होना चाहिए जिसमें फ़ंक्शन होते हैं, और उन भाषाओं में "नकली वर्ग" (यानी बिना डेटा या ऑब्जेक्ट व्यवहार के कार्यों का एक संग्रह) का स्थिर तरीका होना चाहिए, जिसमें फ़ंक्शन नहीं हैं।
  • वैकल्पिक रूप से, इन मापदंडों को हटा दें और इसे वास्तविक वर्ग विधि में बदल दें, लेकिन यह अभी भी पहले बताए गए कारणों के लिए बुरा होगा।
  • क्यों int arrays? vector<int>आपके जीवन को आसान बना देगा।
  • यदि यह उदाहरण वास्तव में आपके डिज़ाइन का प्रतिनिधि है, तो अपने डेटा को ऑब्जेक्ट्स में न डालकर कक्षाओं में देखें और इन ऑब्जेक्ट्स के लिए कंस्ट्रक्टर्स को इस तरह से लागू करें कि निर्माण के बाद ऑब्जेक्ट डेटा के मूल्य को बदलने की आवश्यकता न हो।

0

यह C ++ के लिए एक क्लासिक "सी विद क्लासेस" दृष्टिकोण है। वास्तव में, यह कोई भी अनुभवी सी ++ प्रोग्रामर नहीं लिखेगा। एक के लिए, कच्चे सी सरणियों का उपयोग करना बहुत अधिक सार्वभौमिक रूप से हतोत्साहित करता है, जब तक कि आप कंटेनर लाइब्रेरी को लागू नहीं कर रहे हैं।

ऐसा कुछ अधिक उपयुक्त होगा:

// Don't forget to compile with -std=c++17
#include <iostream>
using std::cout; // This style is less common, but I prefer it
using std::endl; // I'm sure it'll incite some strongly opinionated comments

#include <array>
using std::array;

#include <algorithm>

#include <vector>
using std::vector;


class MyClass {
public: // Begin lazy for brevity; don't do this
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2 = {10, 10, 10, 10, 10};
};

void elementwise_add_assign(
    std::array<int, 5>& lhs,
    const std::array<int, 5>& rhs
) {
    std::transform(
        lhs.begin(), lhs.end(), rhs.begin(),
        lhs.begin(),
        [](auto& l, const auto& r) -> int {
            return l + r;
        });
}

int main() {
    MyClass foo{};

    elementwise_add_assign(foo.arr1, foo.arr2);

    for(auto const& value: foo.arr1) {
        cout << value << endl; // arr1 values have been added to
    }

    for(auto const& value: foo.arr2) {
        cout << value << endl; // arr2 values remain unaltered
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.