GCC: GNU Compiler Collection DLL: Dynamic Link Library LIB: Physical File Library STL: Standard Template Library MinGW: Minimalist GNU for Window C++: C kiterjesztése STD: Standard Library #include <iostream> --> preprocesszor direktíva, NINCS PONTOSVESSZŐ l-value is a value that has an address, bal oldalon van ennek adunk értéket ez const is lehet --> van külön modifyable lvalue (a const nem az) #define X 8 ++X; --> ++ argumentuma nem lehet rvalue --> hiba r-value kifejezés, változó, konstans egy értékre értékelődik ki ezt adjuk értékül az lvalue nak olyan kifejezés, aminek van értéke, viszont nem lehet hozzárendelni értéket (nincs memoriacime) int x; //definition x = 10; //assignment int x = 10; //initializaton (def+assignment egyben) ///////////////// using std; // std használata int cout(){ //saját cout return 5; } int main(){ using namespace std; // makes std::cout accessible as "cout", using namespace KERÜLENDŐ cout << "Hello, world!"; // melyik cout ot használjuk? return 0; } így using std::cout; //megadjuk, hogy melyik cout ot használja cout << "Hello, world!"; ///////////////// Rule of Three: Copy assignment operator , destruktor, copy konstruktor ha az egyik definiálva van, akkor mindenek definiálva kell lennie ez a 3 egyébként automatikusan generálódik, ha nincs definiálva --> rule of 5 (C++11) move constructor move assignment operator copy assignment: Point a(10,2); Point b = a; //itt még a sima copy hívódik Point c; c = a; //copy assignment hívódik variable argument lists: pl int printf(const char *format, ...) //... rész elérhető va_list ap; va_start(ap, n_args);, a printf nem egy overloadolt függvény hanem va_list-et használ http://www.programmerinterview.com/index.php/c-cplusplus/c-declarations/ absztrakt osztály: amiben PURE virtual függvény van, ha a leszármazottja nem definiálja ezt, akkor az is abasztrakta lesz pure virtual == abstract method JAVA ban, de C++ ben a bázisosztály is definiálhatja ezt a függvényt (a már tudva lévő funkcionalitást megadhassuk, pl minden madár levegőt vesz, mielőtt énekel, de az éneklés már madaranként különböző) pure virtual function: = 0 használatával //ez nem egy értékadás, hanem szintaktikai megoldás virtual void pure_virtual() = 0; //pl adattagok: osztályban private (C++ lett definiálva) struct ban public (C ből lett átvéve) sima virtual: - http://stackoverflow.com/questions/2391679/why-do-we-need-virtual-methods-in-c - polimorfizmus esetén nem fog felüldefiniálódni - megjelöli a metódust felüldefiniálhatónak - tehát ha Base *c = new Child(); c.method(); //itt ha a method virtual, a Child szerinti definíció hívódik meg (az futás idejű, aktuális típus --> late binding), //egyébként a Base szerinti (a deklaráció szerinti, fordítási idejű --> early binding) //////////////// In C: void foo() means "a function foo taking an unspecified number of arguments of unspecified type" void foo(void) means "a function foo taking no arguments" In C++: void foo() means "a function foo taking no arguments" void foo(void) means "a function foo taking no arguments" //////////////// macro problémák: #define DOUBLE(X) X*X //PREPROCESSZOR végzi, text helyettesítéssel DOUBLE(++y) //y = 2 --> 3 * 4!!! ++y * ++y DOUBLE(a+b) // a + b * a + b, nem (a + b) * (a + b) #define ADD_TWO(x,y) x += 2; y +=2 bool flag = true; int j = 5, k = 7; if(flag) ADD_TWO(j,k); --> if(flag){ x +=2; } y += 2; //!!!!!!!!!!!!! inline function: //a COMPILER behelyettsíti a törzset, így nem kell meghívni a függvényt --> sok hívás költséges inline int sum(int a, int b){ return (a+b); } többszörös öröklődés: class C : public A, public B{ void a(){ B::a();//---> kiválasztom a megfelelő definíciót } } Diamond --> ambigous lesz megoldás: class B : virtual public A class C : virtual public A --> így az A csak egyszer jön létre (ha az a kérdés, hogy mire a virtual kulcsszó a leszármaztatásnál, ennyi a válasz: 1x jön létre --> nem lesz ambigous) virtual class A //ilyen nincs, virtual csak fv ben, vagy leszármaztatásnál szerepelhet #include "" és <> "": ha én definiáltam, az adott könyvtárban kezd keresni <>: predefined header öket keres, a predefined headörök helyén (C++ verzio függő) tipusdefinicio: typedef int& A; A aref; //ha A egy alias az int& re pointer: - saját címe van, ezen a címen a mutatott objektum címe van - pl: int *y = &x; dereferálás int z = *y; https://en.wikipedia.org/wiki/Pointer_%28computer_programming%29#C_pointers void pointers: void *ptr; //bármire mutathat közvetlenül nem dereferálható (mivel nem tudjuk milyen adattípus), static_cast kell (static_cast<int*>(voidPtr)) pointer műveleteket nem lehet használni (+,-) reference: - tök ugyanaz mint a "mutatott objektum", csak más néven hivatkozunk , https://isocpp.org/wiki/faq/references#overview-refs - nem lehet null, mindig incializálni kell (mivel konstans, így nem is változhat) - int & const rnRef = 6; //ilyet lehet, de minek, egy referencia mindig konstans, !!! nem az int a const hanem a referncia Use references when you can, and pointers when you have to. 1[a] = 13; ez helyes // az a tömb 1-es indexű eleme 13 lesz a[b] == *(a + b) //C definiício --> a+b = b+a -->1[a]= *(1 + a) //a egy memóracím, ezt megnöveljük 1-el többdimenziós, dinamikus tömb: int **array = new int[10][5]; //nem jó int **array = new int*[10]; // így kell for (int count = 0; count < 10; ++count) array[count] = new int[5]; array[9][4] = 3 //elérés //törlés for (int count = 0; count < 10; ++count) delete[] array[count]; delete[] array; /////////////////////// virtual destructor: class Base{ public: Base(){ cout<<"Constructing Base";} }; class Derive: public Base{ public: Derive(){ cout<<"Constructing Derive";} ~Derive(){ cout<<"Destroying Derive";} }; void main(){ Base *basePtr = new Derive(); delete basePtr; } output: Constructing Base Constructing Derive Destroying Base virtual ~Base(){ cout<<"Destroying Base";} output: Constructing Base Constructing Derive Destroying Derive Destroying Base a super konstruktor mindig lefut!!, polimporfizmus esetén csak akkor fut le a destruktor, ha az ős virtual ha van virtual fv. kell írni virtual destructort /////////////////////// friend: metódusra, class ra alkalmazható látja a friend private és protected adattagjait is pl.: linked list és iterator --> az iterator el akarja érni a list elemeit, ne kelljen getter/settert írni class B { friend class A; // A is a friend of B private: int i; } class A{ public: A(B b){ b.i = 12; } } https://en.wikipedia.org/wiki/Friend_function ///////////////// . és ->: (*p).foo() == p->foo() . esetén direct acces van a member felé -> eseténe egy pointerünk van az objektumra, a * ugye dereferálja a pointert, azaz a mutatott memóriacímet éri el int y = 10; int *x = &y; (megadjuk x pointernek y memóriacímét) cout << *x; //--> 10 memory leak: hazsnálatlan, de még lefoglalt (felszabadítatlan) memóriaterület --> probléma: kifogy a memoria, chunk ok miatt nehéz a helyfoglalás void memLeak( ) { int *data = new int; //!!!!! new-t akkor használjuk, ha egy pointernek adunk értéket *data = 15; //*data nem lett törölve } delete: delete vs delete[]: 1. sima elemnél, 2. tömbnél használatos minden, amire new-t írtunk, delete elendő, mert nem aut. élettartamu viszont amit nem new-ztunk, azt nem szabad, mert 2x akarja felszabadítani a területet --> error nem töröl semmit, csak használhatóvá teszi a memóriaterületet (ld. winchesteren való törlés, ott is megmarad az adat, csak felülírható lesz) Base class constructors and derived class destructors are called first: Inside Base constructor Inside Derived constructor Inside Derived destructor Inside Base destructor template: - compiler készíti a megfelelő (használt) típusokkal - muszáj minden template paramétert használni fv: template <class type> ret-type func-name(parameter list) pl. template<class T1, class T2> void someFunc(T1 var1, T2 var2 ){ // some code in here... } class: template <class type> class class-name http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file auto: kikövetkezteti a változó típusát declaration vs definition: declaration: megadjuk a compilernek, hogy milyen szimbólumokat fogunk használni definition: adat vagy kód megadása a változóhoz, így a compiler le tudja foglalni a megfelelő mennyiségű memóriát const: const applies to the type to its left ha ez van legbaloldalt, akkor az első típusra ami jobbra van tőle const const const const const const const int asd = 10 <==> const int asd = 10 //akárhányszor alkalmazható int const f() != int f() const; //az elsőnél egy const int a viszzatérési érték //a 2. nál a függvény nem változtathatja meg az osztály membereit, csak ha azok mutable ök típus hülyeségek: http://stackoverflow.com/questions/1143262/what-is-the-difference-between-const-int-const-int-const-and-int-const http://www.programmerinterview.com/index.php/c-cplusplus/c-declarations/ http://www.codeproject.com/Articles/7042/How-to-interpret-complex-C-C-declarations operátorfelüldefiniálás: <return-type> operator<operator-symbol> (<arguments>) //tehát fv név helyett "operator" és egy "szimbólum" van pl. string operator[] (int s){ return myMap.at(s); } inicializációs lista: Constructor() : <tag1>(<érték1>), <tag2>(<érték2>){} pl.: Point::Point(int x = 10, int y = 11) : x(x), y(y){} 3 esetben kötelező használni: 1 referenciát 2 const változó 3 default konstruktort nem tartalmazó változó esetén class A{ int x; public: A(int x) {} //A nak nicns default konstruktora, mivel adtunk meg neki 1-et }; class B { A a; public: B() : a(10) {} //ezért itt midneképp meg kell hívni, az inicializációs listában a konstruktorát }; default paraméter: csak a Header be, fv definicional kell megadni, az implementálásnál már nem szabad, mert redefinition nek veszi #pragma once: //MINDIG KELL A HEADERBE copy ctor: <T>(const <T> &obj) pl.: Box::Box(const Box &obj){ min = new Point(); *min = *obj.min; } class Pair p; //így is lehet Pair típusu változot definiálni(class kulcsszó), //template és typedef esetén viszont nem működik kooenig lookup: http://www.mycppquiz.com/question.php?qid=24 az argumentum namespace e alapjan keresi ki a fv namespace ét namespace standards{ struct datastructure{ }; void foo(const datastructure& ds){ } void bar(int x){ } } int main() { standards::datastructure ds; foo(ds); //ezt elérjük, mert a ds namespace e standards, ebből kikövetkezteti, hogy a foo i s ott van bar(2); //ezt nem következteti ki, mert az argumentuménak nincs namespace e return 0; } static: variables with static storage duration are zero initialized. Note that x has static storage duration even though the static keyword is not used. According to the standard: "All objects which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration" statikus method nem lehet virtual minden objektum statikus élettartamú, ha nem dinamikus, lokális vagy thread élettartamú globális változóra(blokkon kívül): internal linkage et ad neki blokkon belüli változóra: a megszokott, azaz statikus élettartamú lesz A function call can be an lvalue if and only if the return value is a reference struct: használat előtt kell definiálni (ld.: fv visszatérési értéke Pair, akkor a Pair struct def előbb kell h legyen) struct Person{ int age; string name; } init: Person p = {11, "joe"} Person p {11, "joe"} //C++ 11 struct Company{ Person CEO; // Person is a struct within the Company struct int numberOfEmployees; }; Company myCompany = {{ 1, "Lajos" }, 5 }; tömbbel nem lehet visszatérni fv ből --> pointert kell használni int* test(size_t& arraySize) { return new int[array_size]; } int *array = test(10); //IGY KELL POINTERT DEFINIÁLNI TÖMBRE MER A C++ EGY FASSAG tömb átadása paraméterben int* returnProducts(int arr[]); std::array: std::array<double, 5> array; van size függvénye, ami paraméterátadás után is megmarad pointer +/-, <,>,=,stb műveletek csak array re használható, ha outofbound, akkor undefined a művelet eredménye There is no such thing as an array of references ha egy leszármazott osztály felüldefiniál egy ős fv. t, akkor vagy azonos típusúnak kell lennie, vagy mind2 nek pointerrel kell visszatérnie, ami nem ambigous http://www.mycppquiz.com/question.php?qid=72 ad hoc polimorfizmus == overload move szemantika: ház tulajdonosa John de eladja azt Mary nek, ekkor régi c++ szerint lemásoljuk a házat, ráállítjuk Mary pointerét, John ét pedig töröljük, Move esetén Mary pointere mutasson John házára, John é pedig null ra egy értékadás esetén van egy rvalue-nk, ezt deep copyval belemásoljuk egy lvalue ba, az rvalue-t pedig eldobjuk, move szemantikával ehelyett az adott rvalue pointerét null ra állítjuk, az lvalue pointerét pedig "ellopjuk" az rvalue tól, azaz ráálítjuk copy ctortól és copy assignmenttől csak egy plus & jelben különbözik az argumentumba!!! Matrix::Matrix(Matrix&& m) : totalRows(m.totalRows), totalCols(m.totalCols) /*sima adatok érték szerinti init*/{ data = m.data; //csak adjuk át a dinamikus adat pointerét m.data = nullptr; //régi dinamikus adat pointere legyen 0, hogy ne tudjunk vle műveleteket végezni m.totalRows = 0; m.totalCols = 0; } Matrix& Matrix::operator=(Matrix&& m) { if (&m == this) { //ha önmagába move olom térjünk vissza return *this; } totalRows = m.totalRows; //mivel itt nincs inicalizációs lista, a sima adattagokat állítsuk be totalCols = m.totalCols; delete[] data; //this ben lévő esetleges dinamikus adatot töröljük data = m.data; //innentől ugyanaz mint a move construktorba m.data = nullptr; m.totalRows = 0; m.totalCols = 0; } Használat: Matrix toMove1(2, 2); //1 1 //1 1 Matrix moved = std::move(toMove1); //itt toMove1 pointere már 0, mérete 0x0 mátrix Matrix toMove2(2, 2); Matrix movedWithOperator(2, 2); movedWithOperator = std::move(toMove2); //itt toMove2 pointere már 0, mérete 0x0 mátrix (/*képek*/) http://avid-insight.co.uk/2013/05/understanding-cpp11-move-semantics/ partial template specialization: megadunk egy paramétert, de az eredmény még mindig egy template marad template <typename Key, typename Value> class KeyValuePair {}; template <typename Key> class KeyValuePair<Key, std::string> {}; template <typename Key> class KeyStringPair : public KeyValuePair<Key, std::string> {}; const: nem változhat az étéke mutable: az érték megváltoztatható konstans környezetben is - const fv ben: void m const(){f.a = 10;} - vagy constans objektum mutable értéke változhat: const F f; f.a = 10; volatile: - a memóriaterületre csak írni, vagy onnan olvasni lehet, nincs optimalizáció (pl ha nem változik az értéke egy ciklusban, akkor fordító kioptimalizálná) - viszont egy másik thread változtathatja a területet a mi thread ünk tudta nélkül, ekkor nem akarjuk, hogy a compiler kioptimalizálja, eldobja a változónkat http://stackoverflow.com/questions/72552/why-does-volatile-exist //--------- optimalizáció: init, assignment helyett: T x = t; //Initialization, csak a ctor fut le x = t; // assignment: lefut a copy assignment operator --> ez így 2 hívás T x; // deklaráció: lefut a ctor & használata: - pointer helyett mindenhol ahol lehet (* t deletelni kell) - fv paraméterként akkor, primitív típusoknál érdemes csak, már egy stringet is érdemes referencia szerint mivel érték szerinti átadáskor lemásolódik az egész objektum - mindig használjuk referencia paraméternél a const kulcsszót, ahol lehet, inicializációs lista: - ha nem itt adunk értéket a membereknek, akkor a konstruktor hívása előtt lefut azok saját konstruktora Person(int age) { // age kontruktora lefut this->age = age; // +1 értékadás --> copy assignment operator } Person(int age) : age(age) {} // az age konstruktora lefut a kapott értékkel operátor= T c(a + b); return (c); T c(a); //ez jobb c += b; return (c); ++i vs i++: ++i; //megnöveli i-t madj visszaadja azt i++; //megnöveli i-t, viszont az eredeti értéket adja vissza, ezt pedig egy temp változóban tároljuk --> rossz //--------- explicit: konstruktor esetén megtiltja az implicit converziót http://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-in-c-mean class Foo{ public: // single parameter constructor, can be used as an implicit conversion explicit Foo (int foo) : m_foo (foo) {} int GetFoo () { return m_foo; } private: int m_foo; }; void DoBar (Foo foo){ int i = foo.GetFoo (); } int main (){ DoBar (42); //mivel Foo nak van egy intet váró konstruktora, a 42-t Foo ra implicit castolja, ez minden paraméterre 1x //tehető meg //viszont ha megadtuk, hogy a Foo(int foo) explicit legyen, akkor letiltjuk ezt az implicit konverziót ezzel a //konstruktorral } converting constructor: struct bar{ int x; bar(int x); } struct foo{ foo(float f, int x); // 3 } mire : bar = 2; //a constructor egy intet vár --> implicit cast foo = {100.0f, 2}; //a constructor egy floatot és egy intet vár --> implicit cast std::string s = "This works"; //= std::string("This works") kéne, hogy legyen, ha nem lenne convertiong ctor private constructor: letiltja a default konstruktort --> ne lehessen példányosítani az osztályt trailing return: egy fv típusát a végén adjuk meg -> <típus> szintaxissal decltype(a*b) mul(T a, T b){ //ez nem jó, mert balról jobbra értékelődik ki, a,b-nek nem tudjuk a típusát return a*b; } auto mul(T a, T b)->decltype(a*b) //auto:placeholder, decltype: most már tudjuk a,b típusát function prototype: int myfunction(int n); /* Prototype */ int main(void) {} int myfunction(int n) { /* Called function definition */ //... } int main() //nem muszáj visszatérési értéket adni neki, mert ha nincs definiálva, 0 val tér vissza constexpr: constexpr int get_five() {return 5;} int some_value[get_five() + 7]; // Create an array of 12 integers. Legal C++11 int típusok: char, short, int, long, long long forward declaration: int add(int a, int b); //ez .cpp fájlba kell, hogy legyen (.h nem jó) int main(){... add(1,2); ...} //nem kell importálni az addot tartalmazó fájlt, az előző sor alapján kitalálja //függvénynéál nem kell extern int x = 11; //valami.cpp, ha x static, akkor így nem elérhető, mert akkor internal linakge e van extern int x; //main.cpp linkage: a változó/fv mely fájlokbül érhető el external: más fájlokban is használható internal: csak a saját fájljában használható external internal linkage: http://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage-in-c extern: extern tells the compiler that the variable is defined somewhere else, so it doesn't complain about it being undefined. externel megadott változó csak az adott blokkban él (egy sima aut. élettartamu változohoz hasonloan) mire : http://stackoverflow.com/questions/10422034/when-to-use-extern-in-c a static.h minden egyes includenál lemásolná a benne lévő változókat az adott fájlba: Global symbolic constants rész: http://www.learncpp.com/cpp-tutorial/42-global-variables/ C használata C++-ban (de az extern nem c++ beli kódra is mutathat, nem csak erre ) extern "C" void foo(); void main(){ // the function call: foo( ); } //több függvény esetén ez is használható extern "C" { int foo( ); double foobar(); }; 2es számrendszerbeli megadás (0b előtag és ' elválasztó a tagoláshoz): int bin = 0b1011'0010 konstans paraméter: void printInteger(const int x) --> a programozó tudja, hogy x nem fog változni, illetve nem is változhat constexpr: fordítási idejű konstans int age; std::cin >> age; constexpr int myAge = age; // not okay, age can not be resolved at compile-time #define: nem mert global scope --> conflict debugger hiba esetén nem írja ki a konkrét értéket, pl.: a = MAX_NUM; //error a = MAX_NUM, nem pedgi a = 10; //-->meg kell néznem külön az értékét a kódba operator,=(x, y) //pl.: x,y --> Evaluate x then y, returns value of y | ez nem a paraméterelválasztó operátor fv eknél && nek nagyobb a precedenciája mint a || nek char* c c[2] = 's'; //hiba, mert const string arrayre mutat, helyett használjuk a char c[]-t, //vagy definiáljuk c-t const kulcsszóval bit műveletek: std::bitset<8> bits; test, set, reset, flip --> bitwise műveletkhez RGBA: const unsigned int redBits = 0xFF000000; const unsigned int greenBits = 0x00FF0000; const unsigned int blueBits = 0x0000FF00; const unsigned int alphaBits = 0x000000FF; // use bitwise AND to isolate red pixels, then right shift the value into the range 0-255 unsigned char red = (pixel & redBits) >> 24; unsigned char green = (pixel & greenBits) >> 16; unsigned char blue = (pixel & blueBits) >> 8; unsigned char alpha = pixel & alphaBits; foo(++x, x): az első vagy a második paraméter értékelődik ki előbb? ha az első, akkor a második 1-el nagyobb lesz ++x értéke már a megnövelt érték mindenkéépp, szóval itt nincs ambigousity -7/2: felfelé vagy lefelé kerekít? (C++ 11 től lefelé, mindig 0 felé, a tört részt eldobjuk) -5%2: -1 vagy 1? (C++ 11 től mindig az eredetivel megegyező előjelű) :: operator: névfeloldás namespace alapján ::doSomething() --> nics előtte semmi, akkor global scope namespace: garanátlja, hogy egy azonosito csak 1x szerpeljen több fájl is tartalmazhatja a deklarációt namespace BasicMath{ //subtract.h int subtract(int x, int y){...}// function subtract() is also part of namespace BasicMath } namespace BasicMath{ //add.h int add(int x, int y){...} // function add() is part of namespace BasicMath } implicit konverzió: minden esetben, ha mi nem adjuk meg a konverziót usual arithmetic conversion: ha számmal dolgozunk akkor legalább int re konvertálódnak egy kifejezés elemei: short a(4); short b(5); typeid(a + b).name() //--> int, hiába voltak shortok ha van nagyobb rangú elem a kifejezésbe, akkor a legmagasabb rangúére értékelődik ki a kifejezés explicit konverzió (cast): C style: float f = float(i1) / i2; //kerülendő, mert compile time nem ellenőrzött static_cast: ch = 'a'; static_cast<int>(ch) ; //fordítási időben is ellenőrzött, ez a legjobb bool konverzio: null ptr ből null ptr lesz, 0 bol false, minden másból true std::cin: az első whitespacig olvas, egy sor beolvasásához használljuk az std::getline-t enum: - enum <Név>{ <értékek> } - maga az enum definíció nem foglal memóriát, csak ha használom az értékét enum Color{ BLUE } Color::BLUE, ilyen nincs, egy scope on belül egy enum "konstans" csak 1x szerepelhet (a BLUE nem szerepelhet más enumban) enum A{ BLUE, //-->hiba } enum class: - enum class <név> pl.: Color::BLUE nem lesz implicit int é convertálva, ehhez static castot kel használni: std::cout << static_cast<int>(Color::BLUE) így nem is lehet 2 külön enum classba tartozó értéket összehasonlítani if(Color::BLUE == Fruit:BANANA) //--> compiler error, más a típusuk array: Arrays can be implicity converted to pointers without casting -- 4.2. There is no implicit conversion from pointers to arrays. void printSize(int array[]); == void printSize(int *array); //de a 2. a jobb az átadás pointernek megfelelően működik, azaz ha az array változik a functionben, akkor az átadott változó is int a = {1,2}; std::cout << a; //--> memória cím, mivel egy pointer az első elemre pointer: a pointer + operátora függ a pointer típusától: int a; //0012FF80 cout << a+1;//0012FF84 egy int 4 byte short b; //0012FF80 cout << b+1;//0012FF82 egy short csak 2 std::vector: bool típus esetén 1 bájtot használ 8 bool értékre 0 ra inicializálja az inicializálatlan int értékeket //van default érték hiba: int* allocateArray(const int length){ int temp[length];//length nem egy compile time constant return temp; } //a blokk végén a temp megszűnik, mert automatikus élettartamú, használjunk new t paraméterátadások: érték, referencia, cím (pointer használatával) szerinti cím érték szerinti átadása: (nem inkább pointer referencia szerinti átadása?) void setToNull(int *&tempPtr) stack: fv hívások is itt kezelődnek, a változókhoz hasonlóan assert: #include <cassert> int GetArrayValue(int nIndex){ assert(nIndex >= 0 && nIndex <= 9); // ha nem igaz --> Assertion failed: nIndex >= 0 && nIndex <=9, file ... return g_anArray[nIndex]; } main: char *argv[] == char** argv argv[0] = mindig a fájl elérése //c:/Documents argv[1] = a fájl neve //app.cpp ellipses: ... nincs típusellenőrzés --> nekünk kell megadni nincs megadva, h hány paramétert fogunk átadni legyen az első paraméter double FindAverage(int nCount, ...) legyen termináló szimbólum --> pl string \0 enkapszuláció: ahhoz hogy a tv-t kezelni tudjam nem kell megértenem, hogyan működik initalization list: használjuik ezt const és reference típusú változót kötelező inicializálni --> ezért is double dValue(4.7); //változó implicit inicializációja Something() : x(0), y(0.0), z(0){} //inicializációs lista kompozíció, tartalmazás: autó - kerekek, ember - végtagok deallokálja az alosztályokat is nem dinamikus membereket tartalmaz aggregáció: tantestület - tanárok //ha a tantestület megszűnik a tanárok nem halnak meg, lehet új munkájuk deallokációt, allokációt nem végzi az alosztályokon pointer memberek et tartalmaz az alosztályokra array of values --> kompozíció, ha megszűnik, az objektumok is megszűnnek array of pointer/reference --> aggregáció, az objektumok nem szűnnek meg a tömb törlésekor dangling else: mindig a legközelebbi ifhez tartozik az adott blokkban null statement: if (x == 0); x = 1; ből ez lesz if (x == 0) ; // the semicolon acts as a null statement x = 1; switch() case 1: int x; //illegal case 1: { int x; //legal } scope típusok: local, global, file, function --> goto, goto: blokon belül lehet előre ugrani, de hibát okozhat: int main() { goto skip; // invalid forward jump int x = 5; skip: x += 3; // what would this even evaluate to? return 0; } for: 1. ciklusváltozó/egyéb incializálás kiértékelése , CSAK 1X 2. feltétel vizsgálat -> kilépés, vagy 3. pont 3. ciklusmag, majd jump to 2. while ciklus for-al: int count=0; for ( ; count < 10; ){ cout << count << " "; ++count; } for(int i = 1, int j = 2) //multiple init array: méretmegadás lehet: const int, literal, enum elem, #define size 4 initializer list: int prime[5] = { 2, 3, 5, 7, 11 } uniform init: int prime[5] { 2, 3, 5, 7, 11 }; minden elem 0 amit nem inicializálunk (nem garbage) //std::array? függvényeknél az adott arrayt adjuk át, nem érték szerinti az átadás void passArray(int prime[5]) { //hiszen a tömb lényegében egy pointer, így cím szerinti átadás történik prime[0] = 11; //az átadott array eleme is váltzik !!, hasznáéljunk const ot ha ezt meg akarjuk tiltani } sizeof fv átadás után csak az első elem méretét adja meg //(mivel egy pointer az első elemére) túlindexelés nem ad hibát: int prime[2]; prime[16] = 2; //nem ad hibát, az offsetnek megfelelő területet felülírja foreach: for (auto elment : array) //ekkor a number egy copy lesz a tömbből for (auto &element: array) //így a konkrét elemet érjük el pointer: double *dPtr = 0012FF7C; //így nem lehet memóriacímet adni a ptr-nek, ez egy int literál, literalnak pedig nincs címe egy memória cím egy pointer az adott típussal: int x(4); std::cout << typeid(&x).name(); //--> int*, nem egy memóriacím int literál nullpointer: int *p = 0; if(0 == p) int *p = NULL; //ez C ben egy preprocesszor macro a 0-ra, ne használjuk int *p = nullptr; //C++ way, megfelő típusú pointer, 0 értékkel std::nullptr_t : csak nullptr lehet az értéke mire : fv, ami nullpointert vár: void doSomethingWhenNullReturned(std::nullptr_t ptr){} --> doSomethingWhenNullReturned(getParent()); //nincs parentem--> fusson le a fv paraméterátadás: valójában minden érték szerinti átadás, mivel referencia szerinti esetén pointerrel van lekezelve a háttérben az átadás, cím szerinti átadás esetén pedig a pointer lemásolódik a formális paraméter helyére visszatérés érték szerint: ekkor is lemásolódik a visssszaadandó érték, mint átadásnál nagy adatszerkzetekre nem hatékony cím szerinti: csak egy memóriacím adódik visszatérés dinamikusan allokált (new) változókhoz int* doubleValue(int x){ int value = x * 2; return &value; // return nValue by address here --> törlődik } // value destroyed here referencia: az előző hiba erre is igaz dinamikusan allokált változók visszaadására http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/ int returnByValue() { return 5; } int& returnByReference(){ static int x = 5; // static ensures x doesn't go out of scope when we return it by reference return x; } int main(){ int value = returnByReference(); // case A -- ok, treated as return by value int &ref = returnByValue(); // case B -- compile error const int &cref = returnByValue(); // case C -- ok, the lifetime of return value is extended to the //lifetime of cref } "out paraméter" (mind GLSL be vagy c# ba): void outFunc(int &xOut) //ez ugye nem visszaadja a dolgot, de hasonlóan működik mint az említett nyelveken inline function: csak rövid függvényekre, mert nagy lesz a kód (ami nemtom mért baj) overload: mikor nincs különbség 2 fv között: 1 visszatérési érték alapján (int print(), float print() --> ambigous) 2 static nem static (int a(), static int a()) 3 const nem const (int a(), const int a()) 4 [] vs * () (int a(int *b), int a(int b[])) 5 default value (int a(int x), int a(int x = 10)) VISZONT: EZEK KÜLÖNBÖZNEK void fun() const {} void fun() {} const: non const objektum a non const változatot hívja meg //int f() const objektum a const változatot hívja meg //const int f() kiválasztás folyamata: 1 az adott paraméter egyértelműen illeszkedik 2 ha nem ,akkor promotion 3 ha így sem, akkor standard conversion (implicit) de: void print(unsigned int value); void print(float value); //unsigned int legyen vagy float? -->ambigousity print('a'); print(0); print(3.14159); function pointer: int foo(); esetén foo egy pointer a fucntionre, foo() pedig dereferálja azt --> (*foo)() == foo() int (*pFoo) (); //pointer foo ra pFoo = foo; //nem pFoo = foo(); hívás: int foo(int nX){} int (*pFoo)(int) = foo; (*pFoo)(12));//vagy pFoo(12); memória területek: code area: a lefordított kód globals area: global variables heap stack constructor: Fraction(){} Fraction(int nNumerator=0, int nDenominator=1){} lehet private (constructor chaininghez: egyik konstruktor meghívása a másikból, de ez csak cpp 11 től) Fraction f; //--> ambigous call, nem tudja melyiket kell hívni destruktor: csak egy lehet belőle, nem kaphat paramétert this: implicit konverzió member fv híváskor, a this mindig az 1. paraméter void SetID(int nID) { m_nID = nID; } --> void SetID(Simple* const this, int nID) { this->m_nID = nID; } cSimple.SetID(2); --> SetID(&cSimple, 2); PHP hoz hasonló meghívás: Calc& Add(int nValue) { m_nValue += nValue; return *this; } Calc& Sub(int nValue) { m_nValue -= nValue; return *this; } Calc& Mult(int nValue) { m_nValue *= nValue; return *this; } Calc cCalc; cCalc.Add(5).Sub(3).Mult(4); változódefiníciók: balról-jobbra spirál módszerrel jöhetünk , hogy mi a típusa, de a const keywordöt ebbe nem vesszük bele, az mindig balra apply olódik const: const objecktum adattagjait nem lehet megváltoztatni const függvény nem változtathat meg adattagot, még ha az adattag nem is const) const objektum csak const függvényt hívhat meg, ezt a compiler is ellenőrzi get konstans és nem konstans objektumokra: const int& GetValue() const { return m_nValue; } //ezt const és nem constra is meg lehet hívni, & ez nemtom minek int GetValue() { return m_nValue; } //ezt csak non const ra lehet meghívni static: class A { static int b;}; A a.b = 10; //helyes, ha egy objektumra hívjuk meg a static adattagot (A::b java szerint) prototype: //deklaráció egy osztály, fv használatához kell megadni, hogy lehessen előbb használni, mint hogy deklarálva lenne fv: int plusone(int x); main(){ int x; plusone(x); } int plusone(int x){ return x++; } class: class Humidity; //itt deklaráljuk a prototype ot class Temperature{ friend void PrintWeather(const Temperature &cTemperature, const Humidity &cHumidity); //mert itt már használjuka Humidity t }; class Humidity{ friend void PrintWeather(const Temperature &cTemperature, const Humidity &cHumidity); //viszont itt definiáljuk }; void PrintWeather(const Temperature &cTemperature, const Humidity &cHumidity){ std::cout << "The temperature is " << cTemperature.m_nTemp <<" and the humidity is " << cHumidity.m_nHumidity << std::endl; } friend: class A{ int x; friend B; //nem reflexív, nem tranzitív }; class B{ A a; void setA(){ a.x = 10; }; }; friend nem öröklődik! anonymus változó: add(1, 2); return 2 + 3; return Date('1989.09.13'); //a megadott paraméterek egy névtelen változóba kerülnek, csak érték szerint lehet átadni operator overload: nem lehet overloadolni: ?: , sizeof , :: , . (member selector) , .* (member pointer) precedenciát nem lehet változtatni új operátort nem lehet létrehozni x+y nem ugyanaz mint a y+x --> 2 operator overload fv t kell definiálni friend function: ez érthetőbb, mert tudjuk mi a legbaloldalibb paraméter, explicit meg van adva, ha memberként definiáljuk, akkor a this pointer átadódik a tudtunk nélkül viszont érték member változtatáshoz érdemesebb a membert használni mert ha itt változtatok adattagot, az sérti az encapsulation-t Cents{ ... friend Cents operator+(const Cents &c1, const Cents &c2); } Cents operator+(const Cents &c1, const Cents &c2){...} memberként: Cents{ ... Cents operator+(const Cents &c2){...} //az első paraméter a this } =,[],(),-> csak memberként definiálható felül a legbaloldalibb paraméter mindig a this (így ez mindig az adott osztály típúsú változó lesz), ezért a >>, << operátorok nem lehetnek memberként megadva (+ az összes olyan sem, aminek az első paramétere nem az adott objektum) Cents operator-(const Cents &cCents) == Cents Cents::operator-() Cents operator+(Cents &cCents, int nCents) == Cents Cents::operator+(int nCents) Cents Cents::operator-() erre kasztolódik implicit: Cents operator-(const Cents &cCents) I/O: toString: ostream& operator<< (ostream &out, Point &cPoint){ out << "(" << cPoint.x << ", " << cPoint.y << ")"; return out; } read istream& operator>> (istream &in, Point const &cPoint){ //const: így const objektum is kiírható in >> cPoint.x; in >> cPoint.y; return in; } ++i vs i++: Digit& operator++(); // prefix Digit operator++(int); // postfix Digit Digit::operator++(int){ Digit cResult(m_nDigit); //lokális változó--> nem térhet vissza referenciáéval a fv ++(*this); return cResult; } postfix esetben le kell másolni az objektumot, különben nem tudnank visszatérni a megnövelt értékkel --> overhead nem adhat vissza refernciát sem, mert egy temporary változóval tér vissza (): nem fix a paraméterszáma csak member fucntion lehet mátrix elérés: double& Matrix::operator()(const int nCol, const int nRow){ return data[nRow][nCol]; } typedef: castoláshoz (implicit és explicit is) operator int() { return x; } //x egy int adattag így használható az osztály olyan helyeken, ahol int et várnánk el, ugyanígy minden más osztályra is definiálható szabály: referencia szerint visszaadott érték lehet r vagy lvalue is érték szerint visszaadott értékek csak r value k lehetnek copy ctor vs copy assignment operator: Cents cMark(5); // Cents constructor Cents cNancy; // Cents default constructor cNancy = cMark; // Cents assignment operator Cents cMark(5); // Cents constructor Cents cNancy = cMark; // Cents copy constructor! mindkettő default létrejön ha nem adjuk meg (mint a constructor, ezért vana rule of 3) copy ctor 4 eset amikor a copy ctor hívódik meg: 1 egy objekt inicializációja egy másikkal 2 objektum érték szerinti átadásánál 3 visszatérés egy objektummal érték szerint 4 copmiler temporary objektumot készít 2-esből --> a copy ctor mindig egy referenciát vár paraméterként, különben érték szerinti átadás lenne, amihez le kéne másolni az objektumot, de pont ennek a megvalósítását írjuk a copy ctorba copy assignment operator: mindig *this el tér vissza, hogy lesehessen cahinelni: cMark = cNancy = cFred = cJoe; ha önmagának adjuk értékül és van dinamikusan allokált memória, akkor problémához vezethet, ezért mindig check: Cents& Cents::operator= (const Cents &cSource){ if (this == &cSource) //nézzük meg, hogy a mutatott érték ugyanaz-e mint amire az operator hívjuk return *this; m_nCents = cSource.m_nCents; return *this; } deep copy MyString& MyString::operator=(const MyString& cSource){ if (this == &cSource) //check h önmaga e return *this; delete[] m_pchString; //ha van benne valami töröljük, különben memopry leak maradna, m_nLength = cSource.m_nLength; //shallow copy sima memberekre //deep copy dinamikus memberekre if (nullptr == cSource.m_pchString){ //létezik-e egyáltalán a másikban ez az adattag, nem null pointer m_pchString = new char[m_nLength]; //allokáció strncpy(m_pchString, cSource.m_pchString, m_nLength); //minden faszsa, másoljunk } else m_pchString = nullptr; //ha nullpointer legyen ez is nullpointer return *this; } ha nem akarjuk hogy másolható legyen az objektumunk tegyük az operátort és a copy ctort private é, így nincs default, ami van az pedig private dinamikus adatot nem tartalmazó osztályokhoz elég a shallow copy konstruktor inheritance: parent konstruktor meghívása: class A{ int x; A(int x) : x(x){} }; class B : public A{ int y; B(int x, int y) : A(x), b(y){} } az inicializációs lista csak az adott osztályban hívható, leszármazottban nem, tehát B(int x, int y) : x(x), y(y) nem lenne helyes, mert B ből akarnám inicializálni az A adattagját, ami referencia,const esetén nem lenne szabályos , mivel csak 1x lehet inicializálni chain: A B : A C : B C nem hívhatja meg csak a közvetlen őst (B-t, A-t nem) folyamat: C c(1,2,3); rekurzívan amig van ős hívás felmegyünk a legősibb osztály konstruktorába (C hívja B-t, B hívja A-t, A nem hív senkit azaz A konstruktora fut először) meghívódik A inicializációs listája majd A törzse A visszaadja a vezérlést B-nek ... Bre és C re inheritance láthatóság: ha nincs megadva (Derived : Base), alapból private public: minden marad úgy ahogy volt private: a leszármazott ugyanúgy eléri a public és protected membereket a külvilág felé viszont minden leszármazott (Base beli) member private ként viselkedik, azaz nem elérhetőek a leszármazott leszármazottjai nem érik el az ős protected membereit, mivel private nek tekintendők (C:B:A C nem éri el A protected memberjét, mert B-ben private nek tekintendő) protected: a leszármazott ugyanúgy eléri a public és protected membereket a külvilág felé viszont minden leszármazott (Base beli) member private ként viselkedik, azaz nem elérhetőek a leszármazott leszármazottjai elérik az ős protected membereit, mivel protected nek tekintendők (C:B:A C eléri A protected memberjét, mert B-ben protected nek tekintendő) tehát a leszármaztatás láthatóságától függetlenül a leszármazott ugyanúgy lát mindent, viszont innentől a leszármazott memberek a származtatás láthatóságának megfelelően viselkednek function inheritance: ha meghívok egy leszármazott member fv. t akkor megnézi, hogy van e neki iylen, ha nics, rekurzívan az ősökben kezdi el keresni: A {void x(){cout<<"A");}}; B : public A{void x(){cout<<"B");}}; B ből meghívni A-t ugye a :: operátorral lehet B : public A{ void x(){ A::x(); //ha nincs scope (csak x() lenne), akkor rekurzívan hívná magát --> végtelen ciklus cout<<"B"; } } hiding: A {void x(){cout<<"A");}}; B : public A{ A::x; //így A x ét fogja használni, NINCS () és NINCS TÍPUS SE (void volt x alapból) }; B b; b.x(); //A x-ét hívja láthatóság megváltoztatása: A {void x(){cout<<"A");}}; B : public A{ private: A::x; //így A x e private lesz, nyílván csak B ből hívva }; virtual inheritance: ugye csak 1x fog létrejönni a duplikált osztály diamond problémába ebben az esetben: http://www.learncpp.com/images/CppTutorial/Section11/PoweredDevice2.gif virtual nélkül 2x hívódik meg az ős, a virtual miatt csak 1x a virtual osztály leszármaztatása esetén a "most derived" osztály hívja meg annak konstruktorát, tehát itt nem érvényes a szabály, hogy csak a közvetlen őst hívhatja meg egy leszármazott osztály class A class B : virtual public A class C : virtual public A class D : public B, public C D d(); //A konstruktorának meghívása D konstruktorából történik szabály: először mindig a virtuális ősosztályok jönnek létre virtual function Derived d; Base *b = d; d.x(); //hívás Base::x() re értékelődik ki, megnézi, hogy Base nek van e x fv-e, mivel van ezért azt hívja meg //(ha nem lenne x fv-e akkor ugye felfelé keresné a definíciót) virtual kulcsszó hasznáalta esetén viszont lefelé kezd el menni, a "most derived" definícióig vagy amíg az aktuális típus szerinti definíciót meg nem találja (aktuális típus itt Derived) A &a = new C(); //itt a D beli definíciót már nem nézi, mert az aktuális típus C a leszármazott függvények szignatúrájának viszont meg kell egyeznie a virtual kulcsszó csak a legősibb osztálynál kell de nem árt ha ott van, hogy jelezze h virtual kovariancia: class Base{ public: virtual Base* GetThis() { return this; } }; class Derived: public Base{ virtual Derived* GetThis() { return this; } //itt megengedett, hogy nem eggyezik a szignatúra }; destruktor minidg legyen virtual leszármaztatásnál, különben csak a Base által allokált memóriát törölné (mert annak a destruktorát hívná) a Derived class Base függvénye mindig meghívható :: val: Base &b = new Derived(); b.Base::x(); binding: azonosító hozzárendelése egy gépi kódú memóriacímhez early: direkt cím hozzárendelése egy címhez, compile time lehet tudni h melyik fv t hívom meg Add(1,2); late: pointer a fv re, majd ennek meghívása --> az adott címen lesz a gépi kódú cím, amit a pointerből kiolvasunk , ez lassabb int (*pFcn)(int, int) = Add(); //fv pointer virtuális tábla: virtualis metodusokra mutato pointerek táblázata, minden osztályhoz 1 polimorfizmus megvalósításához vpointer a vtable re mutat egy fv híváskor valami ilyesmi történik: Animal *a1 = new Tiger(); a1 -> getWeight(); //--> *(a1 -> vptr1 -> getWeight()) minden virtual fv t tartalmazó OSZTÁLYHOZ tartozik egy (tehát osztályonként 1) class Base{ public: FunctionPointer *__vptr; virtual void function1() {}; virtual void function2() {}; }; class D1: public Base{ //*__vptr itt is itt van, mert leszármazott public: virtual void function1() {}; }; class D2: public Base{//*__vptr itt is itt van, mert leszármazott public: virtual void function2() {}; }; *__vptr: automatikusan létrejön, egy pointerrel mindig nagyobb lesz az objektum az osztályhoz tartozó vtable re mutat vtable: a benne lévő entryk mindig a "most derived" függvényre mutatnak http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/ Base *b = new Derived(); //*__vptr a Derived vtable jére mutat b = new Base(); //most már a Baswe vtable jére fog mutatni, eml.: a *__vtable egy leszármazott member pure virtual: mint javaban: ha van benen pure virtual fv akkor a class is az (de itt lehet a törzsében kód) a leszármazottnak definiálni kell minden pure virtual fv-t különben ő is pure virtual marad kikényszeríthetjük vele, hogy egy fv-t definiáljunk: b.toString(); //ha B ben nincs definiálva toString(), error, ha A ban nem pure virtual a fv, akkor A::toString hívódna interface: csak abstract fv eket tartalmaz nincs külön kulcsszó, valójában egy osztály, amire nincs nyelv specifikus megkötés iostream: flag: ki be kapcsolható group: flagek csoportja ami egyszerre be kikapcsolható, általában kölcsönösen kizárják egymást manipulator: a kimeneti streamet módosítják pl.: hexaba írja ki, boolalpha, space ek heylére * fájl objektum kimegy a scope ból --> aut bezáródik a destruktor által, de a bufferben maradhat adat ami elvész, file.close esetén viszont flush ölődik, így érdemes a close ot mindig explicit meghívni ofstream outf("Sample.dat"); //fájl létrehozása írásra ifstream inf("Sample.dat"); //olvasás írás, olvasás: >>, << >>: spaacenként olvas nem new lineonként --> getline olvas lineonként while(!file){} //olvasás a fájl végéig (EOF = 0) file pointer: ahol épp áll a kurzor, open esetén a fájl elején, append esetén a fájl végén seekg(): relatív mozgás a kurzor aktuális pozíciójától ios::curr a current pointer RELATÍV pozíciója ios::begin/end RELATÍV pozíció az aktuális kurzortól file.seekg(-10, ios::curr) lépjünk vissza 10 byteot tellg() kurzor ABSZOLUT pozíciója template: template <typename T> == template <class T>, de a typename jobb mert nem utal arra h feltétlen egy osztályt kell megadni template: használatkor csak 1x példányosítódik a function a megfelelő paraméterekkel (ha nem hasznőljuk nem jön létre) expression pasrameter: template <typename T, int nSize> // nSize egy expression parameter class Buffer{ private: T m_atBuffer[nSize]; //eml.: tömb mérete csak compile time constant lehet, nSize az lesz, szóval ez //így OK public: T* GetBuffer() { return m_atBuffer; } T& operator[](int nIndex){ return m_atBuffer[nIndex]; } }; !!!: Buffer<char, 10> != Buffer<char, 11> //az nsize compile time constant, így ennek 2 külön osztálynak //kell lennie template member specializáció: template <typename T> class Storage{ private: T m_tValue; public: Storage(T tValue){ m_tValue = tValue; } }; //a template osztályon kívül definiáljuk az egyéb speciális elvárásainkat az egyes típusokkal szemben Storage<char*>::Storage(char* tValue){ //char* ra egy más fajta constructort szeretnénk megadni m_tValue = new char[strlen(tValue)+1]; strcpy(m_tValue, tValue); //itt copyzunk, mert dinamikus az adat, int nél nem kellene } template class specializáció: általános Storage8 template <typename T> class Storage8{ void print(){ cout << "basic"} } specializált Storage8 bool adattípusra template <> //itt megadjuk hogy ez egy template lesz, aminek nics paramétere class Storage8<bool>{ //itt megadjuk h bool típusra szeretnénk használni void print(){ cout << "bool"} } használatkor ugyanúgy Storage8<bool> t kell megadnunk, de a metódusai a specializált classnak megfelelőek lesznek --> print --> "bool" template class partial specialization: szűkítjük az elvárásainkat: megadunk egy konkrét paramétert (ha több van), vagy a paramétert szűkítjük konkrét param: template <typename T, int nSize> class Buffer{} template<int nSize> void PrintBufferString(Buffer<char, nSize> &rcBuf){ //itt T-t megadtuk, nSize-ot még nem std::cout << rcBuf.GetBuffer() << std::endl; } paraméter szűkítés: template <typename T> class Storage{ Storage(T tValue){ m_tValue = tValue; } } template <typename T> class Storage<T*>{ //szűkítés pointer típusra Storage(T* tValue){ m_tValue = new T(*tValue); //itt new-zunk } } exception: felfelé propagálódik a megfelelő catch block kezeli ha van, ha nincs tovább propagálódik nyílván addig amíg le nins kezelve, vagy a mainbe jutunk try{ throw <típus>; } catch(<típus>){ <kezelés>} catch(<más típus>){<kezelés>} ... throw //így típus nélkül ha van kapott exception, akkor továbbdobja, egyébként hiba catch all: catch(...){ cerr << "This catches every exception" << endl; } viszont ez csak az utolsó catch lehet, különben compiler errot kapunk exception class: class ArrayException{ private: std::string m_strError; ArrayException() {}; // not meant to be called public: ArrayException(std::string strError): m_strError(strError){} std::string GetError() { return m_strError; } } try{ int nValue = anArray[5]; //[] operátor exceptiont dob } catch (ArrayException &cException){ // !! referencia szerint van átadva, hogy ne másolódjon le (költséges lenne) cerr << "An array exception occurred (" << cException.GetError() << ")" << endl; } std::exception --> exc.what()-al kérem le az üzenetet: catch (std::exception &cException){ cerr << cException.what(); } inheritance: úgy működik mint minden más osztálynál try{ throw Derived(); } catch (Base &cBase){ //mivel Derived is a Base, ezt fogja előbb elkapni cerr << "caught Base"; } catch (Derived &cDerived){ //ide nem jutunk el, cseréljük meg a 2 catch ágat, mindig a specifikusabb cerr << "caught Derived"; //exception legyen feljebb } std::auto_ptr: ha kimegy a scope ból a mutatott érték, a delete automatikusan lefut try{ pJohn = new Person("John", 18, E_MALE); auto_ptr<Person> pxJohn(pJohn); ProcessPerson(pJohn); //delete pJhon nem futna le, ha a ProcessPerson exceptiont dobna--> memory leak, de az auot_ptr //megoldja a törlést } catch (PersonException &cException){ cerr << "Failed to process person: " << cException.what() << endl; } destruktorba ne tegyünk exceptiont!! release(); //a destruktort NEM FUTTATJA LE az objektumon, null ra állítja a pointert reset(); //a destruktort LEFUTTATJA az objektumon, null ra állítja a pointert wchar: wide char (nagyobb mint 8 bit) STL típusok: sequence container: vector: pusch_back(12); //mindig a végére deque: az elejére és a végére is lehet pusholni: deq.push_back(1); deq.push_front(2); //deq = {1,2} list: csak az eleje és a vége elérhető, egyébként iterátorral tudunk rajta végigmenni associative container: rendezve vcannak az elemei set: minden elem 1x multiset: minden elem többször is map: minden elem 1x kulcs-érték pár multimap: minden elem töbször is,pl szótárban egy szónak több jelentése is lehet (dictionarynak is hívják) container adapters: stack, queue, priority queue iterátorok: elérhetjük az adatszerkezet elemeit és nem kell tudnunk hogy az hogyan van implementálva műveletek (pointerre hasonlít): *: a mutatott érték dereferálása ++: következő elem ==, =!: összehasonlítás =: értékadás (begin(), end()) member functions: begin(), end() //az end nem az utolsó elemre hanem az az utáni részre mutat! cbegin(), cend() //konstans iterátort ad vissza típusok: container::iterator container::const_iterator pl.: vector<char>::iterator, map<int, string>::const_iterator algorithms: iterátorokkal dolgozik: list<int> li; list<int>::const_iterator it; it = min_element(li.begin(), li.end()); it = find(li.begin(), li.end(), 3); vector<int> vect; sort(vect.begin(), vect.end()); reverse(vect.begin(), vect.end()); string: string: 8 bit karakterek wstring 16 bites capacity(): megadja hogy mennyi bájtnyi adatot tudunk még beletenni reallokáció nélkül reserve(type_size unSize): ha tudom h nagy stringem lesz, lefoglalok neki unSIze byteot, elkerülve a reallokációt at vs []: at exceptiont kezel rossz index esetén append: string hozzáadása push_back: karakter hozzáadása insert: karakterek beszúrása a stringbe aaaa.insert(2, string("bb")) --> "aaabba" iterálás egy adatszerkezeten amin csak iterátorral lehetne: for (auto x: myvector){ //itt x read only cout << x; } for (auto& x: myvector)... //így már nem static_assert: fordítás idejű ellenőrzés (sima assert futásidejű) static_assert(sizeof(int) >= 4, "int needs to be 4 bytes to use this code"); inicializációs lista hívás: std::vector<int> vArray[5] = {3, 2, 7, 5, 8}; //a megfelelő típus konstruktorát hívja ezzel az inicializációs //listával ezt így definiálhatjuk: template <typename T> class MyArray{ private: vector<T> m_Array; public: MyArray() { } MyArray(const initializer_list<T>& il){ //megadtunk egy initializer_list et for (auto x: il) m_Array.push_back(x); } //VAGY SIMÁN MyArray(const std::initializer_list<T>& x): m_Array(x){} //a vector Initializer_list-jét használjuk az //inicialéizációs listában }; ---> int main(){ MyArray<int> foo = { 3, 4, 6, 9 }; return 0; } Initializer lists vs initialization lists: MyStruct(int x, float y): m_nX(x), m_nY(y) {}; //initialization list, amit a konstruktor használ std::vector<int> vArray[5] = {3, 2, 7, 5, 8}; //Initializer list, amit kezdő értékadásnál használunk int a[]{1,2,3}; //uniform initialization std::initializer_list egy típus !!!, amit inicializációhoz használunk X x = {...}, ezt akkor használhatjuk, ha van az X típusnak initializer_list et elfogadó konstruktora uniform inicializáéció: std::vector<int> vArray[5] = {3, 2, 7, 5, 8}; megadhatjuk így is std::vector<int> vArray[5] {3, 2, 7, 5, 8}; type variable { data, elements }; esetén mit használ a fordító?? ha van initializer_listet elfogadó konstruktor akkor azt ha nincs akkor a megfelelő konstruktort próbálja illeszteni konstruktor delegáció: class Foo{ public: Foo(){ // code to do A } Foo(int nValue): Foo(){ //code A, meghívjuk az első kunstruktort // code to do B } }; override: megadjuk vele, hogy egy függvényt felül akarunk írni, ha csak virtualként írjuk meg a felülírandó függvényt a Derived class ba akkor ha pl véletlen másképp paraméterezzük, akkor nem a Base-t fogjuk felülírni, hanem egy új fv.t hzunk létre, de a fordító nem fogja ezt hibának jelezni, az override al az elkerülehető, emrt egyértelműen emgadjuk hogy a Base egy függvényét felül akarjuk írni !!! az override a függvény szignatúra végén van, nem az elején mint javaba: void f() override; //das is gut override void f(); //NEIN NEIN NEIN class Base{ virtual void A(float=0.0); virtual void B() const; virtual void C(); void D(); }; class Derived: public Base{ virtual void A(int=0) override; // compile error: Derived::A(int) nem írja felül Base::A(float) -t virtual void B() override; // compile error: Derived::B() nem írja Base::B() const //mert az const ez meg nem virtual void C() override; // ok! Derived::C() overrides Base::C() void D() override; // compile error: Base::D() nem virtual, nincs mit felülírni }; final: egy osztály, vagy változó nem írható felül class Base{ virtual void A() final; }; class Derived: public Base{ virtual void A(); //compiler error A nem írható felül, mert Base ben final }; class Base final {}; class Derived: public Base {}; //Base final, így nem lehet belőle származtatni default: class Foo{ Foo(int x); Foo() = default; //megadtunk konstruktort, így a compiler nem generálna defaultot, viszont így még is fog }; delete: egy function letiltása class Foo{ Foo& operator=(const Foo&) = delete; //copy assignment letiltása Foo(const Foo&) = delete; // copy construction letiltása void Foo(long long); // long long al hívható void Foo(long) = delete; // sima long al nem }; class Foo{ void Foo(long long); // long long al készíthető objektum template<typename T> void Foo(T) = delete; // minden más típussal nem }; külső libraryk: 2 részből áll egy lib: headerök előre lefordított binary 2 fajtája: static: fordításkor behúzza az egészet a programunk, így nem kell külső fájlokat (pl dll eket) keresni, emrt a library része lesz a programunknak, kiterjesztése windwoson .lib dynamic: .dll, futás közben húzza be a programunk, ezért mindig el kell tudnia érni azt, viszont könnyen cserélhető import library: kis .lib és egy .dll fájl ugyanazzal a névvel -> a .lib behúzza a .dll-t, így az úgy használható, mintha static lenne, azaz része lenne a programunknak delete this: meg lehet tenni ha az objektum new al lett létrehozva A a = new A(11); //erre ok A a(11); //erre nem, undifened behavior viszont nem lehet átállítani, mert const pointer!! this = <akármi> --> compiler error class struct különbség: private class, public struct adattagok alapból leszármaztatás private class esetén ha nem adjuk meg: class A : B == class A : private B public struct esetén ha nem adjuk meg: struct A : B == class A : public B union: csak egy valid értéke lehet, a mérete a legnagyobb elemének mérete union{ char a,b,c,d,e; int a; double d; //union mérete a d mérete mert ez a legnagyobb } nem lehet származtatni belőle, és ő sem származhat senkiből, nem lehet virtual memberje public access (mint structnál) constructor, destructor, assignment operátor -t definiáló objektum nem lehet a tagja lehet konstruktora, és egyéb fv-e!!! union A { int a; unsigned int b; A() { a = 10; } unsigned int getb() {return b;} }; int main(){ A obj; //lefut a konstruktor, a = 10 cout << obj.getb(); //mivel közös a memóriaterület, ezért b értéke is 10 } dynamic_cast: runtime castolás struct Base { }; struct Derived : Base { }; int main() { Derived d; Base *b = &d; dynamic_cast<Derived*>(b); // Invalid, mert a Base nem polymorphic, nincs virtual fv e } void f(signed) //egy int paramétert vár --> signed int modulonak nem lehet real number operandusa: int r, x = 2; float y = 5; r = y%x; char signed típusok: char --> környezettől függ h signed vagy unsigned a másik kettő a megszokott módon viselkedik, viszont mivel a sima char nem eldöntött, ezért ez 3 különböző típus delete 2x: int *p = new int; delete p; delete p; //runtime error, csak 1x szabad deallokálni break csak switchben vagy loop ban használható inheritance típusok: http://www.studytonight.com/cpp/types-of-inheritance.php enkapszuláció: az adat és a rajta végezhető műveletek egybefoglalása (egy osztályba) wchar_t: unicode karakterekhez default constructor: ha adtunik meg konstruktort, akkor nem jön létre, különben igen //public: SomeClass(int x){}; --> nincs default constructor //ez vszeg hülyeség //paraméter nélküli, tehát A(int x) nem a default, nem az első konstruktor amit megadunk a default, hanem a //paraméter nélküli, ha nem adunk meg paraméter nélkülit, akkor a compiler csinál eggyet register: egy kulcsszó, ami hatására a változó nagyobb eséllyel kerül a CPU cache ébe #undef: törli a #define al megadott macrot strlen(input) != sizeof(input)/sizeof(input[0]): az strlen eggyel kevesebbet ad, mert nem számolja bele a \0-t //GEEKS FOR GEEKS TESTS - http://geeksquiz.com/c-plus-plus/ valid inicializáció: class X { public: int x; }; X a = {10}; ha megadunk copy ctort, akkor a compiler nem generál default (sima) konstruktort: class Point{ int x, y; public: Point(const Point &p) { x = p.x; y = p.y; } }; int main(){ Point p1; //!! nincs default konstruktor Point p2 = p1; } viszont ha adunk meg sima konstruktort, akkor, generál default copy ctort -t (cause y not): class Point{ int x, y; public: Point(const Point &p) { x = p.x; y = p.y; } }; int main(){ Point p1; Point p2 = p1;//OK mert a compiler generált copy ctort } malloc csak területet foglal, nem hívja meg a konstruktort class Test{ public: Test(){ cout << "Constructor called"; } }; int main(){ Test *t = (Test *) malloc(sizeof(Test)); //nem ír ki semmit } malloc csak memóriát foglal a megadott byte méretben és egy pointert ad vissza erre a typusra, ha nem sikerült akkor nullptr t ad a new operátor viszont lefoglal memóriát és a konstruktort is meghívja objektum létrehozása egyből a definíció után, cause y not class Test{ public: Test() { cout << "Hello from Test() "; } } a; int main(){ cout << "Main Started "; } output: "Hello from Test() Main Started " copy ctor: Point(const Point p) //MINDIG REFERENCIA SZERINT ADJUK ÁT, különben copyzni kéne az argumentumot,de épp azt írjuk inicializációs listát használjuk: 1 const 2 reference 3 default konstruktor nélküli objektumok inicializációjára conversion ctor: //!!TODO:nézz utána fun: int i; class A{ public: ~A(){ i=10; } }; int foo(){ i=3; A ob; return i; }//A destruktora lefut, i == 10, előbb lefut mint a cout int main(){ cout << foo() << endl; //10 lenne, de mivel i t érték szerint adtuk át, és akkor még az értéke 3 volt, ezért //a másolat is 3 at kap értékül, amivel visszatérünk return 0; } konstruktor - destruktor: a destruktor mindig ellentétesen fut le mint a konstruktor A a; B b(a); //ha a törlődne előbb akkor b t már nem tudnánk törölni { A a[3]; //1. elem ctor, 2. elem ctor, 3. elem ctor, } //3. elem dtor, 2. elem dtor, 1. elem dtor, dinamikus allokáció letiltása: new és new[] operátor megadása üresen, private ként ==-t nem definiőálja a copmiler alapból helyfoglalás/felszabadítás construktor/destruktor sorrend: new called //helyfoglalás Constructor called Destructor called delete called conversion operator csak member lehet (pl: operator int()) private static: class Test{ static int x; }; int Test::x = 0; //ilyet lehet, mert ez egy definíció Test::x = 0; //ilyet nem lehet inheritance ctor sorrend: class Base1 { public: Base1(){ cout << " Base1's constructor called" << endl; } }; class Base2 { public: Base2(){ cout << "Base2's constructor called" << endl; } }; class Derived: public Base1, public Base2 { //: public Base2, public Base1 esetén fordítva futna le public: Derived(){ cout << "Derived's constructor called" << endl; } }; int main(){ Derived d; } //a leszármaztatás megadásásának megfelelő sorrendben hívódnak meg a base construktorok multiple inheritance nél output: Base1's constructor called Base2's constructor called Derived’s constructor calle destruktor fordítva: Derived's destructor Base2's destructor Base1's destructor class méret: class base { protected: int arr[10]; //int legyen 4 byte }; class b1: public base { }; class b2: public base { }; class derived: public b1, public b2 {}; int main(void){ cout << sizeof(derived); //arr változót 2x is megörökölte a derived } hiding: class Base{ public: int fun() { cout << "Base::fun() called"; } int fun(int i) { cout << "Base::fun(int i) called"; } }; class Derived: public Base { public: int fun() { cout << "Derived::fun() called"; } //minden fun nevű fv t elhide ol, //paraméterlistától függetlenül }; int main(){ Derived d; d.fun(5); //derived ben nincs int et váró fun fv, a Base fv-ét pedig elhideolta --> error d.Base::fun(5); // így helyes } object slicing: class Base{ public: virtual string print() const{ return "This is Base class"; } }; class Derived : public Base { public: virtual string print() const{ return "This is Derived class"; } }; void describe(Base p){ //érték szerinti átadás --> létrejön egy Base változó, a Derived rész eldobódik cout << p.print() << endl; } int main(){ Base b; Derived d; describe(b); describe(d); //itt hiába Derived-ot adtam át, a fv Base-t vár, ezért visszakonvertálja //ennek oka, hogy a base szerinti copy ctor hívódik meg } !!!! csak érték szerinti átadásra érvényes, referencia és pointer szerintire nem !!! describe(Base &p) describe(Base *p) copy ctor fun: class A{ public: A(){ cout <<"1";} A(const A &obj){ cout <<"2";} }; class B: virtual A{ public: B(){cout <<"3";} B(const B & obj){cout<<"4";} }; class C: virtual A{ public: C(){cout<<"5";} C(const C & obj){cout <<"6";} }; class D:B,C{ public: D(){cout<<"7";} D(const D & obj){cout <<"8";} }; int main(){ D d1; //legfelülről indul a ctor: 1357, ha az inheritance nem lenne virtual 13157, mivel A 2x hívódik, //1x B ből, 1x C ből D d(d1); //1358, a copy ctorban az ős copy ctor jait explicit meg kell hívni, maguktól nem hívódnak } static virtual function nincs, mert a virtual pont az objektum típúsától függően hívja meg a megfelelő fv t, a static pedig osztályszintű dolog, nem objektumszintű (nincs hozzá objektum) osztályméret: a static nem számít bele a méretbe!! template<class T, class U> class A { T x; U y; static int count; }; int main() { A<char, char> a; //char 1 byte A<int, int> b; //int 4 byte cout << sizeof(a) << endl; //2 cout << sizeof(b) << endl; //8 return 0; } template deefault paraméter: template<class T, class U, class V=double> ugyanúgy megadható mint fv eknél, és ugyanúgy a végén kell lennie --> template<class T=double, class U, class V> // NEM JÓ nem típus template paraméter: konstansnak kell lennie: template <int i> void fun() { i = 20; //const, nem változhat cout << i; } int main(){ fun<10>(); } template metaprogramming, (cause y the fuck not?): template<int n> struct funStruct{ static const int val = 2*funStruct<n-1>::val; }; template<> struct funStruct<0>{ static const int val = 1 ; }; int main(){ cout << funStruct<10>::val << endl; //1024 } 0 paraméterű konstruktor: class Point { public: Point(){} } main(){ Point p; Point p(); //compiler error !!! } strcpy vs memcpy: strcpy(strbuff, str); //str pointertől \0 ig copyzik memccpy(strbuff, str, 10) // str pointertől, 10 hosszan karaktersorozat: char *c = "asds"; //ilyenkor a végére kerül egy \0 is, azaz "asds\0" lesz a memoriaba void a(int) == void a(int x), tehát mindkettő egy int et vár, csak az elsőnél nem lehet elérni inicializációs list vs konstruktori törzsbeli értékadás MyClass(Type a) { //1 a konstruktora variable = a; //2 assignment operator (értékadás) } //3 a destruktora MyClass(Type a):variable(a) { //1 copy ctor a-ra } //2 a destruktora local static variable lifetime: Test::Test() { cout << " Constructor Called. | "; } void fun() { static Test t1; //ez akkor fut le, amikor először idejut a vezérlés } int main() { cout << " Before fun() called. | "; fun(); cout << " After fun() called. | "; return 0; } //eredmény: Before fun() called. | Constructor Called | After fun() called. nem konstans copy ctor: class Test { public: Test(Test &t) { /* Copy data members from t*/} Test() { /* Initialize data members */ } }; Test fun() { cout << "fun() Called\n"; Test t; return t; } int main() { Test t1; Test t2 = fun(); //mivel a copy ctor nem const referenciát vár, viszont egy ilyenhez nem lehet hozzárendelni egy //compiler generated referenciát return 0; } megoldás: 1. a ctor várjon cosnt referenciát 2. Test t2; t2 = fun(); //copy assignment használata const referencia: const értékre csak const referenciát lehet állítani ilyet nem lehet const int a = 10; int &r = a; ilyet lehet: const int a; int a; int a; const int& r; int &r; const int& r; empty class size: class Empty{}; sizeof(Empty); //általában 1, de nagyobb mint 0, gondolom compiler függő class A : Empty{ int a; } sizeof(A); //4!!!, itt az üres osztály méretét nem vesszük figyelembe globális változók 0-ra inicializálódnak, ha nem adunk meg kezdőértéket dinamikus allokáció letiltása: legyen a new és a new[] overrideolva, és private class A{ private: void* operator new(size_t size) {} void* operator new[](size_t size) {} }; nem dinamikus allokáció letiltása (szóval csak dinamikus hozható létre): private destructor --> new esetén nem törlődik automatikusan a változó ha nem new zunk, akkor viszont a destruktor automatikusan meghívódik és mivel private --> runtime error class A { private: ~A(); }; int main() { A *a = new A(); //ez OK A a; //ez nem --> ~A() is private } new és delete operator overload: csak az operatort írhatjuk felül, amivel pedig csak a memóriakezelést módosíthatjuk, new keyword != new operator: new keyword: new operátor meghívása (memoria allokáció)//amit felülírhatunk konstruktor hívás //amit nem, ez mindenképp meghívódik a keyword az, amit használunk általában new vs malloc new visszatér az adott típusú pointerrel malloc void pointerrel tér vissza, és ezt castolnunk kell a megfelelő típusra new egy keyword malloc egy fv new meghívja a constructort, malloc nem nemcs ak memberként overloadolhatóak new és delete operátorok sorrend: allokáció konstruktor destruktor free class Test { public: void* operator new(size_t size); void operator delete(void*); Test() {} ~Test() { cout << "Destructor called \n"; } }; void* Test::operator new(size_t size){ void *storage = malloc(size); return storage; } void Test::operator delete(void *p ){ free(p); } const pointer esetén: const char p*; //konstans char ra mutató pointer, a pointer mutathat máshová char * const p; //itt a pointer konstans, nem mutathat máshová const char *p = new char[2]{'a', '\0' }; char * const cp = new char[2]{'a', '\0'}; p[0] = 's'; //ilyet lehet, mivel a mutatott érték const p = new char[2]{ 'a', '\0' }; //ilyet lehet, a pointer nem const, ezért az általa mutatott terület változhat cp[0] = 'b'; //ilyet lehet, mivel a terület nem konstans cp = p; //ilyet nem lehet, átállítaná a pointert, de az const megj.: char * const cp = "asd"; cp[2] = 'k'; //ez futásidejű hiba, mivel "asd" egy, a compiler által generált terület, ami const, de ez //fordításkor még nem eldönthető, elméletileg a cp definíciója nem tiltja a terület módosítását delete 2x hívása: csak nullptr esetén: int *p = nullptr; delete p; delete p; int *p = new int(12); delete p; delete p; //undefined behavior, de általában runtime error polimorfizmus: definicio: egy típust egy másikkal helyettesítünk class B : A{}; B b; A &a = b; //ilyet lehet, mint pointernél destruktor csak akkor fut le, ha a konstruktor végigfutott exception: van közös base class: exception class A : public exception compiler nem ellenőrzi, hogy elkapták e az exceptiont vagy sem //ez gondolom azt jelenti, hogy lehet throw olni és nem //ellenőrzi listában megadható, hogy az adott fv milyen exceptionoket dobhat: void fun(int a, char b) throw (Exception1, Exception2) de ez nem kötelező, ha oylat dob, amit nem dobhatna, akkor sem történik semmi void fun(int a, char b) throw (A, B) { throw 11; //ez szabályos, hiába nicns a listába az int } ha nem kapjuk el az exceptiont sehol (vagyis a mainben sem): "terminates abnormally" int* == int[]: tehát nincs tömb típus lényegében, csak pointer delete, new overload: void* operator new (size_t sz) { return ::new MyClass(); } void* operator new (size_t sz, void* location) //placement new, megadhatjuk a memoriateruletet is saját paramtéerek átadása esetén így kell meghívni a new t: void* Lama::operator new(size_t size, int age = 27, char* name = "Jozsihun") new(12, "asdsda") Lama(); //that makes sense, NO void operator delete(void* ptr) { ::delete ptr; } const cast: const "eltüntetése" const Node* cn = new Node{10, nullptr}; Node* n = const_cast<Node*>(cn); n->data = 11; //it works méret megadás bitben: struct A { unsigned x : 2; //x mérete 2 bit lesz } A a{3}; cout << bitset<2>(a.x); //x = 3, azaz 10 assert vs exception: hibakezelés kell, az assert errort dob míg exceptiönnél van silent die is -> használj exceptiont az assertet release ben ki lehet kapcsolni BTW decltype: int func1() { return 100; } struct MyStruct { double x; }; class MyClass { private: int x; public: MyClass() { x = 200; }; int getx() { return x; } }; int x = 100; decltype(x) y = x; // type of y is int decltype(func1()) z = x; // type of z is int MyStruct* s = new MyStruct(); decltype(s->x) x1 = 100; // type of x1 is double MyClass* c = new MyClass(); decltype(c->getx()) x2 = x; // type of x2 is int int a[10]; decltype(a) a1; // type of a1 is int[10] reinterpret_cast: egy típust egy másikként kezelünk x innentől legyen int, Animal helyett --> veszélyes használata lehet: raw stream data ból egy aktuális típust hozunk létre castolás: Use dynamic_cast for converting pointers/references within an inheritance hierarchy. Use static_cast for ordinary type conversions. Use reinterpret_cast for low-level reinterpreting of bit patterns. Use with extreme caution. Use const_cast for casting away const/volatile. string copy: struct Person{ int age; char* name; Person(age = 11, name = "Lolkopter") : age(age), name(name){}; } Person p1(22, "John"); Person p2(p1); //itt ugye shallow copyt várnánk, mivel a name egy pointer, ennek ellenére a copy rendesen megtörténik a 2 //pointer nem fog ugyanarra a memoriateruletre mutatni, mivel amikor létrehoztuk p1-et nem mondtunk new-t //a konstruktorba, ezért name lényegében egy char[], tömbök másolása pedig rendesen végrehajtódik a default // copy ctorba, saját memóriaterületük jön létre WTF: int* x = new int(11); int* y = x; *y = 12; cout << *x; //12 char* a = "a"; char* b = a; b = "b"; cout << *a; //a ambigous template specialization esetén error: template<typename T, size_t s> class Array{} template<size_t s> class Array<char, s>{} template<size_t s> class Array<T, 2>{} Array<char, 2> a; //--> error, 2 template re is ileszkedik inicializáló lista: class X { public: int x; }; int main(){ X a = {10}; //ez csak akkor működik, ha x public, ugye structot alapból tudunk {} el inicializálni, de ott ez azért van X a {10}; //mert ott minden alapbol public }