【问题标题】:Attempting to create a dynamic array尝试创建动态数组
【发布时间】:2017-01-15 05:54:15
【问题描述】:

我有以下一段代码,只占整个代码的一半:

// 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,我当然也不知道放置它的最佳位置。

任何帮助将不胜感激。

【问题讨论】:

  • 至于我当时什么都不懂。
  • 我不明白这个问题,但我猜你需要的是std::vector&lt;std::vector&lt;int&gt;&gt;
  • 我尝试将 MAP_BUFFER tileMap(朝向代码底部)更改为 MAP_BUFFER * tileMap = new MAP_BUFFER,但随后我在“new”上收到错误消息,显示“a value of type” ENTITY()[30]" 不能用于初始化类型为 "MAP_BUFFER " " 的实体

标签: c++ arrays memory dynamic runtime


【解决方案1】:

您用于定义数组的语法是用于常量大小的 C 数组。一般来说,你应该避免使用 C 数组,除非数据的大小在编译时确定(并且永远不需要更改)并且数组永远不会离开范围(因为 C 数组不保留关于其自身大小的信息。)

我建议使用 Vector 容器来代替常量或动态大小的 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
}

或者您可以在开始时将 Vector 初始化为您想要的大小,然后使用当前循环来分配图块值:

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()

当您使用它时,您应该查看其他容器,例如 Maps 和 Sets,因为它们让您的生活更轻松。

编辑:

既然你想知道如何不使用向量来制作动态数组,我会给你一个答案:std::vector 是 C++ 定义的动态大小的数组。 C 数组定义后不会改变大小,向量会。

但是我认为您是在询问定义运行时常量大小数组的能力。所以我将解释它们是什么以及为什么你不应该使用它们。

当您定义 C 数组时,您可能会收到一条警告说表达式需要是常量。

C 数组是指向堆栈的指针。而编译时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
 }

如果你想使用运行时常量数组,我建议使用 std:array 容器。它与 vector 类似,因为它管理其内部存储器,但针对您从不需要添加新元素的情况进行了优化。它和vector一样声明,但在其构造函数之后不包含调整大小的函数。

【讨论】:

  • 谢谢你,我会记住的。但是有没有可能创建一个新数组而不是一个向量?
  • 编辑了我的答案。简短的回答是肯定的,但请改用 c++ 容器:通常使用 std:array 或 std::vector 比自己管理堆更好。
猜你喜欢
  • 2019-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多