Peço desculpas se essa for uma pergunta óbvia, mas em C, Opaque Pointers são semelhantes a Java Interfaces? Como estou tentando entendê-los, estou interpretando-os de forma semelhante a Java interfaces no sentido de que eles estabelecem uma maneira obrigatória de interagir com o método/função? Então, se eu quisesse usar um Opaque Pointer, eu precisaria defini-lo em um arquivo .h enquanto eu o chamaria em um arquivo .c? Opaque pointers apenas definem a assinatura do método/função (no arquivo .h) enquanto o arquivo .c pega essas funções com essas assinaturas e as implementa.
Não. Um ponteiro opaco é um design onde a implementação de um tipo (geralmente
struct
; opcionalmentetypedef
'ed) é oculta (encapsulada) do cliente. Este módulo pode instanciar o tipo da forma que achar melhor; frequentemente alocado dinamicamente (e desalocado). Os clientes precisam usar funções fornecidas pelo módulo para operar no ponteiro opaco.Você terá algo parecido com isto no seu arquivo de cabeçalho (interface):
e então o módulo implementa é:
Como adendo à resposta de Allan Wind, ponteiros opacos dependem de um fato muito simples: todos os ponteiros struct têm o mesmo tamanho 1 , enquanto o tamanho das coisas para as quais eles apontam pode diferir muito.
Em C, as funções devem saber os tamanhos de seus argumentos, variáveis locais e valores de retorno. Portanto, em nenhum lugar do arquivo de cabeçalho podemos passar ou retornar um tipo de dado abstrato por valor .
Como o tipo de dado real é definido no arquivo de implementação e, portanto, essa unidade de tradução está "ciente" do tamanho dos dados, podemos trabalhar em valores do tipo de dado abstrato diretamente ali.
Isso permite que o tipo de dado seja definido de qualquer forma que desejarmos, desde que o arquivo de cabeçalho permaneça consistente. Melhorias podem ser feitas na implementação sem ter que ajustar como o tipo de dado é realmente usado por programas clientes.
1 O tamanho de um ponteiro depende da plataforma de hardware e software.
Sua interpretação está no caminho certo, mas está faltando um ponto-chave: o motivo pelo qual usamos ponteiros opacos é para que possamos manter os detalhes da implementação da estrutura ocultos, para que possamos modificar essa estrutura em uma versão futura do nosso software sem quebrar o código existente que a utiliza.
Essencialmente, a analogia com Java é a seguinte:
A declaração do ponteiro opaco, e de cada função que aceita (ou retorna) tal ponteiro opaco, corresponde a um
interface
em Java. (Observação: apenas as declarações. Não suas definições reais.)A estrutura apontada pelo ponteiro opaco e o conjunto de definições de função são mais como uma classe Java concreta cuja
implements
interface contém apenas campos de membros privados, de modo que ninguém pode acessá-los.Outro conceito muito similar a ponteiros opacos é handles . A
open()
função retorna anint
que é um identificador de arquivo , osread()
métodoswrite()
, eclose()
aceitam tal identificador como parâmetro. Esse número pode ser qualquer coisa, você não deve interpretá-lo de forma alguma ou esperar que seu valor esteja dentro de qualquer intervalo específico. Em teoria, em um sistema onde anint
é grande o suficiente para conter um ponteiro para a estrutura, um identificador de arquivo pode, na verdade, ser um ponteiro struct que foi convertido paraint
. Na prática, é um índice em alguma tabela deFILE
(ou similar) que é mantido inteiramente pela biblioteca padrão (ou pelo sistema operacional), então você não consegue vê-lo e, portanto, não pode estragá-lo.Esses são todos mecanismos muito primitivos para atingir o encapsulamento que as pessoas usavam antes da invenção da Programação Orientada a Objetos. E como não havia princípios orientadores e nenhuma maneira padrão de fazer as coisas, as pessoas estavam fazendo hacks aqui e ali, que violavam o encapsulamento de várias maneiras.
Por exemplo, a biblioteca padrão C poderia ser tratada
FILE
como um ponteiro opaco, mas por algum motivo a maioria das implementações opta por não fazê-lo, porque elas definem ostruct
forFILE
em um arquivo de cabeçalho que você pode realmente#include
. (Veja Stack Overflow: Onde FILE é definido no Unix )Como outro exemplo, você não deve interpretar o valor de um identificador de arquivo, mas os identificadores de arquivo para os fluxos padrão (entrada, saída, erro) têm valores muito conhecidos publicamente, que são, essencialmente, nada mais que números mágicos .
Não, é algo completamente sem relação.
Ponteiros opacos são ponteiros para s especificados incompletamente (externamente)
struct
, usados para esconder a representação interna da estrutura. Melhor dito, eles são ponteiros para tipos definidos incompletamente (uma estrutura de campo não especificada, ou um array de comprimento não especificado). Dessa forma, o ponteiro pode ser usado, mas não os dados apontados, pois o conteúdo da estrutura é desconhecido para o compilador.No código acima, definimos o tipo
hash_table_p
como um ponteiro para uma estrutura indefinida. Esse ponteiro pode ser usado por rotinas de tabela de hash como um dado opaco que é fornecido ao usuário, apenas para passá-lo de volta para as rotinas que manipulam os aspectos internos da estrutura como campos externamente inacessíveis.Como ponteiros para tipos não são intercambiáveis, referências de ponteiros não podem ser intermisturadas, fazendo com que os ponteiros para
struct hash_table
sempre tenham que ser inicializados com uma referência de tabela hash autêntica (algo que, se você for cuidadoso, sempre será garantido pelo compilador). Um ponteiro para um tipo diferente nunca será compatível (exceto se você o converter) com um ponteiro para um tipo diferente. A única exceção é o ponteiro paravoid
que é compatível com qualquer outro tipo de ponteiro.Você pode deixar externamente a declaração como tal, e no código de implementação, fornecer uma definição completa de
struct hash_table
. O compilador permitirá que você declare quantos ponteiros do tipo referenciado, mas os internos dos dados são ocultos para o código externo. Astruct hash_table
estrutura é definida normalmente em umhash_tableP.h
arquivo include (que também inclui o externo) e assim C pode emular as unidades de compilaçãoDEFINITION MODULE
andIMPLEMENTATION MODULE
da linguagem Modula-2 (ou opackage
andpackage body
do Ada)Interfaces Java, por outro lado, são conjuntos de declarações de métodos que permitem que classes orientadas a objetos façam
implement
isso, assim chamado, interface, e sejam tratadas como objetos de uma classe que implementa todos esses métodos. Isso de alguma forma define uma classe como objeto, mas sem derivá-la de outra classe.C não pode associar um conjunto de declarações de procedimentos a um tipo, para fazer com que apenas instâncias que implementam esses procedimentos sejam consideradas pertencentes a um único tipo (se um tipo puder ser comparado a uma classe), então não há figura semelhante em C ao modelo de interface Java.