खोज…


आभासी और संरक्षित विध्वंसक

एक वर्ग जिसे विरासत में दिया गया है, को बेस क्लास कहा जाता है। ऐसी कक्षा के विशेष सदस्य कार्यों के साथ देखभाल की जानी चाहिए।

रन-टाइम (बेस क्लास के लिए एक पॉइंटर के माध्यम से) को पॉलीमॉर्फिक रूप से उपयोग किए जाने वाले वर्ग को विनाशकारी virtual घोषित करना चाहिए। यह ऑब्जेक्ट के व्युत्पन्न हिस्सों को ठीक से नष्ट करने की अनुमति देता है, तब भी जब ऑब्जेक्ट बेस क्लास के लिए एक पॉइंटर के माध्यम से नष्ट हो जाता है।

class Base {
public:
    virtual ~Base() = default;

private:
    //    data members etc.
};

class Derived : public Base { //  models Is-A relationship
public:
    //    some methods

private:
    //    more data members
};

//    virtual destructor in Base ensures that derived destructors
//    are also called when the object is destroyed
std::unique_ptr<Base> base = std::make_unique<Derived>();
base = nullptr;  //    safe, doesn't leak Derived's members

यदि वर्ग को बहुरूपी होने की आवश्यकता नहीं है, लेकिन फिर भी इसके इंटरफ़ेस को विरासत में प्राप्त करने की अनुमति देने की आवश्यकता है, तो गैर-आभासी protected विध्वंसक का उपयोग करें।

class NonPolymorphicBase {
public:
    //    some methods

protected:
    ~NonPolymorphicBase() = default; //    note: non-virtual

private:
    //    etc.
};

इस तरह के एक वर्ग को एक सूचक के माध्यम से कभी भी नष्ट नहीं किया जा सकता है, स्लाइसिंग के कारण मूक रिसाव से बचा जा सकता है।

यह तकनीक विशेष रूप से private बेस कक्षाओं के लिए डिज़ाइन की गई कक्षाओं पर लागू होती है। अनुकूलन बिंदुओं के रूप में virtual तरीके प्रदान करते हुए इस तरह के वर्ग का उपयोग कुछ सामान्य कार्यान्वयन विवरणों को संलग्न करने के लिए किया जा सकता है। इस तरह के वर्ग को कभी भी बहुरूपिक रूप से उपयोग नहीं किया जाना चाहिए, और एक protected विध्वंसक इस आवश्यकता को सीधे कोड में दर्ज करने में मदद करता है।

अंत में, कुछ वर्गों को आवश्यकता हो सकती है कि उनका उपयोग आधार वर्ग के रूप में कभी न किया जाए। इस स्थिति में, वर्ग को final रूप से चिह्नित किया जा सकता है। एक सामान्य गैर-आभासी सार्वजनिक विध्वंसक इस मामले में ठीक है।

class FinalClass final {  //    marked final here
public:
    ~FinalClass() = default;

private:
    //    etc.
};

लागू करें और प्रतिलिपि बनाएँ

इस बात को ध्यान में रखें कि विध्वंसक घोषित करने से संकलक को अंतर्निहित चाल निर्माणकर्ताओं को स्थानांतरित करने और असाइनमेंट ऑपरेटरों को स्थानांतरित करने से रोकता है। यदि आप एक विध्वंसक घोषित करते हैं, तो याद रखें कि चाल संचालन के लिए उपयुक्त परिभाषाएं भी जोड़ें।

इसके अलावा, चाल के संचालन की घोषणा कॉपी संचालन की पीढ़ी को दबा देगी, इसलिए इन्हें भी जोड़ा जाना चाहिए (यदि इस वर्ग की वस्तुओं को कॉपी शब्दार्थ की आवश्यकता है)।

class Movable {
public:
    virtual ~Movable() noexcept = default;

    //    compiler won't generate these unless we tell it to
    //    because we declared a destructor
    Movable(Movable&&) noexcept = default;
    Movable& operator=(Movable&&) noexcept = default;

    //    declaring move operations will suppress generation
    //    of copy operations unless we explicitly re-enable them
    Movable(const Movable&) = default;
    Movable& operator=(const Movable&) = default;
};

कॉपी और स्वैप करें

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

person(const person &other)
    : name(new char[std::strlen(other.name) + 1])
    , age(other.age)
{
    std::strcpy(name, other.name);
}

person& operator=(person const& rhs) {
    if (this != &other) {
        delete [] name;
        name = new char[std::strlen(other.name) + 1];
        std::strcpy(name, other.name);
        age = other.age;
    }

    return *this;
}

लेकिन इस दृष्टिकोण में कुछ समस्याएं हैं। यह मजबूत अपवाद गारंटी विफल रहता है - अगर new[] फेंकता है, हम पहले से ही के स्वामित्व वाले संसाधनों साफ़ कर दिया है this और वापस नहीं ले पाता। हम कॉपी असाइनमेंट में कॉपी कंस्ट्रक्शन के बहुत सारे तर्क दोहरा रहे हैं। और हमें स्व-असाइनमेंट चेक को याद रखना होगा, जो आमतौर पर कॉपी ऑपरेशन में ओवरहेड जोड़ता है, लेकिन अभी भी महत्वपूर्ण है।

मजबूत अपवाद गारंटी को पूरा करने और कोड दोहराव से बचने के लिए (बाद के कदम असाइनमेंट ऑपरेटर के साथ ऐसा दोहरा), हम कॉपी-और-स्वैप मुहावरे का उपयोग कर सकते हैं:

class person {
    char* name;
    int age;
public:
    /* all the other functions ... */

    friend void swap(person& lhs, person& rhs) {
        using std::swap; // enable ADL

        swap(lhs.name, rhs.name);
        swap(lhs.age, rhs.age);
    }

    person& operator=(person rhs) {
        swap(*this, rhs);
        return *this;
    }
};

यह काम क्यों करता है? गौर कीजिए कि जब हमारे पास क्या होता है

person p1 = ...;
person p2 = ...;
p1 = p2;

सबसे पहले, हम कॉपी-निर्माण rhs से p2 (जो हम यहाँ नकल करने नहीं था)। यदि वह ऑपरेशन फेंकता है, तो हम operator= में कुछ भी नहीं करते हैं operator= और p1 अछूता रहता है। अगला, हम सदस्यों को *this और rhs बीच स्वैप करते हैं, और फिर rhs दायरे से बाहर हो जाते हैं। जब operator= , कि परोक्ष की मूल संसाधनों साफ this (नाशक है, जो हम नकल नहीं था के माध्यम से)। स्व-असाइनमेंट भी काम करता है - यह कॉपी-एंड-स्वैप (एक अतिरिक्त आवंटन और डील्लोकेशन शामिल है) के साथ कम कुशल है, लेकिन अगर यह संभावना नहीं है, तो हम इसके उपयोग के लिए विशिष्ट उपयोग के मामले को धीमा नहीं करते हैं।

सी ++ 11

उपरोक्त सूत्रीकरण कार्य के लिए पहले से ही है।

p1 = std::move(p2);

यहां, हम p2 से rhs -निर्माण निर्माण करते हैं, और बाकी सभी वैध हैं। यदि कोई वर्ग चल सकने योग्य है, लेकिन प्रतिलिपि योग्य नहीं है, तो कॉपी-असाइनमेंट को हटाने की कोई आवश्यकता नहीं है, क्योंकि यह असाइनमेंट ऑपरेटर केवल हटाए गए कॉपी कंस्ट्रक्टर के कारण बीमार हो जाएगा।

डिफ़ॉल्ट निर्माता

डिफॉल्ट कंस्ट्रक्टर एक प्रकार का कंस्ट्रक्टर होता है जिसे कॉल करने पर कोई पैरामीटर नहीं चाहिए। इसका नाम उस प्रकार के नाम पर रखा गया है जो इसका निर्माण करता है और इसका सदस्य कार्य है (जैसा कि सभी निर्माणकर्ता हैं)।

class C{
    int i;
public:
    // the default constructor definition
    C()
    : i(0){ // member initializer list -- initialize i to 0
        // constructor function body -- can do more complex things here
    }
};

C c1; // calls default constructor of C to create object c1
C c2 = C(); // calls default constructor explicitly
C c3(); // ERROR: this intuitive version is not possible due to "most vexing parse"
C c4{}; // but in C++11 {} CAN be used in a similar way

C c5[2]; // calls default constructor for both array elements
C* c6 = new C[2]; // calls default constructor for both array elements

डेवलपर को सभी मापदंडों के लिए डिफ़ॉल्ट मान प्रदान करने के लिए "कोई मापदंडों" की आवश्यकता को पूरा करने का एक और तरीका है:

class D{
    int i;
    int j;
public:
    // also a default constructor (can be called with no parameters)
    D( int i = 0, int j = 42 ) 
    : i(i), j(j){
    }
};


D d; // calls constructor of D with the provided default values for the parameters

कुछ परिस्थितियों में (अर्थात, डेवलपर कोई निर्माता प्रदान नहीं करता है और कोई अन्य अयोग्य स्थिति नहीं है), संकलक निहित रूप से एक खाली डिफ़ॉल्ट निर्माता प्रदान करता है:

class C{
    std::string s; // note: members need to be default constructible themselves
};

C c1; // will succeed -- C has an implicitly defined default constructor

कुछ अन्य प्रकार के निर्माणकर्ता पहले बताई गई अयोग्य स्थितियों में से एक हैं:

class C{
    int i;
public:
    C( int i ) : i(i){}
};

C c1; // Compile ERROR: C has no (implicitly defined) default constructor
c ++ 11

अंतर्निहित डिफ़ॉल्ट निर्माणकर्ता को रोकने के लिए, एक सामान्य तकनीक इसे private (बिना किसी परिभाषा के) घोषित करने के लिए है। इरादा एक संकलित त्रुटि का कारण बनता है जब कोई कंस्ट्रक्टर का उपयोग करने की कोशिश करता है (यह या तो कंपाइलर के आधार पर एक्सेस में निजी त्रुटि या एक लिंकर त्रुटि का परिणाम है)।

यह सुनिश्चित करने के लिए कि एक डिफ़ॉल्ट निर्माता (कार्यात्मक रूप से निहित एक के समान) को परिभाषित किया गया है, एक डेवलपर खाली एक स्पष्ट रूप से लिख सकता है।

c ++ 11

C ++ 11 में, एक कंपाइलर को डिफ़ॉल्ट कंस्ट्रक्टर प्रदान करने से रोकने के लिए एक डेवलपर delete कीवर्ड का भी उपयोग कर सकता है।

class C{
    int i;
public:
    // default constructor is explicitly deleted
    C() = delete;
};

C c1; // Compile ERROR: C has its default constructor deleted

इसके अलावा, एक डेवलपर को डिफ़ॉल्ट निर्माता प्रदान करने के लिए संकलक को वांछित करने के बारे में भी स्पष्ट हो सकता है।

class C{
    int i;
public:
    // does have automatically generated default constructor (same as implicit one)
    C() = default;

    C( int i ) : i(i){}
};

C c1; // default constructed
C c2( 1 ); // constructed with the int taking constructor 
c ++ 14

आप यह निर्धारित कर सकते हैं कि क्या std::is_default_constructible का उपयोग करके किसी प्रकार का डिफ़ॉल्ट कंस्ट्रक्टर (या कोई आदिम प्रकार है) std::is_default_constructible <type_traits> :

class C1{ };
class C2{ public: C2(){} };
class C3{ public: C3(int){} };

using std::cout; using std::boolalpha; using std::endl;
using std::is_default_constructible;
cout << boolalpha << is_default_constructible<int>() << endl; // prints true
cout << boolalpha << is_default_constructible<C1>() << endl; // prints true
cout << boolalpha << is_default_constructible<C2>() << endl; // prints true
cout << boolalpha << is_default_constructible<C3>() << endl; // prints false
c ++ 11

C ++ 11 में, अभी भी std::is_default_constructible के गैर- std::is_default_constructible संस्करण का उपयोग करना संभव है std::is_default_constructible :

cout << boolalpha << is_default_constructible<C1>::value << endl; // prints true

नाशक

एक विध्वंसक तर्कों के बिना एक फ़ंक्शन है जिसे तब कहा जाता है जब उपयोगकर्ता द्वारा परिभाषित ऑब्जेक्ट नष्ट होने वाला होता है। यह उस प्रकार के नाम पर है जिसे यह एक ~ उपसर्ग के साथ नष्ट कर देता है।

class C{
    int* is;
    string s;
public:
    C()
    : is( new int[10] ){
    }

    ~C(){  // destructor definition
        delete[] is;
    }
};

class C_child : public C{
    string s_ch;
public:
    C_child(){}
    ~C_child(){} // child destructor
};

void f(){
    C c1; // calls default constructor
    C c2[2]; // calls default constructor for both elements
    C* c3 = new C[2]; // calls default constructor for both array elements

    C_child c_ch;  // when destructed calls destructor of s_ch and of C base (and in turn s)

    delete[] c3; // calls destructors on c3[0] and c3[1]
} // automatic variables are destroyed here -- i.e. c1, c2 and c_ch

अधिकांश परिस्थितियों में (यानी, कोई उपयोगकर्ता कोई विध्वंसक प्रदान नहीं करता है, और कोई अन्य अयोग्य स्थिति नहीं है), संकलक एक डिफ़ॉल्ट विध्वंसक प्रदान करता है:

class C{
    int i;
    string s;
};

void f(){
    C* c1 = new C;
    delete c1; // C has a destructor
}

class C{
    int m;
private:
    ~C(){} // not public destructor!
};

class C_container{
    C c;
};

void f(){
    C_container* c_cont = new C_container;
    delete c_cont; // Compile ERROR: C has no accessible destructor
}
c ++ 11

C ++ 11 में, एक डेवलपर कंपाइलर को डिफ़ॉल्ट डिस्ट्रक्टर प्रदान करने से रोककर इस व्यवहार को ओवरराइड कर सकता है।

class C{
    int m;
public:
    ~C() = delete; // does NOT have implicit destructor
};

void f{
    C c1; 
} // Compile ERROR: C has no destructor

इसके अलावा, एक डेवलपर को डिफ़ॉल्ट डिस्ट्रक्टर प्रदान करने के लिए कंपाइलर के बारे में भी स्पष्ट होना चाहिए।

class C{
    int m;
public:
    ~C() = default; // saying explicitly it does have implicit/empty destructor
};

void f(){
    C c1;
} // C has a destructor -- c1 properly destroyed
c ++ 11

आप यह निर्धारित कर सकते हैं कि क्या std::is_destructible का उपयोग करके एक प्रकार का एक विध्वंसक (या एक आदिम प्रकार है) std::is_destructible <type_traits> :

class C1{ };
class C2{ public: ~C2() = delete };
class C3 : public C2{ };

using std::cout; using std::boolalpha; using std::endl;
using std::is_destructible;
cout << boolalpha << is_destructible<int>() << endl; // prints true
cout << boolalpha << is_destructible<C1>() << endl; // prints true
cout << boolalpha << is_destructible<C2>() << endl; // prints false
cout << boolalpha << is_destructible<C3>() << endl; // prints false


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow