2010-03-09 15 views
6
#include <stdio.h> 
#include <stdlib.h> 
#include <memory.h> 
#include <vector> 
#include <string> 
#include <iostream> 
#include <map> 
#include <utility> 
#include <algorithm> 

void * GetMemory(size_t n) { 
    void *ptr = malloc(n); 
    printf("getMem n %d ptr 0x%x\n", n, reinterpret_cast<unsigned int> (ptr)); 
    return ptr; 
} 

void FreeMemory(void *p) { 
    free(p); 
} 

void* operator new (size_t n) { 
    void *p = GetMemory(n); 
    return p; 
} 

void* operator new [] (size_t n) { 
    void *p = GetMemory(n); 
    return p; 
} 

void operator delete (void *p) { 
    FreeMemory(p); 
} 

void operator delete [] (void *p) { 
    FreeMemory(p); 
} 

typedef std::vector<int> vec; 

int main(int argc, char *argv[]) { 
    std::map<int, vec> z; 
    vec x; 
    z.insert(std::pair<int,vec>(1,x)); 
} 

जी ++ -Wall -ansi test.cpp -ओ परीक्षणमेमोरी आवंटन करते समय एक नक्शा

भागो परीक्षण के साथ संकलित में प्रविष्टि।

एन = 0 के साथ GetMemory के लिए तीन कॉल क्यों हैं?

उत्तर

8

स्टिक कुछ ट्रेसिंग की चर 'एक्स' नकल खाली सेट का आह्वान करते हैं और इस के लिए मुख्य बदलें:

int main(int argc, char *argv[]) { 
    printf("map\n"); 
    std::map<int, vec> z; 
    printf("vec\n"); 
    vec x; 
    printf("pair\n"); 
    std::pair<int,vec> y(1,x); 
    printf("insert\n"); 
    z.insert(y); 
    printf("inserted 1\n"); 
    y.first = 2; 
    printf("insert\n"); 
    z.insert(y); 
    printf("inserted 2\n"); 

}

आउटपुट:

$ make mapinsert CXXFLAGS=-O3 -B && ./mapinsert 
g++ -O3 mapinsert.cpp -o mapinsert 
map 
vec 
pair 
getMem n 0 ptr 0x6b0258 
insert 
getMem n 0 ptr 0x6b0268 
getMem n 32 ptr 0x6b0278 
getMem n 0 ptr 0x6b02a0 
FreeMemory ptr 0x6b0268 
inserted 1 
insert 
getMem n 0 ptr 0x6b0268 
getMem n 32 ptr 0x6b02b0 
getMem n 0 ptr 0x6b02d8 
FreeMemory ptr 0x6b0268 
inserted 2 
FreeMemory ptr 0x6b0258 
FreeMemory ptr 0x6b02d8 
FreeMemory ptr 0x6b02b0 
FreeMemory ptr 0x6b02a0 
FreeMemory ptr 0x6b0278 

अपने 3 0 आकार allo की तो cations:

  • एक खाली वेक्टर को जोड़ी में कॉपी करना है।
  • एक मानचित्र में खाली वेक्टर की एक प्रति स्टोर करना है।

ये दोनों स्पष्ट रूप से आवश्यक हैं। क्या मैं के बारे में यकीन नहीं है यह है:

  • एक insert करने के लिए कॉल में कहीं वेक्टर कॉपी करने के लिए है, और यह भी कॉल को सम्मिलित करने में कर सकती है।

यह ऐसा है insert (या कुछ और यह कॉल आंतरिक रूप से) ने अपने पैरामीटर मान के बजाय संदर्भ द्वारा ले जा रहा है, या insert स्पष्ट रूप से कुछ समय के एक स्वत: चर में एक प्रति ले जा रहा है इससे पहले कि यह नया नक्शा नोड आवंटित करता है। इस समय एक डीबगर फायरिंग मेरे लिए प्रयास है, मैं इसे किसी और को छोड़ दूंगा।

संपादित करें: रहस्य हल हो गया। insertstd::pair<const int, vec> लेता है, std::pair<int, vec> नहीं। एक खाली वेक्टर की अतिरिक्त प्रतिलिपि इसलिए है क्योंकि आपके द्वारा बनाई गई जोड़ी को एक (नोटर) अस्थायी रूप में परिवर्तित किया जाना चाहिए, फिर उस अस्थायी का संदर्भ insert पर भेजा गया है। std :: pair में एक कंस्ट्रक्टर टेम्पलेट है जो आपको लगभग किसी भी चीज़ से दूर जाने देता है। 20.2.2/4:

template<class U, class V> pair(const pair<U,V> &p); 

प्रभाव: तर्क के इसी सदस्यों में से सदस्यों initializes, की जरूरत के रूप में प्रदर्शन अंतर्निहित रूपांतरण।

मैं भी देख सकते हैं कि मेरी कार्यान्वयन में, vec x;getMem फोन नहीं है, लेकिन vec x(0); करता है। तो वास्तव में:

z[1] = vec(); 

कम कोड है और आप अतिरिक्त प्रतिलिपि बनाने के लिए अवसर से इनकार करते हैं (हालांकि यह operator= कॉल के बजाय)। यह अभी भी मेरे लिए कम से कम 2 0 आकार के आवंटन करता है।

सी ++ मानक परिभाषित करता है ताकि insert पर एक कॉल शामिल एक निर्दिष्ट अभिव्यक्ति के परिणाम को वापस कर सके। मुझे यकीन नहीं है कि इसका मतलब है कि operator[] के प्रभाव "जैसे" make_pair और insert कहलाए गए थे (यानी, मानक यह निर्धारित करने के लिए उतना ही अच्छा है जितना स्रोत operator[] के लिए होना चाहिए), या केवल मान दिया गया मान है निर्दिष्ट अभिव्यक्ति के समान मूल्य उत्पन्न होगा। यदि उत्तरार्द्ध तो शायद एक कार्यान्वयन इसे एक 0-आकार के आवंटन के साथ कर सकता है। लेकिन निश्चित रूप से map में पहली बार एक जोड़ी बनाने के बिना प्रविष्टि बनाने की कोई गारंटी नहीं है जिसमें मैप किए गए प्रकार हैं, इसलिए 2 आवंटन की अपेक्षा की जानी चाहिए। या अधिक ठीक से, वांछित मैप किए गए मूल्य की 2 प्रतियां: तथ्य यह है कि 0-आकार वाले वेक्टर की प्रतिलिपि बनाने से 0-आकार का आवंटन कार्यान्वयन-निर्भर होता है।

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

std::map<int, vec> z; 
vec x(1000); 
z[1] = x; 
// i.e. (*(z.insert(std::pair<const int, vec>(1,vec())).first)).second = x; 

, आकार 4000 और आकार 0 में से 2 का 2 आवंटन में आता है, जबकि:

std::map<int, vec> z; 
vec x(1000); 
z.insert(std::pair<const int, vec>(2, x)); 

आकार 4000 के 3 बनाता है और आकार 0. में से कोई भी अंततः आकार काफी बड़ा है कि पहले कोड में अतिरिक्त आवंटन की तुलना में सस्ता है दूसरे कोड में अतिरिक्त प्रतिलिपि।

यह संभव है कि सी ++ 0x में चालक रचनाकार इससे मदद करेंगे, मुझे यकीन नहीं है।

+0

आपके सभी प्रयासों के लिए धन्यवाद। अगर मैं यह जवाब दे सकता हूं *, मैं चाहता हूं। –

+0

यह एक अच्छा उदाहरण है कि नक्शा में वैक्टर जैसे चीजों को डालने का सबसे अच्छा अभ्यास क्यों टाइप किया गया है टाइपिफ़ वेक्टर vec_t; टाइपपीफ मानचित्र map_t; vec_t डमी; map_t myvals; vec_t tvals (100000,3);/* vallue */myvals.insert (map_t :: value_type (1, डमी))। पहला-> second.swap (tvals); इसका नक्शा नोड बनाने के लिए केवल खाली vec_t की प्रतिलिपि बनाने का प्रभाव होता है, और उसके बाद नोड का निर्माण होने के बाद मानों को इसमें स्थानांतरित किया जाता है (या यदि यह पहले से मौजूद है तो प्रतिस्थापित किया गया है)। समकक्ष सी ++ 0x रास्ता myvals.insert है (map_t :: value_type (1, std :: move (tvals))); –

6

सभी 3 मामलों खाली वेक्टर के प्रारंभ के साथ संबंध:

  1. पेड़ की जड़ तत्व (std :: नक्शे के आंतरिक कार्यान्वयन) है कि खाली वेक्टर होते हैं init करने के लिए।
  2. 'vec x' का अपना प्रारंभिकरण।
  3. एसटीडी के लिए प्रति निर्माता :: तत्व के लिए 'दूसरा' जोड़ी कि FreeMemory में
+0

नक्शा जेड में प्रत्येक डालने GetMemory() तीन बार कॉल करने लगता है। नीचे दिए गए कोड को नीचे जोड़ने का प्रयास करें: vec y; ; z.insert (std :: जोड़ी (2, y) आप तीन अधिक कॉल, साथ एन = 0. और यहां तक ​​कि एक खाली वेक्टर सब मिल 3 संकेत की जरूरत है: _M_start, _M_finish, और _M_end_of_storage –

+0

@ prasoon99। : "और यहां तक ​​कि एक खाली वेक्टर को 3 पॉइंटर्स की आवश्यकता होती है ..." - पॉइंटर्स और वेक्टर उदाहरणों को मिश्रण न करें! पॉइंटर्स स्मृति आवंटन की अपेक्षा नहीं करते हैं, लेकिन वेक्टर का उदाहरण करता है। खाली वेक्टर बिल्कुल रिक्त बफर आवंटित करता है। आपके नमूने में यह हुआ 3 बार - ऊपर मेरा जवाब देखें। – Dewfy

+0

gdb के साथ जांचें। GetMemory() पर सभी कॉल सम्मिलित कथन पर हैं। वेक्टर प्रारंभिकता GetMemory() –

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