Большой динамический многомерный массив не работает - PullRequest
0 голосов
/ 20 февраля 2011

Мой код, который у меня есть, достаточно большой и сложный, поэтому я не буду тратить ваше время на его чтение, но в результате вам придется сделать определенные предположения о переменных в нем.Я расскажу вам значения переменных, которые я подтвердил в отладчике, чтобы вы знали с уверенностью.Знайте, что я опустил здесь много несвязанного кода, так что вы видите не все, но я включил все, что имеет отношение.

// 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

Кажется, что выглядит правильно.Однако, так как я установил там точку останова, я нажал на play, и он достиг ее на следующем цикле, где 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 массивы указателей на массивы указателей дерьмотолько когда возникла необходимость загрузки некоторых данных в динамическую структуру, где и ключи, значения и числа данных являются полностью динамическими.Поэтому я решил использовать карту массивов символов для целочисленных значений, но единственная проблема заключается в том, что я не могу сохранить реальный массив символов на своей карте, я должен использовать символ *.Я попытался определить карту как:

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

Но это действительно не сработало, и мне все равно кажется странным.Итак, мне нужно было найти место, где можно вставить все эти ExtraDataKeys, и по какой-то причине я подумал, что это круто сделать так.Как я могу хранить массивы символов в таких объектах, как массивы или карты?

Ответы [ 4 ]

3 голосов
/ 20 февраля 2011

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

Обратите внимание, что ни один здравомыслящий программист 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);
}

Если вы никогда раньше не видели этот синтаксис двоеточия в конструкторе, то это список инициализирующих элементов .

Мне бы очень хотелось, чтобы в C ++ была проверка границ массивов

std::vector и std::string do имеют проверку границ, если вы используете синтаксис foo.at(i) вместо foo[i]. В режиме отладки даже в foo[i] включена проверка границ в Visual C ++, IIRC.

1 голос
/ 20 февраля 2011

Хотя код может быть верным, я лично нахожу, что работа с чем-то вроде 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;
};

Я думаю, что это делает вещи более ясными, облегчая вам и другим программистам, работающим над этим кодом, отслеживатьтого, что находится внутри ваших массивов.

0 голосов
/ 20 февраля 2011

Не знаю, почему это не пришло мне в голову прошлой ночью, но я очень устал.Вот, что я решил сделать:

struct AbilityData
{
  // Stuff

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

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

struct AbilityData
{
  // Stuff

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

Потому что в этом есть смысл, но потом я столкнулся с той же проблемой, что и раньше, с массивом char.Мне кажется, что лучше всего отказаться от всей идеи карты и сделать так:

struct AbilityData
{
  // Stuff

  int *ExtraData;
  char **DataKeys;
}

Где ExtraData теперь также является динамически размещаемым массивом.

Единственная проблемаэто означает, что теперь мне нужно получить мои данные с помощью функции, которая будет проходить через все DataKeys, найти соответствующий ключ для моей входной строки, а затем вернуть связанные с ней дополнительные данные.

0 голосов
/ 20 февраля 2011

Я думаю, вы ожидаете, что произойдет что-то неправильное (визуальное отображение в отладчике изменится), даже если ваш код кажется правильным.

Ваш отладчик отображает m_DataKeys, *m_DataKeys и **m_DataKeys, что совпадает с m_DataKeys, m_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] в окно просмотра отладчика.

...