Работа с переменными (с++)
Проблема, с которой я столкнулся, заключается в следующем: на c++ реализован пользовательский класс; в главной процедуре создается его экземпляр и с ним происходит работа через интерфейсные функции класса.
Внутри этих процедур динамически выделяются массивы (средствами выделения динамической памяти new). Т.е. каждый экземпляр класса хранит указатель на массив, который в ходе работы меняется (и структурно и информационно). Проблема возникла в том, что при выделении очередного массива все теми же средствами new, компилятор размещает новый массив на месте, казалось бы, уже занятым другим массивом. Это несколько неожиданно, на мой взгляд т.к. ранее размещенный массив безвозвратно теряется.
Вопрос в следующем - что бы сделать такого, что бы компилятор отдавал себе отчет в том, что ранее выделенные области памяти (до соответствующего вызова оператора detele) заняты и не пригодны для размещения в них новых данных.
На всякий случай приведу выписки из кода:
{
// В классе описаны структуры
struct somestruct1
{
char* string1;
char* string2;
int integer1;
};
struct somestruct2
{
somestruct1* somestruct1_pointer;
int integer;
};
// Каждый экземпляр содержит
somestruct1* struct1_pointer;
somestruct2* struct2_pointer;
int main_integer;
// Процедуры, которые вызываются при работе c объектом
int myClass::g()
{
if (main_integer == 0)
{
struct2_pointer = new somestruct2;
struct2_pointer[0].somestruct1_pointer = NULL;
struct2_pointer[0].integer = 0;
} else {
if ((struct2_pointer[main_integer - 1].integer) == 0)
return 1;
somestruct2* new_struct2;
new_struct2 = new somestruct2[main_integer];
for (int i=0; i<main_integer; i++)
{
new_struct2.somestruct1_pointer = struct2_pointer.somestruct1_pointer;
new_struct2.integer = struct2_pointer.integer;
}
new_struct2[main_integer].somestruct1_pointer = NULL;
new_struct2[main_integer].integer = 0;
delete[]struct2_pointer;
struct2_pointer = new_struct2;
}
main_integer++;
return 0;
}
int myClass::h (char* chars1, char* chars2)
{
somestruct1* struct1 = new somestruct1;
struct1_pointer->integer1++;
// !!!
struct1->string1 = new char[strlen(chars1)]; // !!! Тут происходит выделение ранее используемое памяти
// !!!
struct1->string2 = new char[strlen(chars2)];
strcpy(struct1->string1, chars1);
strcpy(struct1->string2, chars2);
...
return 0;
}
...
}
// И сама процедура, из которой это все вызывается
void f()
{
myClass mc;
...
mc.g();
mc.h();
...
}
В момент выполнения процедуры h в указаной строке происходит затирание выделенной в g памяти (строка struct1->string1 заменяет ранее созданный массив struct2_pointer). Что я делаю не так? :)
P.S. Компилятор - CodeGear C++Builder 2009 (Update 1)
А в целом код, просто мясо... Можно было хотя бы дать осмысленные имена переменным? Или думаете кому-то интересно копаться в somestruct1, somestruct2, somestruct1_pointer, main_integer... ужас...
P.S. Используйте std:string или String (раз уж пишите в билдере...)
Много чего. Например, вы неправильно думаете, что (main_integer == 0) это true.
А в целом код, просто мясо... Можно было хотя бы дать осмысленные имена переменным? Или думаете кому-то интересно копаться в somestruct1, somestruct2, somestruct1_pointer, main_integer... ужас...
P.S. Используйте std:string или String (раз уж пишите в билдере...)
Вот полный исходный код. Тут полно комментариев, но зато имена такие, наверное, более удобные для вас.
По-сути дела, при пошаговом выполнении у меня не работает добавление элемента (v.add_son(a, b) и то, что в демке идет далее). При этом сбой происходит при попытке записать в информационную часть элемента дерева полученной строки через создание дубликата полученной строки - сбивается массив, хранящий структуру дерева - уровни (по крайней мере первый уровень затирается).
Много чего. Например, вы неправильно думаете, что (main_integer == 0) это true.
:) В конструкторе, который тут не приведен, я определяю его изначально равным нулю. В компилятор я, конечно, не верю и не доверяю ему обнуление всех созданных переменных.
Спасибо за отклики!
Сразу непонятно где ошибка, но все же почему не использовать String? :)
Между прочим, делаете это не самым лучшим способом.
Компилятор по стандарту не должен обнулять локальные переменные.
Но все это мелочи. Все же хотелось бы услышать ответ на вопрос, который задал GreenRiver - почему вы не используете STL?
И еще. Попробуйте написать маленький класс, в котором будет повторяться тот же баг. Так может вы и свою ошибку найдете, и другим время сэкономите.
Сразу непонятно где ошибка, но все же почему не использовать String? :)
То, что за основу взято разбиение на уровни, продиктовано требованием к классу включать в себя возможность использования экземпляра класса в качестве псевдопеременной. Цитирую из задания:
d(i,j)=d2; //где d, d2 – деревья
Если основываться на уровнях (да, хитрые процедуры перестройки, но их всего две), то достаточно легко реализуется как это требование, так и другие. String не знаю почему не использую :) если ничего другого не поможет (пока тут кучу вариантов объявлений и модицикаций процедуры увеличения количества уровней перебираю), попробую String, спасибо :).
Компилятор по стандарту не должен обнулять локальные переменные.
Но все это мелочи. Все же хотелось бы услышать ответ на вопрос, который задал GreenRiver - почему вы не используете STL?
И еще. Попробуйте написать маленький класс, в котором будет повторяться тот же баг. Так может вы и свою ошибку найдете, и другим время сэкономите.
Ну я понимаю, что компилятор не обязян обнулять. Я просто отметил, что делаю это вручную (с долей иронии в высказывании о том, что не доверяю компилятору), как оказывается, не лучшим спобобом :) А как, по-вашему, лучше обнулить счетчик уровней?
За совет по поводу создания миниатюрного класса спасибо - попробую.
На счет счетчика уровней ничего не знаю, но советую предпочитать инициализацию вместо присваивания в конструкторах. Например:
int n;
MyClass():n(15){/*...*/}
};
иначе все равно конструктор сам произведет инициализацию по умолчинию, а потом еще и присвоит значение. Лишнее действие.
{
// Информационная часть
char* key;
char* relation;
// Cсылка на следующего соседа по уровню (конец уровня опреледяет ссылка на ноль)
node* next;
// Ссылка на первого сына (конец списка сыновей опреледяет ссылка на ноль)
node* son;
};
Это позволило обойтись без необходимости создавать и перестраивать массив уровней. Немного усложнился поиск i-ого элемента j-ого уровня, но это того стоило. Другие процедуры за счет облегчения структуры стали значительно проще и понятнее.
Если кто надумает повторять подвиг - советую придерживаться классического вида дерева, а неудобства разрешать путем введения дополнительных процедур, облугчающих работу со структурой, вместо того, чтобы перестраивать представление дерева.
Всем отписавшимся большое спасибо за помощь и поддержку. Всем посетителям желаю удачи в реализации проектов.