2016-09-07 6 views
0

У меня есть следующий фрагмент кода, который составляет только половину от всего кода:Попытка создать динамический массив

// Declare map elements using an enumeration 
enum entity_labels { 
    EMPTY = 0, 
    WALL 
}; 
typedef entity_labels ENTITY; 

// Define an array of ASCII codes to use for visualising the map 
const int TOKEN[2] = { 
    32,  // EMPTY 
    178  // WALL 
}; 

// create type aliases for console and map array buffers 

using GUI_BUFFER = CHAR_INFO[MAP_HEIGHT][MAP_WIDTH]; 
using MAP_BUFFER = ENTITY[MAP_HEIGHT][MAP_WIDTH]; 

//Declare application subroutines 
void InitConsole(unsigned int, unsigned int); 
void ClearConsole(HANDLE hStdOut); 
WORD GetKey(); 
void DrawMap(MAP_BUFFER & rMap); 


/************************************************************************** 
    * Initialise the standard output console 
    */ 
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 
if (hStdOut != INVALID_HANDLE_VALUE) 
{ 
    ClearConsole(hStdOut); 

    // Set window title 
    SetConsoleTitle(TEXT("Tile Map Demo")); 

    // Set window size 
    SMALL_RECT srWindowRect; 
    srWindowRect.Left = 0; 
    srWindowRect.Top = 0; 
    srWindowRect.Bottom = srWindowRect.Top + MAP_HEIGHT; 
    srWindowRect.Right = srWindowRect.Left + MAP_WIDTH; 

    SetConsoleWindowInfo(hStdOut, true, &srWindowRect); 

    // Set screen buffer size 
    COORD cWindowSize = { MAP_WIDTH, MAP_HEIGHT }; 
    SetConsoleScreenBufferSize(hStdOut, cWindowSize); 
} 
/*************************************************************************/ 


/************************************************************************* 
* Initialise the tile map with appropriate ENTITY values 
*/ 

MAP_BUFFER tileMap; 

for (unsigned int row = 0; row < MAP_HEIGHT; row++) 
{ 
    for (unsigned int col = 0; col < MAP_WIDTH; col++) 
    { 
     tileMap [row][col] = WALL; 
    } 
} 

По существу весь код используется для создания карты плитки и вывести его на экран, но Я пытаюсь сделать tileMap динамическим массивом во время выполнения. Я попытался создать один там, где создается tileMap. Я попытался создать его сразу после того, как «entity_lables» получили typedef «ENTITY». Я попытался создать его после того, как «MAP_BUFFER» и «GUI_BUFFER» станут псевдонимами. Но все же я в недоумении, я понятия не имею, как успешно реализовать динамический массив для tileMap, и я, конечно же, не знаю лучшего места для его размещения.

Любая помощь была бы принята с благодарностью.

+0

Что касается меня, то я ничего не понял. –

+0

Я не понимаю вопроса, но я думаю, что вам нужен 'std :: vector >' – Mine

+0

Я пробовал изменять MAP_BUFFER tileMap (в нижней части кода) в MAP_BUFFER * tileMap = новый MAP_BUFFER, но затем я получаю сообщение об ошибке «новый», в котором говорится: «Значение типа« ENTITY (*) [30] »не может использоваться для инициализации объекта типа« MAP_BUFFER * »« – Nick

ответ

0

Синтаксис, используемый для определения вашего массива, предназначен для массива C с постоянным размером. В общем случае вы должны уклоняться от массивов C, если размер данных не определяется во время компиляции (и никогда не нужно изменять), и массив никогда не покидает область действия (поскольку массив C не сохраняет информацию по своему размеру.)

Вместо постоянных или динамически массивных массивов C я предлагаю использовать контейнер Vector. Вектор представляет собой динамически размер контейнер, который заполняется со спины, последний элемент, который вы добавили к

std::vector<std::vector<ENTITY>> 

Чтобы добавить вектор контейнер для вашего проекта добавить строку

#include <vector> 

Для заполнения контейнера ваш цикл может выглядеть следующим образом:

MAP_BUFFER tileMap; 

for (unsigned int row = 0; row < MAP_HEIGHT; row++) 
{ 
    std::vector<ENTITY> column; // A column of the tile map 
    for (unsigned int col = 0; col < MAP_WIDTH; col++) 
    { 
     column.push_back(WALL); // Add one element to the column 
    } 
    tileMap.push_back(column); // Add the column to the tile map 
} 

или вы можете инициализировать вектор для размера вы хотите в начале и использовать текущий цикл, чтобы присвоить значение плитки:

using TILE_MAP = vector<vector<ENTITY>>; 
// MAP_WIDTH x MAP_HEIGHT multidimensional vector 
TILE_MAP tileMap(MAP_WIDTH, vector<ENTITY>(MAP_HEIGHT)); 

for (unsigned int row = 0; row < MAP_HEIGHT; row++) 
{ 
    for (unsigned int col = 0; col < MAP_WIDTH; col++) 
    { 
     tileMap [row][col] = WALL; 
    } 
} 

Вызов элемента вектора после его заполнения имеет тот же синтаксис, что и массив.

tileMap[2][4] 

Вы также можете проверить длину вектора:

int rows = tileMap.size(); 
if(rows > 0) 
    int columnsInRow0 = tileMap[0].size() 

Пока вы на него, вы должны смотреть в другие контейнеры, как Карты и наборы, так как они делают вашу жизнь проще.

Edit:

Так как вы хотите, чтобы узнать, как сделать динамический массив не используя вектор Я дам вам ответ: станд :: вектор является C++ определен массив динамически размера. C массивы не изменят размер после того, как они определены, вектор будет.

Однако, я думаю, вы спрашиваете о возможности определять массивы с постоянным размером времени выполнения. Поэтому я объясню, что это такое и почему вы не должны их использовать.

Когда вы определяете массив C, вы, вероятно, получаете предупреждение о том, что выражение должно быть постоянным.

A C массив - это указатель на стек. И реализация массива compiletime C заключается в том, что во время компиляции он должен быть постоянным.

int compiletimeArray[] = { 1, 2, 3 }; 
// turns out c arrays are pointers 
int* ptr = compiletimeArray; 
// prints 2 
std::cout << compiletimeArray[1]; 
// prints 2 
std::cout << ptr[1]; 
// prints 2 
std::cout << *(compiletimeArray + 1); 
// also prints 2 
std::cout << *(ptr + 1); //move pointer 1 element and de-reference 

Указатели напоминают доску с номером телефона, написанным на ней. Такие же проблемы возникают, как и в случае телефонных номеров; число на доске было стерто, число на доске изменилось, получатель не существует, получатель изменил свой номер, у провайдера услуг, заканчивающегося доступными номерами, чтобы дать новых пользователей ... Имейте это в виду.

Чтобы создать массив с постоянным размером времени выполнения, вам необходимо выделить массив в куче и назначить его указателю.

int size = 4; 
int* runtimeArray = new int[size];   // this will work 
delete[] runtimeArray;      // de-allocate 
size = 8;         // change size 
runtimeArray = new int[size];    // allocate a new array 

Основное различие между стека и кучи, что стек будет де-выделить память, используемую переменной, когда программа выходит из сферы переменная была объявлена, с другой стороны, все, объявленного в куче все равно останется в памяти и должен быть явно выделен или вы получите утечку памяти.

// You must call this when you are never going to use the data at the memory address again 
// release the memory from the heap 
delete[] runtimeArray; // akin to releasing a phone number to be used by someone else 

Если вы не освободите память из кучи, в конце концов вы закончите.

// Try running this 
void crashingFunction() { 
    while(true) 
    { 
     // every time new[] is called ptr is assigned a new address, the memory at the old address is not freed 
     // 90001 ints worth of space(generally 32 or 64 bytes each int) is reserved on the heap 
     int* ptr = new int[90001]; // new[] eventually crashes because your system runs out of memory space to give 
    } 
} 

void okFunction() { 
    // Try running this 
    while(true) 
    { 
     // every time new[] is called ptr is assigned a new address, the old is not freed 
     // 90001 ints worth of space is reserved on the heap 
     int* ptr = new int[90001]; // never crashes 
     delete[] ptr; // reserved space above is de-allocated 
    } 
} 

Зачем использовать std :: vector? Поскольку std :: vector внутренне управляет массивом времени выполнения.

// allocates for you 
vector(int size) { 
     // ... 
     runtimeArray = new runtimeArray[size]; 
} 

// When the vector exits scope the deconstructor is called and it deletes allocated memory 
// So you do not have to remember to do it yourself 
~vector() { 
     // ... 
     delete[] runtimeArray; 
} 

Так что, если вы имели один и тот же сценарий, что и в прошлый раз

void vectorTestFunction() { 
    // Try running this 
    while(true) 
    { 
     std::vector<int> vec(9001); // internally allocates memory 
    } // <-- deallocates memory here because ~vector is called 
} 

Если вы хотите использовать во время выполнения постоянного массива я предлагаю зЬй: контейнер массива. Это похоже на вектор, поскольку он управляет своей внутренней памятью, но оптимизирован для того, чтобы вам никогда не приходилось добавлять новые элементы. Он объявлен как вектор, но не содержит функции изменения размера после его конструктора.

+0

Спасибо за все это, я буду помнить об этом. Но можно ли создать новый массив, а не вектор? – Nick

+0

Отредактировал свой ответ. Короткий ответ - да, но вместо этого используйте контейнеры C++: обычно лучше использовать std: array или std :: vector, чем управлять самим кучей. – Gamenotcore

Смежные вопросы