Generische Vektoren

Kurzes Beispiel für Vektoren im kartesischen Raum.

Wir definieren die Klasse
template<size_t N = 3, typename T = double>
class Point
Ohne Spezifizierung haben wir einen Punkt Point<> p im Raum vom doppeltgenauen Gleitkomma-Typ. Wir benutzen using Point3D = Point<> und können daher einfach Point3D p schreiben.

Weiterhin benutzen wir using Point2DInt = Point<2, int> für diskrete Punkte in der Ebene.

Konstruktor

Wir haben den Konstruktor Point(initializer_list<T>) und können damit Vektoren so definieren: Point3D p {1.2, -5.3, 10};. Falls die Initialisierungsliste zu kurz oder zu lang ist, wird mit 0 aufgefüllt bzw. werden überflüssige Werte abgeschnitten.

Weiterhin haben wir Point() = default gesetzt. Damit definiert Point3D p; einen Vektor mit undefinierten Koordinaten und Point3D p {}; den Ursprung. (Das verhält sich analog zu dem Standardverhalten in C++: bei int i; ist i undefiniert und bei int i {}; ist i == 0.)

Die Koordinaten werden intern in der privaten Membervariablen T array[N] gespeichert. (Es ist nicht nötig, die Felder dynamisch mit new zu erzeugen, da N zur Übersetzungszeit bekannt ist. Damit brauchen wir keinen Destruktor, Kopier-/Verschiebe-Konstruktoren und -Zuweisungen zu definieren.)

Operationen

Das Tolle an C++ ist, dass man Operatoren überladen kann. Damit können wir mit den Vektoren wie mit integrierten Datentypen umgehen.

Ausgabe

Wir können die Vektoren ausgeben. cout << "p: " << p << endl; schreibt
p: [1.2, -5.3, 10]
auf die Konsole.

Punktspiegelung im Ursprung

Das ist Point3D p2 = -p;

Verschieben

Point3D p1, p2, p3 = irgendwas;
p3 += {1, 2, 3};
p3 = p1 + p2;
p3 -= p2;
p3 = Point3D{5, 6, 7} - p1;

Vergleichen

Point2DInt p1, p2 = irgendwas;//int lässt sich besser als double vergleichen
cout << boolalpha << (p1 == p2) << ' ' << (p1 != p2);

Skalieren

cout << 0.5*p;

Skalarprodukt

cout << "Laenge: " << sqrt(p*p);

Zugriff auf ein einzelnes Element

cout << p[1] << endl;
p[0] = 15;
Hier findet keine Bereichsüberprüfung statt analog zum []-Zugriff bei integrierten Arrays oder beim vector. C++ traut dem Entwickler zu, den richtigen Index zu setzen, und liefert dafür eine bessere Performance.

Download

Vektoren-sources.zip

Quellcode

Hier befinden sich die Qt Projektdatei und der C++ Quellcode.

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp

HEADERS += \
    point.h


CONFIG += c++11

#include <iostream>
#include <cmath>
#include "point.h"

using namespace std;

void test2D() {
    Point2DInt p2_1;
    Point2DInt p2_2 {1.3, -5};//Hier Compiler-Warnung
    cout << "p2_1"  << p2_1 << endl;
    cout << "p2_2"  << p2_2 << endl;
    cout << boolalpha << (p2_1 == p2_2) << endl;
    p2_1 -= p2_1;
    p2_1 += {1, -5};
    cout << (p2_1 == p2_2) << endl;
    cout << (p2_1 != p2_2) << endl;
    cout << (p2_1 == p2_1) << endl;
    cout << 2*p2_1 << endl;
    cout << "length: " << sqrt(p2_1*p2_1) << endl;
}

void test3D() {
    Point<> p3_1;
    Point3D p3_2 {};
    Point3D p3_3 {1.2, -5.3, 10};
    Point3D p3_4 {-5.};
    Point3D p3_5 = -p3_3;
    cout << "p3_1 " << p3_1 << endl;
    cout << "p3_2 " << p3_2 << endl;
    cout << "p3_3 " << p3_3 << endl;
    cout << "p3_4 " << p3_4 << endl;
    cout << "p3_5 " << p3_5 << endl;
    cout << "-p3_5 " << -p3_5 << endl;
    p3_1 = {};
    p3_1 += {1.5, 2, 3};
    cout << "p3_1 " << p3_1 << endl;
    p3_1 += p3_4;
    cout << "p3_1 " << p3_1 << endl;
    p3_1 -= {3, -0.5};
    cout << "p3_1 " << p3_1 << endl;
    p3_1 = p3_3 + p3_4;
    cout << "p3_1 " << p3_1 << endl;
    p3_1 = Point3D{5, 6, 7} + p3_1;
    cout << "p3_1 " << p3_1 << endl;
    p3_1 = p3_1 - p3_1;
    cout << "p3_1 " << p3_1 << endl;
    cout << p3_3[1] << endl;
    p3_3[2] = -10.3;
    cout << "p3_3 " << p3_3 << endl;
}

int main()
{
    test2D();
    test3D();
    return 0;
}


#ifndef POINT_H
#define POINT_H

#include <initializer_list>
#include <ostream>

using namespace std;

template<size_t N = 3, typename T = double>
class Point
{
    T array[N];
public:
    Point() = default;
    Point(initializer_list<T> values) {
        for (size_t i {0}; i != values.size() && i != N; ++i)
            array[i] = values.begin()[i];
        for (size_t i {values.size()}; i != N; ++i)
            array[i] = 0;
    }
    friend ostream& operator<<(ostream& os, const Point<N,T>& point) {
        os << "[";
        for (size_t i {0}; i != N; ++i) {
            os << point.array[i];
            if (i != N - 1)
                os << ", ";
        }
        os << "]";
        return os;
    }
    Point operator-() const {
        Point p;
        for (size_t i {0}; i != N; ++i)
            p.array[i] = -array[i];
        return p;
    }
    Point operator+=(const Point& other) {
        for (size_t i {0}; i != N; ++i)
            array[i] += other.array[i];
        return *this;
    }
    Point& operator-=(const Point& other) {
        *this+=(-other);
        return *this;
    }
    bool operator==(const Point& other) {
        if (this == &other)
            return true;
        for (size_t i {0}; i != N; ++i)
            if (array[i] != other.array[i])
                return false;
        return true;
    }
    bool operator!=(const Point& other) {
        return !(*this == other);
    }
    friend Point operator*(double s, const Point& point) {
        Point q;
        for (size_t i {0}; i != N; ++i)
            q.array[i] = s*point.array[i];
        return q;
    }
    friend T operator*(const Point& a, const Point& b) {
        T res {0};
        for (size_t i {0}; i != N; ++i)
            res += a.array[i]*b.array[i];
        return res;
    }
    T& operator[](size_t i) {
        return array[i];
    }
    const T& operator[](size_t i) const {
        return array[i];
    }
};

template<size_t N, typename T>
Point<N, T> operator+(const Point<N, T>& a, const Point<N, T>& b) {
    Point<N, T> p {a};
    p += b;
    return p;
}

template<size_t N, typename T>
Point<N, T> operator-(const Point<N, T>& a, const Point<N, T>& b) {
    return a + (-b);
}

using Point3D = Point<>;
using Point2DInt = Point<2, int>;

#endif // POINT_H