इस विशिष्ट मामले में, क्या एक सदस्य इनिशियलाइज़र सूची का उपयोग करने और एक निर्माणकर्ता में मान निर्दिष्ट करने के बीच अंतर है?


90

आंतरिक रूप से और उत्पन्न कोड के बारे में, क्या वास्तव में अंतर है:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

तथा

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

धन्यवाद...

जवाबों:


60

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


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

11
यह केवल वर्णित के रूप में काम करता है जब चर संदर्भ या स्थिर नहीं होते हैं, यदि वे या तो निरंतर या संदर्भ हैं तो यह प्रारंभिक सूची का उपयोग किए बिना संकलन भी नहीं करेगा।
23

77

आपको निरंतर सदस्यों, संदर्भों और आधार वर्ग को आरंभ करने के लिए आरंभीकरण सूची का उपयोग करने की आवश्यकता है

जब आपको निरंतर सदस्य को संदर्भित करने की आवश्यकता होती है, तो बेस निर्माणकर्ताओं को संदर्भ और पास पैरामीटर, जैसा कि टिप्पणियों में बताया गया है, आपको आरंभीकरण सूची का उपयोग करने की आवश्यकता है।

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

उदाहरण (गैर-थकावट) वर्ग / संरचना में संदर्भ शामिल हैं:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

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

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

4
और अगर _capacity, _dataऔर _lenएक सुलभ डिफ़ॉल्ट निर्माणकर्ताओं के बिना वर्ग प्रकार हैं?
सीबी बेली

2
आप कहते हैं कि कंस्ट्रक्टर उपलब्ध हैं, यदि आपको अधिक मान सेट करने की आवश्यकता है, तो आप उन्हें अपने कंस्ट्रक्टर के शरीर से कॉल करते हैं। यहाँ अंतर यह है कि आप constकंस्ट्रक्टर के शरीर में सदस्य को इनिशियलाइज़ नहीं कर सकते , आपको इनिशियलाइज़ेशन लिस्ट का उपयोग करना होगा - नॉन constमेंबर्स को इनिशियलाइज़ेशन लिस्ट में या कंस्ट्रक्टर के बॉडी में इनिशियलाइज़ किया जा सकता है।
स्टेफानबी

@stefan: आप कंस्ट्रक्टरों को कॉल नहीं कर सकते। बिना डिफॉल्ट कंस्ट्रक्टर वाले एक क्लास को कांस्टेबल मेंबर्स की तरह इनिशियलाइज़र लिस्ट में इनिशियलाइज़ करना पड़ता है।
GManNickG

2
आपको इनिशियलाइज़ेशन लिस्ट में संदर्भों को भी आरंभ करना होगा
एंड्री टायलेको

2
@stefanB: अगर मैं वास्तव में आप कर रहे हैं यह नहीं समझा है तो मेरी माफी। आपके उत्तर में आपने केवल यह बताया है कि किसी आधार या सदस्य का नाम इनिशियलाइज़र-लिस्ट में होना चाहिए, आपने यह नहीं बताया है कि इनिशियलाइज़र-लिस्ट में इनिशियलाइज़ करने और कंस्ट्रक्टर बॉडी में असाइन करने के बीच वास्तव में वैचारिक अंतर क्या है जो मेरी गलतफहमी है से आया होगा।
सीबी बेली

18

हाँ। पहले मामले में आप घोषणा कर सकते हैं _capacity, _dataऔर _lenस्थिरांक के रूप में:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

यह महत्वपूर्ण होगा यदि आप constरनटाइम के दौरान अपने मूल्यों की गणना करते समय इन उदाहरण चरों की सुनिश्चित करना चाहते हैं , उदाहरण के लिए:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

constउदाहरणों को प्रारंभिक सूची में आरंभीकृत किया जाना चाहिए या अंतर्निहित प्रकारों को सार्वजनिक पैरामीटर रहित कंस्ट्रक्टर (जो आदिम प्रकार करते हैं) प्रदान करना होगा।


2
वही संदर्भों के लिए जाता है। यदि आपकी कक्षा में संदर्भ सदस्य हैं, तो उन्हें प्रारंभिक सूची में आरंभीकृत किया जाना चाहिए।
मार्क रैंसम

7

मुझे लगता है कि यह लिंक http://www.cplusplus.com/forum/articles/17820/ एक उत्कृष्ट व्याख्या देता है - विशेष रूप से C ++ के लिए उन लोगों के लिए।

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

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


5

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


3

एक बड़ा अंतर यह है कि असाइनमेंट एक मूल वर्ग के सदस्यों को शुरू कर सकता है; इनिशियलाइज़र केवल वर्तमान वर्ग के दायरे में घोषित सदस्यों पर काम करता है।


2

शामिल प्रकारों पर निर्भर करता है। अंतर के बीच समान है

std::string a;
a = "hai";

तथा

std::string a("hai");

जहाँ दूसरा रूप इनिशियलाइज़ेशन लिस्ट है- यानी, अगर कंस्ट्रक्टर तर्कों के लिए टाइप की आवश्यकता है या कंस्ट्रक्शन तर्कों के साथ अधिक कुशल है तो इससे फर्क पड़ता है।


1

वास्तविक अंतर यह है कि कैसे जीसीसी संकलक मशीन कोड उत्पन्न करता है और मेमोरी को नीचे ले जाता है। के बारे में बताएं:

  • (चरण 1) init body (init सूची सहित) से पहले: संकलक वर्ग के लिए आवश्यक मेमोरी आवंटित करता है। वर्ग पहले से ही जीवित है!
  • (चरण 2) init बॉडी में: मेमोरी आवंटित होने के बाद से, हर असाइनमेंट अब पहले से ही बाहर निकलने / 'इनिशियलाइज्ड' वैरिएबल पर एक ऑपरेशन को इंगित करता है।

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

  1. कॉन्स्ट टाइप सदस्यों को इनिट बॉडी से पहले इनिशियलाइज़ किया जाना चाहिए।
  2. चरण 1 के बाद, कोई भी लेखन ऑपरेशन केवल गैर-स्थिर सदस्यों के लिए मान्य है।

1

बेस क्लास इंस्टेंसेस और नॉन-स्टैटिक मेंबर वेरिएबल्स को इनिशियलाइज़ करने का एक ही तरीका है और वह है इनिशियलाइज़र लिस्ट।

यदि आप अपने निर्माता की प्रारंभिक सूची में एक आधार या गैर-स्थैतिक सदस्य चर निर्दिष्ट नहीं करते हैं, तो वह सदस्य या आधार या तो डिफ़ॉल्ट-प्रारंभिक हो जाएगा (यदि सदस्य / आधार गैर-पीओडी वर्ग प्रकार या गैर-पीओडी वर्ग का सरणी है) प्रकार) या अन्यथा असंवैधानिक छोड़ दिया है।

एक बार निर्माणकर्ता के शरीर में प्रवेश करने के बाद, सभी ठिकानों या सदस्यों को आरंभिक या छोड़ दिया गया होगा (यानी उनका अनिश्चित मान होगा)। निर्माणकर्ता निकाय में यह प्रभावित करने का कोई अवसर नहीं है कि उन्हें कैसे आरंभ किया जाए।

आप कंस्ट्रक्टर बॉडी में सदस्यों के लिए नए मान निर्दिष्ट करने में सक्षम हो सकते हैं, लेकिन उन constसदस्यों या सदस्यों को वर्ग प्रकार के लिए असाइन करना संभव नहीं है जिन्हें गैर-असाइन किया गया है और यह संदर्भों को फिर से बनाना संभव नहीं है।

निर्मित प्रकारों और कुछ उपयोगकर्ता-परिभाषित प्रकारों के लिए, कंस्ट्रक्टर बॉडी में असाइन करना बिल्कुल वैसा ही प्रभाव हो सकता है जैसा कि इनिशियलाइज़र सूची में समान मूल्य के साथ इनिशियलाइज़ करना।

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


0

यदि आप एक प्रारंभिक सूची लिखते हैं, तो आप सभी एक ही चरण में करते हैं; यदि आप एक initilizer सूची नहीं लिखते हैं, तो आप 2 चरण करेंगे: एक घोषणा के लिए और एक मान के लिए।


0

एक कंस्ट्रक्टर में इनिशियलाइज़ेशन लिस्ट और इनिशियलाइज़ेशन स्टेटमेंट के बीच अंतर होता है। आइए नीचे दिए गए कोड पर विचार करें:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

जब MyClass का उपयोग किया जाता है, तो एक कंस्ट्रक्टर निष्पादित किए गए पहले कथन से पहले सभी सदस्यों को आरंभीकृत किया जाएगा।

लेकिन, जब MyClass2 का उपयोग किया जाता है, तो सभी सदस्यों को आरंभिक नहीं किया जाता है जब एक निर्माणकर्ता के पहले कथन को निष्पादित किया जाता है।

बाद के मामले में, किसी व्यक्ति द्वारा एक निश्चित सदस्य को इनिशियलाइज़ किए जाने से पहले कंस्ट्रक्शन में कुछ कोड जोड़ने पर रिग्रेशन की समस्या हो सकती है।


0

यहाँ एक बिंदु यह है कि मैंने दूसरों को इसका उल्लेख करते नहीं देखा:

class temp{
public:
   temp(int var);
};

अस्थायी वर्ग में एक डिफ़ॉल्ट ctor नहीं है। जब हम इसका अनुसरण दूसरी कक्षा में करते हैं:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

कोड संकलित नहीं करेगा, क्योंकि कंपाइलर को पता नहीं है कि कैसे इनिशियलाइज़ किया जाए obj, क्योंकि इससे एक स्पष्ट ctor प्राप्त होता है, जो एक int value प्राप्त करता है, इसलिए हमें ctor को निम्नानुसार बदलना होगा:

mainClass(int sth):obj(sth){}

तो, यह केवल कास्ट और संदर्भों के बारे में नहीं है!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.