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.
Desafio
FácilVamos 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 tipoTimeropaco 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) efree_timer(recebe um ponteiro para Timer e faz a limpeza). Use include guards com o símboloTIMER_H.timer.c: É aqui que o segredo vive. Defina astruct Timerreal com dois membros ocultos:int secondseint running(use 1 para rodando, 0 para parado). Implemente seu construtor para alocar a struct, inicializarsecondscom o valor fornecido e definirrunningcomo 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 freedO 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;
}
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
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írculo