কীভাবে প্লেইন অ্যারেগুলির জন্য পরিসীমা ভিত্তিক?


88

সি ++ 11 এ আপনি একটি পরিসর-ভিত্তিক ব্যবহার করতে পারেন for, যা foreachঅন্যান্য ভাষার মতো কাজ করে। এটি সরল সি অ্যারেগুলির সাথেও কাজ করে:

int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
    n *= 2;
}

কীভাবে থামবে কখন? এটি কি কেবল স্থিতিযুক্ত অ্যারেগুলির সাথে কাজ করে যা একই স্কোপটিতে forব্যবহৃত হয় তা ঘোষিত হয়েছে ? আপনি কীভাবে এটিকে forগতিশীল অ্যারে ব্যবহার করবেন ?


10
সি বা সি +++ এর জন্য কোনও "গতিশীল" অ্যারে নেই - সেখানে অ্যারের ধরণ রয়েছে এবং তারপরে এমন পয়েন্টার রয়েছে যা মেমরির একটি অ্যারে বা গতিশীল-বরাদ্দকৃত ব্লকের দিকে ইঙ্গিত করতে পারে বা নাও করতে পারে যা বেশিরভাগ অ্যারের মতো আচরণ করে। টি [এন] টাইপের যে কোনও অ্যারের জন্য, এর আকারটি প্রকারে এনকোড করা থাকে এবং এটির মাধ্যমে প্রবেশ করা যেতে পারে for। কিন্তু যে মুহুর্তে অ্যারে কোনও পয়েন্টারের সাথে সিদ্ধান্ত নেয়, আকারের তথ্যটি হারিয়ে যায়।
JohannesD

4
আপনার উদাহরণে, উপাদানের সংখ্যা মধ্যে numbersহয় sizeof(numbers)/sizeof(int)উদাহরণস্বরূপ,।
JohannesD

উত্তর:


57

এটি এমন কোনও অভিব্যক্তির জন্য কাজ করে যার প্রকারটি অ্যারে। উদাহরণ স্বরূপ:

int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
  n *= 2;
delete [] arraypointer;

আরও বিশদ ব্যাখ্যার জন্য, যদি ডানদিকে অভিব্যক্তির :ধরণটি একটি অ্যারের প্রকার হয়, তবে লুপটি পুনরায় পুনরুক্ত ptrহয় ptr + size( ptrঅ্যারের প্রথম উপাদানটির দিকে ইশারা করে অ্যারের sizeউপাদান গণনা করে)।

এই ব্যবহারকারী সংজ্ঞায়িত ধরনের, যা কাজ আপ তাকিয়ে বিপরীতে হয় beginএবং endযদি আপনি কোন ক্লাসে বস্তু বা পাস অ সদস্য ফাংশন (যদি কোন সদস্য যে ভাবে বলা হয়) সদস্য হিসেবে। এই ফাংশনগুলি সূচনা এবং শেষ পুনরাবৃত্তি অর্জন করবে (যথাক্রমে শেষ উপাদান এবং ক্রমের সূচনার পরে সরাসরি নির্দেশ করবে)।

কেন এই পার্থক্য বিদ্যমান তা এই প্রশ্নটি পরিষ্কার করে দেয়।


8
আমি মনে করি যে প্রশ্নটি এটি কীভাবে কাজ করে, কখন এটি কাজ করে না
সেপ্টেম্বর

4
@ প্রশ্নটিতে একাধিক '?' এসএস রয়েছে। একটি ছিল "এটি কি ... এর সাথে কাজ করে?"। আমি উভয় ব্যাখ্যা কিভাবে এবং যখন এটি কাজ করে।
জোহানেস স্কাউব -

8
@ জোহানেসচাউব: আমি মনে করি "এখানে" সমস্যাটি হ'ল আপনি ঠিক কিভাবে প্রথম স্থানে অ্যারে টাইপের কোনও অবজেক্টের আকার পাবেন (পয়েন্টার বনাম অ্যারে বিভ্রান্তির কারণে, প্রায় সবাই জানেন না যে অ্যারের আকার হয়) প্রোগ্রামার উপলব্ধ)।
JohannesD

আমি বিশ্বাস করি এটি কেবল সদস্যবিহীন st beginশেষের দিকে দেখায়: সদস্য ফাংশনগুলি ব্যবহার . It just happens that শুরু করুন `std::endএবং আরও ভাল ম্যাচ উপলভ্য না হলে ব্যবহৃত হবে।
ডেনিস জিকিফুজ

4
@ মাদ্রিদে ডেনিসকে নোডিসের সিদ্ধান্ত পরিবর্তন করার এবং সদস্যদের পক্ষে এবং শেষের সিদ্ধান্ত নেওয়া হয়েছিল। না শুরু করা এবং শেষ সদস্যদের পক্ষে অস্পষ্টতা তৈরি করা এড়ানো কঠিন caused
জোহানেস স্কাউব -

45

আমি মনে করি যে এই প্রশ্নের সবচেয়ে গুরুত্বপূর্ণ অংশটি হল, সি ++ কীভাবে অ্যারের আকারটি কী তা জানে (এই প্রশ্নটি খুঁজে পাওয়ার পরে আমি এটি জানতে চেয়েছিলাম)।

সি ++ একটি অ্যারের আকার জানে, কারণ এটি অ্যারের সংজ্ঞা একটি অংশ - এটি ভেরিয়েবলের ধরণ। একটি সংকলক টাইপ জানতে হবে।

যেহেতু C ++ 11 std::extentএকটি অ্যারের আকার পেতে ব্যবহার করা যেতে পারে:

int size1{ std::extent< char[5] >::value };
std::cout << "Array size: " << size1 << std::endl;

অবশ্যই, এটি খুব একটা বোঝায় না, কারণ আপনাকে প্রথম লাইনে স্পষ্টভাবে আকার সরবরাহ করতে হবে, যা আপনি দ্বিতীয় লাইনে পাবেন। তবে আপনি এটি ব্যবহার করতে পারেন decltypeএবং তারপরে এটি আরও আকর্ষণীয় হয়ে উঠবে:

char v[] { 'A', 'B', 'C', 'D' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;

6
এটি প্রকৃতপক্ষে আমি মূলত যা জিজ্ঞাসা করেছি। :)
পল মানতা

19

সর্বশেষ সি ++ ওয়ার্কিং ড্রাফ্ট (n3376) অনুসারে বিবৃতি প্রদানের জন্য নীচের সমতুল্য:

{
    auto && __range = range-init;
    for (auto __begin = begin-expr,
              __end = end-expr;
            __begin != __end;
            ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}

সুতরাং এটি যেভাবে forপুনরাবৃত্তকারীগুলি ব্যবহার করে নিয়মিত লুপটি একইভাবে বন্ধ করতে হয় তা জানে ।

আমি মনে করি আপনি কেবলমাত্র পয়েন্টার এবং আকার (গতিশীল অ্যারে) সমন্বিত অ্যারের সাথে উপরের সিনট্যাক্সটি ব্যবহারের জন্য একটি উপায় প্রদানের জন্য নীচের মতো কিছু সন্ধান করতে পারেন:

template <typename T>
class Range
{
public:
    Range(T* collection, size_t size) :
        mCollection(collection), mSize(size)
    {
    }

    T* begin() { return &mCollection[0]; }
    T* end () { return &mCollection[mSize]; }

private:
    T* mCollection;
    size_t mSize;
};

এই শ্রেণীর টেম্পলেটটি তখন একটি পরিসীমা তৈরি করতে ব্যবহার করা যেতে পারে, যার উপরে আপনি সিনট্যাক্সের জন্য নতুন রেঞ্জ ব্যবহার করে পুনরাবৃত্তি করতে পারেন । আমি এটিকে এমন দৃশ্যে সমস্ত অ্যানিমেশন অবজেক্টের মধ্য দিয়ে চালাতে ব্যবহার করছি যা একটি লাইব্রেরি ব্যবহার করে আমদানি করা হয় যা কেবলমাত্র একটি অ্যারে এবং একটি আকারকে পৃথক মান হিসাবে প্রত্যাবর্তন করে using

for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
    // Do something with each pAnimation instance here
}

এই বাক্য গঠনটি আমার মতে আপনি কী ব্যবহার করবেন std::for_eachবা একটি সরল forলুপ তার চেয়ে অনেক পরিষ্কার ।


3

এটি কখন থামবে তা জানে কারণ এটি স্থির অ্যারের সীমানা জানে।

"গতিশীল অ্যারে" বলতে কী বোঝায় তা আমি নিশ্চিত নই, কোনও অবস্থাতেই যদি অনানুষ্ঠানিকভাবে স্থিতিস্থ অ্যারেগুলি দিয়ে পুনরাবৃত্তি না করা হয় তবে সংকলকটি নামগুলি beginএবং endঅবজেক্টের শ্রেণীর ক্ষেত্রের মধ্যে অনুসন্ধান করবে যা আপনি পুনরাবৃত্তি করছেন, বা দেখছেন আর্গুমেন্ট-নির্ভর লুকআপের জন্য begin(range)এবং end(range)ব্যবহার করে এবং তাদের পুনরুক্তি হিসাবে ব্যবহার করে।

আরও তথ্যের জন্য, সি ++ 11 স্ট্যান্ডার্ডে (বা এর সর্বজনীন খসড়া), "6.5.4 পরিসীমা ভিত্তিক forবিবৃতি", পৃষ্ঠা -১45৫


4
একটি "গতিশীল অ্যারে" তৈরি করা হবে new[]। সেক্ষেত্রে আপনি কেবল আকারের কোনও ইঙ্গিত ছাড়াই একটি পয়েন্টার পেয়েছেন, সুতরাং পরিসর-ভিত্তিক forএটির সাথে কাজ করার কোনও উপায় নেই ।
মাইক সিমুর

আমার উত্তরে একটি গতিশীল অ্যারে অন্তর্ভুক্ত রয়েছে যার আকার (4) সংকলনের সময় জানা ছিল, তবে "গতিশীল অ্যারে" এর ব্যাখ্যাটি প্রশ্নকর্তার উদ্দেশ্য কি তা আমি জানি না।
জোহানেস স্কাউব -

3

কীভাবে প্লেইন অ্যারেগুলির জন্য পরিসীমা ভিত্তিক?

এটি কি পড়তে হবে, " আমাকে বলুন একটি রেঞ্জ-ফর (অ্যারে সহ) কী করে? "

আমি এটি ধরে নিয়ে উত্তর দেব - নেস্টেড অ্যারেগুলি ব্যবহার করে নিম্নলিখিত উদাহরণটি ধরুন:

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

for (auto &pl : ia)

পাঠ্য সংস্করণ:

iaঅ্যারে সমন্বিত একটি অ্যারে ("নেস্টেড অ্যারে"), এতে [3]প্রতিটি [4]মান রয়েছে। উপরের উদাহরণটি iaএটির প্রাথমিক 'ব্যাপ্তি' ( [3]) দ্বারা লুপ করে, এবং তাই লুপগুলি [3]বার করে। প্রতিটি লুপ এক উৎপন্ন iaএর [3]ধারণকারী একটি বিন্যাস - একটি প্রাথমিক মূল্যই প্রথম থেকে শুরু এবং শেষ দিয়ে শেষ [4]মান।

  • প্রথম লুপ: plসমান {1,2,3,4}- একটি অ্যারে
  • দ্বিতীয় লুপ: plসমান {5,6,7,8}- একটি অ্যারে
  • তৃতীয় লুপ: plসমান {9,10,11,12}- একটি অ্যারে

প্রক্রিয়াটি ব্যাখ্যা করার আগে এখানে অ্যারে সম্পর্কে কিছু বন্ধুত্বপূর্ণ অনুস্মারক রয়েছে:

  • অ্যারেগুলি তাদের প্রথম মানটির নির্দেশক হিসাবে ব্যাখ্যা করা হয় - কোনও পুনরাবৃত্তি ছাড়াই অ্যারে ব্যবহার করা হলে প্রথম মানটির ঠিকানা ফেরত দেয়
  • pl অবশ্যই একটি রেফারেন্স হতে হবে কারণ আমরা অ্যারেগুলি অনুলিপি করতে পারি না
  • অ্যারে দিয়ে, আপনি অ্যারে বস্তুর নিজেই একটি সংখ্যা যোগ করুন, এটি এগিয়ে যে অনেক বার এবং 'পয়েন্ট' সমতুল্য এন্ট্রি এগিয়ে - যদি nপ্রশ্নে সংখ্যা, তারপর ia[n]হিসাবে একই *(ia+n)(আমরা ঠিকানা থেকে যে dereferencing করছি nএন্ট্রি এগিয়ে) এবং ia+nএটি একই &ia[n](আমরা অ্যারেতে প্রবেশের ঠিকানা পাচ্ছি)।

যা চলছে তা এখানে:

  • প্রতিটি লুপ উপর, plহিসেবে সেট করা হয় রেফারেন্স করার ia[n]সঙ্গে, nবর্তমান লুপ গণনা 0. সুতরাং থেকে শুরু equaling, plহয় ia[0]দ্বিতীয় এটা উপর, প্রথম রাউন্ড উপর ia[1], ইত্যাদি। এটি পুনরাবৃত্তির মাধ্যমে মানটি পুনরুদ্ধার করে।
  • লুপটি যতটা ia+nকম যায় ততক্ষণ যায় end(ia)

... এবং এটি প্রায়।

এটি সত্যিই এটি লেখার সহজ সরল উপায় :

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int n = 0; n != 3; ++n)
  auto &pl = ia[n];

যদি আপনার অ্যারে বাসা বাঁধে না , তবে এই প্রক্রিয়াটি কিছুটা সহজ হয়ে যায় যাতে রেফারেন্সের প্রয়োজন হয় না , কারণ পুনরাবৃত্ত মানটি একটি অ্যারে নয় বরং 'সাধারণ' মান:

 int ib[3] = {1,2,3};

 // short
 for (auto pl : ib)
   cout << pl;

 // long
 for (int n = 0; n != 3; ++n)
   cout << ib[n];

কিছু অতিরিক্ত তথ্য

কী autoতৈরি করার সময় আমরা কীওয়ার্ডটি ব্যবহার করতে চাই না pl? দেখতে কেমন লাগবে?

নিম্নলিখিত উদাহরণে, plএকটি উল্লেখ করে array of four integers। প্রতিটি লুপের উপর plমান দেওয়া হয় ia[n]:

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int (&pl)[4] : ia)

এবং ... কোনও বিভ্রান্তি দূর করার জন্য অতিরিক্ত তথ্য সহ এটি এটি কাজ করে। এটি কেবলমাত্র একটি 'শর্টহ্যান্ড' forলুপ যা স্বয়ংক্রিয়ভাবে আপনার জন্য গণনা করা হয়, তবে বর্তমান লুপটি ম্যানুয়ালি না করে পুনরুদ্ধার করার উপায়ের অভাব রয়েছে।


@ এন্ডি 10 বারের মধ্যে 9 বার শিরোনামটি গুগলে কি মিলছে / যা কিছু অনুসন্ধান করে - শিরোনাম জিজ্ঞাসা করে যে এইগুলি কীভাবে কাজ করে? না, কখন থামবে জানি না? । এমনকি, তাই অন্তর্নিহিত প্রশ্ন উহ্য হয় কিছুটা হলেও এই উত্তর মধ্যে আবৃত, এবং খুঁজছেন অন্য কেউ জন্য উত্তর যায় অন্যান্য উত্তর। সিনট্যাক্স প্রশ্নগুলির মতো এগুলির শিরোনামগুলি বাক্যযুক্ত হওয়া উচিত যাতে উত্তরটি একা ব্যবহার করেই লেখা যায় কারণ অনুসন্ধানকারীদের প্রশ্নটি অনুসন্ধান করার জন্য প্রয়োজনীয় সমস্ত তথ্য। আপনি অবশ্যই ভুল নন - প্রশ্নটি যেমন হওয়া উচিত তেমন শিরোনাম নয়।
সুপার ক্যাট

0

গাদাতে স্ট্যাক বনাম অ্যারেতে অ্যারের মধ্যে পার্থক্য প্রদর্শনের জন্য কিছু নমুনা কোড


/**
 * Question: Can we use range based for built-in arrays
 * Answer: Maybe
 * 1) Yes, when array is on the Stack
 * 2) No, when array is the Heap
 * 3) Yes, When the array is on the Stack,
 *    but the array elements are on the HEAP
 */
void testStackHeapArrays() {
  int Size = 5;
  Square StackSquares[Size];  // 5 Square's on Stack
  int StackInts[Size];        // 5 int's on Stack
  // auto is Square, passed as constant reference
  for (const auto &Sq : StackSquares)
    cout << "StackSquare has length " << Sq.getLength() << endl;
  // auto is int, passed as constant reference
  // the int values are whatever is in memory!!!
  for (const auto &I : StackInts)
    cout << "StackInts value is " << I << endl;

  // Better version would be: auto HeapSquares = new Square[Size];
  Square *HeapSquares = new Square[Size];   // 5 Square's on Heap
  int *HeapInts = new int[Size];            // 5 int's on Heap

  // does not compile,
  // *HeapSquares is a pointer to the start of a memory location,
  // compiler cannot know how many Square's it has
  // for (auto &Sq : HeapSquares)
  //    cout << "HeapSquare has length " << Sq.getLength() << endl;

  // does not compile, same reason as above
  // for (const auto &I : HeapInts)
  //  cout << "HeapInts value is " << I << endl;

  // Create 3 Square objects on the Heap
  // Create an array of size-3 on the Stack with Square pointers
  // size of array is known to compiler
  Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)};
  // auto is Square*, passed as constant reference
  for (const auto &Sq : HeapSquares2)
    cout << "HeapSquare2 has length " << Sq->getLength() << endl;

  // Create 3 int objects on the Heap
  // Create an array of size-3 on the Stack with int pointers
  // size of array is known to compiler
  int *HeapInts2[]{new int(23), new int(57), new int(99)};
  // auto is int*, passed as constant reference
  for (const auto &I : HeapInts2)
    cout << "HeapInts2 has value " << *I << endl;

  delete[] HeapSquares;
  delete[] HeapInts;
  for (const auto &Sq : HeapSquares2) delete Sq;
  for (const auto &I : HeapInts2) delete I;
  // cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.