c++ - प्रत्यक्ष प्रत्यक्ष#किंकर्तव्यविमूढ़ बनाम गैर-संविदात्मक सकर्मक#किंकर्तव्यविमूढ़




header include (4)

MyClass के आंतरिक कामकाज पर निर्भरता को रोकने के लिए। या मुझे करना चाहिए?

हाँ, आपको उस कारण के लिए और बहुत कुछ चाहिए। जब तक आप यह निर्दिष्ट नहीं करना चाहते कि MyClass.hpp को <vector> शामिल करने की गारंटी है, तो आप दूसरे सहित एक पर भरोसा नहीं कर सकते। और ऐसी गारंटी देने के लिए मजबूर होने का कोई अच्छा कारण नहीं है। यदि ऐसी कोई गारंटी नहीं है, तो आप MyClass.hpp के कार्यान्वयन विवरण पर भरोसा करते हैं जो भविष्य में बदल सकता है, जो आपके कोड को तोड़ देगा।

मुझे स्पष्ट रूप से पता है कि MyClass को काम करने के लिए वेक्टर की आवश्यकता है।

क्या यह? उदाहरण के लिए इसे boost::container::small_vector लिए उपयोग नहीं किया जा सकता है boost::container::small_vector इसके बजाय?

इस उदाहरण में MyClass को std :: वेक्टर की आवश्यकता है

लेकिन भविष्य में MyClass की जरूरतों के बारे में क्या? कार्यक्रम विकसित होते हैं, और आज जिस वर्ग की आवश्यकता होती है वह हमेशा वैसा नहीं होता है जैसा कि कल को कक्षा की आवश्यकता होती है।

लेकिन यह तय करना अच्छा नहीं होगा कि आयात करते समय कौन से हेडर सामने आते हैं

सकर्मक समावेश को रोकना संभव नहीं है।

C ++ 20 में पेश किए गए मॉड्यूल एक ऐसी विशेषता है जिसका उपयोग pp-समावेशन के बजाय किया जा सकता है और इसे हल करने में मदद करने के लिए अभिप्रेत है।

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

कहें कि हमारे पास यह हेडर फ़ाइल है:

MyClass.hpp

#pragma once
#include <vector>

class MyClass
{
public:
    MyClass(double);

    /* ... */

private:
    std::vector<double> internal_values;
};

अब, जब भी हम किसी अन्य hpp या cpp फ़ाइल में #include "MyClass.hpp" MyClass.hpp #include "MyClass.hpp" उपयोग करते हैं, तो हम इस तथ्य के बावजूद भी प्रभावी रूप से #include <vector> करते हैं कि हमें इसकी आवश्यकता नहीं है। जिस कारण से मैं कह रहा हूं कि इसकी आवश्यकता नहीं है, यह है कि std::vector का उपयोग केवल MyClass में आंतरिक रूप से किया जाता है, लेकिन वास्तव में इस वर्ग के साथ बातचीत करने के लिए यह बिल्कुल भी आवश्यक नहीं है।

नतीजतन, मैं लिख सकता था

संस्करण 1: SomeOtherHeader.hpp

#pragma once
#include "MyClass.hpp"

void func(const MyClass&, const std::vector<double>&);

जबकि मुझे शायद लिखना चाहिए

संस्करण 2: SomeOtherHeader.hpp

#pragma once
#include "MyClass.hpp"
#include <vector>

void func(const MyClass&, const std::vector<double>&);

MyClass के आंतरिक कामकाज पर निर्भरता को रोकने के लिए। या मुझे करना चाहिए?

मुझे स्पष्ट रूप से पता है कि MyClass को काम करने के लिए <vector> आवश्यकता है। तो यह एक दार्शनिक सवाल हो सकता है। लेकिन यह तय करना अच्छा नहीं होगा कि आयात करते समय कौन से हेडर सामने आते हैं (यानी नामस्थान में लोड हो जाता है)? ताकि प्रत्येक हेडर को #include करने की आवश्यकता हो, उसे क्या चाहिए, बिना किसी निहितार्थ के दूर होने पर, जिसमें किसी अन्य हेडर को चेन की आवश्यकता हो?

हो सकता है कि लोग आगामी C ++ 20 मॉड्यूल पर कुछ प्रकाश डाल सकें, जो मुझे विश्वास है कि इस मुद्दे के कुछ पहलुओं को संबोधित करेंगे।


आपको एक गैर विनाशकारी वर्कफ़्लो के लिए स्पष्ट रूप से #include s का उपयोग करना चाहिए। मान लीजिए कि MyClass का उपयोग 50 विभिन्न स्रोत फ़ाइलों में किया जाता है। उनमें vector शामिल नहीं है। अचानक, आपको कुछ अन्य कंटेनर के लिए MyClass.h में std::vector बदलना होगा। फिर सभी 50 स्रोत फ़ाइलों को या तो vector शामिल करने की आवश्यकता होगी या आपको इसे MyClass.h में छोड़ने की आवश्यकता होगी। यह निरर्थक होगा और यह अनुप्रयोग आकार , संकलन समय और यहां तक ​​कि रन टाइम (स्टेटिक वैरिएबल इनिशियलाइज़ेशन) को अनावश्यक रूप से बढ़ा सकता है।


यदि आपके MyClass में std::vector<double> टाइप का सदस्य है, तो MyClass को परिभाषित करने वाले हेडर को #include <vector> को #include <vector> । अन्यथा, MyClass उपयोगकर्ताओं को संकलन करने का एकमात्र तरीका है यदि वे MyClass की परिभाषा को शामिल करने से पहले #include <vector>

हालांकि सदस्य private , यह अभी भी कक्षा का हिस्सा है, इसलिए कंपाइलर को एक पूर्ण प्रकार की परिभाषा देखने की जरूरत है। अन्यथा, यह गणना sizeof(MyClass) जैसी चीजों को नहीं कर सकता है, या किसी भी MyClass ऑब्जेक्ट को MyClass है।

यदि आप अपने हेडर और <vector> बीच निर्भरता को तोड़ना चाहते हैं तो तकनीकें हैं। उदाहरण के लिए, दाना ("कार्यान्वयन के लिए सूचक") मुहावरा।

class MyClass 
{
public:
    MyClass(double first_value);

    /* ... */

private:
    void *pimpl;
};

और, स्रोत फ़ाइल में जो कक्षा के सदस्यों को परिभाषित करता है;

#include <vector>
#include "MyClass.hpp"

MyClass::MyClass(double first_value) : pimpl(new std::vector<double>())
{

}

(और यह भी, संभवतया, first_value साथ कुछ करें, लेकिन मैंने इसे छोड़ दिया है)।

ट्रेडऑफ़ यह है कि वेक्टर का उपयोग करने की आवश्यकता वाले प्रत्येक सदस्य फ़ंक्शन को इसे pimpl से प्राप्त करने की आवश्यकता होती है। उदाहरण के लिए, यदि आप आवंटित वेक्टर का संदर्भ प्राप्त करना चाहते हैं

void MyClass::some_member_function()
{
    std::vector<double> &internal_data = *static_cast<std::vector<double> *>(pimpl);

}

MyClass विध्वंसक को गतिशील रूप से आवंटित वेक्टर को जारी करने की भी आवश्यकता होगी।

यह वर्ग परिभाषा के लिए कुछ विकल्पों को भी सीमित करता है। उदाहरण के लिए, MyClass में एक सदस्य फ़ंक्शन नहीं हो सकता है जो एक std::vector<double> देता है std::vector<double> मूल्य द्वारा (जब तक आप #include <vector> ) नहीं

आपको यह तय करने की आवश्यकता होगी कि क्या pimpl मुहावरे जैसी तकनीक आपके वर्ग के काम करने के प्रयास के लायक हैं। व्यक्तिगत रूप से, जब तक पिंपल मुहावरे का उपयोग करके कक्षा के कार्यान्वयन को अलग करने के लिए कुछ अन्य सम्मोहक कारण नहीं हैं, मैं आपकी हेडर फ़ाइल में केवल #include <vector> की आवश्यकता को स्वीकार करूंगा।


विचार करें कि कोड को केवल एक बार लिखा जाना नहीं है, बल्कि यह समय के साथ विकसित होता है।

मान लेते हैं कि आपने कोड लिखा है और अब मेरा काम इसे रिफलेक्टर करना होगा। किसी कारण से मैं MyClass को YourClass से बदलना चाहता YourClass और कहता है कि उनके पास एक ही इंटरफ़ेस है। मुझे बस इस पर आने के लिए MyClass की किसी भी घटना को YourClass साथ YourClass होगा:

/* Version 1: SomeOtherHeader.hpp */

#pragma once
#include "YourClass.hpp"

void func(const YourClass& a, const std::vector<double>& b);

मैंने सब कुछ सही किया, लेकिन फिर भी कोड संकलित करने में विफल होगा (क्योंकि YourClass std::vector सहित नहीं है)। इस विशेष उदाहरण में मुझे एक स्पष्ट त्रुटि संदेश मिलेगा और फिक्स स्पष्ट होगा। हालाँकि, चीजें बहुत तेजी से गड़बड़ हो सकती हैं अगर ऐसी निर्भरता कई हेडर में फैले, अगर ऐसी कई निर्भरताएं हैं और अगर SomeOtherHeader.hpp में केवल एक ही घोषणा से अधिक है।

और भी चीजें हैं जो गलत हो सकती हैं। उदाहरण के लिए MyClass के लेखक ने फैसला किया कि वे वास्तव में एक आगे की घोषणा के पक्ष में शामिल ड्रॉप कर सकते हैं। इसके अलावा SomeOtherHeader टूट जाएगा। यह उबलता है: यदि आप SomeOtherHeader में vector को शामिल नहीं करते हैं तो एक छिपी निर्भरता है, जो खराब है।

ऐसी समस्याओं को रोकने के लिए अंगूठे का नियम है: जो भी आप उपयोग करते हैं उसे शामिल करें।







c++17