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

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

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

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

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

Ответ
 
Опции темы Поиск в этой теме
Старый 16.11.2014, 20:17   #1
Dumbl-D
Пользователь
 
Регистрация: 25.11.2013
Сообщений: 26
По умолчанию Абстрактный класс динамического списка

Доброго времени суток, пытаюсь разобраться с абстрактными классами. Прочитала материал, с простыми примерами разобралась, а вот выполнить свою задачу не могу

Спроектировать абстрактный класс, в моем случаи класс Контейнер, затем создать класс потомок этого класса и продемонстрировать работу с ним с помощью функций.
Класс контейнер попыталась создать, от него создала один дочерний - Список. Определила все функции класса-предка как чисто виртуальные.
Программа не работает. Явно что-то не так, но не могу понять.
Помогите, пожалуйста.

Код:
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <string>


using namespace std;

class Container
{
public:
	Container();
	virtual void push_back(int val) = 0;
	virtual void push_front(int val) = 0;
	virtual void insert(int val) = 0;
	virtual void print() = 0;
	virtual void erase(int val) = 0;
	virtual void erase();
	virtual void find(int val) = 0;
	virtual void deleteall() = 0;
	virtual ~Container();
};

class Node
{
public:
	int elem;
	Node* next;
	Node();
};
Node::Node()
{
	next=0;elem=0;
}
class List: public CContainer
{
public:
	Node* head;
	Node* tail;
	List(){head=0;tail=0;}
	~List(){ if(head!=NULL) {delete head;delete tail;}
}
	void push_back(int val);
	void push_front(int val);
	void insert(int val);
	void print();
	void erase(int val);
	void erase();
	void find(int val);
	void deleteall();
};

void List::push_back(int val)
{
	Node* Temp;
	Temp= new Node;
	Temp->elem=val;
	if(head==0)
	{
		head=Temp;
		tail=Temp;
		return;
	}
	tail->next=Temp;
	tail=Temp;
}
void List::push_front(int val)
{
	Node* Temp;
	Temp=new Node;
	Temp->elem=val;
	Temp->next=head;
	head=Temp;
}
void List::insert(int val)
{
	if(head==0)
	{
		push_front(val);
		return;
	}
	Node* Temp;
	Temp=new Node;
	Temp->elem=val;
	Node* founded=head;
	for(founded; founded!=0;founded=founded->next)
	{
		if(founded->elem<val)
			break;
	}
	if(founded==0)
	{
		push_front(val);
		return;
	}
	Temp->next=founded->next;
	founded->next=Temp;
}
void List::print()
{
	if(head==0)
	{
		cout<<"Список пуст\n";
		return;
	}
	
	cout<<"\n Список: ";
	for(Node* ptr=head;ptr!=0;ptr=ptr->next)
	{
		cout<<ptr->elem<<" ";
	}
	cout<<"\n";
}
void  List::find(int val)
{
	if(head==0)
	{
		cout<<"Список пуст\n";
	}
	Node* ptr=head;
	for(ptr;ptr!=0;ptr=ptr->next)
	{
		if(ptr->elem==val)
		{
			cout<<"\n Элемент "<<val<<" найден в списке\n";
			return;
		}
			
	}
	cout<<" Элемента "<<val<<" нет в списке\n";
}
void List::erase(int val)
{
	if(head==0)
	{
		cout<<"Список пуст\n";
	}
	Node* ptr=head;
	Node* ptrPrev;
	ptrPrev=new Node;
	for(ptr;ptr!=0;ptr=ptr->next)
	{
		if(ptr->elem==val)
		{
			ptrPrev->next=ptr->next;
			delete ptr;
			break;
		}
		ptrPrev=ptr;
	}
	if(ptrPrev->next==0)
	cout<<"Элемент не найден\n";
}
void List::erase()
{
	if(head==0)
	{
		cout<<"Список пуст\n";
	}
	Node *delptr=head;
	head=head->next;
	delete delptr;
}
void List::deleteall()
{
	while(head!=NULL)
		erase();
}
int main()
{
	setlocale(LC_ALL, "Russian");
	List Lst;
	Lst.push_back(5);
	Lst.push_back(10);
	Lst.push_back(15);
	Lst.push_front(1);
	Lst.push_front(25);
	Lst.print();
	
	int l;
	cout<<"\nВведите элемент списка";
	cin>>l;
	Lst.insert(l);
	Lst.print();

	cout<<"\nВведите элемент списка для поиска: ";
	cin>>l;
	Lst.find(l);

	cout<<"\nВведите елемент списка для удаления: ";
	cin>>l;
	Lst.erase(l);
	Lst.print();

	Lst.deleteall();
	Lst.print();


	
	
//	_getch();
	return 0;
}
Dumbl-D вне форума Ответить с цитированием
Старый 16.11.2014, 22:38   #2
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Не работает - это:
а) Не компилируется и что-то конкретное пишет.
б) Компилируется, но при запуске падает с какой-нибудь ошибкой.
в) Работает, но выдаёт неверный результат.
какой правильный вариант?
pu4koff вне форума Ответить с цитированием
Старый 16.11.2014, 22:48   #3
Dumbl-D
Пользователь
 
Регистрация: 25.11.2013
Сообщений: 26
По умолчанию

Цитата:
Сообщение от pu4koff Посмотреть сообщение
Не работает - это:
а) Не компилируется и что-то конкретное пишет.
А конкретно:
error LNK2019: ссылка на неразрешенный внешний символ "public: virtual __thiscall Container::~Container(void)" (??1Container@@UAE@XZ) в функции "public: virtual __thiscall List::~List(void)" (??1List@@UAE@XZ)
и
error LNK1120: 2 неразрешенных внешних элементов
Dumbl-D вне форума Ответить с цитированием
Старый 16.11.2014, 22:56   #4
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Деструктор контейнера не реализовали. Добавьте:
Код:
	virtual ~Container() {};
pu4koff вне форума Ответить с цитированием
Старый 16.11.2014, 23:05   #5
Dumbl-D
Пользователь
 
Регистрация: 25.11.2013
Сообщений: 26
По умолчанию

Ага, спасибо большое, еще был конструктор не реализован в контейнере. Исправила - работает. Просто особых изменения не вижу в работе проги, по сравнению с тем, когда класс Список был единственных и не состоял в отношении наследования.
А можете, пожалуйста, пробежаться взглядом по тексту проги? А то я не уверенна в ее правильности. Можно ли вообще обнулять все функции в классе контейнер(как у меня)? Или это бессмыслица?)
Dumbl-D вне форума Ответить с цитированием
Старый 17.11.2014, 08:09   #6
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Цитата:
Сообщение от Dumbl-D Посмотреть сообщение
Ага, спасибо большое, еще был конструктор не реализован в контейнере. Исправила - работает. Просто особых изменения не вижу в работе проги, по сравнению с тем, когда класс Список был единственных и не состоял в отношении наследования.
А изменений и не будет в плане работы. Это нужно для удобства разработки. Представьте, что есть не только односвязный список, но и двусвязный, массив и другие структуры данных. Все они наследуются от этого абстрактного класса Контейнер. Значит с ними всеми можно работать как с объектами класса Контейнер. Значит можно писать функции и методы, которые будут работать не с конкретным типом контейнера, а с любым из них. Не очень занимательно ведь будет писать кучу вариантов функции получения списка студентов для каждого из типов контейнера, а так написали для абстрактного класса и можно передавать в функцию любой из типов.
Цитата:
Сообщение от Dumbl-D Посмотреть сообщение
А можете, пожалуйста, пробежаться взглядом по тексту проги?
Указателям кошернее присваивать значение NULL, но по сути никакой разницы с нулём обычно нет.
В деструкторе списка утечка памяти будет (удаляется только 2 элемента - голова и хвост), лучше просто в нём вызывать deleteall. Про tail везде забыто, так что с ним тоже полезут ошибки (в erase можем удалить как голову, так и хвост, а этот момент не контролируется).
Цитата:
Сообщение от Dumbl-D Посмотреть сообщение
Можно ли вообще обнулять все функции в классе контейнер(как у меня)? Или это бессмыслица?)
Это обнуление именно что показывает абстрактность методов, т.е. что для них нет и не будет реализации. Так что оно не только можно, но и нужно. И да, абстрактный класс не обязательно должен состоять только из абстрактных методов. Достаточно одного абстрактного метода, чтобы класс из "обычного" стал абстрактным. Абстрактный он потому, что нельзя создавать экземпляры этого класса.
pu4koff вне форума Ответить с цитированием
Старый 17.11.2014, 18:28   #7
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,577
По умолчанию

Цитата:
Указателям кошернее присваивать значение NULL, но по сути никакой разницы с нулём обычно нет.
nullptr есть.

Опечатка?:
Цитата:
Код:
class Container { ... }
class List: public CContainer { ... }
Цитата:
Код:
~List(){ if(head!=NULL) {delete head;delete tail;}
}
Создай список из одного элемента. Потом удали список. Я думаю программа сразу упадет с ошибкой. ты дважды вызовешь деструктор у одного и того же узла.
Ну и да, как выше писали - если элементв больше двух - утекает память.
Ты описала деструктор при объявлении класса, я не думаю что это хорошая идея. Ну т.к. такие функции претендуют на то, чтобы быть inline, я не знаю как сработает каждый конкретный компилятор и что в стандарте написано про inline деструктор, но идея явно плохая.

СЕРЬЕЗНЕЙ:

Цитата:
Код:
void List::erase(int val)
{
	if(head==0)
	{
		cout<<"Список пуст\n";
	}
	Node* ptr=head;
	Node* ptrPrev;
	ptrPrev=new Node; // УТЕЧКА ПАМЯТИ
Ну и про тотальный косяк в erase(int) тут тоже уже написали.Прикинь, если ты случайно удалишь там последний элемент? - указатель tail ты не передвигаешь, как и head (если удалишь первый)


Тут прикинь, что ты удаляешь последний элемент (456) из списка [123->456->null]
ptr указывает на 456
prevPtr - на 123
Цитата:
Код:
for(ptr;ptr!=0;ptr=ptr->next)
	{
		if(ptr->elem==val)
		{
			ptrPrev->next=ptr->next; // prevPtr = 0
			delete ptr; // удалил 456
			break;
		}
		ptrPrev=ptr;
	}
	if(ptrPrev->next==0) // null->next - упадет с ошибкой 100%
        cout<<"Элемент не найден\n";
Тут могут быть и другие варианты, вроде бы, когда этот код сломается (он вообще странный какой-то)


-----------------добавил:
ЗЫ: я в код не вчитывался, тут явно гораздо больше ошибок, чем я описал - написал лишь то, что сразу бросилось в глаза.

Последний раз редактировалось rrrFer; 17.11.2014 в 18:31.
rrrFer вне форума Ответить с цитированием
Ответ


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



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Абстрактный класс Сырги C# (си шарп) 3 22.11.2012 01:13
абстрактный класс meta13 C# (си шарп) 2 22.03.2012 19:14
c# абстрактный класс tanek Помощь студентам 1 22.02.2012 11:23
Указатель на абстрактный класс zhenya.ya Общие вопросы C/C++ 0 11.11.2010 23:51
абстрактный класс С++ zhenya.ya Помощь студентам 0 05.11.2010 20:23