Erreurs, bugs, questions - page 2198

 
S'il vous plaît, aidez-moi à faire une chose simple
class A
{
public:  
  int i;
  
  A* operator *( const A &Value ) const
  {
    A* Res = new A;
    
    Res.i = this.i * Value.i;
    
    return(Res);
  }
};

void OnStart()
{
  A a, b, c;
  
  a.i = 2;
  b.i = 3;
  c.i = 5;
  
  A* d = a * b * c;
  
  Print(d.i); // 30
  
  delete d;
}

Vous pouvez clairement voir dans le code qu'il devrait y avoir une fuite de mémoire. Et c'est exactement ce qui se passe. Cependant, comment l'écrire correctement pour pouvoir "multiplier" les objets ? Tout est clair avec l'opérateur "*=", comme c'est clair dans le cas des structures. Mais avec les classes, comment mettre en œuvre cette simple fonctionnalité ?


SZZ Et ce code.

class A
{
private:
  A Tmp;
  
public:
  int i;
  
  A* operator *( const A &Value )
  {
    this.Tmp.i = this.i * Value.i;
    
    return(&Tmp);
  }
};

void OnStart()
{
  A a, b, c;
  
  a.i = 2;
  b.i = 3;
  c.i = 5;
  
  A* d = a * b * c;
  
  Print(d.i); // 30
}

Crash ME par F7 et tue le Terminal par F5. Pourquoi suis-je si chanceux ?

 
fxsaber:

ZS Et ce code

Crash ME sur F7 et tue le Terminal sur F5. Pourquoi suis-je si chanceux ? !

class A
{
private:
  A Tmp;
  
public:
  int i;
  
  A* operator *( const A &Value )
  {
    this.Tmp.i = this.i * Value.i;
    
    return(&Tmp);
  }
};

void OnStart()
{
  A a, b, c;
  
  a.i = 2;
  b.i = 3;
  c.i = 5;
  
  A* d = a * b * c;
  
  Print(d.i); // 30
}

Probablement une récursion infinie et un débordement de pile.

 
Sergey Dzyublik:

Récursion infinie possible et débordement de pile.

Il est certain que le problème se situe au niveau de l'allocation, mais la cause semble être plus profonde.

Et il ne devrait évidemment pas y avoir de récursion au moment de l'exécution.

 
fxsaber:
S'il vous plaît, aidez-moi à faire une chose simple
class A
{
public:
  A() {}
  A(const A& other) 
  {
   this.i = other.i;
  }
  
  int i;
  
  A operator *( const A &Value ) const
  {
    A Res;
    
    Res.i = this.i * Value.i;
    
    return(Res);
  }
};

void OnStart()
{
  A a, b, c;
  
  a.i = 2;
  b.i = 3;
  c.i = 5;
  
  A d = a * b * c;
  
  Print(d.i); // 30
}
Les frais généraux, bien sûr, mais si la commodité est plus importante...
 
Комбинатор:
Les frais généraux le seront bien sûr, mais si la commodité est plus importante...

Merci ! Il s'avère que l'objet de retour et "=" lors de la définition d'un objet passent par un constructeur supplémentaire.

En fait, j'aimerais voir un tableau clair quelque part, indiquant quels cas sont appelés constructeurs (et lesquels), et quels cas sont appelés opérateurs. Pour l'instant, tout est intuitif, ce qui échoue, bien sûr.

 
fxsaber:

En fait, j'aimerais voir un tableau quelque part, où les constructeurs sont appelés (et lesquels) et où les opérateurs sont appelés. Pour l'instant, tout se passe au niveau de l'intuition, et cela échoue, bien sûr.

Si vous retournez quelque chose par valeur, il sera assigné via le copieur-constructeur (si le même type) si c'est une définition ou l'opérateur sinon.

void OnStart()
{
  A a, b, c;
  
  a.i = 2;
  b.i = 3;
  c.i = 5;
  
  A d = a * b * c;
  // код аналогичен A d(a * b * c);
  // будет вызван копи-конструктор.

  A e;
  e = a * b * c;
  // будет вызван оператор =
  // но копи-конструктор все равно нужен для множественного умножения
  
  Print(d.i); // 30
}
 
Комбинатор:

Si vous retournez quelque chose par valeur, il sera assigné via le copieur-constructeur (si le même type) s'il s'agit d'une définition ou l'opérateur sinon.

C'est clair, merci !


SZZ a amorcé votre code

class A
{
public:
  A() { Print(__FUNCSIG__); }
  A(const A& other) 
  {
   Print(__FUNCSIG__);
   this.i = other.i;
  }
  
  ~A()
  {
   Print(__FUNCSIG__);
  }
  
  int i;
  
  A operator *( const A &Value ) const
  {
    Print(__FUNCSIG__);

    A Tmp;
    
    Tmp.i = this.i * Value.i;
        
    return(Tmp);
  }
};

void OnStart()
{
  if (true)
  {
    A a, b, c;
    
    a.i = 2;
    b.i = 3;
    c.i = 5;
    
    A d = a * b * c;

    Print(d.i); // 30
  }
  
  Print("Kill!");  
}


Résultat

void A::A() // a
void A::A() // b
void A::A() // c
A A::operator*(const A&) const
void A::A() // Tmp
void A::A(const A&) // return
void A::~A() // Tmp
A A::operator*(const A&) const
void A::A() // Tmp
void A::A(const A&) // return
void A::~A() // Tmp
void A::A(const A&) // d
30
void A::~A() // a
void A::~A() // b
void A::~A() // c
void A::~A() // d
void A::~A() // return
void A::~A() // return
Kill!

Les lignes surlignées soulèvent des questions. Pourquoi les objets temporaires ne se sont pas plantés dès qu'ils ont été utilisés ? C'est-à-dire, avant la sortie de trente. De tels objets feraient planter ma machine, qui est censée gérer des dizaines de millions de tics. Chaque objet engloutira des gigaoctets de RAM, et je ne veux pas avoir de tels objets temporaires au lieu de les tuer immédiatement.

 
fxsaber:

Les lignes surlignées soulèvent des questions. Pourquoi les objets temporaires ne se sont pas plantés dès qu'ils ont été utilisés ? C'est-à-dire avant la sortie de trente ans.

En général, les objets temporaires ne sont pas supprimés immédiatement mais à la fin du contexte.

Si vous voulez une suppression rapide, contrôlez le contexte.

void OnStart()
{
  if (true)
  {
    A a, b, c;
    
    a.i = 2;
    b.i = 3;
    c.i = 5;
    
    A d;
    {
      d = a * b * c;
    }

    Print(d.i); // 30
  }
  
  Print("Kill!");  
}

Vrai, vous devrez utiliser l'opérateur =

 
A100:

Sur mon nouveau graphique, l'exemple ne fonctionnait que lorsque je le redémarrais (tous les boutons), ce qui est en soi incompréhensible. Mais maintenant je comprends ceci

Avec l'ouverture des transactions, l'effet spécifié disparaît (uniquement au redémarrage).

Des modifications ont été apportées à la documentation. Le problème avec le code était que l'envoi d'une commande d'événement souris au graphique ne fait que placer la commande dans la file d'attente du graphique. Et si cette file d'attente n'est pas traitée (par exemple le week-end), le graphique ne peut pas recevoir les événements spécifiés. Ajout d'une note et d'un exemple corrigé à la description de ChartSetInteger:

Note

La fonction est asynchrone - cela signifie que la fonction n'attend pas l'exécution de la commande, mise en file d'attente avec succès pour le graphique spécifié, mais retourne le contrôle immédiatement. La propriété ne sera modifiée qu'après le traitement de la commande dans la file d'attente du graphique. La fonction ChartRedraw doit être appelée pour exécuter immédiatement les commandes de la file d'attente des graphiques.

Si vous devez modifier immédiatement plusieurs propriétés du graphique, les fonctions appropriées (ChartSetString, ChartSetDouble, ChartSetString) doivent être exécutées dans un seul bloc de code, puis ChartRedraw doit être appelé une seule fois.

Pour vérifier le résultat de l'exécution, vous pouvez utiliser une fonction qui interroge la propriété graphique spécifiée (ChartGetInteger, ChartGetDouble, ChartSetString). Notez que ces fonctions sont synchrones et attendent le résultat de l'exécution.

Exemple :

//+------------------------------------------------------------------+
//| Fonction d'initialisation de l'expert|
//+------------------------------------------------------------------+
voidOnInit()
{
//--- activer les messages sur le mouvement de la souris dans la fenêtre du graphique
ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,1) ;
//--- La mise à jour forcée des propriétés du graphique assure la préparation au traitement des événements
ChartRedraw()
;
}


 
Комбинатор:

Les objets temporaires ne sont généralement pas supprimés immédiatement, mais à la fin du contexte. En C++, si je ne me trompe pas, c'est précisé dans la norme.

Si vous voulez une suppression rapide, contrôlez le contexte.

Il faudra vraiment faire l'opérateur =

Merci ! SD a déclaré qu'elle prévoyait de corriger le comportement actuel, mais aucun calendrier n'a été fixé.