Język C++ jest wieloparadygmatowym językiem programowania. Stworzony w latach osiemdziesiątych XX wieku przez Bjarne Stroustrupa



Podobne dokumenty
#include <iostream> #include <string> using namespace std; auto main() -> int { string s1; // pusty string. Klasa std::string

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Podstawy programowania skrót z wykładów:

Wstęp do programowania. Wykład 1

Język C++ Różnice między C a C++

Część 4 życie programu

Język C++ jest wieloparadygmatowym językiem programowania. Stworzony w latach osiemdziesiątych XX wieku przez Bjarne Stroustrupa

Zmienne, stałe i operatory

1 Podstawy c++ w pigułce.

Operatory na rzecz typu TString

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Programowanie C++ Wykład 2 - podstawy języka C++ dr inż. Jakub Możaryn. Warszawa, Instytut Automatyki i Robotyki

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

C++ wprowadzanie zmiennych

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

Wykład VII. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Wstęp do programowania obiektowego. Przekazywanie parametrów do funkcji w C++ Metody i funkcje operatorowe Strumienie: standardowe, plikowe, napisowe

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227 WYKŁAD 7 WSTĘP DO INFORMATYKI

Język C zajęcia nr 11. Funkcje

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

I - Microsoft Visual Studio C++

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

Podstawowe elementy proceduralne w C++ Program i wyjście. Zmienne i arytmetyka. Wskaźniki i tablice. Testy i pętle. Funkcje.

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Programowanie w C++ Wykład 1. Katarzyna Grzelak. 26 luty K.Grzelak (Wykład 1) Programowanie w C++ 1 / 28

Lab 9 Podstawy Programowania

Programowanie strukturalne i obiektowe

2 Przygotował: mgr inż. Maciej Lasota

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Projektowanie klas c.d. Projektowanie klas przykład

Symfonia C++ standard : programowanie w języku C++ orientowane obiektowo. T. 1 / Jerzy Grębosz. Wyd. 3 C - popr. Kraków, 2015.

Paostwowa Wyższa Szkoła Zawodowa w Płocku Dariusz Wardowski

Język ludzki kod maszynowy

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Wykład II Tablice (wstęp) Przykłady algorytmów Wstęp do języka C/C++

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 5. Karol Tarnowski A-1 p.

Temat 1: Podstawowe pojęcia: program, kompilacja, kod

EGZAMIN PROGRAMOWANIE II (10 czerwca 2010) pytania i odpowiedzi

1 Podstawy c++ w pigułce.

Wykład 8: klasy cz. 4

( wykł. dr Marek Piasecki )

Wykład. Materiały bazują częściowo na slajdach Marata Dukhana

Języki i metodyka programowania. Wprowadzenie do języka C

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Wstęp do programowania

Podstawy programowania. Wykład: 5. Instrukcje sterujące c.d. Stałe, Typy zmiennych c.d. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

STL: Lekcja 1&2. Filozofia STL

Języki i metodyka programowania. Typy, operatory, wyrażenia. Wejście i wyjście.

ZASADY PROGRAMOWANIA KOMPUTERÓW

Wiadomości wstępne Środowisko programistyczne Najważniejsze różnice C/C++ vs Java

Szablony klas, zastosowanie szablonów w programach

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 5

Programowanie w języku C++

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 15 kwietnia K.Grzelak (Wykład 8) Programowanie w C++ 1 / 33

ROZDZIAŁ 2. Operatory

Wykład 5: Klasy cz. 3

Laboratorium 1 Temat: Przygotowanie środowiska programistycznego. Poznanie edytora. Kompilacja i uruchomienie prostych programów przykładowych.

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Szablony funkcji i szablony klas

EGZAMIN 2 (14 WRZEŚNIA 2015) JĘZYK C++

Programowanie obiektowe w C++ Wykład 1

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Programowanie w C++ Wykład 9. Katarzyna Grzelak. 14 maja K.Grzelak (Wykład 9) Programowanie w C++ 1 / 30

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Wstęp do programowania

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Języki i techniki programowania Ćwiczenia 2

Programowanie w C++ Wykład 1. Katarzyna Grzelak. 25 luty K.Grzelak (Wykład 1) Programowanie w C++ 1 / 38

Tablice. Monika Wrzosek (IM UG) Podstawy Programowania 96 / 119

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Podstawy Programowania

Język C++ wykład VIII

Programowanie i struktury danych

Programowanie w języku C++

Funkcje. Spotkanie 5. Tworzenie i używanie funkcji. Przekazywanie argumentów do funkcji. Domyślne wartości argumentów

/* dołączenie pliku nagłówkowego zawierającego deklaracje symboli dla wykorzystywanego mikrokontrolera */ #include <aduc834.h>

Programowanie Obiektowo Zorientowane w języku c++ Przestrzenie nazw

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Struktura programu. Projekty złożone składają się zwykłe z różnych plików. Zawartość każdego pliku programista wyznacza zgodnie z jego przeznaczeniem.

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Słowa kluczowe i nazwy

Język C++ zajęcia nr 2

Przeciążenie (przeładowanie nazw) funkcji

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Wstęp do informatyki- wykład 7

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Spis treści WSTĘP CZĘŚĆ I. PASCAL WPROWADZENIE DO PROGRAMOWANIA STRUKTURALNEGO. Rozdział 1. Wybór i instalacja kompilatora języka Pascal

Zajęcia nr 1 Podstawy programowania. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Instrukcja do pracowni specjalistycznej z przedmiotu. Obiektowe programowanie aplikacji

Transkrypt:

Język C++ historia, współczesność, przyszłość Język C++ jest wieloparadygmatowym językiem programowania. Stworzony w latach osiemdziesiątych XX wieku przez Bjarne Stroustrupa C++98 ISO/IEC 14882:1998 C++03 ISO/IEC 14882:2003 C++11 ISO/IEC 14882:2011 C++14 ISO/IEC 14882:2014

C++11/14 dlaczego standard jest ważny? Standard to brak zależności od rodzaju kompilatora systemu operacyjnego CPU Standard odwołuje się / opisuje działanie abstrakcyjnej maszyny. Kompilator ma za zadanie zrealizować ten opis na konkretnym sprzęcie. C++98/C++03 abstrakcyjna maszyna była jednowątkowa C++11/C++14 abstrakcyjna maszyna zaprojektowana jako wielowątkowa model pamięci (organizacja pamięci i sposoby dostępu do pamięci) na niskim poziomie gwarantowane operacje atomowe w określonej kolejności

Główne cechy języka: C++ podstawowe cechy język kompilowalny, ogólnego przeznaczenia, określany jako język średniego poziomu dokument opisujący standard C++11 ma 1338 stron silna (statyczna) kontrola typów podczas kompilacji: pewna forma weryfikacji poprawności kodu, pozwalająca na wczesne wykrycie błędów lub niezamierzonego działania język swobodnego formatu, rozmieszczenie znaków na stronie nie ma znaczenia, ale każda instrukcja musi być zakończona średnikiem ; C++ nie wspiera własności specyficznych dla danej platformy lub niebędących własnościami ogólnego przeznaczenia

C++ style programowania C++ nie narzuca żadnego stylu, daje programiście możliwość wyboru. programowanie proceduralne: organizowanie kodu w postaci procedur, wykonujących ściśle określone operacje, dane nie powiązane z procedurami, jako parametry wywołania procedur programowanie obiektowe: zbiór obiektów komunikujących się pomiędzy sobą w celu wykonywania zadań, obiekt to element łączący stan (dane) i zachowanie (metody) programowanie funkcjami wirtualnymi programowanie uogólnione: kod programu bez wcześniejszej znajomości typów danych, szukanie i systematyka abstrakcyjnych reprezentacji efektywnych algorytmów, struktur danych i innych elementów programowych programowanie szablonami

C++ literatura (1) kanon literatury International Standard (można kupić cena zaporowa) ISO/IEC 14882:2011(E) Bjarne Stroustrup Język C++ Programowanie. Teoria i praktyka z wykorzystaniem C++ (Wyd. II popr.) C++11 Final Document www.open-std.org/jtc1/sc22/wg21/ N3290 (2011-04-11)

C++ literatura (2) stare ale jare (niestety, nie C++11) Bruce Eckel Thinking in C++, vol. I i II (po angielsku on-line) Jerzy Grębosz Symfonia C++ Standard (C++03) Pasja C++ (niestety stare)

C++ literatura (3) Siddhartha Rao C++. Dla każdego. Wydanie VII Nicholas A. Solter, Scott J. Kleper C++ Zaawansowane programowanie Stephen Prata Język C++. Szkoła programowania. Wydanie VI Wydanie III po angielsku

C++ literatura (4) Anthony Williams Język C++ i przetwarzanie współbieżne w akcji D. Ryan Stephens C++ Receptury (O Reilly) Nicolai M. Josuttis C++. Biblioteka standardowa. Podręcznik programisty David Vandevoorde, Nicolai M. Josuttis C++ szablony. Vademecum profesjonalisty Wydanie II po angielsku Aktualizacja w roku 2015

C++ literatura (5) Scott Meyers Herb Sutter C++ 50 efektywnych sposobów na udoskonalenie Twoich programów Język C++ bardziej efektywny STL w praktyce: 50 sposobów efektywnego wykorzystania KURSY DOSTĘPNE ON-LINE Karol Xion Kuczmarski Kurs C++ (Megatutorial) Sektor van Skijlen C++ bez cholesterolu Piotr Białas, Wojciech Palacz Zaawansowane C++ pl.wikibooks.org/wiki/c++ niekompletny jeszcze Frank B. Brokken C++ Annotations Ver. 9.8.x Wyjątkowy język C++ 47 łamigłówek Wyjątkowy język C++ 40 nowych łamigłówek Niezwykły styl języka C++ 40 nowych łamigłówek Język C++ Standardy kodowania 101 zasad (współautor: Andrei Alexandrescu)

Używanie gcc zamiast g++ Twój edytor i kompilator GCC (GNU Compiler Collection) kompiluje różne języki (C, C++, Objective-C, Objective-C++, Java, Fortran, Ada). gcc rozpoznaje kod źródłowy C++ po rozszerzeniach:.c,.cc,.cpp,.cpp,.c++,.cp,.cxx gcc nie konsoliduje skompilowanego kodu z biblioteką standardową c++ jeśli użyjesz gcc to będziesz musiał podać ręcznie ścieżkę do plików nagłówkowych oraz do biblioteki standardowej! Nie utrudniajmy sobie życia i używajmy g++ W przypadku Dev-C++ chodzi o zapisanie pliku źródłowego z rozszerzeniem.cpp

Kompilator i konsolidator: gcc.gnu.org (g++) Kompilator g++ tłumaczy kod źródłowy na język assembler lub rozkazy komputera Konsolidator g++ (linker) dopasowuje odwołania symboli do ich definicji Najnowsze wersje wspierają standard C++11 (wersja 4.9.1), a także C++14 Program zarządzający bibliotekami ar, ranlib (archiver, librarian) biblioteki statyczne.a (oraz pliki obiektowe.o) wymagane do skonsolidowania pliku wykonywalnego podczas linkowania biblioteki dynamiczne.so zawierają kod maszynowy ładowany do pamięci po uruchomieniu wykorzystującego je programu, muszą więc być dostępne podczas uruchamiania programu Warto zapoznać się z narzędziem make

Kompilator wsparcie nowego standardu Kompilowanie kodu według nowego standardu Wsparcie kompilatora dla standardu C++11 (C++14) wymaga dodatkowej opcji (flagi): g++ -std=c++11 g++ -std=c++14 Przykładowo (linux): Program jest w katalogu /usr/bin Pliki nagłówkowe w katalogu /usr/include/c++/4.8 Biblioteki w katalogu /usr/lib/gcc/i486-linux-gnu/4.8 Na pracowni komputerowej kompilator g++ 5.0.1 jest dostępny tylko w nowych instalacjach (Dev-C++ 5.11)

Kompilowanie i linkowanie (konsolidacja) Prosty program o nazwie myprog z pliku prog1.cc g++ -std=c++11 -o myprog prog1.cc // to samo: g++ -std=c++11 prog1.cc -o myprog Plik obiektowy (bez konsolidacji do programu) g++ -std=c++11 -c -o prog1.o prog1.cc Konsolidacja do programu wykonalnego g++ -std=c++11 -o myprog prog1.o Uruchomienie programu (linux)./myprog gdzie. (kropka) oznacza pełną nazwę ścieżki, chyba że ścieżka do katalogu z programem jest w zmiennej PATH

Kompilatory kilka uwag Można mieć zainstalowane kilka wersji g++ oraz biblioteki standardowej. Napisz: g++ i postukaj tab (pokażą się wszystkie programy zaczynające się na g++) Zwykle g++ to link symboliczny do jednej z wersji. Sprawdzenie wersji: g++ -v // lub: g++ --version Inne kompilatory warte uwagi: clang ver. 3.7, Intel C++ ver. 15, Microsoft Visual Studio C++ 2015 Kompilowanie plików nagłówkowych niektóre kompilatory pozwalają na prekompilowanie plików nagłówkowych, w dużych projektach znacznie może to przyspieszyć proces kompilacji g++ kompilując plik.h tworzy plik z rozszerzeniem.h.gch prekompilowany plik jeśli znaleziony, może być brany jako pierwszy przed plikiem.h student robi to zwykle przez pomyłkę, niepotrzebnie umieszczając na liście plików źródłowych do kompilowania również pliki.h (może to prowadzić do zaskakujących problemów w stylu edytuję plik.h i nic się nie dzieje )

Pierwszy program Program wymaga napisania funkcji: auto main() -> int { } lub int main() { } W kodzie tylko jedna funkcja main. Uwaga: dawniej (przed rokiem 1998) dopuszczano postać funkcji zwracającej void (tzn. nic ), teraz musi zwracać int. Paradoksalnie, jest to jedyna funkcja, w której (skoro coś zwraca) nie trzeba pisać instrukcji return. Można (ale nie trzeba) jawnie napisać: int main() { return 0; }

Pierwszy program: średnik Uważaj na średnik są miejsca, w których średnik jest konieczny, a są, w których jest zbędny lub nieprawidłowy. int fun3() { return 2; } ; Nie stawiaj średnika za nawiasem kończącym definicję funkcji jest niepotrzebny! #include mojplik.h ; #define FLAGA ; Nie stawiaj średnika na końcu dyrektywy preprocesora to jest błąd! class Klasa { } ; Średnik konieczny jest na końcu definicji klasy! for (int i=0; i<10; ++i); // tutaj instrukcja, którą może ktoś // zamierzał wykonać 10 razy Przykład kompilującego się kodu, w którym przez pomyłkę mamy niezamierzone działanie

Pierwszy program który coś robi #include <iostream> using namespace std; int main() { cout << "I am Jan B. " << "za zycia napisalem ponad " << 100 << " ksiazek!\n"; cout << "A Ty ile napisales: "; int liczba; cin >> liczba; if (liczba < 100) cout << "\n Tak malo!"; return 0; // return EXIT_SUCCESS } #include <iostream.h> // NIE UŻYWAĆ czasem implementowane tak: #include <iostream> using namespace std; w nowych kompilatorach ostrzeżenia, a nawet może się nie skompilować Można też wybrać konkretne using std::cout; using std::cin; Albo podczas wywołania pisać std::cout oraz std::cin itd. Biblioteki z C: #include <cstdlib> #include <cstdio> #include <cassert>

Zajrzyjmy w głąb <iostream> #include <ostream> #include <istream> extern istream cin; extern ostream cout; extern ostream cerr; extern ostream clog; Deklaracje obiektów odpowiadających za pracę na strumieniu wejście / wyjście. Obiekty konstruowane przed main() Hierarchia klas odpowiedzialnych za pracę na strumieniach cin obiekt odpowiedzialny za obsługę standardowego strumienia wejściowego (zwykle powiązanego z klawiaturą), wywołanie powoduje opróżnienie buforu cout cout obiekt odpowiedzialny za strumień wyjściowy (zwykle powiązany z monitorem) cerr obiekt standardowego strumienia komunikatów o błędach, powiązany przez system z monitorem, strumień niebuforowany clog obiekt wyprowadzany standardowo tak jak cerr, strumień buforowany

Operatory, manipulatory, znaki specjalne operator<< oraz operator>> są to operatory przesunięcia bitowego, jednak dla obiektów strumienia są przeciążone i stają się operatorami wejścia/wyjścia Co lepiej na końcu: std::endl czy \n? MANIPULATORY ( tak naprawdę funkcje) endl dodaje do buforu znak \n oraz wykonuje flush opróżnienie buforu ends wkłada znak kończący łańcuch znakowy, czyli symbol zerowy \0 flush opróżnia bufor ws czyta i ignoruje białe znaki Kod dla guru (przykład): ostream& ostream::operator<< ( ostream& (*op) (ostream&) ) { return (*op) (*this); } std::ostream& std::endl (std::ostream& s) { s.put('\n'); } s.flush(); return s; Można tak: std::cout << std::endl; lub tak: std::endl ( std::cout ); ZNAKI SPECJALNE (stałe znakowe) \n nowa linia \r powrót do początku linii \t pozioma tabulacja \a alarm dźwiękowy \0 symbol zerowy (koniec łańcucha) MNIEJ UŻYWANE \v pionowa tabulacja \b powrót o jedną pozycję \f nowa strona (drukarka) \? znak zapytania KONIECZNE W ŁAŃCUCHU ZNAKOWYM \\ lewy ukośnik \ apostrof \ cudzysłów

Deklaracja co to jest? Deklaracja to wprowadzenie w danej jednostce translacji (pliku) nazwy (lub nazw), albo redeklaracja nazw wprowadzonych poprzednimi deklaracjami. Deklaracje generalnie określają jak mają być rozumiane dane nazwy. Deklaracja może być też definicją, chyba że (i wtedy są to tylko deklaracje): deklarujemy funkcję bez definiowania jej ciała void fun( double d, short n, int ); nie ma definicji (ciała) funkcji, czyli części ujętej w nawiasy { } to jest to tylko deklaracja deklaracja poprzedzona jest specyfikatorem extern, w znaczeniu obiektu zdefiniowanego w innym pliku extern double d; deklarujemy, że w innym pliku będzie zdefiniowana zmienna typu double o nazwie d. UWAGA: jeśli użyjemy specyfikatora extern oraz inicjalizujemy zmienną, np. extern double d = 3.14; to oznacza to już definicję a nie deklarację! deklaracja z użyciem extern jako sposób konsolidacji (linkowania) kodu extern C int fun ( float ); extern C { /* tutaj lista deklaracj */ } gdy chcemy zlinkować z kodem z innego języka, musimy zadeklarować nazwy obiektów tam zdefiniowanych

Deklaracje (2) deklarujemy statyczną składową w definicji klasy class Foo { static int n; }; deklarujemy nazwę klasy (bez jej definiowania): deklarujemy (silny) typ wyliczeniowy (C++11) enum class EColor; enum struct EShape : char; Deklaracjami nazywamy również: deklarację z użyciem typedef zmienna statyczna w definicji klasy to dopiero jej deklaracja jak się później dowiemy, taką zmienną definiuje się dopiero poza ciałem klasy typedef int Calkowity, *PtrCalkowity; Calkowity n1; PtrCalkowity ptr1; deklarację użycia using lub dyrektywę using using std::cout; using namespace std; class Foo; klasa wyliczeniowa (albo silny typ wyliczeniowy ) pozwala na uprzednią deklarację wraz ze specyfikacją typy danych wyliczeniowych (typ musi być całkowity) n1 jest typu int, zaś ptr1 jest typu wskaźnik do int deklaracja użycia czegoś dyrektywa użycia którejś przestrzeni nazw

Definicje reguła jednej definicji (One Definition Rule) Jedna definicja żadna jednostka translacji (plik) nie może zawierać więcej niż jednej definicji jakiejkolwiek zmiennej, funkcji, klasy, typu wyliczeniowego lub szablonu. Definicja może się znajdować w programie, zewnętrznej bibliotece (standardowej, użytkownika). Definicja klasy konieczna jest w danym pliku, gdy typ klasy używany jest w sposób wymagający znajomości kompletnej definicji. One ring to rule them all, one ring to find them, One ring to bring them all and in the darkness bind them. class Foo; struct Foo* ptr1; Foo *ptr2; w tych przypadkach nie ma konieczności znajomości definicji klasy, wystarczy deklaracja jej nazwy Czasami definicja może się powtórzyć w różnych plikach. Dotyczy to klasy, typu wyliczeniowego, funkcji inline (extern inline), szablonu klasy, statycznej zmiennej oraz metody składowej w szablonie klasy, niestatycznego szablonu funkcji, specjalizacji szablonu (C++11 3.2.5) wszystko to pod pewnymi warunkami! (zasadniczo jest to powtórzenie tego samego kodu z ew. dopisanymi wartościami domyślnymi funkcji)

Organizacja kodu (header guard) DEKLARACJE zmienny, funkcji nie-inline lub DEFINICJE funkcji inline DEFINICJE klas, DEFINICJE szablonów plik nagłówkowy (.h ) Cytat z Megatutorial-u Karola Kuczmarskiego (Państwa niewiele starszego kolegi ) Dyrektywa #include jest głupia jak cały preprocesor. Wielokrotne włączenie tego samego nagłówka (#include) wielokrotna definicja pogwałcenie reguły ODR błąd! Aby temu zapobiec, w plikach nagłówkowych zawsze korzystamy z dyrektyw preprocesora (blokada, tzw. header guard) #ifndef FIGURA_H #define FIGURA_H // tutaj cała zawartość pliku #endif // FIGURA_H DEFINICJE zmiennych, funkcji, metod klas plik źródłowy (.cc ) Nie korzystamy z dyrektywy #pragma once pierwotnie działała tylko w niektórych kompilatorach, np. Visual C++ pragma nie jest polecana przez twórców gcc jako dyrektywa z definicji zależna od implementacji, choć działa w g++ od ver. 3.4

Pułapki myślenia o blokadach header guard (czyli zestaw #ifndef #define #endif) nie chroni przed problemem podczas konsolidacji plików, jeśli w pliku nagłówkowym, włączonym do tych różnych plików, zdefiniowaliśmy coś, co pogwałci ODR. Skompiluje się, ale linker zgłosi multiple definition header guard chroni jeden dany plik źródłowy przed wielokrotnym włączeniem (i kompilacją) tego samego pliku nagłówkowego, wielokrotne włączenie może nastąpić również nie wprost, przez inne włączane pliki plik h1.h plik h2.h plik test.cc plik main.cc #ifndef H1_H #define H1_H void fun() { } #endif #ifndef H2_H #define H2_H #include h1.h // coś jeszcze #endif #include h2.h void fun2() { fun(); } #include h1.h #include h2.h int main() { fun(); } g++ main.cc test.cc o prog /tmp/cccopu18.o: In function `fun()': test.cc:(.text+0x0): multiple definition of `fun()' /tmp/ccgypjjh.o:main.cc:(.text+0x0): first defined here collect2: ld returned 1 exit status

Namespace przestrzeń nazw Namespace przestrzeń nazw obszar posiadający swoją nazwę lub bez nazwy, służący do deklaracji. Nazwy zdeklarowane wewnątrz namespace są zamknięte odwołanie do nich możliwe jest poprzez nazwę przestrzeni nazw lub odpowiednią deklarację / dyrektywę użycia. Namespaces służą unikaniu kolizji nazewniczych. Definicja namespace może być rozbita na wiele części i może się znajdować nawet w różnych plikach zatem sami możemy nawet coś dodać do danej przestrzeni nazw nawet tej zdefiniowanej w jakiejś zewnętrznej bibliotece. inline opcjonalnie namespace nazwa opcjonalnie { /* zawartość */ } inline nowość w C++11 w celu wspierania różnych wersji danej biblioteki, czyli ewolucji kodu, ze wskazaniem na bieżącą (np. najnowszą) wersję Cała biblioteka standardowa jest zamknięta w przestrzeni o nazwie std Konieczna zatem jest dyrektywa użycia: using namespace std; ale nigdy w pliku nagłówkowym! W plikach nagłówkowych raczej piszemy std::obiekt, ewentualnie stosujemy deklarację użycia, np. using std::cout

Namespace reguły istnienia, tworzenia, użycia Namespace tym samym tylko w przestrzeni globalnej albo (zagnieżdżone) wewnątrz innej przestrzeni namespace nie można zagnieździć (definiować) wewnątrz definicji funkcji (również main) ani nie można zdefiniować wewnątrz klasy using deklaracja/dyrektywa użycia czegoś z przestrzeni nazw void f(); namespace A { void g(); namespace X { } using ::f; // globalne f using A::g; // g z A } void h() { X::f(); // woła ::f X::g(); // woła A::g } namespace A { int i; } namespace A1 { using A::i; using A::i; // tu ok, można powtórzyć } void f() { using A::i; using A::i; // a tu błąd! }

Namespace użycie, zagnieżdżenia namespace A { void f(int); } using A::f; // f jest synonimem A::f; // czyli A::f(int) namespace A { void f(char); } void foo() { f( a ); // woła f(int) } // pomimo że f(char) istnieje void bar() { using A::f; // f jest synonimem A::f; // zarówno A::f(int) i A::f(char) f( a ); // woła f(char) } Można dodawać kolejną zawartość przestrzeni nazw (nawet w kolejnych plikach) ale musi być to zrobione w tym samym zasięgu znaczeniowym. Bezpośrednią nadrzędną przestrzenią nazw dla danej deklaracji jest ta przestrzeń, w której deklaracja po raz pierwszy się pojawia. Później definicja (danej deklaracji) może być w innym zakresie, ale z precyzyjną specyfikacją co do pierwotnego wystąpienia deklaracji. namespace A { namespace B { void f(); class C { void m(); }; } void B::f() { extern void h(); // to jest deklaracja A::B::h } void B::C::m() { // definicja metody m() } } Jeśli w dwóch różnych przestrzeniach te same nazwy konflikt w momencie użycia

Typy danych oraz specyfikatory Podstawowe typy wbudowane: char, int, float, double wchar_t rozszerzony typ znakowy (wielkość zależna od implementacji) char16_t i char32_t do reprezentacji znaków standardu Unicode Specyfikatory (rozszerzają lub zawężają, ze znakiem lub bez znaku) short long, signed unsigned short int (inaczej: short), int, long int (inaczej: long), long long int (inaczej: long long) oficjalnie w C++11 ze wzg. na zgodność z C99 float, double, long double Typ bool true odpowiednik wartości całkowitej 1 (dwa stany logiczne: true, false to są stałe) false - odpowiednik wartości całkowitej 0 nie nadawać stanu logicznego za pomocą operacji arytmetycznej (+ lub -) niejawna konwersja typów operatory: &&! < > <= >= ==!= komendy sterujące: if, for, while, do,? : kompilator przekształca int w bool

typedef, using typedef synonim typu istniejącego (nie żadna nowa definicja), najczęściej używany do uproszczenia zapisu (wiele razy w bibliotece standardowej) np. typedef basic_fstream<char> fstream; // w nagłówku fstream typedef basic_string<char> string; // w nagłówku string using może być użyte zamiennie jako typedef typedef std::vector<int>::iterator It; using It = std::vector<int>::iterator; // te dwie linie robią to samo typedef const char* (*Fptr)( double ); using Fptr = const char* (*) (double); // wskaźnik do funkcji, też to samo co wyżej

Zasięg zmiennych, przesłanianie przykład int a = 1; // zmienna globalna zakomentowanie globalnej zmiennej i próba namespace mojeklocki { int a = 7; int b = 8; } odwołania się do niej spowoduje błąd kompilacji namespace { int c = 99; // int a = 3; spowodowałoby kolizję ze zmienną globalną } int main() { int a = 2; { int a = 3, c = 100; for (int i=0; i<10; ++i); // nic nie robi, bo uwaga - gdzie kończy się instrukcja cout << "a lokalne = "<< a <<endl; // 3 using namespace mojeklocki; cout << "a lokalne = "<< a <<endl; // 3 cout << "a z mojeklocki = "<< mojeklocki::a <<endl; // 7 cout << "b z mojeklocki = "<< b <<endl; // 8 int b = 12; cout << "b lokalne = "<< b <<endl; // 12 cout << "a nielokalne = "<< ::a <<endl; // 1 cout << "c z nienazwanej przestrzeni " << ::c << endl; // 99, to też jest zmienna globalna } cout << "a lokalne = "<< a <<endl; // 2 }

Rodzaje obiektów i ich cechy (static global) Obiekt globalny istnieje przez cały czas wykonania programu domyślnie łączony zewnętrznie deklaracja extern można użyć w innych plikach źródłowych deklaracją static zasięg można ograniczyć do pliku wystąpienia definicji (bez kolizji nazw) lepszy sposób na łączenie wewnętrzne użycie nienazwanej przestrzeni nazw (namespace) jeśli const, to zachowuje się jak static (chyba że extern const) domyślnie inicjowany wartością zera Statyczny obiekt lokalny istnieje przez cały czas wykonania programu deklaracja z modyfikatorem static wartość takiego obiektu przetrwa między kolejnymi wywołaniami funkcji zasięg ograniczony jest do bieżącego kontekstu w klasie jeden egzemplarz dla wszystkich obiektów klasy domyślnie inicjowany wartością zera pamięć statyczna

Obiekty globalne i statyczne (globalne) przykłady // w przestrzeni nazw lub przestrzeni globalnej int i; // domyślnie łączenie zewnętrzne const int ci = 0; // domyślnie globalny const jest static (łączony wewnętrznie) extern const int eci; // jawna deklaracja łączenia zewnętrznego static int si; // jawnie static // podobnie funkcje uwaga nie ma globalnych funkcji stałych (const) int foo(); // domyślnie łączenie zewnętrzne static int bar(); // jawna deklaracja static // nienazwana przestrzeń nazw jako polecany sposób na ograniczenie zakresu // widzialności nazw do danej jednostki translacji namespace { int i; // mimo łączenia zewnętrznego niedostępne // w innych jednostkach translacji class niewidoczna_dla_innych { }; }

Rodzaje obiektów i ich cechy (stack, heap) Obiekt automatyczny obiekt lokalny przydział pamięci następuje automatycznie w chwili wywołania funkcji czas trwania obiektu kończy się wraz z zakończeniem bloku, w którym został zaalokowany zasięg ograniczony jest do bieżącego kontekstu należy uważać na wskaźniki i referencje do obiektów lokalnych obiekt domyślnie nie jest inicjowany Obiekt z czasem trwania określanym przez programistę obiekt z pamięcią przydzielaną dynamicznie (operator new) czas życia do usunięcia operatorem delete obiekt bez nazwy identyfikowany pośrednio przez wskaźnik zawieszony wskaźnik - wskazujący na nieobsługiwany obszar pamięci (wskaźnik zwisający) wyciek pamięci - obszar pamięci przydzielany dynamicznie na który nie wskazuje żaden wskaźnik stos sterta

Stałe (const) a preprocesor za pomocą preprocesora #define PI 3.1415 od miejsca zdefiniowana do końca pliku modyfikator const const float pi = 3.1415; zasięg taki jak zasięg zmiennej, typ musi być określony, stała musi być zainicjalizowana stałej zdefiniowanej za pomocą preprocesora nie można śledzić bo polega na zamianie jednego symbolu na np. podaną wartość, zdecydowanie definiujmy stałe jako zmienne danego typu preprocesor można czasem użyć jako sprytnej makrodefinicji, np. wypisywania kontrolnego zmiennych (za Bruce Eckelem): #define PRINT (STR, VAR) cout << STR = << VAR << endl #define PR (x) cout << #x = << x << \n wtedy gdzieś w kodzie: PRINT( wartosc, a );

auto dedukcja typu (C++11) auto dawniej oznaczało tylko zmienną lokalną (automatyczną) dedukcji typu w oparciu o typ inicjalizatora lub typu zwracanego przez funkcję auto i = 7; // typ int auto x = wyrażenie // x będzie typu zwracanego przez wyrażenie dedukcja odbywa się tak jak w szablonach, z wyjątkiem rozpoznawania listy { a, b, c }, którą auto widzi jako std::initializer_list<t> (gdzie T to typ a, b, c) template<class T> int whatever(t t) { T x; // równoważne do auto x poza szablonem };

auto zastosowania ( C++11 ) przykłady auto a = 7; // a jest typu int const auto *ptr = &a, b = 5; // ptr typu const int*, b typu const int static auto d = 3.14; // d typu double auto x = { 1, 2, 3 }; // x typu std::initializer_list<int> działa również z operatorem new new auto(1); // alokowanym typem jest int auto z = new auto( a ); // alokowanym typem jest char, z jest typu char* szczególnie wygodne do dedukcji typów iteratorów for( auto i = m.begin(); i!= m.end(); ++i ) // niech m jest typu map<int,string> const auto& y = m; // y jest typu const std::map<int, std::string>& niestety, wewnątrz wyrażeń lambda auto nie działa

auto zastosowania ( C++11 ) zmienne zadeklarowane za pomocą auto są nadal wielkościami statycznymi, stąd niemożliwe jest: void fun( auto arg ) { } // źle: autodedukcja typu argumentu niemożliwa class Foo { auto m = 1; // źle: autodedukcja typu zwykłej składowej klasy niemożliwa // bo np. auto m = f(); wprowadzałoby spory problem w szukaniu // właściwej interpretacji tego czym jest f() }; auto tablica[5]; // źle: autodedukcja typu z którego zbudowana jest tablica możliwe jest class Foo { static const auto n = 0; // static tak }; uwaga auto s = hello world ; // jest typu const char* auto& s = hello world ; // jest typu referencja do const char[12] czyli tablicy

auto nowe metody w kontenerach, nowa pętla for ( C++11 ) W kontekście auto przydatne są nowe metody kontenerów: zwracają jawnie stałe iteratory: cbegin(), cend(), crbegin(), crend() auto ci = m.cbegin(); // ci typu std::map<int, std::string>::const_iterator Nowa składnia dla pętli for (tzw. range-based loop) vector<int> v { 1,2,3,4,5 }; for ( int i : v ) cout << i << endl; // i bezpośrednio każdym elementem wektora for ( auto i : v ) cout << i << endl; // to samo co powyżej for ( int& i : v ) cout << ++i; // może być też referencją i zmieniać zawartość! for ( auto& i : v ) cout << ++i; // to samo co powyżej for (const int i : v ) jakasmetoda( i ); // const/volatile też możliwe Można przebiegać po tablicach, kontenerach oraz dowolnych typach wyposażonych w iteratory, zwracane przez begin() i end() short tablica[5]; for ( auto& t : tablica ) { t = -t; } std::unordered_multiset<std::shared_ptr< T >> obj; for ( const auto& r : obj ) cout << r; // wypisuje wskaźnik // pytanie: czemu powyższe przez referencję? w C++11 nie ma problemu zagnieżdżonych nawiasów szablonów, nie trzeba rozdzielać spacją

auto referencje, modyfikatory ( C++11 ) Dla zmiennych nie zadeklarowanych wprost jako referencje, modyfikatory const/volatile na najwyższym poziomie są ignorowane: const vector<int> w; auto v1 = w; // v1 typu vector<int>, const zignorowane auto& v2 = w; // v2 typu const vector<int>& - ale jeśli przez referencję, to ok Tablice i nazwy funkcji redukują się do wskaźników: double tablica[5]; auto t1 = tablica; // t1 typu double* - to się nazywa array decay to pointer auto& t2 = tablica; // t2 typu double(&)[5] właściwy typ tylko jeśli przez referencję Jeżeli const/volatile nie na najwyższym poziomie, to zostają: auto i = 10; map<int, string> m; const auto *pi = &i; // pi jest typu const int* const auto& pm = m; // pm typu const map<int, string>& Za pomocą auto można deklarować więcej zmiennych w linii: auto zmienna = s, *ptr_zmienna = &s; // dedukcja typu inicjalizatora ten sam typ auto i = 3, d = 3.14; // błąd rożne typy inicjalizatorów

Operatory zwracają wartości na podstawie argumentów (argumentu) 18 poziomów ważności nie uczyć się wszystkiego! raczej używać nawiasów ( ) do czytelnego oddzielenia; niektóre zapamiętać operatory =, ++, -- dodatkowo zmieniają wartość argumentu (skutek uboczny, ang. side effect) operator przypisania = kopiuje p-wartość do l-wartości operatory matematyczne +, -, *, /, % można połączyć z operatorem przypisania +=, -=, *=, /=, %= zatem np. b %= 4; równoważne jest b = b % 4; operator % (modulo) tylko z liczbami typu całkowitego operatory relacji <, >, <=, >=, ==,!= zwracają wartość logiczną operatory logiczne && (iloczyn), (suma) operatory bitowe & (koniunkcja), (alternatywa), ^ (różnica symetryczna), ~ (bitowy operator negacji)

Operatory ciąg dalszy operatory przesunięć <<, >> jeśli po lewej liczba ze znakiem, to przesunięcie >> nie musi być operacja logiczną można łączyć z operatorem przypisania <<=, >>= bity przesunięte poza granicę są tracone operatory jednoargumentowe! (negacji logicznej), -, + operatory adresu &, wyłuskania *, -> i rzutowania rzutowanie: float a = 3.14; int b = (int)a; albo int b = int(a); operatory alokacji i usuwania: new, delete operator trójargumentowy? : co się stanie: int a = --b? b : (b = -10); // jeśli b=1, to a=-10 operator, zwraca wartość ostatniego z wyrażeń operator sizeof

Operatory tabela ważności Level Operator Description Grouping 1 :: scope Left-to-right 2 () []. -> ++ -- dynamic_cast static_cast reinterpret_cast const_cast typeid postfix Left-to-right ++ -- ~! sizeof new delete unary (prefix) 3 * & indirection and reference (pointers) Right-to-left + - unary sign operator 4 (type) type casting Right-to-left 5.* ->* pointer-to-member Left-to-right 6 * / % multiplicative Left-to-right 7 + - additive Left-to-right 8 << >> shift Left-to-right 9 < > <= >= relational Left-to-right 10 ==!= equality Left-to-right 11 & bitwise AND Left-to-right 12 ^ bitwise XOR Left-to-right 13 bitwise OR Left-to-right 14 && logical AND Left-to-right 15 logical OR Left-to-right 16?: conditional Right-to-left 17 = *= /= %= += -= >>= <<= &= ^= = assignment Right-to-left 18, comma Left-to-right

Operatory rzutowanie static_cast (konwersje niejawne, zawężające, zmieniające typ podczas kompilowania) int b = static_cast<int>(a); void *vp; int *num = static_cast<int*>(vp); const_cast (od typów z modyfikatowem const lub volatile do takich samych typów bez modyfikatora lub w drugą stronę) reinterpret_cast (pełna odpowiedzialność użytkownika, bez kontroli) dynamic_cast (rzutowanie "w dół" od abstrakcyjnego typu ogólnego do typu pochodnego zajdzie gdy operacja taka ma sens podczas wykonywania programu)

Typy złożone w c++ (litania) Poprzez złożone typy w języku c++ rozumie się: tablice obiektów danego typu funkcje, mające parametry danego typu, a zwracające void lub referencje lub obiekty danego typu wskaźniki do void lub obiektów, lub funkcji danego typu (włączając w to statyczne składniki klasy) referencje do obiektów lub funkcji (tzw. referencje lewej wartości i referencje prawej wartości) klasy, zawierające obiekty różnych typów oraz metody składowe, wraz z odpowiednimi ograniczeniami dostępu unie, które są rodzajem klasy, mogącej zawierać obiekt różnych typów, w różnych chwilach czasu typy wyliczeniowe, zawierające listę nazwanych stałych wartości wskaźniki do niestatycznych składowych klasy

enum typ wyliczeniowy konwencjonalny enum autonomiczny typ wyliczeniowy enum EPozycja { easystent, // 0 eadiunkt, // 1 eprofesor // 2 }; poważne mankamenty definiowanie zmiennych podobnie jak dla typu wbudowanego: EPozycja pracownik = easystent; możliwa niejawna konwersja z enum do int (może prowadzić do błędów, jeśli ktoś takiej konwersji nie chce) int a = easystent; // ok, konwersja! pracownik = 3; // bez rzutowania to jest błąd można też zadać wartość enum EPozycja { easystent = 5, eadiunkt = easystent + 2, eprofesor }; nie można robić inkrementacji: pracownik++; wyciekanie identyfikatorów do zewnętrznego zakresu względem miejsca zdefiniowania typu wyliczeniowego (np. enum zdefiniowany w przestrzeni globalnej eksportuje nazwy wszędzie kolizja nazw) sizeof( EPozycja ) =? pewnie 4 ale może być mniej nie można określić typu, na jakim zbudowane sa identyfikatory niemożliwa jest uprzedzająca deklaracja typu wyliczeniowego nienazwany enum ma sens właśnie przez to, że jego identyfikatory (z listy wyliczeniowej) są widziane na zewnątrz jako stałe (całkowite): enum { jeden = 1, dwa = 2, cztery = 4 };

enum silny typ wyliczeniowy (C++11) enum class nazwa { lista identyfikatorów }; zamiast class może być struct nazwy z listy wyliczeniowej nie wyciekają na zewnątrz nie następuje niejawna automatyczna konwersja na int enum Alert { green, yellow, election, red }; // standardowy, stary typ wyliczeniowy enum class Color { red, blue }; // nowy, silny, identyfikatory nieznane na zewnątrz enum struct TrafficLight { red, yellow, green }; // jak widać, nie koliduje z niczym Alert a = 7; // błąd: zwykły przypadek, nie ma konwersji z int na enum Color c = 7; // błąd: nie ma konwersji int->color int a2 = red; // ok: możliwa konwersja Alert::red->int int a3 = Alert::red; // błąd w C++98, ok w C++11 int a4 = blue; // błąd: blue nieznane w tym zakresie int a5 = Color::blue; // błąd: brak konwersji Color->int Color a6 = Color::blue; // ok można (opcjonalnie) zdefiniować typ (musi być całkowity), na którym zbudwany jest nowy enum (domyślnie int) i dzięki temu kontrolować wielkość enum class Color : char { red, blue }; // sizeof( Color ) taki sam jak sizeof( char ) możliwa jest deklaracja wyprzedzająca enum class Color : char; // deklaracja void foo(color* p); // teraz można już użyć

std::array tablica na miarę naszych czasów (C++11) łączy w sobie szybkość zwykłej C-tablicy z zaletami bycia kontenerem standardowym, czyli np. wie jaki ma rozmiar zawiera w sobie agregat; potrzebny nagłówek <array> wielkość i przetrzymywany typ trzeba z góry określić array<int, 3> a = { 1, 3, 7 }; // znak = opcjonalny, ale array<string, 2> b { { string("windows"), "Linux" } }; // powyższe zagnieżdżenie to inicjalizacja wewnętrznego agregatu // ten zapis nie jest przejawem uniwersalnej inicjalizacji poprzez // initializer_list<t> ponieważ array nie ma napisanego konstruktora można używać jak tablicę, albo odpytać daną pozycję metodą at(n), można zapytać o pierwszy front() i ostatni back() element metody empty() true gdy pusta czyli zrobiona tak: array<int, 0> a; size() rozmiar tablicy, max_size() hipotetyczny maksymalny rozmiar fill( const T& val ) wypełnienie wszystkich elementów wartością val

Referencje lewe ( T &, const T & ) Terminologia wprowadzająca l-value (lewa-wartość, l-wartość) coś, co można zmodyfikować, np. poprzez przypisanie (stoi po lewej stronie = ) r-value (prawa-wartość, p-wartość) coś, co stoi po prawej stronie operacji przypisania, często rozumiana jako niemodyfikowalne Referencja ( T & ) zwykła to jakby przezwisko na coś. Przezwisko nie może istnieć samo, bez powiązania z tym, co określa. Zatem referencja w momencie definicji musi być zainicjalizowana i nie może być przestawiona na coś innego. Niestała referencja ( T & ) może wskazywać na l-wartość. Stała referencja ( const T & lub T const & ) może wskazywać na l-warość i p-wartość. Nie istnieją: referencje do referencji tablice referencji wskaźniki do referencji W roli p-wartości może wystąpić obiekt, który nie musi być stały, jak i obiekt, którego nie wolno modyfikować (np. tymczasowy). Do tej pory nie można było rozróżnić, na co pokazuje stała referencja.

Nie istnieje: T & const Przykład Referencje zakazane cv, prawe ( T && ) (C++11) int a = 3; typedef int& RINT; const RINT aref = a; aref = 4; // teraz ma wartość 4 Kwalifikatory cv dla referencji, są niedopuszczalne. Wprowadzone przez typedef, albo argument szablonu, są zignorowane. pamiętajmy, że w c++ funkcjonuje pojęcie kwalifikatora cv, czyli const i/lub volatile, zatem to co piszemy o const, dotyczy też volatile wbrew pozorom, aref jest referencją l-wartości do int, a nie do const int napisanie const RINT tu oznacza nie const int& a próbę int& const coś takiego jest ignorowane albo innymi słowy: referencja musi być zadeklarowana z const, potem tego const nie można dołożyć na zasadzie zmiany typu deklarowanej referencji C++11 wprowadza referencję p-wartości ( && ), która ma służyć wskazywaniu na p-wartości, ale w rozumieniu takim, że można je modyfikować. Służyć to ma budowaniu semantyki (składni) przenoszenia. Pojawiają się dzięki temu konstruktory przenoszące (move constructors) i przenoszące operatory przypisania (move assignment operator). Więcej o tym w dalszej części wykładu.

Wskaźniki Wskaźniki zawierają adres i informację o typie (wyjątek: void*) T* zwykły wskaźnik (do typu T) const T*, T const* wskaźnik do stałego obiektu ( gwarancja nietykalności ) T* const wskaźnik stały ( gwarancja nieprzesuwalności ) const T* const, T const* const stały wskaźnik do stałego obiektu Ponownie uwaga na typedef: typedef int* pointer; typedef const pointer const_pointer; const_pointer jest typu int* const, a nie typu const int* const int ci = 10, *pc = &ci, *const cpc = pc, **ppc; int i, *p, *const cp = &i; pc wskaźnik na stały int, cpc stały wskaźnik na stały int, ppc wskaźnik do wskaźnika na stały int, p wskaźnik na int, cp stały wskaźnik na int

wskaźnik jak tablica Wskaźniki własności i arytmetyka int *vint = n; // wcześniej int n[10]; vint = &n[0]; // to samo * tu jako operator wyłuskania zmiennej ze wskaźnika można nimi operować jakby były tablicą, vint[2] to samo co n[2] vint to adres początku tablicy (pierwszego jej elementu) vint + 1 to adres drugiego elementu tablicy *(vint + 2) to zawartość wskazywana pod adresem vint + 2 operacje ++ lub - - są one inteligentne, tzn. na podstawie typu wskaźnika kompilator wie o ile bajtów ma przeskoczyć operacje + lub ograniczone można dodawać lub odejmować liczby całkowite (operacja inteligentna tzn. z wykorzystaniem wiedzy na temat wskazywanego typu) nie można dodawać dwóch wskaźników można odjąć dwa wskaźniki wynikiem jest liczba elementów danego typu znajdujących się pomiędzy nimi: int tab[] = { 1, 2, 5, 7 }; int *p1 = tab; int *p2 = &tab[3]; cout << p2 p1 << endl; // 3

Wskaźniki przykłady można dokonać zmian double f1 = 0.; const double pi = 3.14; double *vzmienna = &f1; const double *vstala1 = π const double *vstala2; // wskaźnik do stałego obiektu, jeszcze nie ustawiony vstala2 = vzmienna; *vzmienna = 25.; double * const vstalyzmienna = const_cast<double * const>( vstala1 ); vzmienna = vstalyzmienna; nie wszystkie zmiany możliwe nie można usunąć przydomka const z żadnego obiektu (można tylko rzutować) T * & - referencja do wskaźnika na typ T T & * - takie coś nie istnieje! przydaje się jako argument funkcji, wtedy wskaźnik argument, można wewnątrz funkcji przestawić na inny adres

Funkcje argumenty, wartości zwracane funkcja to podprogram funkcję identyfikuje jej nazwa, trzeba ją zadeklarować wyjątek to funkcja main definicja funkcji jest deklaracją, niemniej starajmy się deklarować wszystkie funkcje deklarację funkcji można zagnieździć w innej funkcji, ale definicji funkcji nie można zagnieżdżać w innej funkcji (nawet w main) funkcja może przyjmować dowolne parametry i zwracać dany typ lub nic nie zwracać (wtedy piszemy void) void fun(); // nic nie zwraca, ale można wewnątrz funkcji napisać // pustą instrukcję wyjścia return; int fun(string, int); // deklaracja nie wymaga podania nazw zmiennych, // ale dla czytelności kodu warto je pisać auto fun( double ) -> double; // nowa notacja C++11 ( -> trailing return type ) auto fun( char ); // możliwe w C++14 ale wtedy przed wywołaniem funkcja // musi być zdefiniowana, sama deklaracja nie wystarczy bo nieznany jest typ zwracany nigdy nie zwracamy adresu (referencji) do obiektu lokalnego (czas jego życia się skończył ) main zwraca zawsze int z przyczyn historycznych nie musimy wołać komendy return, kompilator nie napotkawszy jej wstawia na koniec bloku tej funkcji return 0;

Funkcje sposoby przekazania parametrów sposoby przekazywania parametrów do funkcji void fun(float f); // przez wartość, do wnętrza funkcji tworzona jest kopia // obiektu f, więc oryginału nie można zmienić (uszkodzić) void fun(const float f); // to nie ma sensu, tworzona jest kopia // i nawet tej kopii nie da się zmienić, czytelniej więc byłoby // jako argument używać float f, a w pierwsze linii funkcji np. // const float& argf = f; void fun(float& f); // przez referencję (adres), można // modyfikować obiekt podawany jako parametr void fun(const float& f); // przez referencję do stałego obiektu, // optymalny sposób! nie jest tworzona kopia, a argument jest // chroniony przed zmianą void fun(float&& f); // przez referencję do prawej wartości, większy sens // ma dla typów złożonych, które umożliwiają operacje przenoszenia void fun(const float&& f); // zwykle bez sensu, bo blokuje przenoszenie void fun(float* f); // przez wskaźnik, można modyfikować void fun(const float* f); // wskaźnik do stałego obiektu, nie można modyfikować

Funkcje wywołanie a parametry jaka jest różnica pomiędzy parametrem "przez referencję" i "przez wskaźnik"? Sposób wywołania funkcji: float mojaliczba = 0.; fun(mojaliczba); // przez referencję, tak samo jak przez wartość fun(&mojaliczba); // przez wskaźnik, trzeba podać adres obiektu za pomocą & na temat dedukcji typu zwracanego przez funkcję: auto f(); // zwracany typ nieznany auto f() { return 5; } // zwracany typ int auto f(); // redeklaracja ok int f(); // błąd traktowane jako deklaracja inne funkcji auto f() { return f(); } // błąd, dopóki typ zwracany jest nieznany, // nie można wołać rekurencyjnie auto suma(int i) { if (i==1) return i; // zwracany typ teraz znany else return suma(i-1) + i; // można więc dalej wołać rekurencyjnie } // taka funkcja może mieć wiele instrukcji return ale każda zwracająca taki sam typ

Funkcje tablice argumentami, inline tablice jako argumenty funkcji nie są przekazywane przez wartość void func1(int a[], int rozmiar); // musimy podać rozmiar void func2(int *a, int rozmiar); // array-to-pointer decay void func3(int (&a) [10]); // tylko 10-elementowa tablica void func4(int macierz[][3], int rozmiar); funkcje inline (krótkie, w celu szybkiego wywoływania) treść rozwijana w miejscu ich wystąpienia, o ile nie jest zbyt skomplikowana dla zwykłej funkcji: deklaracja (bez specyfikatora) w nagłówku void fun(); definicja w plku źródłowym poprzedzona specyfikatorem inline inline void fun() { /* definicja */ } podobnie dla metody składowej (tylko definicja ze słowem inline) wszystkie funkcje zdefiniowane wewnątrz klas są automatycznie inline jeśli pobierany jest adres funkcji nie następuje rozwinięcie (w szczególności w procesie debugowania krokowego śledzenia działania programu)

Funkcje wartości domyślne argumenty domniemane (od prawej do lewej) tylko w deklaracji void fun(int a, void*, float = 3.14, char znak= '\0'); deklaracja argumentu domyślnego tylko raz (w danym zakresie ważności) w deklaracji funkcji deklaracje można powtarzać, ale nie z powtórzonymi w nich wartościami domyślnymi void fun(int a); // w deklaracjach można zmieniać // nazwy zmiennych, tylko po co void fun(int a = 5); // tak jest dobrze w definicji funkcji jeśli ta jest jednocześnie jej deklaracją obiekty lokalne nie mogą być wartościami domyślnymi w nowym (lokalnym) zakresie ważności możliwa jest deklaracja z innymi wartościami domyślnymi nie jest to dobra praktyka!

Funkcje wartości domyślne, przykłady void g(int = 0,...); // ok, bo (wielokropek) to nie argument, tylko ich lista void f(int, int); void f(int, int = 7); // powtórzenie deklaracji z dodaną wartością domyślną void h() { f(3); // OK, woła f(3, 7) void f(int = 1, int); // błąd: niezależne od wartości domyślnych deklaracji // z innego zewnętrznego zasięgu } void m() { void f(int, int); // nie ma wartości domyślnych f(4); // błąd: niepoprawna liczba argumentów void f(int, int = 5); // OK f(4); // OK, woła f(4, 5); void f(int, int = 5); // błąd: nie można redeklarować, nawet // z taką samą wartością domyślną } void n() { f(6); // OK, woła f(6, 7) }

Funkcje dowolna liczba argumentów - wielokropek umożliwia napisanie funkcji przyjmującej dowolną liczbę argumentów Przynajmniej jeden (pierwszy) argument takiej funkcji musi być podany jawnie. Obsługa (odczyt) takich argumentów za pomocą makr, pochodzących z języka C. Konieczne włączenie nagłówka <cstdarg> ( lub stdarg.h ) int suma ( int liczba, ) { va_list ap; // utworzenie zmiennej typu va_list (variable argument list) va_start( ap, liczba ); // ustawienie ap na pierwszy, jawnie podany, argument int sum = 0; for (int i = 0; i < liczba; ++i ) { sum += va_arg( ap, int ); // odczyt kolejnej zmiennej, sami określamy jej typ! } va_end( ap ); // porządkowanie stosu, ustawienie ap na 0 return sum; } int main() { cout << sum(3, 1, 1, 1, 1, 1) << endl; // OK, możemy mniej liczyć, 3 cout << sum(8, 1, 1, 1, 1, 1, 1) << endl; // śmieci, wyszliśmy poza listę } Wady: argumenty poza kontrolą typów. Popularne przykłady z biblioteki: printf, sprintf

Funkcje argumenty funkcji main int main(int argc, char* argv[]) { // to samo: int main(int argc, char** argv) { // argc liczba argumentów pierwszym zawsze jest ścieżka i nazwa programu argv[0] zapisana w pierwszej pozycji tej tablicy kolejne argumenty można konwertować po włączeniu nagłówka #include <cstdlib> za pomocą funkcji: atoi(), atol(), atof() możemy wykorzystać obiekt klasy istringstream klasa ta dziedziczy po klasie istream, ta zaś dziedziczy po klasie ios, zaś ta po klasie ios_base oznacza to, że obiekt ten "ma w sobie" wszystkie funkcje zdefiniowane w powyższych klasach ponadto ma zdefiniowaną własną funkcję: void str(const string& tekst) const; string str() const;

Pomiędzy innymi typami a łańcuchem znakowym Warto wiedzieć, że sytuacja, gdy skazani byliśmy na printf (sprintf) nie ma już miejsca! W nagłówku <string> dostępna jest seria przeciążonych funkcji to_string, działających komfortowo i bezpiecznie z punktu widzenia kontroli typów. Nie musimy się też martwić o wielkość wypełnianego buforu! #include <string> // konwertuje zmienną typu int na łańcuch znakowy std::string std::string to_string( int value ); // taki sam, gdy działało sprintf o odpowiednio dużym buforze std::sprintf(buf, "%d", value); // podobnie pozostałe: std::string to_string( long value ); std::string to_string( long long value ); std::string to_string( unsigned value ); std::string to_string( unsigned long value ); std::string to_string( unsigned long long value ); std::string to_string( float value ); std::string to_string( double value ); std::string to_string( long double value );

Pomiędzy łańcuchem znakowym a innymi typami Podobnie w drugą stronę, jeśli mamy łańcuchy znakowe (np. parametry programu), możemy teraz skorzystać z następujących funkcji konwersji. Działają one następująco: opuszczają białe znaki, czytają cyfry (tak wiele ile jest poprawne dla ustawionej bazy base, resztę ignorują), jeśli podstawi się jako drugi parametr niezerowy wskaźnik, to wpisane w niego zostaje adres pierwszego nieskonwertowanego znaku oraz jego indeks. #include <string> // konwertuje łańcuch znakowy std::string na typ całkowity Int stoi( const std::string& str, size_t *pos = 0, int base = 10 ); long stol( const std::string& str, size_t *pos = 0, int base = 10 ); long long stoll( const std::string& str, size_t *pos = 0, int base = 10 ); unsigned long stoul( const std::string& str, size_t *pos = 0, int base = 10 ); unsigned long long stoull( const std::string& str, size_t *pos = 0, int base = 10 ); // konwertuje łańcuch znakowy std::string na typ zmiennoprzecinkowy float stof( const std::string& str, size_t *pos = 0 ); double stod( const std::string& str, size_t *pos = 0 ); long double stold( const std::string& str, size_t *pos = 0 );

Klasa std::string Utworzenie obiektu typu std::string odbywa się podobnie jak dowolnej zmiennej typu wbudowanego. Jednak w tym przypadku można też stworzyć obiekt zainicjalizowany danymi obiekt budowany jest przez specjalną metodę składową, konstruktor. Konstruktorów może być dowolnie wiele, muszą różnić się argumentami. #include <iostream> #include <string> using namespace std; auto main() -> int { string s1; // pusty string } Zbadajmy jaki jest rozmiar i bufor obiektu s1: for (auto i(0); i<1025; ++i) { s1 += a ; cout << s1.size() << << s1.capacity() << endl; } Dodatkowo co będzie gdy: s1.clear(); s1.empty(); // zwraca true lub false s1.shrink_to_fit(); s1.reserve(57); // jakie capacity()?

Tworzymy kolejne obiekty std::string Oto kilka sposobów na utworzenie / przypisanie obiektu typu std::string const char *t = tekst do inicjalizacji ; s1 = t; string s2( s1); // obiekt na wzór istniejącego wcześniej string s3( t, 8 ); // pierwsze 8 znaków string s4( s2, 6, 8 ); // od 6-tego do 6+8 -mego, czyli string s5( 100, * ); // chcę mieć sto gwiazdek string s6 = konstrukcja ; string s7 = { uniwersalna inicjalizacja }; // = opcjonalnie Działania na stringach bez problemu: s1 = s1 + drugi + s2; s1 += s6;

Maksymalny rozmiar i pewna stała: Sprawdźcie jaka jest wartość tej stałej: Rozmiary, usuwanie max_size() // zwykła metoda składowa string().max_size(); // string w locie std::string::npos Wielkie usuwanie (erase metoda składowa): erase( nr_od, nr_ile ); // zwraca referencję do erase( adres_od, adres_do ); // zwraca adres nast. znaku Specjalne funkcje adresowe (zwracające tzw. iteratory czyli obiekty udające wskaźniki przechowalniki adresu i wiedzy o typie): begin(); // adres początku zerowej pozycji end(); // adres za ostatnim elementem, za-ostatni

Przykład, dodatkowo z algorytmem find: #include <iostream> #include <algorithm> #include <string> using namespace std; int main () { std::string s = "To jest dobry przyklad"; std::cout << s << '\n'; s.erase(0, 3); // usuń "To " std::cout << s << '\n'; Usuwanie s.erase(std::find(s.begin(), s.end(), ' ')); // usuń pierwszą spację ' ' std::cout << s << '\n'; } s.erase(s.find(' ')); // Znajdź kolejną i usuń wszystko od niej do końca std::cout << s << '\n';

Małe ćwiczenie Narysujmy za pomocą erase taką sekwencję ****************** ***************** **************** *************** I tak dalej #include <iostream> #include <string> using namespace std; int main () { string str (20, * ); while (! str.empty() ) { cout << str << endl; str.erase(str.end()-1); } }

Wczytywanie z pliku Utwórzmy obiekt do obsługi strumienia plikowego i wczytajmy a potem wypiszmy! #include <fstream> string s10; string str; cout << "Wprowadz tekst: "; cin >> str; cout << "Wczytano to: " << str << endl; getline (cin, str, '@'); // koniec = znaczek @ cout << "Wczytano tamto: " << str << endl; Bufor cin nadal trzyma starą zawartość, tu poczytajcie jak to wyczyścić http://cpp0x.pl/kursy/kurs-c++/poziom-1/obsluga-strumienia-wejsciowego/12 ifstream plik("tekst.txt"); // np. wziąć z: pl.lipsum.com while (! plik.eof() ) { getline (plik, str); s10 += str; // czego tu brakuje? Znak końca linii + \n } // wypiszcie na ekran cout << s10;

Przebiegamy po stringu String to forma kontenera sekwencyjnego jakby tablicy znaków s1 = "wlazl kotek na plotek i mruga"; for ( auto c : s1 ) cout << c << " "; // range-based loop for ( auto& c : s1 ) c = ( c== w )? W : c; // zamieniamy na wielkie W, co z nawiasami? for ( int i=0; i < s1.length(); ++i ) cout << s1[i] << " "; ITERATOR inteligentny pośrednik pomiędzy kontenerami (zasobnikami), wskaźnik z adresem do operacji na konkretnych typach, strumieniach string::iterator it; // na razie pusty it = s1.begin(); // początek end() koniec while ( it!= s1.end() ) { cout << *it << endl; ++it; } ITERATOR STRUMIENIA auto it = s1.begin(); copy (s1.begin(), s1.end(), ostream_iterator<char>(cout,"\n")); // używamy algorytmu copy (ten z nagłówka <algorithm>) // tworzymy w locie iterator strumienia wyjściowego, ostream_iterator // konieczny nagłówek #include <iterator>