Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailem C++ OOP - implementace stacku pomocí pointerů

Tak jsem začal s OOP. Ze školy jsem to (jako obvykle :D) moc nepobral, tak to zkouším tady. Moje idea byla, udělat stack defacto jako seznam (bez pole mě nenapadá, jak jinak se "vrátit" k prvkům pod topem), ale mám problém - v metodě Push nemůžu přijít na to, jak předat adresu předchozího prvku tomu novému jako prev (objekt item se vytvoří vždy nově a mimo fci to s ním nějak odmítá manipulovat :D).

P.s.: Jaký je rozdíl mezi stack *identifikator a stack *identifikator = new stack (stack je název třídy)?

Tady je kód:

#include <iostream>

using namespace std;

class stack{
private:
	int num; //hodnota prvku
	stack *next; //dalsi prvek
	stack *prev; //predchozi prvek - nenapada me, jak to lip resit, tady pouzivam pro prirazeni na vrchol, pri odebirani prvku Popem
public:
	stack();
	void Push(int push_num, stack *top); //fce prebira jako parametr cislo, ktere se ma vlozit a nejvyssi prvek
	int Pop(stack *top); //fce vraci prvek z vrcholu zasobniku a zaroven ho odebira
	int Top(stack *top);
	bool IsEmpty(stack *top);
	~stack();
};

/**
	@brief Konstruktor - inicializuje num a next na NULL
*/
stack::stack(){
	num = NULL;
	next = NULL;
	prev = NULL;
}

/**
	@brief Push - funkce vlozi novy prvek na vrchol zasobniku
	@param1 hodnota vkladaneho cisla
	@param2 vrchol zasobniku
*/
void stack::Push(int push_num, stack *top){	
	stack *item = new stack; //vytvorim novy objekt tridy stack

	item->num = push_num;

	if(top->num == NULL){
		*top = *item;
		top->next = NULL;
		item->prev = top;
	}else{
		top->next = item;
		*top = *top->next;
		top->next = NULL;
	}
}

int stack::Pop(stack *top){

	if(top->num == NULL){
		cout << "V zasobniku nejsou zadne prvky!" << endl;
		return NULL;
	}else{
		int ret_num; //vracene cislo
		ret_num = top->num;
		if(top->prev == NULL){
			top->num = 999;
		}else{
			*top = *top->prev;
		}
		return ret_num;
	}	
}

int stack::Top(stack *top){
	return top->num;
}

bool stack::IsEmpty(stack *top){
	if(top->num == NULL){
		return true;
	}
	return false;
}

stack::~stack(){
	cout << "Ahoj :-)" << endl;
}

int main(){
	stack zasobnik; //vytvorim objekt

	zasobnik.Push(5, &zasobnik);
	zasobnik.Push(6, &zasobnik);
	cout << zasobnik.Pop(&zasobnik) << endl;
	cout << zasobnik.Top(&zasobnik) << endl;
	cout << zasobnik.IsEmpty(&zasobnik) << endl;


	return 0;
}

Jinak moc díky za rady, netuším, co bych bez tohohle fóra dělal :D

Předmět Autor Datum
Necital som zatial ten program (sa mi nechce :D) ale mam pocit ze nechapes co to je stack. Principia…
MM.. 12.04.2013 16:33
MM..
Takze ak to fakt chces spojkami tak napr. takto: Staci ti drzat si jedinu privatnu premennu StackPoi…
MM.. 12.04.2013 16:39
MM..
A este k objektovemu modelu, ptz mas v tom asi gulas. Objekt zasobnik v OOP by mal byt objekt ktory…
MM.. 12.04.2013 16:48
MM..
Jaký je rozdíl mezi stack *identifikator a stack *identifikator = new stack to prve nevytvori objek…
MM.. 12.04.2013 17:57
MM..
Strašně moc díky, už mi to funguje a konečně rozumím, jak to vlastně celé funguje :D Mám jen zbývají…
Katsushiro 12.04.2013 19:58
Katsushiro
No, v záchvatu programátorského entusiasmu jsem se rozhodl naprogramovat ještě rozhraní pro uživatel… poslední
Katsushiro 13.04.2013 12:31
Katsushiro

Necital som zatial ten program (sa mi nechce :D) ale mam pocit ze nechapes co to je stack.
Principialne stack nema ziadne spojky, je to len suvisla oblast pamate, a pamatas si len jednu adresu (StackPointer) ktora je top of stack (volne miesto pre dalsiu polozku). Pridanie prvku: *Stackpointer = nova polozka; Stackpointer++; a odobratie prvku: StackPointer--; polozka = *Stackpointer;
Mozes si pamatat aj zakladnu adresu stacku (adresu prveho prvku) aby si vedel vratit error ked sa niekto pokusi vybrat polozku zo stacku a Stackpointer==zakladna adresa stacku tak hlasis error (stack je prazdny).

Ak tam robis spojky medzi prvkami tak to je skor spojkovy zoznam, a ne stack. Ale da sa to robit aj so spojkami len by som to potom uz ja osobne nenazyval stack. (jak co nazyva vas ucitel netusim :D)

T.j. Chces to fakt robit so spojkami? Ak ano tak to budu muset byt spojky smerom nazad, t.j. nova polozka musi mat pointer na predchadzajucu, aby si vedel vyberat polozky zo stacku ptz vtedy potrebujes nastavovat stackpointer na predch. polozku.

Takze ak to fakt chces spojkami tak napr. takto:
Staci ti drzat si jedinu privatnu premennu StackPointer, nepotrebujes nic viac.
Na zaciatku StackPointer=NULL, t.j. stack je prazdny.

Potom pri Push vytvaras novu strukturu alebo objekt ktory bude obsahovat data a pointer na predch.polozku.
V tom pripade Push bude vypadat asi takto (schematicky, spravnu syntax si urob sam):
new(novyobjekt);
novyobjekt->data = data;
novyobjekt->previous = StackPointer;
StackPointer = novyobjekt;

a Pop bude vypadat asi takto:
if(StackPointer == NULL) error;
objekt_na_deletovanie = StackPointer;
navratove_data = StackPointer->data;
StackPointer = StackPointer->previous;
delete(objekt_na_deletovanie);
return(navratove_data);

Je to jasne ci nie? :-)

A este k objektovemu modelu, ptz mas v tom asi gulas.

Objekt zasobnik v OOP by mal byt objekt ktory ma vsetko co potrebuje v sebe a navonok nepredavas ziadne pointre !!! Pointer na top of stack by mal byt ako private a pristzupny vyhradne z vnutra triedy, nie zvonku.
T.j. ak robis class stack tak tam bude
private:
nejakastruktura *TopOfStack;
a nic viac (samozrejme public funkcie tam budu)
nejakastruktura bude struct {data, predch_pointer}

V konstruktore urobis TopOfStack=NULL.
V destruktore si otestujes ci TopOfStack je NULL, ak nie tak mozes robit Pop az pokial TopOfStack bude NULL, kvoli uvolneniu pamate.
Funkcia Push bude mat ako parameter hodnotu (data), nic viac. Funkcia Pop nebude mat ziaden parameter len navr. hodnotu.

To je spravny OOP model stacku, ty to robis uplne naopak, nezmyselne (dovolujes ludom mimo tvoj objekt ovplyvnit tvoj top of stack, co je ale prave to comu ma OOP zabranit, ako "ochrana" pred chybami programatorov ktory budu stack pouzivat).

Jaký je rozdíl mezi stack *identifikator a stack *identifikator = new stack

to prve nevytvori objekt, ale len pointer, ktory by mohol obsahovat adresu na nejaky objekt danej triedy, ak nejaku adresu do neho priradis samozrejme.
To druhe vytvori aj objekt danej triedy a adresu toho objektu ti priradi do toho pointra.

Dufam ze chapes ze je mozne vytvorit aj viac objektov stejnej triedy. T.j. mozem urobit
class automobil;
automobil a1, a2, a3; // vytvorim 3 nezavisle objekty triedy auto.
a1.farba = cervena;
a2.farba = zelena;
apod

Samotny class nerezervuje ziadnu pamat pre data ani nic, class je len sablona pre vytvorenie objektu. Objekt sa vytvori az ked vytvoris nejaky objekt napr. automobil a1; vtedy sa rezervuje pamat pre jeden objekt (vsetky interne premenne tej triedy atd) a mozem k nim pristupovat napr. k premennej farba pomocou a1.farba = neco.

P.S. vasmu ucitelovi povedz ze je ucitel uplne k hovnu :D

Strašně moc díky, už mi to funguje a konečně rozumím, jak to vlastně celé funguje :D Mám jen zbývající 2 dotazy:
1. Ten ukazatel++ a ukazatel-- se dá použít, jen když implementuju stack pomocí pole a příčítám/odečítám celé číslo - index, ne?

2. Co má vlastně udělat ten destruktor? Vyhodit hlášení, když sáhnu Popem do prázdného zásobníku?

A jinak, učiteli bych to mnohdy rád řekl, ale obávám se o zápočet, tak si to asi nechám až po studiu :D

No, v záchvatu programátorského entusiasmu jsem se rozhodl naprogramovat ještě rozhraní pro uživatele, aby se stack dal ovládat jen přes konzoli. Mám ale další problémy :D

1. Háže mi error IsEmpty, a vůbec nevím proč, ověřuju prostě, když top->data == NULL, ale program přestane pracovat, jako ybch tam měl nějakou mekonečnou smyčku či co :D

2. Jak se dá ošetřit situace, že potřebuji chybové hlášení třeba z funkce Top, Pop nebo Num_from_Push, ale tyto můžou vracet jen int? Řeším to tak, že vracím 909090 a doufám, že to nikdo nezadá, ale určitě to jde řešit lépe...

A takhle vypadá můj současný kód :D

#include <iostream>
#include <cstdlib> //kvuli EXIT_FAILURE
#include <string> //kvuli ovladani pro uzivatele
#include <cmath> //kvuli mocneni pri vypoctu cisla

using namespace std;

struct item{ //struktura - typ prvku v zasobniku
	int data;
	item *prev;
};

class stack{ //trida - zasobnik
private:
	item *top; //vrchol zasobniku
public:
	stack();
	void Push(int data);
	int Pop();
	int Top();
	bool IsEmpty();
	~stack();
};

stack::stack(){
	top = NULL;
}

void stack::Push(int data){
	item *node = new item; //prvek zasobniku

	node->data = data;
	node->prev = top;
	top = node;
}

int stack::Pop(){
	if(top == NULL){
		/*cout << "Neni co odebrat!" << endl;
		exit(EXIT_FAILURE);*/
		throw "Neni co odebrat!";
	}else{

	item *del = new item;
	del = top;

	int ret = top->data; //pro return
	top = top->prev;

	delete del;
	
	return ret;
	}
}

int stack::Top(){
	if(!IsEmpty()){
		return top->data;
	}else{
		return 909090;
	}
}

bool stack::IsEmpty(){
	if(top->data == NULL){
		return false;
	}
	return true;
}

stack::~stack(){

}

void ToLower(string &com){
	int last = com.length() - 1;  

	for(int i = 0; i <= last; i++){
		com[i] = tolower(com[i]);
	}
}

int Num_from_Push(string com){
	int last = com.length() - 1;

	if(!isdigit(com[last])){
		return 909090;
	}
	
	int i = 0;
	int cislo = 0;

	while(com[last] != ' ' && com[last] != 'h'){
		cislo += ((com[last] - 48) * pow(10.0, i));
		i++;
		last--;
	}
	return cislo;
}

void Control(stack object){
	cout << "Zadavejte prikazy pro praci se zasobnikem: Push 'cislo', Pop, Top, IsEmpty. Pro ukonceni programu pouzijte prikaz End." << endl;
	string com; //prikaz

	do{
		getline(cin, com);
		ToLower(com); //prevedu prikaz na mala pismena kvuli preklepum

		if(com == "end"){
			exit(EXIT_SUCCESS);
		}else if(com[0] == 'p' && com[1] == 'u' && com[2] == 's' && com[3] == 'h'){
			if(Num_from_Push(com) == 909090){
				cout << "Spatne zadana hodnota pro vlozeni!" << endl;
			}else{
				object.Push(Num_from_Push(com));
			}
		}else if(com == "pop"){
			cout << object.Pop() << endl;
		}else if(com == "top"){
			if(object.Top() == 909090){
				cout << "Zasobnik je prazdny!" << endl;
			}else{
				cout << object.Top() << endl;
			}
		}else if(com == "isempty"){
			if(object.IsEmpty()){
				cout << "Zasobnik je prazdny." << endl;
			}else{
				cout << "Zasobnik neni prazdny." << endl;
			}
		}else{
			cout << "Spatny prikaz!!! Mozne prikazy jsou Push, Pop, Top, IsEmpty nebo End." << endl;
		}
	}while(com != "end");
}

int main(){
	stack a; //objekt tridy stack

	//Control(a);

	//a.IsEmpty();

	a.Top();

	return 0;
}

Zpět do poradny Odpovědět na původní otázku Nahoru