hu pt hu hu hu hu hu pt en hu hu cs hu en hu en hu en hu en hu hu hu en hu en hu en hu es hu de hu hu hu hu hu hu hu de hu hu hu hu hu es hu hu hu hu en hu hu en it hu hu hu hu hu en en hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu eo pl hu hu hu hu hu hu hu hu hu id hu hu hu en ro hu en hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu fr en en it en nl pt ar az ru en en en en en en en ru en en pt en sv en en en fr en en en en es ar en km en en en en ru en en en en ru en da en kn it en en en en de en en en en en kn en en fa hu hu hu hu hu hu hu hu hu es cs hu hu en en hu hu sv fr en en fr ru hbs_lat hu hu hu hu hu hu hu fr fr el ru hbs_lat it fr el ru hu en hu hu hu hu hu hu hu hu hu hu hu de pt de hu hu hu ja el ru de fr it hu hu hu hu hu hu fr th en hu hu hu en en en fr ceb en ceb hu hu hu hu hu hu hu en ru hu hu fr uk hu hu en ceb hu hu ja ja ca hu fr pt hu hu de sv hu hu fr tr ceb en tr en tr en en it en sv hu hu hu hu hu hu hu hu hu hu hu hu es ru hu hu hu hu hu hu hu hu en en en en en hu hu hu hu hu en fa en en en en en en en en en en de en sv ru ru de en en de en ru sv en en sv sv ru de en en de sv ru fr en ru fr en ja de fr en fr fr de sv ja en nb hu hu es hu hu en hu hu hu en hu hu hu hu hu hu hu hu hu hu hu hu hu fr hu hu en hu it hu hu pt hu hu hu en hu en hu en hu hu en hu en hu hu hu hu hu hu hu hu el hu hu hu hu hu hu hu hu hu es hu hu en hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu sq en hu hu hu en hu hu hu hu hu hu hu hu en hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu pt hu hu ca hu hu hu hu hu hu hu hu hu pt hu hu hu hu hu hu hu en hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu ca hu en hu hu hu hu hu hu hu hu hu hu en hu en hu hu en hu hu hu hu ca hu en hu hu hu hu hu hu hu hu hu hu en hu hu hu hu hu el hu hu kn hu hu es hu hu hu hu hu hu hu hu tr hu hu hu hu hu hu hu hu hu hu hu hu hu hu en en en en en en en en de en en hu en en hu en en hu en en hu en cs de hu en id es ar en en ml en pt en en en en nl ja en de en en en zh en de en en en it en de de pl pl en lt en en ja ceb en bg en hu hu hu fr de en ru en zh en hu en sk pt hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu de hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu hu en nl an ar arz as en az ku lt ru sv bg bn bo br hbs_lat ja ca hbs_lat vi da de el eo es et eu fa fi hbs_lat fr de ja lt gu he hi hbs_lat hy it id is it ja sv ka az kk ta ko cs it lb fr lt lv ms en ml be mr ms my ne nl nb nb fr pa als pl pnb pt ro ru bxr hbs_lat si en sk sl sq hbs_lat sv hu ta te en th tr tr oc fy ur uz vi pl zh he da zh fr zh hu hu hu hu hu hu hu hu hu hu
A Wikipédiából, a szabad enciklopédiából
Ez a közzétett változat, ellenőrizve: 2022. április 14.
Ugrás a navigációhoz Ugrás a kereséshez
.h, .hh, .hpp, .hxx, .h++, .cc, .cpp, .cxx, .c++
statikus típusosság, erősen típusos, nem típus biztos, nominatív
GNU Compiler Collection, Microsoft Visual C++, Borland C++ Builder
C, Simula, BCPL, CLU, Algol
A C++ egy általános célú, magas szintű programozási nyelv. Támogatja a procedurális, az objektumorientált és a generikus programozást, valamint az adatabsztrakciót. Napjainkban szinte minden operációs rendszer alá létezik C++ fordító. A nyelv a C programozási nyelv hatékonyságának megőrzése mellett törekszik a könnyebben megírható, karbantartható és újrahasznosítható kód írására, ez azonban sok kompromisszummal jár, erre utal, hogy általánosan elterjedt a mid-level minősítése is, bár szigorú értelemben véve egyértelműen magas szintű.
4.6.1 A felsorolt típus (enum)
5 A C++ programok szerkezete
5.8.1 A new és delete operátorok
Mire a nyelvet szabványosították, már rengeteg C++ nyelvű kód készült, került használatba. Mivel a szabvány fejállományok némileg eltértek az eddigiektől, a bizottság érdekes megoldást választott a kompatibilitás megtartására:
A régi C++ fejállományok (pl. „iostream.h”) továbbra is használhatóak (bár hivatalosan nem támogatottak), de tartalmuk nincs benne a standard névtérben.
Az új, hivatalos fejállományok („iostream”) szinte megegyeznek a régiekkel, de tartalmuk a standard névtérben szerepel.
A szabvány C fejállományok (pl.: „stdio.h”) továbbra is támogatottak, de tartalmuk a globális névtérben van.
A C könyvtárak átvétele szintén a .h eltávolításával történt, beszúrva egy c-t a nevük elé (pl. „stdio.h”-ból „cstdio” lett). Tartalmuk a standard névtérben szerepel.[2]
Windows operációs rendszeren tanuláshoz megfelelő – és ingyenes – eszköz a Code::Blocks. Haladó szinten kényelmes választás a Visual C++ Express Edition, amely ingyen letölthető a Microsoft oldaláról, de több helyen bevallottan eltér a szabványtól.
A legtöbb fordító – ha nem adjuk meg külön – néhány esetben eltér a szabványtól, így optimalizáltabb kódot hozhatnak létre. Természetesen minden esetben lehetőség van a szabvány szerinti fordításra.
Nevét Rick Mascitti találta ki. A C++ név kifejezi, hogy a nyelv a C kibővítése: az inkrementáló operátorra utal a ++.[3]
A kis- és nagybetűs angol ABC, általános írásjelek és a matematikai operátorok, jelek.
A nyelv bizonyos összetevőire (változók, konstansok, függvények stb.) névvel hivatkozunk. A legtöbb fordító csak az első 32 karaktert veszi figyelembe a nevekben. A név első karaktere betű vagy aláhúzásjel (_) lehet, ettől kezdődően már számok is szerepelhetnek benne. Lehetőleg saját névként ne adjunk meg aláhúzásjellel (_) kezdődő nevet, mert ezek a fordító számára vannak fenntartva (pl. __DATE__, __cplusplus, _MSC_VER). A C++ különbséget tesz a kis- és nagybetűk között (case-sensitive). Az alma név nem ugyanaz, mint az Alma név.
Ezek a kifejezések a nyelv részei, önmagukban nem használhatóak névként.
A megjegyzések olyan karaktersorozatok, amelyeket a program dokumentálása érdekében használunk. A fordító nem veszi figyelembe a programban elhelyezett megjegyzéseket.
A C++-ban nem vezethetünk be új operátorokat, de majdnem mindegyiket túlterhelhetjük. Van infix, prefix és postfix jelölésű operátor is. Általában meghatározott számú operandusuk lehet (egy, kettő vagy három), kivéve a függvényhívás operátor (operator()), amelynek bármennyi operandusa lehet.
Az elsőbbségi (precedencia-) szabályok a kifejezések kiértékelésének helyes sorrendjét írják elő. A kiértékelés során először mindig a magasabb precedenciájú kifejezés értékelődik először.
Bitenkénti kizáró VAGY és értékadás
Bitenkénti megengedő VAGY és értékadás
A közönséges függvényekhez hasonlóan a legtöbb operátort is túl lehet terhelni, ami a felhasználói típusok kényelmesebb, szabványosabb használatát teszi lehetővé, aritmetikai operátorokkal (+, +=), és az std::cout-hoz való << operátorral.
A C++ -ban minden felhasznált névről meg kell mondanunk, hogy mi az, amit képvisel, tudatnunk kell a fordítóprogrammal a típusát. Megkülönböztetünk egyszerű és összetett típusokat. Egyszerű típusok az egész típusok (előjeles és előjel nélküli), a lebegőpontos típusok, a karaktertípusok, a bool és a void. Összetett típusok az alaptípusok felhasználásával felépített tömb-, mutató- stb. típusok és a felhasználói típusok (pl. osztály).
Az alapvető adattípusok mérete fordító- és platformfüggő, de a következők adottak:
1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
1 <= sizeof(bool) <= sizeof(long)
sizeof(char) <= sizeof(wchar_t) <= sizeof(long)
sizeof(float) <= sizeof(double) <= sizeof(long double)
sizeof(N) == sizeof(signed N) == sizeof(unsigned N) (ahol N lehet char, short, int vagy long)
Az itt megadott méretek a „megszokott” 32 bites asztali környezetben általánosak, de nem garantáltak.
A memóriában létrehozott tárolókat névvel látjuk el, amelynek segítségével hivatkozhatunk rájuk. Ezeket a tárolókat változóknak nevezzük.
Konstansoknak azokat a változókat nevezzük, amelyeknek pontosan egyszer, a definícióban adhatunk értéket (ekkor kötelező), és ezt a típusnév elé vagy mögé írt const típusminősítővel jelezzük:
Az „enum” olyan adattípust jelöl, melynek lehetséges értékei egy konstanshalmazból kerülnek ki.
enum Animals {bear, wolf, rabbit};
A fordító balról jobbra haladva nullával kezdve egész értékeket feleltet meg a felsorolt konstansoknak. Ha egy konstansnak egész értéket adunk, akkor a következő elemek ettől a kiindulási értéktől kapnak értéket.
enum Animals {bear = 10, wolf, rabbit}; //wolf és rabbit értéke rendre 11 és 12
A felsorolásban azonos értékek is szerepelhetnek. A deklarációban megadott név típusnévként is felhasználható:
enum Animals {bear, wolf, rabbit}; Animals x = bear;
Előfordulhat, hogy valamely kétoperandusú operátor különböző típusú operandusokkal rendelkezik. Ekkor, beépített típusok esetén, hogy a művelet elvégezhető legyen, a fordítónak azonos típusra kell alakítania az operandusokat. Megkülönböztetünk implicit és explicit típuskonverziót.
Implicit típuskonverzió: Ez a fajta konverzió a programozó beavatkozása nélkül automatikusan megy végbe, a C++ definíciójában szereplő szabályok alapján. A „szűkebb” típus adatvesztés nélkül konvertálódik a „szélesebb” típusra, ha szükséges.
int x = 10; float f = 1.23; x + f; //A kifejezés float típusú lesz, 10.00 + 1.23 alakban értékelődik ki
Ebben az esetben adatvesztés léphet fel:
float f = 1.23; int x = f; //x értéke 1 lesz, a törtrész elvész
(típusnév)kifejezés (pl.: (char *)c)
Explicit konverziót a programozó előírhat a típuskonverziós operátorok használatával is.
Futásidejű konverziókat végezhetünk vele. A típus osztályra mutató pointer, referencia, vagy void* típusú kifejezés, míg a ptr pointer vagy referencia típusú kifejezés lehet (a típusnak megfelelően). Ha a konverzió nem végezhető el, akkor pointerek esetén 0 lesz az eredmény, referenciáknál pedig bad_cast kivételt vált ki.
Visszafordítható típuskonverzió egymásba konvertálható típusok között. A típusnak és arg típusának fordítási időben ismertnek kell lennie.
Úgy, mint a (típus)arg kifejezés, csak a reinterpret_cast nem használható const vagy volatile minősítés megszüntetésére.
Akkor használjuk, ha fel akarjuk oldani a const vagy volatile típusmódosítók hatását egy adott mutatón vagy referencián. A típus és arg típusának azonosnak kell lennie, kivéve a fent említett típusmódosítókat.
Amikor egy változót definiálunk, a memóriában létrejön egy megfelelő méretű tároló, amelybe bemásolódik a kezdőérték.
A fenti példában egy int típusú változó címének tárolására használható tároló jön létre. A címet a címképző operátorral (&) érhetjük el.
int x = 7; p = &x;
Most az x név és a *p (a p által mutatott tároló) érték ugyanarra a memóriaterületre hivatkozik.
* p = x + 6;
A kifejezés hatására x értéke 13 lesz.
A referencia a pointernek egy olyan változata, amelyet kötelező inicializálni, és az értékét később nem lehet megváltoztatni, valamint használatának szintaktikája is különbözik:
int x = 10; int &r = x; /* 'r' most 'x'-re mutat */ r = r + 1; /* ugyanaz, mint x = x + 1 */ &r; /* ugyanaz, mint &x */
Elterjedt nézet, hogy a referenciák minőségileg különböznek a pointerektől, ezért nem tartalmazhatnak NULL-értéket, illetve érvénytelen címet. Ez nem egészen igaz, tekintsük az alábbi példákat:
int &ref1= *(int *)0; int &ref2= *new int; delete &ref2; int &ref3= *(int *)malloc (sizeof (int)); free (&ref3);
Itt ref1 tartalma nulla, ref2 és ref3 tartalma érvénytelen (már felszabadított) memóriacím.
A fordító a programban használt neveket különböző névterekben (namespace) tárolja. Egy névtérben lévő neveknek egyedieknek kell lenniük, azonban a különböző névterekben azonos néven is szerepelhetnek, azaz a névterek a láthatósági szabályokat teszik könnyebben alkalmazhatóvá. Egy névtérben logikailag összefüggő változókat, függvényeket, típusokat tárolunk. Egy osztály/struktúra egyben a nevével azonos nevű névteret is definiál.
// Az Állat névtéren belül szerepel a Farkas és Medve osztályok definíciója namespace Allat{ class Farkas{...}; class Medve{...}; }
Egy névtér elemeire háromféleképpen hivatkozhatunk:
Globális névfeloldással (using direktíva), ekkor a névtér összes eleme használható; használata körültekintést igényel, mivel egész névterek importálásakor könnyen névütközés lehet:
#include   using namespace std; //A standard (std) névtér globális használata int main(int argc, char *argv[]) { cout << "Globális névfeloldás" << endl; //std::cout << "Globális névfeloldás" << std::endl; helyett return 0; }
Explicit névfeloldással megadva a kívánt névteret, közvetlenül a feloldani kívánt elem esetében:
#include   int main(int argc, char *argv[]) { std::cout << "Explicit névfeloldás" << std::endl; return 0; }
A kívánt elem nevének feloldásával (using deklaráció):
# include   using std::cout; //A standard (std) névtérbeli cout globális használata int main(int argc, char *argv[]) { cout << "Csak a cout lett feloldva" << std::endl; return 0; }
Létezik az úgynevezett névtelen névtér, amit arra használhatunk, hogy ne szemeteljük tele a globális névteret, megvédjük magunkat a többértelműségektől.
namespace { //amit el akarunk keríteni }
A C++ programok szerkezete[szerkesztés]
Az úgynevezett „Helló, világ!” programot először Brian Kernigham és Dennis Richie alkalmazta A C programozási nyelv című könyvükben példaprogramként. Mindössze annyit csinál, hogy a képernyőre írja az üdvözletet.
#include   int main() { std::cout << "Helló, világ!"; return 0; }
A kettőskereszttel (#) jelzett sorok az előfordítónak (precompiler) szóló utasítások. Az include utasítás behelyettesíti a hívás helyére a megadott fájl tartalmát. Az iostream fejállomány tartalmazza a megfelelő utasításokat a kiíratáshoz. A main egy függvény, ez a program belépési pontja. Minden C++ nyelven írt programnak tartalmaznia kell. Paraméterei közül az argc a parancssori paraméterek számát adja meg, míg az argv[] egy nullpointerrel terminált, karaktermutatókat tartalmazó tömb, amelyben a paraméterek vannak C-stílusú stringként. A C++-ban a tömböket nullától indexeljük, az argv nulladik eleme a futtatható állomány neve, első eleme pedig az első paraméter.
./program elso masodik (vagy program.exe elso masodik) argv[0] == "program" argv[1] == "elso" argv[2] == "masodik"
A két kapcsos zárójel ({}) közti részt blokknak nevezzük. A cout a C++ standard kimenete, az std:: pedig arra szolgál, hogy a fordító a standard névtérben keresse a cout definícióját. A :: az ún. hatókör operátor. A return visszaadja a vezérlést az őt hívó függvénynek, jelen esetben ez a program futásának befejezését jelenti, ezért az operációs rendszernek. A return mögé írt szám a visszatérési érték, a 0 általában azt jelzi, hogy a program rendben lefutott. A main()-ben ez nem kötelező; ha elhagyjuk, akkor automatikusan 0-t ad vissza.
A program futásának eredménye:
A C++ megkülönböztet standard inputot, outputot, illetve errort. A standard output (cout), amire ír, ez alapértelmezés szerint a képernyő. A standard input (cin) a bejövő adatokat fogadja, alapesetben a billentyűzetet. A standard error (cerr) az az eszköz, ahová a hibaüzenetek érkeznek, alapértelmezetten szintén a képernyő.
std::cout  > ch; // A standard bemenetről beolvasunk a ch változóba std::cerr << "Standard error!";
A C++ megörökölte a C előfordítóját. Az előfordító a tényleges fordítás előtt fut le. Feladatai közé tartozik a forráskód megfelelő átalakítása, szimbólumok és makrók definiálása, illetve felhasználható feltételes fordításra is. Az előfordító által definiált szimbólumok és makrók típus nélküli értékekkel dolgoznak, ezért míg a C – gyengén típusos nyelv lévén – erősen épít az előfordítóra, a C++ nyelvben gyakorta (fordítási és futási) hibák forrásai lehetnek.
A szekvencia az egymás után végrehajtott utasításokat jelenti:
Amikor a program a futásakor egy elágazáshoz ér, megvizsgál egy feltételt, és ha igaz, akkor végrehajtja a megfelelő utasítást.
if(feltétel) utasítás; else utasítás; //az utasítások lehetnek blokkok({}) is, az else ág elhagyható
A feltétel több szempontból való megvizsgálására a C++ a switch szerkezetet adja, de ez korlátozott: csak integrális (int, char és enum) típust lehet vizsgálni.
void Eldontendo(char c) { switch(c) { case 'i': case 'I': cout << "Igen\n"; break; //break; nélkül "átesne" a következő case-be is case 'n': //itt viszont szándékosan nincs break; case 'N': cout << "Nem\n"; break; default: cout << "I/i vagy N/n!\n"; break; //nem kötelező, ha semelyik sem igaz, ide ugrik } }
Ha mégis bonyolultabb többszörös feltételt kell megvizsgálnunk, akkor használhatjuk az else if szerkezetet, azaz az else ágban nyitott if -et.
if(feltétel_1) { utasítás_1; } else if(feltétel_2) { utasítás_2; } else if(...) { } . . . else utasítás;
Egy elágazásban pontosan egy if és legfeljebb egy else ág lehet.
A feltételek kiértékelése balról jobbra történik a logikai operátorok asszociativitásának megfelelően, és csak addig megy, amíg a maradék kifejezéstől függetlenül biztosan igaz vagy hamis lesz az eredmény (lusta vagy rövid záras kiértékelés):
bool l = false; bool k = true; if(l && k) { utasítás; }
A fenti példában a logikai és operátort használtuk, ez a feltétel akkor lesz igaz, ha l és k is igaz. Mivel l hamis, ezért a program csak l-t fogja vizsgálni. Ez fontos lehet, ha mellékhatással jár a feltétel megvizsgálása.
Ciklust akkor használunk, ha ugyanazt a feladatot többször kell elvégezni egymás után. Háromféle ciklus áll rendelkezésre:
for(int i = 0; i < 10; ++i) { std::cout << "i = " << i << std::endl; } // A {} egyetlen utasításnál elhagyható.
A ciklusfeltételben felveszünk egy ciklusváltozót, kezdőértéket adunk neki, és megadjuk, meddig menjen a számláló. A ++ a C++ ún. inkrementáló operátora, megvan a párja (--, dekrementáló) is. Két alakja létezik:
változó++ → postfixes alak, csak a kifejezés kiértékelése után növeli az értékét és létrehoz egy átmeneti változót.
++változó → prefixes alak, nincs átmeneti változó, azonnal növeli az értékét, általában ezt használjuk.
while(feltétel) { utasítás; } // A {} egyetlen utasításnál elhagyható.
Az elöltesztelő ciklus előbb vizsgálja a ciklusfeltételt, aztán hajtja végre az utasításokat. Ez azt jelenti, hogy nem biztos, hogy akár egyszer is lefut.
do { // A {} egyetlen utasításnál elhagyható. utasítás; } while(feltétel);
Ez a ciklus biztos, hogy egyszer legalább lefut.
A C++-ban sok nyelvvel ellentétben a for ciklus szinte egy az egyben megfeleltethető while ciklusnak.
for(inicializál; tesztel; inkrementál) { programrész; } // Ekvivalens ezzel inicializál; while(tesztel) { programrész; // Apró különbség: Ha a 'programrész' 'countinue' utasítást tartalmaz, // akkor 'while' esetén az 'inkrementál' nem fut le. inkrementál; } // Ekvivalens ezzel is inicializál; if(tesztel) do { programrész; } while(inkrementál, tesztel); // Ha a tesztel-nek nincs mellékhatása
Speciális vezérlőszavak a break és a continue: a break kilép a legbelső ciklusból (nincs többszörös break), a continue pedig a ciklus végére ugrik, azaz a feltételvizsgálathoz, átugorva a ciklusmag hátralévő részét.
A goto mint utasítás speciális a vezérlők között, a vezérlés szerkezetének felborítását végzi, egyszerű ugróutasítás.
Szerkezete: goto címke;, ahol a címkét címke: alakban vezetjük be. Túlzott használata átláthatatlanná teheti a kódot, néha mégis alkalmazni kell, például ha többszörösen beágyazott ciklusból kell kilépni.
A C stílusú tömb azonos típusú adatok halmaza, amelyek a memóriában folytonosan helyezkednek el. Csak alapértelmezett konstruktorral rendelkező (minden beépített típus ilyen) típusokból lehet tömböt létrehozni. A tömb elemeire a tömb nevével és az indexelő operátorral ([]) hivatkozhatunk:
int t[10]; //10 elemű statikus tömb, más néven vektor for(int i = 0; i < 10; ++i) { t[i] = i; //az i-edik index értéke legyen i }
Elemi típusok foglalása esetén a tárolók kezdeti értéke nem definiált (legtöbbször memóriaszemét, bizonyos futási környezetekben lehet csupa nulla). Osztályok esetén minden elem konstruktora külön meghívódik.
A C++-ban a tömböket nullától indexeljük. A C++-fordító nem ellenőrzi a tömbindexeket, ezért hibás indexeléssel is lefordul a program, de futás közben ez több problémát is okozhat. Egyrészt felülírhatjuk a memóriában előtte vagy utána lévő adatainkat, másrészt akár olyan memóriaterületre próbálhatunk meg írni (vagy onnan olvasni), amely nem a mi programunkhoz tartozik, ekkor az operációs rendszer – amennyiben a hardver érzékeli, ill. támogatja és saját maga is elég fejlett ehhez – megszakítja a programunk futását és értesíti a felhasználót. Ennek formája operációs rendszerenként és felhasználói felületenként változó (Segmentation fault, General protection fault, Access violation…).
int t[10]; // 10 elemű tömb létrehozása // tömb feltöltése std::cout << t[10]; //lefordul, de hibás, mivel nulla az első elem indexe, t[9] a legmagasabb hivatkozható elem
Létrehozhatunk többdimenziós tömböt is:
A leggyakrabban használt a kétdimenziós tömb, azaz a mátrix:
int sizeN, sizeM; sizeN = sizeM = 5; int t[sizeN][sizeM]; //5*5 mátrix for(int i = 0;i < sizeN;++i) { for(int j = 0;j < sizeM;++j) { t[i][j] = i + j; // az i -edik sor j -edik oszlopa legyen i+j } }
A tömb neve kifejezésekben a nulladik elemre mutató pointerként érvényesül. Gyakran nem tudjuk előre a tömbök méretét, sokszor csak futásidőben derül ki, ekkor dinamikus memóriakezelést kell használnunk. Az egy értékre, illetve a több értékre mutató pointerek között nincsen szintaktikai különbség, a programozónak kell tudnia a programlogika alapján, hogy mikor melyikkel van dolga.
int meret; std::cin >> meret; // Beolvasás felhasználótól int *t = new int[meret]; // Foglalás for(int i=0; i > t[i]; // Beolvasás a tömb i-edik elemébe } // Tetszőleges feladat megoldása itt, pl rendezés, átlag számítás, stb... delete[] t; // A dinamikusan foglalt memória felszabadítása
A tömbök és pointerek "mechanikai" azonosságát jól demonstrálják az alábbi sorok, melyek mind ugyanazt jelentik:
t[3] = 9; * (t+3) = 9; // +3: 3 elemnyivel arrébb; *: mutató feloldása, hogy megkapjuk a "rekeszt" * (&(t[2]) + 1) = 9; // &: A második "rekesz" címe; +1: 1 elemnyivel arrébb; *: mint előbb
t[0] = 9; * (t+0) = 9; * t = 9;
C++-ban tömbök helyett gyakran az STL részeként rendelkezésre álló std::vector sablont szokták alkalmazni. Lásd: Tárolók (STL).
A programozás során gyakran találkozhatunk olyan esetekkel, amikor több különböző adatot egy egységként kell kezelnünk, például egy könyvnek van szerzője, címe, kiadója, stb. A C++ nyelvben a struktúra (struct) különböző típusú adatok együttese:
//a Book struktúra definíciója struct Book { std::string title; std::string writer; int year; };
A string típus karaktersorozatot jelöl, és a standard névtérben helyezkedik el. A struktúrák adattagjaira a pont operátor (.) segítségével hivatkozhatunk és adhatunk nekik értéket:
//Book struktúra megadása tagonként Book b; //típusként kezelhetjük b.title = "A C++ programozási nyelv"; b.writer = "Bjarne Stroustrup"; b.year = 2005; //Book kezdőértékadása Book c = {"A C++ programozási nyelv", "Bjarne Stroustrup", 2005}; //c azonos tartalmú b-vel
Struktúrákat a C nyelvben is lehet használni. A C++-ban a struktúrákat kibővítették, hogy alkalmasak legyenek az objektumorientált programozás megvalósítására:
Rendelkezhetnek konstruktorral és destruktorral
Szabályozható az adattagok elérése
A struktúra minden tagjának láthatósága alapértelmezetten nyilvános (public), ettől eltekintve lényegében ekvivalens a class szerkezettel, ahol az alapértelmezett elérés zárt (private).
struct Minta {int x}; struct A {int x; int t[10]}; struct B {int x; int *t}; // ... A a; B b; // ... a.t[3] = 9; b.t[3] = 9;
Az A struktúra „fizikailag” tartalmazza a 10 darab int típusú értéket, a sizeof(A) ennek megfelelően 10 intnyivel nagyobb, mint a sizeof(Minta). A B struktúra csak egy pointert tartalmaz, amely „mögé” tetszőleges számú elemnek lefoglalhatunk memóriát, ekkor a sizeof(B) csak egy pointer méretével több, mint a sizeof(Minta). A tömb elemeire hivatkozáskor a két eset között nincsen szintaktikai különbség, de a struktúra másolásakor óvatosnak kell lennünk.
A C nyelv kidolgozásakor a takarékos memóriahasználat érdekében több megoldást is beépítettek a nyelvbe:
Az uniók (union) lényege, hogy ugyanazt a memóriaterületet több változó közösen használja (persze nem egyidejűleg). Leggyakrabban gépfüggő adatkonverziók megvalósítására használják.
A bitmezők az egy bájtnál kisebb helyfoglalású változókat egyetlen bájton tárolják. Gyakorlati hasznuk főleg a hardverek vezérlésénél van.
A C++ az objektumorientált programozás megvalósításához egyrészt kibővíti a struktúrákat, másrészt bevezeti a class típust. Mindkettő alkalmas osztály definiálására. Egy osztály (class) adattagjának háromféle elérhetősége lehet:
public, nyilvános, mindenki számára elérhető
private, privát, csak az osztályon belülről, illetve barátosztályokból és -függvényekből lehet elérni
protected, védett, a származtatott osztályok számára közvetlen elérhetőséget biztosít. A private tagok a leszármazottakból csak az ősosztály tagfüggvényeiből (metódusok) elérhetőek.
//Egy egyszerű osztály class SimpleBook { public: SimpleBook(std::string param_cim) {cim = param_cim;} private: std:string cim; };
A programozó által definiált híján minden osztály és struktúra rendelkezik alapértelmezett konstruktorral (típusnév()), másolókonstruktorral (típusnév([const] típusnév&)) és értékadó operátorral (operator=([const] típusnév&)), ami egyszerű adatszerkezet esetén általában megfelelő és hatékony.
A függvény (function) a program olyan névvel ellátott egysége, amely a program bármely pontjából hívható. Általában olyan utasítássorozatokat tartalmaz, amelyekre gyakran van szükség, de ismételt megírásuk fárasztó vagy helyigényes. A hagyományos C++-program sok kis méretű, könnyen karbantartható függvényből épül fel.
A Pascal nyelvben megkülönböztettünk függvényeket (function) és eljárásokat (procedure) aszerint, hogy volt-e visszatérési érték vagy sem. A C++-ban azonos módon definiálhatjuk a kettőt:
//Eljárás, nincs visszatérési érték void func_1(){ std::cout << "Hello World!" << std:endl; } //Függvény, string típusu visszatérési érték std::string func_2(){ return "Hello World!"; }
A void a C++ általános típusa, a függvény neve elé írt típus a visszatérési értéket jelöli. A fenti példában tájékoztattuk a fordítót, hogy nem lesz visszatérési érték. A függvény neve után írt zárójelekben lehetnek a függvény paraméterei (ha vannak neki)
//Kiírja a megadott karaktersorozatot void func(std::string msg){ std::cout << msg << std::endl; }
A függvényhívás előtt meg kell adnunk a függvény deklarációját, ami jelzi a fordítónak, hogy egyáltalán létezik a függvény. A definíció nem feltétlenül esik egybe a deklarációval, a programon belül bárhol elhelyezkedhet. A deklarációban szerepelnie kell a visszatérési értéknek, a függvény nevének és paraméterlistájának. Ezeket együttesen (név és paraméterlista) aláírásnak (szignatúra) vagy a függvény prototípusának nevezzük.
#include   //deklaráció void func(std::string msg); std::string func_2(); int main(int argc, char *argv[]) { //hívás func("Hello"); std::cout << func_2(); return 0; } //definíció void func(std::string msg) {std::cout << msg << std::endl;} std::string func_2() {return "Hello World!";}
A főprogram a második függvény által visszaadott karaktersort átadta a cout << operátorának, így a szöveg megjelenik a szabványos kimeneten.
A C++-ban kétféle paraméterátadási mód van:
Érték szerinti, az átadott típusból másolat készül a memóriában, az eredeti értéket nem módosítja a függvény.
Cím szerinti, paraméterként az átadott típus referenciája szerepel, a függvény módosíthatja a paramétereket.
//a és b összegével tér vissza int sum_1(int a, int b) {return a + b;} //az eredményt c-ben tárolja void sum_2(int a, int b, int &c) {c = a + b;}
A második változat harmadik paramétere nem c értéke, hanem a memóriában elfoglalt címe.
A C++-ban minden paraméterátadás érték szerint történik, de referenciák vagy mutatók átadásával azonos hatás érhető el, mint a referencia alapú nyelvekben.
Kis objektumok esetén (pl. egy int) az érték szerinti átadás általában hatékonyabb, de ha a függvénybeli módosításokat nem akarjuk elveszíteni, muszáj referenciaként vagy mutatóval átadni. Fordítva: nagy objektumok másolása általában költséges, így ha tehetjük, kerüljük, de ha nem adhatunk const referenciát valamiért, és nem akarjuk, hogy módosíthassa a függvény az objektumunkat, akkor kénytelenek vagyunk érték szerint átadni. Az ekkor (nagy objektumok másolásakor) fellépő költségek minimalizálásra több technika is született, ilyen például a Copy on Write (CoW, csak akkor másolunk ténylegesen, ha muszáj).
class A { public: A(){...} A(const A & a){...} ~A(){...} ... private: std::string s, k; }; A func_val(A a){ return a; } A func_ref(A & a){ return a; } A x; func_val(x); func_ref(x);
A példában az első függvényhíváskor meghívódik az A osztály másoló konstruktora, hogy inicializálja a-t x-szel, majd a függvény által visszaadott objektumot is inicializálja, végül meghívódik a destruktor a-ra és a visszaadott objektumra. Eközben az A osztály tagjainak is meghívódik a konstruktoruk és a destruktoruk.
A második függvényhívás az objektum címét adja át, így csak a visszaadott példányt kell inicializálni.
A függvénydefinícióban bizonyos paraméterekhez alapértelmezett értéket rendelhetünk. Ezt az értéket a program akkor használja, ha az adott argumentum nem szerepel a listában. Alapértelmezett értékkel rendelkező paraméter után csak ugyanilyen paraméterek szerepelhetnek a formális paraméterlistában.
void sayHello(std::string msg = "Hello"){ std::cout << msg << std::endl; } //A függvény hívása sayHello(); sayHello("Hello"); //A kettő ugyanazt jelenti
Inline függvény esetén a függvényhívás helyére a függvény kódja helyettesítődik be fordítási időben. Ezáltal megspórolható a függvényhívás költsége, viszont növekszik a tárgykód. Általában kis méretű, nem bonyolult függvények esetén használható hatékonyan.
inline void sayHello() {std::cout << "Hello" << std::endl;}
Az inline definíció csak javaslat a fordítónak, amelyet nem muszáj figyelembe vennie. A legtöbb fordító a ciklust vagy rekurzív függvényhívást tartalmazó függvény esetén elutasítja az inline direktívát.
A függvények belsejében (illetve a programban lévő blokkokon belül) deklarált változókat lokális változóknak nevezzük. Ez a gyakorlatban azt jelenti, hogy a láthatóságuk és élettartamuk a függvényen (blokkon) belülre korlátozódik. A lokális változók a függvényhívás végén automatikusan megsemmisülnek és kívülről nem hivatkozhatóak.
//Két változó értékének cseréje void swap(int &a, int &b) { int tmp = a; //nem dinamikusan (statikusan) lefoglalt változó a = b; b = tmp; } tmp = 10; //Hiba, tmp nem hivatkozható a hatókörén (a függvény blokkján) kívül
A dinamikus objektumokra mutató pointerek szintén megsemmisülnek a hatókörükből kikerülve, de az objektum maga nem.
int * createArray(int n) { int * v = new int [n]; return v; //A függvény egy n elemű tömbre mutató pointerrel tér vissza } int * t = createArray(10); t[0] = 12; //Működik, most t mutat a tömbre v[1] = 2; //Hiba, v már nem létezik
Ha nem gondoskodunk a blokkon belül létrehozott dinamikus objektum külső elérhetőségéről, az érvényes hivatkozás nélkül a memóriában marad, azaz memóriaszivárgás keletkezik.
void func() { int * v = new int [10]; } v[0] = 12; /*Hiba, a tömbre mutató pointer már nem létezik, és más sem mutat rá -> memóriaszivárgás*/
A túlterhelés leveszi a programozó válláról a sokféle név megjegyzésének terhét, mivel azonos névvel létezhet több függvény is.
Akkor beszélünk túlterhelésről, ha azonos látókörben (scope) több azonos nevű függvény van deklarálva, különböző szignatúrával. Egyébként, ha egy külső látókörben van a másik név, elfedésről van szó.
Túlterhelt függvény hívásakor a fordító kiválasztja a látható függvények közül a legjobban illeszkedőt, szignatúra alapján. A feloldási szabályok meglehetősen bonyolultak, így óvatosan kell túlterhelt nevet bevezetni.
struct Point { Point(int X, int Y): x(X), y(Y) {} int x,y; }; double VectorLength(int, int); double VectorLength(Point); int main() { cout << VectorLength(3, 4) << endl; cout << VectorLength(Point(3, 4)) << endl; //feltehetőleg mindkét függvény 5.0-t fog visszaadni }
Kivételnek (exception) azt a hibás állapotot vagy váratlan eseményt nevezzük, amely megszakítja a program rendes futását. A kivételkezelés lehetővé teszi, hogy a vezérlés ahhoz a programrészhez kerüljön, amely képes az adott kivétel kezelésére. A throw utasítás segítségével kivételt „dobhatunk”, amelyet a program „elkaphat” (catch). A C++ megszakításos modell alapján kezeli a kivételeket, azaz a kivételt kiváltó programrész futása megszakad. Három elem szükséges a kivételkezelés megvalósításához:
A kivételt kiváltható programrész kijelölése (try)
A kivétel dobása (throw)
A kivétel elkapása (catch)
A C++-ban kevés beépített kivétel található, de bármely típus dobható, így magunknak is definiálhatunk, ha szükséges. A kezelőig (a megfelelő típust elkapó catch a hívási sorban valahol feljebb) tartó minden automatikus változó megsemmisül destruktorhívással.
Ha kivételkezelés közben újabb kivétel keletkezik (például egy felszámolt változó destruktora dob – ez nagyon veszélyes) a futás eredménye definiálatlan, de általában katasztrofális. Érdekesség, hogy a C++ a statikus területen mindig fenntart akkora helyet, hogy a memóriafoglaló bad_alloc kivételét ki tudja váltani.
void f(){ throw 1; } //ez a függvény mindenképpen dob kivételt try{ f(); //kivétel dobódik }catch(int){ //elkapjuk std::cerr << "Exception!\n"; }
Függvények esetében megadhatjuk, hogy milyen kivételeket továbbíthat:
void f() throw(); //nem dobhat kivételt void g() throw(int); //int típusú kivételek try{ g(); }catch(int){ //int típusú kivételek } ... //más kivételek elkapása ... }catch(...){ //minden kivétel elkapása, ez mindig az utolsó helyen áll }
A dinamikus memóriahasználat alapgondolata, hogy adataink számára akkor foglalunk helyet, amikor szükség van rá, ha pedig feleslegessé válnak, azonnal felszabadítjuk a memóriaterületet. A C++-ban különösen fontos szerepe van, mivel nincs „szemétgyűjtő”, mint a Javában vagy C#-ban. A felszabadítatlan memória „memóriaszivárgáshoz” (memory leak) vezethet.
A new és delete operátorok[szerkesztés]
A C++-ban lehetőség van használni a C nyelv malloc (és egyéb változatai) és free utasítását, de ajánlott az újakat alkalmazni. A new operátor az operandusban megadott típusnak megfelelő méretű területet foglal a memóriában, és arra mutató pointert ad vissza. A delete felszabadítja a new által lefoglalt területet:
int *v = new int; //helyfoglalás egy int-nek delete v; //felszabadítjuk
Nemcsak egy elemnyi terület elfoglalására van lehetőség, hanem több egymás után elhelyezkedő elem számára is foglalhatunk területet. Ezt az adatstruktúrát dinamikus tömbnek nevezzük:
int *v = new int [10]; //helyfoglalás 10 db int-nek delete[] v; //felszabadítjuk
Többdimenziós tömböt is foglalhatunk dinamikusan, ekkor a tömb elemei egy-egy pointerre mutatnak:
int **v = new int * [10]; //10 db int-re mutató pointer for(int i = 0;i < 10;++i) { v[i] = new int [10]; // 10x10-es mátrix, v minden eleme 10 db int-re mutat for(int j = 0;j < 10;++j) { v[i][j] = i + j; } } //felszabadítjuk a mátrixot, minden egyes mutatót külön for(int i = 0;i < 10;++i){ delete[] v[i]; } delete[] v; //végül magát v -t is
Fontos, hogy minden new-hoz a megfelelő delete-et használjuk:
int *t = new int; delete[] t; //definiálatlan, a legjobb esetben lefoglalva marad a memória int *v = new int[10]; delete v; //szintén definiálatlan.
Az malloc-kal szemben a new nem csak területet foglal, de automatikusan meghívja a megfelelő konstruktorokat is.
Főleg nagy méretű adatok esetén előfordulhat, hogy nincs elég memória. Ekkor a C++ std::bad_alloc kivételt dob.
try{ int *t = new int[10000]; //40000 byte memóriát foglalunk }catch(std::bad_alloc){ //Hibakezelés }
Az adattagokat a változókhoz hasonlóan deklaráljuk, de az adattagok nem tartalmazhatnak inicializációs listát. Ha egy osztályon belül egy másik osztályt akarunk adattagként használni, akkor előzőleg szerepelnie kell a másik osztály teljes deklarációjának.
Az adattagokon műveleteket végző tagfüggvényeket az osztály törzsében deklaráljuk. Ezen a függvény szignatúráját, vagy a helyben kifejtett implicit inline definícióját értjük (vagyis a függvény prototípusa előtt nem szerepel az inline kulcsszó). Az inline módosító megadásával az osztálydefiníción kívül definiált függvényeket is inline-ná tehetjük.
class MyClass { public: MyClass(int x) { value = x; } void printValue() { std::cout << value << std::endl; } //inline definíció private: int value; };
Amennyiben csak a deklarációt tartalmazza az osztály, a függvény törzsét azon kívül kell definiálni. A definíció sorrendben a visszatérési értékből, az osztály nevéből, a hatókör operátorból, a függvény szignatúrájából és törzséből áll:
class MyClass { public: MyClass(int x) { value = x; } void doSomething(); private: int value; }; void MyClass::doSomething(){ /*do something*/ } //külső definíció
Egy osztály bármely tagfüggvénye hozzáfér az adattagokhoz, függetlenül annak elérésétől.
A static kulcsszóval bevezetett adattagokból és tagfüggvényekből osztályszinten egy darab létezik.
Az objektumok kezdeti értékadásaiért (inicializálás) speciális tagfüggvények, a konstruktorok felelnek. A konstruktor olyan tagfüggvény, amelynek neve megegyezik az osztályéval, és nem rendelkezik visszatérési típussal.
class MyClass { public: MyClass(const int & data) { x = data; } //Konstruktor private: int x; }; MyClass* mc = new MyClass(10); //mc->x egyenlő 10-zel
A fordító minden olyan esetben, mikor egy objektum létrejön, meghívja a konstruktorát. Egy osztálynak bármennyi konstruktora lehet a szignatúrától függően. Alapértelmezés szerint minden osztály két konstruktorral rendelkezik, a paraméter nélküli (default) és a másoló (copy) konstruktorral. Ha saját konstruktort készítünk, attól fogva az alapértelmezett nem lesz elérhető. A konstruktorok egyaránt lehetnek public, private vagy protected elérésűek. A csak private konstruktorokat tartalmazó osztályt rejtett osztálynak nevezzük.
Ha az egyparaméteres konstruktorokkal rendelkező osztályok példányainak nem teljesen illeszkedő típust adunk kezdőértékül, akkor a fordító implicit típuskonverziót hajt végre. Ezt megtilthatjuk az explicit kulcsszó használatával:
class MyClass { public: explicit MyClass(const int & data){ x = data; } //csak int -et fogad el private: int x; }; void f() { MyClass x; x = 3; //hiba! explicit kulcsszó miatt nincs konverzió x = MyClass(3); // jó }
Ha az objektum egy másik osztály példányát is tartalmazza, akkor a belső osztály konstruktorát a külső osztály konstruktorában hívjuk. Ezt a taginicializációs lista használatával oldhatjuk meg, amelyet a konstruktor szignatúrája után kettősponttal elválasztva adhatunk meg:
class MyClass { public: MyClass(const int & data) : x(data) { }; private: int x; };
Taginicializációs listát csak a konstruktorban adhatunk meg. Használata kötelező, ha az osztály referencia típusú vagy paraméterezett típussal rendelkező adattagot tartalmaz. A konstans tagok beállítására is ezt használjuk:
class MyClass { public: MyClass(const double & d_data, const int & i_data) : y(d_data), x(i_data) {}; private: const double y; int x; };
Az objektumok által felhasznált memória mindaddig lefoglalt marad, míg fel nem szabadítjuk. Erre a célra a C++ biztosít számunkra egy speciális tagfüggvényt, a destruktort. Hasonlóan a konstruktorhoz, a destruktornak sincs visszatérési értéke. A destruktornak nem lehetnek paraméterei. A destruktor nevét az osztály nevéből és a hullám karakterből (tilde: ~) képezzük:
class MyClass { public: MyClass(int val){ t = new int(val); } //konstruktor ~MyClass(){ delete t; } //destruktor private: int * t; };
Ha nem definiálunk destruktort az osztályunkban, a fordító automatikusan létrehozza. A destruktor minden olyan esetben meghívódik, amikor az objektum érvényessége megszűnik. Kivételt képeznek a dinamikusan (a new operátorral) létrehozott példányok, amelyek destruktorát csak a megfelelő delete operátor hívhatja meg. A destruktor közvetlenül is hívható.
Dinamikus tömbök esetén a konstruktorok az indexek növekvő sorrendjében hívódnak meg, a destruktorok éppen fordítva, de csak a delete[] operátor alkalmazásával. A statikus tömbök ugyanígy törlődnek, de automatikusan (tehát nem kell delete), amint kikerülnek a hatókörükből. A nem megfelelő delete használatával a legjobb esetben is csak a tömb első eleme semmisül meg.
Egy osztály egy memóriában létrehozott példányát objektumnak nevezzük. Minden objektum rendelkezik a neki megfelelő osztály minden egyes adattagjával (természetesen az egyes példányok külön másolatokat birtokolnak, kivéve a statikus tagokat) és tagfüggvényével. Egy objektumot létrehozhatunk dinamikusan és statikusan is.
class MyClass{ ... }; MyClass my_static_object; //statikus definíció MyClass * my_dynamic_object = new MyClass(); //dinamikus definíció
A két esetben a tagok elérése különbözik. Statikus definíció esetén a pont (.) operátort, míg dinamikus esetben a nyíl (->) operátort használjuk.
Az öröklődés az objektumorientált stílus meghatározó eleme, amely a kód-újrafelhasználást és a dinamikus típuskezelést teszi támogatottá. C++-ban az eszköztára az absztrakt metódusok, a (többszörös) öröklődés és a virtual kulcsszó.
Absztraktnak nevezünk egy osztályt, ha van legalább egy absztrakt tagfüggvénye (ezt a függvénydeklaráció után tett = 0-val jelezzük), ekkor az osztály nem példányosítható.
Az öröklés úgy hoz létre új objektumot, hogy az megtartja az őse minden adattagját és (az ősosztályban nem private) tagfüggvényét.
Segítségével nem kell ismernünk egy objektum pontos típusát, mutatókon és referenciákon keresztül dinamikus kötésű műveleteket hívhatunk meg (polimorfizmus – többalakúság).
Hatékonysági okok miatt C++-ban minden függvény statikus, ha nincs explicit megmondva a virtual kulcsszóval ott vagy a bázisosztályban.
Az öröklődés lehet public, ez a bázisosztály osztály specializációja, protected illetve private, ekkor a származás nem lesz kívülről látható, azaz le kell mondanunk a dinamikus típusazonosításról, de egyébiránt teljes értékű öröklődés.
Ha az ős és a származtatott osztályban szerepel ugyanolyan néven függvény, akkor nincs túlterhelés, a származtatott elfedi azokat, a névtér-szabályok miatt. A nem virtuális függvények ellenben fordítási időben kötődnek, így ősosztályra mutató pointeren keresztül minden nem virtuális függvényhívás az ősosztálybelit fogja végrehajtani, függetlenül a mutatott objektum dinamikus típusától.
struct A { virtual void print1() { cout  print1(); //"B" p->print2(); //"A" delete p; }
A generikus programozás az alapvetően típusfüggetlen algoritmusok (pl. rendezések) és általános célú tároló szerkezetek (pl. listák) létrehozásán alapuló programozási stílus, a generikus programozási nyelv pedig az, ami ezt nyelvi eszközökkel támogatja.
A C++ közvetlenül támogatja ezt a programozási stílust, a template kulcsszóval, mely osztályok és függvények elé egyaránt beszúrható. A formális paraméterek a template után  -ben sorolandók fel, típusuk lehet konkrét típus (pl. int) vagy típusparaméter, ezt a typename kulcsszóval jelöljük.
//sablon-osztály template   class MyBuff { public: MyBuff() {} T GetItem(int); private: T buf[size]; }; template  T MyBuff ::GetItem(int num) { return buf[num]; }
A tagfüggvények és az osztályon belül deklarált osztályok maguk is sablonok, kifejthetők az osztályon kívül is, ekkor jelezni kell a teljes sablonszignatúrát (pl. GetItem). Az osztálysablon nem implicit inline tagfüggvényeit minden olyan forrásállományba be kell építenünk, amelyből azokat hívjuk (bevett szokás az osztályt és tagfüggvényeit egyetlen fejállományban elhelyezni).
A sablondeklarációban typename helyett írható class is, a kettő között nincs különbség. Amikor a fordító számára nem egyértelmű, hogy típussal van dolga, akkor a typename/struct/class szóval jelezhetjük ezt:
template   void func() { typename T::iterator ti; }
A fenti példában a T típus még nem jött létre, ezért tudatnunk kell a fordítóval a létezését.
Az osztály példányosításakor ki kell írni a paramétereket az osztály neve után.
void f() { //... MyBuff  IntBuff; //... }
Sablonfüggvény hívásakor a fordító a paraméterek típusából megállapítja T aktuális értékét, nem kell explicit kiírni.
//sablon-függvény template  void sort(vector & v) { //valamely rendező algoritmus } //... void func(vector & vekt) { sort(vekt); }
A sablonok fordítási időben példányosulnak, így a fordítónak ismernie kell a típusparaméterek típusát, a konkrét típusú paramétereknek konstansnak kell lenniük. A hibát legkésőbb szerkesztéskor jeleznie kell a fordítónak. Mivel csak a használt sablonok példányosulnak, ezért kódtakarékos megoldás lehet a generikus programozás, de akár összetettebb megoldások is elképzelhetők (jellemző példa a   és MyBuff  közötti azonosságot.
Két sablon pontosan akkor azonos típusú, ha a sablon-paramétereik azonosak, egyéb esetekben teljesen különálló típusok. Ez magával vonja, hogy a sablonok teljesen függetlenek az osztályhierarchiától. A sablon nem terhelhető túl a paramétereire, de specializációt lehet adni, konkrét típusokra/értékekre.
A sablonokkal fordítási idejű programokat lehet írni, és ez a nyelv Turing-teljes, azaz minden számítógéppel megoldható problémára alkalmazható.[4] Példaként tekintsük a faktoriális számítást!
template   struct Faktor { enum {value = N * Faktor ::value}; }; template   struct Faktor  { enum {value = 1}; }; // Faktor ::value == 24 // Faktor ::value == 1 int main() { int x = Faktor ::value; // == 24 int y = Faktor ::value; // == 1 }
↑ C++ története. [2008. június 17-i dátummal az eredetiből archiválva]. (Hozzáférés: 2009. május 19.)
↑ C++ szabványos fejállományok elnevezése. [2009. június 3-i dátummal az eredetiből archiválva]. (Hozzáférés: 2009. május 31.)
↑ C++ név eredete. [2008. június 17-i dátummal az eredetiből archiválva]. (Hozzáférés: 2009. május 19.)
↑ Template metaprogramozás Turing-teljes. [2006. szeptember 18-i dátummal az eredetiből archiválva]. (Hozzáférés: 2010. január 22.)
Bjarne Stroustrup: A C++ programozási nyelv
Scott Meyers: Hatékony C++
Stephen C. Dewhurst: C++ hibaelhárító
Tóth Bertalan, Lapteva Natalia: Programozzunk C++ nyelven
Open Directory on C++
Apache C++ Standard Library Documentation
Dinkumware's C++ Library Reference Manual
Standards Committee Page: JTC1/SC22/WG21 – C++
CodePedia C++ main page
The C++ Annotations – for those transitioning from C, Perl, etc.
C++ FAQ Lite by Marshall Cline
comp.lang.c++, comp.lang.c++.moderated, comp.std.c++ Newsgroups
Searchable Open Source C/C++ Code Archive
Portable foundation classes from GNU
links to C++ Tools
A categorized list of C++ related links
Informatikai portál • összefoglaló, színes tartalomajánló lap
A lap eredeti címe: „https://hu.wikipedia.org/w/index.php?title=C%2B%2B&oldid=24850931”
Hivatkozás erre a változatra
Hogyan hivatkozz erre a lapra?
A lap utolsó módosítása: 2022. április 14., 22:57
A lap szövege Creative Commons Nevezd meg! – Így add tovább! 3.0 licenc alatt van; egyes esetekben más módon is felhasználható. Részletekért lásd a felhasználási feltételeket.