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 bem no início da memória de Child. Isso significa que um ponteiro para Child aponta exatamente para o mesmo endereço que um ponteiro para seu membro Parent.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // Conversão de tipo segura!Ambos os ponteiros armazenam 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);
}
// Em 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 de tipo é segura porque os layouts de memória se alinham perfeitamente.
Desafio
FácilVamos explorar a regra do primeiro membro criando 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 ideia principal 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). Declare também 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 em main.c e, em seguida, chame print_animal com o ponteiro convertido para exibir o número de pernas.
Isso demonstra como uma função escrita para o tipo base (Animal) pode funcionar perfeitamente com um tipo derivado (Dog) por meio da conversão de ponteiros: o fundamento da herança em C.
Experimente você mesmo
#include <stdio.h>
#include <string.h>
#include "animal.h"
int main() {
// Leia a entrada
char name[50];
int legs;
scanf("%s", name);
scanf("%d", &legs);
// TODO: Crie uma variável Dog
// TODO: Defina o nome do cachorro usando strcpy
// TODO: Defina o número de pernas (acesse por meio de Animal incorporado)
// TODO: Imprima o nome do cachorro no formato: "Dog: <name>"
// TODO: Converta 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çalhoProteções de inclusãoArquivos-fonteFunções estáticasRevisão: calculadora modular4Encapsulamento
Conceito de ponteiros opacosDefinindo structs opacasGetters e settersValidação em settersRevisão: caixa secreta2Objetos e métodos
Structs como objetosO ponteiro 'Self'Correção de constPonteiro vs. valorMétodos auxiliaresRevisão: Gerenciador de pontos5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de vida do objeto
Padrão de construtorPadrão de destrutorInicialização da pilhaCópia profundaRevisão: wrapper de String6Herança por composição
Incorporação de structRegra do primeiro membroAcessando membros do tipo paiUpcastingRevisão: hierarquia de formas9Projeto: Desenhador de Formas
Visão geral do projetoImplementação do círculoImplementação do retânguloUso polimórficoContêiner de formasPratique por conta própria: Compilador de C online