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

Manualna implementacja kolejki, kilka pytań odnośnie dynamicznej alokacji pamięci i wskaźników.

Object Storage Arubacloud
0 głosów
754 wizyt
pytanie zadane 19 sierpnia 2018 w C i C++ przez Nenth Nowicjusz (150 p.)

Witam wszystkich,

jestem na etapie poznawania struktur danych używanych w programowaniu. Ostatnio zająłem się kolejką. Chciałbym prosić kogoś bardziej doświadczonego o sprawdzenie czy poprawnie ją zaimplementowałem oraz czy nie ma wycieków pamięci. Dodatkowo mam kilka pytań:

a) Czy opłaca się samemu implementować struktury danych czy raczej korzystać z gotowych bibliotek?

b) W metodzie Queue::remove() w linijce oznaczonej komentarzem "1", tworzę kopię wskazującą na "first", natomiast w linii "2" za pośrednictwem tej kopii niszczę element na który wskazuję "first". Rozumiem, że po tej operacji "first" wskazuję teraz na komórkę w pamięci, w której nic się nie znajduję, dopóki nie przekieruję go na następny element kolejki(linia nr 3)? Przepraszam, że tak niejasno ale nie mam pojęcia jak to inaczej ubrać w słowa. Liczę, że ktoś da radę zrozumieć moje wypociny. smiley

// ConsoleApplication4.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include "conio.h"

struct Node {

	int m_id;
	Node *next;

	Node(); // constructor



};

Node::Node() {

	next = nullptr;

}

struct Queue {

	Node *first;
	Node *last;

	Queue(); // constructor

	void add(int id); // dodaj do kolejki
	void remove(); // usuń z kolejki
	void print(); // wyświetl zawartość kolejki

	bool empty(); // czy pusta
	int size(); // zwraca wielkość kolejki

	void destroy(); // niszczy całą kolejkę



};

Queue::Queue() {

	first = last = nullptr;

}

void Queue::add(int id) {

	Node *node = new Node; // Tworzymy nowy węzeł

	node->m_id = id;

	if(!first) { 
		
		first = node;
		return;

	}


	// W przypadku braku informacji, szukamy ostatniego węzła

	if(!last) {
	
		Node *tmp = first;

		while(tmp->next)
			tmp = tmp->next;

		last = tmp;
	
	}

	
	node->next = nullptr;
	
	last->next = node;
	last = node;
		
}

void Queue::remove() {

	Node *tmp = first; // 1

	if(tmp) {
	
		first = nullptr;
		first = tmp->next; // 3
		delete tmp; // 2
	
	}

}

void Queue::print() {

	Node *tmp = first;

	std::cout << "FIRST --> ";

	while(tmp) {
	
		
		std::cout << tmp->m_id << " ";
		
		tmp = tmp->next;
	
	}

	std::cout << " <-- LAST";
}

bool Queue::empty() {

	return first == nullptr;

}

int Queue::size() {

	Node *tmp = first;

	int i = 0;

	while(tmp) {
	
		tmp = tmp->next;
		i++;
	
	}

	return i;

}

void Queue::destroy() {

	while(!this->empty())
		this->remove();

}

int main()
{

	Queue *queue = new Queue;

	queue->add(8);
	queue->add(66);
	queue->add(23);
	queue->print();

	std::cout << "\nSize: " << queue->size() << " Empty: " << queue->empty() << "\n\n";

	queue->remove();
	queue->print();

	std::cout << "\nSize: " << queue->size() << " Empty: " << queue->empty() << "\n\n";
	
	queue->destroy();
	queue->print();
	
	std::cout << "\nSize: " << queue->size() << " Empty: " << queue->empty() << "\n\n";

	delete queue;
	queue = nullptr;

	

	_getch();
	return 0;
}

 

3 odpowiedzi

+2 głosów
odpowiedź 19 sierpnia 2018 przez RafalS VIP (122,820 p.)
wybrane 19 sierpnia 2018 przez Nenth
 
Najlepsza

Na szybko:

oraz czy nie ma wycieków pamięci

Proponuje bardzo prosty stress test for 1000000 razy stworz kolejke, cos dodaj, cos usun i patrz czy pamięć zjadana przez program rośnie, czy to w jakimś debugerze czy po prostu podglądając proces. To najprostsze rozwiązania przy takich prostych programach. Są też fajne programy do analizy wycieków np valgrind, którego użycie skraca się do valgrind moj_program i wypisuje Ci czy wszystko jest cacy z pamięcią.

a - zawsze gdy się da korzystaj z gotowych implementacji. Wynajdywanie koła od nowa jest złe z dwóch powodów:

  • zajmuje czas
  • osoby, które czytają Twój kod muszą dodkowo ogarnąć Twoją implementacje ogólnie znanych i już rozwiązanych problemów

Jedyny plus wynajdywania koła od nowa jest głębsze zrozumienie tego koła. Dlatego warto sobie dla treningu raz zaimplementować a potem korzystać z gotowych rozwiązań.

b - musisz pamiętać, że robiąc delete wskaźnik zwalniasz pamięć na którą ten wskaźnik wskazuje, nic wiecej, sam wskaźnik dalej pokazuje dokładnie w to samo miejsce. Zwolnienie pamięci oznacza, że zrzekasz się tej pamięci i nie należy już ona do programu, czyli jest wolna i może zostać ponownie wykorzystana na np inną zmienną.

        first = nullptr;
        first = tmp->next; // 3

ustawienie firsta na nullptr jest bez sensu skoro zostanie linijke nizej nadpisany. Posiadanie wskaźnika do pamięci która nie należy do Ciebie nie jest przestępstwem :D. Czasem można nawet odczytać jej wartość albo nawet coś tam zapisać. Spowoduje to niezdefiniowane zachowanie i okazjonalnie memory access violation, segmentation fault i takie tam, więc nie polecam.

Co do implementacji to nie jest zła. Nie podoba mi się jedynie brak destruktora i brak składowej size :D. Po przechodzić za każdym razem po wszystkich elementach?

Definiowanie konstruktora tylko po to żeby ustawić nullptr też nie jest za fajne, skoro od C++11 można zrobić po prostu tak:

struct Node {
 
    int m_id;
    Node *next = nullptr;
};

Ostatnia sprawa to ciekawostka - listy to przestarzałość ze względu na to, że nie są cache-friendly i są sensowne tylko w teori. Sytuacje, w których warto wybrać liste ponad tablice dynamiczną czy częściową tablice dynamiczną (std::dequeue) już prawie nie występują. Sam twórca C++ tutaj o tym mówił.

komentarz 19 sierpnia 2018 przez Nenth Nowicjusz (150 p.)
Na taką odpowiedź liczyłem. Bardzo mi pomogła, zajmę się poprawkami w kodzie jak tylko wrócę do domu. Serdecznie dziękuję!
0 głosów
odpowiedź 19 sierpnia 2018 przez niezalogowany
IMO lepiej korzystać z gotowych bibliotek w programach (szczególnie tych większych), zwiększa to czytelność kodu, ale warto rozumieć działanie danej struktury a zaimplementowanie jej pomaga ;)
0 głosów
odpowiedź 19 sierpnia 2018 przez Arkadiusz Sikorski Pasjonat (20,160 p.)

Jeśli chcesz sprawdzać wycieki pamięci - zainteresuj się valgrindem.

Najczęściej standardowe kontenery są odpowiednie, więc nie ma sensu implementować ich samemu. Z drugiej strony warto jest umieć je zaimplementować. Choćby dlatego, że prędzej czy później zdarzy się przypadek, że gotowe struktury będą zwyczajnie gorsze dla jakiegoś szczególnego przypadku użycia.

Jeśli chodzi o sam styl pisania, to klasa węzeł mogłaby być w klasie Kolejka, ponadto zadbaj o odpowiednią hermetyzację - wskaźniki na węzły nie powinny być publiczne!

W ramach poćwiczenia możesz też zaimplementować kolejkę na tablicy (coś jak bufor cykliczny, wykorzystując działanie modulo). Oczywiście w przypadku zapełnienia, alokowałbyś nową, większą tablicę.

komentarz 19 sierpnia 2018 przez Nenth Nowicjusz (150 p.)
Co do valgrinda - czytałem o nim, ale został wydany tylko i wyłącznie w wersji na linuxa. Może czas zmienić system :)

Co do uwag stylu pisania. Jestem w trakcie nauki C++  i jeszcze nie poznałem takich właściwości jak publiczne i prywatne zmienne. Mimo wszystko wezmę sobie to do serca i z pewnością będę zwracał na to uwagę w przyszłości.
1
komentarz 19 sierpnia 2018 przez Arkadiusz Sikorski Pasjonat (20,160 p.)
W takim razie powodzenia!

Pamiętaj, że nie musisz instalować systemu na fizycznej maszynie, zawsze możesz sobie odpalić system na maszynie wirtualnej (Virtual Box czy coś podobnego).

Podobne pytania

0 głosów
1 odpowiedź 185 wizyt
0 głosów
2 odpowiedzi 761 wizyt
pytanie zadane 29 kwietnia 2017 w C i C++ przez Łukasz Świtaj Użytkownik (520 p.)
0 głosów
2 odpowiedzi 472 wizyt

92,568 zapytań

141,424 odpowiedzi

319,634 komentarzy

61,956 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!

...