Adviser's project - page 5

 

George Merts
Стандартный CObject - это "объект списка или сортированного массива". CMyObject - это CObject, имеющий определенный тип, и содержащий некоторое значение, данное при его создании. Этот объект мне понадобился всвязи с повсеместным приведением объектов к базовому абстрактному классу - чтобы понимать по указателю, на какой именно объект "на самом деле" указывает. Тип CMyObject - устанавливается как раз той самой функцией SetMyObjectType(). Эта функция в обязательном порядке вызывается в конструкторах любых наследников от CMyObject, чтобы назначить идентификатор класса, к которому принадлежит создаваемый объект.

Your constructor is open and non-parameterised:

class CTradeHistoryI: public CMyObject
{
public:
   void CTradeHistoryI() {    SetMyObjectType(MOT_TRADE_HISTORY_I); };
}

This means that an inheritor may or may not set its own type. So if you forget to call the SetMyObjectType method inside the descendant, you're screwed.

What you can do?

1. You should close the constructor from outside creation:

class CTradeHistoryI: public CMyObject
{
protected:
   void CTradeHistoryI() {    SetMyObjectType(MOT_TRADE_HISTORY_I); };
}

In this case only CTradeHistoryI descendant will be able to create an instance, which means that there will be no need in the MOT_TRADE_HISTORY_I type. Note that the MOT_TRADE_HISTORY_I type never exists in reality, because CTradeHistoryI is an interface, and there can be no instance of this type. I.e. by closing the constructor we solve the contradiction - description of the type that doesn't exist.

2. Let's set the explicit typing requirement at the moment of instance creation:

enum ENUM_OBJ_TYPE
{
   OBJ_HIST_DEALS_LIST,
   OBJ_HIST_ORDERS_LIST
   ...
};

class CTradeHistoryI: public CMyObject
{
private:
   ENUM_OBJ_TYPE   m_obj_type;
protected:
   CTradeHistoryI(ENUM_OBJ_TYPE obj_type)
   {
      m_obj_type = obj_type;
   }
public:
   ENUM_OBJ_TYPE ObjectType(void) const
   {
      return m_obj_type;
   }
};

class CTradeHistory : public CTradeHistoryI
{
public:
   CTradeHistory(void);
};

CTradeHistory::CTradeHistory(void) : CTradeHistory(OBJ_HIST_DEALS_LIST)
{
}

That's it, now no descendant can create itself without explicit type specification.

 
Vasiliy Sokolov:

Why re-invent the wheel in the form of CMyObject, when there is a standard CObject that everyone understands?

I don't know what good you found in MQL CObject. Two unnecessary pointers crammed into each object = 16 bytes lost + unnecessary overhead on memory allocation and initialization of these pointers. OOP in itself slows down performance, and if you add such useless solutions...
 
Alexey Navoykov:
I don't know what good you have found in MQL's CObject. Two unnecessary pointers crammed into each object = 16 bytes wasted + unnecessary overhead on memory allocation and initialization of these pointers. OOP in itself slows down performance, and if you add such useless solutions...

Learn OOP first, then we'll discuss it. Initialization of m_prev, m_next does not happen in general case.

 
Vasiliy Sokolov:

Learn OOP first, then we'll discuss it. Initialization of m_prev, m_next in general does not happen if anything.

Only after you.

And so for the sake of information, initialization of pointers in MQL is always performed.

 
Vasiliy Sokolov:

Your constructor is open and non-parameterised:

This means that an inheritor may or may not set its own type. So if you forget to call the SetMyObjectType method inside the descendant, you're screwed.

What can you do?

...

Yes, indeed in some cases I forget to specify SetMyObjectType() - and objects are created with CMyObject type by default.

And the solution method is a good one.

Suggestions are accepted.

I don't like the protected constructor though.

 
George Merts:

Yes, indeed, in some cases I forget to specify SetMyObjectType() - and objects are created with CMyObject type by default.

And the solution method is a good one.

Suggestions accepted.

Although I don't like protective constructor.

Protected constructor complicates life only for those classes which are directly inherited from parent with such constructor. But on the user level, you can create derived objects without hesitation and freely.

Therefore, the inconvenience should not be observed.

And there are no other options in MQL, because there is no type checking system, or rather, it exists but in an underdeveloped form, so, we have to protect the type by hiding a constructor.

 
Alexey Navoykov:
I don't know what good you've found in MQL's CObject. Two unnecessary pointers crammed into each object = 16 bytes lost + unnecessary overhead on memory allocation and initialization of these pointers. OOP in itself slows down performance, and when you add such useless solutions to it...

Why "useless" ? The overhead is, in my opinion, very small, and it's obviously worth the convenience that CObject gives when organizing lists and sorted arrays.

If we need to chase microseconds and bytes, then of course, it makes sense to think whether these very "lost bytes" don't account for too many resources. But as my practice shows, convenience of code writing and maintenance is much more important.

By the way, I was recently involved in code optimization - my Expert Advisors were somehow too slow in the tester. The slowest function was in data refresh. So I tried to decrease calling this refresh in every way. But, recently it became possible to profile code in the tester. And to my great surprise, I saw that most of my Expert Advisor's time is spent on the function of asking the terminal state (which I didn't need (almost)) (it was performed at every refresh, and it was just done "at the same time" with the rest). And most of all - wasted on the function requesting the free memory. Slight change in the code, so that the terminal state is queried only once at startup and then at refreshes - only when explicitly pointed out - speeded up testing by more than three times !

So the "unnecessary overhead" may be lost not at all where we're looking for it.

 
George Merts:

So the 'unnecessary overhead' may not be lost at all where we are looking for it.

Don't argue with him. His only goal is to rant. You will never prove anything to people like him. The best strategy for dealing with such characters is total ignoring.
 
George Merts:

Why "useless" ? The overhead is, in my opinion, very small, and it's clearly worth the convenience that CObject provides in organising lists and sorted arrays.

These "conveniences" are implemented over the top. What kind of list changes something in your objects? In essence, it turns out that you can't put constant objects into a list. Or imagine your object in such a list, you send it to a function, which also puts it in its list, and then you get your object back with changed prev and next, and the game begins ...

In civilized world the lists are implemented through auxiliary Node objects, which store the required pointers. And touching the user objects themselves is nonsense. So it's not just the overhead, but the fundamental incorrectness of it all. The developers just cobbled together something fast, and you are happy as if it should be so.

 
Alexey Navoykov:

These 'conveniences' are implemented through one place. What kind of list is it that changes something about your objects? In essence, you can't put constant objects into a list. Or imagine the situation, when you send your object in such a list to a function, which also puts it in its list, and then you get your object back with changed prev and next, and the game begins ...

In civilized world the lists are implemented through auxiliary Node objects, which store the required pointers. And touching the user objects themselves is nonsense. So it's not just the overhead, but the fundamental incorrectness of it all. The developers just cobbled together something at short notice and you are happy as if it must be so.

Well, yes, you can't put constant objects into a list.

However, I constantly use CObject functionality and none of my critics suggested anything even similar to objects of arrays and lists of the Standard Library.

"The way things should be done" is everyone's yelling. But to suggest something, all of a sudden, there is nothing.

Even those participants who actually offer different software solutions, do not offer a replacement for the CObject - more often do not use it at all, less often use its functionality, not paying attention to "implementation in one place", which means that the implementation is quite good.

If it were bad, they would have offered a replacement long ago.