Menu
Coddy logo textTech

Definindo Structs Opacas

Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 18 de 61.

Agora que você entende o conceito de ponteiros opacos, vamos ver como completar o padrão. O cabeçalho declara que um tipo existe, mas o arquivo de origem é onde você define o que realmente há dentro.

Aqui está a configuração completa. Primeiro, o cabeçalho apenas com a declaração antecipada:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Tipo incompleto

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

Então, o arquivo de origem contém a definição real da struct:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // Oculto do exterior!
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

O ponto principal: struct Counter { ... } aparece apenas no arquivo .c. Qualquer arquivo que inclua counter.h sabe que Counter existe, mas não consegue ver value ou step. Tentar escrever c->value em main.c causaria um erro de compilação — os membros são verdadeiramente invisíveis.

Essa separação é a base do encapsulamento em C. O próximo passo é fornecer acesso controlado por meio de funções getter e setter.

challenge icon

Desafio

Fácil

Vamos construir um módulo Timer que demonstra o padrão de ponteiro opaco (opaque pointer). Você praticará a ocultação da definição da struct dentro do arquivo de origem, tornando seus membros internos completamente invisíveis para qualquer código que use seu módulo.

Você criará três arquivos:

  • timer.h: Esta é a sua interface pública. Declare um tipo Timer opaco usando apenas uma declaração antecipada (forward declaration) — sem o corpo da struct aqui! Também declare duas funções: create_timer (recebe um inteiro para os segundos e retorna um ponteiro para Timer) e free_timer (recebe um ponteiro para Timer e faz a limpeza). Use include guards com o símbolo TIMER_H.
  • timer.c: É aqui que o segredo vive. Defina a struct Timer real com dois membros ocultos: int seconds e int running (use 1 para rodando, 0 para parado). Implemente seu construtor para alocar a struct, inicializar seconds com o valor fornecido e definir running como 1. Implemente o destrutor para liberar a memória.
  • main.c: Use seu módulo Timer para criar um timer e confirmar que ele foi criado com sucesso. Como os membros da struct estão ocultos, você não pode acessá-los diretamente — este é o objetivo principal! Simplesmente imprima uma mensagem confirmando a criação, depois libere o timer e imprima uma mensagem de limpeza.

Você receberá uma entrada: o número de segundos (um inteiro) para inicializar o timer.

No seu arquivo principal, crie um timer com o valor de segundos fornecido. Imprima uma mensagem de criação, depois libere o timer e imprima uma confirmação.

Imprima a saída neste formato:

Timer created
Timer freed

O ponto fundamental aqui é que o main.c pode trabalhar com ponteiros de Timer, mas não tem como acessar seconds ou running diretamente. Tentar escrever t->seconds no main.c causaria um erro de compilador porque o corpo da struct só é definido em timer.c. Isso é o verdadeiro encapsulamento — os detalhes de implementação estão completamente ocultos dos usuários do seu módulo.

Folha de consulta

O padrão de ponteiro opaco separa a declaração de um tipo de sua definição, ocultando detalhes de implementação.

O arquivo de cabeçalho contém apenas uma declaração antecipada (forward declaration):

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Tipo incompleto

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

O arquivo de origem contém a definição real da struct:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // Oculto do exterior
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

A definição da struct aparece apenas no arquivo .c. Arquivos que incluem o cabeçalho sabem que o tipo existe, mas não podem acessar seus membros diretamente. Isso fornece encapsulamento verdadeiro em C.

Experimente você mesmo

#include <stdio.h>
#include "timer.h"

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: Criar um temporizador com o valor de segundos fornecido
    
    // TODO: Imprimir "Timer created"
    
    // TODO: Liberar o temporizador
    
    // TODO: Imprimir "Timer freed"
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming