MQL4参考 语言基础 数据类型 架构和类 以及界面

Structures, Classes and Interfaces

Structures

A structure is a set of elements of any type (except for the void type). Thus, the structure combines logically related data of different types.

Structure Declaration

The structure data type is determined by the following description:

struct structure_name
  {
   elements_description
  };

The structure name can't be used as an identifier (name of a variable or function). It should be noted that in MQL4 structure elements follow one another directly, without alignment. In C++ such an order is made to the compiler using the following instruction:

#pragma pack(1)

If you want to do another alignment in the structure, use auxiliary members, "fillers" to the right size.

Example:

struct trade_settings
  {
   uchar  slippage;     // value of the permissible slippage-size 1 byte
   char   reserved1;    // skip 1 byte
   short  reserved2;    // skip 2 bytes
   int    reserved4;    // another 4 bytes are skipped. ensure alignment of the boundary 8 bytes
   double take;         // values of the price of profit fixing
   double stop;         // price value of the protective stop
  };

Such a description of aligned structures is necessary only for transferring to imported dll-functions.

Attention: This example illustrates incorrectly designed data. It would be better first to declare the take and stop large data of the double type, and then declare the slippage member of the uchar type. In this case, the internal representation of data will always be the same regardless of the value specified in #pragma pack().

If a structure contains variables of the string type and/or object of a dynamic array, the compiler assigns an implicit constructor to such a structure. This constructor resets all the structure members of string type and correctly initializes objects of the dynamic array.

Simple Structures

Structures that do not contain strings or objects of dynamic arrays are called simple structures; variables of such structures can be freely copied to each other, even if they are different structures. Variables of simple structures, as well as their array can be passed as parameters to functions imported from DLL.

Access to Structure Members

The name of a structure becomes a new data type, so you can declare variables of this type. The structure can be declared only once within a project. The structure members are accessed using  the point operation (.).

Example:

struct trade_settings
  {
   double take;         // values of the profit fixing price
   double stop;         // value of the protective stop price
   uchar  slippage;     // value of the acceptable slippage
  };
//--- create up and initialize a variable of the trade_settings type
trade_settings my_set={0.0,0.0,5};  
if (input_TP>0) my_set.take=input_TP;

Classes

Classes differ from structures in the following:

  • the keyword class is used in declaration;
  • by default, all class members have access specifier private, unless otherwise indicated. Data-members of the structure have the default type of access as public, unless otherwise indicated;
  • class objects always have a table of virtual functions, even if there are no virtual functions declared in the class. Structures can't have virtual functions;
  • the new operator can be applied to class objects; this operator cannot be applied to structures;
  • classes can be inherited only from classes, structures can be inherited only from structures.

Classes and structures can have an explicit constructor and destructor. If your constructor is explicitly defined, the initialization of a structure or class variable using the initializing sequence is impossible.

Example:

struct trade_settings
  {
   double take;         // values of the profit fixing price
   double stop;         // value of the protective stop price
   uchar  slippage;     // value of the acceptable slippage
   //--- Constructor
          trade_settings() { take=0.0; stop=0.0; slippage=5; }
   //--- Destructor
         ~trade_settings() { Print("This is the end"); } 
  };
//--- Compiler will generate an error message that initialization is impossible
trade_settings my_set={0.0,0.0,5};  

Constructors and Destructors

A constructor is a special function, which is called automatically when creating an object of a structure or class and is usually used to initialize class members. Further we will talk only about classes, while the same applies to structures, unless otherwise indicated. The name of a constructor must match the class name. The constructor has no return type (you can specify the void type).

Defined class members –C strings, dynamic arrays and objects that require initialization –C will be in any case initialized, regardless of whether there is a constructor.

Each class can have multiple constructors, differing by the number of parameters and the initialization list. A constructor that requires specifying parameters is called a parametric constructor.

A constructor with no parameters is called a default constructor. If no constructors are declared in a class, the compiler creates a default constructor during compilation.

//+------------------------------------------------------------------+
//| Class for working with a date                                    |
//+------------------------------------------------------------------+
class MyDateClass
  {
private:
   int               m_year;          // Year
   int               m_month;         // Month
   int               m_day;           // Day of the month
   int               m_hour;          // Hour in a day
   int               m_minute;        // Minutes
   int               m_second;        // Seconds
public:
   //--- Default constructor
                     MyDateClass(void);
   //--- Parametric constructor
                     MyDateClass(int h,int m,int s);
  };

 

A constructor can be declared in the class description and then its body can be defined. For example, two constructors of MyDateClass can be defined the following way:

//+------------------------------------------------------------------+
//| Default constructor                                              |
//+------------------------------------------------------------------+
MyDateClass::MyDateClass(void)
  {
//---
   MqlDateTime mdt;
   datetime t=TimeCurrent(mdt);
   m_year=mdt.year;
   m_month=mdt.mon;
   m_day=mdt.day;
   m_hour=mdt.hour;
   m_minute=mdt.min;
   m_second=mdt.sec;
   Print(__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
MyDateClass::MyDateClass(int h,int m,int s)
  {
   MqlDateTime mdt;
   datetime t=TimeCurrent(mdt);
   m_year=mdt.year;
   m_month=mdt.mon;
   m_day=mdt.day;
   m_hour=h;
   m_minute=m;
   m_second=s;
   Print(__FUNCTION__);
  }

In the default constructor, all members of the class are filled using the TimeCurrent() function. In the parametric constructor only hour values are filled in. Other members of the class (m_year, m_month and m_day) will be automatically initialized with the current date.

The default constructor has a special purpose when initializing an array of objects of its class. The constructor, all parameters of which have default values, is not a default constructor. Here is an example:

//+------------------------------------------------------------------+
//| Class with a default constructor                                 |
//+------------------------------------------------------------------+
class CFoo
  {
   datetime          m_call_time;     // Time of the last object call
public:
   //--- Constructor with a parameter that has a default value is not a default constructor
                     CFoo(const datetime t=0){m_call_time=t;};
   //--- Copy constructor
                     CFoo(const CFoo &foo){m_call_time=foo.m_call_time;};
 
   string ToString(){return(TimeToString(m_call_time,TIME_DATE|TIME_SECONDS));};
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// CFoo foo; // This variant cannot be used - a default constructor is not set
//--- Possible options to create the CFoo object
   CFoo foo1(TimeCurrent());     // An explicit call of a parametric constructor
   CFoo foo2();                  // An explicit call of a parametric constructor with a default parameter
   CFoo foo3=D'2009.09.09';      // An implicit call of a parametric constructor
   CFoo foo40(foo1);             // An explicit call of a copy constructor
   CFoo foo41=foo1;              // An implicit call of a copy constructor
   CFoo foo5;                    // An explicit call of a default constructor (if there is no default constructor,
                                 // then a parametric constructor with a default value is called)
//--- Possible options to receive CFoo pointers
   CFoo *pfoo6=new CFoo();       // Dynamic creation of an object and receiving of a pointer to it
   CFoo *pfoo7=new CFoo(TimeCurrent());// Another option of dynamic object creation
   CFoo *pfoo8=GetPointer(foo1); // Now pfoo8 points to object foo1
   CFoo *pfoo9=pfoo7;            // pfoo9 and pfoo7 point to one and the same object
   // CFoo foo_array[3];         // This option cannot be used - a default constructor is not specified
//--- Show the value of m_call_time
   Print("foo1.m_call_time=",foo1.ToString());
   Print("foo2.m_call_time=",foo2.ToString());
   Print("foo3.m_call_time=",foo3.ToString());
   Print("foo4.m_call_time=",foo4.ToString());
   Print("foo5.m_call_time=",foo5.ToString());
   Print("pfoo6.m_call_time=",pfoo6.ToString());
   Print("pfoo7.m_call_time=",pfoo7.ToString());
   Print("pfoo8.m_call_time=",pfoo8.ToString());
   Print("pfoo9.m_call_time=",pfoo9.ToString());
//--- Delete dynamically created arrays
   delete pfoo6;
   delete pfoo7;
   //delete pfoo8;  // You do not need to delete pfoo8 explicitly, since it points to the automatically created object foo1
   //delete pfoo9;  // You do not need to delete pfoo9 explicitly. since it points to the same object as pfoo7
  }

If you uncomment these strings

  //CFoo foo_array[3];     // This variant cannot be used - a default constructor is not set

or

  //CFoo foo_dyn_array[];  // This variant cannot be used - a default constructor is not set

then the compiler will return an error for them "default constructor is not defined".

If a class has a user-defined constructor, the default constructor is not generated by the compiler. This means that if a parametric constructor is declared in a class, but a default constructor is not declared, you can not declare the arrays of objects of this class. The compiler will return an error for this script:

//+------------------------------------------------------------------+
//| Class without a default constructor                              |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
                     CFoo(string name) { m_name=name;}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Get the "default constructor is not defined" error during compilation
   CFoo badFoo[5];
  }

In this example, the CFoo class has a declared parametric constructor - in such cases, the compiler does not create a default constructor automatically during compilation. At the same time when you declare an array of objects, it is assumed that all objects should be created and initialized automatically. During auto-initialization of an object, it is necessary to call a default constructor, but since the default constructor is not explicitly declared and not automatically generated by the compiler, it is impossible to create such an object. For this reason, the compiler generates an error at the compilation stage.

There is a special syntax to initialize an object using a constructor. Constructor initializers (special constructions for initialization) for the members of a struct or class can be specified in the initialization list.

An initialization list is a list of initializers separated by commas, which comes after the colon after the list of parameters of a constructor and precedes the body (goes before an opening brace). There are several requirements:

  • Initialization lists can be used only in constructors;
  • Parent members cannot be initialized in the initialization list;
  • The initialization list must be followed by a definition (implementation) of a function.

Here is an example of several constructors for initializing class members.

//+------------------------------------------------------------------+
//| Class for storing the name of a character                        |
//+------------------------------------------------------------------+
class CPerson
  {
   string            m_first_name;     // First name 
   string            m_second_name;    // Second name
public:
   //--- An empty default constructor
                     CPerson() {Print(__FUNCTION__);};
   //--- A parametric constructor
                     CPerson(string full_name);
   //--- A constructor with an initialization list
                     CPerson(string surname,string name): m_second_name(surname), m_first_name(name) {};
   void PrintName(){PrintFormat("Name=%s Surname=%s",m_first_name,m_second_name);};
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPerson::CPerson(string full_name)
  {
   int pos=StringFind(full_name," ");
   if(pos>=0)
     {
      m_first_name=StringSubstr(full_name,0,pos);
      m_second_name=StringSubstr(full_name,pos+1);
     }
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Get an error "default constructor is not defined"
   CPerson people[5];
   CPerson Tom="Tom Sawyer";                       // Tom Sawyer
   CPerson Huck("Huckleberry","Finn");             // Huckleberry Finn
   CPerson *Pooh = new CPerson("Winnie","Pooh");  // Winnie the Pooh
   //--- Output values
   Tom.PrintName();
   Huck.PrintName();
   Pooh.PrintName();
   
   //--- Delete a dynamically created object
   delete Pooh;
  }

In this case, the CPerson class has three constructors:

  1. An explicit default constructor, which allows creating an array of objects of this class;
  2. A constructor with one parameter, which gets a full name as a parameter and divides it to the name and second name according to the found space;
  3. A constructor with two parameters that contains an initialization list. Initializers - m_second_name(surname) and m_first_name(name).

Note that the initialization using a list has replaced an assignment. Individual members must be initialized as:

 class_member (a list of expressions)

In the initialization list, members can go in any order, but all members of the class will be initialized according to the order of their announcement. This means that in the third constructor, first the m_first_name member will be initialized, as it is announced first, and only after it m_second_name is initialized. This should be taken into account in cases where the initialization of some members of the class depends on the values in other class members.

If a default constructor is not declared in the base class, and at the same time one or more constructors with parameters are declared, you should always call one of the base class constructors in the initialization list. It goes through the comma as ordinary members of the list and will be called first during object initialization, no matter where in the initialization list it is located.

//+------------------------------------------------------------------+
//| Base class                                                       |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
   //--- A constructor with an initialization list
                     CFoo(string name) : m_name(name) { Print(m_name);}
  };
//+------------------------------------------------------------------+
//| Class derived from CFoo                                          |
//+------------------------------------------------------------------+
class CBar : CFoo
  {
   CFoo              m_member;      // A class member is an object of the parent
public:
   //--- A default constructor in the initialization list calls the constructor of a parent
                     CBar(): m_member(_Symbol), CFoo("CBAR") {Print(__FUNCTION__);}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CBar bar;
  }

In this example, when creating the bar object, a default constructor CBar() will be called, in which first a constructor for the parent CFoo is called, and then comes a constructor for the m_member class member.

A destructor is a special function that is called automatically when a class object is destroyed. The name of the destructor is written as a class name with a tilde (~). Strings, dynamic arrays and objects, requiring deinitialization, will be de-initialized anyway, regardless of the destructor presence or absence. If there is a destructor, these actions will be performed after calling the destructor.

Destructors are always virtual, regardless of whether they are declared with the virtual keyword or not.

Defining Class Methods

Class function-methods can be defined both inside the class and outside the class declaration. If the method is defined within a class, then its body comes right after the method declaration.

Example:

class CTetrisShape
  {
protected:
   int               m_type;
   int               m_xpos;
   int               m_ypos;
   int               m_xsize;
   int               m_ysize;
   int               m_prev_turn;
   int               m_turn;
   int               m_right_border;
public:
   void              CTetrisShape();
   void              SetRightBorder(int border) { m_right_border=border; }
   void              SetYPos(int ypos)          { m_ypos=ypos;           }
   void              SetXPos(int xpos)          { m_xpos=xpos;           }
   int               GetYPos()                  { return(m_ypos);        }
   int               GetXPos()                  { return(m_xpos);        }
   int               GetYSize()                 { return(m_ysize);       }
   int               GetXSize()                 { return(m_xsize);       }
   int               GetType()                  { return(m_type);        }
   void              Left()                     { m_xpos-=SHAPE_SIZE;    }
   void              Right()                    { m_xpos+=SHAPE_SIZE;    }
   void              Rotate()                   { m_prev_turn=m_turn; if(++m_turn>3) m_turn=0; }
   virtual void      Draw()                     { return;                }
   virtual bool      CheckDown(int& pad_array[]);
   virtual bool      CheckLeft(int& side_row[]);
   virtual bool      CheckRight(int& side_row[]);
  }; 

Functions from SetRightBorder(int border) to Draw() are declared and defined directly inside the CTetrisShape class.

The CTetrisShape() constructor and methods CheckDown(int& pad_array[]), CheckLeft(int& side_row[]) and CheckRight(int& side_row[]) are only declared inside the class, but not defined yet. Definitions of these functions will be further in the code. In order to define the method outside the class, the scope resolution operator is used, the class name is used as the scope.

Example:

//+------------------------------------------------------------------+
//| Constructor of the basic class                                   |
//+------------------------------------------------------------------+
void CTetrisShape::CTetrisShape()
  {
   m_type=0;
   m_ypos=0;
   m_xpos=0;
   m_xsize=SHAPE_SIZE;
   m_ysize=SHAPE_SIZE;
   m_prev_turn=0;
   m_turn=0;
   m_right_border=0;
  }
//+------------------------------------------------------------------+
//| Checking ability to move down (for the stick and cube)           |
//+------------------------------------------------------------------+
bool CTetrisShape::CheckDown(int& pad_array[])
  {
   int i,xsize=m_xsize/SHAPE_SIZE;
//---
   for(i=0; i<xsize; i++)
     {
      if(m_ypos+m_ysize>=pad_array[i]) return(false);
     }
//---
   return(true);
  }

Public, Protected and Private Access Modifiers

When developing a new class, it is recommended to restrict access to the members from the outside. For this purpose keywords private or protected are used. In this case, hidden data can be accessed only from function-methods of the same class. If the protected keyword is used, hidden data can be accessed also from methods of classes - inheritors of this class. The same method can be used to restrict the access to functions-methods of a class.

If you need to completely open access to members and/or methods of a class, use the keyword public.

Example:

class CTetrisField
  {
private:
   int               m_score;                            // Score
   int               m_ypos;                             // Current position of the figures
   int               m_field[FIELD_HEIGHT][FIELD_WIDTH]; // Matrix of the well
   int               m_rows[FIELD_HEIGHT];               // Numbering of the well rows
   int               m_last_row;                         // Last free row
   CTetrisShape     *m_shape;                            // Tetris figure
   bool              m_bover;                            // Game over
public:
   void              CTetrisField() { m_shape=NULL; m_bover=false; }
   void              Init();
   void              Deinit();
   void              Down();
   void              Left();
   void              Right();
   void              Rotate();
   void              Drop();
private:
   void              NewShape();
   void              CheckAndDeleteRows();
   void              LabelOver();
  }; 

Any class members and methods declared after the specifier public: (and before the next access specifier) are available in any reference to the class object by the program. In this example these are the following members: functions CTetrisField(), Init(), Deinit(), Down(), Left(), Right(), Rotate() and Drop().

Any members that are declared after the access specifier to the elements private: (and before the next access specifier) are available only to members-functions of this class. Specifiers of access to elements always end with a colon (:) and can appear in the class definition many times.

Access to the members of the basis class can be redefined during inheritance in derived classes.

Modifier 'final'

The use of the 'final' modifier during class declaration prohibits further inheritance from this class. If the class interface requires no further modifications, or modifications are not allowed for security reasons, declare this class with the 'final' modifier. In addition, all the members of the class will also be implicitly considered final.

class CFoo final
  {
  //--- Class body
  };
 
class CBar : public CFoo
  {
  //--- Class body
  };

If you try to inherit form a class with the 'final' modifier as shown in the above example, the compiler will return an error:

cannot inherit from 'CFoo' as it has been declared as 'final'
see declaration of 'CFoo'

Unions (union)

Union is a special data type consisting of several variables sharing the same memory area. Therefore, the union provides the ability to interpret the same bit sequence in two (or more) different ways. Union declaration is similar to structure declaration and starts with the union keyword.

union LongDouble
{
  long   long_value;
  double double_value;
};

Unlike the structure, various union members belong to the same memory area. In this example, the union of LongDouble is declared with long and double type values sharing the same memory area. Please note that it is impossible to make the union store a long integer value and a double real value simultaneously (unlike a structure), since long_value and double_value variables overlap (in memory). On the other hand, an MQL5 program is able to process data containing in the union as an integer (long) or real (double) value at any time. Therefore, the union allows receiving two (or more) options for representing the same data sequence.

During the union declaration, the compiler automatically allocates the memory area sufficient to store the largest type (by volume) in the variable union. The same syntax is used for accessing the union element as for the structures –C point operator.

union LongDouble
{
  long   long_value;
  double double_value;
};
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   LongDouble lb;
//--- get and display the invalid -nan(ind) number
   lb.double_value=MathArcsin(2.0);
   printf("1.  double=%f                integer=%I64X",lb.double_value,lb.long_value);
//--- largest normalized value (DBL_MAX)
   lb.long_value=0x7FEFFFFFFFFFFFFF;
   printf("2.  double=%.16e  integer=%I64X",lb.double_value,lb.long_value);
//--- smallest positive normalized (DBL_MIN)
   lb.long_value=0x0010000000000000;    
   printf("3.  double=%.16e  integer=%.16I64X",lb.double_value,lb.long_value);
  }
/*  Execution result
    1.  double=-nan(ind)                integer=FFF8000000000000
    2.  double=1.7976931348623157e+308  integer=7FEFFFFFFFFFFFFF
    3.  double=2.2250738585072014e-308  integer=0010000000000000
*/

Since the unions allow the program to interpret the same memory data in different ways, they are often used when an unusual type conversion is required.

The unions cannot be involved in the inheritance, and they also cannot have static members due to their very nature. In all other aspects, the union behaves like a structure with all its members having a zero offset. The following types cannot be the union members:

Similar to classes, the union is capable of having constructors and destructors, as well as methods. By default, the union members are of public access type. In order to create private elements, use the private keyword. All these possibilities are displayed in the example illustrating how to convert a color of the color type to ARGB as does the ColorToARGB() function.

//+------------------------------------------------------------------+
//| Union for color(BGR) conversion to ARGB                          |
//+------------------------------------------------------------------+
union ARGB
  {
   uchar             argb[4];
   color             clr;
   //--- constructors
                     ARGB(color col,uchar a=0){Color(col,a);};
                    ~ARGB(){};
   //--- public methods
public:
   uchar   Alpha(){return(argb[3]);};
   void    Alpha(const uchar alpha){argb[3]=alpha;};
   color   Color(){ return(color(clr));};
   //--- private methods
private:
   //+------------------------------------------------------------------+
   //| set the alpha channel value and color                            |
   //+------------------------------------------------------------------+
   void    Color(color col,uchar alpha)
     {
      //--- set color to clr member
      clr=col;
      //--- set the Alpha component value - opacity level
      argb[3]=alpha;
      //--- interchange the bytes of R and B components (Red and Blue)     
      uchar t=argb[0];argb[0]=argb[2];argb[2]=t;
     };
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 0x55 means 55/255=21.6 % (0% - fully transparent)
   uchar alpha=0x55; 
//--- color type is represented as 0x00BBGGRR
   color test_color=clrDarkOrange;
//--- values of bytes from the ARGB union are accepted here
   uchar argb[]; 
   PrintFormat("0x%.8X - here is how the 'color' type look like for %s, BGR=(%s)",
               test_color,ColorToString(test_color,true),ColorToString(test_color));
//--- ARGB type is represented as 0x00RRGGBB, RR and BB components are swapped
   ARGB argb_color(test_color);
//--- copy the bytes array
   ArrayCopy(argb,argb_color.argb);
//--- here is how it looks in ARGB representation  
   PrintFormat("0x%.8X - ARGB representation with the alpha channel=0x%.2x, ARGB=(%d,%d,%d,%d)",
               argb_color.clr,argb_color.Alpha(),argb[3],argb[2],argb[1],argb[0]);
//--- add opacity level
   argb_color.Alpha(alpha);
//--- try defining ARGB as 'color' type
   Print("ARGB как color=(",argb_color.clr,")  alpha channel=",argb_color.Alpha());
//--- copy the bytes array
   ArrayCopy(argb,argb_color.argb);
//--- here is how it looks in ARGB representation
   PrintFormat("0x%.8X - ARGB representation with the alpha channel=0x%.2x, ARGB=(%d,%d,%d,%d)",
               argb_color.clr,argb_color.Alpha(),argb[3],argb[2],argb[1],argb[0]);
//--- check with the ColorToARGB() function results
   PrintFormat("0x%.8X - result of ColorToARGB(%s,0x%.2x)",ColorToARGB(test_color,alpha),
               ColorToString(test_color,true),alpha);
  }
/* Execution result
   0x00008CFF - here is how the color type looks for clrDarkOrange, BGR=(255,140,0)
   0x00FF8C00 - ARGB representation with the alpha channel=0x00, ARGB=(0,255,140,0)
   ARGB as color=(0,140,255)  alpha channel=85
   0x55FF8C00 - ARGB representation with the alpha channel=0x55, ARGB=(85,255,140,0)
   0x55FF8C00 - result of ColorToARGB(clrDarkOrange,0x55)
*/ 

Interfaces

An interface allows determining specific functionality, which a class can then implement. In fact, an interface is a class that cannot contain any members, and may not have a constructor and/or a destructor. All methods declared in an interface are purely virtual, even without an explicit definition.

An interface is defined using the "interface" keyword. Example:

//--- Basic interface for describing animals
interface IAnimal
  {
//--- The methods of the interface have public access by default
   void Sound();  // The sound produced by the animal
  };
//+------------------------------------------------------------------+
//|  The CCat class is inherited from the IAnimal interface          |
//+------------------------------------------------------------------+
class CCat : public IAnimal
  {
public:
                     CCat() { Print("Cat was born"); }
                    ~CCat() { Print("Cat is dead");  }
   //--- Implementing the Sound method of the IAnimal interface
   void Sound(){ Print("meou"); }
  };
//+------------------------------------------------------------------+
//|  The CDog class is inherited from the IAnimal interface          |
//+------------------------------------------------------------------+
class CDog : public IAnimal
  {
public:
                     CDog() { Print("Dog was born"); }
                    ~CDog() { Print("Dog is dead");  }
   //--- Implementing the Sound method of the IAnimal interface
   void Sound(){ Print("guaf"); }
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- An array of pointers to objects of the IAnimal type
   IAnimal *animals[2];
//--- Creating child classes of IAnimal and saving pointers to them into an array    
   animals[0]=new CCat;
   animals[1]=new CDog;
//--- Calling the Sound() method of the basic IAnimal interface for each child  
   for(int i=0;i<ArraySize(animals);++i)
      animals[i].Sound();
//--- Deleting objects
   for(int i=0;i<ArraySize(animals);++i)
      delete animals[i];
//--- Execution result
/*
   Cat was born
   Dog was born
   meou
   guaf
   Cat is dead
   Dog is dead
*/
  }

Like with abstract classes, an interface object cannot be created without inheritance. An interface can only be inherited from other interfaces and can be a parent for a class. An interface is always publicly visible.

An interface cannot be declared within a class or structure declaration, but a pointer to the interface can be saved in a variable of type void *. Generally speaking, a pointer to an object of any class can be saved into a variable of type void *. In order to convert a void * pointer to a pointer to an object of a particular class, use the dynamic_cast operator. If conversion is not possible, the result of the dynamic_cast operation will be NULL.

 

See also

Object-Oriented Programming