2012-02-17 11 views
13

ऐसा लगता है कि हर बार जब मैं वेक्टर m_test पर कोई ऑब्जेक्ट जोड़ता हूं, तो विनाशक विधि को बुलाया जाता है। क्या मैं कुछ भूल रहा हूँ? मेरे द्वारा इसे होने से कैसे रोका जा सकता है?जब मैं एक वेक्टर में उदाहरण जोड़ता हूं तो मेरी कक्षा के विनाशक को क्यों बुलाया जाता है?

class TEST 
{ 
public: 
    TEST(); 
    ~TEST(); 
    int * x; 
}; 

TEST::TEST() 
{ 
} 

TEST::~TEST() 
{ 
... it is called every time I push_back something to the vector ... 
    delete x; 
} 

    vector<TEST> m_test; 
    for (unsigned int i=0; i<5; i++) 
    { 
     m_test.push_back(TEST()); 
    } 
+2

सी ++ 11 में, आप अस्थायी बनाने से बचने के लिए 'm_test.emplace_back()' का उपयोग कर सकते हैं। किसी भी मामले में, हमेशा [नियम का तीन] याद रखें (http://stackoverflow.com/questions/4172722)। –

उत्तर

9

समस्या यहाँ है कि आप Rule of Three उल्लंघन कर रहे हैं। आपकी कक्षा में एक विनाशक है इसलिए आपको एक कॉपी-कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर की भी आवश्यकता है। वैकल्पिक रूप से, आप अपनी कक्षा की प्रतिलिपि बनाने की अनुमति नहीं दे सकते (उदाहरण के लिए T(T const&) और T& operator=(T const&) निजी, या boost::noncopyable से प्राप्त करके), और फिर push_back का उपयोग करने के बजाय वेक्टर का आकार बदलें।

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

std::vector<TEST> vec(5); 
// vec now has five default-constructed elements of type TEST. 

इन बातों से कोई एक कार्य नहीं कुछ एक बुरा विचार है, जैसा कि आप बहुत कुछ बिंदु पर डबल विलोपन समस्या आने पर जाने की संभावना है - भले ही आप आपको लगता है ' TEST जहां x != nullptr कॉपी या असाइन नहीं करेगा, यह स्पष्ट रूप से इसे प्रतिबंधित करने के लिए अधिक सुरक्षित है।

वैसे, अगर आप सदस्य संकेत दिए गए कि हटा दिया जाना चाहिए है एक वस्तु क्षेत्र से बाहर चला जाता है जब, यदि आप बूस्ट या सी का उपयोग करने में असमर्थ हैं scoped_ptr की तरह स्मार्ट संकेत का उपयोग कर, unique_ptr और shared_ptr (और शायद auto_ptr पर विचार + +11)।

+0

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

7

यह कहा जाता है नहीं कर रहा है जब आप push_back, यह कहा जाता है जब अस्थायी नष्ट हो जाता है।

अपने उदाहरण में इसे ठीक करने के लिए:

TEST test; 
for (int i = 0; i < 5; ++i) 
{ 
    m_test.push_back(test); 
} 

केवल एक बार बुलाना चाहिए।

आपका कोड लूप के भीतर अस्थायी TEST बना रहा है, इसे push_back में उपयोग करके, तब लूप समाप्त होता है/दोहराने और नष्ट होने पर अस्थायी रूप से बाहर निकलता है। ऐसा ठीक उसी तरह होता है, क्योंकि अस्थायी TEST को साफ़ करने की आवश्यकता है।

यदि आप इससे बचना चाहते हैं, तो आपको कुछ और करने की आवश्यकता है लेकिन प्रत्येक धक्का के लिए एक अस्थायी वस्तु बनाना है। एक संभावित समाधान यह है:

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects 

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor 

आपके एसटीएल को अनुकूलित करने के तरीके के आधार पर, इसे कई प्रतियां बनाने की आवश्यकता नहीं हो सकती है।

तुम भी बेहतर हैंडलिंग पाने के लिए यदि आप अपने TEST कक्षा में एक प्रतिलिपि निर्माता लागू की तरह, सक्षम हो सकते हैं:

TEST::TEST(const TEST & other) 
{ 
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example. 
} 

क्या यह उचित है, या आप इसे कैसे संभाल, अपने वर्ग और उसके पर निर्भर करता है जरूरत है, लेकिन जब आप अपने नियमित कन्स्ट्रक्टर और विनाशक को परिभाषित करते हैं तो आपको आमतौर पर एक प्रतिलिपि बनाना चाहिए (अन्यथा संकलक एक उत्पन्न करेगा, और इस मामले में, इसके परिणामस्वरूप प्रतिलिपि बनाई जाएगी और पॉइंटर्स को x पर लटकाना होगा)।

1

vector.push_back() दिए गए ऑब्जेक्ट को अपने स्टोरेज एरिया में कॉपी करता है। push_back() कॉल में आपके द्वारा बनाई जा रही अस्थायी वस्तु कॉपी करने के तुरंत बाद नष्ट हो जाती है, और यही वह है जिसे आप देख रहे हैं। कुछ कंपाइलर इस प्रति को अनुकूलित करने में सक्षम हो सकते हैं, लेकिन आपका स्पष्ट रूप से नहीं हो सकता है।

1

m_test.push_back(TEST()); में, टेस्ट() एक अस्थायी चर बना देगा। वेक्टर के बाद इसे अपनी याददाश्त में कॉपी करने के बाद, अस्थायी चर नष्ट हो जाता है।

आप इस तरह कर सकते हैं:

vector<TEST> m_test(5, TEST()); 
0

एक अस्थायी और के विनाश से बचने के लिए प्रति कंस्ट्रक्टर्स से बचने के लिए, vector::resize या vector::emplace_back उपयोग करने पर विचार। यहाँ emplace_back का उपयोग कर एक उदाहरण है:

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) 
{ 
    m_test.emplace_back(); 
} 

वेक्टर तत्व कॉपी करने के लिए आवश्यकता के बिना इन-जगह निर्माण किया जाएगा। जब vt नष्ट हो जाता है, प्रत्येक वेक्टर तत्व स्वचालित रूप से नष्ट हो जाता है।

सी ++ 0x आवश्यक है (gnu के साथ -std=c++0x का उपयोग करें)। #include <vector> निश्चित रूप से भी आवश्यक है।

एक डिफ़ॉल्ट निर्माता के रूप में निम्नानुसार (उदाहरण के लिए, अगर TEST::x एक सूचक के बजाय एक संदर्भ था), बस बहस कॉल करने के लिए emplace_back() में जोड़ने के लिए इस्तेमाल नहीं किया है:

class TEST 
{ 
public: 
    TEST(int & arg) : x(arg) {;} // no default constructor 
    int & x; // reference instead of a pointer. 
}; 

. . . 

int someInt; 

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) { 
    m_test.emplace_back(someInt); // TEST constructor args added here. 
} 

reserve() दिखाया वैकल्पिक है लेकिन यह सुनिश्चित करता है कि वेक्टर तत्वों के निर्माण से पहले पर्याप्त जगह उपलब्ध है।

संबंधित मुद्दे

 संबंधित मुद्दे