大型动态多维数组不工作

时间:2011-02-20 10:53:44

标签: c++ arrays dynamic multidimensional-array

我的代码非常庞大和复杂,所以我不会浪费你的时间阅读它,但你必须对其中的变量做出某些假设。我将告诉你我在调试器中确认的变量值,以便您确切知道。知道我在这里省略了很多不相关的代码,所以你看到的不是一切,但我已经包含了所有相关的内容。

// This is defined in a class:
char**** m_DataKeys;


// This is in a member function of the same class:
m_DataKeys = new char*** [m_iNumOfHeroes];  // m_iNumOfHeroes = 2

while ( pkvHero )
{
  // iHeroNum = 0 and then 1      #define NUM_OF_ABILITIES 4
  m_DataKeys[iHeroNum] = new char** [NUM_OF_ABILITIES];

  for (int ability = 0; ability < NUM_OF_ABILITIES; ability++)
  {
    if (pkvExtraData)  // only is true when iHeroNum == 1 and ability == 0
    {
      // iNumOfExtraData == 2
      m_DataKeys[iHeroNum][ability] = new char* [iNumOfExtraData];

      while ( pkvSubKey )
      {
        // iCurExtraDataNum increments from 0 to 2
        m_DataKeys[iHeroNum][ability][iCurExtraDataNum] = new char [50];

我在线上设置了一个断点

m_DataKeys[iHeroNum] = new char** [NUM_OF_ABILITIES];

在调用该行之前和iHeroNum == 0之前,m_DataKeys数组如下所示:

m_DataKeys | 0x02072a60
  pointer | 0xffeeffee
    Error : expression cannot be evaluated

预期。调用该行后,它看起来像:

m_DataKeys | 0x02072a60
  pointer | 0x02496b00
    pointer | 0xffeeffee
      Error : expression cannot be evaluated

这似乎是正确的。但是,由于我在那里设置了一个断点,我点击播放并在下一个循环中点击它,现在iHeroNum == 1然后运行该行并且m_DataKeys看起来像这样:

m_DataKeys | 0x02072a60
  pointer | 0x02496b00
    pointer | 0xffeeffee
      Error : expression cannot be evaluated

与以前完全相同!这条线没有改变阵列....完全没有!

为了澄清,m_DataKeys是一个三维数组的字符指针,指向大小为50的字符数组。

我无法弄清楚为什么会发生这种情况,看起来我的代码是正确的。垃圾收集器有可能把我搞砸了吗?或者也许是新的分配器?

编辑:更大问题的症状

让我详细说明我的代码结构,因为实际上,这只是解决更大问题的廉价解决方案。

我已经有了明智的建议:

struct HeroData
{
  // Lots o data here
  // ...
  // .
  //
  AbilityData* Abilities[NUM_OF_ABILITIES];
}

struct AbilityData
{
  // More data here
  // ...
  // .
  CUtlMap<char*,int> ExtraData [MAX_ABILITY_LEVELS];
}

现在当它变得复杂并且我不得不这样做DataKeys指针指针数组的指针数组只有当需要将一些数据加载到动态结构时才需要,其中键,值和数据的数量是完全动态的。所以我想使用char数组的映射来实现整数,但唯一的问题是我不能将实际的char数组存储在我的地图中,我必须使用char *。我尝试将地图定义为:

CUtlMap<char[50],int> ExtraData [MAX_ABILITY_LEVELS];

但那真的没用,反正我觉得有些奇怪。所以,我必须找到一些地方来坚持所有这些ExtraDataKeys,出于某种原因,我认为这样做很酷。如何将char数组存储在数组或映射等对象中?

4 个答案:

答案 0 :(得分:3)

由于您使用指针作为类成员,我最好的猜测是您违反了The Rule Of Three。也就是说,您没有为您的类提供复制构造函数和复制赋值运算符。这通常会导致在传递班级对象时丢失奇怪的数据。

请注意,没有理智的C ++程序员会使用char****。这是我使用向量和字符串解决问题的最佳尝试,但对于您的特定问题,可能有更好的设计:

#include <string>
#include <vector>

class Foo
{
    int m_iNumOfHeroes;
    std::vector<std::vector<std::vector<std::string> > > m_DataKeys;

    enum { NUM_OF_ABILITIES = 4, iNumOfExtraData = 2 };

public:

    explicit Foo(int iNumOfHeroes)
    : m_iNumOfHeroes(iNumOfHeroes)
    , m_DataKeys(m_iNumOfHeroes, std::vector<std::vector<std::string> >
                 (NUM_OF_ABILITIES, std::vector<std::string>(iNumOfExtraData)))
    {
    }
};

int main()
{
    Foo x(2);
}

如果您之前从未在构造函数中看到过冒号语法,那就是member initializer list

  

我真的希望C ++有数组边界检查

std::vectorstd::string 执行检查是否使用foo.at(i)语法而不是foo[i]。在调试模式下,即使foo[i]已在Visual C ++,IIRC中启用了边界检查。

答案 1 :(得分:1)

虽然代码可能是正确的,但我个人觉得使用像char ****这样的东西可能会很快混乱。
这只是我个人的偏好,但我总是试图以最明确,最明确的方式组织事情,所以我在你的情况下会做的事情就像

struct Ability
{
    char extraData[NUM_OF_EXTRA_DATA][50];
};

struct HeroData
{
    Ability abilities[NUM_OF_ABILITIES];
};

class Foo
{
    // here you can choose a 
    HeroData *heroArray;
    // and then you would alloc it with "heroArray = new HeroData[m_iNumOfHeroes];"
    // or you can more simply go with a
    std::vector<HeroData> heroVector;
};

我认为这会使事情变得更加清晰,使您和其他编写代码的程序员更容易跟踪数组中的内容。

答案 2 :(得分:0)

我认为您希望发生错误的事情(调试器中的视觉显示会发生变化),即使您的代码看起来是正确的。

您的调试器显示m_DataKeys*m_DataKeys**m_DataKeys,与m_DataKeysm_DataKeys[0]m_DataKeys[0][0]相同。当您更改m_DataKeys[1]时,您将不会在调试器输出中注意到它。

以下内容可能会对您有所帮助:在我的调试器(MS Visual Studio 2005)中,如果您输入例如: m_DataKeys,5作为您的观察表达式,您将看到数组的前5个元素,即m_DataKeys[0]m_DataKeys[1],...,m_DataKeys[4] - 排列整齐表。如果此语法(使用,5)不适合您,只需将m_DataKeys[1]添加到调试器的监视窗口中。

答案 3 :(得分:0)

不知道为什么昨晚我没有发生这种情况,但我很累。继承人我决定做的事情:

struct AbilityData
{
  // Stuff

  CUtlMap<char*,int> ExtraData [MAX_ABILITY_LEVELS];
  char **DataKeys;
}

这就是我的abilityData结构现在看起来像什么,它现在有效,但现在我想将它重组为:

struct AbilityData
{
  // Stuff

  CUtlMap<char*,int[MAX_ABILITY_LEVELS]> ExtraData;
  char **DataKeys;
}

因为它更有意义,但后来我遇到了与之前使用char数组相同的问题。对我来说,似乎最好放弃整个地图的想法并使之成为:

struct AbilityData
{
  // Stuff

  int *ExtraData;
  char **DataKeys;
}

ExtraData现在也是一个动态分配的数组。

唯一的问题是我现在必须通过一个函数获取我的数据,该函数将循环遍历所有DataKeys,找到我输入字符串的匹配键,然后返回与之关联的ExtraData。