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.
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:
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
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ć ).
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
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)
#include <iostream>
using namespace std;
template<class typeData>
class list
{
class record
{
public:
typeData data;
record* nextRecord;
};
private:
record* head=new record; //wskaźnik do pierwszego elementu listy
record* tail=new record; //wskaźnik do ostatniego elementu listy
bool ifEmpty(); //zwraca true-lista jest pusta, false-lista zawiera elementy
record* umpteenthRecord (int); //zwraca wskaźnik na n-ty element
public:
list(); //konstruktor
// list(list); //konstruktor, który kopiuje listę, która jest argumentem
~list(); //destruktor
int numberOfRecord(); //zwraca liczbę elementów
void addRecord(typeData); //dodaje element
void displayAllRecord(); //wyświetla listę
void displayRecord(int); //wyświetla n-ty rekord
void deleteRecord(int); //usuwa n-ty rekord
void insertRecord(typeData, int); //wstawia rekord w n-te miejsce
void swapRecord(int, int); //zamienia n-te elementy
list operator --(int); //usuwa ostatni element
list operator + (list); //przepisuje najpierw lista1, a po niej lista2 do lista3
list operator += (list); //dodaje do lista 1 lista2
list operator = (list); //przepisuje lista1 do lista2
};
template<class typeData> bool list<typeData>::ifEmpty()
{
return(head==NULL?1:0);
}
template<class typeData> class list<typeData>::record* list<typeData>::umpteenthRecord (int number)
{
int counter=0;
record* tmpptr;
tmpptr=head;
while(counter<number-1)
{
counter++;
tmpptr=tmpptr->nextRecord;
}
return tmpptr;
}
template<class typeData>list<typeData>::list()
{
head=NULL;
tail=NULL;
return;
}
/*template<class typeData>list<typeData>::list(list list1)
{
*this+=list1;
return;
}*/
template<class typeData>list<typeData>::~list()
{
while(head!=NULL)
deleteRecord(1);
delete head;
delete tail;
return;
}
template<class typeData>int list<typeData>::numberOfRecord()
{
record* tmpptr=head;
int counter=0;
while(tmpptr!=NULL)
{
counter++;
tmpptr=tmpptr->nextRecord;
}
return counter;
}
template<class typeData> void list<typeData>::addRecord (typeData dataIn)
{
record* tmpptr=new record;
tmpptr->data=dataIn;
tmpptr->nextRecord=NULL;
if(ifEmpty)
head=tmpptr;
else
tail->nextRecord=tmpptr;
tail=tmpptr;
return;
}
template<class typeData> void list<typeData>::displayAllRecord()
{
if(ifEmpty)
{
cout << "The list is empty";
return;
}
record* tmpptr=head;
while(tmpptr!=NULL)
{
cout <<tmpptr->data<<" ";
tmpptr=tmpptr->nextRecord;
}
return;
}
template<class typeData> void list<typeData>::displayRecord(int number)
{
cout << umpteenthRecord(number)->data;
return;
}
template<class typeData> void list<typeData>::deleteRecord(int number)
{
if(number==1)
{
record* tmpptr=head;
head=head->nextRecord;
delete tmpptr;
return;
}
if(number>1&&number<numberOfRecord())
{
record* tmpptr=umpteenthRecord(number-1);
record* tmpptr2=umpteenthRecord(number);
tmpptr->nextRecord=tmpptr->nextRecord;
delete tmpptr2;
return;
}
if(number==numberOfRecord())
{
record* tmpptr=umpteenthRecord(number-1);
delete tmpptr->nextRecord;
tmpptr->nextRecord=NULL;
tail=tmpptr;
return;
}
}
template<class typeData> void list<typeData>::insertRecord (typeData dataIn, int number)
{
if(number==1)
{
record* tmpptr = new record;
tmpptr->data=dataIn;
tmpptr->nextRecord=head->nextRecord;
head=tmpptr;
return;
}
if(number>1&&number<numberOfRecord())
{
record* tmpptr=umpteenthRecord(number-1);
record* tmpptr2=new record;
tmpptr2->data=dataIn;
tmpptr2->nextRecord=tmpptr->nextRecord;
tmpptr->nextRecord=tmpptr2;
return;
}
if(number==numberOfRecord())
{
addRecord(dataIn);
return;
}
}
template<class typeData> void list<typeData>::swapRecord (int number1, int number2)
{
if(number1>number2)
{
int tmp=number2;
number2=number1;
number1=tmp;
}
if(number1<number2)
{
insertRecord(umpteenthRecord(number2)->data, number1);
insertRecord(umpteenthRecord(number1+1)->data, number2+1);
deleteRecord(number1+1);
deleteRecord(number2+1);
return;
}
}
template<class typeData> list<typeData> list<typeData>::operator--(int)
{
*this.deleteRecord(numberOfRecord);
return *this;
}
template<class typeData> list<typeData> list<typeData>::operator + (list list2)
{
list* list3 = new list;
record* tmpptr=this->head;
while(tmpptr!=NULL)
{
list3->addRecord(tmpptr->data);
tmpptr=tmpptr->nextRecord;
}
tmpptr=list2.head;
while(tmpptr!=NULL)
{
list3->addRecord(tmpptr->data);
tmpptr=tmpptr->nextRecord;
}
return list3;
}
template<class typeData> list<typeData> list<typeData>::operator = (list list1)
{
while(this->head!=NULL)
--(*this);
record* tmpptr=list1.head;
while(tmpptr!=NULL)
{
this->addRecord(tmpptr->data);
tmpptr=tmpptr->nextRecord;
}
return *this;
}
template<class typeData> list<typeData> list<typeData>::operator += (list list1)
{
record* tmpptr=list1->head;
while(tmpptr!=NULL)
{
this->addRecord(tmpptr->data);
tmpptr=tmpptr->nextRecord;
}
return *this;
}
int main()
{
int size=10;
list<int>* list1=new list<int>;
for(int i=0; i<size; i++)
list1->addRecord(i);
list1->displayRecord;
}
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:
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.
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
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).
ifEmpty to adres funkcji (kompilator pokazuje jej typ bool (list<int>::)()). W tym miejscu chciałeś ją raczej wywołać, czyli z nawiasami:
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:
list(const list& l); // albo - jak wolisz taki zapis: list(list const& l);
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.
Tak, list* to inny typ niż list. Ale jeśli masz dwa wskaźniki, możesz listy, na które one wskazują dodać tak:
więc nie jest konieczne definiowanie nowego operatora.
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 .
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
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
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
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.
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:
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 (
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=:
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
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 .