Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 19.12.2012, 02:17   #1
ArmanPrestige
Главное желание.
Форумчанин
 
Регистрация: 28.11.2011
Сообщений: 122
Печаль Ошибка с памятью

Задание:
Цитата:
Реализовать класс строки с разделяемыми данными, и стратегией при изменении. То есть данные строки хранятся где-то в памяти. При создании копии строки, копия лишь ссылается на данные. Только при изменении строки происходит реальное копирование данных. (из тестового задания на вакансию junior)
+
Добавить для строки несколько методов(удаление позиции, изменение позиции и т.д.)
Решение оформить в виде пользовательской библиотеки.
Единственной проблемой, над которой ломал голову - это деструктор. Ведь если объект ссылается на копию, то при вызове деструктора уничтожатся все строки, которые были копиями(если конечно не изменялись). Это я решил следующим образом - в классе указатель на инт. Который хранит в себе кол-во копий обхекта. И при вызове деструктор проверяет это. Далее приведу код и потом опишу в чем трабла.

str.h
Код:
#pragma once
#pragma warning(disable: 4018) 
// прагма чтобы не выводилось warning C4018: '<=' : signed/unsigned mismatch


#include <iostream>
#include <clocale>

#define RU setlocale(LC_ALL, "Russian")

namespace str{	

	enum MemoryStatus { on, off };

	class Line{
	private:
		Line();	// нельзя создавать "никакую" строку((
		MemoryStatus NeedMemory;
		char *data;
		unsigned int *COPY__COUNT;
		void MemoryForChange();			
		
	public:
		Line(char* );
		Line(const Line&);			
		~Line(void);
		void DeleteElement(int _Position);
		void ChangeElement(int _Position, char Element);
		void out();						
	};
}
str.cpp
Код:
#include "str.h"
#include <iostream>
#include <clocale>
#include <cstring>

using namespace std;
using namespace str;


Line::Line(char* tmp){
	NeedMemory = off;
	COPY__COUNT = new unsigned int;
	*COPY__COUNT= 0;

	data = new char[strlen(tmp)+1];
	for(int i = 0; i <= strlen(tmp); i++)
		data[i] = tmp[i];
}



Line::Line(const Line &PARENT){
	NeedMemory = on;		//устанавливаем флаг, что это копия
	data = PARENT.data;
	COPY__COUNT = PARENT.COPY__COUNT;
	(*COPY__COUNT)++;
	cout << "Вы скопировали строку. Память не выделена." << endl;
}


//ДЕСТРУКТОР ЗАККОМЕНТИРОВАН
//ПОТОМУ ЧТО ВЫЗЫВАЕТ ОШИБКУ(ЧЕРЕЗ DEV-CPP ВПЛОТЬ ДО СИНЕГО ЭКРАНА)
Line::~Line(void){
	/*if(*COPY__COUNT){
		cout << "Строка <" << data << "> используется другими объектами. Удаление невозможно." << endl;
	}
	else{
		cout << "Строка <" << data << "> удалена." << endl;
		delete COPY__COUNT;
		delete [] data;
	}*/
}


void Line::MemoryForChange(){
	/* Если выделяем память - то это уже самостоятельная	 */
	/* и полноценная строка. По-этому для нее создаем свой  */
	/* COPY__COUNT - чтобы отслеживать кол-во копий строки */
	if(NeedMemory==on){	
		char *tmp = new char[strlen(data)];

		for(int i = 0; i <= strlen(data); i++)
			tmp[i] = data[i];

		data = new char[strlen(tmp)];
		
		for(int i = 0; i <= strlen(tmp); i++)
			data[i] = tmp[i];

		NeedMemory = off;
		(*COPY__COUNT)--;
		COPY__COUNT = new unsigned int;
		*COPY__COUNT = 0;
		
		cout << "Вы модифицируете строку. Выделяется память." << endl;
	}		


	/* Если же память уже выделялась для строки - ничего не делаем. */
	else{
		cout << "Вы модифицируете строку. Память уже была выделена." << endl;
	}
}



void Line::DeleteElement(int _Position){
	_Position--;
	/* Поизицию метод класса получает реальную, которую видет пользователь */
	/* Соответственно, индекс у элемента будет равен позиция минус 1.     */
	if(_Position < 0) throw 0;
	if(_Position > strlen(data)) throw 1;
	MemoryForChange();
	int i;
	for(i = _Position; i<strlen(data); i++)
		data[i] = data[i+1];
}

void Line::ChangeElement(int _Position, char Element){
	_Position--;
	/* Поизицию метод класса получает реальную, которую видет пользователь */
	/* Соответственно, индекс у элемента будет равен позиция минус 1.     */
	if(_Position < 0) throw 0;
	if(_Position > strlen(data)) throw 1;

	MemoryForChange();
	data[_Position] = Element;
}

void Line::out(){
	cout << data << endl;
}
Я могу, Я хочу, Я буду!

Последний раз редактировалось ArmanPrestige; 19.12.2012 в 02:20.
ArmanPrestige вне форума Ответить с цитированием
Старый 19.12.2012, 02:17   #2
ArmanPrestige
Главное желание.
Форумчанин
 
Регистрация: 28.11.2011
Сообщений: 122
По умолчанию

//продолжение


main.cpp // тестирование
Код:
#include "str.h"

using namespace std;
using namespace str;
int main(){
	RU;

	try{
		Line A("Test Line.");
		Line B = A;
		Line C = B;
		A.out();
		B.out();
		C.out();
		A.DeleteElement(2);
		A.out();
		B.out();
		C.out();
		A.ChangeElement(1,'^');		
		A.out();
		B.out();
		C.out();
		C.DeleteElement(2);				
		A.out();
		B.out();
		C.out();
		B.DeleteElement(1);
		A.ChangeElement(4, 'Z');
		A.out();
		B.out();
		C.out();

	}

	catch(int n){
		if(n==0)
			cout << "Аргумент _Position должен быть больше нуля!" << endl;
		if(n==1)
			cout << "Аргумент _Position не должна превышать длинну строки!" << endl;
	}
	system("pause");
	return 0;

}
Так вот, проблема в деструкторе. Если отработает метод MemoryForChange() - беда. Потому что как только дело доходит до деструктора, зависает.

Через дебаг получаю вот что
Цитата:
First-chance exception at 0x755ab9bc in UserString.exe: Microsoft C++ exception: Win32Util::Error at memory location 0x003081e0..
Что скажете?
Я могу, Я хочу, Я буду!
ArmanPrestige вне форума Ответить с цитированием
Старый 19.12.2012, 03:41   #3
EUGY
Форумчанин
 
Аватар для EUGY
 
Регистрация: 11.07.2010
Сообщений: 914
По умолчанию

Что тут можно сказать...
Код:
char *tmp = new char[strlen(data)];
Сколько выделяется памяти? По количеству символов.
А копирование строки идет с выходом за пределы массива
Код:
for(int i = 0; i <= strlen(data); i++)
tmp[i] = data[i];
Короче не выделена память под нуль-терминатор.
EUGY вне форума Ответить с цитированием
Старый 19.12.2012, 10:37   #4
Suby
Пользователь
 
Аватар для Suby
 
Регистрация: 03.11.2012
Сообщений: 89
По умолчанию

Цитата:
Сообщение от ArmanPrestige Посмотреть сообщение
Line A("Test Line."); // создание и инициализация объекта
Line B = A; // операция копирования одного объекта в другой
Попробую предположить, что когда вы создаете и инициализируете объект (Line A("Test Line.")), то проблем нет, ибо он создается с помощью вашего пользовательского конструктора, в котором происходит выделение памяти new под строку. Деструктор при удалении этого объекта (А) без проблем отрабатывается, за счет содержания в нем delete. Но когда вы копируете один объект в другой (Line B = A), то т.к. у вас не создан свой конструктор копирования, то компиль создает свой (в котором не происходит выделение памяти new) для создания временного объекта чтоб в него скопировать данные из А и присвоить их В, в котором создает указатели (char *data и unsigned int *COPY__COUNT) на те же данные, что и А без выделения памяти new. Как только данные скопировались, временный объект уничтожается вашим деструктором с delete (происходит удаление непонятно чего), что и вызывает ошибку. Возможно от этого все проблемы. Помимо своего конструктора копирования возможно нужен и конструктор присваивания.
Сорри за масло маслянное и если не до конца вкурил ваш код, и ошибаюсь
Suby вне форума Ответить с цитированием
Старый 19.12.2012, 20:35   #5
ArmanPrestige
Главное желание.
Форумчанин
 
Регистрация: 28.11.2011
Сообщений: 122
По умолчанию

Цитата:
Сообщение от EUGY Посмотреть сообщение
Что тут можно сказать...
Код:
char *tmp = new char[strlen(data)];
Сколько выделяется памяти? По количеству символов.
А копирование строки идет с выходом за пределы массива
Код:
for(int i = 0; i <= strlen(data); i++)
tmp[i] = data[i];
Короче не выделена память под нуль-терминатор.
Большое спасибо, действительно позорную ошибку допустил. Благодарствую!

Цитата:
Сообщение от Suby Посмотреть сообщение
Попробую предположить, что когда вы создаете и инициализируете объект (Line A("Test Line.")), то проблем нет, ибо он создается с помощью вашего пользовательского конструктора, в котором происходит выделение памяти new под строку. Деструктор при удалении этого объекта (А) без проблем отрабатывается, за счет содержания в нем delete. Но когда вы копируете один объект в другой (Line B = A), то т.к. у вас не создан свой конструктор копирования, то компиль создает свой (в котором не происходит выделение памяти new) для создания временного объекта чтоб в него скопировать данные из А и присвоить их В, в котором создает указатели (char *data и unsigned int *COPY__COUNT) на те же данные, что и А без выделения памяти new. Как только данные скопировались, временный объект уничтожается вашим деструктором с delete (происходит удаление непонятно чего), что и вызывает ошибку. Возможно от этого все проблемы. Помимо своего конструктора копирования возможно нужен и конструктор присваивания.
Сорри за масло маслянное и если не до конца вкурил ваш код, и ошибаюсь
По-моему, когда Line A = B - вызывается определенные мною конструктор копирования. В этом случае B передается в качестве аргумента. Спасибо за проявленное внимание к данной теме!
Я могу, Я хочу, Я буду!
ArmanPrestige вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Ошибка при работе с памятью alexey_kip Общие вопросы C/C++ 2 18.11.2012 10:08
Работа с динамической памятью через указатели. Загадочная ошибка. Hqw33 Общие вопросы C/C++ 2 28.03.2012 18:42
Ошибка работы с памятью Skender Помощь студентам 1 07.09.2011 17:30
ошибка в работе с динамической памятью thrashkid Общие вопросы C/C++ 8 30.06.2011 00:48
Ошибка работы с памятью Alex Cones Общие вопросы Delphi 4 04.12.2010 06:45