Sei sulla pagina 1di 25

4.12.

2a Clases genricas II
1 Las clases genricas y la organizacin del cdigo
Para la organizacin de las plantillas de clases genricas pueden utilizarse las mismas pautas
indicada para las funciones genricas (
4.12.1b):

Fichero nico

Fichero combinado

Ficheros mltiples (compilacin separada)

.
.
.

1.1 El mtodo de fichero nico exige que el compilador encuentre la definicin de las plantillas
antes de su uso en cada unidad de compilacin. Presenta el inconveniente de que las plantillas
definidas en un mdulo no son visibles desde los dems. Responde al siguiente esquema:
template<T> class C1 { // declaracin/definicin
...
// definicion...
};
...
int main() {
...
C1<char> c1;
...
}

// uso de una instancia

1.2 El mtodo de fichero combinado consiste en situar en un fichero de cabecera las


definiciones de todas las clases genricas (y de sus funciones-miembro). Posteriormente este
fichero es incluido en todos los mdulos que utilicen la clase genrica. La situacin queda
esquematizada como sigue:

// cabecera.h

// main.cpp
#include <cabecera.h>

#ifndef PLT_1

...

#define PLT_1

int main() {

template<T> class C1 { //
declaracin

...
C1<char> c1;

...

// definicion...
...

};

#endif
...

// PLT_1

// modulo-1.cpp

// modulo-2.cpp

#include <cabecera.h>

#include <cabecera.h>

...

...

{
...

...

C1<char*> ch;

C1<X> cx;

...

...

De esta forma se garantiza que la plantilla pueda ser utilizada por ms de una unidad de
compilacin.

1.3 El mtodo de compilacin separada consiste en incluir en un fichero de cabecera las


declaraciones de las clases genricas, e incluir este fichero en todos los mdulos de compilacin
mediante la oportuna directiva #include. Las definiciones off-line de todos los mtodos van
precedidas de la directiva export, y se incluyen en un fichero que es compilado separadamente:

// cabecera.h

// definiciones.cpp
#include <cabecera.h>

#ifndef C_1

...

#define C_1

export template<class T> void


C1<T>::f1() {

template<T> class C1 { //
declaracin

...

// definicin 1

void C1<T>::f1();

void C1<T>::f2();

export template<class T> void


C1<T>::f2() {

...

...

// definicin 2

};
#endif
...

}
...
// C_1

// modulo-1.cpp

// modulo-2.cpp

#include <cabecera.h>

#include <cabecera.h>

...

...

{
...

...

C1<int> n1;

C1<X> cx;

...

...

}
...

}
...

Desafortunadamente, a la fecha ninguno de los compiladores que hemos utilizado para plataformas
PC incluye soporte para el especificadorexport en estas condiciones [1].

2 El especificador friend con funciones genricas


Recordemos que friend es un especificador de acceso, utilizado para declarar que ciertas
funciones o clases (que denominamos "invitadas") tienen acceso al mbito de ciertas clases
"anfitrionas" (
4.11.2a1). En el captulo correspondiente se explicaron sus caractersticas y las
indicaciones pertinentes sobre su uso. Sin embargo, cuando las funciones friend de una clase son
a su vez funciones genricas ("templates"), su utilizacin requiere algunas cauciones particulares.
Para empezar, debemos recordar que los "invitados" (friend) de una clase ordinaria o genrica
(plantilla), pueden ser: una funcin, o una clase ordinarias (no genricas); una funcin genrica
("template); una clase genrica; la especializacin de una funcin genrica, y la especializacin de
una clase genrica [2].
Recordemos tambin, que cuando la clase anfitriona es una clase genrica (plantilla), sus
propiedades y mtodos son a su vez clases genricas (
4.12.2). Como es usual, en estos casos
la declaracin de tales mtodos debe hacerse en el cuerpo de la clase genrica, mientras que la
definicin puede hacerse dentro (on-line) o fuera (off-line), a continuacin de la anterior. Es el caso
esquematizado a continuacin, en el que suponemos una clase Array destinada a almacenar
matrices unidimensionales de elementos de tipo numrico (int, float, long, float*, etc).
template <class T> class Array {
public:
Array();
Array& operator= (const Array&);

// clase genrica
// constructor por defecto
// operador de asignacin

...
};

template<class T> Array<T>::Array()


/* alguna funcin de T ... */
}

// definicion del constructor

template<class T> Array<T>& Array<T>::operator= (const Array<T>& ar) {


/* alguna funcin de T ... */
...
return *this;
}
En este ejemplo, tanto el constructor por defecto como el operador de asignacin, se han definido
off-line a continuacin del cuerpo de la clase; exactamente igual que en una clase ordinaria,
aunque teniendo en cuenta que en esta ocasin, ambos mtodos son a su vez funciones
genricas. En realidad, cualquiera que sea la forma de su definicin (on-line u off-line), ambos
mtodos estn en el mbito de la clase y pertenecen a ella. Cuando se obtiene una especializacin
de la clase genrica. Por ejemplo:
Array <float> a1;
el compilador conoce el tipo de argumento <T> que deber utilizar para construir la especializacin
de cada mtodo de la clase.

Por su parte, la declaracin de funciones friend genricas, debe hacerse en el cuerpo de la clase
anfitriona, pero la definicin debe hacerse antesdel cuerpo de dicha clase. A su vez, los prototipos
situados en el cuerpo de la clase, adems del especificador friend, deben incluir un indicador del
tipo de especializacin que se pretende, mediante el indicador <T> situado entre el nombre de la
funcin y la lista de argumentos (lo hemos denominado instanciacin explcita especfica
4.12.1). Es el caso esquematizado a continuacin relativo a la clase anterior, en el que aadimos
dos funciones amigas, que son a su vez funciones genricas.
template<class T> Array<T> operator+ (const Array<T>& ar1, const
Array<T>& ar2) {
// versin sobrecargada de operator+() para concatenar rrays
Array<T> ax(len1 + len2);
// Array auxiliar para el resultado
...
return ax;
}
template<class T> std::ostream& operator<< (std::ostream& stream, const
Array<T> arr) {
// versin sobrecargada de operador<<() para salida estndar
...
return stream;
}
template <class T> class Array {
// clase genrica
friend Array<T> operator+ <T> (const Array<T>&, const Array<T>&);
/ L1.

friend std::ostream& operator<< <T> (std::ostream&, const


Array<T>); // L.2
public:
Array();
Array& operator= (const Array&);
...

// constructor por defecto


// operador de asignacin

};

...
La sentencia L1 declara el operador suma para los objetos de la clase. Observe que de las dos
opciones disponibles para este operador, se ha elegido la de funcin externa con dos argumentos (
4.9.18); la otra alternativa nos habra conducido a una funcin-miembro con un argumento, con
lo que estaramos en el supuesto del ejemplo anterior
. Por su parte, L2 es una versin
sobrecargada de la funcin operator<< , definida como una funcin externa, que facilitar la
salida para objetos tipo Array.
Aunque los prototipos de ambas funciones-operador estn en el cuerpo de la clase (para sealar
que son funciones invitadas), en realidad ninguna de ellas pertenece al mbito de la clase (en este
caso pertenecen al mbito global del fichero). La consecuencia es que, aunque sus definiciones
contengan referencias a un tipo T, el compilador no puede deducir que el tipo utilizado para
instanciar estas funciones sea justamente el utilizado en una especializacin concreta de la clase
(piense que estas funciones pueden ser utilizadas desde espacios distintos de la clase Array). Por
esta razn hay que recurrir a la instanciacin explcita especfica; para que el compilador pueda
conocer el tipo de argumento que se utilizar para instanciar la funcin genrica correspondiente a
cada especializacin de la clase Array.

4.12.2b Clases genricas en la Librera Estndar


1 Sinopsis
Hemos sealado (
4.12) que las plantillas fueron introducidas en C++ para dar soporte a
determinadas tcnicas utilizadas en la Librera Estndar; de hecho, la STL (
5.1) est
constituida casi exclusivamente por plantillas. A continuacin se relacionan algunas de las ms
importantes [1].

9.1 basic_string una plantilla para utilizar entidades como secuencias de caracteres. Est
definida en el fichero de cabecera <string>, y responde a la siguiente declaracin:
template <class charT, class traits = char_traits<charT>,
class Allocator = allocator<charT> >
class basic_string;

Como puede verse, acepta tres argumentos, de los que dos tienen valores por defecto. Existen dos
especializaciones que tienen nombres especficos. En concreto si charT es char la
especializacin se denomina string, y wstring si charT es wchar_t (
2.2.1a1). Esto se hace
mediante sendos typedef:

typedef basic_string <char> string;


typedef basic_string <wchar_t> wstring;
de forma que las dos sentencias siguientes son equivalentes:
basic_string<char> s1;
string s1;

9.2 vector. Un vector es la versin STL de una matriz dinmica de una dimensin. Las
instancias de esta clase genrica conforman unasecuencia (un tipo de contenedor
5.1.1). La
clase dispone de acceso aleatorio a sus elementos y de un mecanismo muy eficiente de insercin y
borrado de elementos al final. Aunque tambin pueden insertarse y borrarse elementos en medio.
Est definida en el fichero <vector> y responde a la siguiente declaracin:
template <class T, class Allocator = allocator<T> > class vector;
Una especializacin concreta de esta plantilla, cuando el tipo T es un booleano (
3.2.1b) vector<bool>, es considerada por la STL como un caso especial, con un cdigo
optimizado que incluye mtodos no disponibles para el resto de las instancias (los
mtodos flip y swap).

9.3 list. Las instancias de esta plantilla conforman tambin una secuencia
que dispone de
mecanismos muy eficientes para insertar y eliminar elementos en cualquier punto. Est definida en
el fichero <list> y responde a la siguiente declaracin:
template <class T, class Allocator = allocator<T> > class list;

9.4 map. Esta plantilla est definida en el fichero <map>, y tiene la siguiente declaracin:
template <class Key, class T, class Compare = less<Key>
class Allocator = allocator<pair<const Key, T>> > class map;
Las instancias de esta plantilla conforman un contenedor asociativo (una clase de contenedor
5.1.1) que permite almacenar y acceder objetos de tipo T indexados mediante una nica clave Key,
disponiendo de mecanismos muy eficientes de insercin y borrado.

9.5 auto_ptr. Esta plantilla genera punteros "inteligentes" que destruyen automticamente el
objeto sealado cuando ellos mismos salen de mbito. Su definicin est en el fichero <memory>,
y su declaracin es:
template <class X> class auto_ptr;

En realidad esta plantilla se ha incluido en la Librera Estndar para ayudar a resolver el problema
de los objetos persistentes, creados con el operador new, cuyo referente (el puntero que los
seala) es un objeto automtico que puede ser destruido inadvertidamente al salir de mbito. Lo
que puede ocurrir en el los procesos de lanzamiento y captura de excepciones (
1.6).

4.12.2b1 Punteros inteligentes


1 Prembulo
Al presentar el operador new (
4.9.20), se insisti en el peligro que supone la permanencia de
los objetos creados, junto al hecho de que sus referentes (los punteros que sealan a esos
objetos), sean a su vez objetos automticos que pueden ser destruidos inadvertidamente, tanto por
"olvidos" del programador como por accin del mecanismo de excepciones.
Para evitar este tipo de problemas se ha incluido en la Librera Estndar STL (
5.1) una plantilla
especial denominada auto_ptr, con la que generar punteros inteligentes. Esto es: punteros que
destruyen automticamente el objeto sealado cuando ellos mismos salen de mbito.
Nota: en el captulo dedicado a objetos-puntero (
4.13.3), puede encontrarse una
descripcin ms detallada de los principios tericos utilizados.
Esta plantilla, definida en el fichero <memory> [1], responde a la siguiente declaracin:
template <class X> class auto_ptr;

2 Descripcin
La plantilla auto_ptr puede instanciar un objeto que contenga un valor nulo, o la direccin de un
objeto creado con new; encargndose de destruirlo automticamente, cuando l mismo sea
destruido (por ejemplo por salir de mbito).
Hay que advertir que estos objetos no son punteros en el sentido formal. Es decir, en la
instanciacin concreta:
auto_ptr<tipoX> obj;
obj no es un objeto tipoX* (puntero a objeto tipo X), sino auto_ptr<tipoX>. Sin embargo,
funcionalmente se comportan en parte como punteros [2], en el sentido que pueden contener la
direccin de un objeto, y pueden aplicrsele los operadores de indireccin * y selector indirecto ->.
En otros aspectos. Por ejemplo, frente al operador de asignacin, su comportamiento es un tanto
singular.
La base de su funcionamiento consiste en que los objetos automticos (como estos punteros)
creados en un mbito, son automticamente destruidos por el compilador cuando salen de l. Para
esto el compilador invoca los destructores adecuados. En caso de objetos auto_ptr, el destructor
se define de forma que destruya tambin el objeto sealado.
Este comportamiento permite que los objetos creados con new puedan coexistir de forma segura
con el mecanismo de excepciones, ya que el proceso de limpieza de la pila ("Stack unwindig
1.6) garantiza la destruccin de los objetos persistentes involucrados.
Para que este comportamiento sea eficaz, la clase auto_ptr contiene un indicador de propiedad.
De forma que los objetos sealados se consideran una especie de "propiedad" del puntero. Un
objeto puede ser sealado de forma segura por un puntero auto_ptr, que detenta entonces su

"propiedad"; pero la copia de este puntero no solo produce un clon del anterior; tambin le
transfiere la propiedad del objeto sealado en caso que el primero no fuese un puntero nulo.
Nota: existe una tcnica similar, que tambin persigue la destruccin automtica de los objetos
referenciados cuando las referencias salen de mbito, que utiliza un procedimiento distinto; se
denomina de contador de referencias ("Reference counting"). En esta tcnica se ha eliminado
el concepto de indicador de propiedad, de forma que varios objetos-puntero pueden sealar al
mismo objeto persistente. Para evitar que cuando uno de estos objetos salga de mbito
destruya el objeto, si existen otros que todava lo sealan (lo que producira punteros
descolgados), el objeto referenciado mantiene un contador de los punteros que lo referencian.
Cuando un objeto-puntero sale de mbito, su destructor consulta el contador y lo disminuye en
una unidad. Solo si el contador llega a cero, el objeto referenciado es destruido.

3 Interfaz
La plantilla auto_ptr tiene la siguiente interfaz:
template <class X> class auto_ptr {
template <class Y> class auto_ptr_ref { // puntero subyacente (privado)
public:
const auto_ptr<Y>& p;
auto_ptr_ref (const auto_ptr<Y>&);
};
public:
typedef X element_type;
explicit auto_ptr (X* = 0) throw();
// constructor explcito por
defecto
auto_ptr (const auto_ptr<X>&) throw(); // constructor-copia
template <class Y>
// constructor
auto_ptr (const auto_ptr<Y>&) throw();
auto_ptr<X>& operator=(const auto_ptr<X>&) throw(); // funcin-operador
template <class Y>
// funcin operador
auto_ptr<X>& operator= (const auto_ptr<Y>&) throw();
~auto_ptr ();
// destructor
X& operator* () const throw();
// funcin-operador
X* operator-> () const throw();
// funcin-operador
X* get () const throw();
// mtodo
X* release () throw();
// mtodo
void reset (X*=0) throw();
// mtodo
auto_ptr(auto_ptr_ref<X>) throw();
// constructor
template <class Y>
operator auto_ptr_ref<Y>() throw();
template <class Y>
operator auto_ptr<Y>() throw();
};

3.1 Descripcin
Lo primero que sorprende, es que esta plantilla tiene un primer (y nico), miembro
privado auto_ptr_ref, que es a su vez otra plantilla. Este miembro tiene a su vez dos miembros
pblicos: una referencia a un objeto auto_ptr, y un constructor que recibe como argumento una
referencia a un objeto tipo auto_ptr.

El objeto de este miembro es almacenar una referencia a un objeto auto_ptr que solo pueda ser
construida dentro de un objeto auto_ptr utilizando una referencia a auto_ptr. El objeto
referenciado por este miembro se denomina indicador de propiedad o puntero subyacente, y se
utiliza en el mecanismo de copia de objetos de esta clase, en el que se transfieren tambin los
"derechos de propiedad" sobre el objeto sealado.
Adems del anterior, la clase tiene un destructor, cuatro constructores (uno de ellos por defecto y
otro explcito) y tres funciones miembro. Tambin se definen las siguientes funciones operador para
que puedan ser aplicadas con miembros de la clase: operador de indireccin *; selector indirecto >; el de asignacin =.

3.2 Miembros
Aparte del indicador de propiedad ya mencionado, la clase dispone de los siguientes miembros
pblicos:
Constructores y destructores:

explicit auto_ptr (X* p = 0) throw();


Este constructor explicit (
4.11.2d1) se encarga de construir un objeto de la
clase auto_ptr<X>, inicializando el valor sealado a p, y adquiriendo la propiedad del
objeto. Por supuesto p debe sealar a un objeto de la clase X, o de una subclase para la
que est definida y sea accesible la operacin delete p. p puede ser tambin un puntero
nulo (
4.2.1).

auto_ptr (const auto_ptr<X>& a) throw();


template <class Y> auto_ptr (const auto_ptr<Y>& a) throw();
Estos constructores-copia construyen un objeto de la clase auto_ptr<X>, y copian el
argumento a (que es la referencia a un objetoauto_ptr) a *this (el nuevo objeto). Si el
objeto a posee el puntero subyacente, entonces el nuevo objeto se transforma en el nuevo
propietario.

auto_ptr (const auto_ptr_ref<X> r) throw();


Construye un objeto auto_ptr a partir del miembro privado auto_ptr_ref.

~auto_ptr ();
Destructor de la clase que incluye una llamada a delete para destruir el objeto sealada
por el puntero subyacente.

Observe que los constructores se han declarado de forma que no puedan lanzarse excepciones
desde su interior (
1.6.4).

Operadores:

auto_ptr<X>& operator= (const auto_ptr<X>& a);


template <class Y> auto_ptr<X>& operator= (const auto_ptr<Y>& a);
Estas funciones-operador (una de ellas genrica) definen versiones particulares del
operador de asignacin = para miembros de la claseauto_ptr. Copian el argumento a a
*this. Si a es propietario del puntero subyacente, entonces *this se convierte en el nuevo
propietario. Si *this ya era el propietario de un puntero, entonces el objeto asociado es
destruido antes de asignrsele la nueva propiedad.
Tienen las definiciones siguientes:
auto_ptr<X>& operator= (auto_ptr<X>& a) throw() {
reset(a.release());
return *this;
}
template <class Y>
auto_ptr<X>& operator= (auto_ptr<Y>& a) throw() {
reset(a.release());
return *this;
}

X& operator* () const;


Esta funcin sobrecarga el operador de indireccin * para objetos auto_ptr. Aplicado a un
objeto devuelve una referencia al objeto sealado por el puntero subyacente.

X* operator-> () const;
Esta funcin sobrecarga el selector indirecto de miembro -> para objetos de la clase. La
aplicacin de este opeador a un objeto auto_ptrdevuelve el puntero subyacente.

template <class Y> operator auto_ptr_ref<Y> ();


Operador que construye objetos de la sub-clase. Construye un objeto auto_ptr_ref a
partir de *this.

template <class Y> operator auto_ptr<Y> ();


Operador que construye un nuevo objeto auto_ptr a partir del puntero subyacente del
objeto sealado por *this. Esta funcin realiza una invocacin al mtodo release
, de
forma que a partir de entonces el objeto *this no es propietario del puntero. La funcin
devuelve un nuevo objeto auto_ptr.

Mtodos:

X* get () const;

Este mtodo devuelve el puntero subyacente.

X* release();
Deshace la propiedad del objeto sobre el puntero subyacente y devuelve su valor.

void reset(X* p)
Este mtodo es en cierta forma complementario del anterior. Establece el valor del puntero
subyacente del objeto al valor p suministrado, borrando el valor anterior.

4 Utilizacin
Generalmente la utilizacin prctica de objetos auto_ptr se limita a su uso como punteros de los
objetos creados con new. Es rara la utilizacin explcita de sus miembros pblicos, y la asignacin
explcita que podamos llamar clsica:
class C {...};
...
auto_ptr<C> ptr = new C;
presenta alguna dificultad. Como se ha sealado
, los miembros de la asignacin no son del
mismo tipo, por lo que el intento anterior produce un error de compilacin: Cannot convert 'C
*' to 'auto_ptr<C>'. A esto se suma que los compiladores actuales no permiten un modelado
con plantillas, de forma que tampoco funciona un intento del tipo:
auto_ptr<C> ptr = static_cast<auto_ptr<C>> (new tipoC);

En la prctica, para realizar la asignacin debe utilizarse una invocacin implcita o explcita al
constructor de auto_ptr (
4.11.2d3) con el argumento adecuado. De esta forma el constructor se
encarga de iniciar el puntero al objeto:
auto_ptr<C> ptr(new C);
auto_ptr<C> ptr = auto_ptr<C>(new C);

// Ok. invocacin implcita


// Ok. invocacin explcita

Ejemplo
#include <iostream>
#include <memory>
using namespace std;

// para usar auto_ptr

struct C {
// una clase
int mi;
int getmi() { return mi; }
C (int i = 0) : mi(i) { } // constructor por defecto
};
int main () {
// ===================
auto_ptr<C> pi = auto_ptr<C>(new C(12345));
// M.1
cout << "1 Valor mi: " << (*pi).mi << endl;
// M.2
cout << "2 Valor mi: " << pi->mi << endl;
// M.3

cout << "3 Valor mi: " << (*pi).getmi() << endl; // M.4
cout << "4 Valor mi: " << pi->getmi() << endl;
// M.5
cout << "5 Valor mi: " << pi.get() << endl;
// M.6
auto_ptr<C> pi2 = pi;
// M.7
//cout << "6 Valor mi: " << pi->getmi() << endl; Error!!
cout << "7 Valor mi: " << pi2->getmi() << endl; // M.9
return 0;

// el objeto es destruido

}
Salida:
1
2
3
4
5
7

Valor
Valor
Valor
Valor
Valor
Valor

mi:
mi:
mi:
mi:
mi:
mi:

12345
12345
12345
12345
00672E18
12345

Comentario
En M.1 se crea un objeto con new al que denominaremos ob (en realidad no tiene nombre). El
puntero resultante de new es aplicado al constructor de auto_ptr, crendose un objeto temporal
que seala a ob y es su propietario. Simultneamente en el lado izquierdo de la asignacin se crea
un puntero inteligente pi de tipo auto_ptr<C>, al que mediante el constructor-copia se le asigna el
contenido del objeto temporal. A partir de este momento pi es el nuevo propietario de ob.
pi acta como puntero del objeto y se le pueden aplicar los operadores de indireccin * y selector
indirecto ->. En M.2 se obtiene el miembro mi, que es pblico, sealando el objeto ob mediante la
indireccin de su puntero. En M.3 se obtienen el mismo resultado con una notacin ms ortodoxa
que utiliza el selector indirecto (
4.2.1f).
La sentencias M.4 y M.5 son similares a las anteriores. Ahora se invoca el mtodo getmi del
objeto, que tambin es pblico, utilizando ambas notaciones.
En M.6 se invoca el mtodo get del objeto pi. El resultado es una direccin de memoria
(recordemos que este mtodo devuelve el puntero subyacente).
La sentencia M.7 define un nuevo objeto auto_ptr pi2, al que se asigna el valor del puntero
anterior. El operador de asignacin se encarga de transferirle la propiedad del objeto ob, de forma
que en adelante el puntero inicial pi se convierte en un puntero nulo.
La sentencia M.8 produce un error de runtime, consecuencia del intento de deferenciar un puntero
nulo (
4.2.1). En cambio, un intento anlogo en M.9 con el nuevo propietario pi2, proporciona el
resultado apetecido.
En la sentencia M.9 en que ocurre el abandono del mbito de main, se produce la destruccin de
todos los objetos automticos, incluyendo el puntero pi2 y el objeto asociado ob.

Tema relacionado

Control de recursos (

4.1.5a)

4.13 Tecnicismos
1 Introduccin
Generalmente el conocimiento de un lenguaje de programacin comienza con el estudio de la
finalidad (descripcin) y utilizacin (sintaxis) de los elementos que lo componen. Sin embargo, en el
caso de C++, en el que la Librera Estndar est indisolublemente unida al lenguaje, este estudio
debe incluir una segunda fase dedicada a la Librera; por lo dems muy importante, ya que sin su
existencia, el lenguaje en s carecera de utilidad prctica.
Puesto que la Librera Estndar es una extensin del C++, damos por supuesto que su estudio
debe efectuarse despus de conocer el lenguaje propiamente dicho, y podra pensarse que con
este conocimiento ya estamos perfectamente capacitados para abordar nuestras propias
aplicaciones. Sin embargo C++ es complejo y extenso; para manejarlo con un mnimo de soltura se
requiere mucha prctica, as como el conocimiento de ciertas tcnicas, modos de hacer y
precauciones que facilitan la andadura y evitan caer en los mltiples peligros que acechan en el
arcn.
Cada lenguaje de programacin tiene caractersticas propias; su propio estilo para el abordaje y
solucin de problemas. Estas caractersticas, junto con el tiempo y la experiencia, han consagrado
una serie de mtodos, algoritmos, y formas especficas de solucin de ciertos problemas, cuyo
conocimiento condensa buena parte de la sabidura prctica de los expertos, y constituye lo que
algn autor ha definido como la tica particular de cada lenguaje de programacin. Por ser el
lenguaje C++ tan extenso y general, la coleccin de estos tecnicismos es tambin especialmente
extensa, y su conocimiento especialmente importante para el programador C++ que desee un
mnimo de soltura.
Dedicaremos esta seccin a la descripcin de algunas de estas tcnicas cuya utilidad no es solo
prctica como apuntbamos antes [1], sino tambin terica y desde luego didctica. Cualquier
estudioso del C++ que desee un conocimiento ms que superficial del lenguaje, en especial de la
Librera Estndar, descubrir que muchos de los sofisticados algoritmos que se utilizan en ella son
en realidad una aplicacin refinada de ciertas tcnicas ya conocidas y estudiadas previamente. Sin
una introduccin y explicacin adecuadas es virtualmente imposible que una persona normal
pueda descubrir por s mismo "porqu diablos" son as muchas de las construcciones de la citada
librera.
Nota: en la literatura inglesa, estas tcnicas son conocidas como "Idioms". Desgraciadamente
no existe en espaol una sola palabra exactamente equivalente. Significa tecnicismo, tcnica o
modo particular de hacer las cosas en determinado ambiente o profesin. En el lenguaje
natural los idioms corresponderan a las frases hechas; formas de expresar determinadas
ideas que son propias de cada idioma [2].

Vaya por delante, que estas materias no suelen encontrarse en los textos generales o
introductorios sobre el lenguaje. Son ms propias de textos sobre programacin o tcnicas
avanzadas de C++. Sin embargo, se incluyen aqu porque las considero de alto inters didctico.
Muchas de ellas se han tomado del magnfico libro de Coplien ya sealado [ 2], donde se
presentaron algunas por primera vez, intentando simplificar la presentacin ya de por s bastante
difcil en algunos casos. He incluido tambin algunas consideraciones y comentarios que considero

relevantes, o que resultaron en su momento motivo de duda o cavilacin para m. Estoy seguro
que aclararn ms de una idea al que tenga paciencia para leerlas.

4.13.1 Reglas de buena prctica


"Remember that the aims are simplicity, regularity, and performance. You don't
prove that you are clever by producing the most complicated code". Bjarne
Stroustrup en "The State of the Language" una entrevista con Danny Kalev en
DevX.com

1 Prembulo
En C++ existen una serie de reglas y consejos de buena prctica que ayudan a escribir mejores
programas con menos errores. Hemos sealado que la "Biblia" del C++ [1] contiene al final de
cada captulo una serie de consejos ("Advice") que constituyen en s mismos todo un compendio
de programacin C++ (las ms de las veces incluyen cosas que no deben hacerse).
Scott Meyers [2] ha escrito dos libros muy populares sobre C++: "Effective C++" y "More Effective
C++"; ambos publicados por Addison-Wesley, en los que se recogen una serie de
recomendaciones y consejos de buena prctica. Algunas de ellas consideran tan importantes, que
el compilador GNU cpp dispone de una opcin especial (-Weffc++) que advierte cuando el cdigo
las contradice. Son las siguientes:

Definir un constructor-copia ( 4.11.2d4) y una versin sobrecargada del operador de


asignacin ( 4.9.18a) para clases que utilicen memoria dinmica (persistente
1.3.2).
Preferir inicializacin antes que asignacin en los constructores (
4.11.2d3).
Hacer virtuales ( 4.11.8a) los destructores ( 4.11.2d2) en las superclases, en especial
si los constructores de las subclases asignan memoria con new.
Cuando se sobrecargue el operador de asignacin = ( 4.9.18a), disponerlo de forma que
devuelva una referencia a *this ( 4.11.6).
No devolver una referencia ( 4.2.3) cuando se deba devolver un objeto.
Cuando se sobrecarguen los operadores de incremento ++ y decremento -- ( 4.9.1),
distinguir entre las formas "Pre" y "Post" ( 4.9.18c).
No sobrecargar nunca los operadores coma , ( 4.9.5); Y lgico && (AND
4.9.8) y O
lgico || (OR
4.9.8).

2 Diseo cannico
Como regla general, no olvide que la definicin de cualquier clase C que no sea trivial debe
contener al menos:
Un constructor por defecto: C::C();
Un constructor-copia: C::C(const C&);
Un operador de asignacin para miembros de la clase: C& C::operator=(const C&);
Un destructor: C::~C();
Cuando todos estos mtodos son pblicos, que es lo ms frecuente, se dice que la clase adopta
forma o diseo cannico. Pero son posibles toda clase de variantes para fines especficos. Por
ejemplo: que los objetos no puedan ser libremente copiados, creados o asignados.

4.13.2 Clases dentro de clases


1 Prembulo
Es frecuente que, en las fase inicial del abordaje de un problema, nos encontremos frente a
disyuntivas en las que debemos elegir entre dos o ms soluciones posibles. Esta circunstancia,
que no es exclusiva del mbito de la programacin de ordenadores, sino de muchas otras
situaciones de la vida cotidiana; es conocida en el mundo del software por su acrnimo ingls
TMTOWTDI ("There's more than one way to do it"). La diversidad de soluciones puede referirse
tanto al planteamiento estratgico (global) del problema, como a cuestiones de detalle, e incluso a
los recursos sintcticos utilizados. Por ejemplo, un for puede ser sustituido por un while y un
contador. En este sentido el lenguaje C++ es lo suficientemente rico y flexible como para que en la
mayora de los casos puedan plantearse distintas soluciones que, en principio, nos parecen
igualmente vlidas.
Siendo generosos, podramos afirmar que cualquier solucin que sirva (resuelva el problema)
puede considerarse aceptable [1]. Sin embargo, en cada caso hay una que es mejor que las
dems. Quizs sea la de cdigo ms elegante; quizs la de ms fcil lectura; la de cdigo ms
compacto; la de ms rpida ejecucin; la de menor demanda de recursos, Etc.
Nota: al hilo de estas consideraciones, existe una circunstancia adicional que tambin debe
ser tenida en cuenta. Me refiero a las condiciones de desarrollo y mantenimiento del programa.
No pueden mantenerse los mismos considerandos para una aplicacin mediana o pequea,
en la que pueden estar involucrados dos o tres programadores, que en una gran aplicacin en
la que participen varias decenas (lo que es corriente en compaas de software medianas y
grandes).

Puede que elegir la alternativa ms adecuada sea cuestin de tcnica, inspiracin, o sencillamente
experiencia. Es difcil dar recetas generales, y en mi opinin, solo los grandes maestros pueden
hablar con autntica autoridad sobre el tema [3]. No obstante, admitido lo anterior, y sin pretender
por tanto que mis palabras deban ser tomadas como el orculo, a continuacin incluyo algunos
consejos y consideraciones que en su da constituyeron para m motivo de meditacin. Uno de los
conceptos que ms me inquietaba y que, he de reconocer, ms me cost entender del todo, era
relativo a la utilizacin de clases. En concreto la utilizacin de clases dentro de clases y sus
diversas posibilidades de uso.

2 Miembros de clases anidadas "versus" miembros de clases externas


En el captulo dedicado a la construccin de clases (
4.11.2a) sealamos que los miembros de
clases pueden ser otras clases (clases anidadas) u objetos (instancias de otras clases). La
situacin puede esquematizarse como sigue:
2.1 Clases independientes:
class A { ... };
class B {
A a1, a2, a3;
...

// Clases independientes
// Ok. B contiene objetos tipo A

};
Este diseo suele denominarse de contencin ("containtment"). Se dice que la clase B contiene
una A ("'has-a' relationship" en la literatura inglesa). Es tpico cuando se desea ampliar la
funcionalidad de una clase contenedora (B) utilizando en su interior objetos de otra clase A. En
estos casos, si adems se desea que algunas de las propiedades o mtodos pblicos de la clase
interior A, sean accesible desde el exterior de un objeto tipo-B, se suelen implementar (en B)
los acessors y mutators adecuados (
E4.11), lo que se denomina delegacin ("delegation"). Por
esta razn este diseo es conocido tambin como de contencin/delegacin
("contention/delegation).
2.2 Clases anidadas
class B {
class A { ... };

// clase anidada

A a1, a2, a3;

// Ok. B contiene objetos tipo A

...
};
Esta sera otra forma de contencin/delegacin si estamos seguros de que los objetos de la
clase A nunca van a ser utilizados fuera de la clase B.

2.3 Por supuesto tambin puede ocurrir que la clase B no necesite contener ningn objeto de A:
class B {
class A { ... };

// clase anidada

...
};
Este diseo equivaldra en realidad a definir un espacio de nombres (namespace) en el interior de
la clase contenedora.

2.4 La opcin 2.2


clase anidada:

puede ser llevada al extremo, cuando solo necesitamos un objeto de la

class B {
class { x; y; z; ... } a;
...

};
Esta situacin podra ser equivalente a trasladar todas las propiedades y mtodos de A a la B:
class B {
x; y; z;
...
};
sin embargo, un principio de encapsulamiento y claridad conceptual podran justificar la frmula
2.2
, manteniendo la definicin de la clase Acomo ente independiente, aunque contenido en B.
2.5 Otra alternativa a considerar en este ltimo caso, sera la proporcionada por la herencia
(teniendo en cuenta las excepciones correspondientes
4.11.2b):
class A { x; y; z; ... };
class B : A { ... };
Esta ltima forma es conocida como "subclassing", que quizs podramos traducir aqu por
derivacin.
Aunque todas las combinaciones son posibles, debemos sealar aqu que en las aplicaciones
reales son mucho ms frecuentes los diseos 2.1(clases independientes) y 2.5 (herencia simple
o mltiple), mientras que los diseos 2.2, 2.3 y 2.4 son menos utilizados.

2.6 Volviendo a las dos formas bsicas, clases independientes o anidadas, aunque ambas
soluciones son tericamente posibles. La primera es ms flexible que la segunda.
En principio la utilizacin de 2.1
siguientes:

est justificada cuando se dan algunas de las condiciones

Deben utilizarse varias instancias a1, a2, ... etc. de la misma clase.
Los objetos de la clase A, o de clases derivadas de ella, tienen justificacin por s mismos.
Por ejemplo, pueden ser utilizados para otros usos con independencia de que sean
componentes de B. Observe que estos "otros usos" pueden ser incluso formar parte de
otras clasesC, D, Etc. y que los objetos tipo-A nunca sean instanciados directamente. De
hecho, en mltiples ocasiones la clase A suele ser abstracta ( 4.11.8c).
La utilizacin de 2.2
se justifica cuando:

Deben utilizarse varias instancias a1, a2, ... etc. de la misma clase.
Los objetos de la clase A no pueden tener existencia independiente ms que como
miembros de la clase contenedora B, sin participar en ninguna otra.

El lector habr observado que las opciones consideradas hasta ahora, han incluido instancias de

clases como miembros de clases. Son las opciones ya mencionadas, que esquematizamos a
continuacin:

Clases independientes

Clases anidadas

class A { ... };

class B {

class B {

class A { ... };

A a1, a2, a3;

A a1, a2, a3;

...

...

};

};

En realidad, el hecho de que la clase A sea independiente de B, o anidada en ella, es una cuestin
de detalle que solo afecta al mbito en que se define A; en uno y otro caso los
miembros a1, a2, a3 de B siguen siendo objetos de tipo-A.
Como sealaba al principio, uno de los conceptos que ms me inquietaba se refera a la utilizacin
de clases dentro de clases y sus diversas posibilidades de uso. Por ejemplo, me asaltaba el
siguiente tipo de cuestin: Supongamos una clase A contenida en otra B. A continuacin, instancio
la clase exterior B (contenedora) para obtener un objeto b. Que pasa con la clase A anidada?.
Podra instanciar a su vez diversos objetos a1, a2, a3, etc dentro del objeto b?. Se formara as
una especie de submundo dentro de dicho objeto?. El creador del lenguaje dice a este respecto [4]:
"La simple declaracin de una clase anidada en otra no supone que la clase contenedora incluya
un objeto de la clase interior. El anidamiento solo indica mbito ("scoping") no inclusin de subobjetos".
Nota: la Librera Estndar C++, que en s misma es un compendio de programacin
extraordinariamente avanzado, ofrece varios ejemplos de clases anidadas. Por ejemplo, la
clase locale (
5.2.2) cuya definicin es del tipo:
class B {
class A;
...
};
class B::A {
...
};

2.7 La clase anidada est en el mbito de la clase contenedora, y su identificador es local al


mbito de aquella. Ejemplo:
class B {
class A { /* ... */ };
...
};
...
A a;

// Error!! A no es visible

B b;

// Ok.

2.8 Las declaraciones en la clase anidada solo pueden utilizar typenames (


3.2.1e), miembros
estticos (
4.11.7) y enumeradores (
4.8) de la clase contenedora (siempre que sean
pblicos). Por lo dems, aparte de los conocidos (por ejemplo declararlas friend
4.11.2a1), no
existe ningn mecanismo especial de acceso entre los miembros de la clase exterior sobre la clase
anidada o viceversa.
Ejemplo:
class B {
public:
static int counter;
enum { KT1 = 33 };
class A {
int x;
public:
A() : x(KT1 + counter) {}

// Ok.

};
B(): KT2(34), y(0) {}
};
int B::counter = 35;

Observe que la visibilidad de los miembros KT1 y counter desde la clase A no se produce en
cualquier otra circunstancia [5]. Ejemplo:
class B {
public:
static int counter;

enum { KT1 = 33 };
B(): KT2(34), y(0) {}
};
int B::counter = 35;

class A {
int x;
public:
A() : x(KT1 + counter) {}

// Error!!

};

3 Utilizacin de miembros-objeto (instancias de otras clases)


Los miembros de clases pueden ser objetos (instancias de otras clases). Este sera el caso del
ejemplo clsico del automvil que tiene un motor cuatro ruedas, etc. Estos objetos son a su vez
miembros de las clases de los motores de las ruedas, etc. Lo hemos esquematizado en el epgrafe
2.1
.
A continuacin se exponen dos ejemplos que muestran la tcnica de utilizacin. En realidad se
trata de dos versiones del mismo supuesto, con la diferencia de que en el primer caso los
miembros son instancias de una clase externa; correspondera al caso 2.1
. En el segundo, la
clase auxiliar se define dentro del mbito de la clase anfitriona; corresponde al caso 2.2
.
3.1 Ejemplo-1
En el siguiente programa se construye una clase Triangulo destinada a representar tringulos en el
plano. Esta clase tiene tres propiedades, denominadas vrtices, que son los puntos que
caracterizan a cada posible tringulo. A su vez tiene varios mtodos, el principal de ellos es la
funcin getArea, que devuelve el rea delimitada en el tringulo.
Para definir los vrtices se ha utilizado una clase auxiliar, de modo que estas propiedades de la
clase Triangulo son objetos de otra clase denominada Vertice.
A su vez la clase Vertice tiene dos propiedades y dos mtodos principales. Las propiedades son
las coordenadas cartesianas X e Y de cada objeto (que representa un punto en el plano). El
mtodo getxy muestra las coordenadas X, Y del objeto. El mtodo getDist obtiene la distancia
entre el objeto y otro que se pasa como argumento (distancia entre dos puntos).
#include <iostream>
using namespace std;
class Vertice {
float x, y;
public:
friend class Triangulo;
void getxy() {

// L.4: Clase auxiliar


// privados por defecto
// L.7:
// muestra las propiedades x y del vertice

cout << "vertice (" << x << "," << y << ")" << endl;
}
float getDist(const Vertice&);
Vertice(int i =0, int j =0) { x = i; y = j; }
// L.12: constructor
Vertice(const Vertice& rf) { x = rf.x; y = rf.y; } // constructor-copia
}
float Vertice::getDist(const Vertice& rf) {
float dX = rf.x - x, dY = rf.y - y;
float dist = pow(dX * dX + dY * dY, .5);
cout << "distancia: " << dist << endl;
return dist;
}

// L.17:

class Triangulo {
// L.22: Clase contenedora
Vertice vA, vB, vC;
// L.23: miembros-objeto
public:
Triangulo(Vertice v1 = Vertice(),
// L.25: Constructor
Vertice v2 = Vertice(),
Vertice v3 = Vertice() ) {
vA = v1; vB = v2; vC = v3;
}
void getT() { vA.getxy(); vB.getxy(); vC.getxy(); } // L.30:
float getArea();
// declaracion de getArea
};
float Triangulo::getArea() {
// L.32: definicin de getArea
int Ax = vB.x - vA.x; int Ay = vB.y - vA.y;
int Bx = vC.x - vA.x; int By = vC.y - vA.y;
double s = fabs ((Ax * By - Ay * Bx) * .5);
cout << "Area: " << s << endl;
return s;
}
void main() {
// =======================
Vertice origen, v1(1,2), v2(3,4);
// M.1:
origen.getDist(v2);
v1.getDist(v2);
Triangulo T1(v1, v2, Vertice(7,3)), T2; // M.4:
T1.getT(); T1.getArea();
T2.getT(); T2.getArea();
}
Salida:
distancia: 5
distancia: 2.82843
vertice (1,2) vertice (3,4) vertice (7,3)
Area: 5
vertice (0,0) vertice (0,0) vertice (0,0)
Area: 0

Comentario:
En L.7 la clase auxiliar Vertice declara a la clase Triangulo como friend (
4.11.2a1) al
objeto de que desde esta ltima, puedan ser accedidos todos sus miembros (incluso privados). De
esta forma, desde la funcin Triangulo::getArea (L.33 y 34) se puede acceder a las
propiedades x e y (privadas) de los vrtices.
El mtodo Vertice::getDist calcula la distancia entre el objeto sobre el que se aplica la
funcin (sealado por el puntero this
4.11.6) y otro objeto que se pasa por referencia. Se trata
pues del clculo de la distancia entre dos puntos de coordenadas cartesianas conocidas, para lo
que utilizamos el consabido teorema de Pitgoras (L.17).
Nota: el ANSI C dispone en la Librera Estndar de la funcin sqrt() que calcula directamente
la raz cuadrada, pero esta funcin no es estndar en ANSI C++, por lo que usamos pow() que
s lo es.

Adems del mtodo anterior, la clase dispone de un constructor por defecto y un constructor-copia
definidos explcitamente. Observe (L.12) que cuando este constructor es invocado sin argumentos,
se obtiene un punto de coordenadas (0, 0). Este es precisamente el caso del objeto origen
instanciado en la funcin main; este objeto tiene coordenadas (0, 0), y su distancia respecto al
objeto v2 de coordenadas (3,4) es precisamente la primera salida del programa.
Observe que en la definicin de la clase principal Triangulo, los miembros vA, vB y vC, que son
los vrtices del objeto-tringulo, se definen directamente como instancias de la
clase Vertice (L.23).
Merece especial atencin el constructor por defecto definido en L.25. Esta funcin acepta como
argumentos tres objetos de la clase Vertice, que son precisamente los vrtices del objetotringulo que se pretende construir, pero cuando es invocada sin argumentos, el constructor adopta
como argumentos por defecto el resultado de sendas invocaciones (tambin sin argumentos) al
constructor de la clase Vertice. Hemos visto que cuando este constructor es invocado sin
argumentos se obtiene un punto de coordenadas (0, 0), as que invocando Vertice() sin
argumentos, obtendramos un tringulo de tres vrtices coincidentes y superficie 0. Este es
precisamente el resultado que se obtiene para el objeto T2 (las cuatro ltimas salidas).
Observe (L.30) como el mtodo Triangulo::getT utiliza directamente los mtodos de las
propiedades vA, vB y vC.
El mtodo Triangulo::getArea, definido en L.32 y siguientes, utiliza las propiedades del
producto vectorial de dos vectores [2] para calcular el rea del tringulo definido por ellos.

El producto vectorial de los vectores A y B de


la figura adjunta, es el rea del paralelogramo
determinado por ambos (de forma que el rea
del tringulo formado por los vrtices vA-vBvCes la mitad de dicho producto).
El valor del producto vectorial A ^ B en funcin
de sus componentes cartesianas viene
determinado por la expresin:
A ^ B = Ax * By - Ay * Bx
Esta es precisamente la frmula utilizada para
el clculo en L.35.

La funcin main se limita a instanciar tres objetos de la clase Vertice en M.1 y dos de la
clase Triangulo en M.4. Adems se invocan algunos mtodos sobre ambos objetos.
Observe que para crear el objeto T1 en M.4, se realiza una invocacin implcita al constructor de la
clase con argumentos; que los dos primeros (v1 y v2) son objetos ya creados, mientras que el
ltimo es el resultado de una invocacin directa (con argumentos) al constructor de la
claseVertice.
Por simplicidad se ha limitado la extensin de la funcin main, pero se podra implementar
fcilmente una funcin para obtener los datos de cualquier tringulo cuyos vrtices se introdujeran
por teclado.
Ejemplos adicionales en: 4.9.18e;
3.2 Ejemplo-2:
Presentamos aqu una modificacin del caso de los tringulos
, en el que la clase Vertice se
incluye dentro de la clase principal Triangulo. Aparte de este cambio de posicin, el programa
es prcticamente idntico, con pequesimas modificaciones de detalle que comentamos:
La inclusin de la clase Vertice se ha realizado al principio, con objeto de que en las sucesivas
referencias a dicha clase (la primera ocurre en L.17), el compilador sepa a que corresponde este
identificador, de lo contrario tendramos que haber usado una declaracin adelantada ( 4.11.4a).
Los miembros-objeto vA, vB y vC que eran privados, se han hecho pblicos porque nos interesa
acceder sus mtodos desde el exterior (invocaciones de L.46 y L.47).
Recordar que los vrtices de cualquier tringulo sern los proporcionados por el constructor al
crear el objeto a travs de sus argumentos (explcitos, o por defecto). Para poder modificar los
valores iniciales de los vrtices se han incluido tres nuevos mtodos: setvA; setvB y setvC, que
permiten modificar las coordenadas de cualquiera de ellos.

#include <iostream>
using namespace std;
class Triangulo {
// clase contenedora
class Vertice {
// clase auxiliar (anidada)
float x, y;
public:
friend class Triangulo;
void getxy() {
cout << "vertice (" << x << "," << y << ")" << endl;
}
float getDist(const Vertice&);
Vertice(int i =0, int j =0) { x = i; y = j; }
// constructor
Vertice(const Vertice& rf) { x = rf.x; y = rf.y; } // constructorcopia
};
public:
Vertice vA, vB, vC;
// L.17: miembros-objeto
Triangulo(Vertice v1 = Vertice(),
// constructor
Vertice v2 = Vertice(),
Vertice v3 = Vertice() ) {
vA = v1; vB = v2; vC = v3;
}
void getT() { vA.getxy(); vB.getxy(); vC.getxy(); }
float getArea();
void setvA(int i, int j) { vA.x = i; vA.y = j; }
// L.25:
void setvB(int i, int j) { vB.x = i; vB.y = j; }
void setvC(int i, int j) { vC.x = i; vC.y = j; }
};

float Triangulo::Vertice::getDist(const Vertice& rf) {


float dX = rf.x - x, dY = rf.y - y;
float dist = pow(dX * dX + dY * dY, .5);
cout << "distancia: " << dist << endl;
return dist;
}

float Triangulo::getArea() {
int Ax = vB.x - vA.x; int Ay = vB.y - vA.y;
int Bx = vC.x - vA.x; int By = vC.y - vA.y;
double s = fabs ((Ax * By - Ay * Bx) * .5);
cout << "Area: " << s << endl;
return s;
}
void main() {
// L.43: ======================
Triangulo T1(Triangulo::Vertice(1,2), Triangulo::Vertice(3,4)), T2;
T1.setvC(7,3);
// L.45:
T2.vA.getDist(T1.vB);
// L.46
T1.vA.getDist(T1.vB);
// L.47
T1.getT();

T1.getArea();
}
Salida:
distancia: 5
distancia: 2.82843
vertice (1,2) vertice (3,4) vertice (7,3)
Area: 5
Comentario:
En contra de lo que ocurra en el primer ejemplo, aqu los objetos Vertice no tienen existencia
aislada, solo existen como miembros de los objetos-tringulo. Ntese como en la funcin main no
existen ahora vrtices como tales objetos aislados.
Es interesante la utilizacin de los dos primeros argumentos explcitos del constructor de la clase al
crear el objeto T1 en L.44. Se trata de invocaciones explcitas al constructor de la
subclase Vertice con los argumentos adecuados (observe la utilizacin del operador de
resolucin de mbito). Podramos haber usado tres, pero se han usado solo dos parmetros; en
estas condiciones el constructor Triangulo() suple el tercero utilizando su valor por defecto.
Recuerde que en caso de omitirse argumentos en C++, deben ser siempre los ltimos (
4.4.5).
Para restituir el valor del tercer vrtice a las coordenadas deseadas, en L.45 se utiliza el
mtodo setvC sobre el objeto.
Tal como se comprueba en las salidas, con la modificacin de L.45, los objetos T1 y T2 son
idnticos a sus homlogos del primer ejemplo (tienen los mismos vrtices).
Observe en L.46/47 la notacin utilizada para acceder al mtodo getDist del miembro vA en los
objetos T2 y T1. Observe as mismo el formato del argumento pasado en ambos casos.
Ejemplos adicionales en: 4.9.18e.

4.13.3 Objetos-puntero

1179

Potrebbero piacerti anche