Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.1.11c1  Acceso a subespacios en clases


§1  Lo mismo que ocurre con las funciones ( 4.1.11a), dentro de una clase no puede declararse un espacio de nombres. Tampoco puede utilizarse la directiva using:

class A {
   int x;
   namespace UNO {       // Error: no permitido
     ...
   }
   using namespace DOS;  // Error: no permitido
}


§2  Sin embargo, sí está permitida la declaración using (con ciertas limitaciones) y puede resultar muy útil; de hecho, las clases son auténticos subespacios de nombres [1], a los que nos podemos referir como tales utilizando la declaración using con las siguientes condiciones:

  • La declaración using en la definición de una clase debe referirse siempre a elementos de la clase base [*].
  • La declaración using no puede ser utilizada para acceder a información inaccesible [**].

Ejemplo:

class A {
  char ch;
  public:  char func() { return ch; }
  ...
};
class B {
  char ch;
  public:  char func(char c) { return c; }
  ...
};
class C : public A {
  int x;
  public:
  int func(int i) {return i+x; }
  using B::func;       // Error: B no es ancestro de C *
  using A::ch;         // Error: ch es privado en A **
  using A::func()      // Ok: sobrecarga C::func
};


  Obsérvese como en las declaraciones using precedentes, se utilizan los nombres de las clases como subespacios.

La cuestión de los subespacios de nombres en el interior de clases derivadas se contempla también en los capítulos dedicados a la herencia simple ( 4.11.2b) y múltiple ( 4.11.2c).

Ejemplo 2:

Nota: el comportamiento exacto de los ejemplos de este capítulo depende del compilador. El que se muestra corresponde a BC++ 5.5 y MS VC++ 6.0 para Win 32.  En cambio GNU Cpp 2.95.2 produce errores: cannot adjust access to 'void A::func(char)' in 'class B' because of the local method 'void B::fun(char*)' with same name. Nuestra opinión es que algunas características "++" de este compilador, derivado del C, no están suficientemente desarrolladas en estas versiones tan tempranas.

En el programa que sigue se utiliza un subespacio de nombre dentro de una clase [2], lo que permite sobrecargar una función miembro:

#include <iostream>
using namespace std;
 
class A {                 // Superclase
  public:
  void func(char ch) { cout << "carácter = " << ch << endl; }
};
 
class B : public A {      // Clase derivada
  public:
  void func(char* str) { cout << "cadena = " << str << endl; }
  using A::func;          // declaración using, sobrecarga B::func()
};
 
int main() {              // =========================
   B b;                   // instancia B
   b.func('c');           // M.2:  Invoca A::func()
   b.func("c");           // M.3:  Invoca B::func()
   return 0;
}

Salida:

Carácter = c
Cadena = c


Observe como el efecto de la declaración using en la definición de la subclase, hace que sea visible la versión heredada de func, de forma que son visibles simultáneamente ambas versiones (privativa y heredada 4.11.2b), lo que tiene un efecto análogo a sobrecargar la función privativa. Esto hace posible que en M.2 y M.3 se invoquen las versiones correspondientes según el tipo de argumento utilizado.

Ejemplo-3

Se trata de una variación del ejemplo anterior para demostrar que, como hemos señalado anteriormente ( 4.1.11c), aunque sean visibles dos miembros dentro del mismo objeto con identificadores iguales, los nombres del subespacio local tienen precedencia sobre los heredados (se dice que los miembros privativos dominan sobre los heredados 4.11.2c).

#include <iostream>
using namespace std;

class B {                // superclase
  public:
  void fun(int x) { cout << "Superclase: " << x << endl; }
};

class D : public B {     // subclase
  public:
  void fun (int x) { cout << "Subclase: " << x << endl; }
  using B::fun;          // L.12
};

int main() {         // ==============
  D d;
  d.fun(10);         // M.2
  d.B::fun(20);      // M.3
  return 0;
}

Salida:

Subclase: 10
Superclase: 20

Comentario:

Como se demostró en el ejemplo anterior, gracias a la declaración using de L.12 en el espacio de la subclase D, son visibles ahora dos versiones (privativa y heredada) del método fun. Es importante señalar que, a pesar que las "firmas" de ambas funciones son idénticas, no se produce ningún error de compilación por declaración múltiple en D.

La primera salida demuestra que la invocación directa de fun, utiliza la versión privativa (que podríamos considerar está en el espacio "local" de D). En M.3 comprobamos que la utilización de la versión heredada (que podríamos considerar existe en un "subespacio" del anterior), exige el uso de un identificador cualificado para la función ( 4.1.11c).

Nota: esta imagen mental de "subespacios", que contienen los miembros heredados dentro del espacio definido por la clase, se refiere a una compartimentación realizada automáticamente por el compilador que no está permitida al programador, y que por lo demás, sigue las reglas de los subespacios estándar C++.

En efecto: a la luz de los expuesto en este ejemplo, podría argumentarse que la ausencia de error de compilación, a pesar que ambas funciones tienen la misma firma, es que pertenecen a subespacios distintos, y el argumento sería correcto. A su vez, en el ejemplo anterior, vimos que la utilización de los mecanismos de sobrecarga entre estos "subespacios" exige la utilización de la declaración using, ya que de otro modo, C++ no permite la sobrecarga entre espacios de nombres distintos ( 4.4.1a).

  Inicio.


[1]  En efecto: una clase define un ámbito, lo mismo que un espacio de nombres, de forma que una clase puede ser definida como un subespacio de nombres con algunos añadidos (como se manejan sus elementos) y alguna diferencia (la clase es un ámbito cerrado mientras que el subespacio es abierto).

Nota (de nota :-)  como casi siempre, también aquí tiene C++ su pasadizo secreto... Al tratar de las clases amigas ( 4.11.2a1), veremos que el especificador friend es en cierta forma, un mecanismo para ampliar el ámbito de la clase.

[2]  Observe que decimos se "utiliza", no que se "declare". Como hemos señalado al principio, esto último no está permitido en el interior de una clase.