La regola del primo membro
Fa parte della sezione Programmazione orientata agli oggetti del percorso C di Coddy. Lezione 29 di 61.
Quando incorpori una struct come primo membro di un'altra struct, accade qualcosa di potente: l'indirizzo di memoria della struct esterna è identico all'indirizzo di memoria del suo primo membro. Questo è garantito dallo standard C.
Considera il nostro esempio precedente:
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // Primo membro
int grade;
} Child;Quando crei una variabile Child, il membro Parent si trova proprio all’inizio della memoria di Child. Ciò significa che un puntatore a Child punta esattamente allo stesso indirizzo di un puntatore al suo membro Parent.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // Conversione sicura!Entrambi i puntatori contengono lo stesso indirizzo. Questo ti permette di passare un Child* a qualsiasi funzione che si aspetta un Parent*:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
// In main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // Works perfectlyQuesta tecnica è il fondamento dell'ereditarietà in C. Una funzione scritta per il tipo base può operare su qualsiasi tipo derivato, purché il tipo base sia incorporato per primo. Il cast è sicuro perché le disposizioni della memoria corrispondono perfettamente.
Sfida
FacileEsploriamo la regola del primo membro creando una semplice gerarchia di animali. Creerai una struct base Animal e una struct derivata Dog, poi mostrerai come un puntatore a Dog possa essere convertito in sicurezza in un puntatore a Animal.
L'idea chiave che applicherai: quando Animal è il primo membro di Dog, entrambe le struct condividono lo stesso indirizzo di memoria iniziale. Ciò significa che puoi passare un Dog* a qualsiasi funzione che si aspetta un Animal*, convertendo il puntatore.
Organizzerai il codice in tre file:
animal.h: Definisci entrambe le struct con le protezioni di inclusione. La structAnimaldeve avere un solo campo:legs(un intero che rappresenta il numero di zampe). La structDogdeve contenereAnimalcome primo membro e aggiungere un camponame(un array di caratteri di 50 caratteri). Dichiara anche una funzioneprint_animalche accetta un parametroAnimal*.animal.c: Implementa la funzioneprint_animal. Questa funzione opera sul tipo base. Conosce soloAnimale stampa quante zampe ha l'animale.main.c: Crea una variabileDoge valorizzane i campi (sia le zampe dell'animale incorporato sia il nome del cane). Poi mostra la regola del primo membro convertendo il tuoDog*in unAnimal*e passandolo aprint_animal. Stampa anche separatamente il nome del cane per mostrare il quadro completo.
Riceverai due input: il nome del cane (una stringa) e il numero di zampe (un intero).
L'output dovrebbe essere così:
Dog: Buddy
Legs: 4Dove Buddy è il nome del cane e 4 è il numero di zampe. Stampa prima il nome del cane da main.c, poi chiama print_animal con il puntatore convertito per visualizzare le zampe.
Questo dimostra come una funzione scritta per il tipo base (Animal) possa funzionare senza problemi con un tipo derivato (Dog) tramite la conversione dei puntatori: il fondamento dell'ereditarietà in C.
Provalo tu
#include <stdio.h>
#include <string.h>
#include "animal.h"
int main() {
// Leggi l'input
char name[50];
int legs;
scanf("%s", name);
scanf("%d", &legs);
// TODO: Crea una variabile Dog
// TODO: Imposta il nome del cane usando strcpy
// TODO: Imposta il numero di zampe (accedendo tramite Animal incorporato)
// TODO: Stampa il nome del cane nel formato: "Dog: <name>"
// TODO: Esegui il cast da Dog* ad Animal* e chiama print_animal
// Questo dimostra la regola del primo membro!
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Basi di programmazione modulare
File di intestazioneGuardie di inclusioneFile sorgenteFunzioni staticheRipasso: calcolatrice modulare4Incapsulamento
Il concetto di puntatori opachiDefinire struct opacheGetter e setterLa convalida nei setterRiepilogo: scatola segreta2Oggetti e metodi
Le struct come oggettiIl puntatore 'Self'Correttezza constPuntatore o valoreMetodi di supportoRiepilogo: gestore di punti5Progetto: Conto bancario semplice
Configurazione del progettoImplementazione del conto8Polimorfismo
Puntatori a funzione nelle structSimulare i metodiIl concetto di interfacciaImplementare le interfacceIterazione polimorficaRiepilogo: Greeter11Pattern di progettazione in C
Pattern SingletonPattern FactoryPattern IteratorRiepilogo: Factory di Logger3Ciclo di vita degli oggetti
Pattern del costruttorePattern del distruttoreInizializzazione sullo stackCopia profondaRiepilogo: wrapper di stringhe6Ereditarietà tramite composizione
Incorporamento delle structLa regola del primo membroAccesso ai membri della classe baseUpcastingRiepilogo: gerarchia di forme9Progetto: Disegnatore di forme
Panoramica del progettoImplementazione del cerchioImplementazione del rettangoloUtilizzo polimorficoContenitore di formeEsercitati da solo: Compilatore C online