[C++] Przeciążanie operatorów dla listy - niezgodność typów

gabrysb1995
Użytkownik
Użytkownik
Posty: 96
Rejestracja: 12 mar 2011, o 14:27
Płeć: Mężczyzna
Lokalizacja: Przemyśl
Podziękował: 27 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: gabrysb1995 »

W spojlerze jest cały kod, a poniżej definicje przeciążenia operatorów.
Ukryta treść:    

Kod: Zaznacz cały

lista* lista::operator -- ()				
{
	usunelement(iloscelementow());
	return this;
}
		
lista* lista::operator + (lista *lista2)
{
	lista* wskpom=new lista;
	for(int i=1; i<this->iloscelementow(); i++)
	{
		wskpom->dodaj(this->wsknaelement(i)->dane);
	}
	for(int i=1; i<lista2->iloscelementow(); i++)
	{
		wskpom->dodaj((lista2->wsknaelement(i))->dane);
	}
	return wskpom;
}


Jest to lista, którą pisałem temat wcześniej. Próbuje przeciążyć operator -- (usuwanie ostatniego elementu) i operator ++ (scalanie dwóch list po kolei). Problem z "--" jest taki, że usuwa mi całą listę, a z "+" jest niezgodność typów, pierwszy raz przeciążam operatory i wiele rzeczy nie wiem, więc prosiłbym o wytłumaczenie co robię źle, byłbym bardzo wdzięczny.

Definicje oepratów znajdują się na samym końcu przed funkcją main, która służy do tesstowania całej klasy
Ostatnio zmieniony 21 sie 2015, o 17:06 przez Afish, łącznie zmieniany 1 raz.
Powód: Staraj się lepiej dobierać nazwy tematów, tak by wskazywały o czym jest treść zadania.
lukequaint
Użytkownik
Użytkownik
Posty: 219
Rejestracja: 5 maja 2010, o 18:27
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 1 raz
Pomógł: 75 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: lukequaint »

Operator przeładowany dla danego typu to po prostu taka specjalna metoda obiektu danej klasy, w tym przypadku klasy lista. W linii 250, gdy próbujesz jej (tj. metody operator+(lista*) ) użyć, wcale nie zostaje wywołana, ponieważ zarówno lista1, jak i lista2 są typu lista* (czyli wskaźnik na obiekt klasy lista). Definiując ją, określiłes tylko działanie operatora + na typy lista i lista*. Jeśli dodasz "*", przed lista1:

Kod: Zaznacz cały

lista* lista3 = *lista1 + lista2;
"spod" wskaźnika lista1 zostanie wyłuskany obiekt klasy lista i dla niego zostanie wykonana metoda operator+.

To samo w drugim przypadku.

Kod: Zaznacz cały

lista1--;
dekrementuje wskaźnik, czyli adres pamięci. Gdy następnie próbujesz się do niego dostać dostajesz albo segfault albo jakieś śmieci. Zdefiniowana przez Ciebie metoda jest operatorem predekrementacji, czyli jest wywoływana przy zapisie

Kod: Zaznacz cały

++a;
gdzie a to obiekt klasy lista. Jeśli chcesz używać tego operatora po zmiennej (powinien wtedy zwracać listę przed usunięciem elementu, by semantycznie był spójny z tym dla liczb całkowitych), powinieneś zdefiniować metodę operator--(int) (tak już sobie wymyślili, że trzeba int dopisać ).

Jest też błąd w linii nr 252, powinno być:

Kod: Zaznacz cały

lista2->dodaj(liczba[6-i-1]);
Inaczej wychodzisz za tablicę (element o indeksie 6 w 6-elementowej tablicy).

Ponadto brakuje chyba kilku operatorów delete, Valgrind pokazuje niezwolnioną pamięć (po dodaniu delete w main() dla zdefiniowanych wskaźników lista1, lista2, lista3).

Kilka pouczających rzeczy, które mógłbyś dodać:
  • przenieść klasę rekord do klasy lista
  • zaimplementować konstruktory kopiujący i przenoszący oraz odpowiednie operatory przypisania
  • korzystać z std::unique_ptr, zamiast z gołych wskaźników
  • uczynić klasę lista wzorcem, by można było jej użyć do przechowywania obiektów dowolnego typu
gabrysb1995
Użytkownik
Użytkownik
Posty: 96
Rejestracja: 12 mar 2011, o 14:27
Płeć: Mężczyzna
Lokalizacja: Przemyśl
Podziękował: 27 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: gabrysb1995 »

No to tak, po pierwsze dziękuję ze odpisałeś i jakbyś miał (albo ktoś inny) trochę czasu to mam parę pytań.

Zastosowałem się do tych rad(oprócz inteligentnych wskaźników, dopiero się uczę, więc chciałbym nauczyć się też jak sobie radzić z gołymi wskaźnikami), zrobiłem listę jako szablon, przeniosłem klasę rekord do środa klasy lista i dodałem trochę operatorów. Oprócz tego przepisałem cały kod stosując angielskie nazewnictwo zmiennych, funkcji i klas, gdyż chyba taki jest standard w informatyce(nie posługuje się biegle angielskim, więc nazwy niekoniecznie muszą być trafne, z góry przepraszam)

mała uwaga do kodu tmpptr oznacza temporary pointer (nie wiem czy tak się oznacza tymczasowy wskaźnik)
Ukryta treść:    
Mam jednak parę problemów pytań:

1.Szablony, czy jeśli zdefiniuje klasę jako szablon, to jeśli chcę zdefiniować funkcję poza klasą, to czy muszę używać takiej konstrukcji, czy da się to zrobić krócej:

Kod: Zaznacz cały

template<class typeData> void list<typeData>::insertRecord (typeData dataIn, int number)

Kod: Zaznacz cały

template<class typeData> class list<typeData>::record* list<typeData>::umpteenthRecord (int number)
w szczególności do drugiego przykładu ciekawi mnie odwołanie do wskaźnika record* po co jest potrzebne znowu słówko class

2.Przeciążanie operatora "+". Problem mam z tym, że jak mam taką konstrukcję list3=list2+list1, to przy definiowaniu "+" tworzę tymczasową listę, którą następnie przepisuję do list3, jednak nie wiem jak później usunąć, podejrzewam, że jest tutaj wyciek pamięci.

3.Funkcja ifEmpty [Error] cannot convert 'list<typeData>::ifEmpty<int>' from type 'bool (list<int>::)()' to type 'bool' całkowicie nie rozumiem tego błędu co jest nie tak

4.Funkcja displayRecord przy kompilacji wyskakuje mi błąd dotyczący przeładowania, ale przecież funkcja displayRecord jest tylko jedna i nie jest przeładowana.

5.Konstruktor kopiujący chciałem stworzyć konstruktor który jako argument będzie przyjmował listę, jednak sama deklaracja funkcji mi nie wychodzi niestety

6.Wyciek pamięciMiałem też taki problem, że jak tworzę operatorem new nowy obiekt klasy lista, to tworzą mi się dwa wskaźniki, które potem nie miałem jak usunąć, czy teraz jak tworze je operatorem new wewnątrz klasy i usuwam je w destruktorze t jest już ok? I czy warto tworzenie tych wskaźników przenieść do konstruktora, lecz mam problem, że wtedy inne funkcje nie widzą tych wskaźników.

7.Operatory znówJak tworzę operatorem new obiekt korzystając ze wskaźnika, to jedyna możliwość do niego jest właśnie przez ten wskaźnik. Jednak jeśli nie będę dynamicznie zarządzał pamięcią i po prostu zinicjalizuje obiekt w taki sposób list list1, to będę mógł się do niego dostać bez wskaźnika. Moje pytanie brzmi, czy muszę przeładowywać operator, żeby oba poniższe sposoby działały
list + list zwraca list
list* + list* zwraca list*
oraz czy nie będzie problemu ze wskaźnikiem this w pierwszym przypadku, pierwszy przypadek jest w kodzie, więc prosiłbym, żeby ktoś sprawdziłczy jest prawidłowy.

8.klasa w klasie czy jeśli tworzę klase record w klasie list i chcę metodą klasy list odnieść się do elementu klasy record, to czy ten element musi być publiczny? Intuicyjnie mi się wydawało, że jeśli klasa record jest w klasie list, to ten element może być prywatny, lecz kompilator nie pozwala na to.

9. z powodow błedów na samym poczatku (punkt 4 i 5) nie jestem w stanie przetestować kodu, mógłby ktoś pokrótce sprawdzić go, byłbym wdzięczny.
lukequaint
Użytkownik
Użytkownik
Posty: 219
Rejestracja: 5 maja 2010, o 18:27
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 1 raz
Pomógł: 75 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: lukequaint »

Trudno tyle kodu sprawdzić pokrótce .
  1. Z tego, co mi wiadomo, nie da się - definiujesz metodę wzorca, więc musisz tę informację zawrzeć. Słówko class (albo typename) jest potrzebne, bo kompilator nie jest w stanie określić, co stoi za list<typeData>::record - czy jest to składowa klasy czy typ zdefiniowany wewnątrz. Zobacz tutaj:
    ... dent-scope
  2. Przy zdefiniowanym destruktorze i konstruktorze kopiującym (o tym niżej) wszystko powinno być ok. operator+ możesz zdefiniować korzystając ze zdefiniowanego +=, kopiując pierwszą listę (wygodniej może być mieć operator+ zdefiniowany na zewnątrz klasy).
  3. ifEmpty to adres funkcji (kompilator pokazuje jej typ bool (list<int>::)()). W tym miejscu chciałeś ją raczej wywołać, czyli z nawiasami:

    Kod: Zaznacz cały

    if(ifEmpty())
    
  4. U mnie wyskoczył tylko taki błąd:

    Kod: Zaznacz cały

    error: invalid use of non-static member function list1->displayRecord;
    
    Z tego samego powodu, co wyżej - brak nawiasów i argumentów wywołania, np.:

    Kod: Zaznacz cały

    list1->displayRecord(4);
    
    I to samo jest w metodzie displayAllRecord().
  5. Nie możesz zdefiniować konstruktora kopiującego (powinieneś go mieć, jesli klasa zarządza zasobami, inaczej składowe są kopiowane tak jak są - tzn. np. same wartości wskaźników, bez tego na co wskazują), który przyjmowałby listę (przy aktualnym stanie rzeczy) - w takim przypadku ta lista musiałaby być kopiowana, czyli musiałby być wywoływany konstruktor kopiujący, który właśnie definiujesz albo konstruktor przenoszący, którego jeszcze nie masz.

    Najlepiej jest przyjmować stałą referencję do listy (zauważyłem, że gcc 5.2.0 to podpowiada, nie wiem czy wcześniejsze wersje też), czyli:

    Kod: Zaznacz cały

    list(const list& l); // albo - jak wolisz taki zapis: list(list const& l); 
    
  6. Tak, najlepiej przenieść je do konstruktora (choć standard C++11 pozwala podawać domyślne wartości przy deklaracji składowych). Inne funkcje powinny je widzieć, opisz dokładniej ten problem.
  7. Tak, list* to inny typ niż list. Ale jeśli masz dwa wskaźniki, możesz listy, na które one wskazują dodać tak:

    Kod: Zaznacz cały

    list l3 = *l1 + *l2;
    
    więc nie jest konieczne definiowanie nowego operatora.
  8. W kodzie z poprawionymi błędami z 4. i 5., zarówno wskaźniki do obiektów klasy record jak i sama klasa są składowymi prywatnymi i nie mam z tym żadnych problemów. Mógłbyś opisać to dokładniej?
Uwagi:
  • korzystaj z nullptr zamiast NULL, pozwala to wyłapać kilka błędów
  • wpisuj nazwy parametrów przy deklaracji klasy - łatwiej jest wtedy pracować z kodem
  • zdefiniuj operator przypisania
  • ...i od razu konstruktor przenoszący i przenoszący operator przypisania ( ... e-with-c11), najlepiej spróbuj wykorzystać idiom [url=https://progdoo.wordpress.com/2012/06/03/c11-copy-and-swap-idiom/]copy-and-swap[/url].
Możesz dopisać do tego kilka testów, wykorzystując asercje (czyli funckję assert(), dołączając wcześniej<cassert>; przykład w kodzie poniżej), dzięki czemu będziesz wiedział, czy wszystko działa jak należy. Np. pisząc tych kilka liniejk odkryłem, że gdzieś w kodzie jest podwójne zwalnianie pamięci... błąd był w destruktorze - napisana przez Ciebie metoda deleteRecord już czyści head i tail .
Ukryta treść:    
Baw się dobrze !
gabrysb1995
Użytkownik
Użytkownik
Posty: 96
Rejestracja: 12 mar 2011, o 14:27
Płeć: Mężczyzna
Lokalizacja: Przemyśl
Podziękował: 27 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: gabrysb1995 »

Jeszcze raz dzięki, że odpisujesz, to bardzo miłe z Twojej strony. Zanim zadam jeszcze parę pytań odnośnie kodów, to chciałbym się Ciebie czegoś doradzić. Ja się uczyć dobrze programować? Tydzień temu ledwie co znałem podstawy C, bez wskaźników nawet, dość dużo dzięki Tobie się nauczyłem, teraz muszę sobie dość dużo poukładać, próbowałem uczyć się np z Symfonii ale ja jej nie potrafię czytać, za nudna, z kolei czytanie dokumentacji to jednak za trudne dla mnie i mam teraz problem co robić. I druga rzecz, mam problem z wymyślaniem co można kodzić, na całe szczęście wpadłem na pomysł, żeby zaimplementować listę dzięki czemu dużo się nauczyłem od Ciebie, ale mam problem z szukaniem pomysłów na kolejne programy, jak sobie z tym poradzić?

I jeszcze parę pytań odnośnie kodu (oczywiście jak nie masz czasu, albo chęci odpowiadać to nic się nie stanie )

1. Dlaczego w definicji konstruktora kopiującego używasz listy inicjalizującej, a nie po prostu w ciele konstruktora? To jest powszechna praktyka programistyczna? Szczerze mówiąc pierwszy raz się spotkałem z tym pojęciem

2.Dlaczego w konstruktorze kopiującym jako argument stosujemy stałą referencyjną do tablicy? Podejrzewam, że referencja dlatego, żeby nie przesyłać całej tablicy do konstruktora (optymalizacja), ale zastanawia mnie dlaczego po usunięciu referencji kompilator pokazuje mi błąd i wręcz domaga się referencji.

3. w linku odnośnie copy-and-swap nei rozumiem następujących dwóch linijek

Kod: Zaznacz cały

String(const String& other) = delete;
    String& operator=(String other) = delete;
4. Trochę mi się namieszało z konstruktorem kopiującym i operatorem = . Jak napiszę tak

Kod: Zaznacz cały

list list2 = list1
to zamiast dwóch funkcji (zwykły konstruktor i operator =) wywoła się konstruktor kopiujący, prawda?

I jeszcze mam pytanie do nieszczęsnego operatora +

Kod: Zaznacz cały

list list3(przykladowalista);    //konstruktor kopiujący
list3=list2+list1;
[pisane wczesniej]w tej chwili wydaje mi się, że tak to będzie działać, najpierw wykona się część list2+list1, w definicji tej funkcji jest inicjalizacja nowej listy, która zostanie przesłana do operatora = który ją przekopiuje do list3. Tylko naprawdę nie rozumiem gdzie zostanie usunięta ta lista tymczasowa w operatorze+

[pisane później]Teraz coś zrozumiałem, wcześniej w operatorze + tworzyłem operatorem new tą nową listę, co jest po prostu nie potrzebne, mogę po prostu ztobić to tak

Kod: Zaznacz cały

template<class typeData> list<typeData> list<typeData>::operator + (list list2)
{
   list list3
   record* tmpptr=this->head;
   while(tmpptr!=nullptr)
   {
      list3.addRecord(tmpptr->data);
      tmpptr=tmpptr->nextRecord;
   }
   tmpptr=list2.head;
   while(tmpptr!=nullptr)
   {
      list3.addRecord(tmpptr->data);
      tmpptr=tmpptr->nextRecord;
   }
   return list3;
}
nie tworzę dynamicznie nowego obiektu więc nie muszę się martwić o jego usunięcie, tylko po wywołaniu funkcji sam się on usunie, prawda?

5.tak jeszcze do operatora = , co się stanie jak zdefiniujemy go taklist1=list2 wykona dwie funkcje, najpierw destruktor na list1 , a potem konstruktor kopiujący?

Zdefiniowałem tak ten operator, kompiluje się dobrze, jednak coś jest nie tak, gdyż po wywołaniu tego operatora lista jest pusta, liniie około 238 i 288
Ukryta treść:    
I mam problem z taką linijką

Kod: Zaznacz cały

   --(*this);
jeśli użyje operatora= ale nie jako konstruktor kopiujący to wyskakuje mi błąd. To do wcześniej wersji kodu gdzie nie zmieniałem sposobu działania operatora =. Teraz chyba nie chodzi o przesuwanie wskaźnika, bo *this to już chyba obiekt a nie wskaźnik do niego.
Ukryta treść:    
linijka 236

Jeszcze raz dziękuję
lukequaint
Użytkownik
Użytkownik
Posty: 219
Rejestracja: 5 maja 2010, o 18:27
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 1 raz
Pomógł: 75 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: lukequaint »

To bardzo trudne pytanie. Zależy co chcesz programować, w jakim języku, na jaką platformę. Różni ludzie uczą się w różnym tempie, więc gdybyś przejął czyjeś "wyzwania", mógłbyś albo się znudzić, albo zniechęcić.

Tutaj piszemy o języku C++, a i z samym nim to nie taka prosta sprawa (pomijając, że to bardzo trudny i skomplikowany język). Obok samego języka powinieneś znać kilka narzędzi - na pewnym poziomie stają się niezbędne - np. nauczyć się korzystać z systemu kontroli wersji (git, svn,...), debuggera (gdb i eee... ), czegoś do profilowania/badania pamięci programu, np. gprof, valgrind (w czym zawierają się różne podnarzędzia: memcheck, callgrind, cachegrind), nauczyć się testować kod (na Udacity są dwa dobre kursy: Software Testing i Software Debugging - tak, dobra znajomość języka angielskiego jest niezbędna, jeśli chciałbyś "wyjść do ludzi" z kodzeniem czy zająć się czymś ciekawszym od poznawania języka), poznać jak działa komputer (w szczególności pamięć - C++ pozwala pisać bardzo wydajny kod, ale bez odpowiedniej wiedzy jest to bardzo trudne), algorytmiki (możesz ćwiczyć i uczyć się np. na takich portalach jak SPOJ, codeforces.com, topcode.com, codechef.com), wzorców projektowych itd.

Myślę, że to dobry znak, że "Symfonia C++" Cię nudzi . Na początku też miałem z nią do czynienia i to raczej nie jest dobra droga - sporo zapomnisz, zanim usiądziesz do pisania, a później i tak będziesz szukał wskazówek w Sieci. Sądzę, że najlepiej będzie obrać sobie jakiś projekt (tak jak napisanie listy), a następnie próbować do niego dodawać kolejne elementy języka (a w razie problemu, powinieneś znaleźć rozwiązanie na jakimś blogu lub StackOverflow). I świetnym pomysłem wydaje mi się kopiowanie elementów biblioteki standardowej (ze względu na jej ogólność, będziesz musiał sięgnąć do najbardziej zaawansowanych konstrukcji języka), np. napisanie:
  • inteligentnego wskaźnika
  • mapy
  • iteratora
  • wątkowo-bezpiecznej kolejki
Możesz spróbować dołączyć do jakiegoś otwartoźródłowego projektu i tam krok po kroku się rozwijać, zajmując się coraz to bardziej zaawansowanymi zadaniami (np. LibreOffice ma długą listę tzw. EasyHacków, które mają ułatwić początkującym wdrożenie się - choć jest to za duży projekt jak na początek).

Jeśli chodzi o bardziej użyteczne programy... to też powszechny problem. Możesz zająć się algorytmicznymi zadaniami i spróbować napisać rozwiązanie "ładnie" i poprawnie. No albo... coś spróbować jednak wymyślić . Trzeba zdobyć nieco wiedzy, by uświadomić sobie, co nas naprawdę interesuje i jakie możliwości stwarza umiejętność programowania - z tym niektórzy mają duży problem. Możesz skodzić np. (większość będzie wymagała zdobycia "wiedzy domenowej" - sprawdź Courserę - lub poznania jakiejś biblioteki/silnika):
  • kalendarz
  • notatnik
  • grę - prostą lub bardziej zaawansowaną - np. kółko i krzyżyk, statki, tetris,...
  • moduł jądra Linux
  • serwer http
  • klienta ftp
  • sniffer pakietów
  • prosty komunikator
  • tagger plików mp3
  • menadżer plików
  • czytnik RSS
  • konwerter np. Markdown do HTML
  • interpreter jakiegoś języka (np. Brainf*ck)
  • generator losowych map
  • katalog książek/zakładek
  • scraper ("pobieracz" treści z Sieci, np. danych z arykułów z Wikipedii)
  • ...
1. Z przyzwyczajenia. Tak, bardzo często będziesz widzieć coś takiego - zwykle inicjalizuje się wszystko co się da i to jest miejsce na wywołanie konstruktorów klas bazowych. Niektórych składowych nie można zainicjalizować w ciele konstruktora - stałych czy referencji - wtedy trzeba je umieścić na liście inicjalizacyjnej.

2. O tym napisałem wcześniej, w punkcie 5. Gdybyśmy mieli zdefiniowany tak konstruktor kopiujący:

Kod: Zaznacz cały

list(list ll);
to przy konstruowaniu listy z już istniejącej, musielibyśmy skopiować listę - czyli wywołać konstruktor kopiujący - by wywołać konstruktor kopiujący:

Kod: Zaznacz cały

list a;
list b(a); // wywołujemy list::list(ll) dla b, co wywołuje list::list(ll) dla a
Właściwie to referencja do stałej, nie stała referencja (nie można zmienić adresu, na który wskazuje referencja - zawsze są stałe). Dzięki słówku const nie ma obawy, że przez przypadek wewnątrz konstruktora zmienimy kopiowany obiekt - od razu wyłapie to kompilator.

3. Te dwie linijki (takie zastosowanie słówka delete weszło do języka wraz ze standardem C++11) sprawiają, że obiektów danej klasy nie można kopiować. W tekście wpisu jest informacja o tym, że te dwie metody zostaną dodane później. Dzięki temu zapobiegamy użyciu domyślnych implementacji tych metod, które zostałyby dostarczone przez kompilator (klasa zarządza zasobami - przez wskaźnik data_ - więc nie można w prosty sposób kopiować jej atrybutów).

O szczegółach domyślnej implementacji - m.in. kiedy jest dostępna - możesz przeczytać tutaj: ... onstructor. Przed standardem C++11 konstruktor kopiujący i operator przypisania umieszczało się w sekcji prywatnej klasy. Czasami możesz się też spotkać z dziedziczeniem po boost::noncopyable.

4. Tak. Dopisz wypisywanie do konstruktorów/operatorów przypisania i sprawdź, co kiedy jest wywoływane .

Co do operatora+ to wszystko powinno być ok, ale nie do końca jest jak piszesz. Mogę się mylić, ale w tym przypadku będzie tak, że albo przy zwracaniu zmienna list3 zostanie skopiowana przy pomocy konstruktora kopiującego - co jest bardzo nieefektywne - i ta lokalna wersja zakończy swój żywot (wywoła się destruktor), albo przeniesiona, jeśli masz konstruktor przenoszący, albo kompilator zoptymalizuje to miejsce i uniknie kopiowania, wykorzystując tzw. copy elision (

Kod: Zaznacz cały

https://en.wikipedia.org/wiki/Copy_elision
; jak tutaj jest napisane, pod względem wydajności zadziała tak jak bezpośrednia inicjalizacja zmiennej, która ma przechowywać wynik).

Zauważ, że możesz ten operator zdefiniować tak:

Kod: Zaznacz cały

template<typename T>
list<T> operator+(list<T> l1, list<T> l2)
{
    return l1 += l2;    
}
5. Nie. W linii 238 usuwasz to, co jest pod wskaźnikiem this - niedobrze . By zdefiniować poprawnie musiałbyś wewnątrz przepisać dane z listy, którą kopiujesz w nowe miejsce, usunąć to, co jest pod wskaźnikami w oryginalnej liście, a następnie podmienić odpowiednie wskaźniki (head i tail). Właśnie po to powstały inteligentne wskaźniki i copy-and-swap idiom. Niżej tak zdefiniowałem operator=:
Ukryta treść:    

Jeśli dobrze widzę, to w linijce:

Kod: Zaznacz cały

 --(*this)
próbujesz użyć operatora predekrementacji, a masz zdefiniowany tylko postdekrementacji, tzn. powinieneś mieć operator:

Kod: Zaznacz cały

list::operator--()
// obok operatora list::operator(int)
.
gabrysb1995
Użytkownik
Użytkownik
Posty: 96
Rejestracja: 12 mar 2011, o 14:27
Płeć: Mężczyzna
Lokalizacja: Przemyśl
Podziękował: 27 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: gabrysb1995 »

Lukequaint, dzięki wielkie jeszcze raz, przepraszam, że dopiero teraz odpisuję, ale musiałem jechać szukać mieszkania na studia

Pomysłów to mi aż za nad to podałeś, będę miał czymś się zająć przez najbliższy czas, chyba na początek wezmę bibliotekę standardową i spróbuję parę rzeczy z niej napisać na nowo, potem zobaczę jeszcze, coś z listy na pewno wybiorę.

Mam takie pytanie(już nie odnośnie języka ), uważasz, że warto pójść na studia informatyczne? Planowałem pójść za rok (teraz idę na 2. rok matematyki), ale zastanawiam się czy warto, czy lepiej jednak w zaciszu domowym uczyć się języka (od razu powiem, że nie chciałbym być zwykłym klepaczem kodu, jakbym miał się tym zająć na poważnie, to też poświęciłbym na tyle czasu, żeby coś sensownego osiągnąć)

Już Ci nie będę zawracał głowy tą listą, naprawdę dużo mi pomogłeś i zmotywowałeś do dalszej nauki
lukequaint
Użytkownik
Użytkownik
Posty: 219
Rejestracja: 5 maja 2010, o 18:27
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 1 raz
Pomógł: 75 razy

[C++] Przeciążanie operatorów dla listy - niezgodność typów

Post autor: lukequaint »

To bardzo trudne pytanie (zobacz np. na Quorę, ile osób tam pyta, czego brakuje programistom bez studiów informatycznych albo czy w ogóle można być programistą bez nich) ...sam musiałem je sobie zadać trzy lata temu. Skończyłem matematykę (pierwszy stopień, za miesiąc zaczynam studia drugiego stopnia na tym samym kierunku na Uniwersytecie Wrocławskim) i mogę napisać jak to wygląda z mojej perspektywy, przede wszystkim w oparciu o obserwację studentów obu kierunków i współpracę z nimi.

Myślę, że warto, ale studia matematyczne to świetna alternatywa. Z jednej strony na studiach informatycznych zdobędziesz coś w rodzaju "obycia informatycznego" - poznasz wiele gałęzi informatyki, czasami z przymusu (często samemu nie udaje się nauczyć takich rzeczy - są albo zniechęcająco trudne, albo nudne, albo wydają się niezbyt przydatne), wśród studentów poznasz ludzi z pasją (na matematyce przez trzy lata spotkałem bodajże sześć osób, które studiowało tylko matematykę i pracowało/chciało pracować w "zawodzie informatycznym"), od których może nie nauczysz się wiele (bo czego można się dowiedzieć na przerwach ) ale którzy zachęcą Cię do własnych poszukiwań i podrzucą pomysły czy problemy, które - kto wie - mogą okazać się przełomowe w Twojej karierze, zdobędziesz doświadczenie oraz, jak mi się zdaje, uzbierasz kilka projektów "pokazowych" do CV. Ważna też jest rywalizacja, która zmotywuje Cię do dopisania dodatkowych 100 linii kodu, gdy powieki są już na wpół przymknięte. Mnie brakuje kilku umiejętności, które mógłbym zdobyć na informatyce, ale lubię uczyć się sam i tego, co mnie w danej chwili interesuje (aktualnie w Sieci można znaleźć wiele kursów online - MOOC-ów (o czym już pisałem) - które to ułatwiają, choć na pewno nie zastąpią solidnej nauki na studiach).

Z drugiej strony obycie matematyczne może być bardziej przydatne, szczególnie jeśli nie chcesz być "klepaczem" i zająć się nauką. Trudno przewidzieć, jak za kilka, kilkanaście i więcej lat będzie wygladała informatyka. Aktualnie, z tego co widzę, coraz częściej na wierzch wypływa Haskell i z nim programowanie funkcyjne (bądź funkcjonalne - i tak słyszałem). Łatwiej jest je pojąć, mając dobre podstawy z matematyki. Już teraz (choć danych jest sporo, za pięć, dziesięć lat - tak sądzę - będzie ich sto albo nawet tysiąc razy więcej, patrz np. Internet of Things) w wielu, wielu miejscach pojawia się zaawansowana statystyka (wybacz angielskie terminy; machine learning, data science: natural language processing, data mining, signal processing, computer vision). W innych gałęziach informatyki, wyższa matematyka już jakiś czas temu się zadomowiła - w kryptografii czy grafice komputerowej. A zawsze studiując matematykę, możesz przejść się na wykład na informatykę by opanować podstawy teoretyczne. Zwróć też uwagę na to, że samemu dużo trudniej jest się nauczyć matematyki niż informatyki. Matematyka jest też bardziej ponadczasowa . Może w podstawach logice czy teorii złożoności obliczeniowej nic się nie zmieni, ale np. jeśli chodzi o systemy operacyjne, języki programowania, obliczenia w chmurze, sztuczną inteligencję czy projektowanie oprogramowania, trzeba nadążać za aktualnym stanem wiedzy - to, czego się nauczysz na studiach informatycznych może być za pięć lat przestarzałe.

Ale to wszystko zależy od Twojej motywacji oraz poziomu uczelni czy dostępu do materiałów do nauki. Czy wybierzesz matematykę czy informatykę i tak musisz być gotowy na ciągłe rozwijanie umiejętności, zdobywanie wiedzy, by być "programistą od zadań specjalnych". Teraz dopiero przeczytałem w innych tematach, że celujesz w karierę naukową w matematyce lub fizyce. W takim przypadku widzę dużo mniej pożytku ze studiowania informatyki (w stosunku do poświęconego czasu). Jeśli będziesz chciał coś sobie skodzić, szybko nauczysz się jak to zrobić (znajdziesz też wiele programów czy języków programowania specjanie dostosowanych do Twych celów naukowych). Jeśli to będzie coś większego, znajdziesz "klepaczy", którzy zrobią to dla Ciebie szybciej i lepiej niż Ty sam po studiach informatycznych.

A zawsze możesz spróbować studiów informatycznych i zastanowić się, czy Cię ubogacają w jakikolwiek sposób i jeśli nie, zrezygnować po miesiącu, semestrze czy roku. Czy po matematyce, czy po fizyce, czy po informatyce i tak będziesz wygranym w "nowym wspaniałym świecie", w który jesteśmy wciągani... będziesz "kapłanem nowych czasów".

No ale nie musisz wierzyć pierwszemu lepszemu "klepaczowi", który Ci odpowiedział na temat listy w C++.

Jeśli masz jeszcze jakieś pytania spoza C++, wyślij prywatną wiadomość albo, lepiej, załóż nowy wątek, bo zaraz nas "Źli Moderatorzy" zganią za taki offtop .
ODPOWIEDZ