नए के बिना c ++ में कंस्ट्रक्टर्स को कॉल करना


142

मैंने अक्सर देखा है कि लोग C ++ का उपयोग करके ऑब्जेक्ट बनाते हैं

Thing myThing("asdf");

इसके अलावा:

Thing myThing = Thing("asdf");

यह काम करने के लिए (gcc का उपयोग करके) लगता है, कम से कम जब तक कोई टेम्पलेट शामिल नहीं हैं। मेरा प्रश्न अब, क्या पहली पंक्ति सही है और यदि मुझे इसका उपयोग करना चाहिए?


25
कोई भी रूप नया नहीं है।
डैनियल डारानास

13
दूसरा फॉर्म कॉपी कंस्ट्रक्टर का उपयोग करेगा इसलिए नहीं, वे समकक्ष नहीं हैं।
एडवर्ड स्ट्रेंज

मैंने इसके साथ थोड़ा सा खेला, पहला तरीका कभी-कभी असफल हो जाता है जब पैरामीटर पैरामीटर के साथ टेम्पलेट्स का उपयोग किया जाता है ..
Nils

1
उह और मुझे उस के लिए "अच्छा सवाल" बिल्ला मिला, क्या शर्म की बात है!
निल्स

जवाबों:


153

दोनों पंक्तियाँ वास्तव में सही हैं, लेकिन अलग-अलग चीजें हैं।

पहली पंक्ति प्रारूप के एक निर्माता को कॉल करके स्टैक पर एक नई वस्तु बनाती है Thing(const char*)

दूसरा वाला थोड़ा अधिक जटिल है। यह अनिवार्य रूप से निम्नलिखित करता है

  1. Thingकंस्ट्रक्टर का उपयोग करके प्रकार का ऑब्जेक्ट बनाएंThing(const char*)
  2. Thingकंस्ट्रक्टर का उपयोग करके प्रकार का ऑब्जेक्ट बनाएंThing(const Thing&)
  3. ~Thing()चरण # 1 में बनाई गई वस्तु पर कॉल करें

7
मुझे लगता है कि इन प्रकार के कार्यों को अनुकूलित किया जाता है और इसलिए, प्रदर्शन पहलुओं में महत्वपूर्ण रूप से भिन्न नहीं होते हैं।
एम। विलियम्स

14
मुझे नहीं लगता कि आपके कदम काफी सही हैं। Thing myThing = Thing(...)असाइनमेंट ऑपरेटर का उपयोग नहीं करता है, यह अभी भी कहने की तरह कॉपी-निर्माण किया गया है Thing myThing(Thing(...)), और इसमें एक डिफ़ॉल्ट-निर्माण शामिल नहीं है Thing(संपादित करें: पोस्ट को बाद में ठीक किया गया था)
AshleysBrain

1
तो आप कह सकते हैं कि दूसरी पंक्ति गलत है, क्योंकि यह बिना किसी स्पष्ट कारण के संसाधनों को बर्बाद करता है। निश्चित रूप से यह संभव है कि पहले उदाहरण का निर्माण कुछ साइड इफेक्ट्स के लिए जानबूझकर किया गया हो, लेकिन यह और भी बुरा है (स्टाइल से)।
एम.के.

3
नहीं, @ जारेड, इसकी गारंटी नहीं है। लेकिन फिर भी यदि कंपाइलर उस ऑप्टिमाइज़ेशन को चुनता है, तो भी कॉपी कंस्ट्रक्टर को एक्सेस करने की आवश्यकता होती है (यानी, संरक्षित या निजी नहीं), भले ही वह कार्यान्वित या कॉल न किया गया हो।
रॉब कैनेडी

3
ऐसा प्रतीत होता है कि कॉपी को तब भी उतारा जा सकता है, जब कॉपी कंस्ट्रक्टर के साइड-इफेक्ट्स हों - मेरा जवाब देखें: stackoverflow.com/questions/2722879/…
डगलस लीडर

31

मुझे लगता है कि दूसरी पंक्ति के साथ आप वास्तव में मतलब है:

Thing *thing = new Thing("uiae");

जो नई गतिशील वस्तुओं (गतिशील बंधन और बहुरूपता के लिए आवश्यक) बनाने और एक सूचक को अपना पता संग्रहीत करने का मानक तरीका होगा । आपका कोड वही करता है, जिसका वर्णन JaredPar ने किया है, अर्थात् दो ऑब्जेक्ट्स बनाना (एक पास हुआ const char*, दूसरा पास हुआ const Thing&), और फिर ~Thing()पहली वस्तु ( const char*एक) पर डिस्ट्रक्टर ( ) कॉल करना ।

इसके विपरीत, यह:

Thing thing("uiae");

एक स्थिर वस्तु बनाता है जो वर्तमान दायरे से बाहर निकलने पर स्वचालित रूप से नष्ट हो जाती है।


1
दुर्भाग्य से, यह वास्तव में auto_ptr, unique_ptr, या संबंधित का उपयोग करने के बजाय नई गतिशील वस्तुओं को बनाने का सबसे आम तरीका है।
फ्रेड नर्क

3
ओपी का सवाल सही था, यह जवाब पूरी तरह से एक और मुद्दे पर चिंता करता है (देखें @
JaredPar

21

संकलक दूसरे रूप को पहले रूप में अच्छी तरह से अनुकूलित कर सकता है, लेकिन यह करने की आवश्यकता नहीं है।

#include <iostream>

class A
{
    public:
        A() { std::cerr << "Empty constructor" << std::endl; }
        A(const A&) { std::cerr << "Copy constructor" << std::endl; }
        A(const char* str) { std::cerr << "char constructor: " << str << std::endl; }
        ~A() { std::cerr << "destructor" << std::endl; }
};

void direct()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void assignment()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a = A(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void prove_copy_constructor_is_called()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    A b = a;
    static_cast<void>(b); // avoid warnings about unused variables
}

int main()
{
    direct();
    assignment();
    prove_copy_constructor_is_called();
    return 0;
}

4.4 gcc से आउटपुट:

TEST: direct
char constructor: direct
destructor

TEST: assignment
char constructor: assignment
destructor

TEST: prove_copy_constructor_is_called
char constructor: prove_copy_constructor_is_called
Copy constructor
destructor
destructor

स्थैतिक जातियों का उद्देश्य क्या है?
स्टीफन क्रॉस

1
@ स्टीफन अप्रयुक्त चर के बारे में चेतावनी से बचें।
डगलस लीडर

10

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


ढेर के रूप में ढेर पर करने का विरोध किया पर instantiating वस्तुओं के बीच का अंतर (जो उपयोग कर रहा है के साथ अपरिचित उन लोगों के लिए नए और प्रयोग नहीं कर नया ), यहाँ एक अच्छा धागा है।
edmqkk

2

आदर्श रूप से, एक संकलक दूसरे का अनुकूलन करेगा, लेकिन इसकी आवश्यकता नहीं है। पहला तरीका सबसे अच्छा है। हालांकि, सी ++ में स्टैक और हीप के बीच के अंतर को समझना बहुत महत्वपूर्ण है, साइन इन करने के लिए आपको अपनी हीप मेमोरी का प्रबंधन करना होगा।


क्या कंपाइलर गारंटी दे सकता है कि कॉपी कंस्ट्रक्टर के साइड इफेक्ट्स नहीं हैं (जैसे कि I / O)?
स्टीफन

@Stephen - यह कोई फर्क नहीं पड़ता कि अगर कॉपी कंस्ट्रक्टर I / O करता है - तो मेरा उत्तर देखें stackoverflow.com/questions/2722879/…
डगलस लीडर

ठीक है, मैं देख रहा हूं, कंपाइलर को दूसरे फॉर्म को पहले में बदलने की अनुमति है और इस तरह कॉपी कंस्ट्रक्टर को कॉल से बचा जाता है।
स्टीफन क्रॉस

2

मैंने इसके साथ थोड़ा सा खेला और वाक्यविन्यास काफी अजीब लगता है जब एक निर्माता कोई तर्क नहीं लेता है। मुझे एक उदाहरण देने दें:

#include <iostream> 

using namespace std;

class Thing
{
public:
    Thing();
};

Thing::Thing()
{
    cout << "Hi" << endl;
}

int main()
{
    //Thing myThing(); // Does not work
    Thing myThing; // Works

}

इसलिए सिर्फ Thing myThing w / o कोष्ठक लिखना वास्तव में कंस्ट्रक्टर को कॉल करता है, जबकि Thing myThing () आपको कंपाइलर चीज़ बनाता है जिसे आप फ़ंक्शन पॉइंटर या कुछ बनाना चाहते हैं ?? !!


6
यह C ++ में एक प्रसिद्ध सिंटैक्टिक अस्पष्टता है। जब आप "int rand ()" लिखते हैं, तो कंपाइलर को पता नहीं चल सकता है कि क्या आपका मतलब है "int और default-initialize it" या "function rand घोषित करें"। नियम यह है कि यह जब भी संभव हो बाद को चुनता है।
jpalecek


2

करने के लिए संलग्न में JaredPar जवाब

अस्थायी ऑब्जेक्ट के साथ 1-सामान्य ctor, 2nd-function-like-ctor।

इस स्रोत को कहीं और संकलित करें http://melpon.org/wandbox/ विभिन्न संकलक के साथ

// turn off rvo for clang, gcc with '-fno-elide-constructors'

#include <stdio.h>
class Thing {
public:
    Thing(const char*){puts(__FUNCTION__ );}
    Thing(const Thing&){puts(__FUNCTION__ );}   
    ~Thing(){puts(__FUNCTION__);}
};
int main(int /*argc*/, const char** /*argv*/) {
    Thing myThing = Thing("asdf");
}

और आपको परिणाम दिखाई देगा।

आईएसओ / आईईसी 14882 2003-10-15 से

8.5, भाग 12

आपका पहला, दूसरा निर्माण प्रत्यक्ष-आरंभीकरण कहलाता है

12.1, भाग 13

एक कार्यात्मक संकेतन प्रकार रूपांतरण (5.2.3) का उपयोग इसके प्रकार की नई वस्तुओं को बनाने के लिए किया जा सकता है। [नोट: सिंटैक्स कंस्ट्रक्टर के स्पष्ट कॉल की तरह दिखता है। ] ... इस तरह से बनाई गई एक वस्तु अनाम है। [नोट: 12.2 अस्थायी वस्तुओं के जीवनकाल का वर्णन करता है। ] [ध्यान दें: स्पष्ट कंस्ट्रक्टर कॉल लैवल्स का उत्पादन नहीं करते हैं, 3.10 देखें। ]


आरवीओ के बारे में कहां पढ़ें:

12 विशेष सदस्य कार्य / 12.8 कक्षा की वस्तुओं की प्रतिलिपि बनाना / भाग 15

जब कुछ मानदंडों को पूरा किया जाता है, तो एक कार्यान्वयन को एक क्लास ऑब्जेक्ट की कॉपी निर्माण को छोड़ना होता है, भले ही कॉपी कंस्ट्रक्टर और / या ऑब्जेक्ट के लिए विनाशकारी साइड इफेक्ट हो

इस तरह के कॉपी-व्यवहार को देखने के लिए टिप्पणी से संकलक ध्वज के साथ इसे बंद करें)

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