डिफॉल्ट कंस्ट्रक्टर और डिस्ट्रक्टर के लिए "{}" कैसे "डिफॉल्ट" से अलग है?


169

मैंने मूल रूप से इसे केवल विध्वंसक के बारे में एक प्रश्न के रूप में पोस्ट किया था, लेकिन अब मैं डिफ़ॉल्ट कंस्ट्रक्टर पर विचार कर रहा हूं। यहाँ मूल प्रश्न है:

अगर मैं अपनी कक्षा को एक विध्वंसक देना चाहता हूं जो आभासी है, लेकिन अन्यथा कंपाइलर उत्पन्न करने के समान है, मैं इसका उपयोग कर सकता हूं =default:

class Widget {
public:
   virtual ~Widget() = default;
};

लेकिन ऐसा लगता है कि मैं एक खाली परिभाषा का उपयोग करके कम टाइपिंग के साथ समान प्रभाव प्राप्त कर सकता हूं:

class Widget {
public:
   virtual ~Widget() {}
};

क्या कोई तरीका है जिसमें ये दोनों परिभाषाएं अलग-अलग व्यवहार करती हैं?

इस प्रश्न के लिए पोस्ट किए गए उत्तरों के आधार पर, डिफ़ॉल्ट निर्माणकर्ता के लिए स्थिति समान लगती है। यह देखते हुए कि विनाशकों के लिए " =default" और " {}" के बीच अर्थ में लगभग कोई अंतर नहीं है, क्या डिफ़ॉल्ट निर्माणकर्ताओं के लिए इन विकल्पों के बीच अर्थ में लगभग कोई अंतर नहीं है? यह मानते हुए कि मैं एक प्रकार बनाना चाहता हूं, जहां उस प्रकार की वस्तुएं निर्मित और नष्ट दोनों होंगी, मैं क्यों कहना चाहता हूं

Widget() = default;

के बजाय

Widget() {}

?

मैं माफी माँगता हूँ अगर इस सवाल का विस्तार अपनी मूल पोस्टिंग के बाद कुछ एसओ नियमों का उल्लंघन कर रहा है। डिफ़ॉल्ट निर्माणकर्ताओं के लिए लगभग समान प्रश्न पोस्ट करने से मुझे कम वांछनीय विकल्प के रूप में मारा गया।


1
ऐसा नहीं है कि मुझे पता है, लेकिन = defaultअधिक स्पष्ट imo है, और इसके लिए निर्माणकर्ताओं के साथ समर्थन के अनुरूप है।
क्रिस

11
मुझे यकीन नहीं है, लेकिन मुझे लगता है कि पूर्व "तुच्छ विध्वंसक" की परिभाषा के अनुरूप है, जबकि बाद वाला नहीं करता है। तो std::has_trivial_destructor<Widget>::valueहै trueपहले के लिए है, लेकिन falseदूसरे के लिए। उस के निहितार्थ क्या हैं मुझे नहीं पता। :)
GMNNickG

10
एक आभासी विध्वंसक कभी भी तुच्छ नहीं होता है।
ल्यूक डैंटन

@ ल्यूकैंटन: मुझे लगता है कि मेरी आंखें खुल रही हैं और कोड को देखकर भी काम होगा! सही करने के लिए धन्यवाद।
GMANNICKG 1

जवाबों:


103

विध्वंसक की तुलना में निर्माणकर्ताओं के बारे में पूछने पर यह एक पूरी तरह से अलग सवाल है।

यदि आपका विध्वंसक है virtual, तो अंतर नगण्य है, जैसा कि हावर्ड ने बताया है । हालांकि, यदि आपका विध्वंसक गैर-आभासी था , तो यह पूरी तरह से अलग कहानी है। कंस्ट्रक्टरों का भी यही हाल है।

= defaultविशेष सदस्य फ़ंक्शंस के लिए सिंटैक्स का उपयोग करना (डिफ़ॉल्ट कंस्ट्रक्टर, कॉपी / मूव कन्स्ट्रक्टर्स / असाइनमेंट, डिस्ट्रक्टर्स आदि) का अर्थ है कि बस करने के लिए कुछ बहुत अलग {}। उत्तरार्द्ध के साथ, फ़ंक्शन "उपयोगकर्ता-प्रदान" हो जाता है। और वह सब कुछ बदल देता है।

यह C ++ 11 की परिभाषा द्वारा एक तुच्छ वर्ग है:

struct Trivial
{
  int foo;
};

यदि आप एक निर्माण को डिफ़ॉल्ट रूप से करने का प्रयास करते हैं, तो कंपाइलर एक डिफ़ॉल्ट निर्माणकर्ता को स्वचालित रूप से उत्पन्न करेगा। एक ही प्रतिलिपि / आंदोलन और विनाशकारी के लिए जाता है। क्योंकि उपयोगकर्ता ने इन सदस्य कार्यों में से कोई भी प्रदान नहीं किया, इसलिए C ++ 11 विनिर्देश इसे "तुच्छ" वर्ग मानता है। इसलिए ऐसा करना कानूनी है, जैसे कि उनकी सामग्री को इनिशियलाइज़ करना और उन्हें आगे बढ़ाना।

यह:

struct NotTrivial
{
  int foo;

  NotTrivial() {}
};

जैसा कि नाम से पता चलता है, यह अब तुच्छ नहीं है। इसका एक डिफ़ॉल्ट कंस्ट्रक्टर है जो उपयोगकर्ता द्वारा प्रदान किया गया है। इससे कोई फर्क नहीं पड़ता कि यह खाली है; जहां तक ​​C ++ 11 के नियमों का सवाल है, यह एक तुच्छ प्रकार नहीं हो सकता।

यह:

struct Trivial2
{
  int foo;

  Trivial2() = default;
};

जैसा कि नाम से पता चलता है, यह एक तुच्छ प्रकार है। क्यों? क्योंकि आपने कंपाइलर को डिफ़ॉल्ट कंस्ट्रक्टर को स्वचालित रूप से जेनरेट करने के लिए कहा था। कंस्ट्रक्टर इसलिए "उपयोगकर्ता-प्रदान" नहीं है। और इसलिए, प्रकार तुच्छ के रूप में गिना जाता है, क्योंकि इसमें उपयोगकर्ता-प्रदान डिफ़ॉल्ट निर्माता नहीं है।

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


2
तो मुख्य मुद्दा यह प्रतीत होता है कि क्या परिणामी वर्ग तुच्छ है, और अंतर्निहित यह मुद्दा एक विशेष फ़ंक्शन के बीच अंतर है जो उपयोगकर्ता-घोषित किया जा रहा है (जो कि =defaultकार्यों के लिए मामला है) और उपयोगकर्ता-प्रदान (जो मामला है {}) कार्यों के लिए। दोनों उपयोगकर्ता-घोषित और उपयोगकर्ता-प्रदान किए गए फ़ंक्शन अन्य विशेष सदस्य फ़ंक्शन की पीढ़ी को रोक सकते हैं (उदाहरण के लिए, एक उपयोगकर्ता-घोषित विध्वंसक चाल संचालन की पीढ़ी को रोकता है), लेकिन केवल एक उपयोगकर्ता-प्रदान विशेष फ़ंक्शन एक वर्ग को गैर-तुच्छ प्रदान करता है। सही?
KnowItAllWannabe

@KnowItAllWannabe: यह सामान्य विचार है, हाँ।
निकोल बोलस

मैं इसे स्वीकार किए गए उत्तर के रूप में चुन रहा हूं, केवल इसलिए कि यह दोनों निर्माणकर्ताओं को शामिल करता है और (हावर्ड के उत्तर के संदर्भ में) विनाशकारी।
KnowItAllWannabe

यहाँ एक लापता शब्द लगता है "जहाँ तक C ++ 11 के नियमों का सवाल है, आप एक तुच्छ प्रकार का अधिकार" मैं इसे ठीक कर दूंगा लेकिन मैं 100% सुनिश्चित नहीं कर रहा हूं कि क्या इरादा था।
जर्कोडर

2
= defaultअन्य कंस्ट्रक्टरों की उपस्थिति के बावजूद एक डिफ़ॉल्ट निर्माणकर्ता को उत्पन्न करने के लिए मजबूर करने के लिए उपयोगी प्रतीत होता है; यदि कोई अन्य उपयोगकर्ता-घोषित कंस्ट्रक्टर प्रदान किए जाते हैं, तो डिफ़ॉल्ट निर्माता को स्पष्ट रूप से घोषित नहीं किया जाता है।
bgfvdu3w

42

वे दोनों गैर-तुच्छ हैं।

इन दोनों का आधार और सदस्यों के नोइसेप्ट स्पेसिफिकेशन के आधार पर एक ही नॉइसेप्ट स्पेसिफिकेशन है।

एकमात्र अंतर जो मैं अब तक पता लगा रहा हूं वह यह है कि अगर Widgetकोई दुर्गम या हटाए गए विध्वंसक के साथ आधार या सदस्य है:

struct A
{
private:
    ~A();
};

class Widget {
    A a_;
public:
#if 1
   virtual ~Widget() = default;
#else
   virtual ~Widget() {}
#endif
};

तब =defaultसमाधान संकलित होगा, लेकिन Widgetएक विनाशकारी प्रकार नहीं होगा। यानी यदि आप एक को नष्ट करने की कोशिश करते हैं Widget, तो आपको एक संकलन-समय त्रुटि मिलेगी। लेकिन अगर आप ऐसा नहीं करते हैं, तो आपको एक काम करने का कार्यक्रम मिल गया है।

ओटोह, यदि आप उपयोगकर्ता द्वारा प्रदत्त विध्वंसक की आपूर्ति करते हैं , तो चीजें संकलित नहीं करेंगी या नहीं Widget:

test.cpp:8:7: error: field of type 'A' has private destructor
    A a_;
      ^
test.cpp:4:5: note: declared private here
    ~A();
    ^
1 error generated.

9
दिलचस्प: दूसरे शब्दों में, =default;संकलक के साथ विध्वंसक उत्पन्न नहीं करेगा जब तक कि इसका उपयोग नहीं किया जाता है, और इसलिए एक त्रुटि को ट्रिगर नहीं करेगा। यह मेरे लिए अजीब लगता है, भले ही जरूरी नहीं कि बग हो। मैं सोच भी नहीं सकता कि यह व्यवहार मानक में अनिवार्य है।
निक बोगालिस

"फिर = डिफ़ॉल्ट समाधान संकलित करेगा" नहीं यह नहीं होगा। बस में बनाम परीक्षण किया
नैनो

त्रुटि संदेश क्या था, और वीएस का कौन सा संस्करण?
हावर्ड हिनांत

35

के बीच महत्वपूर्ण अंतर

class B {
    public:
    B(){}
    int i;
    int j;
};

तथा

class B {
    public:
    B() = default;
    int i;
    int j;
};

ऐसा B() = default;माना जाता है कि डिफॉल्ट कंस्ट्रक्टर को उपयोगकर्ता द्वारा परिभाषित नहीं माना जाता है । इसका अर्थ है कि मूल्य-आरंभ के मामले में जैसा कि

B* pb = new B();  // use of () triggers value-initialization

एक तरह के निर्माण का उपयोग नहीं करने वाले विशेष प्रकार के आरंभीकरण जगह ले लेंगे और बिल्ट-इन प्रकारों के परिणामस्वरूप शून्य-आरंभीकरण होगा । इस मामले में B(){}जगह नहीं होगी। C ++ मानक n3337 § 8.5 / 7 कहता है

टी प्रकार की एक वस्तु को महत्व देने के लिए इसका मतलब है:

- यदि T, उपयोगकर्ता द्वारा प्रदान किए गए कंस्ट्रक्टर (12.1 ) के साथ एक (संभवतः cv-योग्य) वर्ग प्रकार (क्लाज 9) है , तो T के लिए डिफॉल्ट कंस्ट्रक्टर को बुलाया जाता है (और यदि टी का कोई सुलभ डिफॉल्ट कंस्ट्रक्टर नहीं है, तो इनिशियलाइज़ेशन बीमार हो जाता है) );

- यदि T, उपयोगकर्ता द्वारा प्रदान किए गए कंस्ट्रक्टर के बिना एक (संभवतः cv-योग्य) गैर-यूनियन वर्ग प्रकार है , तो ऑब्जेक्ट शून्य-आरंभीकृत है और, यदि T के अंतर्निहित घोषित डिफ़ॉल्ट गैर-तुच्छ है, तो उस कंस्ट्रक्टर को कहा जाता है।

- यदि टी एक सरणी प्रकार है, तो प्रत्येक तत्व मूल्य-आरंभिक है; - अन्यथा, वस्तु शून्य-आरंभिक है।

उदाहरण के लिए:

#include <iostream>

class A {
    public:
    A(){}
    int i;
    int j;
};

class B {
    public:
    B() = default;
    int i;
    int j;
};

int main()
{
    for( int i = 0; i < 100; ++i) {
        A* pa = new A();
        B* pb = new B();
        std::cout << pa->i << "," << pa->j << std::endl;
        std::cout << pb->i << "," << pb->j << std::endl;
        delete pa;
        delete pb;
    }
  return 0;
}

संभावित परिणाम:

0,0
0,0
145084416,0
0,0
145084432,0
0,0
145084416,0
//...

http://ideone.com/k8mBrd


फिर, "{}" और "= डिफ़ॉल्ट" हमेशा एक std :: string ideone.com/LMv5Uf को इनिशियलाइज़ क्यों करते हैं ?
नवाफेल bgh

1
@nawfelbgh डिफ़ॉल्ट कंस्ट्रक्टर A () {} std :: string के लिए डिफॉल्ट कंस्ट्रक्टर को कॉल करता है क्योंकि यह गैर-POD प्रकार है। Std :: string का डिफ़ॉल्ट ctor इसे खाली, 0 साइज स्ट्रिंग के लिए इनिशियलाइज़ करता है। स्केलर के लिए डिफ़ॉल्ट ctor कुछ भी नहीं करता है: स्वचालित भंडारण अवधि (और उनके उप-विषय) के साथ वस्तुओं को अनिश्चितकालीन मानों के लिए प्रारंभ किया जाता है।
ः 00
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.