Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

11

24.08.2014, 12:33

Und wenn man von dieser Basis Klasse eine spezifische Unterklasse benötigt, habe ich mir (u.a. für mein Compiler Projekt) ein Pattern der folgenden Art angewöhnt: (...)

Wenn man sowas braucht, hat man normalerweise einen Designfehler gemacht.

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

12

24.08.2014, 12:39

Hab mit keiner anderen Antwort von dir gerechnet :D
Ich denke nicht, dass ich in dem Fall einen Design Fehler gemacht habe. Aber guck dir bei Interesse & Zeit den Source an (hab ihn ja hier im Forum im betreffenden Thread verlinkt) und schreib mir (im Falle du entdeckst wirklich einen derben Schnitzer beim Design) einen Kommentar.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

13

24.08.2014, 15:09

Und wenn man von dieser Basis Klasse eine spezifische Unterklasse benötigt, habe ich mir (u.a. für mein Compiler Projekt) ein Pattern der folgenden Art angewöhnt: [...]

Wenn du eigentlich eine spezifische Unterklasse benötigst, wieso nimmt die Funktion dann einen Zeiger auf die Basisklasse als Parameter?

Ja das ist richtig, dass es so geht. Aber das geht nur bei Zeigern.
Bei so etwas:

[...]

Bekomme ich doch ObjectSlicing? Und immer von dem Zeiger der Basisklasse auf die Childklasse zu casten, finde ich schlechten Stil.

Aber wenn du so etwas willst, wieso muss das übergebene Objekt dann von einer bestimmten Basisklasse abgeleitet sein?

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

14

24.08.2014, 15:21

Und wenn man von dieser Basis Klasse eine spezifische Unterklasse benötigt, habe ich mir (u.a. für mein Compiler Projekt) ein Pattern der folgenden Art angewöhnt: [...]

Wenn du eigentlich eine spezifische Unterklasse benötigst, wieso nimmt die Funktion dann einen Zeiger auf die Basisklasse als Parameter?

Das war nur Themen bezogen. Ich benutze es für meine Member. Einfaches wenn auch nicht reales Beispiel:

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
class Float;
class Int;

class Numeric {
public:
    virtual Float* isFloat() {
        return nullptr;
    }

    virtual Int* isFloat() {
        return nullptr;
    }
};

class Int : public Numeric {
public:
    Int* isInt() override {
        return this;
    }
};

class Float : public Numeric {
public:
    Float* isFloat() override {
        return this;
    }
};

class Var {
private:
    Numeric* value;
    
public:
    explicit Var(Numeric* num) : value(num) {

    }

    void semantic() {
        if (Float* fp = this->value->isFloat()) {
            // semantic for float
        } else if (Int* ip = this->value->isInt()) {
            // semantic for integer
        }
    }
};

Je nachdem um was es sich genau handelt, habe ich z.B. ein anderes semantisches Vorgehen für diesen Typ. Verschafft das Beispiel etwas mehr Klarheit? Ich würde ungern eine der berüchtigten Seitenlangen und sinnlosen Diskussionen damit beginnen. :)
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

15

24.08.2014, 15:32

Wieso machst du aus (einem Teil von) semantic nicht eine virtuelle Methode, die Float und Int dann überladen?

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

16

24.08.2014, 15:41

Weil die semantic zu 'Var' gehört und nicht zu Int oder Float. Aber sagen wir mal, ich mache das: Man könnte es sich natürlich so hinbiegen und semantic aufsplitten. Das wäre eine Art es zu lösen, aber nicht die einzige. Mit 'meiner' Methode hätte man es zentral an einem Punkt gelöst. Also wieso soll das eine ein Design Fehler und das andere die bewährte Art sein?
Anderes Beispiel:
Ich habe eine Expression Klasse für Terme, Compare's, Condition's, Arrays etc. Nun hat jede Klasse für sich zwar die Eigenart, dass sie Natur gemäß eine Expression ist, jedoch ganz andere Verhaltensweise und damit Methoden. Ein Term nimmt Literale, Variablen und Operatoren entgegen, ein Array jedoch z.B. keine Operatoren an sich. Somit hat man in dem einen Konstrukt Methoden, die die jeweils andere Klasse gar nicht benötigt / haben darf.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

17

25.08.2014, 02:53

Weil die semantic zu 'Var' gehört und nicht zu Int oder Float. Aber sagen wir mal, ich mache das: Man könnte es sich natürlich so hinbiegen und semantic aufsplitten. Das wäre eine Art es zu lösen, aber nicht die einzige. Mit 'meiner' Methode hätte man es zentral an einem Punkt gelöst. Also wieso soll das eine ein Design Fehler und das andere die bewährte Art sein?

Mit deiner Methode müssen, sobald ein neuer Numeric Typ hinzugefügt wird, alle Stellen im Code, an denen der konkrete Typ eine Rolle spielt entsprechend angepasst werden; das Liskov'sche Substitutionsprinzip ist verletzt. Wo liegt der Sinn eines Interface, wenn der Code, der das Interface verwendet, plötzlich wissen muss, was sich hinter dem Interface verbirgt?

Anderes Beispiel:
Ich habe eine Expression Klasse für Terme, Compare's, Condition's, Arrays etc. Nun hat jede Klasse für sich zwar die Eigenart, dass sie Natur gemäß eine Expression ist, jedoch ganz andere Verhaltensweise und damit Methoden. Ein Term nimmt Literale, Variablen und Operatoren entgegen, ein Array jedoch z.B. keine Operatoren an sich. Somit hat man in dem einen Konstrukt Methoden, die die jeweils andere Klasse gar nicht benötigt / haben darf.

Sobald du dich in der Situation findest, dass du eine Methode hast, um den konkreten Typ hinter einer Schnittstelle abzufragen (oder einen dynamic_cast, läuft beides in der Regel auf das Selbe raus), kannst du dir praktisch sicher sein, dass dein Design ein Problem hat. Zumindest ist das in den genannten Beispielen imo definitiv der Fall und ich hab in all meinen Jahren noch kein Beispiel gesehen, wo dies eine gute Lösung gewesen wäre. Eine bessere Lösung findet sich meist im Visitor bzw. Double Dispatch Pattern.

Alternative Sichtweise: Sobald du von Außen den konkreten Typ hinter einem Interface abfrägst, bist du an einem Punkt, wo du dir selbst widersprichst. Denn wenn du an einer Stelle mit einem Interface arbeitest, dann drückst du damit aus, dass der entsprechende Code mit jedem Objekt, das dieses Interface implementiert, arbeiten kann; genau das ist die Idee von Interfaces bzw. Vererbung im Sinne der OOP. Wenn genau dieser Code dann nun aber für konkrete Typen etwas anderes tun muss, kann das nur einen von zwei Gründen haben: Entweder sollte der Code von vorn herein eigentlich nicht mit der Basisklasse, sondern mit den konkreten Typen arbeiten (weil das, was dort getan werden soll, eben doch nicht auf alle Typen generalisierbar ist) oder das Interface stellt keine passende Abstraktion dar (weil etwas, das konzeptionell eigentlich auf alle Typen generalisierbar sein sollte, über das gewählte Interface nicht abgedeckt ist).

Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von »dot« (25.08.2014, 03:09)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

18

25.08.2014, 06:40

Könnt ihr diese Diskussion eventuell auslagern? Ihr betreibt gerade Topic-Napping. (auch wenn ihr Recht habt)

@FSA: Polymorphes Verhalten bekommst Du eben nicht auf diese Art und Weise. Entweder hast Du einen Pointer und bist polymorph oder nicht. Was ist hier Dein konkreter Anwendungsfall? Worauf willst Du hinaus?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »BlueCobold« (25.08.2014, 06:49)


buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

19

25.08.2014, 07:47

Eigentlich sind dafür die sogenannten Traits gedacht. Dadurch kann man spezifische Aufgaben bei der Verwendung von Templates
dann für bestimmte Typen definieren.
Hier mal ein Beispiel. Sagen wir mal Du möchtest eine spezifische Methode zum Löschen Deiner Variable haben.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template class T>
struct NumericTrait {};

<template>
struct NumericTrait<float> {
  static void clear(float& v) {
    v = 0.0f;
  }
}
template<class T>
class Numeric {

public:
  NumericTrait<T> trait;
  Numeric() {
    trait::clear(data);
  }
private:
  T data;
}

Am besten mal nach dem Thema googlen. Ich glaube mein Beispiel ist etwas arg doof gewählt. Ist halt noch früh.

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

20

25.08.2014, 09:28

Könnt ihr diese Diskussion eventuell auslagern? Ihr betreibt gerade Topic-Napping. (auch wenn ihr Recht habt)

Jeder hat seinen Standpunkt zum Ausdruck gebracht, Diskussion ist damit beendet. ;)
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Werbeanzeige