Wypełnienie dynamicznej tablicy wskaźników

0

Bry,

Zabrałem się za pewien projekt mający na celu ugruntowanie paradygmatów programowania obiektowego, padło na temat jakim jest "symulator supermarketu".

Mamy klasę podstawową Produkt, oraz kilka klas pochodnych (np ProduktySpozywcze czy RTV) które będą dziedziczyły po po klasie podstawowej Produkt.
Jak to w markecie bywa, wyszukujemy interesujące nas produkty i wrzucamy je do koszyka.
Rozplanowałem to sobie tak, że klasa koszyk będzie zawierała dynamiczną tablicę wskaźników na obiekty typu Produkt (pytanie 1) oraz metodę dodaj do koszyka. Problem w tym, że nie bardzo wiem jak tę tablicę wypełnić. Chodzi o to by tablica zawierała wskaźniki na obiekty, które zostały dodane do koszyka. Wydaje mi się ze metoda dodaj do koszyka powinna jako argument przyjmować wskaźnik. (pytanie 2 i 3).

Pytanie 1: Czy w ten sposób będę mógł dodać do tablicy wskaźników wskaźniki na obiekty klas pochodnych?
Pytanie 2: Czy tak właśnie to powinno wyglądać? Jeśli tak to jakiego typu powinien być ten wskaźnik (argument)?
Pytanie 3: Jak optymalnie powinienem każdemu obiektowi stworzyć do niego wskaźnik, który będę w stanie przekazać do metody innej klasy?

1

Wskaźnik powinien być na klasę bazową czyli "Produkt" uwzględniając to, że klasy dziedziczące po klasie bazowej będą polimorficzne. Dzięki temu typ obiektu będzie określany w trakcie działania programu, mimo iż wskaźniki będą na typ Produkt ( "późne wiązanie" )

0

Rozumiem, dziękuję za odpowiedź. Nie wiem jeszcze jak w takim razie mam utworzyć wskaźnik do każdego istniejącego obiektu klasy Produkt, który w przypadku dodania produktu do koszyka będę przekazywał tej właśnie metodzie. Metoda dodaj do koszyka nie będzie przecież mogła skorzystać ze wskaźnika this który zawsze wskazuje na obiekt na rzecz którego wywołuje się metodę ponieważ wydaje mi się, że koszyk nie będzie w żaden sposób powiązany z klasą Produkt.

1

Jeśli nie ma powodów aby stosować wskaźnik (np. kazali stosować wskaźnik), to radził bym stosować kontener. W tym przypadku powinien wystarczyć zwykły vector. Tym bardziej że w swoim kodzie będziesz miał do rozwiązania następujące problemy:

  1. Dynamiczne alokowanie miejsca na nowy produkt
  2. Wyciągnięcie i włożenie sztuki produktu z/do koszyka tak aby nie był on dostępny w miejscach jednocześnie (ma być albo na półce, albo w koszyku)
  3. Dodawanie nowych produktów do rodzaju produktów

Pkt. 1 Załatwi Ci vector z metodą push_back() która automatycznie alokuje to miejsce.
Pkt. 2 Załatwi Ci wskaźnik inteligentny unique_ptr który uniemożliwia kopiowanie i wymaga przenoszenia, co w połączeniu z usunięciem konstruktora kopiującego w Produkt, uniemożliwi "rozmnożenie produktów" :-)
Pkt. 3 Załatwi Ci klasa w pełni abstrakcyjna Produkt w której będziesz mógł zobowiązać przez dziedziczenie konkretne produkty do implementowania określonych metod (jakieś getPrice(), getName() .. jeśli chcesz jeszcze na tym etapie jeszcze używać get/set). Co ważniejsze będzie to typ przechowywany w kontenerze i w ten sposób załatwisz sobie przechowanie dowolnego produktu (o ile dziedziczy z abstract Produkt)

Przy wskaźnikach z każdym z tych problemów będziesz się borykał. Oczywiście się da, ale po co kruszyć kopie. To nie jest kod niskopoziomowy w którym należy koniecznie użyć wskaźnika.

Przy upartym obstawaniu przy wskaźnikach (bo np. "kazali" albo nie umiem czegoś z pkt. 1-3), będą następujące problemy:
Ad 1. Ciągłe new/delete/malloc/free/realloc i "wachlowanie pamięcią" aż do wycieku lub błędu.
Ad 2. Pilnowanie aby wskaźnik wyzerować jeśli nie ma w nim produktu i wtedy należy "przeorganizować" tablicę ze wskaźnikami
Ad 3. Pamiętanie aby w Produkt (o ile nie będzie abstrakcyjny) stosować virtual i zaimplementować poprawne destruktory.

Tak więc wskaźnik czy kontener? :-) Bo zrozumiałem że funkcjonalność ale brniesz we wskaźnik :-)

0

Dziękuję za odpowiedź. Sam na etapie propozycji tematu projektu (jest to projekt podsumowujący na uczelnie) zaproponowałem zastosowanie vectora, jednak odradzono mi wykorzystanie go ze względu na "jego powolną pracę". Osobiście preferuję stosowanie "dobrych praktyk" nawet w małych projekcikach. Nie wiem w tym przypadku co począć...
Kontenera vector prawie w ogóle nie znam, ale jeżeli jest to rozwiązanie praktykowane również w profesjonalnych projektach to dobra okazja by się tego nauczyć.

Co byś mi radził? Ze wskaźnikami miałem trochę styczności, oczywiście nie operuje jeszcze nimi wybitnie (zadania akademickie nie pozwolą nabyć tej wprawy) podobnie jak i vectorem, którego praktycznie w ogóle nie znam.

0

Co do vector w (tym przypadku - projekt akademicki, niewiele elementów itd.), bardzo ciekawa opinia że jest mniej wydajny niż "bujanie się z dynamicznym alokowaniem", no ale nie ma co dyskutować z nieznanym mi autorytetem..
Powiedziałeś że nie znasz vector? To go zastosuj by poznać :-) To taki kontener w C++ typu "jak nie wiadomo co, to vector" :-) Jak będziesz miał inserty lub szybkie szukania, zmienisz na list lub map/unordered_map. Ale odradzam tablice i dynamiczne alokowanie ręcznie.
Wracając do vector vs tablica alokowana dynamicznie. Jak dodasz kilka elementów do vector, to dokonuje sam alokowania pamięci na zapas więc kosztowna alokacja nie jest wykonywana jak przy tablicy dynamicznej za każdym razem. Jak chcesz go zmniejszyć, ma sensowne metody, jak wiesz że w koszyku nie możesz modyfikować produktu to w wektor przechowasz stałą referencję. Ja widzę więcej zalet niż wad. Jeśli będziesz wiedział że średnio masz np. 10 elementów w vector, zastosujesz na nim reserve(). Najważniejsze abyś nie robił tego na tablicy bo stracisz czas.
Jak napisałem, moim zdaniem zrobisz na vector a jak będzie niewydajne, zmienisz kontener na inny (np. list)... To mit (nie kojarzyć z eM Aj Ti :-) ) że ręczne operowanie wskaźnikami jest szybsze niż zlecenie tego kontenerowi :-)

0

Rozumiem, zaproponuje ponownie w takim razie tę metodę. Na koniec takie pytanko, masz może jakiś fajny link/literaturę z przystępnie objaśnionym vectorem?

0

Poklikaj i przeczytaj. Tu masz także przykłady..
http://en.cppreference.com/w/cpp/container/vector
Jak nie to co wyżej, to jeszcze tu:
http://www.cplusplus.com/reference/vector/vector/

Na tych witrynach masz także i inne kontenery które w Twoim przypadku, jeśli będzie taka potrzeba będą adekwatne do zastosowania.

0

Poklikaj i przeczytaj. Tu masz także przykłady..
http://en.cppreference.com/w/cpp/container/vector
Jak nie to co wyżej, to jeszcze tu:
http://www.cplusplus.com/reference/vector/vector/

Na tych witrynach masz także i inne kontenery które w Twoim przypadku, jeśli będzie taka potrzeba będą adekwatne do zastosowania.

0

Poklikaj i przeczytaj. Tu masz także przykłady..
http://en.cppreference.com/w/cpp/container/vector
Jak nie to co wyżej, to jeszcze tu:
http://www.cplusplus.com/reference/vector/vector/

Na tych witrynach masz także i inne kontenery które w Twoim przypadku, jeśli będzie taka potrzeba będą adekwatne do zastosowania.

0

Poklikaj i przeczytaj. Tu masz także przykłady..
http://en.cppreference.com/w/cpp/container/vector
Jak nie to co wyżej, to jeszcze tu:
http://www.cplusplus.com/reference/vector/vector/

Na tych witrynach masz także i inne kontenery które w Twoim przypadku, jeśli będzie taka potrzeba będą adekwatne do zastosowania.

0

O kontenerach nie słyszałeś, dlatego, że na studiach jak jest coś z zakresu C++ to podstawy podstaw. A vector to jest klasa szablonowa, oczywiście nie musisz znać się na programowaniu uogólnionym ( szablonach ) bo korzystasz z gotowca vector nie mniej jednak jeśli kazali Ci wykorzystać do tego tablicę ze wskaźnikami pewnie dlatego, żeby opanować w jakimś podstawowym stopniu operacje na wskaźnikach.
Tak więc jeśli ma być zwykła tablica wskaźników, to mają być to wskazniki klasę bazową ( poczytaj o polimorfizmie ). Jak sam zaimplementujesz dynamiczną zmianę wielkości tablicy wskaźników to pewnie też będziesz miał dużego plusa :) O dynamicznej zmianie wielkości tablicy z wykorzystaniem operatora new też masz sporo w necie no i też do trudnych rzeczy to nie należy :)

0

Dziękuję za liczne i "przyjazne" odpowiedzi. Postanowiłem, że na cele ćwiczenia JEDNAK zabawie się wskaźnikami, chociażby dlatego, że nie bardzo wiem jak to w tej chwili wykonać i nie odpuszczę:) Vector natomiast chętnie spróbuję opanować sam dla siebie (nie wykluczam, że zwrócę się o pomoc). Nurtuje mnie kwestia którą opiszę niżej, jak dynamicznie tabliczkę rozszerzyć mam pewien obraz.
Załóżmy, że moje klasy mają następujące pola (oczywiście będzie ich trochę więcej, jednak na cel demonstracji niech będzie tak niewiele):

class Produkt{
  string nazwa;
  float cena;
};

class Napoje: public Produkt{
   int pojemnosc; //pojemnosc butelki/opakowania 
};
  1. Produkty planuję ładować z pliku tekstowego, i tu moje pytanie jaką strukturę (chodzi mi o "kompozycję" pliku tekstowego) powinienem stworzyć (pasującą do jakiej klasy) Wydaje mi się, że również plik tekstowy powinien "pasować" do klasy podstawowej - Produkt, z tym że później trzeba będzie "dopasowywać" Produkty do konkretnych klas pochodnych (i uzupełniać brakujące pola - np. pojemnosc w przypadku produktu jakim będzie jakiś napój).

  2. Druga sprawa, skoro ustaliliśmy, że koszyk przechowywał będzie produkty dodane do koszyka w tablicy wskaźników, jak (automatycznie) tworzyć mam wskaźnik do każdego jednego stworzonego produktu (bo przecież każdy może zostać dodany do koszyka)? Metoda dodaj do koszyka, znajdująca się w klasie koszyk ma taki wskaźnik właśnie przyjmować z tym że nie mam pojęcia jak ten problem rozwiązać. Wskaźnik this tutaj chyba nie pomoże, przecież będziemy dodawać wiele produktów do tego samego koszyka a nie odwrotnie.

0

Zwróć uwagę że w produkt używasz przy cenie typu float. Jeśli to kod ćwiczebny, nie ma sprawy. Ale jeśli ma być (cokolwiek) zbliżony do rzeczywistego, ceny nie powinieneś definiować jako float. Float ma bardzo nieprzyjemne cechy jeśli chodzi o .. kwoty (nie chciał byś mieć swojej pensji liczonej jako float... uwierz.. :-) ).

Ad. 1. Tu pytasz tak naprawdę o metodę fabrykującą obiekt lub nawet o fabrykę abstrakcyjną (bo nie wiem co wymyślisz). Dlatego radzę w pliku tekstowym umieścić pole z rodzajem obiektu a za nimi oddzielone separatorem pola właściwe dla danego obiektu. Mogą to być pola w kolejności którymi zasilisz konstruktor obiektu. To kod ćwiczebny więc nie ma co wchodzić w XML'a. Wystarczy separator np. w postaci .. | (pionowa krecha) lub innego znaku nie używanego w nazwach/polach/cenach. W takcie czytania, metoda parsująca plik zwróci specyficzny produkt (lub jak wybrałeś wskaźnik) na podstawie pierwszego pola rodzaju i zasili budowaną klasę w konstruktorze dalszymi polami.

Ad. 2. Jeśli tablica (lub jaki tam wybierzesz kontener), będzie przechowywała wskaźnik na Produkt a sam produkt będzie np. klasą abstrakcyjną, to możesz do takiego wskaźnika przypisać każdy wskaźnik klasy dziedziczącej od Produkt. Zdefiniuj tylko poprawnie Produkt z metodami virutal bo w przeciwnym razie będą problemy z wywołaniem metod specyficznego produktu. W klasie Koszyk, powinien być jakiś kontener przechowujący wiele wskaźników na Produkt. Jak to zrobisz tablicą (bo napisałeś że nie innym kontenerem), to tablicę będziesz zmuszony alokować/realokować przy każdym powiększeniu ilości produktów w koszyku. Tu .. będzie rzeź (tak wydajnościowo jak i w komplikacji kodu).. ale sam chciałeś :-/

Czas na kod :-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1