Return my custom class from a function (copy constructor not found).

ZetaTrader
181

I'd like to return my custom Candle class (inheriting from CObject) from a function.

The compiler will complain:

"object of 'Candle' cannot be returned, copy constructor 'Candle::Candle(const Candle &)' not found.

How can I implement this copy constructor if my class has the following constructor:

Candle(datetime date, double open, double high, double low, double, close);


Apart from this, working with my class is just fine, I just can't use it as a return value.


Thanks for your help!

nicholish en
2690
nicholish en  
ZetaTrader:

I'd like to return my custom Candle class (inheriting from CObject) from a function.

The compiler will complain:

"object of 'Candle' cannot be returned, copy constructor 'Candle::Candle(const Candle &)' not found.

How can I implement this copy constructor if my class has the following constructor:

Candle(datetime date, double open, double high, double low, double, close);


Apart from this, working with my class is just fine, I just can't use it as a return value.


Thanks for your help!


You have to use pointers. 

Candle *MyPointer() { return &this; }
ZetaTrader
181
ZetaTrader  
nicholishen:

You have to use pointers. 


Thank you for the answer!

But it's not clear to me where I would put that code, I guess inside my Candle class?


A function:


Candle returnCandle() {

   Candle *candle = new Candle(TimeCurrent(), 0, 0, 0, 0); //Just some fill-ins

   return candle;

}


still gives the same error.

nicholish en
2690
nicholish en  
ZetaTrader:

Thank you for the answer!

But it's not clear to me where I would put that code, I guess inside my Candle class?


A function:


Candle returnCandle() {

   Candle *candle = new Candle(TimeCurrent(), 0, 0, 0, 0); //Just some fill-ins

   return candle;

}


still gives the same error.


You need to post your code, the whole class and the context of which you are trying to get the return value. These snippets are useless, and nobody can help you unless you provide context.

ZetaTrader
181
ZetaTrader  
nicholishen:

You need to post your code, the whole class and the context of which you are trying to get the return value. These snippets are useless, and nobody can help you unless you provide context.


This is the basic setup for the Candle class:


#include <Arrays/ArrayObj.mqh>

class Candle: public CObject {
private:

public:
   int number;
   datetime date;
   double open;
   double high;
   double low;
   double close;

   Candle();

   Candle(int _number, datetime _date, double _open, double _high, double _low, double _close);

   ~Candle();

};


Candle::Candle() {}

Candle::Candle(int _number, datetime _date, double _open, double _high, double _low, double _close) {
   number = _number;
   date = _date;
   open = _open;
   high = _high;
   low = _low;
   close = _close;
}

Candle::~Candle() {}


This is an include file that contains the following function which should create a Candle for the M1 Bar with the daily high and return a pointer to that object:


#include "Candle.mqh"

Candle todaysHighestCandle() {
   Candle *candle = new Candle(0, iTime(Symbol(), PERIOD_M1, 0), iOpen(Symbol(), PERIOD_M1, 0), iHigh(Symbol(), PERIOD_M1, 0), iLow(Symbol(), PERIOD_M1, 0), iClose(Symbol(), PERIOD_M1, 0));
  
   for (int i = 1; i <= 840; i++) {
      if (TimeDay(iTime(Symbol(), PERIOD_M1, i)) != TimeDay(candle.date)) {
         // This is the last candle from the day before.
         break;
      }
     
      double thisCandleHigh = iHigh(Symbol(), PERIOD_M1, i);
     
      if (thisCandleHigh >= candle.high) {
         candle.high = thisCandleHigh;
         candle.date = iTime(Symbol(), PERIOD_M1, i);
         candle.low = iLow(Symbol(), PERIOD_M1, i);
         candle.open = iOpen(Symbol(), PERIOD_M1, i);
         candle.close = iClose(Symbol(), PERIOD_M1, i);
      }
   }
  
   return candle;
}

nicholish en
2690
nicholish en  
ZetaTrader:


This is the basic setup for the Candle class:


#include <Arrays/ArrayObj.mqh>

class Candle: public CObject {
private:

public:
   int number;
   datetime date;
   double open;
   double high;
   double low;
   double close;

   Candle();

   Candle(int _number, datetime _date, double _open, double _high, double _low, double _close);

   ~Candle();

};


Candle::Candle() {}

Candle::Candle(int _number, datetime _date, double _open, double _high, double _low, double _close) {
   number = _number;
   date = _date;
   open = _open;
   high = _high;
   low = _low;
   close = _close;
}

Candle::~Candle() {}


This is an include file that contains the following function which should create a Candle for the M1 Bar with the daily high and return a pointer to that object:


#include "Candle.mqh"

Candle todaysHighestCandle() {
   Candle *candle = new Candle(0, iTime(Symbol(), PERIOD_M1, 0), iOpen(Symbol(), PERIOD_M1, 0), iHigh(Symbol(), PERIOD_M1, 0), iLow(Symbol(), PERIOD_M1, 0), iClose(Symbol(), PERIOD_M1, 0));
  
   for (int i = 1; i <= 840; i++) {
      if (TimeDay(iTime(Symbol(), PERIOD_M1, i)) != TimeDay(candle.date)) {
         // This is the last candle from the day before.
         break;
      }
     
      double thisCandleHigh = iHigh(Symbol(), PERIOD_M1, i);
     
      if (thisCandleHigh >= candle.high) {
         candle.high = thisCandleHigh;
         candle.date = iTime(Symbol(), PERIOD_M1, i);
         candle.low = iLow(Symbol(), PERIOD_M1, i);
         candle.open = iOpen(Symbol(), PERIOD_M1, i);
         candle.close = iClose(Symbol(), PERIOD_M1, i);
      }
   }
  
   return candle;
}


#include "Candle.mqh"

Candle* todaysHighestCandle() {
   Candle *candle = new Candle(0, iTime(Symbol(), PERIOD_M1, 0), iOpen(Symbol(), PERIOD_M1, 0), iHigh(Symbol(), PERIOD_M1, 0), iLow(Symbol(), PERIOD_M1, 0), iClose(Symbol(), PERIOD_M1, 0));
   
   for (int i = 1; i <= 840; i++) {
      if (TimeDay(iTime(Symbol(), PERIOD_M1, i)) != TimeDay(candle.date)) {
         // This is the last candle from the day before.
         break;
      }
      
      double thisCandleHigh = iHigh(Symbol(), PERIOD_M1, i);
      
      if (thisCandleHigh >= candle.high) {
         candle.high = thisCandleHigh;
         candle.date = iTime(Symbol(), PERIOD_M1, i);
         candle.low = iLow(Symbol(), PERIOD_M1, i);
         candle.open = iOpen(Symbol(), PERIOD_M1, i);
         candle.close = iClose(Symbol(), PERIOD_M1, i);
      }
   }
   
   return candle;
}

This is just asking for a memory leak though. I wouldn't recommend this. Here would be my suggestion... 

class Candle : public CObject
{
   MqlRates          m_rate;
public:
            Candle(){}
            Candle(const MqlRates &rate) { Init(rate); }
   double   High()  { return m_rate.high; }
   double   Open()  { return m_rate.open; }
   double   Close() { return m_rate.close; }
   double   Low()   { return m_rate.low; }
   datetime Time()  { return m_rate.time; }
   
   void     Init(const MqlRates &rate) { this.m_rate = rate; }
};

class DailyHighCandle : public Candle
{
public:
   DailyHighCandle()
   {
      MqlRates r[];
      ArraySetAsSeries(r,true);
      MqlDateTime stop;
      TimeCurrent(stop);
      stop.hour=0;
      stop.min=0;
      stop.sec=0;
      int total = CopyRates(_Symbol,PERIOD_M1,TimeCurrent(),StructToTime(stop),r);
      int index=-1;
      double highest = DBL_MIN;
      for(int i=0;i<total;i++)
      {
         if(r[i].high > highest)
         {
            highest = r[i].high;
            index = i;
         }
      }
      Init(r[index]); 
   }     
};

void OnStart()
{
   DailyHighCandle high_candle;
   Print(high_candle.Time());
}

ZetaTrader
181
ZetaTrader  
nicholishen:

This is just asking for a memory leak though. I wouldn't recommend this. Here would be my suggestion... 


Thanks so much for typing all this out!

So, turns out, my initial problem could be fixed with the * behind the return value. I've tried it before...


Regarding your alternative solution, I've tried to follow through to understand everything correctly (learning MQL, some things are still weird to me).

I should probably make more use of MQLs standard data structures.

I've commented on every statement, if you would be so kind to have a quick look at the question, so I'm sure I understand correctly?

public:
   DailyHighCandle() {
      // Array that will hold the data of a yet undefined amount of candles.
      MqlRates r[];
      // Order of candle data should be backwards in time.
      ArraySetAsSeries(r, true);
  
      // Struct that holds time components in separate properties, not holding values yet.
      MqlDateTime stop;
      // TimeCurrent() returns the time of the last tick, what does it do with the MqlDateTime parameter?
      // Does it just paste the current datetime into 'stop'?
      TimeCurrent(stop);
      // the 3 intraday-relevant time properties get set to the lowest values (day's beginning),
      // the other ones should be held valid, as they indicate today, as we're looking for >today's< highest candle?.
      stop.hour = 0;
      stop.min = 0;
      stop.sec = 0;
     
      // Finds the amount of candles between now and today's first M1 candle.
      int total = CopyRates(Symbol() PERIOD_M1, TimeCurrent(), StructToTime(stop), r);
     
      // Why initialize with -1 and not just 0? Will this return an error when rates is empty?
      int index = -1;
      // Initialize with the lowest possible value, so that the first high will be higher.
      double highest = DBL_MIN;
  
      // Loops through the candle data and keeps track of the highest high and it's candle's index position to refer to later.
      for (int i = 0; i < total; i++) {
         if (r[i].high > highest) {

            highest = r[i].high;

            index = i;
         }
      }
     
      // This candle will be initialized with the data of the candle with the found high.
      Init(r[index]);
   }
  
  
   //~DailyHighCandle();
};

Besides, I have issue with importing the other candle file, I'll try to get that sorted. EDIT: Fixed

I've adapted it to give the day's lowest candle and it works fine, too. Is there a better choice than using DBL_MAX as the initial low value then?

Also, in the Candle class, is this part necessary? I think it works fine without it.

 CandleNew(const MqlRates &_rate) {
    Init(_rate);
 }

Thank you very much again, this is of great help!

btw how can I preserve color in code samples on here?


EDIT: If I may add, out of interest, why would my approach be prone to memory leaks?

BankRobbers
7
BankRobbers  
nicholishen:

You have to use pointers. 


there are NO pointers in MQL4, right?

nicholish en
2690
nicholish en  
BankRobbers:

there are NO pointers in MQL4, right?


Yes, there are pointers in MQL4 & 5, but only to classes (not std types) and they're accessed with '.' instead of '->'

nicholish en
2690
nicholish en  
ZetaTrader:

Thanks so much for typing all this out!

So, turns out, my initial problem could be fixed with the * behind the return value. I've tried it before...


Regarding your alternative solution, I've tried to follow through to understand everything correctly (learning MQL, some things are still weird to me).

I should probably make more use of MQLs standard data structures.

I've commented on every statement, if you would be so kind to have a quick look at the question, so I'm sure I understand correctly


EDIT: If I may add, out of interest, why would my approach be prone to memory leaks?

In order to preserve the site's syntax highlighting you have to do all your code editing in the SRC popup window before you hit insert. 
public:
   DailyHighCandle() { 
      // Array that will hold the data of a yet undefined amount of candles.
      MqlRates r[];
      // Order of candle data should be backwards in time.
      ArraySetAsSeries(r, true);
   
      // Struct that holds time components in separate properties, not holding values yet.
      MqlDateTime stop;
      // TimeCurrent() returns the time of the last tick, what does it do with the MqlDateTime parameter?
// it creates a struct with the current time
      // Does it just paste the current datetime into 'stop'?
      TimeCurrent(stop);
      // the 3 intraday-relevant time properties get set to the lowest values (day's beginning), 
      // the other ones should be held valid, as they indicate today, as we're looking for >today's< highest candle?.
// correct
      stop.hour = 0;
      stop.min = 0;
      stop.sec = 0;
      
      // Finds the amount of candles between now and today's first M1 candle.
      int total = CopyRates(Symbol() PERIOD_M1, TimeCurrent(), StructToTime(stop), r);
      
      // Why initialize with -1 and not just 0? Will this return an error when rates is empty?
      int index = -1;
// just a habit so the program intentionally throws a fatal error during debugging.
      // Initialize with the lowest possible value, so that the first high will be higher.
      double highest = DBL_MIN;
   
      // Loops through the candle data and keeps track of the highest high and it's candle's index position to refer to later.
      for (int i = 0; i < total; i++) {
         if (r[i].high > highest) {

            highest = r[i].high;

            index = i;
         }
      }
      
      // This candle will be initialized with the data of the candle with the found high.
      Init(r[index]);
   }
   
   
   //~DailyHighCandle();
};

You're asking for a memory leak anytime you instantiate a dynamic object from a temporary scope (ie. function) and start passing it elsewhere without any memory management. If you aren't properly managing that memory then you're going to get a leak, and on the surface you had zero memory management implemented. The object collection classes in the std lib have memory management built in so here are some examples of how you can safely create dynamic objects and pass them out. (Use debugger to step into so you know what's going on behind the scenes)

#include <Arrays\ArrayObj.mqh>

class Candle : public CObject
{
protected:
   MqlRates          m_rate;
public:
            Candle(){}
            Candle(const Candle &other)   { this.m_rate = other.m_rate;}
            Candle(const MqlRates &rate)  { this.m_rate = rate;   }
            
    
   double   High()  const { return m_rate.high; }
   double   Open()  const { return m_rate.open; }
   double   Close() const { return m_rate.close; }
   double   Low()   const { return m_rate.low; }
   datetime Time()  const { return m_rate.time; }
   void     CopyIn(Candle &other)   { this.m_rate = other.m_rate; }
   void     CopyOut(Candle &other) const  { other.m_rate = this.m_rate; }
   void     Init(const MqlRates &rate){ this.m_rate = rate; }
protected:
   int      CopyDailyRates(MqlRates &rates[])
   {
      MqlDateTime stop;
      TimeCurrent(stop);
      stop.hour=0;
      stop.min=0;
      stop.sec=0;
      return CopyRates(_Symbol,PERIOD_M1,TimeCurrent(),StructToTime(stop),rates);
   }
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class DailyMaxCandle : public Candle
{
public:
   DailyMaxCandle()
   {
      MqlRates rates[];
      int total = CopyDailyRates(rates);
      if(total <=0)
         return;
      double highest = DBL_MIN;
      int index=0;
      for(int i=0;i<total;i++)
      {
         if(rates[i].high > highest)
         {
            highest = rates[i].high;
            index = i;
         }
      }
      Init(rates[index]); 
   }     
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class DailyMinCandle : public Candle
{
public:
   DailyMinCandle()
   {  
      MqlRates rates[];
      int total = CopyDailyRates(rates);
      if(total <=0)
         return;
      double lowest = DBL_MAX;
      int index=0;
      for(int i=0;i<total;i++)
      {
         if(rates[i].low < lowest)
         {
            lowest = rates[i].low;
            index = i;
         }
      }
      Init(rates[index]); 
   }     
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CandleVector : public CArrayObj
{
protected:
public:
   Candle* operator[](const int i)const{ return At(i);}
   void Refresh()
   {
      Clear();
      MqlRates r[];
      ArraySetAsSeries(r,true);
      int total = CopyRates(_Symbol,_Period,0,Bars,r);
      int count = 0;
      for(int i=0;i<total;i++)
         Add(new Candle(r[i]));
   }
   Candle* Min()
   {
      Candle *min=NULL;
      double lowest = DBL_MAX;
      for(int i=0;i<Total();i++)
      {
         if(this[i].Low() < lowest)
         {
            min = this[i];
            lowest = min.Low();
         }
      }  
      return min;
   }
   Candle* Max()
   {
      Candle *max=NULL;
      double highest = DBL_MIN;
      for(int i=0;i<Total();i++)
      {
         if(this[i].High() > highest)
         {
            max = this[i];
            highest = max.High();
         }
      }  
      return max;
   }
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

void OnStart()
{
   const DailyMaxCandle high_candle;
   const DailyMinCandle low_candle;
   Print(high_candle.Time());
   
   Candle candle2 = high_candle; //using copy constructor;
   Candle candle3;
   candle2.CopyOut(candle3);
   
   CandleVector vect;
   vect.Refresh();
   
   Candle *oldest = vect[vect.Total()-1];
   Candle *newest = vect[0];
   Candle *highest= vect.Max();
   Candle *lowest = vect.Min();
   Print("Oldest bar = ",oldest.Time()," | Recent bar = ",newest.Time());
// or get the same results -->
   Print("Oldest bar = ",vect[vect.Total()-1].Time()," | Recent bar = ",vect[0].Time());
   
}
William Roeder
25873
William Roeder  
ZetaTraderThe compiler will complain:

"object of 'Candle' cannot be returned, copy constructor 'Candle::Candle(const Candle &)' not found.

How can I implement this copy constructor if my class has the following constructor:

Candle(datetime date, double open, double high, double low, double, close);

Using pointers just gets around the OP's post. If you want a copy constructor, make one.
Candle::Candle(int _number, datetime _date, double _open, double _high, double _low, double _close) { // Ctor
   number = _number;
   date = _date;
   open = _open;
   high = _high;
   low = _low;
   close = _close;
}
Candle::Candle(const Candle& that) { // Copy
   this.number = that.number;
   this.date = that.date;
   this.open = that.open;
   this.high = that.high;
   this.low = that.low;
   this.close = that.close;
}
The "this." is optional.