Eu sei que a final
palavra-chave é usada para impedir que o método virtual seja substituído por classes derivadas. No entanto, não consigo encontrar nenhum exemplo útil quando realmente devo usar a final
palavra-chave com o virtual
método Ainda mais, parece que o uso de final
métodos virtuais é um mau cheiro, pois impede que os programadores estendam a classe no futuro.
Minha pergunta é a seguinte:
Existe algum caso útil quando eu realmente devo usar final
na virtual
declaração de método?
Respostas:
Uma recapitulação do que a
final
palavra - chave faz: digamos que temos classe baseA
e classe derivadaB
. A funçãof()
pode ser declarada emA
comovirtual
, significando que a classeB
pode substituí-la. Mas então a classeB
pode desejar que qualquer classe da qual seja derivada posteriormenteB
não possa substituí-laf()
. É quando precisamos declararf()
comofinal
emB
. Sem afinal
palavra - chave, uma vez que definimos um método como virtual, qualquer classe derivada estará livre para substituí-lo. Afinal
palavra-chave é usada para acabar com essa liberdade.Um exemplo de por que você precisaria disso: suponha que a classe
A
defina uma função virtualprepareEnvelope()
e a classe aB
substitua e a implemente como uma sequência de chamadas para seus próprios métodos virtuaisstuffEnvelope()
,lickEnvelope()
esealEnvelope()
. O ClassB
pretende permitir que as classes derivadas substituam esses métodos virtuais para fornecer suas próprias implementações, mas a classeB
não deseja permitir que nenhuma classe derivada substituaprepareEnvelope()
e, portanto, altere a ordem das coisas, lamba, lacre ou omita a chamada de uma delas. Portanto, neste caso, a classeB
declaraprepareEnvelope()
como final.fonte
class B might wish that any class which is further derived from B should not be able to override f()
? Existe algum caso no mundo real em que essa classe B e o método f () existam e se encaixam bem?Geralmente, do ponto de vista do design, é útil poder marcar as coisas como imutáveis. Da mesma forma, o
const
compilador fornece proteções e indica que um estado não deve ser alterado.final
Pode ser usado para indicar que o comportamento não deve ser alterado ainda mais na hierarquia de herança.Exemplo
Considere um videogame no qual os veículos levam o jogador de um local para outro. Todos os veículos devem verificar se estão viajando para um local válido antes da partida (certificando-se de que a base no local não seja destruída, por exemplo). Podemos começar usando o idioma da interface não virtual (NVI) para garantir que essa verificação seja feita independentemente do veículo.
Agora, digamos que temos veículos voadores em nosso jogo, e algo que todos os veículos voadores exigem e têm em comum é que eles devem passar por uma inspeção de segurança dentro do hangar antes da decolagem.
Aqui, podemos
final
garantir que todos os veículos voadores passarão por essa inspeção e também comunicar esse requisito de design dos veículos voadores.Ao usar
final
dessa maneira, estamos efetivamente estendendo a flexibilidade do idioma da interface não virtual para fornecer um comportamento uniforme na hierarquia de herança (mesmo como uma reflexão tardia, combatendo o frágil problema da classe base) para as próprias funções virtuais. Além disso, adquirimos espaço de manobra para fazer alterações centrais que afetam todos os tipos de veículos voadores como uma reflexão tardia, sem modificar cada implementação de veículo voador existente.Este é um exemplo de uso
final
. Existem contextos que você encontrará onde simplesmente não faz sentido a substituição de uma função de membro virtual - isso pode levar a um design frágil e a uma violação dos seus requisitos de design.É aí que
final
é útil do ponto de vista de design / arquitetura.Também é útil do ponto de vista de um otimizador, pois fornece ao otimizador essas informações de design que permitem desirtualizar as chamadas de função virtual (eliminando a sobrecarga dinâmica de despacho e, muitas vezes de maneira mais significativa, eliminando uma barreira de otimização entre o chamador e o chamado).
Questão
Dos comentários:
Não faz sentido para uma classe base na raiz de uma hierarquia declarar uma função como ambos
virtual
efinal
. Isso me parece bastante tolo, pois faria com que o compilador e o leitor humano tivessem que passar por obstáculos desnecessários que podem ser evitados simplesmente evitando-se devirtual
imediato nesse caso. No entanto, as subclasses herdam funções de membro virtual da seguinte forma:Nesse caso, se
Bar::f
usa ou não explicitamente a palavra-chave virtual,Bar::f
é uma função virtual. Avirtual
palavra-chave se torna opcional neste caso. Portanto, pode fazer sentidoBar::f
ser especificado comofinal
, mesmo que seja uma função virtual ( sófinal
pode ser usada para funções virtuais).E algumas pessoas podem preferir, estilisticamente, indicar explicitamente que
Bar::f
é virtual, assim:Para mim, é meio redundante usar ambos
virtual
efinal
especificadores para a mesma função neste contexto (da mesma formavirtual
eoverride
), mas é uma questão de estilo nesse caso. Algumas pessoas podem achar quevirtual
comunica algo valioso aqui, como usarextern
para declarações de função com ligação externa (mesmo que seja opcional sem outros qualificadores de ligação).fonte
private
método dentro de suaVehicle
classe? Você não quis dizer emprotected
vez disso?private
não se estende à substituição (um pouco contra-intuitivo). Especificadores públicos / protegidos / privados para funções virtuais aplicam-se apenas a chamadores e não a overriders, de maneira grosseira. Uma classe derivada pode substituir funções virtuais de sua classe base, independentemente de sua visibilidade.protected
pode fazer um pouco mais de sentido intuitivo. Eu apenas prefiro alcançar a menor visibilidade possível sempre que posso. Eu acho que a razão pela qual a linguagem é projetada dessa maneira é que, caso contrário, funções-membro virtuais privadas não fariam sentido algum fora do contexto da amizade, já que nenhuma classe, mas um amigo, poderia substituí-las se os especificadores de acesso importassem no contexto de substituir e não apenas de ligar.protected
para evitar confundir os outros. Acabei colocando um comentário, pelo menos, descrevendo como as funções virtuais privadas ainda podem ser substituídas.Ele permite muitas otimizações, porque pode ser conhecido em tempo de compilação qual função é chamada.
Cuidado ao jogar a palavra "cheiro de código". "final" não torna impossível estender a aula. Clique duas vezes na palavra "final", pressione backspace e estenda a classe. NO ENTANTO final é uma excelente documentação que o desenvolvedor não espera que você substitua a função e que, por isso, o próximo desenvolvedor deve ter muito cuidado, porque a classe pode parar de funcionar corretamente se o método final for substituído dessa maneira.
fonte
final
evirtual
sempre seria usado ao mesmo tempo?final
eoverride
.final
pode realmente ajudá-los