Buscando un artículo sobre C++

Con respecto al diseño irrazonable del constructor de C ++ en C ++, el constructor es una función especial llamada al construir un objeto. Su propósito es inicializar el objeto y ponerlo en un estado razonable antes de su uso. Sin embargo, el diseño del constructor no es perfecto e incluso tiene algunas características poco razonables. Por ejemplo, restrinja la condición de que el nombre del constructor sea el mismo que el nombre de la clase. Vale la pena tener en cuenta estas características al construir un compilador de C++. Además, estas características deberían evitarse en futuras revisiones del estándar C++ o en el desarrollo de otros lenguajes de diseño orientados a objetos. Este artículo también propone algunas soluciones.

En C++, cualquier clase tiene un (al menos uno) constructor, incluso si no se declara ningún constructor. Estos constructores se llaman cuando un objeto se declara o se genera dinámicamente. El constructor realiza una gran cantidad de trabajo invisible, incluso si no hay código en el constructor, incluida la asignación de memoria para el objeto y la inicialización de miembros mediante asignación. El nombre del constructor debe ser el mismo que el nombre de la clase, pero se pueden proporcionar muchas versiones sobrecargadas diferentes y las versiones del constructor se distinguen por los tipos de parámetros. Los constructores pueden ser llamados explícitamente mediante código de usuario o insertados implícitamente por el compilador cuando el código no existe. Por supuesto, se recomienda llamarlo explícitamente mediante código, porque el efecto de la llamada implícita puede no ser el que esperamos, especialmente cuando se trata de asignación de memoria dinámica. El código llama al único constructor con parámetros. Aunque las declaraciones pueden regresar dentro del cuerpo de una función, los constructores no devuelven valores. Cada constructor puede crear una instancia de un objeto de una manera diferente, y dado que cada clase tiene un constructor, al menos el predeterminado, cada objeto usa un constructor en consecuencia antes de usarlo. Debido a que el constructor es una función, su visibilidad no es más que pública, privada y protegida. Normalmente, los constructores se declaran públicos. Si el constructor se declara privado o protegido, la creación de instancias del objeto está restringida. Esto es eficaz para evitar que otras clases creen instancias de clases. Puede haber cualquier declaración de C++ en el constructor, como una declaración de impresión, que se puede agregar al constructor para indicar dónde se llama.

Tipos de constructores Hay muchos tipos de constructores en C++. Los constructores predeterminados y de copia más utilizados también tienen algunos constructores menos utilizados. Aquí hay cuatro constructores diferentes.

1. Constructor predeterminado

El constructor predeterminado es una función sin parámetros. Además, también se puede declarar un constructor predeterminado como valor predeterminado para los parámetros en la lista de parámetros. La función del constructor predeterminado es inicializar el objeto a su estado predeterminado. Si no hay ningún constructor definido explícitamente en la clase, el compilador creará automáticamente uno implícitamente, similar a un constructor vacío. No hace más que crear instancias de objetos. En muchos casos, el constructor predeterminado se llamará automáticamente. Por ejemplo, declarar un objeto hará que se llame al constructor predeterminado.

2. Constructor de copia

El constructor de copia, generalmente llamado x(x&), es un constructor especial al que llama el compilador para completar algunos componentes y se basa en la inicialización de otros. objetos de la misma clase. Su único parámetro (una referencia al objeto) es inmutable (porque es de tipo constante). Esta función se utiliza normalmente para pasar y devolver valores de tipos definidos por el usuario durante llamadas a funciones. El constructor de copia llama al constructor de copia y a las funciones miembro de la clase base. Si es posible, se llamará como una constante, pero también se puede llamar de forma no convencional.

En C++, hay tres situaciones en las que es necesario copiar objetos. Por lo tanto, se llamará al constructor de copias.

1) Un objeto se pasa al cuerpo de la función por valor.

2) Devuelve un objeto de una función pasando valor.

3) Un objeto necesita ser inicializado por otro objeto.

La situación anterior requiere una llamada al constructor de copia. Si el constructor de copia no se utiliza en los dos primeros casos, hará que el puntero apunte al espacio de memoria que se ha eliminado. Para el tercer caso, los diferentes significados de inicialización y asignación son el motivo de la llamada al constructor. De hecho, el constructor de copia se implementa mediante un constructor general y una operación de asignación. Hay muchas referencias que describen las similitudes y diferencias entre los constructores de copias y los operadores de asignación.

El constructor de copia no puede cambiar el objeto al que hace referencia, por la siguiente razón: cuando un objeto se pasa a una función por valor, se llama automáticamente al constructor de copia para generar el objeto en la función.

Si un objeto se pasa a su propio constructor de copia, se llamará a su constructor de copia para hacer una copia del objeto de modo que la copia pueda pasarse a su propio constructor de copia, lo que dará como resultado un bucle infinito.

Además de ser llamado implícitamente cuando un objeto se pasa a una función, el constructor de copia también se llama cuando un objeto es devuelto desde una función. En otras palabras, lo que obtienes de la función es sólo una copia del objeto. Pero nuevamente, el constructor de copias se llama correctamente, no te preocupes.

Si no declaras explícitamente un constructor de copia en la clase, entonces el compilador creará en secreto una función para que puedas copiar bit a bit entre objetos. Este constructor de copias implícito simplemente asocia a todos los miembros de la clase. Muchos autores citarán este constructor de copia predeterminado. Tenga en cuenta que la diferencia entre un constructor de copia implícito y un constructor de copia declarado explícitamente es cómo se asocian los miembros. Un constructor de copia declarado explícitamente se asocia solo con el constructor predeterminado que crea una instancia de un miembro de la clase, a menos que se llame a otro constructor durante la inicialización de la clase o la construcción de la lista.

Un constructor de copias hace que un programa sea más eficiente porque no tiene que cambiar la lista de argumentos del constructor al construir un objeto. Es un buen estilo diseñar un constructor de copias, incluso si el sistema de compilación le proporciona un constructor de copias predeterminado para ayudarlo a asignar en la memoria. De hecho, el constructor de copias predeterminado puede manejar muchas situaciones.

3. Constructores definidos por el usuario

Los constructores definidos por el usuario permiten inicializar objetos al mismo tiempo que se definen. Este constructor puede tener cualquier tipo de parámetros. Los constructores para tipos definidos por el usuario y otros tipos están contenidos en la clase mystring: ClassMyString.

{......

public:mystring(); //Constructor predeterminado

Mi cadena(mystring&src )

//Copiar constructor

mystring(char * SCR);

//Constructor forzado

mystring ( char scr [ ], size_t len ​​

//Constructor definido por el usuario};

4. Constructor obligatorio

En C++, puedes declarar un constructor con un solo parámetro de conversión de tipo. Obliga al constructor a especificar una conversión de tipo (implícita o explícita) a partir del tipo de parámetro. En otras palabras, el compilador puede llamar al constructor con una instancia de cualquier parámetro. El propósito de esto es crear una instancia temporal para reemplazar la instancia del tipo de parámetro. Tenga en cuenta que la palabra clave explícita se agregó recientemente al C++ estándar para deshabilitar las conversiones de tipos implícitas. Sin embargo, no todos los compiladores admiten esta característica. El siguiente es un ejemplo de un constructor forzado:

Clase A

{

Público:

A(int ){ }< / p>

};

void f(A) { }

void g()

{

a Mi _ Objeto = 17;

A a2 = A(57);

a3(64);

Mi _ Objeto = 67

f(77);

} Como My_Object = 17; Esta declaración significa llamar al constructor A(int) para generar un objeto a partir de una variable entera. Un constructor de este tipo es un constructor obligatorio.

Características generales Los siguientes son algunos diseños de constructores de C++ poco razonables. Por supuesto, puede haber otros aspectos irrazonables. Sin embargo, en su mayor parte, todavía tenemos que lidiar con estas características y tenemos que explicarlas una por una. 1. Los constructores pueden estar en línea, pero no lo hagas.

En términos generales, la mayoría de las funciones miembro pueden convertirse en funciones en línea agregando la palabra clave "inline" delante de ellas, y los constructores no son una excepción, ¡pero no hagas esto! El constructor definido como en línea es el siguiente: Clase X.

{ ..........

público:x(int);

:

:

p>

};

Inline x::x(int)

{...} En el código anterior, la función se inserta en el código de programa, y ​​no como una entidad separada. Esto mejorará la eficiencia de las funciones con solo una o dos declaraciones, ya que no hay gastos generales al llamar a la función.

Los peligros de utilizar constructores en línea se reflejan en la definición de constructores en línea estáticos. En este caso, el constructor estático se debe llamar sólo una vez. Sin embargo, si el archivo de encabezado contiene un constructor estático en línea y otras unidades lo incluyen, la función se copiará varias veces. De esta manera, cuando se inicia el programa, se llaman todas las copias de la función en lugar de la copia que se supone que debe llamar el programa. La causa principal es que las funciones estáticas son objetos reales disfrazados de funciones.

Una cosa para recordar es que la integración es una recomendación, no un requisito, y el compilador genera código en línea. Esto significa que la inserción depende de la implementación y diferentes compiladores pueden marcar una gran diferencia. Por otro lado, las funciones en línea pueden incluir algo más que código. Los constructores se declaran en línea y es necesario llamar a todos los constructores, incluidos los objetos y las clases base. Estas llamadas están implícitas en el constructor. Esto puede crear un segmento de función en línea grande, por lo que no se recomienda utilizar constructores en línea. 2. El constructor no tiene tipo de retorno.

Es un error especificar un tipo de retorno para un constructor porque esto introduce la dirección del constructor. Esto significa que el error no será manejado. De esta manera, si un constructor crea exitosamente un objeto no se determina al devolverlo. De hecho, aunque el constructor de C++ no puede regresar, existe una manera de determinar si la asignación de memoria fue exitosa. Este método es un mecanismo integrado en el lenguaje para manejar emergencias. Un nuevo controlador de puntero de función predeterminado, que se puede configurar como una función definida por el usuario para manejar la falla del nuevo operador. Esta función puede realizar cualquier acción, incluida establecer un indicador de error, reasignar memoria, salir del programa o generar una excepción. Puede utilizar de forma segura los nuevos controladores integrados en el sistema. La mejor manera de hacer que un constructor indique un error es lanzar una excepción. Lanzar una excepción en el constructor borrará todos los objetos y la memoria asignada antes de que ocurriera el error.

Si el constructor falla y se utiliza el manejo de excepciones, puede ser una mejor idea inicializar en otra función. De esta manera, el programador puede construir el objeto de forma segura y obtener un puntero razonable. Luego, llame a la función de inicialización. Si la inicialización falla, el objeto se borra directamente. 3. El constructor no puede declararse estático.

En C++, cada objeto de clase tiene una copia de los miembros de datos de la clase. Sin embargo, este no es el caso de los miembros estáticos. En cambio, todos los objetos * * * tienen un miembro estático. Las funciones estáticas son operaciones que operan sobre clases en lugar de objetos. Puede llamar a funciones estáticas utilizando el nombre de clase y los operadores de control de acción. Una excepción es el constructor, ya que viola los conceptos orientados a objetos.

Un fenómeno similar son los objetos estáticos, que se inicializan al comienzo del programa (antes de la función main()). El siguiente código explica esta situación. MiClase static_object(88, 91); barra hueca()

{

if(static_object . count()> 14) {

. /p>

}

En este ejemplo, las variables estáticas se inicializan al principio. Normalmente estos objetos constan de dos partes. La primera parte es el segmento de datos, donde las variables estáticas se leen en el segmento de datos global. La segunda parte es la función de inicialización estática, que se llama antes de la función main(). Descubrimos que algunos compiladores no verificaron la confiabilidad de la inicialización. Entonces obtienes un objeto no inicializado. La solución es escribir una función contenedora y poner todas las referencias a objetos estáticos en la llamada a esa función. El ejemplo anterior debería reescribirse así.

static mi clase * static _ object = 0; mi clase *

getStaticObject()

{

if (! static_object)

Objeto estático =

Nueva MiClase(87, 92);

Devolver objeto_estático

}Barra hueca()

{

if(getStaticObject()->count()>15)

{

...

}

}

4. El constructor no puede ser una función virtual.

Los constructores virtuales significan que el programador puede crear un objeto sin conocer su tipo exacto antes de ejecutarlo. Los constructores ficticios no son posibles en C++. El lugar más común para encontrar esta situación es al implementar E/S en un objeto. Incluso si en el archivo se proporciona suficiente información interna sobre una clase, aún debemos encontrar una manera de crear una instancia de la clase correspondiente. Sin embargo, los programadores experimentados en C++ tendrán otras formas de simular constructores virtuales.

Para simular una función virtual, es necesario especificar el constructor que se llamará al crear el objeto. El enfoque estándar es llamar a una función miembro virtual. Desafortunadamente, C++ no soporta sintácticamente constructores virtuales. Para sortear esta limitación, existen métodos ya preparados para determinar el objeto del componente en tiempo de ejecución. Estos son equivalentes a constructores virtuales, pero esto es algo que no existe en C++.

El primer método es implementar la selección manualmente usando un interruptor o una declaración de selección if-else. En el siguiente ejemplo, se selecciona la construcción type_info basada en biblioteca estándar, que se admite al activar la información de tipo en tiempo de ejecución. Pero también puede implementar la biblioteca de clases RTTI mediante funciones virtuales.

{

Público:

Virtual const char * get_type_id()const;

Base estática *objeto generado

(const char * tipo _ nombre);

};const char * Base::get _ tipo _ id()const

{

Regresar typeid(*esto). raw_name();

} clase Niño1: Base pública

{

}; clase Niño2: base pública

{< /p >

};Base* Base::make_object(constante char*nombre de tipo)

{

if (strcmp(type_name,

typeid (Hijo1 ). raw_name()) == 0)

Devuelve el nuevo hijo 1;

else if (strcmp(type_name, typeid

(Child2). raw_name(). )) == 0)

Devuelve nuevo Niño2

Otro

{

Lanza una excepción

(" Se pasó el nombre del tipo no reconocido");

Devuelve 0X00 //Indica un valor nulo

}

}

Esta implementación es muy simple , requiere que el programador guarde una tabla que contenga todas las clases en main_object. Esto rompe la encapsulación de la clase base, ya que la clase base debe conocer sus subclases. Red de mezcolanza de secretarias