Descargar MetaTrader 5

Estructuras, clases e interfaces

Estructuras

Una estructura es un conjunto de elementos del tipo libre, salvo el tipo void. De esa manera, la estructura une los datos de diferentes tipos que están vinculados de forma lógica.

Declaración de estructura

El tipo de datos estructural se define de la siguiente manera:

struct nombre_de_estructura 
  {
   descripción_de_elementos
  };

No se puede usar el nombre de la estructura en calidad del identificador (nombre de la variable o función). Hay que tener en cuenta que en MQL5 los elementos de una estructura siguen directamente uno detrás del otro sin que sean alineados. En el lenguaje C++ este comando se proporciona al compilador mediante la instrucción

#pragma pack(1)

Si hace falta hacer otra alineación dentro de la estructura, es necesario utilizar los elementos "de relleno" adicionales de tamaño necesario.

Ejemplo:

struct trade_settings
  {
   uchar  slippage;     // valor del deslizamiento permitido — tamaño 1 byte
   char   reserved1;    // 1 byte de permiso
   short  reserved2;    // 2 bytes de permiso
   int    reserved4;    // otros 4 bytes de permiso. Aseguramos la alineación al margen de 8 bytes
   double take;         // valor del precio de fijación del beneficio
   double stop;         // valor del precio del stop de protección
  };

Esta descripción de alineación de las estructuras es necesaria unicamente para la transmisión a las funciones dll importadas.

Atención: este ejemplo refleja los datos proyectados de una manera errónea. Sería mejor declarar al principio los datos take y stop de mayor tamaño que el tipo double, y luego declarar el elemento slippage del tipo uchar. En este caso, la presentación interna de los datos siempre va a ser igual independientemente del valor indicado en #pragma pack().

Si la estructura contiene las variables del tipo string y/o el objeto del array dinámico, entonces para esta estructura el compilador asigna un constructor implícito donde se efectúa la anulación de todos los elementos del tipo string y la inicialización correcta para el objeto del array dinámico.

Estructuras simples

Las estructuras que no contienen las cadenas y objetos del array dinámico se llaman estructuras simples, las variables de estas estructuras pueden copiarse libremente una en otra, incluso si se trata de las estructuras diferentes. Las variables de estructuras simples, igual que sus matrices, pueden ser pasadas como parámetros en las funciones importadas de DLL.

Acceso a los elementos de la estructura

El nombre de la estructura es un tipo de datos nuevo y permite declarar las variables de este tipo. Se puede declarar la estructura sólo una vez dentro de un proyecto. El acceso a los elementos de las estructuras se realiza mediante operación punto (.).

Ejemplo:

struct trade_settings
  {
   double take;         // valor del precio de fijación del beneficio
   double stop;         // valor del precio del stop de protección
   uchar  slippage;     // valor del deslizamiento permitido
  };
//--- creamos e inicializamos la variable del tipo trade_settings
trade_settings my_set={0.0,0.0,5};  
if (input_TP>0) my_set.take=input_TP;

Modificador final

La existencia del modificador final al declarar la estructura, prohíbe la posterior herencia a partir de ella. Si la estructura es tal que no haya necesidad de introducir cambios posteriormente, o los cambios no están permitidos por motivos de seguridad, declárela con el modificador final. Además, todos los miembros de la estructura también se considerarán implícitamente como "final".

struct settings final
  {
  //--- cuerpo de la estructura
  };
 
struct trade_settings : public settings
  {
  //--- cuerpo de la estructura
  };

Al intentar heredar de una estructura con el modificador final, como se muestra en el ejemplo de más arriba, el compilador dará error:

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

Clases

Las clases llevan una serie de diferencia de las estructuras:

  • en la declaración se utiliza la palabra clave class;
  • si no se indica lo contrario todos los elementos de las clase por defecto tienen el especificador de acceso private. Los elementos-datos de la estructura por defecto tienen el tipo de acceso public, si no se indica lo contrario;
  • los objetos de las clases siempre tienen una tabla de funciones virtuales, incluso si en la clase ninguna función virtual esté declarada. Las estructuras no pueden tener funciones virtuales;
  • para los objetos de la clase se puede aplicar el operador new, para las estructuras no se puede aplicar este operador;
  • las clases pueden ser heredadas unicamente de las clases, y las estructuras sólo de las estructuras.

Las clases y las estructuras pueden tener el constructor y destructor explícitos. En el caso, si el constructor está determinado de una manera explícita, la inicialización de variable del tipo de la estructura o clase con la ayuda de la sucesión inicializadora es imposible.

Ejemplo:

struct trade_settings
  {
   double take;         // valor del precio de fijación del beneficio
   double stop;         // valor del precio del stop de protección
   uchar  slippage;     // valor del deslizamiento permitido
   //--- constructor
          trade_settings() { take=0.0; stop=0.0; slippage=5; }
   //--- destructor
         ~trade_settings() { Print("Es el final"); } 
  };
//--- compilador mostrará el error con el mensaje sobre la imposibilidad de inicialización
trade_settings my_set={0.0,0.0,5};  

Constructores y destructores

El constructor es una función especial que se llama automáticamente cuando se crea un objeto de estructura o clase, y normalmente se utiliza para la inicialización de los miembros de la clase. A continuación vamos a hablar sólo de las clases, pero todo lo dicho también se refiere a las estructuras, si no se especifica lo otro. El nombre del constructor debe coincidir con el de la clase. El constructor no tiene el tipo devuelto (se puede indicar el tipo void).

Algunos miembros definidos de la clase, tales como — cadenas, arrays dinámicos y objetos que requieren la inicialización — de cualquier manera serán inicializados, independientemente de la presencia del constructor.

Cada clase puede tener varios constructores que se diferencian por el número de parámetros y listas de inicialización. Un constructor que se requiere la especificación de parámetros se llama el constructor paramétrico.

Un constructor que no tiene parámetros se llama un constructor por defecto. Si en la clase no está declarado ningún constructor, entonces durante la compilación el compilador creará un constructor por defecto.

//+------------------------------------------------------------------+
//|  clase para trabajar con la fecha                                        |
//+------------------------------------------------------------------+
class MyDateClass
  {
private:
   int               m_year;          // año
   int               m_month;         // mes
   int               m_day;           // día del mes
   int               m_hour;          // hora del día
   int               m_minute;        // minutos
   int               m_second;        // segundos
public:
   //--- constructor por defecto
                     MyDateClass(void);
   //--- constructor paramétrico
                     MyDateClass(int h,int m,int s);
  };

 

Se puede declarar el constructor en la descripción de la clase y luego definir su cuerpo. Por ejemplo, así se puede definir dos constructores de la clase MyDateClass:

//+------------------------------------------------------------------+
//| constructor por defecto                                         |
//+------------------------------------------------------------------+
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__);
  }
//+------------------------------------------------------------------+
//| constructor paramétrico                                        |
//+------------------------------------------------------------------+
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__);
  }

En el constructor por defecto se llenan todos los miembros de la clase por medio de la función TimeCurrent(), en el constructor paramétrico se llenan sólo los valores de la hora. Los demás miembros de la clase (m_year, m_month y m_day) serán inicializados automáticamente con la fecha en curso.

El constructor por defecto tiene un propósito especial cuando se inicializa un array de objetos de su clase. El constructor cuyos parámetros tienen los valores por defecto, no es constructor por defecto. Ejemplificaremos esto:

//+------------------------------------------------------------------+
//|  clase con un constructor por defecto                              |
//+------------------------------------------------------------------+
class CFoo
  {
   datetime          m_call_time;     // hora de la última llamada al objeto
public:
   //--- un constructor con el parámetro que tiene el valor predefinido no es un constructor por defecto
                     CFoo(datetime t=0){m_call_time=t;};
   string ToString(){return(TimeToString(m_call_time,TIME_DATE|TIME_SECONDS));};
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CFoo foo; // Esta opción se puede utilizar - se llamará a un constructor con parámetros predefinidos
//--- posibles opciones de creación del objeto CFoo
   CFoo foo1(TimeCurrent());     // la primera opción de creación automática del objeto
   CFoo foo2();                  // la segunda opción de creación automática del objeto
   CFoo foo3=TimeTradeServer();  // la tercera opción de creación automática del objeto
//--- posibles opciones de creación de punteros CFoo por medio del operador new
   CFoo *foo4=new CFoo();
   CFoo *foo5=new CFoo(TimeTradeServer());
   CFoo *foo6=GetPointer(foo5);  // ahora foo5 y foo6 apuntan al mismo objeto
   CFoo *foo7,*foo8;
   foo7=new CFoo(TimeCurrent());
   foo8=GetPointer(foo7);        // foo7 y foo8 apuntan al mismo objeto
   //CFoo foo_array[3];     // esta opción no se puede utilizar - el constructor por defecto no está establecido
   //CFoo foo_dyn_array[];  // esta opción no se puede utilizar - el constructor por defecto no está establecido
//--- mostramos los valores m_call_time
   Print("foo.m_call_time=",foo1.ToString());
   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("foo6.m_call_time=",foo6.ToString());
   Print("foo7.m_call_time=",foo7.ToString());
   Print("foo8.m_call_time=",foo8.ToString());
//--- eliminaremos los objetos creados dinámicamente
   delete foo4;
   delete foo5;
   delete foo7;
  }

Si añadimos comentarios a estas cadenas en este ejemplo

  //CFoo foo_array[3];     // esta opción no se puede utilizar - el constructor por defecto no está establecido

o

  //CFoo foo_dyn_array[];  // esta opción no se puede utilizar - el constructor por defecto no está establecido

el compilador devolverá el error para ellas "default constructor is not defined".

Si la clase tiene un constructor declarado por el usuario, entonces el compilador no generará el constructor por defecto. Esto quiere decir que si en una clase está declarado un constructor paramétrico pero no está declarado un constructor por defecto, entonces no se puede declarar los arrays de los objetos de esta clase. Pues, para este script el compilador devolverá el error:

//+------------------------------------------------------------------+
//| clase sin constructor por defecto                              |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
                     CFoo(string name) { m_name=name;}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- durante la compilación obtenemos el error "default constructor is not defined"
   CFoo badFoo[5];
  }

En este ejemplo la clase CFoo tiene declarado un constructor paramétrico — en este caso durante la compilación el compilador no crea automáticamente el constructor por defecto. Al mismo tiempo, cuando se declara un array de objetos se supone que todos los objetos tienen que ser creados e inicializados automáticamente. Durante la inicialización automática del objeto, es necesario llamar a un constructor por defecto, pero debido a que el constructor por defecto no está declarado explicitamente y no ha sido generado automáticamente por el compilador, entonces resulta imposible crear este objeto. Precisamente por esta razón el compilador muestra el error aún en la fase de compilación.

Existe una sintaxis especial para la inicialización del objeto mediante el constructor. Los inicializadores del constructor (construcciones especiales para la inicialización) para los miembros de una estructura o clase se puede especificar en la lista de inicialización.

La lista de inicialización es una lista de inicializadores separados por comas que sigue tras dos puntos después de la lista de parámetros del constructor y precede el cuerpo (va antes de la llave que abre). Existen algunas exigencias:

  • las listas de inicialización se puede utilizar sólo en los constructores;
  • no se puede inicializar los miembros de los padres en la lista de inicialización;
  • tras las lista de inicialización debe ir la definición (implementación) de la función.

Vamos a mostrar algunos ejemplos de constructores para la inicialización de los miembros de la clase.

//+------------------------------------------------------------------+
//| clase para almacenar los apellidos y el nombre de una persona                     |
//+------------------------------------------------------------------+
class CPerson
  {
   string            m_first_name;     // nombre 
   string            m_second_name;    // apellido
public:
   //--- constructor por defecto vacío
                     CPerson() {Print(__FUNCTION__);};
   //--- constructor paramétrico
                     CPerson(string full_name);
   //--- constructor con la lista de inicialización
                     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()
  {
//--- obtenemos el 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("Whinnie","Pooh");  // Winnie the Pooh
   //--- mostraremos los valores
   Tom.PrintName();
   Huck.PrintName();
   Pooh.PrintName();
   
   //--- eliminaremos el objeto creado dinámicamente
   delete Pooh;
  }

En este caso, la clase CPerson tiene tres constructores:

  1. un constructor por defecto explícito que permite crear un array de los objetos de esta clase;
  2. un constructor con un parámetro que obtiene el nombre completo como parámetro, y lo divide en el nombre y el apellido según el espacio encontrado;
  3. un constructor con dos parámetros que contiene la lista de inicialización. Los inicializadores — m_second_name(surname) y m_first_name(name).

Fíjese cómo la inicialización reemplazó la asignación utilizando la lista. Los miembros individuales deben inicializarse como sigue:

 miembro_de_la_clase (lista de expresiones)

Los miembros pueden seguir cualquier orden en la lista de inicialización, pero todos los miembros de la clase van a inicializarse según el orden de su declaración. Esto significa que en el tercer constructor primero será inicializado el miembro m_first_name porque va declarado primero, y sólo después de él será inicializado el miembro m_second_name. Esto hay que tener en cuenta cuando la inicialización de unos miembros de la clase depende de los valores en otros miembros de la clase.

Si en la clase base no está declarado el constructor por defecto pero al mismo tiempo está declarado uno o varios constructores paramétricos, habrá que llamar sí o sí a uno de los constructores de la clase base en la lista de inicialización. Éste va tras la coma, como los demás miembros de la lista, y será llamado en primer lugar durante la inicialización del objeto independientemente de su ubicación en la lista de inicialización.

//+------------------------------------------------------------------+
//|  clase base                                                   |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
   //--- constructor con la lista de inicialización
                     CFoo(string name) : m_name(name) { Print(m_name);}
  };
//+------------------------------------------------------------------+
//|  descendiente de la clase CFoo                                             |
//+------------------------------------------------------------------+
class CBar : CFoo
  {
   CFoo              m_member;      // el miembro de la clase es el objeto del padre
public:
   //--- el constructor por defecto en la lista de inicialización llama al constructor del padre
                     CBar(): m_member(_Symbol), CFoo("CBAR") {Print(__FUNCTION__);}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CBar bar;
  }

En el ejemplo mencionado, durante la creación del objeto bar se llamará al constructor por defecto CBar() en el que primero se llama al constructor para el padre CFoo, y luego se llama al constructor para el miembro de la clase m_member.

Los destructores son unas funciones especiales llamadas automáticamente a la hora de eliminar un objeto de la clase. El nombre del destructor se escribe como el de la clase con la tilde (~). Las cadenas, arrays dinámicos y los objetos que necesitan deinicialización van a ser deinicializados de cualquier manera independientemente de la presencia del destructor. Disponiendo del destructor, estas acciones van a ser ejecutadas después del arranque del mismo.

Los destructores siempre son virtuales, independientemente de que si están declarados con la palabra clave virtual o no.

Determinación de los métodos de clase

Las funciones-métodos de clase pueden ser determinados tanto dentro de la clase, como fuera de la declaración de la clase. Si el método se determina dentro de la clase, entonces su cuerpo sigue directamente después de la declaración del método.

Ejemplo:

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[]);
  }; 

Las funciones con SetRightBorder(int border) por Draw() se declaran y se determinan directamente dentro de la clase CTetrisShape.

El constructor CTetrisShape() y los métodos CheckDown(int& pad_array[]), CheckLeft(int& side_row[]) y CheckRight(int& side_row[]) se declaran sólo dentro de la clase, pero por ahora no están determinados. Las determinaciones de estas funciones deben seguir más adelante en el código. Para determinar el método fuera de la clase se utiliza la operación del permiso de contexto, como contexto se utiliza el nombre de la clase.

Ejemplo:

//+------------------------------------------------------------------+
//| Constructor de la clase base                                      |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Prueba de posibilidad de moverse abajo (para la vara o el cubo)           |
//+------------------------------------------------------------------+
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);
  }

 
Modificadores de acceso public, protected y private

A la hora de crear nueva clase se recomienda limitar el acceso a los elementos desde fuera. Para eso se utilizan las palabras claves private o protected. En este caso el acceso a los datos encubiertos puede realizarse sólo desde las funciones-métodos de la misma clase. Si se utiliza la palabra clave protected, entonces el acceso a los datos encubiertos se puede realizar también de los métodos de las clases que son herederos de esta clase. De la misma manera se puede limitar el acceso a las funciones-métodos de clase.

Si se necesita abrir totalmente el acceso a los elementos y/o métodos de clase, entonces se utiliza la palabra clave public.

Ejemplo:

class CTetrisField
  {
private:
   int               m_score;                            // cuenta
   int               m_ypos;                             // posición actual de la pieza
   int               m_field[FIELD_HEIGHT][FIELD_WIDTH]; // matrix del vaso
   int               m_rows[FIELD_HEIGHT];               // numeración de las filas del vaso 
   int               m_last_row;                         // la última fila libre
   CTetrisShape     *m_shape;                            // pieza del tetris
   bool              m_bover;                            // fin del juego
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();
  }; 

Cualquier elemento y método de la clase que están declarados después del especificador public (y hasta el siguiente especificador del acceso), son accesibles a la hora de cualquier referencia del programa hacia el objeto de esta clase. En este ejemplo son los siguientes elementos: funciones CTetrisField(), Init(),  Deinit(), Down(), Left(), Right(), Rotate() y Drop().

Cualquier elemento de la clase que está declarado después del especificador private (y hasta el siguiente especificador del acceso), es accesible sólo para las funciones-elementos de la misma clase. Los especificadores de acceso a los elementos siempre se terminan con dos puntos (:) y pueden aparecer en la determinación de la clase varias veces.

El acceso a los elementos de la clase base puede volver a determinarse durante la herencia en las clases derivadas.

Modificador final

La presencia del modificador final al declarar una clase, prohíbe la posterior herencia a partir de ella. Si la interfaz de la clase es tal que no haya necesidad de introducir cambios posteriormente, o los cambios no están permitidos por motivos de seguridad, declare la clase con el modificador final. Además, todos los métodos de la clase se también considerarán implícitamente como "final".

class CFoo final
  {
  //--- cuerpo de la clase
  };
 
class CBar : public CFoo
  {
  //--- cuerpo de la clase
  };

Al intentar heredar de una clase con el modificador final, como se muestra en el ejemplo de más arriba, el compilador dará error:

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

Interfaces

La interfaz ha sido diseñada para definir una cierta funcionalidad, cuya clase en consecuencia puede implementar. En la práctica, se trata de una clase que no puede contener miembros y puede tener un constructor y/o destructor. Todos los métodos declarados en la interfaz son puramente virtuales, incluso sin definición explícita.

Se define la interfaz con la ayuda de la palabra clave interface, como se muestra en el ejemplo:

//--- interfaz básica para describir animales
interface IAnimal
  {
//--- los métodos de la interfaz, por defecto, tienen acceso public
   void Sound();  // sonido que hace el animal
  };
//+------------------------------------------------------------------+
//|  la clase CCat se hereda de la interfaz IAnimal                    |
//+------------------------------------------------------------------+
class CCat : public IAnimal
  {
public:
                     CCat() { Print("Cat was born"); }
                    ~CCat() { Print("Cat is dead");  }
   //--- implementamos el método Sound de la interfaz IAnimal
   void Sound(){ Print("meou"); }
  };
//+------------------------------------------------------------------+
//|  la clase CDog se hereda de la interfaz IAnimal                    |
//+------------------------------------------------------------------+
class CDog : public IAnimal
  {
public:
                     CDog() { Print("Dog was born"); }
                    ~CDog() { Print("Dog is dead");  }
   //--- implementamos el método Sound de la interfaz IAnimal
   void Sound(){ Print("guaf"); }
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- matriz de punteros a los objetos del tipo IAnimal
   IAnimal *animals[2];
//--- generamos los descendientes de IAnimal y guardamos los punteros a estos en una matriz    
   animals[0]=new CCat;
   animals[1]=new CDog;
//--- llamamos el método Sound() de la interfaz básica IAnimal para cada descendiente  
   for(int i=0;i<ArraySize(animals);++i)
      animals[i].Sound();
//--- eliminamos los objetos
   for(int i=0;i<ArraySize(animals);++i)
      delete animals[i];
//--- resultado de la ejecución
/*
   Cat was born
   Dog was born
   meou
   guaf
   Cat is dead
   Dog is dead
*/
  }

Como sucede con las clases abstractas, no se puede crear un objeto de la interfaz sin herencia. La interfaz puede heredarse solo de otras interfaces y puede actuar como descendiente para la clase. Además, siempre tiene visibilidad pública.

La inetrfaz no se puede declarar dentro de la declaración de una clase o estructura, pero así y con todo, el puntero a la interfaz se puede guardar en una variable del tipo void *. Hablando en general, en una variable del tipo void * se puede guardar un puntero a un objeto de cualquier clase. Para transformar el puntero void * en el puntero a un objeto de una clase concreta, es necesario usar el operador dynamic_cast.  En el caso de que la transformación no sea posible, el resultado de la operación dynamic_cast será NULL.

Véase también

Programación orientada a objetos


Actualizado: 2016.08.31