Einfaches Beispiel – Quadrat- und Kubikfunktion:
1 Argument wird an Funktionen
quadrat(..), kubik(..) übergeben,
Ergebnis (
double Wert) wird zurückgegeben.
double quadrat( double x ) // Berechne Quadrat { double r = x*x; return r; } double kubik( double x ) // Berechne Kubik { double r = x * quadrat(x); // verwende quadrat funktion return r; } int main() { double a1 = 2.5; double a2 = quadrat(a1); // verwende Funktion double a3 = kubik(a1); // Argument kann auch komplexer Ausdruck sein ... double b = quadrat( kubik(a3) * sin(0.8) + 0.99 ); }
double GravForce( double mass1, double mass2, double distance) // Kopf der Funktion { const double GRAV_CONST = 6.673 e-11; // Gravitationskonstante m^3 / ( kg s^2 ) double f = GRAV_CONST * mass1 * mass2 / (distance * distance); return(f); } int main() { double mSonne = 1.9889e30, mErde = 5.974e24, dErdeSonne = 1.49597e11, rErde = 6.378e6; // Aufruf von Funktion GravForce double gErdeSonne = GravForce( mSonne, mErde, dErdeSonne ); // Kraft Sonne--Erde double gPerson = GravForce( mErde, 80., rErde ); // Gewichtskraft 80 kg auf Erde ... }
Programm–Design
void newline( ) // Funktion gibt nur neue Zeile aus { // keine Parameter-uebergabe cout << endl; // keine Rueckgabe } int main() { newline(); }
int a = 42;
tfunc(a);
...
void tfunc(int b)
{...}
Nur für Spezialisten
int a = 42;
tfunc(& a);
...
void tfunc(int *b)
{...}
int a = 42;
tfunc(a);
...
void tfunc(int & b)
{...}
Hinweis: In fast allen Fällen würde ich letzteres Verfahren empfehlen,
d.h. Deklaration der Variablen in Funktion als Referenz:
keine komplexe Pointer–Syntax,
gute Performance. Gab's aber noch nicht in C, d.h. alte Programme nur
call-by-value oder
call-by-address.
#include <typeinfo> #include <iostream> #include <cmath> using namespace std; void tfunc1( double x); void tfunc2( double *x); void tfunc3( double & x); int main() { double a = 3.14; cout << "main start " << a << endl; tfunc1( a ); cout << "main tfunc1 " << a << endl; tfunc2( &a ); cout << "main tfunc2 " << a << endl; tfunc3( a ); cout << "main tfunc3 " << a << endl; } void tfunc1( double x ) { // x ist Kopie von a x *=2; cout << "tfunc1 " << x << endl; } void tfunc2( double * x ) { // x ist pointer auf a *x *= 2; cout << "tfunc2 " << *x << endl; } void tfunc3( double & x ) { // x ist identisch a x *= 2; cout << "tfunc3 " << x << endl; }
Bei call-by-reference muss man eine weitere Komplikation beachten. Der Aufruf wie oben geht nur wenn echte Variablen (=l-value) als Argument übergeben werden, aber nicht bei temporären Variablen oder Zahlen (=r-value), dafür Übergabe als const reference nötig:
#include <iostream> using namespace std; void tfuncr1( double & x); void tfuncr2( const double & x); int main() { double a = 3.14; tfuncr1( a ); // call ok tfuncr1( 2.5 ); // error, cannot take number arg with ref tfuncr1( 6*a ); // error, cannot take temp value arg with ref tfuncr2( a ); // call ok tfuncr2( 2.5 ); // ok, const guarantees value is not changed tfuncr2( 6*a ); // ok, dto } void tfuncr1( double & x ) { cout << "tfuncr1 " << x << endl; } void tfuncr2( const double & x ) { cout << "tfuncr1 " << x << endl; }
C++ ist eine sog. strong–typing Sprache, d.h.
Um Funktionen “bekanntzumachen” gibt es drei Möglichkeiten:
// demo function overloading // Deklaration Funktions Prototypen void tfunc1( double x = 999); // deklariert tfunc1() und tfunc1( double ) void tfunc2( int x = -1); // deklariert tfunc2() und tfunc2( int ) void tfunc2( double x); // void tfunc2( float x = 0.1); // illegal, tfunc2() schon deklariert int main() { int a = 5; int *b; char *c = "ABC"; b = &a; tfunc1( 1. ); tfunc1( 1 ); // int wird umgewandelt tfunc1( a ); tfunc1( ); // default wird genommen tfunc1( b ); // illegal, mit pointern geht die Umwandlung nicht tfunc1( *c ); // Seltsam, aber char sind einfach Zahlen == tfunc(65) tfunc2( 1 ); // int version wird benutzt tfunc2( 1. ); // double version "" tfunc2( ); // default darf nur einmal deklariert sein, // in dem Fall also die int version. } // Definition/Implementierung der Funktionen void tfunc1( double x ) { cout << "tfunc1 " << x << endl; } void tfunc2( int x ) { cout << "tfunc2 int " << x << endl; } void tfunc2( double x ) { cout << "tfunc2 double " << x << endl; }
double power( double x, int n ) { if ( n > 1 ) { return( x * power(x, n-1) ); // rekursiver Aufruf } else { return( x ); } }
double y = power( x, 4 );
Manchmal möchte man nicht nur einzelnen Wert (oder vector) zurückgeben, sondern Kombination verschiedener Werte.
Mit
std::tuple lässt sich das in modernem C++ relativ leicht erreichen.
Praktisches Beispiel: Nullstellen der quadratischen Gleichung – je nach Parametern gibt es 0, 1, oder 2 reele Nullstellen
#include <tuple> #include <cmath> #include <iostream> using namespace std; std::tuple<int,double,double> qg_root(double A, double B, double C) { // Returns (0, 1, or 2) real roots of // the quadratic equation A*x*x + B*x + C = 0. std::tuple<int,double,double> rval(0,0,0); if ( A != 0 ) { double disc = B*B - 4*A*C; if (disc == 0) { get<0>(rval) = 1; get<1>(rval) = -B / (2*A); } else if ( disc > 0 ) { get<0>(rval) = 2; get<1>(rval) = (-B + sqrt(disc)) / (2*A); get<2>(rval) = (-B - sqrt(disc)) / (2*A); } } return rval; } int main() // function definition { auto r = qg_root( 1, 2, -1 ); cout << get<0>(r) << ", " << get<1>(r) << ", " <<get<2>(r) << endl; }
sind "anonyme" Funktionen, sehr praktisch als Argument bei Aufruf von anderen Funktionen, z.B. STL Algorithmen die Funktion-Objekt bzw. Functor als Argument erwarten:
// lambda function example vector <double> vec2 = { 1.2, 3.7, -2.6, 0.9, -7.1 }; // we want sort vector elements according to abs of value // -- lambda funktion as 3. Argument sort(vec2.begin(), vec2.end(), [](double x, double y) { return abs(x)<abs(y);} ); // equivalent with explicit function bool chkabs(double x, double y) { return abs(x)<abs(y); } sort(vec2.begin(), vec2.end(), chkabs );