• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Kolejka jako szablon

Object Storage Arubacloud
0 głosów
515 wizyt
pytanie zadane 11 września 2017 w C i C++ przez B0nkers Początkujący (310 p.)
edycja 11 września 2017 przez B0nkers

Witam,

Mam problem z zadaniem z książki S. Prata. Zadanie dotyczy utworzenia szablonu QueueTP i przetestowania jego działania tworząc kolejkę wskaźników na klasę Worker. Kolejkę szablonową utworzyłem na podstawie zwykłej kolejki, więc nie wiem czy jest na 100% dobra. Z kolei klasa Worker jest prawidłowo zadeklarowana i zaimplementowana, bo przepisałem z książki. Przy kompilacji programu wyświetla się jeden warning o treści: użycie potencjalnie niezainicjowanej, lokalnej zmiennej wskaźnikowej pt.

W takim razie przypuszczam, że błąd jest w programie głównym, ale nie umiem go znaleźć. Poniżej wrzucam kod programu głównego:

#include "stdafx.h"
#include "QueueTP.h"
#include <cstring>
#include <new>
#include "workermi.h"

int main()
{
	using std::cin;
	using std::cout;
	using std::endl;
	using std::strchr;
	const int BUFFOR = 512;
	
	char * bufor = new char[BUFFOR];
	cout << "Podaj maksymalna dlugosc kolejki: ";
	int qs;
	char ch;
	cin >> qs;
	QueueTP<Worker *> line(qs);
	Worker * pt;
	
	cout << "Nacisnij D, aby wprowadzic strukture, \n"
		<< "P, aby przetworzyc strukture, lub K, aby zakonczyc.\n";
	cin.get();
	while (cin >> ch && toupper(ch) != 'K')
	{
		
		while (cin.get() != '\n')
			continue;
		if (!isalpha(ch))
		{
			cout << '\a';
			continue;
		}
		switch (ch)
		{
		case 'D':
		case 'd':
			if (line.isfull())
				cout << "Kolejka pelna!\n";
			
			char choice;
			cout << "Podaj kategorie pracownika:\n"
				<< "k: kelner  p: piosenkarz  "
				<< "s: spiewajacy kelner  w: wyjscie\n";
			cin >> choice;
			while (strchr("kpsw", choice) == NULL)
			{
				cout << "Wpisz k, p, s lub w: ";
				cin >> choice;
			}
			if (choice == 'w')
				break;
			switch(choice)
			{
			case 'k': pt = new(bufor) Waiter;
				break;
			case 'p': pt = new(bufor) Singer;
				break;
			case 's': pt = new(bufor) SingingWaiter;
				break;
			}
			cin.get();
			pt->Set();
			line.enqueue(pt);
			break;

		case 'P':
		case 'p':
			if (line.isempty())
				cout << "Kolejka pusta\n";
			else
			{
				cout << "Obiekt sciagniety z poczatku kolejki:\n";
				line.dequeue(pt);
				pt->Show();
				delete pt;
			}
			break;

		default:
			cout << "Wybrales zla opcje!\n";
			break;
		}
		cout << "\nNacisnij D, aby wprowadzic strukture, \n"
			<< "P, aby przetworzyc strukture, lub K, aby zakonczyc.\n";

	}
	delete [] bufor;
	


	return 0;
}

Plik nagłówkowy z abstrakcyjną klasą Worker i jej pochodnymi klasami, oraz implementacje w osobnym pliku:

#pragma once
#ifndef WORKERMI_H_
#define WORKERMI_H_
#include <iostream>
#include <string>

class Worker
{
private:
	std::string fullname;
	long id;
protected:
	virtual void Data() const;
	virtual void Get();
public:
	Worker()
		: fullname("brak"), id(0L) {}
	Worker(const std::string & s, long n)
		: fullname(s), id(n) {}
	virtual ~Worker() = 0; //funkcja czysto wirtualna
	virtual void Set() = 0;
	virtual void Show() const = 0;
};

class Waiter : virtual public Worker
{
private:
	int panache;
protected:
	virtual void Data() const;
	virtual void Get();
public:
	Waiter() 
		: Worker(), panache(0) {}
	Waiter(const std::string & s, long n, int p = 0)
		: Worker(s, n), panache(p) {}
	Waiter(const Worker & wk, int p = 0)
		: Worker(wk), panache(p) {}
	void Set();
	void Show() const;
};

class Singer : virtual public Worker
{
protected:
	enum {inna, alt, kontralt, sopran, bas, baryton, tenor};
	enum {Vtypes = 7};
	virtual void Data() const;
	virtual void Get();
private:
	static const char *pv[Vtypes];	//odpowiednik skali głosu w postaci ciągu znaków
	int voice;
public:
	Singer()
		: Worker(), voice(inna) {}
	Singer(const std::string & s, long n, int v = inna)
		: Worker(s, n), voice(v) {}
	Singer(const Worker & wk, int v = inna)
		: Worker(wk), voice(v) {}
	void Set();
	void Show() const;
};

class SingingWaiter : public Singer, public Waiter
{
protected:
	virtual void Data() const;
	virtual void Get();
public:
	SingingWaiter() {}
	SingingWaiter(const std::string & s, long n, int p = 0, int v = inna)
		: Worker(s, n), Waiter(s, n, p), Singer(s, n, v) {}
	SingingWaiter(const Worker & wk, int p = 0, int v = inna)
		: Worker(wk), Waiter(wk, p), Singer(wk, v) {}
	SingingWaiter(const Worker & wt, int v = inna)
		: Worker(wt), Waiter(wt), Singer(wt, v) {}
	SingingWaiter(const Singer & wt, int p = 0)
		: Worker(wt), Waiter(wt, p), Singer(wt) {}
	void Set();
	void Show() const;
};

#endif
#include "stdafx.h"
#include "workermi.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;

//metody klasy Worker
Worker::~Worker() {}
//metody chronione
void Worker::Data() const
{
	cout << "Imie i nazwisko: " << fullname << endl;
	cout << "numer identyfikacyjny: " << id << endl;
}

void Worker::Get()
{
	getline(cin, fullname);
	cout << "Podaj numer identyfikacyjny: ";
	cin >> id;
	while (cin.get() != '\n')
		continue;
}

//metody klasy Waiter
void Waiter::Set()
{
	cout << "Podaj imie i nazwisko kelnera: ";
	Worker::Get();
	Get();
}

void Waiter::Show() const
{
	cout << "Kategoria: kelner\n";
	Worker::Data();
	Data();
}
//metody chronione
void Waiter::Data() const
{
	cout << "Elegancja: " << panache << endl;
}

void Waiter::Get()
{
	cout << "Podaj poziom elegancji kelner: ";
	cin >> panache;
	while (cin.get() != '\n')
		continue;
}

//metody klasy Singer

const char * Singer::pv[Singer::Vtypes] = { "inna", "alt", "kontralt", "sopran", "bas", "baryton", "tenor" };

void Singer::Set()
{
	cout << "Podaj imie i nazwisko piosenkarza: ";
	Worker::Get();
	Get();
}

void Singer::Show() const
{
	cout << "Kategoria: piosenkarz\n";
	Worker::Data();
	Data();
}
//metody chronione
void Singer::Data() const
{
	cout << "Skala glosu: " << pv[voice] << endl;
}

void Singer::Get()
{
	cout << "Podaj numer dla skali glosu piosenkarza:\n";
	int i;
	for (i = 0; i < Vtypes; i++)
	{
		cout << i << ": " << pv[i] << "   ";
		if (i % 4 == 3)
			cout << endl;
	}
	if (i % 4 != 0)
		cout << "\n";
	cin >> voice;
	while (cin.get() != '\n')
		continue;
}

//metody klasy SingingWaiter
void SingingWaiter::Get()
{
	Waiter::Get();
	Singer::Get();
}

void SingingWaiter::Data() const
{
	Singer::Data();
	Waiter::Data();
}

void SingingWaiter::Set()
{
	cout << "Podaj imie i nazwisko spiewajacego kelnera: ";
	Worker::Get();
	Get();
}

void SingingWaiter::Show() const
{
	cout << "Kategoria: spiewajacy kelner\n";
	Worker::Data();
	Data();
}

oraz klasę szablonową kolejki QueueTP z implementacją w osobnym pliku:

#ifndef QUEUETP_H_
#define QUEUETP_H_
#include <string>
#include <iostream>


template <typename Item>
class QueueTP 
{
protected:
	//definicje zasiegu klasy
	//Node to definicja zagnieżdżona klasy, lokalna względem tej klasy
	struct Node { Item item; struct Node * next; };
	enum { Q_SIZE = 10 };
	//prywatne składowe klasy
	Node * front;		//wskaźnik czoła kolejki
	Node * rear;		//wskaźnik ogona kolejki
	int items;			//bieżąca liczba elementów
	const int qsize;	//maksymalna liczba elementów kolejki
						//definicje blokujące publiczny dostęp do operacji kopiowania
	QueueTP(const QueueTP & q) : qsize(0) {}
	QueueTP & operator=(const QueueTP & q) { return *this; }
public:

	QueueTP(int qs);			//tworzy kolejkę o pojemności qs
	~QueueTP();
	bool isempty() const;
	bool isfull() const;
	int	 queuecount() const;
	bool enqueue(const Item &item);	//dodaje element na koniec kolejki
	bool dequeue(Item & item);		//wyciąga element z czoła kolejki
	bool operator>(const QueueTP & l)
	{
		if (this->items > l.items)
			return true;

		return false;
	}
};


#endif
#include "stdafx.h"
#include "QueueTP.h"
#include <cstdlib>

//metody klasy Queue
template <typename Item>
QueueTP<Item>::QueueTP(int qs) : qsize(qs)
{
	front = rear = nullptr;
	items = 0;
}
template <typename Item>
QueueTP<Item>::~QueueTP()
{
	Node * temp;
	while (front != NULL)	//do wyczerpania kolejki
	{
		temp = front;		//zachowanie adresu elementu bieżącego
		front = front->next;	//przesunięcie wskaźnika do elementu następnego
		delete temp;		//zwolnienie elementu spod zapamiętanego adresu
	}
}
template <typename Item>
bool QueueTP<Item>::isempty() const
{
	return items == 0;
}
template <typename Item>
bool QueueTP<Item>::isfull() const
{
	return items == qsize;
}
template <typename Item>
int QueueTP<Item>::queuecount() const
{
	return items;
}

//dodaje element do kolejki
template <typename Item>
bool QueueTP<Item>::enqueue(const Item &item)
{
	if (isfull())
		return false;
	Node * add = new Node;		//utworzenie węzła
								//w przypadku niepowodzenia przydziału new zrzudzi wyjątek na std::bad_alloc
	add->item = item;			//ustawienie wskaźników węzłów
	add->next = nullptr;
	items++;
	if (front == NULL)			//jeśli kolejka pusta
		front = add;			//umieść element na czele listy
	else
		rear->next = add;		//w przeciwnym wypadku dołącz do końca
	rear = add;					//rear wskazuje teraz nowy węzeł

	return true;
}

//kopiuje element czołowy kolejki do argumentu wywołania i usuwa go z kolejki
template <typename Item>
bool QueueTP<Item>::dequeue(Item & item)
{
	if (front == NULL)
		return false;
	item = front->item;			//skopiowanie do item pierwszego elementu
	items--;
	Node * temp = front;		//zachowanie położenia pierwszego elementu
	front = front->next;		//przestawienie wskaźnika front na następny element
	delete temp;				//usunięcie dotychczasowego pierwszego elementu
	if (items == 0)
		rear = NULL;
	return true;
}

Proszę o pomoc.

 

komentarz 11 września 2017 przez unknown Nałogowiec (39,560 p.)

Przy kompilacji programu wyświetla się komunikat o jednym błędzie o treści: użycie potencjalnie niezainicjowanej, lokalnej zmiennej wskaźnikowej pt.

 Raczej warning 

komentarz 11 września 2017 przez B0nkers Początkujący (310 p.)

No tak sory, przejęzyczenie smiley

1 odpowiedź

+1 głos
odpowiedź 11 września 2017 przez criss Mędrzec (172,590 p.)
edycja 11 września 2017 przez criss

Nie napisałeś jakiej linii warning dotyczy. Domyślam się jednak, że chodzi o destruktor kolejki. IMO kod jest jednak bezpieczny, więc nie powinieneś się przejmować warningiem. Dobry styl jednak nakazuje coś przypisać do każdej zmiennej typu podstawowego, więc możesz mu przypisać nullptr - będzie schludniej i warning zniknie.

Kilka uwag:

  • Osobny bloczki kodu na definicje template-a i jej metod podpowiadają mi, że to są 2 różne pliki, tak? W przypadku szablonów tak nie można - wszystko musi być znane w czasie kompilacji - poczytaj o procesie kompilacji c++. U ciebie najwyraźniej jakoś to działa - słyszałem, że niektóre kompilatory są w stanie sobie z tym poradzić, ale myślę że warto napisać.
  • Dostęp protected w QueueTP sugeruje, że zamierzasz z tego dziedziczyć. Szczerze mówiąc nie widze powodu na dziedziczenie z kontenera, ale jeśli jednak, to destruktor powinien być writualny.
  • W QueueTP masz niestatyczne const pole,co z oczywistego powodu blokuje automatyczne utworzenie operatora przypisania. Ty jednak zdefiniowałeś własny operator przypisania, który nie wykonuje swojej funkcjonalności - po prostu nie robi nic. Bardzo mylące - zdecydowanie nie powinieneś tak robić. W chwili obecnej możliwa jest sytuacja, że będziesz chciał przypisać jeden obiekt kolejki do drugiego, kompilator ci na to pozwoli, ale nic takiego się nie stanie (obiekt po lewej stronie przypisania pozostaje nienaruszony).
    Druga sprawa to, że nie widze sensu istnienia pola qsize skoro utworzyłeś już taką stała posiłkując się enumem. (edit) Ok, widzę teraz, że wartość qsize można dowolnie nadać przez konstruktor. W takim razie nie widzę sensu dla tego enuma :P I ofc wyrzuć ten operator przypisania.
komentarz 11 września 2017 przez B0nkers Początkujący (310 p.)

Dzięki za pomoc, twoje uwagi podziałały.

  • Co do implementacji kolejki to przeniosłem ją do pliku nagłówkowego, bo jednak bez tego nie chciało poprawnie działać, usunąłem enuma.
  • Dodałem nullptr przy wskaźniku.
  • Usunąłem kontener i wstawiłem zwykłą wersję operatora new (jakbyś mógł sprawdzić jeszcze czy równie dobrze wstawiłem delete, bo z tym ma czasami problemy)
  •  operator przypisania na celu miał być nic nie robiący, bo tak było w książce że jak się nie chce zdefiniować operatora to trzeba zdefiniować w takim razie nic nie robiący, wiem że wtedy przy próbie przypisania nie prowadzi  to do błędów i program się skompiluje, no ale masz rację, lepiej jednak to usunąć 

Ponownie wrzucam kod:

#include "stdafx.h"
#include "QueueTP.h"
#include <cstring>
#include "workermi.h"

int main()
{
	using std::cin;
	using std::cout;
	using std::endl;
	using std::strchr;


	cout << "Podaj maksymalna dlugosc kolejki: ";
	int qs;
	char ch;
	cin >> qs;
	QueueTP<Worker *> line(qs);
	Worker * pt = nullptr;
	
	cout << "Nacisnij D, aby wprowadzic strukture, \n"
		<< "P, aby przetworzyc strukture, lub K, aby zakonczyc.\n";
	cin.get();
	while (cin >> ch && toupper(ch) != 'K')
	{
		
		while (cin.get() != '\n')
			continue;
		if (!isalpha(ch))
		{
			cout << '\a';
			continue;
		}
		switch (ch)
		{
		case 'D':
		case 'd':
			if (line.isfull())
				cout << "Kolejka pelna!\n";
			else 
			{
				char choice;
				cout << "Podaj kategorie pracownika:\n"
					<< "k: kelner  p: piosenkarz  "
					<< "s: spiewajacy kelner  w: wyjscie\n";
				cin >> choice;
				while (strchr("kpsw", choice) == NULL)
				{
					cout << "Wpisz k, p, s lub w: ";
					cin >> choice;
				}
				if (choice == 'w')
					break;
				switch (choice)
				{
				case 'k': pt = new Waiter;
					break;
				case 'p': pt = new Singer;
					break;
				case 's': pt = new SingingWaiter;
					break;
				}
				cin.get();
				pt->Set();
				line.enqueue(pt);
			}
			break;

		case 'P':
		case 'p':
			if (line.isempty())
				cout << "Kolejka pusta\n";
			else
			{
				cout << "Obiekt sciagniety z poczatku kolejki:\n";
				line.dequeue(pt);
				pt->Show();
				delete pt;
			}
			break;

		default:
			cout << "Wybrales zla opcje!\n";
			break;
		}
		cout << "\nNacisnij D, aby wprowadzic strukture, \n"
			<< "P, aby przetworzyc strukture, lub K, aby zakonczyc.\n";

	}
	delete pt;

	return 0;
}

#ifndef QUEUETP_H_
#define QUEUETP_H_
#include <string>
#include <iostream>
#include "stdafx.h"
#include <cstdlib>


template <typename Item>
class QueueTP 
{
protected:
	//definicje zasiegu klasy
	//Node to definicja zagnieżdżona klasy, lokalna względem tej klasy
	struct Node { Item item; struct Node * next; };
	//prywatne składowe klasy
	Node * front;		//wskaźnik czoła kolejki
	Node * rear;		//wskaźnik ogona kolejki
	int items;			//bieżąca liczba elementów
	const int qsize;	//maksymalna liczba elementów kolejki
						//definicje blokujące publiczny dostęp do operacji kopiowania
	//QueueTP(const QueueTP & q) : qsize(0) {}
	//QueueTP & operator=(const QueueTP & q) { return *this; }
public:

	QueueTP(int qs);			//tworzy kolejkę o pojemności qs
	virtual ~QueueTP();
	bool isempty() const;
	bool isfull() const;
	int	 queuecount() const;
	bool enqueue(const Item &item);	//dodaje element na koniec kolejki
	bool dequeue(Item & item);		//wyciąga element z czoła kolejki
	bool operator>(const QueueTP & l)
	{
		if (this->items > l.items)
			return true;

		return false;
	}
};



//metody klasy Queue
template <typename Item>
QueueTP<Item>::QueueTP(int qs) : qsize(qs)
{
	front = rear = nullptr;
	items = 0;
}
template <typename Item>
QueueTP<Item>::~QueueTP()
{
	Node * temp;
	while (front != NULL)	//do wyczerpania kolejki
	{
		temp = front;		//zachowanie adresu elementu bieżącego
		front = front->next;	//przesunięcie wskaźnika do elementu następnego
		delete temp;		//zwolnienie elementu spod zapamiętanego adresu
	}
}
template <typename Item>
bool QueueTP<Item>::isempty() const
{
	return items == 0;
}
template <typename Item>
bool QueueTP<Item>::isfull() const
{
	return items == qsize;
}
template <typename Item>
int QueueTP<Item>::queuecount() const
{
	return items;
}

//dodaje element do kolejki
template <typename Item>
bool QueueTP<Item>::enqueue(const Item &item)
{
	if (isfull())
		return false;
	Node * add = new Node;		//utworzenie węzła
								//w przypadku niepowodzenia przydziału new zrzudzi wyjątek na std::bad_alloc
	add->item = item;			//ustawienie wskaźników węzłów
	add->next = nullptr;
	items++;
	if (front == NULL)			//jeśli kolejka pusta
		front = add;			//umieść element na czele listy
	else
		rear->next = add;		//w przeciwnym wypadku dołącz do końca
	rear = add;					//rear wskazuje teraz nowy węzeł

	return true;
}

//kopiuje element czołowy kolejki do argumentu wywołania i usuwa go z kolejki
template <typename Item>
bool QueueTP<Item>::dequeue(Item & item)
{
	if (front == NULL)
		return false;
	item = front->item;			//skopiowanie do item pierwszego elementu
	items--;
	Node * temp = front;		//zachowanie położenia pierwszego elementu
	front = front->next;		//przestawienie wskaźnika front na następny element
	delete temp;				//usunięcie dotychczasowego pierwszego elementu
	if (items == 0)
		rear = NULL;
	return true;
}

#endif

 

komentarz 11 września 2017 przez criss Mędrzec (172,590 p.)

Dopiero teraz widze metode dequeue. Dość dziwny pomysł, ale ok (argument do którego kopiujemy usuwany element). Wydaje mi się, że kod jest bezpieczny.

Usunąłem kontener i wstawiłem zwykłą wersję operatora new (jakbyś mógł sprawdzić jeszcze czy równie dobrze wstawiłem delete, bo z tym ma czasami problemy)

A tego nawet nie zauważyłem. Właściwie, to w ogóle nie patrzyłem na maina. Tak, wygląda, że wszystko ok. 

operator przypisania na celu miał być nic nie robiący, bo tak było w książce że jak się nie chce zdefiniować operatora to trzeba zdefiniować w takim razie nic nie robiący

O_O nie czytałbym bym książki uczącej czegoś takiego. 

komentarz 11 września 2017 przez B0nkers Początkujący (310 p.)

No właśnie laugh. To tak chyba było napisane dla celów demonstracyjnych, a ja to przepisałem i nie zauważyłem, że ten operator przypisania jest taki debilny. Teraz będę musiał więcej uważać. Jeszcze raz dzięki za dobre rady.

komentarz 11 września 2017 przez mokrowski Mędrzec (155,460 p.)
Przeczytać koniecznie co do (nie do końca) ścisłego przekonania że szablonów nie można wydzielić do pliku implementacji i nagłówka: https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl . Także FAQ o 2 niżej..  Zresztą polecam całość  tej strony :-)
komentarz 11 września 2017 przez B0nkers Początkujący (310 p.)
ok, dzięki

Podobne pytania

0 głosów
2 odpowiedzi 753 wizyt
pytanie zadane 29 kwietnia 2017 w C i C++ przez Łukasz Świtaj Użytkownik (520 p.)
0 głosów
1 odpowiedź 168 wizyt
pytanie zadane 21 maja 2023 w C i C++ przez Dani Obywatel (1,450 p.)
0 głosów
0 odpowiedzi 237 wizyt
pytanie zadane 25 listopada 2019 w Algorytmy przez Oskardes Użytkownik (600 p.)

92,539 zapytań

141,382 odpowiedzi

319,477 komentarzy

61,928 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...