Questions sur la POO dans MQL5 - page 45

 
Dmitry Fedoseev:

Quelqu'un peut-il expliquer en quoi cette initialisation des champs est meilleure que cela ?

est meilleur que ça :

Et quel est le but de toute façon ?

Les champs const peuvent être initialisés.

 
Dmitry Fedoseev:

Quelqu'un peut-il expliquer en quoi cette initialisation des champs est meilleure que cela ?

est meilleur que ça :

Et quel est le but de toute façon ?

Dans le premier cas, vous avez l'initialisation, et dans le deuxième cas - les opérations sont effectuées pour les champs initialisés par défaut. Donc tout dépend du compilateur, soit il n'y a aucune différence, soit il y a un tas d'actions supplémentaires à l'exécution. Dans les plus, la deuxième variante est considérée comme une mauvaise forme.
 
L'opérateur =, comme il se doit, renvoie la valeur r (5<(val=7) fonctionne), mais tout comme la sémantique de report, il n'y en a pas ici, et cela a été demandé, malheureusement, donc il n'y a pas non plus de constructeur de report.
 
Igor Makanu:

la question est purement théorique :

ont vu, peut-être dans SB, un appel de constructeur comme celui-ci :

comment ce code serait-il différent :

Je l'ai décompressé, je ne vois pas de différence, alors soyez un peu plus précis - quoi ou pourquoi pouvons-nous utiliser un appel forcé au constructeur pour les objets a1 et a2 ?

quelle est la "commodité" de la première option ?

La première option est appelée - Initialisation de la valeur, la seconde - Initialisation par défaut. Il existe des subtilités et des différences selon le type de l'objet (s'il s'agit d'un agrégat ou s'il a un constructeur par défaut, ...). Par exemple, le résultat ici sera absolument différent selon que l'on utilise des parenthèses ou non :

#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;
}

Si vous voulez saisir la théorie jusqu'à l'oreille, allez ici https://en.cppreference.com/w/cpp/language/initialization.

ZS : ce n'est pas tout à fait pertinent pour le mcl, mais pour comprendre le modèle de référence.

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:

Si vous voulez vous plonger dans la théorie, allez sur le site https://en.cppreference.com/w/cpp/language/initialization.

Pas encore nécessaire, mais je vais le sauvegarder pour savoir où lire la source.

Merci à tous !

Vladimir Simakov:
Dans les plus, la deuxième option est considérée comme une mauvaise forme.

Il y aura le même problème que j'ai rencontré - la duplication du code pour l'initialisation des champs, lorsqu'il devient nécessaire d'écrire plus d'un constructeur dans la classe.

si vous utilisez la variante 2, vous pouvez mettre le code dupliqué dans une méthode séparée et appeler cette méthode après avoir effectué les actions nécessaires dans chaque constructeur (j'ai l'initialisation de la classe par la structure et la variante 2 par le nom du fichier où sont écrites les données de cette structure (sauvegarde de secours))


Écrire que la répétition du code est mauvaise... ce n'est pas intéressant, mais le problème est que si j'ajoute un nouveau champ à la classe, je dois me souvenir d'initialiser ce champ autant de fois que les constructeurs - le fait que ce ne soit pas pratique, mais le fait que vous pouvez oublier de le faire N fois, est un problème, imho

 
Igor Makanu:

Pas encore nécessaire, mais je vais le garder, je saurai où lire la source originale.

Merci à tous !

Il y aura le même problème que j'ai rencontré - la duplication du code pour l'initialisation des champs, lorsqu'il devient nécessaire d'écrire plus d'un constructeur dans la classe.

si vous utilisez la variante 2, vous pouvez mettre le code dupliqué dans une méthode séparée et appeler cette méthode après les actions nécessaires dans chaque constructeur (j'ai l'initialisation de la classe par la structure et la variante 2 par le nom du fichier où sont écrites les données de cette structure (sauvegarde de secours))


Écrire que la répétition du code est mauvaise... ce n'est pas intéressant, mais le problème est que si j'ajoute un nouveau champ à la classe, je dois me souvenir d'initialiser ce champ autant de fois que les constructeurs - le fait que ce ne soit pas pratique, mais le fait que vous pouvez oublier de le faire N fois, est un problème, imho

Une macro qui peut également être paramétrique. Oubli (j'en souffre moi-même), se traite aussi avec cette))))

 
Igor Makanu:

il y aura le même problème que j'ai rencontré - la duplication du code d'initialisation des champs lorsqu'il est nécessaire d'écrire plus d'un constructeur dans la classe.

Si vous utilisez la variante 2, vous pouvez mettre le code dupliqué dans une méthode séparée et appeler cette méthode après les actions nécessaires dans chaque constructeur (j'ai l'initialisation de la classe par la structure et la variante 2 par le nom d'un fichier où les données de cette structure sont écrites (sauvegarde de secours)).


Écrire que la répétition du code est mauvaise... Ce n'est pas intéressant, mais le problème est que si j'ajoute un nouveau champ à la classe, je devrai me souvenir d'initialiser ce champ autant de fois qu'il y a de constructeurs - le fait que ce ne soit pas pratique, mais le fait que vous puissiez oublier de le faire N fois, est un problème, imho.

Allez, tout peut être résolu :

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
        ...
    }
};

Je ne suis pas contre les constructeurs par défaut (ou l'initialisation par défaut), mais si votre constructeur laisse l'objet (enfin, si ce n'est pas un agrégat débile) dans un état indéfini et qu'ensuite vous l'initialisez via quelques béquilles, alors vous faites tout de travers.

ZS : au fait, vous pouvez déléguer le constructeur dans les pros, à défaut de bien sûr

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

Allez, c'est soluble :

un peu en avance sur moi, vient de s'asseoir au PC.

Le conseil de @Vladimir Simakov d'initialiser les champs de classe en utilisant une macro, pour ne pas oublier d'initialiser correctement, eh bien, oui - bonne astuce, mais le code se lira comme un appel à une méthode qui initialise les champs, difficile de supposer à quel point ce ton est bon....


Votre exemple n'est pas non plus le plus raffiné, mais il résout le problème exactement de la façon dont je l'ai fait maintenant - une méthode d'initialisation séparée.

imho, c'est une question d'objectif - la bonne façon d'écrire une classe de base avec tous les champs nécessaires, d'hériter d'elle, de rendre le constructeur protégé et d'initialiser la classe de base à partir des héritiers et ainsi sera la protection contre "l'oubli" - il n'y a pas de constructeur par défaut ? - au moins, vous pouvez regarder chez mikrosoft si vous tapez "protected constructor" sur google - il y aura un article.


Mon problème est un peu différent, j'ai volontairement laissé l'héritage, pour sauvegarder l'état des champs de la classe dans un fichier, j'ai 2 champs de 2 classes et leur état est également sauvegardé dans le même fichier en appelant les méthodes appropriées. J'ai essayé de tout sauvegarder en héritant d'une classe de base, c'est devenu très désordonné, je l'ai réécrit sans héritage, tout est devenu "transparent" maintenant.

 
Igor Makanu:

Un peu plus tôt que moi, il vient de s'asseoir au PC.

Le conseil de @Vladimir Simakov d'initialiser les champs de la classe en utilisant une macro, afin de ne pas oublier d'initialiser correctement, eh bien, oui - bonne astuce, mais le code se lira comme un appel à une méthode qui initialise les champs, difficile de supposer à quel point le ton est bon....


Votre exemple n'est pas non plus le plus raffiné, mais il résout le problème exactement de la façon dont je l'ai fait maintenant - une méthode d'initialisation séparée.

imho, c'est une question de but - la bonne façon d'écrire une classe de base avec tous les champs nécessaires, d'en hériter, de rendre le constructeur protégé et d'initialiser la classe de base à partir des héritiers et ainsi sera la protection contre "l'oubli" - il n'y a pas de constructeur par défaut, non ? - au moins, vous pouvez regarder chez mikrosoft si vous tapez "protected constructor" sur google - il y aura un article.


Mon problème est un peu différent, j'ai volontairement laissé l'héritage, pour sauvegarder l'état des champs de la classe dans un fichier, j'ai 2 champs de 2 classes, et leur état est aussi sauvegardé dans le même fichier en appelant les méthodes appropriées. J'ai essayé de tout sauvegarder lorsque j'ai hérité d'une classe de base, cela est devenu très compliqué ; je l' ai réécrit sans héritage, tout est devenu "transparent" maintenant.

Qu'est-ce qu'il y a de si déroutant ?

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

Qu'est-ce qu'il y a de si déroutant ?

c'est exactement ce dont je me suis éloigné, et au début j'ai fait

avec cette approche - hériter d'une classe de base, "où tout se trouve" - tout fonctionne, mais jusqu'à ce que nous voulions essayer de faire plusieurs champs de classe, et ensuite nous voulons ajouter plusieurs champs dans chaque classe de champ et clouer le tout avec un tableau dynamique de classes

et ensuite nous obtiendrons que nous ne pouvons pas implémenter la méthode Save(int hndl) elle-même dans une classe de base - cette méthode sera en fait une interface, qui, comme discuté ci-dessus, ne sera pas du tout nécessaire - vous pouvez l'écrire sans interfaces

j'ai l'habitude de maximiser mes efforts sur la flexibilité du code - un minimum d'agitation - le résultat est un nouveau problème résolu, que ma terminologie soit pardonnée))))


le problème sera que, lors de la mise en œuvre de l'enregistrement dans un fichier, vous devez élaborer l'en-tête du fichier, qui doit décrire la quantité totale des différents types d'entrées et ensuite vous devez ne pas perdre la logique / l'ordre de l'enregistrement des données, afin de tout lire correctement..... et la finale sera si vous changez même un seul champ dans une classe

à mon avis, la difficulté d'un héritage aussi "facile" à partir d'une classe de base se résume au développement et à la maintenance d'une petite base de données, à la surveillance constante des modifications apportées à chaque classe.

Raison: