A Regra do Primeiro Membro
Parte da seção Programação Orientada a Objetos do Journey de C da Coddy. Lição 29 de 61.
Quando você incorpora uma struct como o primeiro membro de outra struct, algo poderoso acontece: o endereço de memória da struct externa é idêntico ao endereço de memória de seu primeiro membro. Isso é garantido pelo padrão C.
Considere nosso exemplo anterior:
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // Primeiro membro
int grade;
} Child;Quando você cria uma variável Child, o membro Parent fica exatamente no início da memória de Child. Isso significa que um ponteiro para Child aponta para o mesmo endereço exato que um ponteiro para seu membro Parent.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // Cast seguro!Ambos os ponteiros contêm o mesmo endereço. Isso permite passar um Child* para qualquer função que espere um Parent*:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
// No main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // Funciona perfeitamenteEssa técnica é a base da herança em C. Uma função escrita para o tipo base pode operar em qualquer tipo derivado, desde que a base seja incorporada primeiro. A conversão é segura porque os layouts de memória se alinham perfeitamente.
Desafio
FácilVamos explorar a regra do primeiro membro construindo uma hierarquia simples de animais. Você criará uma struct base Animal e uma struct derivada Dog e, em seguida, demonstrará como um ponteiro para Dog pode ser convertido com segurança em um ponteiro para Animal.
A principal ideia que você aplicará é: quando Animal é o primeiro membro de Dog, ambas as structs compartilham o mesmo endereço inicial de memória. Isso significa que você pode passar um Dog* para qualquer função que espere um Animal* convertendo o ponteiro.
Você organizará seu código em três arquivos:
animal.h: Defina ambas as structs com guardas de inclusão. Sua structAnimaldeve ter um único campo:legs(um inteiro que representa o número de pernas). Sua structDogdeve incorporarAnimalcomo seu primeiro membro e adicionar um camponame(um array de caracteres com 50 caracteres). Também declare uma funçãoprint_animalque recebe um parâmetroAnimal*.animal.c: Implemente a funçãoprint_animal. Essa função trabalha com o tipo base. Ela conhece apenasAnimale exibe quantas pernas o animal tem.main.c: Crie uma variávelDoge preencha seus campos (tanto as pernas do animal incorporado quanto o nome do cachorro). Em seguida, demonstre a regra do primeiro membro convertendo seuDog*em umAnimal*e passando-o paraprint_animal. Exiba também o nome do cachorro separadamente para mostrar o quadro completo.
Você receberá duas entradas: o nome do cachorro (uma string) e o número de pernas (um inteiro).
Sua saída deve ser semelhante a esta:
Dog: Buddy
Legs: 4Em que Buddy é o nome do cachorro e 4 é o número de pernas. Exiba primeiro o nome do cachorro a partir de main.c e, em seguida, chame print_animal com o ponteiro convertido para exibir as pernas.
Isso demonstra como uma função escrita para o tipo base (Animal) pode trabalhar perfeitamente com um tipo derivado (Dog) por meio da conversão de ponteiros: a base da herança em C.
Experimente você mesmo
#include <stdio.h>
#include <string.h>
#include "animal.h"
int main() {
// Ler entrada
char name[50];
int legs;
scanf("%s", name);
scanf("%d", &legs);
// TODO: Crie uma variável Dog
// TODO: Defina o nome do dog usando strcpy
// TODO: Defina o número de pernas (acesse através do Animal embutido)
// TODO: Imprima o nome do dog no formato: "Dog: <name>"
// TODO: Faça cast de Dog* para Animal* e chame print_animal
// Isso demonstra a regra do primeiro membro!
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do CírculoImplementação do RetânguloUso PolimórficoContainer de FormasPratique por conta própria: Compilador de C online