Como configuro uma classe que representa uma interface? Isso é apenas uma classe base abstrata?
c++
inheritance
interface
abstract-class
pure-virtual
Aaron Fischer
fonte
fonte
Respostas:
Para expandir a resposta de bradtgmurray , convém fazer uma exceção à lista de métodos virtuais puros da sua interface adicionando um destruidor virtual. Isso permite que você passe a propriedade do ponteiro para outra parte sem expor a classe derivada concreta. O destruidor não precisa fazer nada, porque a interface não possui membros concretos. Pode parecer contraditório definir uma função como virtual e embutida, mas confie em mim - não é.
Você não precisa incluir um corpo para o destruidor virtual - alguns compiladores têm problemas para otimizar um destruidor vazio e é melhor usar o padrão.
fonte
=0
destruidor virtual ( ) puro com um corpo. A vantagem aqui é que o compilador pode, teoricamente, ver que o vtable não tem membros válidos agora e descartá-lo completamente. Com um destruidor virtual com um corpo, o destruidor pode ser chamado (virtualmente), por exemplo, no meio da construção viathis
ponteiro (quando o objeto construído ainda é doParent
tipo) e, portanto, o compilador deve fornecer uma tabela válida. Portanto, se você não chamar explicitamente os destruidores virtuaisthis
durante a construção :), poderá economizar no tamanho do código.override
palavra-chave para permitir argumentos em tempo de compilação e verificar o tipo de valor de retorno. Por exemplo, na declaração de Childvirtual void OverrideMe() override;
Faça uma aula com métodos virtuais puros. Use a interface criando outra classe que substitui esses métodos virtuais.
Um método virtual puro é um método de classe definido como virtual e atribuído a 0.
fonte
override
em C ++ 11Todo o motivo pelo qual você possui uma categoria de tipo de interface especial, além das classes base abstratas em C # / Java, é porque o C # / Java não suporta herança múltipla.
O C ++ suporta herança múltipla e, portanto, não é necessário um tipo especial. Uma classe base abstrata sem métodos não abstratos (virtual puro) é funcionalmente equivalente a uma interface C # / Java.
fonte
Thread
instância. A herança múltipla pode ter um design ruim e também uma composição. Tudo depende do caso.Não existe um conceito de "interface" propriamente dito em C ++. AFAIK, as interfaces foram introduzidas pela primeira vez em Java para solucionar a falta de herança múltipla. Esse conceito acabou sendo bastante útil, e o mesmo efeito pode ser alcançado em C ++ usando uma classe base abstrata.
Uma classe base abstrata é uma classe na qual pelo menos uma função de membro (método no Java Java) é uma função virtual pura declarada usando a seguinte sintaxe:
Uma classe base abstrata não pode ser instanciada, ou seja, você não pode declarar um objeto da classe A. Você só pode derivar classes de A, mas qualquer classe derivada que não forneça uma implementação de
foo()
também será abstrata. Para deixar de ser abstrata, uma classe derivada deve fornecer implementações para todas as funções virtuais puras que herda.Observe que uma classe base abstrata pode ser mais do que uma interface, porque pode conter membros de dados e funções de membro que não são puramente virtuais. Um equivalente de uma interface seria uma classe base abstrata sem dados com apenas funções virtuais puras.
E, como Mark Ransom apontou, uma classe base abstrata deve fornecer um destruidor virtual, como qualquer classe base, nesse caso.
fonte
Até onde pude testar, é muito importante adicionar o destruidor virtual. Estou usando objetos criados
new
e destruídos comdelete
.Se você não adicionar o destruidor virtual na interface, o destruidor da classe herdada não será chamado.
Se você executar o código anterior sem
virtual ~IBase() {};
, verá que o destruidorTester::~Tester()
nunca é chamado.fonte
Minha resposta é basicamente a mesma que as outras, mas acho que há duas outras coisas importantes a fazer:
Declare um destruidor virtual em sua interface ou faça um destruidor não virtual protegido para evitar comportamentos indefinidos se alguém tentar excluir um objeto do tipo
IDemo
.Use herança virtual para evitar problemas com herança múltipla. (Muitas vezes, há herança múltipla quando usamos interfaces.)
E como outras respostas:
Use a interface criando outra classe que substitui esses métodos virtuais.
Ou
E
fonte
No C ++ 11, você pode facilmente evitar completamente a herança:
Nesse caso, uma interface possui semântica de referência, ou seja, você precisa garantir que o objeto sobreviva à interface (também é possível fazer interfaces com semântica de valores).
Esse tipo de interface tem seus prós e contras:
Finalmente, a herança é a raiz de todo mal no design de software complexo. Em Sean Parent, o valor da semântica e do polimorfismo baseado em conceitos (altamente recomendado, são explicadas melhores versões dessa técnica), o seguinte caso é estudado:
Digamos que eu tenha um aplicativo no qual trato de formas polimorficamente usando a
MyShape
interface:No seu aplicativo, você faz o mesmo com formas diferentes usando a
YourShape
interface:Agora diga que deseja usar algumas das formas que desenvolvi em seu aplicativo. Conceitualmente, nossas formas têm a mesma interface, mas para fazer com que minhas formas funcionem em seu aplicativo, você precisará estender minhas formas da seguinte maneira:
Primeiro, modificar minhas formas pode não ser possível. Além disso, a herança múltipla leva o caminho para o código espaguete (imagine um terceiro projeto que use a
TheirShape
interface ... o que acontece se eles também chamarem sua função drawmy_draw
?).Atualização: Há algumas novas referências sobre polimorfismo não baseado em herança:
fonte
Circle
classe é um projeto pobre. Você deve usarAdapter
padrão nesses casos. Desculpe se vai parecer um pouco duro, mas tente usar alguma biblioteca da vida real comoQt
antes de fazer julgamentos sobre herança. A herança torna a vida muito mais fácil.Adapter
padrão? Estou interessado em ver suas vantagens.Square
ainda não está lá? Presciência? É por isso que está separado da realidade. E, na realidade, se você optar por confiar na biblioteca "MyShape", poderá adotar sua interface desde o início. No exemplo de formas, existem muitas bobagens (uma das quais é que você tem duasCircle
estruturas), mas o adaptador seria algo parecido com isso -> ideone.com/UogjWkTodas as boas respostas acima. Uma coisa extra que você deve ter em mente - você também pode ter um destruidor virtual puro. A única diferença é que você ainda precisa implementá-lo.
Confuso?
A principal razão pela qual você gostaria de fazer isso é se você deseja fornecer métodos de interface, como eu, mas faça a substituição deles opcional.
Para transformar a classe em uma classe de interface, é necessário um método virtual puro, mas todos os seus métodos virtuais têm implementações padrão; portanto, o único método que resta para tornar o virtual puro é o destruidor.
Reimplementar um destruidor na classe derivada não é grande coisa - eu sempre reimplemento um destruidor, virtual ou não, nas minhas classes derivadas.
fonte
Se você estiver usando o compilador C ++ da Microsoft, poderá fazer o seguinte:
Eu gosto dessa abordagem porque resulta em um código de interface muito menor e o tamanho do código gerado pode ser significativamente menor. O uso do novtable remove todas as referências ao ponteiro do vtable nessa classe, para que você nunca possa instancia-lo diretamente. Veja a documentação aqui - novtable .
fonte
novtable
o padrãovirtual void Bar() = 0;
= 0;
que adicionei). Leia a documentação se não a entender.= 0;
e assumi que era apenas uma maneira não-padrão de fazer exatamente o mesmo.Uma pequena adição ao que está escrito lá em cima:
Primeiro, verifique se o seu destruidor também é virtual
Segundo, você pode querer herdar virtualmente (em vez de normalmente) ao implementar, apenas por boas medidas.
fonte
Você também pode considerar as classes de contrato implementadas com o NVI (Non Virtual Interface Pattern). Por exemplo:
fonte
Eu ainda sou novo no desenvolvimento de C ++. Comecei com o Visual Studio (VS).
No entanto, ninguém parece mencionar isso
__interface
no VS (.NET) . Estou não muito certo se esta é uma boa maneira de declarar uma interface. Mas parece fornecer uma aplicação adicional (mencionada nos documentos ). De modo que você não precisa especificar explicitamente ovirtual TYPE Method() = 0;
, pois ele será convertido automaticamente.Se alguém tiver algo interessante sobre isso, compartilhe. :-)
Obrigado.
fonte
Embora seja verdade que esse
virtual
é o padrão de fato para definir uma interface, não vamos esquecer o padrão clássico do tipo C, que vem com um construtor em C ++:Isso tem a vantagem de poder vincular novamente o tempo de execução de eventos sem precisar construir sua classe novamente (como o C ++ não possui uma sintaxe para alterar tipos polimórficos, é uma solução alternativa para as classes camaleão).
Dicas:
click
o construtor do seu descendente.protected
membro e ter umpublic
referência e / ou getter.if
alterações s vs. estado em seu código, isso pode ser mais rápido queswitch()
es ouif
s (espera-se uma recuperação em torno de 3-4if
s, mas sempre meça primeiro.std::function<>
mais de ponteiros de função, você pode ser capaz de gerenciar todos os seus dados de objeto dentroIBase
. A partir deste ponto, você pode ter esquemas de valores paraIBase
(por exemplo,std::vector<IBase>
funcionará). Observe que isso pode ser mais lento, dependendo do seu compilador e código STL; também que as implementações atuaisstd::function<>
tendem a ter uma sobrecarga quando comparadas com ponteiros de função ou até funções virtuais (isso pode mudar no futuro).fonte
Aqui está a definição de
abstract class
padrão c ++n4687
13.4.2
fonte
Resultado: área do retângulo: 35 área do triângulo: 17
Vimos como uma classe abstrata definiu uma interface em termos de getArea () e duas outras classes implementaram a mesma função, mas com algoritmos diferentes para calcular a área específica da forma.
fonte