Jak poprawnie tworzyć nowe obiekty podczas działania programu?

0

Szczegółowy opis:
Do tej pory z symfonii ogarnąłem już instrukcje sterujące, typy, operatory, funkcje, wskaźniki, wiem też co nieco o preprocesorze i przeładowaniu funkcji, klasach oraz klasie string (właściwie skończyłem czytać cały pierwszy tom).

Doszedłem do wniosku, że najwyższy czas zdobytą wiedzę wykorzystać w praktyce, rozwiązując troszeczkę bardziej skomplikowany problem niż te, które napotykałem do tej pory na końcu każdego rozdziału tej książki. Pomyślałem o napisaniu prostej książki adresowej, w której będę miał możliwość:

  1. Dodawania kontaktu
  2. Wyświetlania kontaktu
  3. Usunięcia kontaktu
  4. Wyświetlenia wszystkich kontaktów
  5. Edycji informacji nt. konkretnego kontaktu
  6. Zapis do zewnętrznego pliku
  7. Odczytania z zewnętrznego pliku

Problem właściwy:

No i już na początku napotkałem pewien problem. Nie wiem za bardzo,w jaki sposób mam, w czasie działania programu, utworzyć nowy obiekt klasy osoba. Tzn wiem, że mógłbym posłużyć się w tym celu dynamiczną alokacją tablicy, tylko moje pytanie brzmi - jak? Bo ona pozwala mi utworzyć tablicę o zadanym rozmiarze, ale powiększyć jej nie mogę. Myślałem o rozwiązaniu tego problemu w ten sposób, iż jeśli będę musiał dodać nowy kontakt, to po prostu zostanie stworzona nowa tablica tych obiektów, o 1 większa od poprzedniej i wszystkie wpisy ze starej tablicy zostaną skopiowane do nowej. Wydaje mi się jednak, że jest to bardzo niewydajne rozwiązanie, bo tych danych do skopiowania jest całkiem sporo.

Myślałem również o utworzeniu czegoś w rodzaju spisu adresów danych obiektów, czyli dajmy na to, mamy to dynamicznie zmieniającą się tablicę wskaźników, która przechowują adresy obiektów klasy osoba i podobnie jak poprzednio, gdy tworzę nowy obiekt, to tworzona jest nowa tablica tych wskaźników, adresy są przepisywane a stara jest kasowana - tym razem muszę skopiować tylko adresy tych obiektów, na które pokazywały, więc pracy jest jakby mniej.

Czy jest jakiś lepszy sposób? Z góry dzięki za odpowiedź.

1
transient napisał(a)

Tzn wiem, że mógłbym posłużyć się w tym celu dynamiczną alokacją tablicy, tylko moje pytanie brzmi - jak? Bo ona pozwala mi utworzyć tablicę o zadanym rozmiarze, ale powiększyć jej nie mogę.

skorzystaj z STL. poczytaj o std::vector i innych kontenerach. w wektorze przetrzymuj obiekty, lub wskaźniki do obiektów (nie zapomnij o dealokacji przed usuwaniem elementu z wektora), jeśli w innym miejscu chcesz mieć niezmienny adres do konkretnego obiektu.

0

A czy można ten problem rozwiązać, nie korzystając z zewnętrznej klasy vector? Wiem, można powiedzieć, że to byłoby jak sztuka dla sztuki, bo przecież rozwiązanie (właśnie w formie tej klasy vector) już istnieje, więc po co wynajdywać koło na nowo. Ja jednak chciałbym właśnie takie rzeczy poćwiczyć, bo co jeżeli np. będę kiedyś coś w C pisał? Tam przecież klas w ogóle nie ma, więc będę musiał coś własnego zaimplementować, co pomoże mi rozwiązać np. taki problem jak ten tutaj :)

1

Jeśli do nauki, to jasne, że warto przećwiczyć konstruowanie takich struktur danych ;)
Dobrze myślisz. Ja też bym stworzył coś podobnego jak to opisałeś w pierwszym poście.
Jeśli to C, to najlepiej stworzyć sobie coś takiego:
struct tablica
{
unsigned rozmiar ;
Typ **tab ;
};

i za pomocą dynamicznej alokacji pamięci oraz zmiennej rozmiar kontrolować obiekt. Za pomocą makr można to sparametryzować

Jeśli to C++, to lepiej stworzyć klasę z konstruktorami i destruktorami, setterami, getterami i innymi funkcjami oraz sparametryzować za pomocą szablonów.

Właściwie to jest wymyślanie vectora na nowo :)

1

Vector w gruncie rzeczy działa tak jak napisałeś, ale nie zwiększa swojej pojemności o 1, ale więcej, co ileś dodanych elementów.
Ty w swoim programie możesz użyć listy jedno/dwukierunkowej. Wtedy przy dodaniu czy usunięciu pozycji nie będziesz musiał (de)alokować i przenosić wszystkich elementów. Do tego przy dodawaniu od razu możesz dodać sortowanie. Różnica z vectorem będzie taka, że ten gwarantuje to, że elementy w pamięci będą ułożone obok siebie - możesz pobrać adres pierwszego elementu i mieć pewność, że przejdziesz je wszystkie zwykłym wskaźnikiem. Ja bym zrobił listę.

1

Mozesz tez klase osoba zdefiniowac tak:

class Osoba {
    // twoj kod
    Osoba *p_nastepna_osoba;
} 

i teraz kazdy obiekt typu osoba ma wskaznik na nastepny obiekt typu osoba, powstaje w ten sposob lista osob. ostatni na liscie ma wskaznik p_nastepna_osoba ustawiony na NULL.

0

Panowie, dzięki serdecznie za odpowiedzi i poświęcony czas :)
@rafal__
Myślę, że zrobię to... na dwa opisane przez Ciebie sposoby :) Skoro mam to przećwiczyć, to najlepiej na wszystkie możliwe sposoby :)

@Rev
Ha, skoro mówisz, że klasa Vector działa tak jak napisałem, to przypadkiem nie jest ona niesamowicie powolna? Chociaż w sumie, skoro nie zwiększa swojej pojemności o 1, tylko o więcej elementów, to może kopiowanie tych elementów z jednej tablicy do drugiej nie odbywa się tak często...

A o tej liście też poczytam :)

@EgonOlsen

Ha, to też dobry sposób jest :)

Pozdrawiam

0
transient napisał(a)

@Rev
Ha, skoro mówisz, że klasa Vector działa tak jak napisałem, to przypadkiem nie jest ona niesamowicie powolna?

nie, ona jest szybka ;)

w twoim wypadku operacja push_back() powinna:

  • utworzyć nową tablicę N+1 elementów
  • przekopiować N elementów ze starej tablicy do nowej,
  • na pozycji N (N+1, jeśli liczymy od 1) zapisać nowy element
  • usunąć starą tablicę
  • zwiększyć licznik

a tak, to realokacja następuje tylko w przypadku przekroczenia rozmiaru określonego w zmiennej składowej capacity, a push_back powoduje:

  • sprawdzić, czy nie została przekroczona capacity i ewentualnie realokować
  • na pozycji N zapisać nowy element
  • zwiększyć licznik

http://www.cplusplus.com/reference/stl/ - tu jest tabelka, gdzie complex to złożoność obliczeniowa - std::push_back() ma O(1)

pozdrawiam

0

@EgonOlsen wydaje mi się że ładnie byloby skorzystać z zasady jednej odpowiedzialności i zrobić osobno klasę przechowującą jakąś logikę i osobno klasę będącą węzłem listy.

class Person{
//cośtam
};

class Node{
  Person* osoba;
  Node* nextNode;
}

(A juz oczywiście najładniej byłoby zrobić sobie szablonową klasę dla węzłów i listy i parametryzować ją typem przechowywanych danych ;) )

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