2017-07-12 10 views
7

मैं मेरे सवाल का वर्णन करने के लिए निम्न उदाहरण दे:सी ++ में स्वयं परिभाषित कक्षा के लिए + ऑपरेटर मेमोरी खपत को कैसे कम करें?

operator + के संबंध में
class BigClass 
{ 
public: 
    static int destruct_num; 
    friend BigClass operator + (const BigClass &obj1, const BigClass &obj2); 
    std::vector<int> abc; 

    BigClass() 
    { 

    } 

    ~BigClass() 
    { 
     destruct_num++; 
     std::cout << "BigClass destructor " <<destruct_num<< std::endl; 

    } 

    BigClass(BigClass&& bc) :abc(std::move(bc.abc)) 
    { 
     std::cout << "move operation is involed" << std::endl; 
    } 
}; 

int BigClass::destruct_num = 0; 

BigClass operator + (const BigClass &obj1, const BigClass &obj2) 
{ 
    BigClass temp; 
    temp.abc = obj1.abc; 
    temp.abc.insert(temp.abc.end(), obj2.abc.begin(), obj2.abc.end()); 

    return temp; 

} 

int main(void) 
{ 

    BigClass a; 
    a.abc = { 1,2,3 }; 
    BigClass b; 
    b.abc = { 5,6,7 }; 
    BigClass c = a + b; 

// for (auto& v : c.abc) 
//  std::cout << v << " "; 

    return 0; 


} 

एक समस्या यह एक अस्थायी BigClass वस्तु अस्थायी उत्पन्न करने के लिए और फिर इसे वापस है कि हम है। क्या इस बोझ को कम करने के लिए कुछ रास्ता हैं?

+0

संभावित डुप्लिकेट: https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading – Rakete1111

+2

प्रतिलिपि इलिजन? http://en.cppreference.com/w/cpp/language/copy_elision – cppBeginner

+4

आप अस्थायी से बच सकते हैं, लेकिन एक नया उदाहरण बनाने से बचने का एकमात्र तरीका 'ऑपरेटर =' का उपयोग नहीं करना है। 'ऑपरेटर + =' आपको एक नया उदाहरण बनाने की आवश्यकता नहीं है – user463035818

उत्तर

7
आम तौर पर

:

[...] compilers अनुमति दी जाती है, लेकिन प्रति छोड़ करने की आवश्यकता नहीं [...]

वैसे भी अपने कार्य किसी भी आधुनिक संकलक द्वारा अनुकूलित किया जाना चाहिए क्योंकि copy elision

Here एक उदाहरण:

operator+ का परिणाम विधानसभा खंड में है:

call operator+(BigClass const&, BigClass const&) 
addl $12, %esp 

आप देख सकते हैं, कोई प्रतिलिपि निर्माता आदेश परिणाम नकल करने के लिए शुरू हो जाती है।

वास्तव में, हमने में प्रति इलिजन अनुकूलन अक्षम अगर जीसीसी, result परिवर्तन:

call operator+(BigClass const&, BigClass const&) 
addl $12, %esp 
subl $8, %esp 
leal -20(%ebp), %eax 
pushl %eax 
leal -56(%ebp), %eax 
pushl %eax 
call BigClass::BigClass(BigClass&&) 
addl $16, %esp 
subl $12, %esp 
leal -20(%ebp), %eax 
pushl %eax 
call BigClass::~BigClass() 
addl $16, %esp 

operator+ की कॉल की नकल करने के बाद (या इस मामले में कदम) निर्माता कहा जाता है, और अस्थायी वस्तु के विनाशक के बाद।

ध्यान दें कि प्रतिलिपि elision भी ऑप्टिमाइज़ेशन अक्षम करने (-O0) प्राप्त किया जाता है।

वही परिणाम पुराने संस्करण के साथ प्राप्त किया जाता है: GCC 4.4.7


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

कॉलर को उस स्थान के आरक्षण की मांग करते हुए, एक संभावित समाधान फ़ंक्शन के अंदर अस्थायी चर के आवंटन से बचने के लिए है। ऐसा करने के लिए, आपको "कस्टम" विधि का उपयोग करना चाहिए और operator+ को अधिभारित करने से बचें।

void sum_bigClasses(const BigClass& obj1, const BigClass& obj2, BigClass& output) { 
    // output.resize(obj1.size() + obj2.size()); 
    // std::copy(...); 
} 

एक अन्य समाधान यह राशि के लिए एक गैर स्थिरांक ऑपरेटर लागू करने के लिए हो सकता है।एक उदाहरण:

BigClass& operator+=(const BigClass& rhs) { 
    // std::copy(rhs.cbegin(), rsh.cend(), std::back_inserter(abc)); 
    return *this; 
} 

इस तरह वर्ग इंटरफ़ेस विभिन्न रणनीतियों की अनुमति देता है: 3 अलग वस्तु है, लेकिन केवल 2 आवंटित करने के लिए

  • से बचें, यदि आप सभी अलग-अलग राज्यों के संरक्षण की जरूरत नहीं है।
  • 3 अलग-अलग ऑब्जेक्ट आवंटित करने और ऑपरेटर के अंदर अस्थायी निर्माण से बचने की अनुमति दें।

यहां, दो उदाहरण।

पहले बिंदु:

BigClass o1; 
BigClass o2; 
// Fill o1 and o2; 
o1 += o2; 
// only 2 object are alive 

दूसरी बात:

BigClass o1; 
BigClass o2; 
// Fill o1 and o2; 
BigClass o3 = o1; // Costructor from o1 
o3 += o2; 
// three different object 

संपादित: समारोह चूंकि यह एक NRVO है (लौटे अभिव्यक्ति नहीं एक है prvalue) न तो नया मानक सी ++ 17 डब्ल्यू बीमार प्रतिलिपि प्रतिलिपि गारंटी।

+0

क्या आर + ओ के लिए सी ++ 17 गारंटी प्रति एलिशन नहीं है? 'ऑपरेटर +' का शरीर एनआरवीओ का आह्वान करेगा। –

+1

@underscore_d आप बिल्कुल सही हैं! मैं अपना जवाब अपडेट करूंगा, धन्यवाद! –

1

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

0

अस्थायी लोगों का उपयोग न केवल स्मृति को बर्बाद करता है बल्कि समय को संसाधित करता है (एन BigClass उदाहरणों की गणना की गणना एन में चौकोर समय जटिलता हो सकती है)। इससे बचने के लिए कोई सामान्य समाधान नहीं है क्योंकि यह इस बात पर निर्भर करता है कि आपकी वस्तुओं का उपयोग कैसे किया जाता है। इस परिदृश्य में:

BigClass c = a + b; 

संकलक पहले से ही के रूप में banana36 से समझाया प्रतिलिपि इलिजन उपयोग करने के लिए नि: शुल्क (सी ++ 17 या आवश्यक है,) है, और जानकारी के lvalues, इसलिए वे संभावित महान पैदा करने के बिना नहीं बदला जा सकता रहे हैं आश्चर्य की बात है।

एक अलग परिदृश्य होगा:

BigClass f(); 
BigClass g(); 

BigClass h = f() + g(); 

इस मामले में, f() और g() rvalues ​​हैं और उन दोनों को कॉपी बेकार है। उनमें से कम से कम एक का भंडारण पुन: उपयोग किया जा सकता है, उदाहरण के लिए एक मामले का अनुकूलन करने के लिए एक अतिरिक्त operator + अधिभार लिख सकता है जहां छोड़ दिया योज्य एक rvalue है:

BigClass operator +(BigClass &&a, const BigClass &b) 
{ 
    a.abc.insert(a.abc.end(), b.abc.begin(), b.abc.end()); 
    return std::move(a); 
} 

यह a.abc के भंडारण पुनः उपयोग कर लेता और जब तक क्षमता पर्याप्त है इसकी सामग्री को कॉपी बचा जाता है। एक अच्छा दुष्प्रभाव यह है कि उदा। 10 तत्वों के साथ एन ऑब्जेक्ट्स को संक्षेप में प्रत्येक के पास रैखिक प्रदर्शन होगा क्योंकि std::vector के अंत में तत्वों की निरंतर संख्या में प्रविष्टि निरंतर अमूर्त लागत है। लेकिन यह केवल तभी काम करता है जब operator + का दायां अधिभार चुना गया है, जो उदा। std::accumulate के मामले में है। यहाँ अपने मुख्य विकल्पों में से एक अवलोकन है:

  1. आपूर्ति operator +(const BigClass &, const BigClass &) और operator +=, और लापरवाही से पूर्व का उपयोग करने का प्रदर्शन निहितार्थ पर अपने उपयोगकर्ताओं को शिक्षित।
  2. संभवतः operator +(BigClass &&, const BigClass &) और शायद operator +(const BigClass &, BigClass &&) और operator +(BigClass &&, BigClass &&) के लिए अधिभार जोड़ें।ध्यान दें कि यदि आपके पास एक रावल्यू संदर्भ के साथ दोनों ओवरलोड हैं, तो आपको दो रावल्यू संदर्भों के साथ ओवरलोड भी जोड़ना चाहिए, या अन्य f() + g() एक संदिग्ध कॉल होगा। यह भी ध्यान रखें कि ओवरलोड जहां दाहिने हाथ पैरामीटर एक रावल्यू संदर्भ है, उदाहरण के लिए उपयोग के लिए सबसे उपयुक्त है। std::deque और std::vector नहीं क्योंकि इसके सामने सम्मिलन पर कम समय की जटिलता है, लेकिन एक डेक के साथ वेक्टर की जगह केवल तभी उपयोगी होती है जब यह उपयोग सामान्य है, क्योंकि डेक अन्यथा वेक्टर से धीमा है।
  3. केवल operator += जैसे कुशल संचालन प्रदान करें और उपयोगकर्ताओं की निराशा से निपटें (वैकल्पिक रूप से, copyAdd जैसे कम कुशल संचालन नामों को कम करें)।
संबंधित मुद्दे