sobrecarga - que es un destructor en c++
Constructor por defecto en C (13)
¿Hay alguna manera de tener algún tipo de constructor predeterminado (como uno de C ++) para los tipos de usuario C definidos con una estructura?
Ya tengo una macro que funciona como un inicializador rápido (como el de pthread_mutex
), pero quería saber si, por casualidad, puede tener algunos (o todos) los campos de una estructura rellenada en la declaración.
Por ejemplo, con el ejemplo pthread_mutex
, me gustaría
pthread_mutex_t my_mutex;
tener el mismo efecto que
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
Aquí hay un poco de magia macro alrededor de los literales compuestos malloc()
, memcpy()
y C99:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define new(TYPE, ...) memdup(&(TYPE){ __VA_ARGS__ }, sizeof(TYPE))
void * memdup(const void * obj, size_t size)
{
void * copy = malloc(size);
return copy ? memcpy(copy, obj, size) : NULL;
}
struct point
{
int x;
int y;
};
int main()
{
int * i = new(int, 1);
struct point * p = new(struct point, 2, 3);
printf("%i %i %i", *i, p->x, p->y);
return 0;
}
Básicamente, C ++ crea listas de punteros que contienen las direcciones de los métodos. Esta lista se llama definición de clase (hay más datos en la clase def, pero ignoramos eso por el momento).
Un patrón común para tener "clases" en C puro es definir una "clase de estructura". Uno de los campos de la estructura es una función de fábrica que devuelve "instancias" de la clase. Sugiero usar una macro para ocultar los moldes:
typedef struct __class * class;
typedef void (*ctor_ptr)(class);
struct class {
char * name;
ctor_ptr ctor;
... destructor and other stuff ...
}
#define NEW(clz) ((struct something *)(((struct class *)clz)->ctor(clz)))
Ahora puede definir las clases que tiene creando estructuras de tipo "struct class" para cada clase que tenga y luego llame al constructor almacenado en ellas. Lo mismo aplica para el destructor, etc.
Si desea métodos para sus instancias, debe ponerlos en las estructuras de clase y mantener un puntero a la clase en la estructura de la instancia:
#define NEW_SOMETHING() ((struct something *)NEW(&something_definition))
#define METHOD(inst, arg) ((struct something_class *)(((struct something *)inst)->clz)->method(inst, arg))
NEW_SOMETHING
creará una nueva instancia de "algo" usando la definición de clase almacenada en la estructura something_definition
. METHOD invocará un "método" en esta instancia. Tenga en cuenta que para el código real, querrá comprobar que inst
es en realidad una instancia de something
(compare los punteros de clase, algo así).
Tener herencia es un poco complicado y se deja como un ejercicio para el lector.
Tenga en cuenta que puede hacer todo lo que puede hacer en C ++ en C. puro. Un compilador de C ++ no reemplaza mágicamente su CPU con otra cosa. Solo se necesita mucho más código para hacerlo.
Si desea ver un ejemplo, consulte glib (parte del proyecto Gtk +).
C ++ es diferente de C en este caso en el sentido de que no tiene "clases". Sin embargo, C (como muchos otros lenguajes) aún puede usarse para programación orientada a objetos. En este caso, su constructor puede ser una función que inicializa una estructura. Esto es lo mismo que los constructores (solo una sintaxis diferente). Otra diferencia es que debe asignar el objeto utilizando malloc () (o alguna variante). En C ++, simplemente usaría el operador ''nuevo''.
por ejemplo, código C ++:
class A {
public:
A() { a = 0; }
int a;
};
int main()
{
A b;
A *c = new A;
return 0;
}
código C equivalente:
struct A {
int a;
};
void init_A_types(struct A* t)
{
t->a = 0;
}
int main()
{
struct A b;
struct A *c = malloc(sizeof(struct A));
init_A_types(&b);
init_A_types(c);
return 0;
}
la función ''init_A_types'' funciona como un constructor en C ++.
El uso de funciones para crear y eliminar estructuras ya ha sido mencionado. Pero en un comentario mencionaste que querías un "constructor predeterminado": creo que quieres decir que quieres inicializar algunos (¿todos?) De los campos de estructura a los valores predeterminados.
Esto se hace en C usando alguna convención de codificación: funciones, macros o una mezcla. Normalmente lo hago de la siguiente manera:
struct some_struct {
int a;
float b;
};
#define some_struct_DEFAULT { 0, 0.0f}
struct some_struct *some_struct_create(void) {
struct some_struct *ptr = malloc(sizeof some_struct);
if(!ptr)
return ptr;
*ptr = some_struct_DEFAULT;
return ptr;
}
// (...)
struct some_struct on_stack = some_struct_DEFAULT;
struct some_struct *on_heap = some_struct_create();
Es posible que desee echar un vistazo a un compilador de C ++. Le brinda más funciones orientadas a objetos de las que puede recordar en un lenguaje que tiene una sintaxis similar a C de una manera más elegante y estándar que intentar atornillar constructores y demás en C con bibliotecas y macros.
Si eso no funciona para usted, eche un vistazo a GObject. http://en.wikipedia.org/wiki/GObject . Es un sistema de objetos para C usado en GTK y Gnome que básicamente hace girar "objetos en C" a 11.
De la Wikipedia:
El GLib Object System, o GObject, es una biblioteca de software libre (cubierta por la LGPL) que proporciona un sistema de objetos portátil y una interoperabilidad transparente entre idiomas.
El sistema admite constructores, destructores, herencia única, interfaces, métodos virtuales públicos y privados, etc. También es mucho más tedioso y difícil que hacer las mismas cosas en C ++. ¡Que te diviertas!
Hablemos de la solución de ingeniería completa que se consideró la mejor práctica en los viejos tiempos.
El problema con las estructuras es que todo es público, por lo que no se ocultan datos.
Podemos arreglarlo.
Usted crea dos archivos de encabezado. Uno es el archivo de encabezado "público" utilizado por los clientes de su código. Contiene definiciones como esta:
typedef struct t_ProcessStruct *t_ProcessHandle;
extern t_ProcessHandle NewProcess();
extern void DisposeProcess(t_ProcessHandle handle);
typedef struct t_PermissionsStruct *t_PermissionsHandle;
extern t_PermissionsHandle NewPermissions();
extern void DisposePermissions(t_PermissionsHandle handle);
extern void SetProcessPermissions(t_ProcessHandle proc, t_PermissionsHandle perm);
luego creas un archivo de encabezado privado que contiene definiciones como esta:
typedef void (*fDisposeFunction)(void *memoryBlock);
typedef struct {
fDisposeFunction _dispose;
} t_DisposableStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PID _pid;
/* etc */
} t_ProcessStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PERM_FLAGS _flags;
/* etc */
} t_PermissionsStruct;
y luego en su implementación puede hacer algo como esto:
static void DisposeMallocBlock(void *process) { if (process) free(process); }
static void *NewMallocedDisposer(size_t size)
{
assert(size > sizeof(t_DisposableStruct);
t_DisposableStruct *disp = (t_DisposableStruct *)malloc(size);
if (disp) {
disp->_dispose = DisposeMallocBlock;
}
return disp;
}
static void DisposeUsingDisposer(t_DisposableStruct *ds)
{
assert(ds);
ds->_dispose(ds);
}
t_ProcessHandle NewProcess()
{
t_ProcessHandle proc = (t_ProcessHandle)NewMallocedDisposer(sizeof(t_ProcessStruct));
if (proc) {
proc->PID = NextPID(); /* etc */
}
return proc;
}
void DisposeProcess(t_ProcessHandle proc)
{
DisposeUsingDisposer(&(proc->_disposer));
}
Lo que sucede es que usted realiza declaraciones para sus estructuras en sus archivos de encabezado públicos. Ahora sus estructuras son opacas, lo que significa que los clientes no pueden penetrar con ellas. Luego, en la declaración completa, incluye un destructor al comienzo de cada estructura al que puede llamar de forma genérica. Puede usar el mismo asignador malloc para todos la misma función de eliminación y así sucesivamente. Realiza funciones públicas de configuración / obtención de los elementos que desea exponer.
De repente, tu código es mucho más sensato. Solo puede obtener structs de asignadores o funciones que llamen asignadores, lo que significa que puede obstaculizar la inicialización. Construyes destructores para que el objeto pueda ser destruido. Y en tu ir. Por cierto, un nombre mejor que t_DisposableStruct podría ser t_vTableStruct, porque eso es lo que es. Ahora puede construir herencia virtual al tener una vTableStruct que es todo punteros a funciones. También puede hacer cosas que no puede hacer en un lenguaje de oo puro (típicamente), como cambiar elementos seleccionados de la tabla de presentación sobre la marcha.
El punto importante es que existe un patrón de ingeniería para hacer que las estructuras sean seguras e inicializables.
No, las estructuras no son más que un montón de datos. No puede declarar funciones dentro de las estructuras, por lo que no hay forma de crear un constructor para ella.
No, no directamente. Lo más cerca que puede hacer es escribir una función que asigna una instancia y llena algunos de los campos.
Puede crear funciones de inicializador que toman un puntero a una estructura. Esta era una práctica común.
También funciona para crear una estructura e inicializarla (como una fábrica), por lo que nunca hay un momento en que la estructura esté "sin inicializar" en el código del "cliente". Por supuesto, eso supone que las personas siguen la convención y usan el "constructor" / fábrica ...
horrible pseudo código sin error al verificar malloc o gratis
somestruct* somestruct_factory(/* per haps some initializer agrs? */)
{
malloc some stuff
fill in some stuff
return pointer to malloced stuff
}
void somestruct_destructor(somestruct*)
{
do cleanup stuff and also free pointer
free(somestruct);
}
Alguien probablemente vendrá y explicará cómo algunos preprocesadores / compiladores de C ++ trabajaron para hacer todo esto en C.
Puede escribir una función que devuelva la estructura C:
struct file create_file(int i, float f) {
struct file obj = { i, f };
// other code here...
return obj;
}
Si te preguntas si puedes tener funciones miembro "normales" en C. Bueno, puedes hasta cierto punto. Prefiero el estilo de objeto como primer argumento. Pasas un puntero a tu estructura como primer argumento. De esta forma, puede tener varias funciones, definiendo la interfaz para sus objetos:
int file_get_integer(struct file *self) { return self->i; }
float file_get_float(struct file *self) { return self->f; }
Si escribe con ese estilo, lo que tiene al final es un tipo de datos abstracto. He visto chicos emulando la sintaxis de llamada de función miembro utilizada en C ++ al tener punteros de función en su estructura y luego hacer:
obj.get_integer(&obj);
Es utilizado por el kernel de Linux para definir la interfaz de los controladores del sistema de archivos. Es un estilo de escritura que a uno le puede gustar o no le gusta. No me gusta demasiado, porque sigo usando miembros de estructuras para datos y no para emular llamadas a funciones miembro para que se vean en los lenguajes orientados a objetos populares.
Realmente no. Si mal no recuerdo, lo más cerca que llegas a las clases son estructuras. Usted asigna memoria y llena los campos. No veo cómo harías un tipo de constructor genérico para esto. Teóricamente, podría escribir una macro que haría algo de esto. Sin embargo, no estoy seguro de si eso realmente vale la pena.
Suponiendo que quiere hacer esto en C, entonces su pregunta no es sobre las estructuras en C ++:
Lo que generalmente hago es crear una función, init_whatever, que toma un puntero a la estructura (u otra variable) y lo establece en los valores que quiero.
Si se trata de una variable global (que se inicializa a cero), se puede simular un constructor sin argumentos con un indicador "inicializado", y luego dejar que las otras funciones verifiquen ese indicador e inicializar la estructura si el indicador es cero. Sin embargo, no estoy nada seguro de que sea una buena idea.
Y, como señaló otra persona, también podrías hacer algo horrible con las macros ...
Fallowing es el programa simple que hace uso de un constructor. La función "default_constructor" se llama dentro de main sin ninguna llamada de función explícita.
#include <stdio.h>
void __attribute__ ((constructor)) default_constructor()
{
printf("%s/n", __FUNCTION__);
}
int main()
{
printf("%s/n",__FUNCTION__);
return 0;
}
Salida: default_constructor main
Dentro de la función constructora puede incluir algunas instrucciones de inicialización en la función constructora y finalmente puede hacer que la liberación en el destructor sea en barbecho,
#include <stdio.h>
#include <stdlib.h>
struct somestruct
{
int empid;
char * name;
};
struct somestruct * str1;
void __attribute__ ((constructor)) a_constructor()
{
str1 = (struct somestruct *) malloc (sizeof(struct somestruct));
str1 -> empid = 30228;
str1 -> name = "Nandan";
}
void __attribute__ ((destructor)) a_destructor()
{
free(str1);
}
int main()
{
printf("ID = %d/nName = %s/n", str1 -> empid, str1 -> name);
return 0;
}
Espero que esto te ayudará.