Assignment Operator Overload (operator=)

 

Hi,


I am trying to overload the Assignment Operator as this demonstrates, but regardless of what I have tried, my overload in my class is completely ignored.

Here is the Object class that I have defined...

class Object
{
    public:
        Object();
        ~Object();

        int value;
        void operator=(const Object *that);
};

Object::Object(){}
Object::~Object(){}

void Object::operator=(const Object *that)	//I have also tried ...::operator=(const Object &that), with the same result
{
    this.value = that.value;
}

This is the code I use to test the overload:

int OnInit()
{
    EventSetTimer(60);

    Object* obj1 = new Object();
    obj1.value = 45;
    Print("Value of Object ", obj1, " = ", obj1.value);

    Object* obj2 = obj1;
    Print("Value of Object(2) ", obj2, " = ", obj2.value);
}

Basically I am trying to intercept when an Object variable becomes a reference to another Object (reference counting), (such as Object* obj2 = obj1).  I need to intercept that assignment, but the operator code I have written is completely ignored.

The overload works perfectly fine if I use any other type, but not for Object itself.
i.e.

void Object::operator=(int i) {} //... this works fine
void Object::operator=(Object *that) {} //... this is completely ignored

As I was expecting, obj2 points to obj1 (and I am not trying to change that behaviour), but I have some code to run when an object is assigned to, but as I said before, the operator overload is ignored.  Am I missing something?

Documentation on MQL5: Language Basics / Functions / Operation Overloading
Documentation on MQL5: Language Basics / Functions / Operation Overloading
  • www.mql5.com
Language Basics / Functions / Operation Overloading - Reference on algorithmic/automated trading language for MetaTrader 5
 
RoboPogo:

Hi,


I am trying to overload the Assignment Operator as this demonstrates, but regardless of what I have tried, my overload in my class is completely ignored.

Here is the Object class that I have defined...

This is the code I use to test the overload:

Basically I am trying to intercept when an Object variable becomes a reference to another Object (reference counting), (such as Object* obj2 = obj1).  I need to intercept that assignment, but the operator code I have written is completely ignored.

The overload works perfectly fine if I use any other type, but not for Object itself.
i.e.

As I was expecting, obj2 points to obj1 (and I am not trying to change that behaviour), but I have some code to run when an object is assigned to, but as I said before, the operator overload is ignored.  Am I missing something?

This code is working as expected :

2017.07.25 11:15:34.726 212002 EURUSD,M5: Value of Object 1 = 45
2017.07.25 11:15:34.731 212002 EURUSD,M5: Value of Object(2) 1 = 45
Though you would have the same result without the overloading. Not sure what you are expecting ?
 
Alain Verleyen:

This code is working as expected :

Though you would have the same result without the overloading. Not sure what you are expecting ?


As I said, I am trying to 'intercept' the assignment so that I can execute some code (that relies on the assignment)...

example:

Object::operator=(const Object &that)
{
    OnReference(that);
    return that;
}

I guess an easier way to describe it would be like an event listener, where any time a reference to an object is made, a function is called.  Similar to the OnTick() event in the EA, but rather than firing every tick, it fires every time a reference to an Object is created, such as:

class SomeClass
{
    private:
        Object* objHandle;

    public:
        SomeClass(Object &inobj);
};

SomeClass::SomeClass(Object &inobj)
{
    this.objHandle = inobj;  <---- A reference to an existing Object has been made, so call OnReference() like so:
                                   operator=(const Object &obj)
                                   {
                                       OnReference(obj);
                                       ....so on and so forth
                                   }
}

As I said, the problem I have is that the overload doesn't work, and OnReference() is not called.  I got the operator overload to work, however it, when I used it, it would only work on raw Object, not Object*, and the resulting Object had a different address to the Object I was referencing, further, did not even point to the object it was meant to reference.

 
RoboPogo:


As I said, I am trying to 'intercept' the assignment so that I can execute some code (that relies on the assignment)...

example:

I guess an easier way to describe it would be like an event listener, where any time a reference to an object is made, a function is called.  Similar to the OnTick() event in the EA, but rather than firing every tick, it fires every time a reference to an Object is created, such as:

As I said, the problem I have is that the overload doesn't work, and OnReference() is not called.  I got the operator overload to work, however it, when I used it, it would only work on raw Object, not Object*, and the resulting Object had a different address to the Object I was referencing, further, did not even point to the object it was meant to reference.


Additional:

I got the overload to work, however, like I said in my previous reply, it will work only on raw Object, and not Object*, and the resulting Object is not a pointer to, and does not at all point to the assigned Object instance.

//Definition
Object* operator= (Object* obj)
{
    OnReference(obj);    <---- Note: OnReference does nothing to obj itself, just pulls information from it
    return obj;
}

Object* obj1 = new Object();

Object* newObj = obj1;    <---- does not work, operator= not called
Object newObj2 = obj1;    <---- works, however newObj2 is not a reference to obj1, newObj2 does not point to obj1 at all, it has its own address, and 
                                does not even have the same data stored in it as obj1, almost like newObj2 was created with 'new Object()'
 
What you are talking about is assigment of pointers. They are not part of the class or object. All you can do, if you want to intercept operations with a pointer, is to create another class, say SafePointer, which will control access to a contained pointer.


#property strict

class Object
{
  public:
    int value;
    
    Object(){};
    Object(const int v): value(v){}
    Object(const Object &that)
    {
      this.value = that.value;
    }

    void operator=(const Object *that)
    {
      this.value = that.value;
    }
};

template<typename T>
class SafePointer
{
  private:
    const T *pointer;
    
  public:
    SafePointer(){}
    
    SafePointer(const T *obj)
    {
      pointer = obj;
    }
    
    void operator=(const T *that)
    {
      pointer = that;
    }
    
    const T *operator~()
    {
      return pointer;
    }
    
};

void OnStart()
{
  Object obj1(10);
  Object *obj2;
  Object obj3 = obj1; // copy constructor, not an assignment!
  SafePointer<Object> obj4;
  
  obj2 = &obj1;
  obj4 = &obj1;

  Print(&obj1, " ", obj2, " ", &obj3, " ", ~obj4);
}
 
Stanislav Korotky:
What you are talking about is assigment of pointers. They are not part of the class or object. All you can do, if you want to intercept operations with a pointer, is to create another class, say SafePointer, which will control access to a contained pointer.



This worked perfectly! Thank you!  You sir, are a life saver.

 
Stanislav Korotky:
What you are talking about is assigment of pointers. They are not part of the class or object. All you can do, if you want to intercept operations with a pointer, is to create another class, say SafePointer, which will control access to a contained pointer.


I'm on mobile so pardon my brevity... 

I'm not sure that OP fully understands the difference between pointers and references because OP is using a NULL pointer as if it had already been pointing to an object. You know that, but does op know that?

A different solution would be to use pointers properly, and overload the overloaded operator so you can pass the object by reference or pointer and it won't matter. See this example  

https://www.mql5.com/en/forum/212043


 
RoboPogo:

Am I missing something?


Hi OP, I'm back at a computer... Yes you are missing something. A NULL pointer cannot do anything until it points to an object. So asking a NULL pointer to call a method isn't ever going to work. Also, you should overload your overload by allowing to accept either the object as a ref or pointer param. Finally, if you absolutely must declare a NULL pointer and create an object to point it to (on the same line) then the easiest way is to have a class method that creates a new object of itself and then return the pointer to your newly declared pointer. Here are some examples

#property strict
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
class Object
{
public:
   int val;
   Object(){}
   Object(int v):val(v){}
   void Assign(Object *other){ this.val=other.val; } //main method for assign not necessary, but for demonstration / to make a point.
   void operator =(Object &other){ this = &other;  } //overload: accept object passing as ref, uses the other [overload =] by passing it the object ref to pointer
   void operator =(Object *other){ Assign(other);  } //make sure to step thru debugger to really see what's happening here
   Object* AssignNew(){ return new Object(val);    } 
};

void OnStart()
{
   // All valid
   Object a(10),b;
   b=a;
   Object *c = new Object();
   c=b;
   Object *d = c.AssignNew();
   Object obj = d;
   Print("Object obj = ",obj.val);
   //this
   a=d;
   //is the same as this
   a.Assign(d);
   
   //not valid
   //consider [operator =] is the same as [Object::Assign()]
   /*
   Object *e;     //NULL pointer
   e.Assign(d);  //critical error
   */
   delete c;
   delete d;

}
//+------------------------------------------------------------------+
 
nicholishen:

Hi OP, I'm back at a computer... Yes you are missing something. A NULL pointer cannot do anything until it points to an object. So asking a NULL pointer to call a method isn't ever going to work. Also, you should overload your overload by allowing to accept either the object as a ref or pointer param. Finally, if you absolutely must declare a NULL pointer and create an object to point it to (on the same line) then the easiest way is to have a class method that creates a new object of itself and then return the pointer to your newly declared pointer. Here are some examples

Your example is dangerous! Never do this assignment! I wonder why MQL5 does allow this at all without a compiler error.

 
Stanislav Korotky:

Your example is dangerous! Never do this assignment! I wonder why MQL5 does allow this at all without a compiler error.


How exactly is it dangerous? Are you talking about this line? 

Object* AssignNew(){ return new Object(val);    } 

It's only dangerous if you forget to delete it, but you'd have that same issue regardless of assignment since you're using pointers to dynamic objects to begin with. This does the same thing as your "Safepointer" example with less lines of code.

I wonder why MQL5 does allow this at all without a compiler error.

Because this isn't java... And you can do it in C++ so naturally mql would inherit the same behavior

 
Stanislav Korotky:

Your example is dangerous! Never do this assignment! I wonder why MQL5 does allow this at all without a compiler error.


Here is another example of the same style of assignment. Would you consider this "dangerous" as well?

#property strict
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayObj.mqh>

class MyIntArray : public CArrayInt
{
   public: bool operator += (const int element) { return Add(element);}
};
//+------------------------------------------------------------------+
class DynamicMatrix : public CArrayObj
{
   public:MyIntArray* operator[](const int index)
   {
      if(index < Total())
         return (MyIntArray*)At(index);
      MyIntArray *temp = NULL;
      while(index >= Total())
      {
         temp = new MyIntArray();
         Add(temp);
      }
      return temp;
   }
};
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
//---
   int c = 1;
   srand(GetTickCount());
   DynamicMatrix m;
   int iMax = rand()%10+1;
   for(int i=0;i<iMax;i++)
   {
      int jMax = rand()%10+1;
      for(int j=0;j<jMax;j++)
         m[i]+= c++;
   }    
   for(int i=0;i<m.Total();i++)
      for(int j=0;j<m[i].Total();j++)
         Print("[",i,"][",j,"] = ",m[i][j]);

}
//+------------------------------------------------------------------+