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
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).
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