Programozás alapjai II. (1. ea) C++

C++ kialakulása, nem OO újdonságok

Szeberényi Imre, Somogyi Péter BME IIT szebi@iit.bme.hu

C++ kialakulása

C és C++ viszonya

C és C++ változatai

03

C++ szabványok

C99

Tanácsok (ism.)

A lényeg a következetességen van!

Deklaráció és definíció

Ott deklaráljunk, ahol használjuk

int y = 12; //...
int z = 3;  // és egyből inicializáljuk
for (int i = 0; i < 10; i++) {
  z += i;
  int k = i - 1;
  y *= k; //élettartam, hatókör u.a, mint a C-ben
}
// i és k itt már nem léteznek !

C++ újdonságok, bővítések

Típusok

Logikai típus (új típus)

Aritmetikai és logikai konverzió

bool b1, b2, b3; int i;
b1 = true; b2 = 3; i = b2; b3 = false;
// b2 == true, i == 1

Struktúra név típussá válik

// C++-ban a név típus értékű
struct Komplex {
  float re;
  float im;
};
// önhivatkozó struktúránál kényelmes
struct Lista_elem {
  int i;
  Lista_elem *kov;
};

Konstans (ism.)

#define PI 3.14

helyett:

const float PI = 3.14;

const: Típusmódosító, amely megtiltja az objektum átírását (a fordító nem engedi, hogy balértékként szerepeljen).

Mutatók esetén:

const char *p; // p által címzett terület nem módosítható
char const *p; // ua.
char *const q;  // q-t nem lehet megváltoztatni
//          ^~
//            = 0
// error: default initialization of an object
// of const type 'char *const'

Két trükkös próbálkozás

Felsorolás típus (szigorúbb)

enum Szinek { // típus
    piros, sarga, zold = 4
};

Szigorúbb ellenőrzés, mint az ANSI C-ben. Pl.:

Szinek jelzo;
jelzo = 4; // fordítási hiba 
//      ^
// error: assigning to 'Szinek' from incompatible type 'int'
jelzo = Szinek(8); // nincs hiba, de meghatározatlan
//      ^             érték létrehozása

Prototípus kötelező

Előrehivatkozáskor kötelező.

// Tipikus C hiba:
double z = sqrt(2); // C feltételezi, hogy int sqrt(),
                    // de (double) kellene:
$ man 3 sqrt
NAME
       sqrt, sqrtf, sqrtl - square root function

SYNOPSIS
       #include <math.h>

       double sqrt(double x);
       [...]
       Link with -lm.

Miért baj ha elmarad?

Mutatók és címek (ism.)

Indirekció (ism.)

Értékparaméter (ism.)

Változó paraméter (ism.)

Hivatkozás (új típus)

A hivatkozás (referencia) egy alternatív név (álnév, alias): tipus&

int i = 1;
int &r = i; // kötelező inicializálni, mert
// tudni kell, hogy kinek az alternatív neve
int x = r; // x = 1;
r = 2;     // i = 2;

Változó paraméter referenciával

C:

void inc(int *a) { (*a)++; }
int x = 2;
inc(&x); // az & könnyen lemarad

C++:

void inc(int &a) { a++; }
int x = 2;
inc(x); // nem kell jelölni a címképzést híváskor

Példa

// két érték felcserélése
// Változó paraméter pointerrel
void csere(int *a, int *b) {
  int tmp = *a;
  *a = *b;
  *b = tmp;
}
int main() {
  int x, y;
  csere(&x, &y);
}

Példa

// két érték felcserélése
// Változó paraméter referenciával
void csere(int &a, int &b) {
  int tmp = a;
  a = b;
  b = tmp;
}
int main() {
  int x, y;
  csere(x, y);
}

Paraméterátadás összefogl.

Függvény argumentumok

Csak az argumentumlista végén lehetnek default argumentumok, akár több is.

Inline függvények

Inline fv. példa

inline int max(int a, int b) { return (a > b ? a : b); }
int main() {
  int x = 3, y = 4, z;
  z = max(x++, y++);
  // a hívás helyére beépül a kód, miközben
  // fv. hívás szabályai érvényesülnek
  z = a > b ? a : b; // a = 3, b = 4
  //            ^ optimalizáló ezen még optimalizálhat
  //  Eredmény:
  //  x -> 4, y -> 5, z -> 4
}

Függvény túlterhelés (overload)

int max(int a, int b) { return (a > b ? a : b); }
double max(double a, double b) { return (a > b ? a : b); }
int main() {
  // Azt a változaot használja,
  // ami illeszkedik a hívásra
  int x = max(1, 2);
  double f = max(1.2, 0.2);
}

Túlterhelt (overloaded) függvények: azonos a nevük, de eltér a paraméterlista (szignatúra), ami alapján illesztődik. Típusbiztonságot támogatja.

Érdekesség: A C++ szabvány lábjegyzetben jegyzi meg, hogy a függvény visszatérési típusa nem része a függvény szignatúrájának, azaz ez alapján nem lehet túlterhelni a függvényt! Más nyelvek másként viselkedhetnek!

standard I/O, iostream

Miért iostream?

C-ben ezt írtuk:

printf("i=%d j=%d\n",  i,  j);

C++-ban ezt kell:

cout << "i = " << i << " j=" << j << endl;

Kinek jó ez ?

A << és a >> új operátor?

std::ostream &operator<<(std::ostream &os, int i);
std::ostream &operator<<(std::ostream &os, double d);
std::ostream &operator<<(std::ostream &os, const char *p);

… alakú függvények. (Később pontosítjuk)

Függvény, mint balérték?

#include <iostream>
int main() { std::cout << "Hello C++" << std::endl; }
//                      ^
//       ostream& operator<< (ostream& os, const char* s);
//       alakra illeszkedik. Bal oldalon van a függvény?
}

Referencia értékű függvény lehet bal oldalon is.

Egyszerű példa

#include <iostream>
int x; // ronda globális!
int &f1() { return x; }
double &f2(double &d) { return d; }
//               ^ Referenciát, azaz alternatív
//                 nevet szolgáltatnak
int main() {
  f1() = 5;
  f1()++;
  double y = 0.1;
  f2(y) *= 100;
  std::cout << "x=" << x << " y=" << y << std::endl;
} // kiírás:  x=6 y=10

Példa: nagyobb

// Fájl: nagyobb_main.cpp
#include <iostream>
// fv prototípusok, konstansok, típusok, egyéb deklarációk:
#include "fuggvenyeim"
using namespace std;

int main() {
  cout << "Kerek ket egesz szamot:" << endl;
  int i, j;
  cin >> i >> j; // i és j értékének beolvasása
  int k = max(i, j);
  cout << "A nagyobb: " << k << endl; // nagyobb kiírása
}
// Fájl: fuggvenyeim.cpp
// Ebben valósítom meg a gyakran használt függvényeket.
// Saját header-t is célszerű behúzni ellenőrzés miatt:
#include "fuggvenyeim"

// Két int adat felcserélése
void csere(int &a, int &b) {
  int tmp = a;
  a = b;
  b = tmp;
}
// ....
// Fájl: fuggvenyeim
// Ebben találhatók a függvények prototípusai, típusok...

#ifndef FUGGVENYEIM   // Egy fordítási egységben
#define FUGGVENYEIM 1 // csak egyszer

/* csere
 * Két int adat felcserélése // Automatikus dok. generálás
 * @param a - egyik adat
 * @param b - másik adat
 */
void csere(int &a, int &b); // Függvény prototípusa
/*
 * max
 * Két int adat közül a nagyobb
 * @param a - egyik adat
 * @param b - másik adat
 */
// Ez egy inline függvény, amit minden fordítási egységben
// definiálni kell.
inline int max(int a, int b) { return a > b ? a : b; }

#endif // FUGGVENYEIM

Példa fordítása

Fordítás parancssorból:

g++ nagyobb_main.cpp fuggvenyeim.cpp -o nagyobb_main

Fordítás IDE segítségével:

Fordítás parancssorból make segítségével:

  1. Elő kell állítani a függésegeket leíró Makefile-t pl:

Figyelem: a Makefile tabulátort kell tartalmazzon, szemben a C ill. C++-al!

  1. le kell futtatni a make programot

    make