Давайте вместе разберем CObject ? - страница 3

 
Vladimir Pastushak:

Как сделать что бы t видел только func_2 и func_4 ?

Объявить функции, которые ты не хочешь , чтобы были доступны наследнику - в секции private. Хотя, правильность не давать доступ к функциям наследника - на мой взгляд, под вопросом. Лично я считаю, что наследнику всегда должен быть доступен весь класс - то есть, все данные и функции класса должны быть либо в секции public, либо в секции protected.

И насчет protected-наследования - вопрос также спорный.

Лично я предпочитаю наследование делать всегда public, а те вещи, которые не должны наследоваться - выносить в отдельный protected член класса (композиция, а не наследование)

И вобще, насчет области видимости - я предпочитаю интерфейсы - специальные классы с полностью виртуальными функциями (приравненными нулю).

Например, у меня есть интерфейс :


class CTradePositionI: public CMyObject
{
public:
   void CTradePositionI() {SetMyObjectType(MOT_TRADE_POSITION_I); }; // Устанавливаем идентификатор класса
   virtual void ~CTradePositionI() {};
   
   virtual uint GetTotalComponents() = 0;  // Получение общего числа компонент
   virtual CTradePosComponentI* GetComponent(uint uiComponentIdx) = 0; // Получение отдельной компоненты
};

Это - переносимый платформонезависимый интерфейс.

В шаблоне советника есть функция:

CTradePositionI* GetTradePosition(symbol,magic);

Которая при вызове возвращает именно этот интерфейс. Как видишь - все функции в интерфейсе нулевые, и к реализации пользователь не имеет никакого доступа. Нужна позиция - запрашиваешь этот интерфейс, и у него можешь выяснить, сколько компонент в позции, и запросить каждую компоненту в отдельности. Причем, для МТ4 - реально это класс CMT4PositionInfo (на прошлой странице я его приводил), а для МТ5 - совсем другой класс CMT5PositionInfo. Однако, оба этих класса - поддерживают данный виртуальный интерфейс. И пользователю не надо знать, на какой платформе он работает.  Функция вернет указатель на один из этих классов, в виде виртуального интерфейса, общего для них обоих.

Причем, по запросу компоненты - опять-таки, получаешь не конкретный ордер МТ4 или позицию МТ5 (реально - именно они хранятся в объекте позиции), а виртуальный интерфейс:

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
   
   // Основной интерфейс
   virtual long               GetTPCTicket() = 0;
   virtual long               GetTPCMagic() = 0;
   virtual ECurrencySymbol    GetTPCSymbol() = 0;
   virtual ENUM_POSITION_TYPE GetTPCType() = 0;
   virtual datetime           GetTPCOpenTime() = 0;
   virtual double             GetTPCVolume() = 0;
   virtual double             GetTPCOpenPrice() = 0;
   virtual double             GetTPCStopLoss() = 0;
   virtual double             GetTPCTakeProfit() = 0;
   virtual string             GetTPCCommentary() = 0;
   
   virtual bool               IsTPCInUnloss() { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };
};

class CHistoryPosComponentI: public CTradePosComponentI
{
public:
   void CHistoryPosComponentI() {    SetMyObjectType(MOT_HISTORYPOS_COMPONENT_I); };
   virtual void ~CHistoryPosComponentI() {};

   virtual datetime           GetTPCCloseTime() = 0;
   virtual double             GetTPCClosePrice() = 0;
   virtual double             GetTPCProfit() = 0;  // Возвращает профит по исторической позиции
};

Тут же есть интерфейс и исторической позиции - он тоже виртуальный, есть класс истории, который возвращает компоненты позиции в истории - в виде вот этого виртуального интерфейса.

На мой взгляд, наиболее ценным в этом подходе является то, что блоки программы - полностью отделены друг от друга, взаимодействие осуществляется исключительно через виртуальные интерфейсы, при изменениях и модификациях - прочие блоки совершенно не затрагиваются. Более того, реально существует четыре различных класса - ордер МТ4, исторический ордер МТ4, позиция МТ5, историческая позиция МТ5 - которые существенно различаются по структуре (например, в позиции МТ5 есть списки ордеров и сделок, а в ордере МТ4 этого нет), но для пользователя - это совершенно один и тот же интерфейс.  Собственно, я вижу в этом как раз идею ООП  - инкапсуляцию.

Кроме того - все реальные классы, являющиеся наследниками CTradePosComponentI, одновременно являются и наследниками CMyObject, и CObject - как раз используется CArrayObj, чтобы сложить все ордера МТ4 или позиции МТ5 в массив, и иметь при этом сортировку, в зависимости от ключа. 

 
George Merts:

И вобще, насчет области видимости - я предпочитаю интерфейсы - специальные классы с полностью виртуальными функциями (приравненными нулю).

Например, у меня есть интерфейс :


Сейчас есть интерфейсы, не помню, когда их ввели

//--- базовый интерфейс для описания животных 
interface IAnimal 
  { 
//--- методы интерфейса по умолчанию имеют public-доступ 
   void Sound();  // звук, который издает животное 
  }; 
//+------------------------------------------------------------------+ 
//|  класс CCat наследуется от интерфейса IAnimal                    | 
//+------------------------------------------------------------------+ 
class CCat : public IAnimal 
  { 
public: 
                     CCat() { Print("Cat was born"); } 
                    ~CCat() { Print("Cat is dead");  } 
   //--- реализуем метод Sound интерфейса IAnimal 
   void Sound(){ Print("meou"); } 
  }; 

***

 
Alexey Volchanskiy:

Сейчас есть интерфейсы, не помню, когда их ввели

***

традиционно "немного через зад" при помощи костылей и подпорок - "интерфейс это такой сугубо виртуальный класс без полей, конструкторов и деструкторов" :-)

а по логике вещей должна быть ключевое слово implements и допустимо множественные (класс может имплементировать множество интерфейсов) и реализацию интерфейсов нельзя путать с наследованием, миксами и делегатами..

 
Maxim Kuznetsov:

традиционно "немного через зад" при помощи костылей и подпорок - "интерфейс это такой сугубо виртуальный класс без полей, конструкторов и деструкторов" :-)

а по логике вещей должна быть ключевое слово implements и допустимо множественные (класс может имплементировать множество интерфейсов) и реализацию интерфейсов нельзя путать с наследованием, миксами и делегатами..


В этом и есть вся суть, минимум для наследования. А от делегатов а-ля C# я бы не отказался :))

 
George Merts:

Считаю класс CObject - очень даже правильным и нужным классом.

...

   CObject          *m_prev;
   CObject          *m_next;

Вот - это как раз указатели, которые используются классом CList - объекты "цепляются" один за другой, что позволяет очень просто организовать списки любых объектов, проснаследованных от CObject.

Соответственно, в классе есть public-методы получения предыдущего и следующего объекта в списке.

Имхо, это не правильная концепция. Если пользователь захочет поместить один объект одновременно в несколько списков (почему бы и нет?), то выйдет облом.  Поэтому для списков нужны специальные объекты-контейнеры, в которые помещается наш указатель, и все списочные манипуляции проводятся именно с этими контейнерами. Т.е. вот в них и должны содержаться указатели *prev и *next.   А тут намешали всё в кучу, и теперь каждый объект вынужден хранить 2 лишних указателя (16 байт), даже если они никогда не понадобятся.

Поэтому я лично никогда не пользуюсь этим нелепым CObject.  У меня своя реализация базового объекта, там никаких указателей, но зато есть счётчик ссылок - вот это реально нужная вещь.  Соответственно операции присвоения и удаления указателей делаю через специальные функции.

 
Maxim Kuznetsov:

традиционно "немного через зад" при помощи костылей и подпорок - "интерфейс это такой сугубо виртуальный класс без полей, конструкторов и деструкторов" :-)

а по логике вещей должна быть ключевое слово implements и допустимо множественные (класс может имплементировать множество интерфейсов) и реализацию интерфейсов нельзя путать с наследованием, миксами и делегатами..

Множественное наследование почему-то не в почете. Хотя, мне лично оно кажется очень даже полезной штукой.

Никаких "костылей и подпорок" я в этом определении не вижу. Все верно - интерфейс - это сугубо виртуальный класс. Главная его задача - определить протокол взаимодействия. С чем он вполне себе справляется, на мой взгляд.

 
Alexey Volchanskiy:

Сейчас есть интерфейсы, не помню, когда их ввели

Да, я про них знаю, все верно, интерфейс - это как раз то, что надо, но придется изменять отлаженный код - кто его знает, какие подводные камни при этом появятся. А плюсов при этом немного. Я и так использую чисто виртуальные классы как интерфейсы. Плюс - у меня есть специальное поле "идентификатор класса", чтобы при преобразовании указателя - имелась бы возможность выяснить, на какой именно реальный объект он указывает.

P.S. Ага ! Сейчас попробовал заменить один из своих виртуальных классов на интерфейс - не вышло, как раз из-за CObject'а ! Если класс наследуется от интерфейса - то в нем нет функциональности CObject'а и  CMyObject'а - а мне она нужна. С интерфейсами - нужно множественное наследование, тогда их использование оправданно. А так - получается, я не могу добавить к какому-то классу реализацию некоего интерфейса.
 
Alexey Navoykov:

Имхо, это не правильная концепция. Если пользователь захочет поместить один объект одновременно в несколько списков (почему бы и нет?), то выйдет облом.  

Согласен, для множественных списков данный класс не годится.  Но, у меня пока еще ни разу не возникала необходимость один и тот же объект использовать в разных списках. Хотя, действительно, такая необходимость может возникнуть.

Поэтому я лично никогда не пользуюсь этим нелепым CObject.  У меня своя реализация базового объекта, там никаких указателей, но зато есть счётчик ссылок - вот это реально нужная вещь.  Соответственно операции присвоения и удаления указателей делаю через специальные функции.

Ну, он не более нелепый, чем Объект со счетчиком ссылок. И тот, и другой - вполне себе полезен в своем случае.

К смарт-поинтерам у меня неоднозначное отношение - мне кажется, их использование оправдано весьма нечасто. А вот код они усложняют очень заметно. Поэтому, я сперва было хотел себе такой класс сделать, но, идея на уровне идеи и осталась...  

 
George Merts:

Множественное наследование почему-то не в почете. Хотя, мне лично оно кажется очень даже полезной штукой.

Никаких "костылей и подпорок" я в этом определении не вижу. Все верно - интерфейс - это сугубо виртуальный класс. Главная его задача - определить протокол взаимодействия. С чем он вполне себе справляется, на мой взгляд.


Для использования множественного нужны грамотные программисты, которых тут мало. Потому что можно такого наворотить..

 

Я не понимаю этих вещей

private:
   CObject          *m_prev;               // previous item of list
   CObject          *m_next;               // next item of list

public:
   CObject          *Prev(void)                                    const { return(m_prev); }
   void              Prev(CObject *node)                                 { m_prev=node;    }
   CObject          *Next(void)                                    const { return(m_next); }
   void              Next(CObject *node)                                 { m_next=node;    }


Как работают эти ссылки ? Зачем они ? Может кто нить на пальцах объяснить или показать пример ?

Причина обращения: