Curso C++



Punteros a miembros no estáticos de clases

§1 Ejemplo-1

Muestra de utilización de miembros-puntero.  Es una versión ejecutable (compilable) del caso planteado en la página anterior, en el que una clase CTransporte representa el costo de envío de los productos de un fabricante, y deseamos obtener el costo mediante invocación de uno de sus métodos en función del peso total del envío, pero los métodos utilizados varían. Por ejemplo, para menos de 1 Kg se utiliza correo ordinario; para 1 a 10 Kg. mensajería normal; para 10 a 20 Kg agencia de transporte exterior, y para más de 20 Kg medios propios. Cada una de estas formas de envío tiene distintas formas de cálculo de costo, por lo que utiliza métodos distintos.

La solución adoptada consiste en diseñar distintos métodos que representaran las distintas formas de cálculo de costo, y la utilización de un puntero-a-método para acceder a la función correspondiente. El puntero es iniciado con la dirección del método adecuado en el momento de la construcción del objeto en función la clase de envío:

#include <iostream>
using namespace std;

class CTransporte {
  private:
  float correo(float);
  float mensajeria(float);
  float agencia(float);
  float propio(float);
  public:
  float (CTransporte::* costo)(float); // puntero
  CTransporte(float);                  // constructor
};

float CTransporte::correo (float peso) {
  cout << "Correo" << endl; return 4*peso;
}
float CTransporte::mensajeria (float peso) {
  cout << "Mensajeria" << endl; return 3*peso;
}
float CTransporte::agencia (float peso) {
  cout << "Agencia" << endl; return 2*peso;
}
float CTransporte::propio (float peso) {
  cout << "M Propios" << endl; return peso;
}

CTransporte::CTransporte(float peso) {
  if (peso < 1)       costo = &CTransporte::correo;
  else if (peso < 10) costo = &CTransporte::mensajeria;
  else if (peso < 20) costo = &CTransporte::agencia;
  else                costo = &CTransporte::propio;
}

int main() {      // ==============
  float exp[6] = {0.1, 2.2, 4.4, 8.8, 16.16, 32.32};
  float cTot = 0;
  int items = sizeof exp / sizeof exp[0];

  for (int item = 0; item < items; item++) {
    CTransporte ct(exp[item]);          // M6:
    cTot += (ct.*ct.costo)(exp[item]);  // M7:
  }
  cout << "Costo total: " << cTot << " Euros" << endl;
  return 0;
}

Salida:

Correo
Mensajeria
Mensajeria
Mensajeria
Agencia
M Propios
Costo total: 111.24 Euros

Comentario:

Una forma equivalente de construir el bucle de cálculo, sustituiría las sentencias M6 y M7 por las siguientes:

CTransporte* cPt = new CTransporte(exp[item]);  // M6bis
cTot += (cPt->*(cPt->costo))(exp[item]);        // M7bis

§2 Ejemplo-2

En cualquier caso, la solución propuesta presenta un inconveniente importante: el bucle for construye tantos objetos CTransporte como bultos componen la expedición, lo que supone un consumo considerable de memoria y tiempo de construcción de los objetos. Como solución proponemos un diseño alternativo en el que la decisión del método de cálculo utilizado, se realiza en el cuerpo de un método auxiliar costo() que es la interfaz de la clase con el exterior. Este método sustituye al puntero del ejemplo anterior, que ahora pasa a denominarse pfcost. Como consecuencia, no es necesario definir un constructor explícito. Es suficiente con la versión por defecto proporcionada por el compilador.

#include <iostream>
using namespace std;

class CTransporte {
  private:
  float correo(float);
  float mensajeria(float);
  float agencia(float);
  float propio(float);
  float (CTransporte::* pfcost)(float); // puntero
  public:
  float costo(float);
};

float CTransporte::correo (float peso) {
  cout << "Correo" << endl; return 4*peso;
}
float CTransporte::mensajeria (float peso) {
  cout << "Mensajeria" << endl; return 3*peso;
}
float CTransporte::agencia (float peso) {
  cout << "Agencia" << endl; return 2*peso;
}
float CTransporte::propio (float peso) {
  cout << "M Propios" << endl; return peso;
}

float CTransporte::costo(float peso) {
  if (peso < 1)       pfcost = &CTransporte::correo;
  else if (peso < 10) pfcost = &CTransporte::mensajeria;
  else if (peso < 20) pfcost = &CTransporte::agencia;
  else                pfcost = &CTransporte::propio;
  return (this->*(this->pfcost))(peso);
}

int main() {              // ==============
  float exp[6] = {0.1, 2.2, 4.4, 8.8, 16.16, 32.32};
  float cTot = 0;
  int items = sizeof exp / sizeof exp[0];
  CTransporte ct;         // un único objeto
  for (int item = 0; item < items; item++) {
     cTot += ct.costo(exp[item]);
  }
  cout << "Costo total: " << cTot << " Euros" << endl;
  return 0;
}

La salida es exactamente análoga a la del ejemplo anterior.

Comentario:

El método público costo() es ahora el encargado de iniciar el puntero pfcost con el valor del método adecuado en función del peso del envío, y de invocarlo a continuación utilizando el puntero.

Observe que en realidad, el método costo() actúa como "handle" o interfaz del algoritmo que realmente se encarga de la computación.