Du bist nicht angemeldet.

Werbeanzeige

1

09.08.2014, 12:44

[c++] operator aus Basisklasse nutzen

Guten Tag,
leider ist mir keine bessere Überschrift eingefallen, die das besser beschreiben würde.
Mein Problem ist, dass ich die operatoren +, -, ..., +=, -=, ... einer Basisklasse auch gerne in meiner Childklasse nutzen würde.
Das bringt einige Probleme mit sich.
Zu aller erst einmal:
- Basisklasse und Childklassen haben identische Variabeln
- Die Funktionsweise der operatoren soll sich nicht ändern

Da diese Operatoren aber eine Referenz zurück geben sollen kann ich sie leider nicht einfach so nur durch Vererbung nutzen (bzw. können schon, aber dann wird das Objekt ja gesliced). Und das ist eigentlich auch schon mein Problem.
Was wäre in diesem Fall der korrekte Weg um solche Operatoren zu implementieren, um entsprechende Redundanzen zu vermeiden?
Habe jetzt ein paar Dinge im Internet gelesen. Von einer Helper Class Template, über operator als Freie Funktion, etc.
Wie löst ihr so etwas?

mfg

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »anti-freak« (09.08.2014, 13:03)


2

09.08.2014, 13:52

Die Membervariablen sind die selben und die Funktionsweise der Operatoren soll sich nicht ändern?

Seltsam, aber was du machen kannst, ist die Operators der vererbenden Klasse zu überladen und darin die der Base-Klasse aufzurufen:

C-/C++-Quelltext

1
2
3
4
5
class Derived : public Base {
public:
    // for each operator:
    Derived& operator+(const Derived& o) { Base::operator+(static_cast<const Base&>(o)); return *this; }
};
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

3

09.08.2014, 13:53

"Basisklasse und Childklassen haben identische Variabeln"?
Meinst du so etwas:

C-/C++-Quelltext

1
2
struct Foo{int a;};
class Bar : public Foo {int a;};

Dann dazu mal ganz quick & dirty:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
struct Foo{int a;};
struct Bar : public Foo {int a;
int& getFooA(){return Foo::a;};};
//later
Bar hi;
hi.a = 4;
hi.getA() = 6;
std::cout << hi.a << " " << hi.getFooA(); //stdout:4 6

Ich würde in so einem Fall zu einer Hilfsfunktion tendieren, die dann die jeweiligen zu verändernden Werte als Referenz entgegen nimmt und schließlich diese nach Wunsch des operators verändert.
So könnte der aus der Basisklasse die Funktion callen und ein jenen operator überschreibender würde das ganze dann mit den relevanten Variablen eben dieser Klasse callen.

MfG
Check

4

09.08.2014, 14:05

"Basisklasse und Childklassen haben identische Variabeln"?
Meinst du so etwas:

C-/C++-Quelltext

1
2
struct Foo{int a;};
class Bar : public Foo {int a;};


Nein, nein. Ich meinte damit nur, das in der Childklasse keine neuen Variabeln dazu kommen ;)



Die Membervariablen sind die selben und die Funktionsweise der Operatoren soll sich nicht ändern?

Seltsam, aber was du machen kannst, ist die Operators der vererbenden Klasse zu überladen und darin die der Base-Klasse aufzurufen:

C-/C++-Quelltext

1
2
3
4
5
class Derived : public Base {
public:
    // for each operator:
    Derived& operator+(const Derived& o) { Base::operator+(static_cast<const Base&>(o)); return *this; }
};

Wieso denn seltsam?
An sich dachte ich da auch schon an sowas, aber irgendwie werde ich damit nicht warm. Es muss doch irgendwie eine elegante Möglichkeit geben, das ich meine arithmetischen Operatoren nicht in jeder Childklasse neu implementieren muss, nur weil ich den Rückgabewert auf die Childklasse anpassen möchte.

An sich würden mir auch solche lustigen Konstrukte einfallen, aber irgendwie ist das auch nicht sonderlich schön <.<

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Foo
{
private:
   int x;

public:
   void setX(int x) //...
   int getX() const //...
}

class Bar : public Foo
{
}

template <class T>
T operator+(const T& lhs, const Bar& rhs)
{
   auto tmp = lhs;
   tmp.setX(lhs.getX() + rhs.getX());
   return tmp;
}

template <class T>
T& operator+=(const T& lhs, const Bar& rhs)
{
   lhs.setX(lhs.getX() + rhs.getX());
   return lhs;
}


EDIT: Wahlweise natürlich auch als friend funktion.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »anti-freak« (09.08.2014, 14:21)


5

09.08.2014, 14:28

Zitat

Wieso denn seltsam?

Weil mir der Sinn nicht wirklich klar wird. Deine Bar Klasse in deinem Beispiel kannst du ganz getrost static casten, sogar ein typedef würde da einiges vereinfachen.
Ich vermute, du stellst dir eher sowas vor:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Salt {
private:
    int m_AmountInGramm;
public:
    Salt& operator+(const Salt& rhs) { m_AmountInGramm += rhs.m_AmountInGramm; return *this; }
};
class RoadSalt : public Salt {
public:
    void PutOnRoad(const Road& r) { r.PutSaltOnIt(*this); } // Notice the forwarding = bad design!
};
class PotatoSalt : public Salt {
public:
    void PutOnPotato(const Potato& p) { ... }
};

Das ist aber eher schlechtes Design. Besser wäre dann die Auslagerung in die jeweilige Benutzung der Klasse:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
struct ISaltable{
    virtual void PutSaltOnit(const Salt& salt) = 0;
};
class Road : public ISaltable {
public:
    virtual void PutSaltOnIt(const Salt& salt) { ... }
};
class Potato : public ISaltable {
    virtual void PutSaltOnIt(const Salt& salt) { ... }
};


Vielleicht ist es hilfreich, wenn du uns deine konkrete Anwendung zeigst. Dann können wir eventuell dazu Alternativen finden ;)
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »iSmokiieZz« (09.08.2014, 14:36)


6

09.08.2014, 14:37

Ok, jetzt weiß ich nicht, was du mir eigentlich sagen möchtest :P

Ok, dann schieb ich jetzt mal mein Beispiel rein, um was es letztendlich auch geht:

Die Basisklasse:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename T>
    class PointBase
    {
    public:
        PointBase(T X, T Y) : m_X(X), m_Y(Y) { }

        T getX() const { return m_X; }
        T getY() const { return m_Y; }

        T& getX() { return m_X; }
        T& getY() { return m_Y; }

    private:
        T m_X;
        T m_Y;
    };


Und eine der Childklassen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Point : public PointBase<int32>
    {
    public:
        Point(int32 x = 0, int32 y = 0) : PointBase<int32>(x, y) { }

        Point& operator +=(const Point& p)
        {
            getX() += p.getX();
            getY() += p.getY();
            return *this;
        }


        Point& operator -=(const Point& p);
        Point& operator *=(const Point& p);
        Point& operator /=(const Point& p);
        Point& operator +=(const int32 &t);
        Point& operator -=(const int32 &t);
        Point& operator *=(const int32 &t);
        Point& operator /=(const int32 &t);
    };

    Point operator +(Point const& lhs, const Point& rhs)
    {
        Point tmp(lhs);
        tmp += rhs;
        return tmp;
    }

    Point operator -(Point const& lhs, const Point& rhs);
    Point operator *(Point const& lhs, const Point& rhs);
    Point operator /(Point const& lhs, const Point& rhs);
    Point operator +(Point const& lhs, int32 rhs);
    Point operator -(Point const& lhs, int32 rhs);
    Point operator *(Point const& lhs, int32 rhs);
    Point operator /(Point const& lhs, int32 rhs);


Es geht mir hier tatsächlich nur darum, dass ich nicht für jede Childklasse meine Rückgabetypen anpassen möchte!

7

09.08.2014, 14:46

Pack die Operatoren in deine template class und spezifier deine ChildClasses mit typedef:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
template<typename T> class PointBase
{
private:
    T m_X, m_Y;
public:
    PointBase<T>& operator+(const PointBase<T>& rhs) { m_X += rhs; m_Y += rhs; return *this; }
    // ...
};
typedef PointBase<int> IntPoint;
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

8

09.08.2014, 14:50

Das ist nicht Sinn der Sache.
PointF (auch eine der Childklassen) z.B. hat noch ein paar andere Methoden, die nichts in PointBase zu suchen haben.

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

9

09.08.2014, 14:54

Verwende doch ein Template mit Spezialisierung.
Hier mal ein Beispiel:

C-/C++-Quelltext

1
2
3
4
template<class T,int SIZE>
struct Point {
  T data[SIZE];
}

Dann für 2D:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template<class T>
struct Point<T,2> {

  union {
    T data[2];
   struct {
     T x,y;
   }
  }
  Point<T,2>() : x(0) , y(0) {}
  // mehr Konstruktoren 
  // und noch ein bisschen was
  const T& operator[] (int idx) const { return data[idx];}

  T* operator() () {
        return &data[0];
    }
    //! assignment operator
    Point<T,2>& operator = (const Point<T,2>& other) {
        x = other.x;
        y = other.y;
        return *this;
    }
}


Dann mal ein Beispiel für einen Operator

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
template<class T,int SIZE>
bool operator == (const Point<T,SIZE>& u,const Point<T,SIZE>& v) {
    for ( int i = 0; i < SIZE; ++i ) {
        if ( u.data[i] != v.data[i] ) {
            return false;
        }
    }
    return true;
}


Dann noch praktischerweise ein typedef:

C-/C++-Quelltext

1
2
typedef Point<float,2> Point2f;
typedef Point<float,3> Point3f;

Meine ganze Vector/Point Klasse ist so aufgebaut.

10

09.08.2014, 15:09

Zitat

PointF (auch eine der Childklassen) z.B. hat noch ein paar andere Methoden, die nichts in PointBase zu suchen haben.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
template <typename T> class PointBase
{
private:
    T m_X, m_Y;
public:
    PointBase()
    {
    }

    PointBase(const PointBase<T>& o)
        : m_X(o.m_X), m_Y(o.m_Y)
    {   
    }

    PointBase(const T& x, const T& y)
    {
        m_X = x; m_Y = y;
    }

    PointBase<T>& operator+(const PointBase<T>& rhs)
    {
        m_X += rhs.m_X; m_Y += rhs.m_Y;
        return *this;
    }
};

class PointF : public PointBase<float>
{
public:
    PointF()
        : PointBase<float>()
    {
    }

    PointF(const PointBase<float>& baseF)
        : PointBase<float>(baseF)
    {
    }

    void DoSomething()
    {
    }
};

PointF p1, p2;
p1 = p1 + p2;
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Werbeanzeige