Страница 1 из 2

Удаление динамических потомков

Добавлено: 30 июн 2016, 20:08
Skwoogey

Код: Выделить всё

class module
{
...
};

class memory: public module
{
...
};

int main()
{
	//module *p;
	//p = new memory;
	memory a;
	
	//p->solved = p->interface();
	a.solved = a.interface();
	
	//cout << p->solved << endl;
	cout << a.solved << endl;
	
	//delete p;
	
	return 0;
}
Если объект создается динамически, то иногда программа крашится при удалении объекта. при обычном создании объекта программа завершается корректно. Деструкторы сделал виртуальными. В чем дело понять не могу. Нужна помощь. Заранее спасибо.

Re: Удаление динамических потомковж

Добавлено: 01 июл 2016, 14:49
Romeo
По первым описанным признакам, а также по тому, что удаление объекта полиморфное, первая же мысль была о том, что деструктор не сделан виртуальным в базовом классе. Но раз этот факт отдельно оговорен, то нужна дополнительная информация о том, что там скрыто в трёх точках. В частности, что за поле solved и что за метод interface?

Re: Удаление динамических потомковж

Добавлено: 01 июл 2016, 14:56
Skwoogey
содержимое модуля:

Код: Выделить всё

	public:
	int solved;
	int answer;
	int tried;
	
	//simple_wires
	int wires_num;
	wires w[6];
	
	//button
	int colour;
	int name;
	
	//simon_says
	int sequence[6];
	int seq_length;
	int cur_beat;
	
	//memory
	int stage;
	int answer_type;
	int screen_number;
	int button_numbers[4];
	int previous_stages[][2];
	
	int virtual right_answer()
	{
		
	}
	
	int virtual interface()
	{
		
	}
	
	virtual ~module()
	{
		cout << "mod" << endl;
	}
содержимое класса "memory":

Код: Выделить всё

	public:
	memory()
	{
		stage = 1;
		tried = 1;
		solved = 0;
	}
	
	virtual ~memory()
	{
		cout << "mem" << endl;
	}
	
	int interface()
	{
		while(stage != 6)
		{
			if(tried)
			{
				screen_number = rand() % 4 + 1;
				for(int i = 0; i < 4; i++)
				{
					int bak = rand() % 4 + 1;
					int additional = 0;
					for(int j = 0; j <  i; j++)
					{
						if(button_numbers[j] == bak)
						{
							bak = (bak % 4) + 1;
							j = -1;
						}
					}
					button_numbers[i] = bak;
				}
			}
			tried = 0;
		
			switch(stage)
			{
				case 1:
				{
					answer_type = POSITION;
					if((screen_number == 1) || (screen_number == 2))
						answer = 2;
					if(screen_number == 3)
						answer = 3;
					if(screen_number == 4)
						answer =4;
					break;
				}
				
				case 2:
				{
					answer_type = POSITION;
					if((screen_number == 4) || (screen_number == 2))
						answer = previous_stages[0][POSITION];
					if(screen_number == 1)
					{
						answer = 4;
						answer_type = NUMBER;
					}
					if(screen_number == 3)
						answer =1;
					break;
				}
				
				case 3:
				{	
					answer_type = NUMBER;
					if(screen_number == 1)
						answer = previous_stages[1][NUMBER];
					if(screen_number == 2)
						answer = previous_stages[0][NUMBER];
					if(screen_number == 3)
					{
						answer = 3;
						answer_type = POSITION;
					}
					if(screen_number == 4)
						answer =4;
					break;
				}
			
				case 4:
				{
					answer_type = POSITION;
					if(screen_number == 1)
						answer = previous_stages[0][POSITION];
					if(screen_number == 2)
						answer = 1;
					if((screen_number == 3) || (screen_number == 4))
					{
						answer = previous_stages[1][POSITION];
					}
					break;
				}
				
				case 5:
				{
					answer_type = NUMBER;
					if(screen_number == 1)
						answer = previous_stages[0][NUMBER];
					if(screen_number == 2)
						answer = previous_stages[1][NUMBER];
					if(screen_number == 3)
						answer = previous_stages[3][NUMBER];
					if(screen_number == 4)
						answer = previous_stages[2][NUMBER];
					break;
				}
			}
			//system("cls");
			
			
			cout << "strikes: " << strikes << endl;
			cout << "stage: " << stage << endl;
			cout << "answer_type: " << answer_type << endl;
			cout << "answer: " << answer << endl;
			cout << "prev_pos: " << previous_stages[stage-2][POSITION] << endl;
			cout << "prev_num: " << previous_stages[stage-2][NUMBER] << endl;
			 
			
			
			cout << "screen" << endl;
			cout << "| " << screen_number << " |" << endl << endl;
			cout << "buttons" << endl;
			cout << "| ";
			for(int i = 0; i < 4; i++)
			{
				cout << button_numbers[i] << " | ";
			}
			cout << endl << endl;
			cout << "type button position" << endl;
			char player_answer;
			scanf("%c", &player_answer);
			getchar();
			cout << endl;
			if(player_answer == 'q')
				return 0;
			tried = 1;
			switch(answer_type)
			{
				case POSITION:
					if(((int)player_answer - 48) == answer)
					{
						previous_stages[stage - 1][POSITION] = answer;
						previous_stages[stage - 1][NUMBER] = button_numbers[answer - 1];
						stage++;
					}
					else
					{
						strikes++;
						stage = 1;
					}
					break;
				case NUMBER:
					if(button_numbers[(int)player_answer - 49] == answer)
					{
						previous_stages[stage-1][POSITION] = (int)player_answer - 48;
						previous_stages[stage-1][NUMBER] = answer;
						stage++;
					}
					else
					{
						strikes++;
						stage = 1;
					}
					break;
						
			}
		}
		return 1;
	}
Чтобы было понятнее, это я для проекта в институте "портирую" игру "keep talking and nobody explodes" на командную строку.

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 00:12
Romeo
Во-первых, ничего критичного и могущего привести к крашу я не обнаружил.

Во-вторых, после того, как засунул сей опус в cpp файл, поправил какое-какие места (так как не хватало дефайнов) и вернул полиморфное создание/удаление объекта, то у меня всё отработало без краша.

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

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 00:25
Skwoogey
если просто сразу выходить с помошью "Q", то все нормально, если немного поиграться, то крашится.

Про дефайны чего-то сразу не додумался кинуть. Вот все на всякий случай.

Код: Выделить всё

#define GREEN 0
#define BLUE 1
#define YELLOW 2
#define RED 3
#define BLACK 4
#define WHITE 5

#define ABORT 1
#define DETONATE 2
#define HOLD 3

#define P_R 0
#define H_R 1

#define POSITION 0
#define NUMBER 1
Базовый класс нужен, чтобы для бомбы рандомные модули генерировать, но держать их в одном массиве указателей.

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

Я тут еще поэкспериментировал, просто вводя 2 как ответ: начинает крашится после 4 введеных 2. После 3, не получилось.

Я до того как обратиться сюда за помощью, поискал в интернете на эту тему. Встретил подобную ситуацию, но она вроде так и осталась неразрешенной.
http://www.programmersforum.ru/showthread.php?t=165992

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 00:55
Romeo
А что такое за поле strikes и что за тип wires? Слушай, может всё сложишь в один файлик, проверишь, что компилируется и выложишь сюда, чтобы я мог забрать к себе и собрать? А то занимаюсь гаданием на кофейной гуще...

И да, инициализация полей базового класса в конструкторе наследника - это очень-очень плохой тон. Поля базового класса должен инициализировать конструктор базового класса. К сожалению, это не единственное спорное место в этом коде. Там таких мест куча. Но сейчас хочется лучше на них не обращать внимания. Для начала нужно понять причину краша.

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 01:03
Skwoogey
Вот. В мейне у меня там много различных тестов в комментариях лежат, но тест с указателями не закомментирован.

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 02:06
Romeo
Получилось воспроизвести. Судя по тому, как дебагер показывает виртуальную таблицу в деструкторе module, ты портишь указатель на виртуальную таблицу, оперируя с полями. Скорее всего где-то едешь по памяти, вылезая за границы массива.

Изображение

В качестве проверки, можешь закомментить тело solve_interface и обратить внимание, что краш пропадает, так что виноват именно проезд по памяти, а не то, что неправильно работают виртуальные деструкторы.

Более подробно определять проблемное место сейчас лень, так как ночь уже и пора на боковую.

Чтобы точнее узнать место, которое нужно исправить, комментируй кусками проблемный метод и каждый раз пробуй воспроизвести проблему. Как только воспроизводимость меняется, значит ты только что закомментил/раскомментил искомый код.

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 02:29
Skwoogey
Нашел. Я случайно забыл указать один из размеров массива previous_stages, и у меня там просто были пустые скобки. Я удивлен, что он вообще заработал. Насколько я знаю пустые скобки можно оставлять только если сразу инициализируешь. Спасибо за помощь. Я вроде репутацию повысил, но не уверен (После 4PDA все такое непривычное, так что скажите если я ошибся). Еще есть просьба: можете рассказать о хорошем тоне кодинга? Все,что я слышал в институте, это соблюдать табуляцию и не использовать goto.

Re: Удаление динамических потомковж

Добавлено: 02 июл 2016, 11:13
Romeo
Ну, в двух словах о правильном проектировании рассказать просто не получится.

На данный момент у тебя не соблюдается даже принцип инкапсуляции, а ведь это один из трёх базовых столпов ООП. Данные должны сокрываться в классе. В идеале в секции public должны быть расположены только методы. Мотивацию смотри в статье.

Следующий шаг - разобраться с принципами SOLID.

Когда с базой всё будет в порядке, то если захочешь расти дальше, нужно уже читать книги. Начать можно с чего-нибудь не очень тяжелого, например "Рефакторинга" Фаулера. Благо книга не новая, так что доступна для бесплатного скачивания, например тут. Есть и парочка других книжек этого же уровня, названия которых легко гуглятся. Хотя, в принципе, и одного Фаулера будет достаточно.

После ещё одного левел апа, уже следует браться за более серьёзное чтиво, а именно за шаблоны проектирования. Вкратце о них можно почитать на вики, но там очень сжато и непонятно зачем вообще это нужно. Лучше, конечно, будет прочитать книгу знаменитой "банды четырёх". Залакировать можно будет книжкой "Рефакторинг с использованием шаблонов" Джошуа Кириевски.

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

И это только чтиво по архитектуре. По хорошему нужно ещё и язык постигать. Здесь нужно читать Страуструпа, Майерса и даже Александреску (только последнего крайне осторожно, чтобы не сломать голову). Полный список книг, который обязан прочесть любой уважающий себя разработчик С++ состоит из около десятка произведений.

P.S. По поводу репутации. У тебя всё получилось. Этой кнопочкой народ так толком и не научился пользоваться, так как админы что-то намутили, и после смены пары движков форума, как у нас сейчас работает репутация, не понимает почти никто :)