2011-09-21 12 views
16

टीबीइट्स चर के लिए सही उपयोग पैटर्न क्या है? मेरी समझ से, टीबीइट्स एक वर्ग नहीं है, बल्कि "बाइट्स की गतिशील सरणी" है। मुझे यकीन नहीं है कि इसके लिए स्मृति आवंटित की जाती है, जब इसे मुक्त किया जाता है, और निर्माता से उपभोक्ता तक इसे पास करने का सबसे अच्छा तरीका कौन सा होता है। मैं चाहता हूं कि मेरा निर्माता टीबीइट्स उदाहरण बनाये, फिर इसे उपभोक्ता को पास कर दें। ऐसा होने के बाद, निर्माता अपने टीबीइट्स सदस्य चर का पुन: उपयोग करना चाहता है, इस ज्ञान में सामग्री कि उपभोक्ता अंततः सिस्टम को स्मृति वापस कर देगा। यदि टीबीइट्स एक वस्तु थी, तो मुझे कोई समस्या नहीं होगी, लेकिन मुझे यकीन नहीं है कि इस परिदृश्य में टीबीइट्स कैसे काम करता है।डेल्फी एक्सई टीबीइट्स सही उपयोग

उदाहरण के लिए, ऑब्जेक्ट ए में, मैं कुछ डेटा को एक टीबीइट्स सरणी में इकट्ठा करना चाहता हूं जो ऑब्जेक्ट ए का सदस्य है। जब यह पूरा हो जाता है, तो मैं फिर टीबीइट्स सरणी को किसी अन्य ऑब्जेक्ट बी में पास करना चाहता हूं, जो तब बन जाता है डेटा के मालिक। इस बीच, ऑब्जेक्ट ए में वापस, मैं TBytes सदस्य चर का पुन: उपयोग करते हुए, अधिक डेटा एकत्र करना प्रारंभ करना चाहता हूं।

type 
    TClassA = class 
    private 
    FData: TBytes; 
    public 
    procedure AssembleInput(p: Pointer; n: Cardinal); 
    end; 

    TClassB = class 
    public 
    procedure ProcessData(d: TBytes); 
    end; 

var 
    a: TClassA; 
    b: TClassB; 

procedure TClassA.AssembleInput(p: Pointer; n: Cardinal); 
begin 
    SetLength(FData, n); 
    Move(p^, FData, n); // Is this correct? 
    ... 
    b.ProcessData(FData); 

    ... 

    // Would it be legal to reuse FData now? Perhaps by copying new (different) 
    // data into it? 
end; 

procedure TClassB.ProcessData(d: TBytes); 
begin 
    // B used the TBytes here. How does it free them? 
    SetLength(d, 0); // Does this free any dynamic memory behind the scenes? 
end; 

अग्रिम धन्यवाद!

उत्तर

13

डेल्फी गतिशील सरणी प्रबंधित प्रकार हैं जिनके पास स्वचालित जीवनकाल प्रबंधन होता है। वे संदर्भ गिना जाता है और जब संदर्भ संख्या 0 पर जाती है, तो निपटान किया जाता है। आप उनके बारे में स्ट्रिंग्स, इंटरफेस और वेरिएंट के संबंध में समकक्ष होने के बारे में सोच सकते हैं।

आप स्पष्ट रूप से तीन तरीकों में से एक में एक गतिशील सरणी के लिए एक संदर्भ जारी कर सकते हैं:

a := nil; 
Finalize(a); 
SetLength(a, 0); 

हालांकि, यह बस कुछ भी नहीं करना बहुत आम है और संदर्भ जारी किया करते हैं, जब चर गुंजाइश छोड़ देता है।

गतिशील सरणी के साथ देखने के लिए एक चीज तब होती है जब आपके पास एक ही गतिशील सरणी के दो संदर्भ होते हैं। उस स्थिति में, एक संदर्भ के माध्यम से लागू परिवर्तन अन्य संदर्भ से दिखाई दे रहे हैं क्योंकि केवल एक वस्तु है।

SetLength(a, 1); 
a[0] := 42; 
b := a; 
b[0] := 666;//now a[0]=666 

आप से पूछें कि यह सही है:

Move(p^, FData, n); 

नहीं ऐसा नहीं है। आपने यहां p की सामग्री को FData पर कॉपी करने के लिए किया है। आप Move साथ प्रतिलिपि बनाना चाहते हैं तो आप लिख सकते हैं:

Move(p^, Pointer(FData)^, n); 

या आप लिख सकते हैं अगर आप थोड़ा और वर्बोज़ होना पसंद करते हैं और कलाकारों से बचने:

if n>0 then 
    Move(p^, FData[0], n); 

मैं व्यक्तिगत रूप से नहीं लग रहा है Move के बाद से कास्ट के बारे में बहुत बुरा है वैसे भी बिल्कुल कोई प्रकार की सुरक्षा नहीं है।


यह अब FData का पुन: उपयोग करने के लिए कानूनी होगा? शायद इसमें नए (अलग) डेटा की प्रतिलिपि बनाकर?

मुझे नहीं लगता कि मैं बिना किसी संदर्भ के इसका उत्तर दे सकता हूं। उदाहरण के लिए मुझे नहीं पता कि क्यों FData एक फ़ील्ड है क्योंकि यह केवल उस फ़ंक्शन के लिए स्थानीय रूप से उपयोग किया जाता है। यह स्थानीय चर के रूप में अधिक समझ में आता है। संभवतः एक कारण के रूप में घोषित किया जाने का एक कारण है लेकिन इसे आसानी से इस कोड से नहीं पहचाना जा सकता है।


निर्माता/उपभोक्ता पैटर्न का उपयोग करने के बारे में आप। आम तौर पर यह खपत से उत्पादन को कम करने के लिए किया जाता है। हालांकि, आपका उदाहरण कोड ऐसा नहीं करता है, संभवतया क्योंकि decoupled कोड यहां शामिल करने के लिए बहुत जटिल होगा।

एक वास्तविक निर्माता/उपभोक्ता कार्यान्वयन के लिए आपको उत्पादक से उपभोक्ता तक डेटा के स्वामित्व को स्थानांतरित करने की आवश्यकता है। हमने ऊपर वर्णित किए गए शब्दों से, संदर्भ गणना का उपयोग करना एक बहुत ही सरल और प्रभावी तरीका है। जब डेटा उपभोक्ता को स्थानांतरित किया जाता है, तो निर्माता को इसका संदर्भ जारी करना चाहिए।

+0

स्पष्टीकरण के लिए धन्यवाद। हां, कारण एफडीएटी एक सदस्य चर है क्योंकि निर्माता को टीसीपी सॉकेट से डेटा मिल रहा है, इसलिए इसे आम तौर पर प्रत्येक बार कॉल किए जाने वाले डेटा का केवल एक हिस्सा मिलता है। हां, मैं निर्माता/उपभोक्ता चाहता हूं, और "डेटा के स्वामित्व को स्थानांतरित करने की आवश्यकता" के बारे में टिप्पणी _exactly_ है जो मैं करने की कोशिश कर रहा हूं। और हाँ, decoupled कोड बल्कि जटिल था; इसलिए सरलीकरण। अंत में, मुझे अभी भी यकीन नहीं है कि कैसे "स्वामित्व स्थानांतरित करें" सही तरीके से करें - क्या आप उस मुद्दे को थोड़ा सा स्पष्ट कर सकते हैं? –

+0

उपभोक्ता को संदर्भ लेने दें और फिर निर्माता का संदर्भ छोड़ दें। Consumer.data: = FData; एफडीटा: = शून्य; –

-2

मूव (पी ^, एफडीटा, एन); यह ठीक है

प्रक्रिया TClassB.ProcessData (डी: टीबीइट्स); // डी FData की संदर्भ संख्या // डी शुरू होती है, लेकिन कुछ भी नहीं रखता है लेकिन एफडीटा पहले से ही रीफॉउंट = 1 // के साथ रहता है // यदि आप डी के सामने "var" कीवर्ड डालते हैं, तो FData सेटलेथेंथ (डी, 0);
अंत;

+1

नहीं, यह ठीक नहीं है। यह पी में पते से बाइट को एफडीटा में पॉइंटर में ले जाता है। यह एफडीटा [0] होना चाहिए। –

4

आपके कोड में कुछ दुरुपयोग हैं। निम्नलिखित अधिक सही होंगे:

type 
    TClassA = class 
    private 
    FData: TBytes; 
    public 
    procedure AssembleInput(p: Pointer; n: NativeUInt); 
    end; 

    TClassB = class 
    public 
    procedure ProcessData(var d: TBytes); 
    end; 

var 
    a: TClassA; 
    b: TClassB; 

procedure TClassA.AssembleInput(p: Pointer; n: NativeUInt); 
begin 
    SetLength(FData, n); 
    if n <> 0 then Move(p^, FData[0], n); 
    ... 
    b.ProcessData(FData); 
    // FData is ready for reuse here... 
end; 

procedure TClassB.ProcessData(var d: TBytes); 
begin 
    ... 
    SetLength(d, 0); 
end; 
संबंधित मुद्दे