Kihagyás

C++ alapok

Első program

Ahhoz, hogy tudjunk fordítani, szükségünk lesz egy fordítandó forráskódra is. Mivel még nem tudunk C++-ban programozni, nyugodtan írhatunk egy C nyelvű programot is, mentsük el elso.cpp néven (a .cpp kiterjesztés fontos).

1
2
3
4
5
6
#include <stdio.h>

int main() {
  printf("Hello World!\n");
  return 0;
}

Fordítás

Ha már eddig kész vagyunk, a Programozás alapjairól már ismert módon fordíthatjuk a programunkat, annyi különbséggel, hogy az eddigi gcc parancs helyett g++ parancsot kell használnunk.

g++ -o elso -Wall elso.cpp

Ismétlés a kapcsolókról:

  • -o kapcsolóval adhatjuk meg a kimeneti fájl nevét
  • -Wall kapcsolóval további fordítóprogram figyelmeztetést (warningot) kapcsolhatunk be
  • Ilyen például az, hogy egy boolean értéket egy nem boolean-nek megfelelő egésszel hasonlítunk össze
  • vagy mondjuk a kiírt, de üresen hagyott if/else blokkokról is kaphatunk warningot.
  • elso.cpp a fordítandó forrás fájl neve

A lefordított, linkelt alkalmazást ezután már futtathatjuk elso.exe vagy ./elso utasítással. A fordító -o paraméter nélkül a.exe vagy a.out nevű futtatható állományt készít.

Fordítás fázisai

Ugyanaz, mint C esetében (lásd Programozás Alapjai jegyzet).

Hello Világ C++-ban

A megszokott, jól ismert C programunkat átalakíthatjuk úgy, hogy a C++-ban használatos kiíratás szerepeljen benne.

1
2
3
4
5
6
#include <iostream>

int main() {
  std::cout << "Hello World!" << std::endl;
  return 0;
}

Hogy ne kelljen mindenhova kiírni az std:: előtagot, a program elejére beírhatjuk a using namespace std; sort. (Ennek a jelentéséről majd később beszélünk.)

1
2
3
4
5
6
7
8
#include <iostream>

using namespace std;

int main() {
  cout << "Hello World!" << endl;
  return 0;
}

A felkommentelt fájl letöltése

C++ input/output

A hagyományos C-ben megtanult beolvasás és kiíratás is működik C++-ban.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main(int argc, char* argv[]) {
  int kor = 0;
  float magassag = 0.0;
  char nev[10]; // fix meret

  printf("Adja meg a korat es magassagat szokozzel elvalasztva: ");
  scanf("%d %f", &kor, &magassag);
  printf("\n");

  printf("Adja meg a nevet: ");
  scanf("%s", nev);

  printf("Nev: %s, kor: %d, magassaga: %f", nev, kor, magassag);

  return 0;
}

A fájl letöltése

C++-ban az input/output műveletek streamek (folyamok) segítségével vannak megvalósítva

  • cout: az alapértelmezett kimenet
  • cerr: az alapértelmezett hibakimenet
  • cin: az alapértelmezett bemenet

A streamekbe (folyamokba) a << operátor segítségével írhatunk, és a >> operátor segítségével olvashatunk belőlük.

Ezek használatához a C-ben megszokott stdio.h header fájl helyett a C++-ban használatos iostream header fájlt kell include-olni. Ezzel az új, stream-eken alapuló műveleteket adjuk hozzá programunkhoz.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>

using namespace std;

int main(int argc, char* argv[]) {
  int kor = 0;
  float magassag = 0.0;
  char nev[10];

  cout << "Adja meg a korat (evek szama) es magassagat szokozzel elvalasztva: ";
  cin >> kor >> magassag;

  cout << "Adja meg a nevet: ";
  cin >> nev;

  cout << "Nev: " << nev << " kor: " << kor << " ev, magassag: " << magassag << " cm" << endl;
  return 0;
}

Sorvéget az endl segítségével írhatunk.

A fájl letöltése

C++ string

Mi történik az alábbi programmal akkor, ha a fix méretű karakter tömbbe hosszabb méretű karakterláncot adunk meg?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <iostream>
#include <string>

using namespace std;

int main() {
  char nev[10];
  cout << "Adja meg a nevet: ";
  cin >> nev;
  cout << "A nev: " << nev << endl;
}

Az eredmény (ha a program lefut):

1
2
Adja meg a nevet: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Nev: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Azonban ha túl hosszú a bemenet, akkor a program hibával megállhat. A megoldás az, hogy az inputnak megfelelő méretű területet foglalunk, és oda írjuk be a felhasználó által megadott nevet. A C++-ban a "szövegeknek" van egy hatékonyabb, kevésbé körülményes megvalósítása is, a string osztály (osztályokról később lesz szó, most csak azt mutatjuk be, hogyan lehet használni a string-et). A string objektumok dinamikusan változtatják a méretüket a tartalmazott karakterek számától függően. A félév folyamán stringek alatt ezt a reprezentációt értjük, nem pedig a char*-ot. A string objektumok használatához, string műveletekhez szükségünk lesz a string header include-olására is (és nem string.h). Ezt követően kényelmesen használhatjuk a szövegeket, ahogy már Javaban is megszoktuk. Itt csak a fontosabb használati eseteket emeljük ki, bővebb leírásokhoz linkek:

A string osztály fontosabb metódusai, amelyeket a leggyakrabban használunk:

Létrehozás, azaz a konstruktorok

1
2
3
4
string s1;           // ures string
string s2("alma");   // string, amely az "alma" szoveget tartalmazza
string s3 = "korte"; // string, amely az "korte" szoveget tartalmazza
string s4(s2);       // string, amely az "alma" szoveget tartalmazza, mert az s2 "alma"

Beolvasás, kiírás

1
2
3
string str;
cin >> str;
cout << "A beolvasott szoveg: " << str << endl;

A scanf nem használható a string beolvasására, de a cin segítségével be tudjuk olvasni, és a cout segítségével ki tudjuk írni.

Összehasonlítás

1
2
3
4
5
6
string s1, s2;
cin >> s1 >> s2;
if (s1 == s2)
    cout << "A ket string azonos" << endl;
else
    cout << "A ket string nem azonos" << endl;

A string-eket az == operátor segítéségével össze lehet hasonlítani.

Összefűzés

1
2
3
4
string s1("Hello"), s2("World"), s3;
s3 = s1 + " " + s2 + "!";
cout << s3 << endl;
// kimenet: Hello World!

Méret, üres-e, tartalom törlése

1
2
3
4
5
6
7
8
string s("alma");
// size() es length() ugyanaz, length olvasmanyosabb
cout << "meret: " << s.size() << " " << s.length() << endl; // meret: 4 4
if (s.empty()) // van benne karakter -> false
    cout << "s ures" << endl;
s.clear();
if (s.empty())
    cout << "s ures" << endl; // most kiirja, hogy "s ures"

i-edik elem

1
2
3
4
5
string s("alma");
cout << s[2] << endl; // m
s[0] = 'l';
s[1] = 'a';
cout << s << endl;    // lama

Ha olyan indexre hivatkozunk, amelyik érvénytelen, azaz a string hosszánál nagyobb, akkor nem definiált viselkedés lesz az eredménye.

Konverzió szám és string között

Számból string-gé a to_string segítségével konvertálhatunk.

1
2
3
4
int i = 5;
string s1 = to_string(i);
string s2 = to_string(3.14);
cout << s1 << " " << s2 << endl; // 5 3.140000

string-et számmá konvertálni az stoi függvénnyel lehet.

1
2
3
string s1("12345");
int i = stoi(s1);
cout << i << endl; // 12345

Konverziók a különböző típusokra:

  • int: stoi
  • long: stol
  • long long: stoll
  • unsigned long: stoul
  • unsigned long long: stoull
  • float: stof
  • double: stod
  • long double: stold

Mi történik,

  • ha a szöveg elején van szám, de van mögötte ,,egyéb'', akkor a számot átkonvertálja
1
2
string s("123 alma");
cout << stoi(s) << endl; // 123
  • ha nem fér bele a tartományba, akkor std::out_of_range kivétel dobódik (kivételkezelés, const és referencia később)
1
2
3
4
5
6
7
try {
  string s1("999999999999999999999999999999");
  cout << stoi(s1) << endl;
}
  catch (const out_of_range& e_out) {
  cout << "out_of_range exception" << endl;
}
  • ha nem lehet konvertálni, akkor std::invalid_argument kivétel dobódik
1
2
3
4
5
6
7
try {
  string s1("alma");
  cout << stoi(s1) << endl;
}
  catch (const invalid_argument& e_inv) {
  cout << "invalid_argument exception" << endl;
}

Hibakeresés

Debugger használata

Az IDE eszközök általában tartalmaznak debuggert is, ami lehetővé teszi, hogy a program végrehajtását utasításról utasításra lépve kövessük végig úgy, hogy közben a változók állapotát, értékét folyamatosan monitorozni tudjuk. Fontos, hogy ahhoz, hogy a debugger megfelelően tudjon működni, a kódot úgy kell fordítani, hogy abba belekerüljenek a debugger számára szükséges információk, illetve a kódot ne optimalizálja a fordító, amellyel esetlegesen kihagy belőle utasításokat, amely miatt egy kicsit nehezebben követhető lehet, hogy a program miért úgy fut, ahogy.

Assertek

C++-ban lehetőség van a assert preprocesszor makró által arra, hogy bizonyos feltételeket ellenőrizzünk a program futtatása során, amelyek ha nem teljesülnek, a program végrehajtása megszakad. Ennek használatához importálnunk kell a cassert headert.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>
#include <cassert>
using namespace std;

int main() {

  int even_num = 3;

  // asserts ami ellenőrzi, hogy az even_num páros-e (ahogy azt a neve alapján elvárjuk tőle)
  assert((even_num % 2 == 0));

  return 0;
}

Kiiratás

Sok esetben persze a legegyszerűbb és leggyorsabb megoldás, ha a programunkban kiirató utasításokat használunk egy-egy változó értékének lekérésére és ellenőrzésére. Annak érdekében, hogy ne vesszünk el esetlegesen a sok kiíratásban, illetve adott esetben ezeket a saját debugolásra használt üzeneteket könnyen ki tudjuk kapcsolni, érdemes a feltételes fordítás lehetőségét alkalmaznunk, illetve a megfelelő makrókkal jelezhetjük azt is, hogy az adott kiiratás a kódunk melyik részén történt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>
using namespace std;

int main() {

  int even_num = 3;

  #define DEBUG
  #ifdef DEBUG
  cout << "LOG: [" << __FILE__ << ": " << __LINE__ << "]: az even_num valtozo erteke: " << even_num << endl;
  #endif

  return 0;
}

A #define DEBUG sor hozzáadásával, vagy a -DDEBUG fordítási kapcsolóval tudjuk elérni, hogy a programba bekerüljün a megfelelő kiirató utasítás futtatása. Ezek elhagyásával a program a kiiratás nélkül fut le.


Utolsó frissítés: 2024-06-21
Létrehozva: 2024-06-20