5 का नियम - इसका उपयोग करना है या नहीं?


20

3 का नियम ( नए c ++ मानक में 5 का नियम ) कहता है:

यदि आपको स्पष्ट रूप से विध्वंसक, कॉपी कंस्ट्रक्टर या कॉपी असाइनमेंट ऑपरेटर की स्पष्ट रूप से घोषणा करने की आवश्यकता है, तो आपको संभवतः उन तीनों को स्पष्ट रूप से घोषित करने की आवश्यकता है।

लेकिन, दूसरी ओर, मार्टिन के " क्लीन कोड " से सभी खाली कंस्ट्रक्टर और डिस्ट्रक्टर्स (पेज 293, जी 12: अव्यवस्था ) को हटाने की सलाह दी जाती है :

बिना किसी क्रियान्वयन के डिफ़ॉल्ट डिफाल्टर किस उपयोग का है? यह सब करने के लिए अर्थहीन कलाकृतियों के साथ कोड अव्यवस्था है।

तो, इन दो विपरीत विचारों को कैसे संभालना है? क्या खाली कंस्ट्रक्टर / डिस्ट्रक्टर्स को वास्तव में लागू किया जाना चाहिए?


अगला उदाहरण बिल्कुल वही दिखाता है जो मेरा मतलब है:

#include <iostream>
#include <memory>

struct A
{
    A( const int value ) : v( new int( value ) ) {}
    ~A(){}
    A( const A & other ) : v( new int( *other.v ) ) {}
    A& operator=( const A & other )
    {
        v.reset( new int( *other.v ) );
        return *this;
    }

    std::auto_ptr< int > v;
};
int main()
{
    const A a( 55 );
    std::cout<< "a value = " << *a.v << std::endl;
    A b(a);
    std::cout<< "b value = " << *b.v << std::endl;
    const A c(11);
    std::cout<< "c value = " << *c.v << std::endl;
    b = c;
    std::cout<< "b new value = " << *b.v << std::endl;
}

जी + का उपयोग कर संकलन ठीक है। 4.6.1 इसके साथ:

g++ -std=c++0x -Wall -Wextra -pedantic example.cpp

के लिए विध्वंसक struct Aखाली है, और वास्तव में जरूरत नहीं है। तो, क्या यह वहाँ होना चाहिए, या इसे हटा दिया जाना चाहिए?


15
2 उद्धरण विभिन्न चीजों के बारे में बात करते हैं। या मैं आपकी बात को पूरी तरह से याद करता हूं।
बेंजामिन बैनियर

1
@ हॉनक मेरी टीम के कोडिंग मानक में, हमारे पास हमेशा सभी 4 (कंस्ट्रक्टर, विध्वंसक, कॉपी कंस्ट्रक्टर) घोषित करने का नियम है। मैं सोच रहा था कि क्या यह वास्तव में करने के लिए समझ में आता है। क्या मुझे वास्तव में हमेशा विनाशकारी घोषित करना होगा, भले ही वे खाली हों?
B35овиЈ

के रूप में के लिए खाली desctructors इस बारे में सोचते हैं: codeynthesis.com/~boris/blog/2012/04/04/… । अन्यथा 3 (5) का नियम मेरे लिए एकदम सही है, कोई विचार नहीं करता कि कोई 4. का नियम क्यों चाहता है
बेंजामिन बैनियर

@ हांक आप नेट पर मिलने वाली जानकारी के बारे में देखें। सब सच नहीं है। उदाहरण के लिए, virtual ~base () = default;संकलन नहीं करता है (एक अच्छे कारण के साथ)
Bћови

@VJovic, नहीं, आपको एक खाली विध्वंसक घोषित करने की आवश्यकता नहीं है, जब तक कि आपको इसे आभासी बनाने की आवश्यकता न हो। और जब हम विषय पर हों, तो आपको उपयोग नहीं करना चाहिए auto_ptr
दीमा

जवाबों:


44

एक शुरुआत के लिए नियम "शायद" कहता है, इसलिए यह हमेशा लागू नहीं होता है।

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

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

अपने उदाहरण के लिए: ऐसे मामले में, आप विध्वंसक को छोड़ सकते हैं। यह स्पष्ट रूप से कुछ नहीं करता है। स्मार्ट पॉइंटर्स का उपयोग एक आदर्श उदाहरण है कि 3 का नियम कहां और क्यों नहीं है।

यह आपके लिए एक कोड है जहां आप अपने कोड पर दूसरा नज़र डालना चाहते हैं, यदि आप महत्वपूर्ण कार्यक्षमता को लागू करना भूल गए हैं, तो आप चूक गए होंगे।


स्मार्ट पॉइंटर्स के उपयोग के साथ, विध्वंसक अधिकांश मामलों में खाली होते हैं (मैं कहूंगा> मेरे कोड बेस में 99% विध्वंसक रिक्त हैं, क्योंकि लगभग हर वर्ग pimpl मुहावरे का उपयोग करता है)।
B39овиЈ

वाह, यह बहुत तेज़ है मैं इसे बदबूदार कहूँगा। कई कंपाइलरों के साथ pimpled को ऑप्टिमाइज़ करना अधिक कठिन होगा (जैसे कठिन इनलाइन)।
बेंजामिन बैनियर

@ हाँक "आपको कई कंपाइलरों द्वारा pimpled" से क्या मतलब है? :)
बिग

@VJovic: क्षमा करें, टाइपो: 'पिम्पल कोड'
बेंजामिन बैनियर

4

यहां वास्तव में कोई विरोधाभास नहीं है। विध्वंसक, कॉपी कंस्ट्रक्टर और कॉपी असाइनमेंट ऑपरेटर के बारे में 3 का नियम। अंकल बॉब खाली डिफॉल्ट कंस्ट्रक्टर की बात करते हैं।

यदि आपको एक विध्वंसक की आवश्यकता है, तो आपकी कक्षा में संभवतः गतिशील रूप से आवंटित मेमोरी में पॉइंटर्स होते हैं, और आप शायद एक प्रतिलिपि ctor और operator=()एक गहरी प्रतिलिपि बनाना चाहते हैं। यह पूरी तरह से ऑर्थोगोनल है कि आपको डिफ़ॉल्ट कंस्ट्रक्टर की आवश्यकता है या नहीं।

यह भी ध्यान दें कि C ++ में ऐसे हालात हैं जब आपको एक डिफ़ॉल्ट कंस्ट्रक्टर की आवश्यकता होती है, भले ही वह खाली हो। मान लीजिए कि आपकी कक्षा में एक गैर-डिफ़ॉल्ट निर्माता है। उस स्थिति में, कंपाइलर आपके लिए डिफ़ॉल्ट कंस्ट्रक्टर नहीं बनाएगा। इसका मतलब है कि इस वर्ग की वस्तुओं को एसटीएल कंटेनरों में संग्रहित नहीं किया जा सकता है, क्योंकि उन कंटेनरों से वस्तुओं के डिफ़ॉल्ट-निर्माण योग्य होने की उम्मीद है।

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


2

यहां आपकी क्षमता (*) डिफ़ॉल्ट एक कंस्ट्रक्टर / असाइनमेंट / डिस्ट्रक्टर का एक उद्देश्य है: आपके पास इस मुद्दे के बारे में तथ्य का दस्तावेजीकरण करें और निर्धारित करें कि डिफ़ॉल्ट व्यवहार सही था। BTW, C ++ 11 में, चीजों को यह जानने के लिए पर्याप्त रूप से स्थिर नहीं किया गया है कि क्या =defaultवह उद्देश्य पूरा कर सकता है।

(एक और संभावित उद्देश्य है: डिफ़ॉल्ट इनलाइन एक के बजाय लाइन परिभाषा से बाहर प्रदान करें, स्पष्ट रूप से दस्तावेज़ के लिए बेहतर यदि आपके पास ऐसा करने का कोई कारण है)।

(*) संभावित क्योंकि मुझे एक वास्तविक जीवन का मामला याद नहीं है जहाँ तीन का नियम लागू नहीं हुआ था, अगर मुझे एक में कुछ करना था, तो मुझे दूसरों में कुछ करना था।


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


उदाहरण मेरी बात को प्रदर्शित करता है। विध्वंसक वास्तव में जरूरत नहीं है, लेकिन 3 का नियम बताता है कि यह होना चाहिए।
B48овиЈ

1

5 का नियम 3 के नियम का एक सतर्क विस्तार है जो कि एक सतर्क व्यवहार है, जो संभवतया वस्तु के दुरुपयोग के लिए है।

यदि आपको एक विध्वंसक की आवश्यकता है, तो इसका मतलब है कि आपने डिफ़ॉल्ट के अलावा कुछ "संसाधन प्रबंधन" किया (बस निर्माण और विनाश मूल्यों )।

चूंकि डिफ़ॉल्ट कॉपी मानों द्वारा कॉपी, असाइन, मूव और ट्रांसफर किया जाता है , यदि आप सिर्फ मान नहीं रख रहे हैं, तो आपको परिभाषित करना है कि क्या करना है।

यदि आपने इस कदम को परिभाषित किया है और यदि आप कॉपी को परिभाषित करते हैं तो चाल को हटाते हुए C ++ ने टीएच कॉपी को हटा दिया है। यदि आप किसी मान का अनुकरण करना चाहते हैं, तो अधिकांश मामलों में (इसलिए संसाधन मून क्लोन कॉपी करें, और इस कदम का कोई मतलब नहीं है) या संसाधन प्रबंधक (और इसलिए संसाधन को स्थानांतरित करें, जहां प्रतिलिपि का कोई अर्थ नहीं है: नियम 3 में से अन्य 3 का नियम बन जाता है )

ऐसे मामले जब आपको कॉपी और मूव (5 का नियम) दोनों को परिभाषित करना पड़ता है, तो यह बहुत ही कम होता है: आमतौर पर आपके पास "बड़ी वैल्यू" होती है जिसे कॉपी करना होता है यदि अलग-अलग ऑब्जेक्ट्स को दिया जाता है, लेकिन अस्थायी ऑब्जेक्ट से लिया जा सकता है (टालना) एक क्लोन तबाह )। एसटीएल कंटेनरों या अंकगणित कंटेनरों का यही हाल है।

मामला matrixes हो सकता है: वे समर्थन की नकल करने के लिए है, क्योंकि वे कर रहे हैं मूल्यों, ( a=b; c=b; a*=2; b*=3;एक दूसरे को प्रभावित नहीं होना चाहिए) लेकिन वे भी समर्थन बढ़ रहा है (द्वारा अनुकूलित किया जा सकता है a = 3*b+4*cएक है +कि दो temporaries लेता है और एक अस्थायी उत्पन्न करता है: परहेज क्लोन और हटाना हो सकता है उपयोगी)


1

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

इसे विध्वंसक से एक तरह से संबंध के रूप में निर्दिष्ट करने से कुछ चीजें स्पष्ट हो जाती हैं:

  1. यह उन मामलों में लागू नहीं होता है जहां आप केवल एक अनुकूलन के रूप में एक गैर-डिफ़ॉल्ट प्रतिलिपि निर्माता या असाइनमेंट ऑपरेटर प्रदान करते हैं।

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


-3

चर्चा में अभी तक एक और बिंदु का उल्लेख नहीं किया गया है: एक विध्वंसक हमेशा आभासी होना चाहिए।

struct A
{
    A( const int value ) : v( new int( value ) ) {}
    virtual ~A(){}
    ...
}

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

यदि आप (-Wextra -Weffc ++) g ++ पर सभी चेतावनी देते हैं, तो आपको इस बारे में चेतावनी देगा। मैं हमेशा किसी भी वर्ग में एक आभासी विध्वंसक घोषित करने के लिए इसे अच्छा अभ्यास मानता हूं, क्योंकि आप कभी नहीं जानते हैं, अगर आपकी कक्षा अंततः आधार वर्ग बन जाएगी। यदि वर्चुअल विध्वंसक की आवश्यकता नहीं है, तो यह कोई नुकसान नहीं पहुंचाता है। यदि यह है, तो आप त्रुटि को खोजने के लिए समय बचाते हैं।


1
लेकिन मुझे वर्चुअल कंस्ट्रक्टर नहीं चाहिए। अगर मैं ऐसा करता हूं, तो किसी भी विधि से हर कॉल वर्चुअल डिस्पैच का उपयोग करेगा। btw ध्यान दें कि c ++ में "वर्चुअल कंस्ट्रक्टर" जैसी कोई चीज नहीं है। इसके अलावा, मैंने बहुत उच्च चेतावनी स्तर के रूप में उदाहरण संकलित किया।
B:07овиЈ

IIRC, वह नियम जो अपनी चेतावनी के लिए gcc का उपयोग करता है, और जो नियम मैं आमतौर पर वैसे भी अनुसरण करता हूं, वह यह है कि कक्षा में कोई अन्य वर्चुअल विधियाँ होने पर वर्चुअल विध्वंसक होना चाहिए।
जूल्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.