Questions on OOP (Object Oriented Programming) - page 6

 
C-4:
Pointers are indispensable for complex object conversions where dynamic type identification is needed.

Vasily, an example, please!

I only know of one case where you need to allocate memory and need a pointer to it.

I'm sure you can almost always do without it. It is desirable not to use manual memory management. There is always a standard library that already handles these issues.

 
Integer:


One principle applies everywhere: everything must be done as simply as possible. You don't have to go into the thicket just for the sake of being in the thicket. If a problem can be solved simply, it should be solved simply.


Exactly. And this is where it is important to design the class correctly. And to do this, it is important to predict what the next classes (descendants) will be.

Usually, the base class should have a minimum functionality, but it is desirable to create a lot of virtual methods that would set the vector of class usage.

In the case of MQL - probably the opposite - more functionality in the base class, like in VOLDEMAR. But without fanaticism.


There are trade symbol properties (POINT, STOPLEVEL etc.). MODE_XXX for MarketInfo() ). We'd better shove them into cSymbol class, for example.


There are order properties (opening price, type, lot, etc.). OrderXXX() ). It would be better to put them into a separate cOrder class, for example.

If we remember that the order can be closed in parts, then we should also assign the BaseLot field to the class (in order to know which part of the lot has already been closed), etc.


There is (for me) a schedule - specified periods of time (by day) when we can open and close orders, periods when we only close (trailing if we have already breakeven) and the period when we do not work at all and then close all open and delete unopened orders.

Remember about gaps and that in the last hour of trading in many (if not all) brokerage companies may change trading conditions and that many brokerage companies have a break in trading metals every trading day.


And finally, there is an EA - a certain algorithm that works with a certain symbol, on a certain schedule, with a certain list of "its" orders and with its own data for calculating.

We can create a cExpert class, which will contain an object of cSymbol class and an array of objects of cOrder class. This basic cExpert will contain functions updating the properties of cOrder objects, functions of order handling, error handling, statistics, etc.

I personally find it useful the functions of lot calculation in %% of AccountFreeMargin(), generation of unique wizard for each order (easier to put a reverse order to a specific order), function of placing a reverse order, etc.

And this is where cExpert could descend from, with unique sets of additional data and functions which would decide what to do in the market (open/company/close orders).

I.e. implement trading strategies. And it will be what we call an "XXX strategy EA".


But ALL of the base functionality will be in the base cExpert class. The child classes will contain algorithms of trading strategies.

Well, maybe someone will add some advanced error handling or advanced statistics on trading (if there is no source code and it cannot be crammed into basic cExpert).

Please note that we are taking our time and creating a class that will simplify writing EAs. In fact, it is an Expert Advisor template. Structurally complete and unbrushed (all the necessary data in one place with the code).


As for the alleged "redundancy" of wrapper functions over openorders(), I personally just support such wrappers (especially - if they have extra functionality, and not just call the basic function).

For example, if in a call of wrapper SL and TP are specified in points and we have to convert them to absolute digits, and moreover add or subtract depending on order type (though in this case too such conversions can be placed in operorders()).

Personally I find it easier to understand the code calling BuyStop(...) and check once that BuyStop() does everything correctly than parsing OrderSend() parameters for correctness of parameters every time.

PS: It was a lot of work, including on weekends. Thanks to Pavlick and mql5 for the example codes.

Have a look at those examples and think, is it really necessary to create a hierarchy of classes in the area of programming tasks, which are written in MQL?

Do you really need a whole family of classes based on a basic one (all those "triangles", "squares" etc.)?

And once again Integer's golden words: Don't go into the thicket just to be in the thicket.

Oh. Either I didn't notice, or the documentation section with examples appeared recently.

 
Zhunko:

I only know of one case where you need to allocate memory and need a pointer to it.

I'm sure you can almost always do without it. It is advisable not to use manual memory management. There is always a standard library that has already solved these issues.


Here's probably what it's all about.


Let's assume there is a cFather class, it has int GetData() method which returns 3. And method PrintData(), which outputs what it gets from GetData().

There is its descendant cChild that has overridden GetData() which now returns 5.

If we declare an object of cFather type TestObject, then TestObject.GetData() will always return 3.

If we declare cFather* TestObject=new cChild1, then TestObject.GetData() will return 5, even though it seems to be cFather.

It is needed so that code written now can call GetData() method on any descendant of cFather class, even if it (descendant class) does not exist yet.

That is, if the cChild2 class then appears, with which GetData() will return 7, then after cFather* Test2=new cChild2 function Test2.PrintData() will start outputting 7.

If there is some function that expects a "reference to cFather class object" parameter and uses it GetData(), it will get correct data for any cFather descendant.

Method binding occurs when new is called. If it's not referenced, then the binding will be hard, i.e. methods of the declared class will be called.

See here and here

 
EverAlex:

This seems to be what it's all about.

...

There is a "::" operator that allows you to access any method in the entire chain of base and derived classes without allocating memory or a pointer.
 
C-4:


Your class is 90% redundant. Only two functions do the main work, these are openorders and tip Why do you use Sel, Buy SelStop, etc., when in fact they all just call Openorders? Furthermore, the order type is passed as int, so it's not protected. Instead of int you'd better use either your own enumeration or the standard ENUM_ORDER_TYPE. And in general, you'd better never use magic numbers "1", "2" etc., only enumerations. This will prevent you from sending the left order value to the function. The Openorders function itself is too big. Obviously, it consists of two blocks, the block of making a deal and the block of checking conditions. Each one of them should be a separate private function.

That's a good start, but we still have a lot to learn. The tip function would be better rewritten as follows:

It's convenient for me to visually see what type of order I'm placing when calling the method...

About the comparison, describe in detail why it is not recommended to compare double with 0 ?

 
C-4:
Pointers are indispensable for complex object conversions where dynamic type identification is needed.

The presence of dynamic type identification usually indicates the crutch architecture of a project.
 
if a class is declared globally in an EA (Class c;) will the states of internal class objects changed on one tick be saved when the next tick arrives ?
 
EverAlex:

This seems to be what it's about.


Assume, there is the cFather class; it has int GetData() method which returns 3. And method PrintData(), which outputs what it gets from GetData().

There is its descendant cChild, which has overridden GetData(), which now returns 5.

If we declare an object of cFather type TestObject, then TestObject.GetData() will always return 3.

If we declare cFather* TestObject=new cChild1, then TestObject.GetData() will return 5, even though it seems to be cFather.

It is needed so that code written now can call GetData() method on any descendant of cFather class, even if it (descendant class) does not exist yet.

That is, if the cChild2 class then appears, with which GetData() will return 7, then after cFather* Test2=new cChild2 function Test2.PrintData() will start outputting 7.

If there is some function that expects a "reference to cFather class object" parameter and uses it GetData(), it will get correct data for any cFather descendant.

Method binding occurs when new is called. If it's not referenced, then the binding will be hard, i.e. methods of the declared class will be called.

See here and here

class cFather
{
public:
    int GetData() {return 3;}
};

class cChild : public cFather
{
public:
    int GetData() {return 5;}
};
    
int f(cFather *p) {return p->GetData();}
    
int main()
{
    cChild obj;
    f(&obj);                // вернет 3
    obj.cFather::GetData(); // вернет 3
    
    return 0;
}
 
Pavlick:


Perhaps I wrote the example for nothing. It doesn't work in MKL:

cChild obj;
f(&obj);                // вернет 3
obj.cFather::GetData(); // вернет 3

H.k., not pointers, but laughter on a stick.

P.S: write in dlls, there is a chance to learn a normal language.

 
Pavlick:


Perhaps I wrote the example for nothing. It doesn't work in MKL:

H.k., not pointers, but laughter on a stick.

P.S: write in dlls, there is a chance to learn a normal language.


class cFather
  {
public:
   int GetData() {return 3;}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class cChild : public cFather
  {
public:
   int GetData() {return 5;}
  };

int f(cFather *p) {return p.GetData();}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnStart()
  {
   cChild obj,*ptr=GetPointer(obj);
   f(ptr);                     // вернет 3
   ((cFather *)ptr).GetData(); // вернет 3

   return 0;
  }