Вы находитесь на странице: 1из 26

4.11.

2d Construccin y destruccin de objetos


1 Sinopsis:
Recordemos que los objetos pueden ser creados y destruidos bajo diversas circunstancias:

Los objetos automticos son creados cada vez que el proceso encuentra una
declaracin y destruidos cuando la ejecucin sale del bloque en que son declarados (
4.1.5).
Los objetos persistentes son creados con el operador new y destruidos con delete (
4.1.5)
Un miembro no esttico de un objeto es creado y destruido cuando el objeto del que es
miembro es a su vez creado y destruido.
Un objeto esttico local es creado la primera vez que se encuentra la declaracin durante
la ejecucin del programa, y es destruido cuando el programa finaliza (
4.1.5).
Un objeto esttico global es creado al principio del programa, durante la ejecucin del
mdulo inicial (
1.5) y destruido cuando el programa finaliza.
Un objeto temporal puede ser creado como parte de la evaluacin de una expresin y
destruido al final de la evaluacin misma.

Las condiciones enunciadas de destruccin de objetos son las que podramos considerar
"normales"; en el sentido que ocurren durante la ejecucin normal del programa. Pero existen
otras causas de destruccin de objetos que acontecen durante el proceso de limpieza de pila
("Stack unwinding"
1.6) que forma parte del mecanismo C++ de excepciones.

2 Unas funciones muy especiales


La adecuada creacin y destruccin de los objetos es cuestin de la mayor importancia para la
correcta ejecucin de un programa, y una de las causas ms frecuentes de errores en los
programas C++. Como botn de muestra, recordemos que al tratar de la declaracin de punteros
advertamos lo fcil que resulta causar un desastre con un simple error en su inicializacin (
4.2.1a). Para evitar este tipo de inconvenientes, C++ implementa mecanismos que garantizan
en lo posible, la correcta creacin, inicializacin y destruccin de objetos.
En la definicin de clases se utilizan ciertas funciones-miembro un tanto particulares que son
responsables de la creacin, inicializacin, copia y destruccin de los objetos de la clase. Son
los constructores ( 4.11.2d1) y destructores ( 4.11.2d2). De los primeros hay dos
variedades: los que crean un objeto desde cero (especificando sus propiedades), y los que lo crean
e inicializan como imagen de otro ya existente que sirve de modelo, son los
denominados constructor-copia (
4.11.2d4). Estas funciones gozan de las caractersticas del
resto de las funciones-miembro; son declaradas y definidas dentro de la clase (o declaradas dentro
y definidas fuera); como la mayora de las funciones C++, los constructores pueden tener
argumentos por defecto, o utilizar listas de argumentos de inicializacin (los destructores no
reciben argumentos), pero unos y otros tienen caractersticas que los hace especiales. En atencin
a esta singularidad, el Estndar las denomina funciones-miembro especiales ("special member
funcitons").

2.1 Denominacin: La primera singularidad est en el nombre de estas funciones:


los constructores adoptan el mismo nombre que la clase a que pertenecen; por su parte
los destructores adoptan el nombre precedido por la tilde ~.
Ejemplo:
class C {
// definicin de una clase
...
C() { /* definicin de constructor */ }
~C() { /* definicin del destructor */ }
};

Evidentemente, caso de existir varios constructores, lo que es muy frecuente,


se trata de
versiones sobrecargadas de la misma funcin, porque al compartir todos el mismo identificador, se
diferencian solo en el nmero y tipo de los argumentos que aceptan. En este caso, son las reglas
de resolucin de sobrecarga de funciones (
4.4.1a) las que deciden cual de ellos ser invocado
para construir el objeto.
Puesto que el destructor no acepta argumentos y tiene un nombre especfico la consecuencia
inmediata es que existe un solo destructor para cada clase (no existe posibilidad de sobrecarga).

2.2 Su declaracin no especifica ningn valor devuelto (ni siquiera void). En este sentido,
constructores y destructores son un caso especialsimo de funciones!!
Nota: el hecho que no puedan devolver ningn valor, hace que, por lo general, no existan
procedimientos sencillos o elegantes para tratar los errores que pudieran producirse durante la
fase de creacin del objeto.

2.3 Estos miembros no pueden ser heredados, aunque una clase derivada puede invocar a los
constructores y destructores de su clase-base si son accesibles. Es decir: si no han sido
declarados privados o protegidos (
4.11.2d1).
2.4 Un constructor no puede ser friend (
4.11.2a1) de ninguna otra clase. Tampoco pueden
ser declarados virtual (
4.11.2d2), static,const o volatile.
Nota: aunque el lenguaje no contempla la posibilidad de declarar constructores virtuales, s
ofrece soporte para algunas tcnicas que permiten simular este comportamiento (
4.13.5).

2.5 No pueden obtenerse sus direcciones, por lo que no pueden definirse punteros a este tipo de
funciones miembro. La sentencia del ejemplo es ilegal
int main (void) {
...
void* ptr = base::base;

// ilegal

...
}

2.6 Recuerde que, como en el resto de las funciones miembro no estticas, el primer argumento
(oculto) de constructores y destructores es el puntero this (
4.11.6), a travs del cual la funcin
sabe sobre que objeto debe actuar para inicializarlo o destruirlo.
2.7 Tenga en cuenta que un objeto que tenga constructor o destructor no puede ser utilizado
como miembro de una unin (
4.6).

3 Invocacin explcita de constructores y destructores


Al margen de la particularidad que representan sus invocaciones implcitas, en general su
invocacin sigue las pautas del resto de los mtodos. Ejemplos:
X x1;
X::X();
X x1 = X::X()
X x1 = X();
X x1();
x1.X();
....
X* p = &x1;
...
p->~X();
p>X::~X();
x1.~X()

//
//
//
//
//
//

Ok. Invocacin implcita del constructor


Error: invocacin ilegal del constructor
Error: invocacin ilegal del constructor
Ok. Invocacin legal del constructor
Ok. Variacin sintctica del anterior [2]
Error: no se puede invocar el constructor de un
objeto despus de creado

// p es puntero-a-X sealando al objeto x1


// Ok: Invocacin legal del destructor del objeto x1
// Ok: variacin sintctica del anterior
// Ok: otra posible invocacin

Recordar que cuando se trata de iniciar o destruir objetos de tipos definidos por el usuario
(clases), los operadores new y delete pueden realizar invocaciones implcitas a los constructores y
destructores de tales clases. A su vez, constructores y destructores pueden realizar invocaciones
explcitas a los operadores new (
4.9.20) y delete (
4.9.21) si se requiere espacio persistente
para algn miembro del objeto (ver ejemplo
4.11.2d1).

3.1 El compilador invoca los constructores y destructores correspondientes cuando se definen y


destruyen objetos (estas invocaciones pueden ser explcitas o implcitas). El constructor
correspondiente crea un objeto y lo inicia. Despus, cuando estos objetos deben ser destruidos,
susdestructores invierten el proceso destruyendo los objetos creados.
Lo mismo que ocurre con los tipos simples, los objetos abstractos pueden ser creados en memoria
dinmica o ser automticos. En el primer caso su destruccin debe realizarse explcitamente. En
caso de ser automticos, los destructores son invocados por el compilador en el momento que los
objetos salen de mbito. Son las situaciones esquematizadas en el siguiente ejemplo:
{
C c1;

// objeto automtico

C* cpt = new C;
// objeto persistente (annimo) +
objeto cpt automtico
delete cpt;
// destruccin explcita del objeto annimo
}
// destruccin implcita de c1 y cpt

3.2 Si en una clase X no se ha definido ningn constructor para aceptar un tipo de argumento
particular, el compilador no se realizar ningn intento para encontrar otro constructor, o alguna
conversin para convertir un valor asignado a un tipo aceptable para algn constructor de dicha
clase. Esta regla se aplica solo a constructores con un parmetro y sin iniciadores, que utilice la
sintaxis de asignacin =. Por ejemplo:
class X {
...
X(int);
};
class Y {
...
Y(X);
};
Y a = 1;

// constructor

// constructor
// ilegal: No es transformado a Y(X(1))

3.3 Cualquiera que sea el mtodo de invocacin del constructor (implcito o explcito) para crear
un objeto, si la clase contiene miembros abstractos ADTs (
2.2), el constructor invoca a su vez
los constructores de estos miembros. Como el constructor de los tipos escalares reserva espacio
en memoria, pero no realiza ningn tipo de inicializacin concreta, los miembros de tipo escalar
quedan sin una correcta inicializacin a menos que esta sea proporcionada explcitamente por el
programador.
Como consecuencia de la regla anterior, en el diseo de constructores no es generalmente
necesario preocuparse de la iniciacin de los miembros abstractos, ya que sus constructores sern
invocados automticamente y (suponemos) han sido correctamente establecidos al definir sus
clases. En cambio, dado que los tipos escalares (tipos simples preconstruidos en el lenguaje) no
reciben automticamente una correcta iniciacin, es probable que sus contenidos iniciales (basura)
puedan ocasionar problemas, por lo que es generalmente necesario proporcionarles una correcta
inicializacin en el constructor.
Nota: algunos autores sostienen que en estos casos, la iniciacin de los miembros escalares
debe realizarse en la lista de iniciadores ( 4.11.2d3a), dejando el cuerpo del constructor para
cualquier lgica adicional que sea necesaria durante la construccin [1]. La razn
argumentada es que al agruparlas as, se facilita la legibilidad de cdigo y el manejo de
excepciones en los procesos de creacin de objetos. El consejo llega al extremo de
recomendar que, si es imprescindible realizar al alguna manipulacin de estos miembros en el
cuerpo del constructor, al menos se inicien con un valor adecuado en la lista de iniciadores.
Valor que ser actualizarlo despus en el cuerpo del constructor.
La destruccin sigue el proceso inverso, de forma que la destruccin de estos objetos implica a su
vez la invocacin de los destructores de los objetos contenidos.
Ejemplo:

class Coordenada { public: int x; int y; };


class Triangulo {
public:
int color;
Coordenada verticeA; Coordenada verticeB; Coordenada verticeC;
};
...
Triangulo T1;

Al instanciar el objeto T1, el constructor de la clase Triangulo invocar tres veces al constructor
de la clase Coordenada. As mismo, el destructor de T1 tambin invocar al destructor
de Coordenada. Sin embargo, tanto la propiedad Triangulo::color como los
miembros x e yde los vrtices, permanecern sin una inicializacin especfica, aunque desde
luego, sern destruidos cuando el objeto T1 sea destruido.

4 Tipos de constructores
Una vez que se ha definido un tipo abstracto C (una clase), su utilizacin supone una operatoria
mnima que puede ser esquematizada en las siguientes sentencias:
{
C c1, c2;
C c3 = c1;
c2 = c1;
}

//
//
//
//

Creacin
Creacin a partir de un modelo
Asignacin
destruccin

La primera exige la existencia de un constructor; en este caso un constructor que se encargue de


crear los objetos con las inicializaciones por defecto. La segunda exige la presencia de un
constructor capaz de crear un objeto a imagen de otro tomado como referencia. La tercera exige la
presencia de un operador de asignacin capaz de realizar la asignacin de los miembros del
Rvalue en el Lvalue. Finalmente se precisa de destructores que garanticen la correcta destruccin
de los objetos y que puedan ser invocados automticamente por el compilador al llegar al final del
mbito.
Nota: se podra argumentar que la segunda puede ser sustituida por la creacin de un
objeto c3 desde "scratch" (caso primero) seguido de una asignacin c3 = c1 (caso tercero).
Sin embargo, esta no es la solucin adoptada. Todas las consideraciones de diseo del
lenguaje han gravitado alrededor de la eficiencia del cdigo, de forma que se ha preferido
mantener una sola operacin. Es importante entender que una sentencia como
esta no implica ninguna asignacin. Es una forma de expresar la utilizacin de un constructor
especial que acepta un objeto como argumento, objeto que ser utilizado como modelo para la
creacin del nuevo; algo como: C::C(c1);.

Los diseadores del lenguaje decidieron que estas utilidades eran imprescindibles, y adems
pretendieron simplificar el trabajo del usuario, de forma que, aunque dejaron al programador
facultad para definir sus propios constructores, destructores y operadores, decidieron que, en caso
de no hacerlo explcitamente, el compilador debera proporcionarlos por defecto. Estos algoritmos
se denominan oficiales o de oficio para distinguirlos de los creados por el programador, a los que

llamaremos explcitos. Cualquier constructor, destructor u operador de asignacin "oficial"


(generado por el compilador) es pblico.
Nota: es importante distinguir estos conceptos; "de oficio" (generados por el compilador) y
"explcitos" (definidos por el usuario), y no confundirlos con el concepto "por defecto" (que
puede ser invocado sin argumentos
4.11.2d1). Desgraciadamente son confundidos y/o no
suficientemente enfatizadas sus diferencias en la mayora de los textos. Otra cosa es que, por
ejemplo, el constructor "oficial" sea adems "por defecto" (puede ser invocado sin
argumentos).

Si el programador no define explcitamente ningn constructor, el compilador genera


un constructor oficial o de oficio (
4.11.2d1), que hace posible expresiones como:
C c1;
Sin embargo, si el programador proporciona un constructor explcito (con o sin argumentos) el
constructor oficial no es generado. En consecuencia, si se proporciona un constructor explcito con
argumentos y adems se desea un constructor por defecto (sin argumentos), este ltimo debe ser
proporcionado por el programador de la clase.
Si el programador no define ningn constructor adecuado, el compilador genera un constructorcopia oficial (
4.11.2d4) que hace posible que puedan utilizarse expresiones como:
C c2 = c1;
Si no se define explcitamente una versin de la funcin operator=() para miembros de la clase, el
compilador genera un operador de asignacin oficial que hace posible expresiones como:
c3 = c1;
Finalmente, si el programador no define explcitamente un destructor, el compilador proporciona
un destructor oficial.

Todos estos algoritmos "oficiales" suministrados por el compilador cuando no hay ninguno
explcito, tienen un comportamiento predefinido que ser comentando ms adelante. Por ahora
indicaremos que cuando existen versiones explcitas, el compilador aporta automticamente
algunos detalles si la versin explcita los omite. Estos detalles tienden a garantizar un
comportamiento correcto del algoritmo desde el punto de vista lgico y varan en funcin del
algoritmo (constructor, constructor-copia o destructor).
Es significativo que, aparte de las invocaciones explcitas o implcitas a los constructores, que
ocurren cuando se instancia deliberadamente un objeto, el compilador tambin crea infinidad de
objetos temporales (cuya existencia pasa ms o menos inadvertida) utilizando el mencionado
constructor-copia. Considere el siguiente ejemplo:
class UnaClase { ... };
UnaClase func (UnaClase) {
...
return UnaClase;

}
...
{
UnaClase obj1;
UnaClase obj2 = obj1;
UnaClase obj3 = func(obj1);
onj2 = onj3;
}

//
//
//
//
//
//

Bloque B.
L.1
L.2
L.3
L.4
L.5

Estas sentencias provocan las siguientes invocaciones (ver en la pgina adjunta un ejemplo
ejecutable de verificacin
ejemplo):
L.1: Invocacin al constructor por defecto (sin argumentos). Este constructor puede ser oficial o
explcito, segn el diseo de la clase.
L.2: Invocacin al constructor-copia (crea un objeto obj2 con el mismo contenido que obj1).
L.3: a.- Invocacin de la funcin: Es invocado el constructor-copia para crear un objeto
temporal tmp local a la funcin, e igual que el objeto obj1 pasado como argumento. Al terminar la
funcin, el objeto tmp es finalmente destruido, junto con el resto de objetos locales, mediante la
invocacin a su destructor.
b.- Invocacin del constructor-copia para crear un objeto obj3, con el mismo contenido que el
valor devuelto por la funcin.
L.4: No se invoca ningn constructor. La asignacin es totalmente distinta de la construccin y de
la construccin-copia (se realiza entre objetos ya creados).
L.5: Invocacin de los destructores de los objetos obj1, obj2 y obj3.

Tamas relacionados:

Constructores de conversin (
4.11.2d1)
Operadores de conversin (
4.9.18k)
Control de recursos (
4.1.5a)

4.11.2d1 Constructores
1 Sinopsis
Podemos imaginar que la construccin de objetos tiene tres fases:
1. instanciacin, que aqu representa el proceso de asignacin de espacio al objeto, de
forma que este tenga existencia real en memoria.
2. Asignacin de recursos. Por ejemplo, un miembro puede ser un puntero sealando a una
zona de memoria que debe ser reservada; un "handle" a un fichero; el bloqueo de un
recurso compartido o el establecimiento de una lnea de comunicacin.
3. Iniciacin, que garantiza que los valores iniciales de todas sus propiedades sean
correctos (no contengan basura).
La correcta realizacin de estas fases es importante, por lo que el creador del lenguaje decidi
asignar esta tarea a un tipo especial de funciones (mtodos) denominadas constructores. En
realidad la consideraron tan importante, que como veremos a continuacin, si el programador no

declara ninguno explcitamente, el compilador se encarga de definir un constructores de oficio


, encargndose de utilizarlo cada vez que es necesario. Aparte de las invocaciones explcitas que
pueda realizar el programador, los constructores son frecuentemente invocados de forma implcita
por el compilador.
Es significativo sealar que las fases anteriores se realizan en un orden, aunque todas deben ser
felizmente completadas cuando finaliza la labor del constructor.

2 Descripcin
Para empezar a entender como funciona el asunto, observe este sencillo ejemplo en el que se
definen sendas clases para representar complejos; en una de ellas definimos explcitamente un
constructor; en otra dejamos que el compilador defina un constructor de oficio:
#include <iostream>
using namespace std;
class CompleX {
// Una clase para representar complejos
public:
float r; float i;
// Partes real e imaginaria
CompleX(float r = 0, float i = 0) { // L.7: construtor explcito
this->r = r; this->i = i;
cout << "c1: (" << this->r << "," << this->i << ")" << endl;
}
};
class CompX {
// Otra clase anloga
public:
float r; float i;
// Partes real e imaginaria
};
void main() {
// ======================
CompleX c1;
// L.18:
CompleX c2(1,2);
// L.19:
CompX c3;
// L.20:
cout << "c3: (" << c3.r << "," << c3.i << ")" << endl;
}
Salida:
c1: (0,0)
c2: (1,2)
c3: (6.06626e-39,1.4013e-45)
Comentario
En la clase CompleX definimos explcitamente un constructor que tiene argumentos por defecto
( ), no as en la clase CompX en la que es el propio compilador el que define un constructor de
oficio.
Es de destacar la utilizacin explcita del puntero this ( 4.11.6) en la definicin del constructor
(L.8/L.9). Ha sido necesario hacerlo as para distinguir las propiedades i, j de las variables locales

en la funcin-constructor (hemos utilizado deliberadamente los mismos nombres en los


argumentos, pero desde luego, podramos haber utilizado otros ;-)
En la funcin main se instancian tres objetos; en todos los casos el compilador realiza una
invocacin implcita al constructor correspondiente. En la declaracin de c1, se utilizan los
argumentos por defecto para inicializar adecuadamente sus miembros; los valores se comprueban
en la primera salida.
La declaracin de c2 en L.19 implica una invocacin del constructor por defecto pasndole los
valores 1 y 2 como argumentos. Es decir, esta sentencia equivaldra a:
c2 = CompleX::CompleX(1, 2); // Hipottica invocacin explcita al
constructor
Nota: en realidad esta ltima sentencia es sintcticamente incorrecta; se trata solo de un recurso
pedaggico, ya que no es posible invocar de esta forma al constructor de una clase (
4.11.2d).
Una alternativa correcta a la declaracin de L.19 sera:
CompleX c2 = CompleX(1,2);
El resultado de L.19 puede verse en la segunda salida.
Finalmente, en L.20 la declaracin de c3 provoca la invocacin del constructor de oficio construido
por el propio compilador. Aunque la iniciacin del objeto con todos sus miembros es correcta, no lo
es su inicializacin (
4.1.2). En la tercera salida vemos como sus miembros adoptan valores
arbitrarios. En realidad se trata de basura existente en las zonas de memoria que les han sido
adjudicadas.
El corolario inmediato es deducir lo que ya sealamos en la pgina anterior: aunque el constructor
de oficio inicia adecuadamente los miembros abstractos (
4.11.2d), no hace lo mismo con los
escalares. Adems, por una u otra causa, en la mayora de los casos de aplicaciones reales
esimprescindible la definicin explcita de uno o varios de estos constructores
.

3 Tcnicas de buena construccin


Recordar que un objeto no se considera totalmente construido hasta que su constructor ha
concluido satisfactoriamente. En los casos que la clase contenga sub-objetos o derive de otras, el
proceso de creacin incluye la invocacin de los constructores de las subclases o de las superclases en una secuencia ordenada que se detalla ms adelante
.
Los constructores deben ser diseados de forma que no puedan (ni an en caso de error) dejar un
objeto a medio construir. En caso que no sea posible alistar todos los recursos exigidos por el
objeto, antes de terminar su ejecucin debe preverse un mecanismo de destruccin y liberacin de
los recursos que hubiesen sido asignados. Para esto es posible utilizar el mecanismo de
excepciones.

4 Invocacin de constructores
Al margen de la particularidad que representan sus invocaciones implcitas, en general su
invocacin sigue las pautas del resto de los mtodos. Ejemplos:

X x1;
X::X();
X x2 = X::X()
X x3 = X();
X x4();

//
//
//
//
//

L.1: Ok. Invocacin implcita del constructor


Error: invocacin ilegal del constructor [4]
Error: invocacin ilegal del constructor
L.4: Ok. Invocacin legal del constructor [5]
L.5: Ok. Variacin sintctica del anterior [6]

Nota: observe como la nica sentencia vlida con invocacin explcita al constructor (L.4) es
un caso de invocacin de funcin miembro muy especial desde el punto de vista sintctico
(esta sintaxis no est permitida con ningn otro tipo de funcin-miembro, ni siquiera con
funciones estticas o destructores). La razn es que los constructores se diferencian de todos
los dems mtodos no estticos de la clase en que no se invocan sobre un objeto (aunque
tienen puntero this
4.11.6). En realidad se asemejan a los dispositivos de asignacin de
memoria, en el sentido que son invocados desde un trozo de memoria amorfa y la convierten
en una instancia de la clase [7].

Como ocurre con los tipos bsicos (preconstruidos en el lenguaje), si deseamos crear objetos
persistentes de tipo abstracto (definidos por el usuario), debe utilizarse el operador new (
4.9.20). Este operador est ntimamente relacionado con los constructores. De hecho, para
invocar la creacin de un objeto a traves de l, debe existir un constructor por defecto
.
Si nos referimos a la clase CompleX definida en el ejemplo

, las sentencias:

{
CompleX* pt1 = new(CompleX);
CompleX* pt2 = new(CompleX)(1,2);
}
provocan la creacin de dos objetos automticos, los punteros pt1 y pt2, as como la creacin de
sendos objetos (annimos) en el montn. Observe que ambas sentencias suponen un invocacin
implcita al constructor. La primera al constructor por defecto sin argumentos, la segunda con los
argumentos indicados. En consecuencia producirn las siguientes salidas:
c1: (0,0)
c1: (1,2)
Observe tambin, y esto es importante, que los objetos pt1 y pt2 son destruidos automticamente
al salir de mbito el bloque. No as los objetos sealados por estos punteros (ver comentario al
respecto
4.11.2d2).

5 Propiedades de los constructores


Aunque los constructores comparten muchas propiedades de los mtodos normales, tienen
algunas caractersticas que las hace ser un tanto especiales. En concreto, se trata de funciones
que utilizan rutinas de manejo de memoria en formas que las funciones normales no suelen utilizar.
5.1 Los constructores se distinguen del resto de las funciones de una clase porque tienen
el mismo nombre que esta. Ejemplo:

class X {
public:
X();
};

// definicin de la clase X
// constructor de la clase X

5.2 No se puede obtener su direccin, por lo que no pueden declararse punteros a este tipo de
mtodos.
5.3 No pueden declararse virtuales (
class C {
...
virtual C();
};

4.11.8a). Ejemplo:

// Error !!

La razn est en la propia idiosincrasia de este tipo de funciones. En efecto, veremos que declarar
que un mtodo es virtual ( 4.11.8a) suponeindicar al compilador que el modo concreto de
operar la funcin ser definido ms tarde, en una clase derivada. Sin embargo, un constructor debe
conocer el tipo exacto de objeto que debe crear, por lo que no puede ser virtual.

5.4 Otras peculiaridades de los constructores es que se declaran sin devolver nada, ni
siquiera void, lo que no es bice para que el resultado de su actuacin (un objeto) s pueda ser
utilizado como valor devuelto por una funcin:
class C { ... };
...
C foo() {
return C();
}

5.5 No pueden ser heredados, aunque una clase derivada puede llamar a los constructores y
destructores de la superclase siempre que hayan sido declarados public o protected (
4.11.2a). Como el resto de las funciones (excepto main), los constructores tambin pueden ser
sobrecargados; es decir, una clase puede tener varios constructores.
En estos casos, la invocacin (incluso implcita) del constructor adecuado se efectuar segn los
argumentos involucrados. Es de destacar que en ocasiones, la multiplicidad de constructores
puede conducir a situaciones realmente curiosas; incluso se ha definido una palabra
clave, explicit, para evitar los posibles efectos colaterales
.
5.5 Un constructor no puede ser friend (

4.11.2a1) de ninguna otra clase.

5.6 Una peculiaridad sintctica de este tipo de funciones es la posibilidad de incluir iniciadores (
4.11.2d3), una forma de expresar la inicializacin de variables fuera del cuerpo del constructor.
Ejemplo:
class X {
const int i;

char c;
public:
X(int entero, char caracter): i(entero), c(caracter) { };
};

5.7 Como en el resto de las funciones, los constructores pueden tener argumentos por
defecto. Por ejemplo, el constructor:
X::X(int, int = 0)
puede aceptar uno o dos argumentos. Cuando se utiliza con uno, el segundo se supone que es un
cero int.
De forma anloga, el constructor
X::X(int = 5, int = 6)
puede aceptar dos, uno, o ningn argumento. Los valores por defecto proporcionan la informacin
necesaria cuando faltan datos explcitos.
Observe que un constructor sin argumentos, como X::X(), no debe ser confundido
con X::X(int=0), que puede ser llamado sin argumentos o con uno, aunque en realidad siempre
tendr un argumento. En otras palabras: que una funcin pueda ser invocada sin
argumentosno implica necesariamente que no los acepte.

5.8 Cuando se definen constructores deben evitarse ambigedades. Es el caso de los


constructores por defecto del ejemplo siguiente:
class X {
public:
X();
X(int i = 0);
};
int main() {
X uno(10);
X dos;
return 0;
}

// Ok; usa el constructor X::X(int)


// Error: ambigedad cual usar? X::X() o X::X(int = 0)

5.9 Los constructores de las variables globales son invocados por el mdulo inicial antes de que
sea llamada la funcin main y las posibles funciones que se hubiesen instalado mediante la
directiva #pragma startup ( 1.5).

5.10 Los objetos locales se crean tan pronto como se inicia su mbito. Tambin se invoca
implcitamente un constructor cuando se crea, o copia, un objeto de la clase (incluso temporal). El
hecho de que al crear un objeto se invoque implcitamente un constructor por defecto si no se

invoca ninguno de forma explcita, garantiza que siempre que se instancie un objeto ser
inicializado adecuadamente.
En el ejemplo que sigue se muestra claramente como se invoca el constructor tan pronto como se
crea un objeto.
#include <iostream>
using namespace std;
class A {
// definicin de una clase
public:
int x;
A(int i = 1) {
// constructor por defecto
x = i;
cout << "Se ha creado un objeto" << endl;
}
};
int main() {
// =========================
A a;
// se instancia un objeto
cout << "Valor de a.x: " << a.x << endl;
return 0;
}
Salida:
Se ha creado un objeto
Valor de a.x: 1
5.11 El constructor de una clase no puede admitir la propia clase como argumento (se dara lugar
a una definicin circular). Ejemplo:
class X {
public:
X(X);
};

// Error: ilegal

5.12 Los parmetros del constructor pueden ser de cualquier tipo, y aunque no puede aceptar su
propia clase como argumento, en cambio s pueden aceptar una referencia a objetos de su propia
clase, en cuyo caso se denomina constructor-copia (su sentido y justificacin lo exponemos con
ms detalle en el apartado correspondiente
4.11.2d4).
Ejemplo:
class X {
public:
X(X&);
};

// Ok. correcto

Aparte del referido constructor-copia, existe otro tipo de constructores de nombre especfico:
el constructor oficial y el constructor por defecto
.

6 Constructor oficial
Si el programador no define explcitamente ningn constructor, el compilador proporciona uno por
defecto al que llamaremos oficial o de oficio. Es pblico, "inline" (
4.11.2a), y definido de forma
que no acepta argumentos. Es el responsable de que funcionen sin peligro secuencias como esta:
class A {
int x;
};
...
A a;

// C++ ha creado un constructor "de oficio"


// invocacin implcita al constructor de oficio

Recordemos que el constructor de oficio invoca implcitamente los constructores de oficio de todos
los miembros. Si algunos miembros son a su vez objetos abstractos, se invocan sus constructores.
As sucesivamente con cualquier nivel de complejidad hasta llegar a los tipos bsicos
(preconstruidos en el lenguaje
2.2) cuyos constructores son tambin invocados. Recordar que
los constructores de los tipos bsicos inician (reservan memoria) para estos objetos, pero no los
inicializan con ningn valor concreto. Por lo que en principio su contenido es impredecible
(basura) [1]. Dicho en otras palabras: el constructor de oficio se encarga de preparar el ambiente
para que el objeto de la clase pueda operar, pero no garantiza que los datos contenidos sean
correctos. Esto ltimo es responsabilidad del programador y de las condiciones de "runtime". Por
ejemplo:
struct Nombre {
char* nomb;
};
struct Equipo {
Nombre nm;
size_t sz;
};
struct Liga {
int year;
char categoria;
Nombre nLiga;
Equipo equipos[10];
};
...
Liga primDiv;

En este caso la ltima sentencia inicia primDiv mediante una invocacin al constructor por
defecto de Liga, que a su vez invoca a los constructores por defecto de Nombre y Equipo para
crear los miembros nLiga y equipos (el constructor de Equipo es invocado diez veces, una por
cada miembro de la matriz). A su vez, cada invocacin a Equipo() produce a su vez una
invocacin al constructor por defecto deNombre (size_t es un tipo bsico y no es invocado su
constructor
4.9.13). Los miembros nLiga y equipos son iniciados de esta forma, pero los
miembros year y categoria no son inicializados ya que son tipos simples, por lo que pueden
contener basura.
Si el programador define explcitamente cualquier constructor, el constructor oficial deja de
existir. Pero si omite en l la inicializacin de algn tipo abstracto, el compilador aadir por su

cuenta las invocaciones correspondientes a los constructores por defecto de los miembros omitidos
( Ejemplo).

6.1 Constructor trivial


Un constructor de oficio se denomina trivial si cumple las siguientes condiciones:

La clase correspondiente no tiene funciones virtuales (


4.11.8a) y no deriva de ninguna
superclase virtual.
Todos los constructores de las superclases de su jerarqua son triviales
Los constructores de sus miembros no estticos que sean clases son tambin triviales

7 Constructor por defecto


Constructor por defecto de la clase X es aquel que "puede" ser invocado sin argumentos, bien
porque no los acepte, bien porque disponga de argumentos por defecto (
4.4.5).
Como hemos visto en el epgrafe anterior, el constructor oficial creado por el compilador si no
hemos definido ningn constructor, es tambin un constructor por defecto, ya que no acepta
argumentos.
Tenga en cuenta que diversas posibilidades funcionales y sintcticas de C++ precisan de la
existencia de un constructor por defecto (explcito u oficial). Por ejemplo, es el responsable de la
creacin del objeto x en una declaracin del tipo X x;.

8 Un constructor explcito puede ser imprescindible


En el primer ejemplo , el programa ha funcionado aceptablemente bien utilizando el constructor
de oficio en una de sus clases, pero existen ocasiones en que es imprescindible que el
programador defina uno explcitamente, ya que el suministrado por el compilador no es adecuado.
Consideremos una variacin del citado ejemplo en la que definimos una clase para contener las
coordenadas de puntos de un plano en forma de matrices de dos dimensiones:
#include <iostream>
using namespace std;
class Punto {
public: int coord[2];
};
int main() {
// ==================
Punto p1(10, 20);
// L.8:
cout << "Punto p1; X == " << coord[0] << "; Y == " << coord[1] << endl;
}
Este programa produce un error de compilacin en L.8. La razn es que si necesitamos este tipo
de inicializacin del objeto p1 (utilizando una lista de argumentos), es imprescindible la existencia
de un constructor explcito (
4.11.2d3). La versin correcta del programa seria:

#include <iostream>
using namespace std;
class Punto {
public: int coord[2];
Punto(int x = 0, int y = 0) {
coord[0] = x; coord[1] = y;
}
};

// construtor explcito
// inicializa

int main() {
// ==================
Punto p1(10, 20);
// L.8: Ok.
cout << "Punto p1; X == " << coord[0] << "; Y == " << coord[1] << endl;
}

8.1 La anterior no es por supuesto la nica causa que hace necesaria la existencia de
constructores explcitos. Ms frecuente es el caso de que algunas de las variables de la clase
deban ser persistentes. Por ejemplo, supongamos que en el caso anterior necesitamos que la
matriz que almacena las coordenadas necesite este tipo de almacenamiento. En este caso, puesto
que la utilizacin del especificador static aplicado a miembros de clase puede tener efectos
colaterales indeseados ( 4.11.7), el nico recurso es situar el almacenamiento en el montn (
1.3.2), para lo que utilizamos el operador new ( 4.9.20) en un constructor definido al
efecto. La definicin de la clase tendra el siguiente aspecto [8]:
class Punto {
public: int* coord;
Punto(int x = 0, int y =
coord = new int[2];
coord[0] = x; coord[1]
cout << "Creado punto;
<< coord[0] << ";
}
};

0) {

// construtor por defecto


// asigna espacio
// inicializa

= y;
X == "
Y == " << coord[1] << endl;

Posteriormente se podran instanciar objetos de la clase Punto mediante expresiones como:


Punto p1;
Punto p2(3, 4);
Punto p3 = Punto(5, 6);
Punto* ptr1 = new(Punto)
Punto* ptr2 = new(Punto)(7, 8)

//
//
//
//
//

invocacin
invocacin
invocacin
invocacin
invocacin

implcita
implcita
explcita
implcita
implcita

con
con
sin
con

argumentos
argumentos
argumentos
argumentos

9 Orden de construccin
Dentro de una clase los constructores de sus miembros son invocados antes que el constructor
existente dentro del cuerpo de la propia clase. Esta invocacin se realiza en el mismo orden en que
se hayan declarado los elementos. A su vez, cuando una clase tiene ms de una clase base
(herencia mltiple 4.11.2c), los constructores de las clases base son invocados antes que el de
la clase derivada y en el mismo orden que fueron declaradas. Por ejemplo en la inicializacin:

class Y {...}
class X : public Y {...}
X one;
los constructores son llamados en el siguiente orden:
Y();
X();

// constructor de la clase base


// constructor de la clase derivada

En caso de herencia mltiple:


class X : public Y, public Z
X one;
los constructores de las clase-base son llamados primero y en el orden de declaracin:
Y();
Z();
X();

// constructor de la primera clase base


// constructor de la segunda clase base
// constructor de la clase derivada

Nota: al tratar de la destruccin de objetos (


4.11.2d2), veremos que los destructores son
invocados exactamente en orden inverso al de los constructores.

9.1 Los constructores de clases base virtuales (


4.11.8a) son invocados antes que los de
cualquier clase base no virtual. Si la jerarqua contiene mltiples clases base virtuales, sus
constructores son invocados en el orden de sus declaraciones. A continuacin de invocan los
constructores del resto de las clase base, y por ltimo el constructor de la clase derivada.

9.2 Si una clase virtual deriva de otra no virtual, primero se invoca el constructor de la clase base
(no virtual), de forma que la virtual (derivada) pueda ser construida correctamente. Por ejemplo, el
cdigo:
class X : public Y, virtual public Z
X one;
origina el siguiente orden de llamada en los constructores:
Z();
Y();
X();

// constructor de la clase base virtual


// constructor de la clase base no virtual
// constructor de la clase derivada

Un ejemplo ms complicado:
class base;
class base2;
class level1 : public base2, virtual public base;
class level2 : public base2, virtual public base;
class toplevel : public level1, virtual public level2;
toplevel view;

El orden de invocacin de los constructores es el siguiente:


base();
base2();
level2();
base2();
level1();

//
//
//
//
//
//
//

clase virtual de jerarqua ms alta


base es construida solo una vez
base no virtual de la base virtual level2
debe invocarse para construir level2
clase base virtual
base no virtual de level1
otra base no virtual

toplevel();

9.3 Si una jerarqua de clases contiene mltiples instancias de una clase base virtual, dicha base
virtual es construida solo una vez. Aunque si existen dos instancias de la clase base: virtual y no
virtual, el constructor de la clase es invocado solo una vez para todas las instancias virtuales y
despus una vez para cada una de las instancias no virtuales.
9.4 En el caso de matrices de clases, los constructores son invocados en orden creciente de
subndices.

10 Los constructores y las funciones virtuales


Debido a que los constructores de las clases-base son invocados antes que los de las clases
derivadas, y a la propia naturaleza del mecanismo de invocacin de funciones virtuales (
4.11.8a), el mecanismo virtual est deshabilitado en los constructores, por lo que es peligroso
incluir invocaciones a tales funciones en ellos, ya que podran obtenerse resultados no esperados
a primera vista.
Considere los resultados del ejemplo siguiente, donde se observa que la versin de la
funcin fun invocada no es la que cabra esperar en un funcionamiento normal del mecanismo
virtual.
#include <string>
#include <iostream>
using namespace std;
class B {
// superclase
public:
virtual void fun(const string& ss) {
cout << "Funcion-base: " << ss << endl;
}
B(const string& ss) {
// constructor de superclase
cout << "Constructor-base\n";
fun(ss);
}
};
class D : public B {
// clase derivada
string s;
// private por defecto
public:
void fun(const string& ss) { cout << "Funcion-derivada\n"; s = ss; }
D(const string& ss) :B(ss) { // constructor de subclase
cout << "Constructor-derivado\n";

}
};
int main() {
D d("Hola mundo");
}

// =============
// invocacin implcita a constructor D

Salida:
Constructor-base
Funcion-base: Hola mundo
Constructor-derivado
Nota: la invocacin de destructores (
4.11.2d2) se realiza en orden inverso a los
constructores. Las clases derivadas se destruyen antes que las clases-base [2]. Por esta
razn el mecanismo virtual tambin est deshabilitado en los destructores (lo que no tiene
nada que ver con que los destructores puedan ser en s mismos funciones
virtuales
4.11.2d2). As pues, en la ejecucin de un destructor solo se invocan las
definiciones locales de las funciones implicadas. De lo contrario se correra el riesgo de
referenciar la parte derivada del objeto que ya estara destruida.

11 Constructores de conversin
Normalmente a una clase con constructor de un solo parmetro puede asignrsele un valor que
concuerde con el tipo del parmetro. Este valor es automticamente convertido de forma implcita
en un objeto del tipo de la clase a la que se ha asignado. Por ejemplo, la definicin:
class X {
public:
X();
X(int);
X(const char*, int = 0);
};

// constructor C-1
// constructor C-2
// constructor C-3

en la que se han definido dos constructores que pueden ser utilizados con un solo
argumento, permite que las siguientes asignaciones sean legales:
void f() {
X a;
X b = X();
X c = X(1);
X d(1);
X e = X("Mexico");
X f("Mexico");
X g = 1;
X h = "Madrid";
a = 2;
}

//
//
//
//
//
//
//
//
//

Ok invocado C-1
Ok dem.
Ok invocado C-2
Ok igual que el anterior
Ok invocado C-3
Ok igual que el anterior
L.1 Ok.
L.2 Ok.
L.3 Ok.

La explicacin de las tres ltimas sentencias es la siguiente:


En L.1, el compilador intenta convertir el Rvalue (que aqu es una constante numrica entera de
valor 1) en el tipo del Lvalue, que aqu es la declaracin de un nuevo objeto (una instancia de la

clase). Como necesita crear un nuevo objeto, utilizar un constructor, de forma que busca si hay
uno adecuado en X que acepte como argumento el tipo situado a la derecha. El resultado es que
el compilador supone un constructor implcito a la derecha de L.1:
X a = X(1);

// interpretacin del compilador para L.1

El proceso se repite en la sentencia L.2 que es equivalentes a:


X B = X("Madrid");

// L.2bis

La situacin en L.3 es completamente distinta, ya que en este caso ambos operandos son objetos
ya construidos. Para poder realizar la asignacin, el compilador intenta convertir el tipo del Rvalue
al tipo del Lvalue, para lo cual, el mecanismo de conversin de tipos busca si existe un constructor
adecuado en X que acepte el operando derecho. Caso de existir se crear un objeto temporal
tipoX que ser utilizado como Rvalue de la asignacin. La asignacin propiamente dicha es
realizada por el operador correspondiente (explcito o implcito) de X. La pgina adjunta incluye un
ejemplo que muestra grficamente el proceso seguido (
Ejemplo)
Este tipo de conversin automtica se realiza solo con constructores que aceptan un argumento o
que son asimilables (como C-2), y suponen una conversin del tipo utilizado como argumento al
tipo de la clase. Por esta razn son denominadas conversiones mediante constructor, y a este tipo
de constructores constructores de conversin ("Converting constructor"). Su sola presencia
habilita no solo la conversin implcita, tambin la explcita. Ejemplo:
class X {
public:
X(int);
};

// constructor C-2

la mera existencia del constructor C-2 en la clase X, permite las siguientes asignaciones:
void f() {
X a = X(1)
X a = 1;
a = 2;
a = (X) 2;
a = static_cast<X>(2);
}

//
//
//
//
//

L1:
Ok.
Ok.
Ok.
Ok.

Ok. invocacin explcita al constructor


invocacin implcita X(1)
invocacin implcita X(2)
casting explcito (estlo tradicional)
casting explcito (estilo C++)

Si eliminamos el constructor C-2 de la declaracin de la clase, todas estas sentencias seran


errneas.
Observe que en L1 cabra hacerse una pregunta: Se trata de la invocacin del constructor, o un
modelado explcito al estilo tradicional?. La respuesta es que se trata de una invocacin al
constructor, y que precisamente el modelado (explcito o implcito) se apoya en la existencia de
este tipo de constructores para realizar su trabajo.

Temas relacionados

Operadores de conversin (
4.9.18k)
Conversin automtica a tipos simples (

4.13.6)

12 Constructor explicit
El problema es que en ocasiones el comportamiento descrito en el epgrafe anterior puede resultar
indeseable y enmascarar errores. Es posible evitarlo declarando el constructor de la clase con la
palabra clave explicit, dando lugar a los denominados constructores explicit [3]. En estos casos,
los objetos de la clase solo podrn recibir asignaciones de objetos del tipo exacto. Cualquier otra
asignacin provocar un error de compilacin.

12.1 La sintaxis de utilizacin es:


explicit <declaracin de constructor de un solo parmetro>
Aplicndolo al ejemplo anterior:
class X {
public:
explicit X(int);
// constructor C-2b
explicit X(const char*, int = 0); // constructor C-3b
};
...
void f() {
X a = 1;
// L.1 Error!!
X B = "Madrid";
// L.2 Error!!
a = 2;
// L.3 Error!!
}
Ahora los objetos de la clase X, dotada con constructores explicit, solo pueden recibir
asignaciones de objetos del mismo tipo:
void f() {
X a = X(1);
X b = X("Madrid", 0);
a = (X) 2;
}

// L.1 Ok.
// L.2 Ok.
// L.3 Ok.

En L.3 se ha utilizado una conversin de tipos ("Casting") explcita (


4.9.9). Para realizarla, el
mecanismo de conversin busca si en la clase Xexiste un constructor que acepte como argumento
el tipo de la derecha, con lo que estaramos en el caso de L.1.

13 Constructores privados y protegidos


Cuando los constructores no son pblicos (son privados o protegidos
4.11.2b-1), no pueden ser
accedidos desde el exterior, por lo que no pueden ser invocados explcita ni implcitamente al
modo tradicional (4 ). Ejemplo:
class C {
int x;
C(int n=0): x(n) {}
};
...
void foo() {

// privado por defecto

C c(1);

// Error!! cannot access private member

}
Adems, como las clases derivadas necesitan invocar los constructores de las superclases para
instanciar sus objetos, caso de no ser protegidos o pblicos tambin pueden existir limitaciones
para su creacin. Ejemplo:
class B {
int x;
B (): x(10) {}
};
class D : public B {
...
};
void foo() {
D d;
// Error!! no appropriate default constructor available
...
}

Puesto que los miembros private o protected no pueden ser accedidos desde el exterior de la
clase, este tipo de constructores se suelen utilizar siempre a travs de funciones-miembro pblicas
con objeto de garantizar cierto control sobre los objetos creados. El esquema de utilizacin sera el
siguiente:
class C {
C(int n) { /* constructor privado */ }
public:
static C makeC(int m) {
...
return C(m);
}
...
};
void foo() {
C c = C::makeC(1);
}

// Ok.

Observe que makeC() es esttica para que pueda ser invocada con independencia de la
existencia de cualquier objeto (
4.11.7). Observe tambin que mientras una expresin como:
C c = C(1);
es una invocacin al constructor, en cambio, la sentencia
C c = C::makeC(1);
es una invocacin al operador de asignacin, ya que la funcin devuelve un objeto que ser
tomado como Rvalue de la asignacin.

Esta tcnica, que utiliza constructores privados o protegidos junto con mtodos pblicos para
accederlos, puede prevenir algunas conversiones de tipo no deseadas, pudiendo constituir una
alternativa al uso de constructores explicit (12 ).

4.11.2d2 Destructores
1 Sinopsis
Los destructores son un tipo especial de funcin miembro, estrechamente relacionados con los
constructores. Son tambin funciones que no devuelven nada (ni siquiera void). Tampoco aceptan
ningn parmetro, ya que la destruccin de un objeto no acepta ningn tipo de opcin o
especificacin particular y es idntica para todos los objetos de la clase. Los destructores no
pueden ser heredados, aunque una clase derivada puede llamar a los destructores de su
superclase si no han sido declarados privados (son pblicos o protegidos). Lo mismo que ocurre
con los constructores, tampoco puede obtenerse su direccin, por lo que no es posible establecer
punteros a este tipo de funciones.
La misin ms comn de los destructores es liberar la memoria asignada por los constructores,
aunque tambin puede consistir en desasignar y/o liberar determinados recursos asignados por
estos. Por ejemplo, cerrar un fichero; una lnea de comunicacin, o desbloquear un recurso
compartido que hubiera sido bloqueado previamente por el constructor.
Se ha sealado que, si el programador no define uno explcitamente, el compilador C++
proporciona un destructor de oficio, que es declarado pblico y puede ser invocado sin
argumentos. Por lo general en la mayora de los casos este destructor de oficio es suficiente, por lo
que el programador no necesita definir uno por s mismo, a no ser que la clase incluya la
inicializacin de objetos persistentes (
1.3.2). Por ejemplo, matrices que necesiten del
operador new en el constructor para su inicializacin, en cuyo caso es responsabilidad del
programador definir un destructor adecuado (ver ejemplo
).
Los destructores son invocados automticamente (de forma implcita) por el programa en multitud
de ocasiones; de hecho es muy raro que sea necesario invocarlos explcitamente. Su misin es
limpiar los miembros del objeto antes que el propio objeto se auto-destruya.

2 Declaracin
Los destructores se distinguen porque tienen el mismo nombre que la clase a que pertenecen
precedido por la tilde ~ para simbolizar su estrecha relacin con los constructores que utilizan el
mismo nombre (son el "complemento" de aquellos). Ejemplo:
class X {
public:
~X();
};
...
X::~X() {
...
}

// destructor de la clase X
// definicin (off-line) del destructor

Ejemplo:
La clase Punto definida en el epgrafe anterior (
4.11.2d1) sera un buen exponente del caso en
que es necesario definir un destructor explcito que se encargue de las correcta destruccin de los
miembros. En efecto, manteniendo aquella definicin, una sentencia del tipo:
{
...
Punto p1(2,3);
...
}
provoca la creacin de un objeto en memoria dinmica. El miembro coord es un puntero-a-int que
seala un rea en el montn capaz para albergar dos enteros. Cuando la ejecucin sale del mbito
del bloque en que se ha creado el objeto, es invocado el destructor de oficio y el objeto es
destruido, incluyendo su nico componente, el puntero coord; sin embargo el rea sealada por
este permanece reservada en el montn, y por tanto irremediablemente perdida.
La forma sensata de utilizar tales objetos sera modificando la definicin de la clase para aadirle
un destructor adecuado. La versin correcta tendra el siguiente aspecto:
class Punto {
public: int* coord;
Punto(int x = 0, int y = 0) { // construtor
coord = new int[2];
coord[0] = x; coord[1] = y;
}
~Punto() {
// destructor
delete [] coord;
// L.8:
};
En este caso, la sentencia de la lnea 8 provoca que al ser invocado el destructor del objeto, se
desasigne el rea del montn sealada por el puntero (recuerde que, al igual que el resto de las
funciones-miembro, los destructores tambin tienen un argumento oculto this, por lo que la funcin
sabe sobre que objeto tiene que operar en cada caso).

3 Invocacin
Como hemos sealado, los destructores son invocados automticamente por el compilador, y es
muy raro que sea necesario invocarlos explcitamente.
3.1 Invocacin explcita de destructores
En caso necesario los destructores pueden ser invocados explcitamente de dos formas:
indirectamente, mediante una llamada a delete ( 4.9.21) o directamente utilizando el nombre
cualificado completo.
Ejemplo
class X {...};
...

// X es una clase

{
X obj1;
X* ptr = new(X)
X* pt2 = &obj1;
...
pt2>X::~X();
// pt2->~X();
// obj1.~X();
X::~X();
delete ptr;
}

// L.4: objeto automtico


// L.5: objeto persistente
// Ok: pt2 es puntero a obj1 de la clase X
// L.8: Ok: llamada legal del destructor
L.9: Ok: variacin sintctica de la anterior
L.10: Ok otra posibilidad anloga
// L.11: Error: llamada ilegal al destructor [1]
// L.12: Ok. invocacin implcita al destructor

Comentario
L.4 crea el objeto obj1 en la pila (
1.3.2), se trata de un objeto automtico, y en cuanto el
bloque salga de mbito, se producir una llamada a su destructor que provocar su
eliminacin. Sin embargo, el objeto annimo sealado por ptr es creado en el montn.
Observe que mientras ptr es tambin un objeto automtico, que ser eliminado al salir del bloque,
el objeto al que seala es persistente y su destructor no ser invocado al salir de mbito el bloque.
Como se ve en el punto siguiente, en estos casos es imprescindible una invocacin explcita al
destructor mediante el operador delete (cosa que hacemos en L.12), en caso contrario, el espacio
ocupado por el objeto se habr perdido.
Es muy importante advertir que la invocacin explcita al destructor de obj1 en L.8 (o su versiones
equivalentes L.9 y L.10) son correctas, aunque muy peligrosas [2]. En efecto, en L.8 se produce la
destruccin del objeto, pero en el estado actual de los compiladores C++, que no son
suficientemente "inteligentes" en este sentido [3], al salir el bloque de mbito vuelven a invocar los
destructores de los objetos automticos creados en su interior, por lo que se producir un error de
ejecucin irrecuperable (volcado de memoria si corremos bajo Linux).

3.1.1 Los objetos que han sido creados con el operador new ( 4.9.20) deben
destruirse obligatoriamente con una llamada explcita al destructor. Ejemplo:
#include <stdlib.h>
class X {
// clase
public:
...
~X(){};
// destructor de la clase
};
void* operator new(size_t size, void *ptr) {
return ptr;
}
char buffer[sizeof(X)];
// matriz de caracteres, del tamao de X
void main() {
X* ptr1 = new X;
X* ptr2;
ptr2 = new(&buffer) X;
...
delete ptr1;

//
//
//
//

========================
puntero a objeto X creado con new
puntero a objeto X
se inicia con la direccin de buffer

// delete destruye el puntero

ptr2>X::~X();
buffer
}

// llamada directa, desasignar el espacio de

3.2 Invocacin implcita de destructores

1052

Вам также может понравиться