Ugrás a tartalomhoz

evaluation strategy

A Wikiszótárból, a nyitott szótárból


Főnév

evaluation strategy (tsz. evaluation strategies)

  1. (informatika) kiértékelési stratégia

Kiértékelési stratégiák C++ nyelvben

Bevezetés

A kiértékelési stratégia egy programozási nyelv azon szabályainak összessége, amelyek meghatározzák, hogyan és mikor értékelődnek ki a kifejezések és paraméterek egy adott környezetben. C++ egy összetett és hatékony nyelv, amely különböző értékelési stratégiákat használ, beleértve az operandusok kiértékelési sorrendjét, a függvényparaméterek átadásának módját és az optimalizációs technikákat.

Ebben a cikkben részletesen bemutatjuk a C++ kiértékelési stratégiáit, beleértve az alapvető koncepciókat, a függvényparaméterek kiértékelését, az optimalizációkat és az ezekből eredő lehetséges mellékhatásokat.



1. Kiértékelési sorrend (Evaluation Order)

C++-ban az operandusok kiértékelésének sorrendje nem meghatározott sok esetben, ami azt jelenti, hogy a fordító optimalizálhatja a kiértékelést anélkül, hogy fix sorrendet követne.

Vegyünk egy példát:

#include <iostream>

int f1() {
    std::cout << "f1 ";
    return 1;
}

int f2() {
    std::cout << "f2 ";
    return 2;
}

int main() {
    int x = f1() + f2();
    return 0;
}

A kimenet lehet:

f1 f2

vagy

f2 f1

A C++ szabvány nem határozza meg, hogy az f1() vagy az f2() hívódik meg előbb.

Határozott sorrendű műveletek

Néhány operátor esetében a C++ meghatározott kiértékelési sorrendet alkalmaz: - Logikai ÉS (&&) és logikai VAGY (||): Rövidzár (short-circuit) értékelést használ. - Vessző operátor (,): Balról jobbra értékelődik ki. - Hozzárendelés (=) és láncolt hozzárendelés (a = b = c): Jobbról balra történik a kiértékelés.

Példa rövidzár értékelésre:

int a = 0;
if (a && f1()) { // f1() sosem hívódik meg
    std::cout << "Ez sosem íródik ki!";
}

2. Függvényparaméterek értékelési stratégiái

A függvényparaméterek kiértékelésére különböző stratégiák léteznek.

a) Call by Value (Érték szerinti átadás)

A legtöbb esetben az alapértelmezett módszer az érték szerinti átadás, amely során a függvény egy másolatot kap a változóról.

void foo(int x) {
    x = 10; // Nem módosítja a hívó változóját
}

int main() {
    int a = 5;
    foo(a);
    std::cout << a; // 5
}

b) Call by Reference (Referencia szerinti átadás)

A referencia szerinti átadás lehetővé teszi az eredeti változó módosítását a függvényen belül.

void foo(int& x) {
    x = 10; // Módosítja az eredeti változót
}

int main() {
    int a = 5;
    foo(a);
    std::cout << a; // 10
}

c) Call by Pointer (Mutató szerinti átadás)

Hasonló a referencia szerinti átadáshoz, de mutatóval működik.

void foo(int* x) {
    *x = 10;
}

int main() {
    int a = 5;
    foo(&a);
    std::cout << a; // 10
}

d) Move Semantics (Mozgatás)

C++11 bevezette az R-value referencia (&&) fogalmát, amely lehetővé teszi az erőforrások hatékony átvitelét.

#include <iostream>
#include <vector>

void foo(std::vector<int>&& v) { // Mozgatással vesz át egy ideiglenes objektumot
    v.push_back(42);
}

int main() {
    foo(std::vector<int>{1, 2, 3});
}

A mozgatás csökkenti a fölösleges másolásokat, és javítja a teljesítményt.



3. Optimalizációs stratégiák

A C++ fordító számos optimalizációt végez az értékelési stratégiák során.

a) Copy Elision (Másolás elhagyása)

A fordító optimalizációt végezhet a másolások elkerülésére.

class MyClass {
public:
    MyClass() { std::cout << "Konstruktor" << std::endl; }
};

MyClass foo() {
    return MyClass(); // A másolás kihagyható
}

A C++ szabvány garantálja, hogy a fenti esetben nem jön létre felesleges másolat.

b) Lazy Evaluation (Lusta kiértékelés)

Egyes kifejezések értékelése elhalasztható.

bool expensiveComputation() {
    std::cout << "Számítás..." << std::endl;
    return true;
}

int main() {
    bool x = false && expensiveComputation(); // Nem hívódik meg
}

c) Loop Unrolling (Ciklus kibontás)

A fordító egy ciklusban lévő kifejezéseket kibontja, hogy csökkentse az iterációk számát.



4. Mellékhatások és veszélyek

A kiértékelési stratégia miatt előfordulhatnak nem várt mellékhatások.

a) Undefinált viselkedés

A következő példa nem definiált viselkedést eredményez:

int i = 0;
std::cout << i++ << i++; // A kiértékelési sorrend nem meghatározott

b) Dangling Reference (Lógó referencia)

int& foo() {
    int x = 10;
    return x; // Hibás! Egy lokális változóra hivatkozik
}

c) Mutató és referencia élettartam problémák

A dinamikusan foglalt memória elfelejtése memóriaszivárgáshoz vezethet.

int* foo() {
    int* p = new int(10);
    return p;
}

int main() {
    int* p = foo();
    delete p; // Ha elfelejtjük ezt, memóriaszivárgás történik!
}

Összegzés

A C++ kiértékelési stratégiája rugalmasságot és teljesítményt biztosít, ugyanakkor megköveteli a programozótól, hogy tisztában legyen a fordító által végzett optimalizációkkal és a nem definiált viselkedésekkel. Az értékelési sorrend ismerete és a megfelelő paraméterátadási mód kiválasztása segít elkerülni a hibákat és optimalizálni a kódot.