Sono particolarmente interessato agli oggetti che devono essere utilizzati all'interno di C, al contrario delle implementazioni di oggetti che formano il nucleo di linguaggi interpretati come Python.Quali tecniche/strategie usano le persone per costruire oggetti in C (non C++)?
risposta
Librerie come GObject.
Fondamentalmente GObject fornisce un modo comune per descrivere valori opachi (interi, stringhe) e oggetti (descrivendo manualmente l'interfaccia - come una struttura di puntatori di funzione, fondamentalmente correspoinding a un VTable in C++) - maggiori informazioni sulla struttura possono essere trovato nelle sue VTables reference
Si potrebbe spesso anche a mano implementare come in "COM in plain C"
EFraim - Potresti descrivere la strategia di implementazione di GObject nella tua risposta in modo da poterla confrontare con alcune delle altre soluzioni fornite? – SetJmp
Guarda IJG's attuazione. Non usano solo setjmp/longjmp per la gestione delle eccezioni, hanno vtables e tutto. È una libreria ben scritta e abbastanza piccola da poter dare un ottimo esempio.
tendo a fare qualcosa di simile:
struct foo_ops {
void (*blah)(struct foo *, ...);
void (*plugh)(struct foo *, ...);
};
struct foo {
struct foo_ops *ops;
/* data fields for foo go here */
};
Con queste definizioni di strutture, il foo codice che implementa simile a questa:
static void plugh(struct foo *, ...) { ... }
static void blah(struct foo *, ...) { ... }
static struct foo_ops foo_ops = { blah, plugh };
struct foo *new_foo(...) {
struct foo *foop = malloc(sizeof(*foop));
foop->ops = &foo_ops;
/* fill in rest of *foop */
return foop;
}
Poi, nel codice che utilizza pippo:
struct foo *foop = new_foo(...);
foop->ops->blah(foop, ...);
foop->ops->plugh(foop, ...);
Questo codice può essere riordinato con macro o funzioni inline in modo che appaia più simile a C
foo_blah(foop, ...);
foo_plugh(foop, ...);
anche se si bastone con un tempo ragionevolmente breve nome per il campo "ops", semplicemente scrivendo il codice mostrato in origine non è particolarmente dettagliato.
Questa tecnica è del tutto adeguata per l'implementazione di un progetto basato su oggetti relativamente semplice in C, ma è non gestire i requisiti più avanzati come rappresentare esplicitamente le classi e l'ereditarietà del metodo. Per quelli, potresti aver bisogno di qualcosa come GObject (come menzionato da EFraim), ma ti suggerisco di assicurarti che tu abbia davvero bisogno delle funzionalità extra dei framework più complessi.
L'uso del termine "oggetti" è un po 'vago, quindi presumo che tu stia chiedendo come usare C per raggiungere determinati aspetti della programmazione orientata agli oggetti (sentiti libero di correggermi su questo presupposto .)
Metodo polimorfismo:
Metodo polimorfismo è tipicamente emulata in C usando puntatori a funzione. Per esempio, se ho avuto una struct che ho usato per rappresentare un image_scaler (cosa che richiede un'immagine e ridimensiona a nuove dimensioni), avrei potuto fare qualcosa di simile:
struct image_scaler {
//member variables
int (*scale)(int, int, int*);
}
Poi, ho potuto fare diverse scaler di immagini come ad esempio:
struct image_scaler nn, bilinear;
nn->scale = &nearest_neighbor_scale;
bilinear->scale = &bilinear_scale;
Questo mi permette di ottenere un comportamento polimorfico per qualsiasi funzione che prende in un image_scaler e usa è il metodo scala semplicemente passando un image_scaler diverso.
Inheritance
L'ereditarietà è di solito realizzato in quanto tale:
struct base{
int x;
int y;
}
struct derived{
struct base;
int z;
}
Ora, io sono libero di utilizzare i campi extra di derivati, insieme con ottenere tutti i campi 'ereditato' di base. Inoltre, se si dispone di una funzione che accetta solo una struttura struct. puoi semplicemente lanciare il tuo struct dervied pointer in un puntatore struct base senza conseguenze
Grazie Falain (ho upvoted). Potresti indicare una libreria che fa questo così io o chiunque altro possa esaminare un'intera implementazione? – SetJmp
Simile all'approccio di Dale ma un po 'più di un fucile a pedale è come PostgreSQL rappresenti internamente nodi di albero di analisi, tipi di espressioni e simili. Ci sono predefiniti Node
e Expr
struct, lungo le linee di
typedef struct {
NodeTag n;
} Node;
dove NodeTag
è un typedef per unsigned int, e c'è un file di intestazione con un mazzo di costanti che descrivono tutti i possibili tipi di nodo. Nodi stessi aspetto:
typedef struct {
NodeTag n = FOO_NODE;
/* other members go here */
} FooNode;
e FooNode
possono essere espressi in una Node
impunemente, a causa di un capriccio di C struct: se due struct hanno identiche primi membri, possono essere espressi all'altro.
Sì, questo significa che è possibile trasmettere un FooNode
a un BarNode
, che probabilmente non si desidera eseguire. Se vuoi il corretto controllo del tipo di runtime, GObject è la strada da percorrere, anche se preparati a odiare la vita mentre stai imparando a farlo.
(nota:. Esempi dalla memoria, non hanno inciso sulle parti interne Postgres in un po 'Il developer FAQ ha più informazioni.)
Molto tempo fa (circa 1988) ho lavorato su un compilatore C in cui i nodi di analisi erano un po 'uniti di singoli tipi di nodi, con un tag di tipo al di fuori dell'unione per decidere quale ramo dell'unione era valido: questo è moralmente equivalente a ciò tu descrivi. Oggi, quasi certamente lo farei sulla falsariga del mio suggerimento sopra. Trovo che l'uso di puntatori di funzione mi costringa a definire l'API pubblica desiderata - poiché il chiamante non conosce il nome della funzione che sta chiamando, è molto difficile che superi quella funzione per utilizzare i dati interni sull'implementazione. –
Sigh ... s/bit union/big union/in sopra. Ci scusiamo per l'errore di battitura. –
L'approccio del puntatore a funzione è decisamente superiore: per prima cosa, consente di approssimare i metodi di associazione all'oggetto. Mi piacciono i tuoi esempi e ho votato. –
Come si può vedere da navigare tutte le risposte, ci sono librerie, puntatori di funzioni, metodi di ereditarietà, incapsulamento, ecc., tutti disponibili (il C++ era originariamente un front-end per C).
Tuttavia, ho trovato che un aspetto MOLTO importante per il software è la leggibilità . Hai provato a leggere il codice di 10 anni fa? Di conseguenza , tendo a prendere l'approccio più semplice quando fare le cose come oggetti in C.
porre la seguente:
- È questo per un cliente con una scadenza (in tal caso, prendere in considerazione OOP) ?
- Posso utilizzare un OOP (spesso meno codice, più veloce da sviluppare, più leggibile)?
- Posso usare una libreria (codice esistente, modelli esistenti)?
- Sono limitato dalla memoria o dalla CPU (ad esempio Arduino)?
- Esiste un altro motivo tecnico per utilizzare C?
- Posso mantenere la mia C molto semplice e leggibile?
- Quali funzioni OOP ho veramente bisogno del mio progetto?
Io di solito tornare ad una cosa del genere l'API GLIB, che mi permette di incapsulare il mio codice e fornisce un'interfaccia molto leggibile. Se è necessario più , aggiungo i puntatori alle funzioni per il polimorfismo.
class_A.h:
typedef struct _class_A {...} Class_A;
Class_A* Class_A_new();
void Class_A_empty();
...
#include "class_A.h"
Class_A* my_instance;
my_instance = Class_A_new();
my_instance->Class_A_empty(); // can override using function pointers
- 1. Perché le persone non usano i tetraedri per le skybox?
- 2. Quando si usano oggetti funzione in C++?
- 3. Perché le persone usano ProjectData
- 4. In che modo le persone usano Yeoman?
- 5. C++ perché le persone non usano maiuscole nel nome dei file di intestazione?
- 6. pixel.gif, perché le persone lo usano?
- 7. Perché le persone non usano <CFLOGIN>?
- 8. Perché le persone usano Command-line invece di IDE?
- 9. Perché le persone usano ATL per la programmazione COM?
- 10. Non si usano eccezioni C++ per progettazione, in llvm/clang
- 11. Quali sono le funzionalità più abusate in Visual Studio/C#?
- 12. Come si usano le sezioni in app.config C# 4.0?
- 13. Capire quante persone usano il mio software
- 14. Perché le persone usano i = i + 1 invece di i ++?
- 15. Combinare gli ABI C++ per costruire contro le librerie legacy
- 16. C++ OpenCV: tracciare le persone in movimento sulla strada
- 17. Per quali ragioni le persone scelgono Ruby su Java?
- 18. Quando si usano le esuberanti ctags quali opzioni usare?
- 19. Quali attributi .Net le persone applicano al loro codice?
- 20. Come si usano numeri casuali in C#?
- 21. Che modalità usano le persone quando usano Emacs per modificare pagine Web che contengono CSS, javascript e HTML?
- 22. C++ Pointer oggetti vs non Pointer Oggetti
- 23. Identificatore sconosciuto quando si usano le costanti C#
- 24. Come costruire un framework plug-in C#?
- 25. Quali sono le convenzioni di denominazione in C#?
- 26. Quali sono alcuni modi in cui le persone distribuiscono le modifiche ai database relazionali utilizzando Node.js?
- 27. Oggetti transazionali in C#?
- 28. JavaScript Speech-to-Text per le persone non vedenti
- 29. Quale hosting normalmente usano le persone per pubblicare il sito Web Django?
- 30. Quali classi stringa usare in C++?
Anche se ho sempre usato quello che immagino si possa chiamare uno stile di programmazione C "object based", è molto, molto più facile a questo (e quasi a tutto il resto) in C++. Devo chiedere perché non sembra che tu voglia usarlo? –
@Neil: se non ci fosse alcun motivo, non ci sarebbe alcun GObject. Ci sono molte ragioni per C: piattaforme obsolete, protezione, compatibilità ABI binaria, il tuo nome. – EFraim
@EFraim Sono perfettamente a conoscenza di questi motivi, ma stavo chiedendo quali fossero le ragioni del questionario. –