2014-06-25 23 views
9

Supponiamo di avere la solita classe astratta class Animal e la class Dog : public Animalclass Cat : public Animal che la rende una classe concreta da cui è possibile creare un'istanza di un oggetto. Supponiamo inoltre di avere una funzione foo(Animal a), prendendo sia gatti che cani come oggetti. All'inizio, C++ si compilava in C e creava un vTable mantenendo gli oggetti lì.Quali erano gli idiomi C per il polimorfismo e l'ereditarietà prima che i concetti fossero resi espliciti?

Ma un mio studente mi ha chiesto questa domanda: prima che questi concetti erano soliti discorsi tra i programmatori, come hanno fatto in realtà fanno nel loro giorno per giorno la codifica in C? Qual è stato (è?) Il modo idiomatico di programmare questi concetti in C?

Ho setacciato il kernel di Linux e altri progetti OSS, ma non sono stato in grado di trovare uno schema chiaro: a volte sono i sindacati (per le diverse strutture), a volte i puntatori di funzione, ecc. Ma vorrei come una risposta diretta da parte di persone esperte nel settore che hanno fatto e hanno molta esperienza con C.

In una frase: qual è la C idiomatica per ereditarietà e polimorfismo?

+3

1) unioni intelligenti 2) puntatori a funzione come membri struct/sindacali. Per un esempio, guarda come Linux gestisce i driver di dispositivo (blocco). – wildplasser

+0

Non credo ci fosse un modo idiomatico per farlo, AFAIK era uno dei punti di forza del C++. Anche questo articolo viene in mente http://www.yosefk.com/blog/oo-c-is-passable.html (su emulazione OO in C) –

+0

@wildplasser: Grazie. I "sindacati intelligenti" sono diversi dai "sindacati"? Non ho familiarità con il concetto, dato che ci sono "puntatori" e poi ci sono "puntatori intelligenti". –

risposta

7

Programmi semplici, come quelli scritti per compiti scolastici, implementano il polimorfismo usando una struttura che consiste in un sindacato e opzionalmente un enum come discriminatori di tipo. Ogni "metodo" contiene quindi un'istruzione switch che richiama la funzione appropriata per il sottotipo. Ovviamente questo non si adatta a più sistemi che richiedono la possibilità di aggiungere sottoclassi senza modificare la definizione della classe base.

Il polimorfismo stesso è facilmente espresso con i puntatori di funzione che ricevono un argomento esplicito self. Open-ended ereditarietà può essere raggiunto con la struttura "ereditato" embedding sua superclasse:

struct base { 
    // ... members here ... 
}; 

struct inherited { 
    struct base base; 
    // ... inherited members here ... 
}; 

Puntatori a struct inherited può essere tranquillamente gettato a struct base *, una pratica esplicitamente benedetto dallo standard C. Questi cast sono generalmente nascosti dietro le macro, che possono anche eseguire il controllo del tipo di runtime, ove possibile.

L'implementazione di questo è abbastanza ingombrante, poiché non ci sono modelli, nessuna invocazione automatica di distruttori, nessuna eccezione e nessun STL. In altre parole, la gestione degli errori e il richiamo del distruttore devono essere gestiti con cura dal programmatore, e la varianza del tipo deve essere gestita dai callback in fase di esecuzione (considerare la differenza tra std::sort() e qsort()) o da difficili trucchi per il preprocessore.

Nonostante le difficoltà, è certamente possibile implementare un sottoinsieme significativo di funzionalità C++ in C e persino una parvenza di semplicità di C nel processo. Per studiare esempi reali di questo approccio che viene portato a livello di produzione, dare un'occhiata all'implementazione dell'interprete CPython o del sistema di oggetti glib usato da GTK +.

+0

Grazie! È forse questo pezzo della tua risposta che chiarisce un po ': "I puntatori a struct ereditati possono essere tranquillamente inseriti nella struct base, una pratica esplicitamente benedetta dallo standard C." Questo aiuta. Presumo che * sia * il modo in cui lo hanno fatto, al punto che lo standard parla esplicitamente. –

+0

@DervinThunk Sì, questo è ciò che C++ chiamerebbe "upcast" e persino eseguire implicitamente. – user4815162342

+0

E che dire del contrario: "downcasting", diciamo, quando passi 'struct base', nel tuo esempio, e hai bisogno di trovare l'oggetto" reale "? Immagino che potresti anche passare il tipo di oggetto nella funzione, o il tipo è qualche enum nella struct? Sarebbe una pratica comune? –

-1

Prima dei vantaggi della programmazione orientata agli oggetti, le applicazioni utilizzavano codice procedurale in cui la funzionalità era "incapsulata" in una gerarchia di funzioni con quelle in cima alla gerarchia che lavorano con le astrazioni e diventano sempre più concreti e dettagliati scendere la gerarchia. Alcune API offrono modi per creare strutture che possono essere manipolate solo tramite handle. Esempio: in C, gli handle di file nascondono i dettagli relativi alla gestione dei file.

Per quanto riguarda l'esecuzione di OO in C, ritengo che non sia mai stata un'opzione valida senza un adeguato supporto linguistico con i costrutti appropriati. Quello che otterresti non valeva la pena di essere coinvolto nel simulare OO con i puntatori alle funzioni.

+0

Nella mia esperienza, i programmi C più grandi sono cresciuti * alcuni * tipi di implementazione OOP, tipicamente bug-driven, underperforming e sottospecificati, come si suol dire. Il supporto linguistico incorporato per questo stile era la ragione d'essere del pre-template C++. – user4815162342

0

Ho visto che il codice usato per creare #defines nomi astratti della sorgente e includere i file. per esempio, la stessa routine può essere riutilizzato per affrontare byte, short s, s, intfloat s, e double s. Il codice era disordinato e difficile da capire.

+2

Quello userebbe la macro x per creare codice _generic_, non codice OOP. – ninjalj

+0

polimorfismo = generici. – Jiminion

+0

In un C++ contesto, ritengo polimorfismo si riferisce a runtime metodo di attivazione attivato richiamando 'metodi virtual' override in una sottoclasse attraverso puntatore o un riferimento a una classe base. La programmazione generica in C++ è realizzata con modelli e normalmente non viene definita polimorfismo. – user4815162342