किसी फ़ंक्शन से 'वेक्टर' वापस करना क्यों ठीक है?


108

कृपया इस कोड पर विचार करें। मैंने कई बार इस प्रकार का कोड देखा है। wordsएक स्थानीय वेक्टर है। किसी फ़ंक्शन से इसे वापस करना कैसे संभव है?

क्या हम गारंटी दे सकते हैं कि यह मर नहीं जाएगा?

 std::vector<std::string> read_file(const std::string& path)
 {
    std::ifstream file("E:\\names.txt");

    if (!file.is_open())
    {
        std::cerr << "Unable to open file" << "\n";
        std::exit(-1);
    }

    std::vector<string> words;//this vector will be returned
    std::string token;

    while (std::getline(file, token, ','))
    {
        words.push_back(token);
    }

    return words;
}

18
लौटते समय इसकी नकल हो जाती है।
गीतुयनाओ

6
कोई भी गारंटी देता है .. यह होगा मर जाते हैं, लेकिन उसके बाद यह की नकल की है।
मारून

7
आपको केवल एक समस्या है यदि आपका फ़ंक्शन एक संदर्भ देता है:std::vector<std::string>&
Caduchon

14
@songyuanyao नहीं, इसे स्थानांतरित किया जाएगा।
ठीक

15
@songyuanyao हाँ। C ++ 11 वर्तमान मानक है, इसलिए C ++ 11 C ++ है।
ठीक

जवाबों:


68

क्या हम गारंटी दे सकते हैं कि यह मर नहीं जाएगा?

जब तक कोई संदर्भ नहीं लौटा, तब तक ऐसा करना पूरी तरह से ठीक है। wordsपरिणाम प्राप्त करने वाले चर में ले जाया जाएगा।

स्थानीय चर दायरे से बाहर हो जाएगा। बाद में इसे ले जाया गया (या कॉपी किया गया)।


2
लेकिन कुशल है या वेक्टर के लिए कोई प्रदर्शन चिंताएं हैं जो 1000 प्रविष्टियों को रोक सकती हैं?
zar

@zadane क्या यह सवाल था? इसके अलावा मैंने ऐसे मूविंग का उल्लेख किया है जो वास्तव में रिटर्न वैल्यू की एक प्रति लेने से बचेंगे (कम से कम वर्तमान मानक के साथ उपलब्ध)।
atν 20

2
नहीं वास्तव में सवाल नहीं है, लेकिन मैं स्वतंत्र रूप से उस दृष्टिकोण से जवाब की तलाश में था। मुझे नहीं पता कि अगर मैं अपना प्रश्न पोस्ट करता हूं, मुझे डर है कि वे इसे इस डुप्लिकेट के रूप में चिह्नित करेंगे :)
zar

@ ज़ादेन "मुझे डर है कि वे इसे इस के डुप्लिकेट चिह्नित करेंगे" अच्छी तरह से हो सकता है। उच्च मतदान के जवाब पर बस एक नजर । यहां तक ​​कि पुराने कार्यान्वयनों के लिए आपको चिंता नहीं करनी चाहिए, उन कंपाइलरों द्वारा वैसे भी ज्यादातर सही ढंग से अनुकूलित किया जाएगा।
atν 21α ῥεῖ

107

पूर्व C ++ 11:

फ़ंक्शन स्थानीय चर नहीं लौटाएगा, बल्कि इसकी एक प्रति। हालाँकि आपका कंपाइलर एक ऑप्टिमाइज़ेशन कर सकता है जहाँ कोई वास्तविक कॉपी एक्शन नहीं किया जाता है।

देखें इस सवाल और जवाब अधिक जानकारी के लिए।

सी ++ 11:

फ़ंक्शन मान को स्थानांतरित करेगा। देखें इस उत्तर अधिक जानकारी के लिए।


2
इसे कॉपी नहीं किया जाएगा। इसकी गारंटी है।
सही

1
क्या यह C ++ 10 के लिए भी लागू होता है?
टिम मेयर

28
C ++ 10 जैसी कोई चीज नहीं है।
ठीक

C ++ 03 में कोई भी कदम शब्दार्थ नहीं था (लेकिन प्रतिलिपि को बढ़ाया जा सकता है, हालांकि), लेकिन C ++ C ++ 11 है और प्रश्न C ++ के बारे में था।
ठीक

19
C ++ 11 से अनन्य प्रश्नों के लिए एक अलग टैग है। हम में से कई, विशेष रूप से बड़ी कंपनियों में प्रोग्रामर अभी भी संकलक के लिए अटके हुए हैं जो अभी तक C ++ 11 का पूरी तरह से समर्थन नहीं करते हैं। मैंने दोनों मानकों के सटीक होने के सवाल को अद्यतन किया।
टिम मेयर

26

मुझे लगता है कि आप C (और C ++) में समस्या का जिक्र कर रहे हैं कि किसी फ़ंक्शन से सरणी वापस करने की अनुमति नहीं है (या कम से कम अपेक्षित रूप से काम नहीं करेगा) - यह इसलिए है क्योंकि सरणी वापसी होगी (यदि आप इसे लिखते हैं सरल फॉर्म) स्टैक पर वास्तविक सरणी के लिए एक पॉइंटर लौटाता है, जो तब फ़ंक्शन वापस आने पर तुरंत हटा दिया जाता है।

लेकिन इस मामले में, यह काम करता है, क्योंकि std::vectorएक वर्ग है, और कक्षाएं, जैसे संरचना, कॉल (और कर सकते हैं) को कॉलिंग संदर्भ में कॉपी किया जा सकता है। [वास्तव में, अधिकांश कंपाइलर्स इस विशेष प्रकार की कॉपी को "रिटर्न वैल्यू ऑप्टिमाइजेशन" नामक कुछ का उपयोग करके अनुकूलित करेंगे, विशेष रूप से बड़ी वस्तुओं की नकल करने से बचने के लिए शुरू की गई जब वे एक फ़ंक्शन से वापस आ जाते हैं, लेकिन यह एक अनुकूलन है, और प्रोग्रामर के दृष्टिकोण से, यह होगा व्यवहार करें मानो असाइनमेंट कंस्ट्रक्टर को ऑब्जेक्ट के लिए बुलाया गया था]

जब तक आप एक पॉइंटर या किसी ऐसी चीज का संदर्भ नहीं देते हैं, जो कि वापस लौटने वाले फ़ंक्शन के भीतर है, तो आप ठीक हैं।


13

व्यवहार को अच्छी तरह से समझने के लिए, आप इस कोड को चला सकते हैं:

#include <iostream>

class MyClass
{
  public:
    MyClass() { std::cout << "run constructor MyClass::MyClass()" << std::endl; }
    ~MyClass() { std::cout << "run destructor MyClass::~MyClass()" << std::endl; }
    MyClass(const MyClass& x) { std::cout << "run copy constructor MyClass::MyClass(const MyClass&)" << std::endl; }
    MyClass& operator = (const MyClass& x) { std::cout << "run assignation MyClass::operator=(const MyClass&)" << std::endl; }
};

MyClass my_function()
{
  std::cout << "run my_function()" << std::endl;
  MyClass a;
  std::cout << "my_function is going to return a..." << std::endl;
  return a;
}

int main(int argc, char** argv)
{
  MyClass b = my_function();

  MyClass c;
  c = my_function();

  return 0;
}

आउटपुट निम्नलिखित है:

run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run constructor MyClass::MyClass()
run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run assignation MyClass::operator=(const MyClass&)
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()

ध्यान दें कि यह उदाहरण C ++ 03 संदर्भ में प्रदान किया गया था, इसे C ++> = 11 के लिए बेहतर बनाया जा सकता है


1
यह उदाहरण अधिक पूर्ण होगा यदि इसमें मूव कंस्ट्रक्टर और मूव असाइनमेंट ऑपरेटर भी शामिल हो, और न केवल कंस्ट्रक्टर को कॉपी करें और असाइनमेंट ऑपरेटर को कॉपी करें। (यदि चाल कार्य मौजूद नहीं हैं, तो कॉपी वाले का उपयोग किया जाएगा।)
कुछ लड़के

@SomeGuy मैं सहमत हूं, लेकिन मैं C ++ 11 का उपयोग नहीं करता हूं। मेरे पास वह ज्ञान नहीं है जो मेरे पास नहीं है। मैं एक नोट जोड़ देता हूं। C ++> = 11. :-)
Caduchon

-5

मैं सहमत नहीं हूं और वापस लौटने की सिफारिश नहीं करता हूं vector:

vector <double> vectorial(vector <double> a, vector <double> b)
{
    vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
    return c;
}

यह बहुत तेज़ है:

void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
    c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}

मैंने रिलीज़ मोड में निम्न परिणामों के साथ Visual Studio 2017 पर परीक्षण किया:

8.01 MOPs संदर्भ
5.09 MOPs वेक्टर से लौटते हुए

डिबग मोड में, चीजें बहुत खराब होती हैं:

0.053
एमओपीएस संदर्भ के अनुसार 0.034 एमओपी रिटर्न वेक्टर द्वारा


-10

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

संदर्भ / पॉइंटर पर निर्णय के साथ एक वापसी पैरामीटर के माध्यम से आदर्श समाधान को लागू किया जाना चाहिए और एक डिस्क्रिप्टर के रूप में "कॉन्स्ट \" y \ "नेस का उचित उपयोग।

इसके शीर्ष पर, आपको महसूस करना चाहिए कि C और C ++ में एक सरणी पर लेबल प्रभावी रूप से एक सूचक है और इसकी सदस्यता प्रभावी रूप से एक ऑफसेट या एक अतिरिक्त प्रतीक है।

इसलिए लेबल या ptr array_ptr === सरणी लेबल इस प्रकार foo [ऑफ़सेट] लौटा रहा है, जो वास्तव में मेमोरी पॉइंटर लोकेशन foo + ऑफ़सेट ऑफ़ रिटर्न प्रकार में रिटर्न एलिमेंट कह रहा है।


5
..........क्या। यह स्पष्ट लगता है कि आप "डिजाइन की विफलता" जैसे आरोपों के आसपास फेंकने के लिए योग्य नहीं हैं। और वास्तव में, आरवीओ और मूव ऑपरेशंस द्वारा मूल्य शब्दार्थ का प्रचार आधुनिक सी ++ शैली की मुख्य सफलता में से एक है। लेकिन आप कच्चे सरणियों और बिंदुओं के बारे में सोचते हुए फंस गए हैं, इसलिए मैं आपसे यह उम्मीद नहीं करूंगा।
अंडरस्कोर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.