Fragen zu OOP in MQL5 - Seite 45

 
Dmitry Fedoseev:

Kann jemand erklären, warum diese Initialisierung der Felder besser ist als diese:

ist besser als dies:

Und was ist überhaupt der Sinn?

const-Felder können initialisiert werden.

 
Dmitry Fedoseev:

Kann jemand erklären, warum diese Initialisierung der Felder besser ist als diese:

ist besser als dies:

Und was ist überhaupt der Sinn?

Im ersten Fall handelt es sich um eine Initialisierung, im zweiten Fall werden Operationen für standardmäßig initialisierte Felder durchgeführt. So hängt alles von Compiler, entweder es macht keinen Unterschied, oder eine Scheiße-Ladung von zusätzlichen Aktionen in der Laufzeit. Bei Pluszeichen gilt die zweite Variante als schlechte Form.
 
Der =-Operator gibt, wie es sein sollte, den r-Wert zurück (5<(val=7) funktioniert), aber gerade mit der Carry-Semantik gibt es hier keinen, und es wurde leider danach gefragt, also gibt es auch keinen Carry-Konstruktor.
 
Igor Makanu:

die Frage ist rein theoretisch:

haben, möglicherweise in SB, einen solchen Konstruktoraufruf gesehen:

wie würde sich dieser Code unterscheiden:

Ich habe es entpackt, ich sehe keinen Unterschied, dann ein bisschen genauer sein - was oder warum können wir einen erzwungenen Konstruktor Aufruf für a1 und a2 Objekte verwenden?

Worin besteht die "Bequemlichkeit" der ersten Option?

Die erste Option heißt - Wertinitialisierung, die zweite - Standardinitialisierung. Es gibt Feinheiten und Unterschiede je nach Art des Objekts (ob es sich um ein Aggregat handelt oder mit Standardkonstruktor, ...). Zum Beispiel wird das Ergebnis hier völlig anders ausfallen, wenn Sie Klammern verwenden, als wenn Sie sie weglassen:

#include <iostream>
using namespace std;
struct S {
        int i;
        int q;
        int f;
};
struct Q : S {
        Q():S() {}
        //Q() {}
};
int main() {
        Q s;
        cout << s.i << s.q << s.f << endl;
}

Wenn Sie die Theorie bis zu Ihren Ohren begreifen wollen, gehen Sie hier https://en.cppreference.com/w/cpp/language/initialization

ZS: Es ist nicht alles relevant für den MCL, aber für das Verständnis des Referenzmodells.

Initialization - cppreference.com
  • en.cppreference.com
Initialization of a variable provides its initial value at the time of construction. The initial value may be provided in the initializer section of a declarator or a new expression. It also takes place during function calls: function parameters and the function return values are also initialized. For each declarator, the initializer may be one...
 
Vict:

Wenn Sie sich bis zu den Ohren in der Theorie verlieren wollen, gehen Sie hier https://en.cppreference.com/w/cpp/language/initialization

Das ist noch nicht nötig, aber ich werde es speichern, damit ich weiß, wo ich die Quelle lesen kann.

Vielen Dank an alle!

Wladimir Simakow:
Bei Pluszeichen gilt die zweite Option als schlechte Form.

Es wird das gleiche Problem auftreten, auf das ich gestoßen bin - Verdoppelung des Codes für die Initialisierung von Feldern, wenn es notwendig wird, mehr als einen Konstruktor in der Klasse zu schreiben

wenn Sie Variante 2 verwenden, können Sie den doppelten Code in eine separate Methode einfügen und diese Methode nach den erforderlichen Aktionen in jedem Konstruktor aufrufen (ich habe die Klasseninitialisierung nach Struktur und Variante 2 nach dem Namen der Datei, in die die Daten dieser Struktur geschrieben werden (Backup-Sicherung))


Diesen sich wiederholenden Code zu schreiben ist schlecht... es ist nicht interessant, aber das Problem ist, wenn ich ein neues Feld zur Klasse hinzufüge, muss ich daran denken, dieses Feld so oft wie Konstruktoren zu initialisieren - die Tatsache, dass es nicht bequem ist, aber die Tatsache, dass man vergessen kann, es N-mal zu tun, ist ein Problem, imho

 
Igor Makanu:

Das ist noch nicht nötig, aber ich werde es aufbewahren, damit ich weiß, wo ich die Originalquelle lesen kann.

Ich danke Ihnen allen!

Es wird das gleiche Problem auftreten, auf das ich gestoßen bin - Verdoppelung des Codes für die Initialisierung von Feldern, wenn es notwendig wird, mehr als einen Konstruktor in der Klasse zu schreiben

Wenn Sie Variante 2 verwenden, können Sie den doppelten Code in eine separate Methode einfügen und diese Methode nach der Durchführung der erforderlichen Aktionen in jedem Konstruktor aufrufen (ich habe die Klasseninitialisierung nach Struktur und Variante 2 nach dem Namen der Datei, in die die Daten dieser Struktur geschrieben werden (Backup-Sicherung))


Diesen sich wiederholenden Code zu schreiben ist schlecht... es ist nicht interessant, aber das Problem ist, wenn ich ein neues Feld zur Klasse hinzufüge, muss ich daran denken, dieses Feld so oft wie Konstruktoren zu initialisieren - die Tatsache, dass es nicht bequem ist, aber die Tatsache, dass man vergessen kann, es N-mal zu tun, ist ein Problem, imho

Ein Makro, das auch parametrisch sein kann. Vergesslichkeit (ich leide selbst darunter), behandelt auch mit this))))

 
Igor Makanu:

wird das gleiche Problem auftreten wie bei mir - Duplizierung des Feldinitialisierungscodes, wenn es notwendig ist, mehr als einen Konstruktor in der Klasse zu schreiben

Wenn Sie Variante 2 verwenden, können Sie den duplizierten Code in einer separaten Methode unterbringen und diese Methode nach allen notwendigen Aktionen in jedem Konstruktor aufrufen (ich habe die Klasseninitialisierung nach Struktur und Variante 2 nach dem Namen einer Datei, in die die Daten dieser Struktur geschrieben werden (Backup-Sicherung))


Diesen sich wiederholenden Code zu schreiben ist schlecht... Es ist uninteressant, aber das Problem ist, wenn ich der Klasse ein neues Feld hinzufüge, muss ich mich daran erinnern, dieses Feld so oft zu initialisieren, wie es Konstruktoren gibt - die Tatsache, dass es nicht bequem ist, aber die Tatsache, dass man vergessen kann, es N-mal zu tun, ist ein Problem, imho

Komm schon, alles kann gelöst werden:

class Q {
    void ctr_base(T t, S s) {//make init tasks
        }
public:
    Q(T t, S s, int i) {
        // make base init
        ctr_base(t, s);
        // make additional tasks
        ...
    }
    Q(T t, S s, strint str) {
        // make base init
        ctr_base(t, s);
        // make additional tasks
        ...
    }
};

Ich bin nicht gegen Standard-Konstruktoren (oder Standard-Initialisierung), aber wenn Ihr Konstruktor lässt das Objekt (gut, wenn es nicht ein dummes Aggregat) in undefinierten Zustand und dann Sie doinitialize es über einige Krücken, dann sind Sie tun es ganz falsch.

ZS: Übrigens, man kann Konstruktoren in den Pros delegieren, natürlich ohne

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};
 
Vict:

Komm schon, das ist lösbar:

ein wenig vor mir, hat sich gerade an meinen PC gesetzt.

@Vladimir Simakovs Ratschlag, Klassenfelder mit einem Makro zu initialisieren, um nicht zu vergessen, richtig zu initialisieren, nun ja - guter Trick, aber der Code liest sich wie ein Aufruf einer Methode, die Felder initialisiert, schwer anzunehmen, dass dies ein guter Ton ist...


Ihr Beispiel ist auch nicht das raffinierteste, aber es löst das Problem genau so, wie ich es jetzt gemacht habe - eine separate Initialisierungsmethode \.

imho ist es eine Frage des Zwecks - der richtige Weg, um eine Basisklasse mit allen notwendigen Feldern zu schreiben, erben von ihm, machen den Konstruktor geschützt und initialisieren die Basisklasse von den Erben und somit wird Schutz vor "Vergessen" - es gibt keine Standard-Konstruktor, richtig? - Sie können also zumindest bei Microsoft nachsehen, wenn Sie "geschützter Konstruktor" googeln - dort finden Sie einen Artikel


Mein Problem ist ein wenig anders, ich absichtlich links die Vererbung, um den Zustand der Klasse Felder in einer Datei zu speichern, ich habe 2 Felder von 2 Klassen, und ihr Zustand ist auch in der gleichen Datei durch Aufruf der entsprechenden Methoden gespeichert. Ich habe versucht, alles zu speichern, wenn ich von einer Basisklasse geerbt habe, es wurde sehr unübersichtlich, ich habe es ohne Vererbung neu geschrieben, alles wurde jetzt "transparent".

 
Igor Makanu:

Ein bisschen vor mir, hat sich gerade an den PC gesetzt.

@Vladimir Simakovs Ratschlag, Klassenfelder mit einem Makro zu initialisieren, um nicht zu vergessen, richtig zu initialisieren, nun ja - guter Trick, aber der Code wird sich wie ein Aufruf einer Methode lesen, die Felder initialisiert, schwer anzunehmen, wie gut der Ton ist...


Ihr Beispiel ist auch nicht das raffinierteste, aber es löst das Problem genau so, wie ich es jetzt gemacht habe - eine separate Initialisierungsmethode \.

imho ist es eine Frage des Zwecks - der richtige Weg, um eine Basisklasse mit allen notwendigen Feldern zu schreiben, erben von ihm, machen den Konstruktor geschützt und initialisieren die Basisklasse von den Erben und damit wird Schutz vor "Vergessen" - es gibt keine Standard-Konstruktor? - Sie können also zumindest bei Microsoft nachschauen, wenn Sie "geschützter Konstruktor" googeln - dort finden Sie einen Artikel


Mein Problem ist ein wenig anders, ich absichtlich links die Vererbung, um den Zustand der Klasse Felder in einer Datei zu speichern, ich habe 2 Felder von 2 Klassen, und ihr Zustand ist auch in der gleichen Datei durch Aufruf der entsprechenden Methoden gespeichert. Ich habe versucht, alles zu speichern, wenn ich es von einer Basisklasse geerbt habe, es wurde sehr kompliziert; ich habe es ohne Vererbung neu geschrieben, alles wurde jetzt "transparent".

Was ist daran so verwirrend?

...
virtual void CBase::Save(int hndl){...}
...
CObj:public CBase
...
void CObj::Save(int hndl){
   CBase::Save(hndl);
   ...
}
 
Vladimir Simakov:

Was ist daran so verwirrend?

Das ist genau das, wovon ich weggekommen bin, und anfangs habe ich

mit diesem Ansatz - von einer Basisklasse zu erben, "wo alles ist" - funktioniert alles, aber bis wir versuchen wollen, mehrere Klassenfelder zu machen, und dann wollen wir mehrere Felder in jeder Feldklasse hinzufügen und alles mit einem dynamischen Array von Klassen festnageln

und dann werden wir feststellen, dass wir die Methode Save(int hndl) selbst nicht in einer Basisklasse implementieren können - diese Methode wird eigentlich eine Schnittstelle sein, die, wie oben besprochen, überhaupt nicht benötigt wird - man kann sie ohne Schnittstellen schreiben

In der Regel maximiere ich meine Bemühungen auf die Flexibilität des Codes - ein Minimum an Aufwand - das Ergebnis ist ein neues Problem, das gelöst wurde, man möge mir meine Terminologie verzeihen)))


Das Problem besteht darin, dass Sie bei der Implementierung des Speicherns in eine Datei den Dateikopf ausarbeiten müssen, der die Gesamtmenge der verschiedenen Arten von Einträgen beschreiben sollte, und dann müssen Sie die Logik/Reihenfolge der Datenspeicherung nicht verlieren, um alles korrekt zu lesen.... und das Finale wird sein, wenn Sie auch nur ein Feld in einer Klasse ändern

imho ist die "einfache" Vererbung von einer Basisklasse sehr mühsam - sie erfordert die Entwicklung und Pflege einer kleinen Datenbank und die ständige Überwachung von Änderungen in jeder Klasse