كيفية التعامل مع مصفوفة ثنائية الأبعاد كبيرة جدًا في لغة C++

StackOverflow https://stackoverflow.com/questions/61680

سؤال

أحتاج إلى إنشاء مجموعة int ثنائية الأبعاد بحجم 800 × 800.ولكن القيام بذلك يؤدي إلى تجاوز سعة المكدس (ها ها).

أنا جديد على لغة C++، فهل يجب أن أفعل شيئًا مثل ناقل المتجهات؟وقم فقط بتغليف المصفوفة ثنائية الأبعاد في فصل دراسي؟

على وجه التحديد، هذه المصفوفة هي zbuffer الخاص بي في برنامج الرسومات.أحتاج إلى تخزين قيمة z لكل بكسل على الشاشة (وبالتالي الحجم الكبير 800 × 800).

شكرًا!

هل كانت مفيدة؟

المحلول

أنت بحاجة إلى حوالي 2.5 ميغا، لذا فإن استخدام الكومة فقط سيكون أمرًا جيدًا.لا تحتاج إلى ناقل إلا إذا كنت بحاجة إلى تغيير حجمه.يرى الأسئلة الشائعة حول C++ Lite للحصول على مثال لاستخدام مصفوفة كومة الذاكرة المؤقتة "ثنائية الأبعاد".

int *array = new int[800*800];

(لا تنسى أن delete[] عند الانتهاء.)

نصائح أخرى

كل منشور حتى الآن يترك إدارة الذاكرة للمبرمج.وهذا يمكن وينبغي تجنبه.ReaperUnreal قريب جدًا مما سأفعله، باستثناء أنني سأستخدم ناقلًا بدلاً من مصفوفة وأقوم أيضًا بإنشاء معلمات قالب الأبعاد وتغيير وظائف الوصول - وأوه فقط تقوم IMNSHO بتنظيف الأشياء قليلاً:

template <class T, size_t W, size_t H>
class Array2D
{
public:
    const int width = W;
    const int height = H;
    typedef typename T type;

    Array2D()
        : buffer(width*height)
    {
    }

    inline type& at(unsigned int x, unsigned int y)
    {
        return buffer[y*width + x];
    }

    inline const type& at(unsigned int x, unsigned int y) const
    {
        return buffer[y*width + x];
    }

private:
    std::vector<T> buffer;
};

يمكنك الآن تخصيص هذه المصفوفة ثنائية الأبعاد على المكدس بشكل جيد:

void foo()
{
    Array2D<int, 800, 800> zbuffer;

    // Do something with zbuffer...
}

آمل أن يساعد هذا!

يحرر:تمت إزالة مواصفات الصفيف من Array2D::buffer.شكرا لأندرياس لالتقاط ذلك!

لكن مثال كيفن جيد:

std::vector<T> buffer[width * height];

يجب ان يكون

std::vector<T> buffer;

بتوسيعها قليلاً، يمكنك بالطبع إضافة التحميل الزائد للمشغل بدلاً من وظائف at():

const T &operator()(int x, int y) const
{
  return buffer[y * width + x];
}

و

T &operator()(int x, int y)
{
  return buffer[y * width + x];
}

مثال:

int main()
{
  Array2D<int, 800, 800> a;
  a(10, 10) = 50;
  std::cout << "A(10, 10)=" << a(10, 10) << std::endl;
  return 0;
}

يمكنك عمل متجه للمتجهات، لكن ذلك سيكون له بعض النفقات العامة.بالنسبة للمخزن المؤقت z، الطريقة الأكثر شيوعًا هي إنشاء مصفوفة بحجم 800*800=640000.

const int width = 800;
const int height = 800;
unsigned int* z_buffer = new unsigned int[width*height];

ثم قم بالوصول إلى البيكسلات كما يلي:

unsigned int z = z_buffer[y*width+x];

قد أقوم بإنشاء مصفوفة ذات بعد واحد تبلغ 800*800.من المحتمل أن يكون استخدام تخصيص واحد مثل هذا أكثر كفاءة، بدلاً من تخصيص 800 متجه منفصل.

int *ary=new int[800*800];

ومن ثم، ربما قم بتغليف ذلك في فئة تعمل كمصفوفة ثنائية الأبعاد.

class _2DArray
{
  public:
  int *operator[](const size_t &idx)
  {
    return &ary[idx*800];
  }
  const int *operator[](const size_t &idx) const
  {
    return &ary[idx*800];
  }
};

التجريد الموضح هنا به الكثير من الثغرات، على سبيل المثال، ماذا يحدث إذا قمت بالخروج بعد نهاية "الصف"؟يحتوي كتاب "Effective C++" على مناقشة جيدة لكتابة مصفوفات جيدة متعددة الأبعاد في C++.

شيء واحد يمكنك القيام به هو تغيير حجم المكدس (إذا كنت تريد حقًا المصفوفة الموجودة على المكدس) مع وضع علامة VC للقيام بذلك هي [/F](http://msdn.microsoft.com/en-us/library/tdkhxaks(VS.80).aspx).

لكن الحل الذي ربما تريده هو وضع الذاكرة في الكومة بدلاً من المكدس، لذلك يجب عليك استخدام ملف vector ل vectors.

يعلن السطر التالي أ vector من 800 عنصر، كل عنصر هو vector من 800 ints ويوفر عليك إدارة الذاكرة يدويًا.

std::vector<std::vector<int> > arr(800, std::vector<int>(800));

لاحظ المسافة بين قوسي زاوية الإغلاق (> >) وهو مطلوب لتوضيحه عن عامل التشغيل Shift Right (والذي لن تكون هناك حاجة إليه بعد الآن C++0x).

أو يمكنك تجربة شيء مثل:

boost::shared_array<int> zbuffer(new int[width*height]);

يجب أن تظل قادرًا على القيام بذلك أيضًا:

++zbuffer[0];

لا مزيد من القلق بشأن إدارة الذاكرة، ولا توجد فئات مخصصة للعناية بها، ومن السهل التخلص منها.

هناك طريقة تشبه C:

const int xwidth = 800;
const int ywidth = 800;
int* array = (int*) new int[xwidth * ywidth];
// Check array is not NULL here and handle the allocation error if it is
// Then do stuff with the array, such as zero initialize it
for(int x = 0; x < xwidth; ++x)
{
    for(int y = 0; y < ywidth; ++y)
    {
         array[y * xwidth + x] = 0;
    }
}
// Just use array[y * xwidth + x] when you want to access your class.

// When you're done with it, free the memory you allocated with
delete[] array;

يمكنك تغليف y * xwidth + x داخل فصل دراسي باستخدام طريقة الحصول والضبط السهلة (ربما مع التحميل الزائد لملف [] المشغل إذا كنت تريد البدء في الدخول في لغة C++ الأكثر تقدمًا).أوصي بالوصول إلى هذا ببطء على الرغم من أنك إذا كنت قد بدأت للتو في استخدام لغة C++ ولم تبدأ في إنشاء قوالب فئة كاملة قابلة لإعادة الاستخدام لمصفوفات ذات أبعاد n والتي ستربكك عند البدء.

بمجرد أن تبدأ في العمل الرسومي، قد تجد أن الحمل الزائد لإجراء مكالمات صفية إضافية قد يؤدي إلى إبطاء التعليمات البرمجية الخاصة بك.ومع ذلك، لا تقلق بشأن هذا حتى لا يكون تطبيقك سريعًا بدرجة كافية ويمكنك تخصيصه لإظهار مكان ضياع الوقت، بدلاً من جعل استخدامه أكثر صعوبة في البداية مع احتمالية التعقيد غير الضروري.

لقد وجدت أن الأسئلة الشائعة حول C++ lite كانت رائعة للحصول على معلومات مثل هذه.على وجه الخصوص، يتم الرد على سؤالك من خلال:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.16

يمكنك تخصيص مجموعة على وحدة تخزين ثابتة (في نطاق الملف، أو إضافة static مؤهل في نطاق الوظيفة)، إذا كنت بحاجة إلى مثيل واحد فقط.

int array[800][800];

void fn()
{
    static int array[800][800];
}

بهذه الطريقة لن يذهب إلى المكدس، ولن تضطر إلى التعامل مع الذاكرة الديناميكية.

حسنًا، بناءً على ما بدأه نيال رايان، إذا كان الأداء يمثل مشكلة، فيمكنك اتخاذ خطوة أخرى إلى الأمام من خلال تحسين الرياضيات وتغليف ذلك في الفصل الدراسي.

لذلك سنبدأ بالقليل من الرياضيات.تذكر أنه يمكن كتابة 800 بقوى العدد 2 على النحو التالي:

800 = 512 + 256 + 32 = 2^5 + 2^8 + 2^9

لذلك يمكننا كتابة وظيفة العنونة الخاصة بنا على النحو التالي:

int index = y << 9 + y << 8 + y << 5 + x;

لذلك إذا قمنا بتغليف كل شيء في فئة لطيفة نحصل على:

class ZBuffer
{
public:
    const int width = 800;
    const int height = 800;

    ZBuffer()
    {
        for(unsigned int i = 0, *pBuff = zbuff; i < width * height; i++, pBuff++)
            *pBuff = 0;
    }

    inline unsigned int getZAt(unsigned int x, unsigned int y)
    {
        return *(zbuff + y << 9 + y << 8 + y << 5 + x);
    }

    inline unsigned int setZAt(unsigned int x, unsigned int y, unsigned int z)
    {
        *(zbuff + y << 9 + y << 8 + y << 5 + x) = z;
    }
private:
    unsigned int zbuff[width * height];
};
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top