TODAS as funções virtuais precisam ser implementadas em classes derivadas?

94

Pode parecer uma pergunta simples, mas não consigo encontrar a resposta em nenhum outro lugar.

Suponha que eu tenha o seguinte:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Está tudo bem que a classe Derived não implementa a função bar ()? E se não TODAS as minhas classes derivadas precisarem da função bar (), mas algumas sim. Todas as funções virtuais de uma classe base abstrata precisam ser implementadas nas classes derivadas ou apenas aquelas que são puramente virtuais? obrigado

Mikestaub
fonte

Respostas:

84

As classes derivadas não precisam implementar todas as funções virtuais sozinhas. Eles só precisam implementar os puros . 1 Isso significa que a Derivedclasse da pergunta está correta. Ele herda a barimplementação de sua classe ancestral Abstract,. (Isso pressupõe que Abstract::barestá implementado em algum lugar. O código na pergunta declara o método, mas não o define. Você pode defini-lo in-line como mostra a resposta de Trenki ou pode defini-lo separadamente.)


1 E mesmo assim, apenas se a classe derivada for instanciada . Se uma classe derivada não é instanciada diretamente, mas existe apenas como uma classe base de mais classes derivadas, então são essas classes que são responsáveis ​​por ter todos os seus métodos virtuais puros implementados. A classe "média" na hierarquia tem permissão para deixar alguns métodos virtuais puros não implementados, assim como a classe base. Se a classe "média" faz implementar um método virtual puro, em seguida, seus descendentes vão herdar que a implementação, para que eles não tem que re-implementá-lo eles mesmos.

Rob Kennedy
fonte
3
E mesmo isso (implementação de funções virtuais puras) apenas se elas forem criadas para serem instanciadas (em contraste com serem elas próprias uma classe base abstrata).
Christian Rau
1
Isso é o que pensei. Mas estou fazendo isso no meu projeto, e estou recebendo um erro de vinculação dizendo que há um "símbolo externo não resolvido" para Derived :: bar (); Mas eu nunca declarei bar em Derived, então por que o vinculador está procurando o corpo da função?
mikestaub
1
@pixelpusher Claro que Derived::bartem um corpo de função, o de Abstract::bar. Portanto, parece que a unidade de tradução onde isso é definido (será que está definida em algum lugar?) Não está vinculada à unidade de tradução onde é chamada.
Christian Rau
2
@Rob: They only need to implement the pure ones.É enganoso. As classes derivadas também não precisam necessariamente implementar funções virtuais puras .
Nawaz
Agradeço a ajuda, mas @trenki acertou em cheio. Embora você também estivesse correto, Christian Rau, pois NÃO foi definido.
mikestaub
47

Apenas os métodos virtuais puros devem ser implementados em classes derivadas, mas você ainda precisa de uma definição (e não apenas uma declaração) dos outros métodos virtuais. Se você não fornecer um, o vinculador pode muito bem reclamar.

Portanto, apenas colocar {}depois de seu método virtual opcional fornece uma implementação padrão vazia:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Porém, uma implementação padrão mais envolvida entraria em um arquivo de origem separado.

trenki
fonte
7

O padrão ISO C ++ especifica que todos os métodos virtuais de uma classe que não sejam virtuais puros devem ser definidos.

A regra é simplesmente:
se sua classe derivada substituir o método virtual da classe Base, ela também deverá fornecer uma definição. Caso contrário, a classe Base deverá fornecer a definição desse método.

Conforme a regra acima em seu exemplo de código, virtual void bar();precisa de uma definição na classe Base.

Referência:

Padrão C ++ 03: 10.3 Funções virtuais [class.virtual]

Uma função virtual declarada em uma classe deve ser definida, ou declarada pura (10.4) naquela classe, ou ambos; mas nenhum diagnóstico é necessário (3.2).

Portanto, você deve tornar a função puramente virtual ou fornecer uma definição para ela.

O gcc faq também documenta:

O padrão ISO C ++ especifica que todos os métodos virtuais de uma classe que não sejam virtuais puros devem ser definidos, mas não requer nenhum diagnóstico para violações desta regra [class.virtual]/8 . Com base nessa suposição, o GCC emitirá apenas os construtores definidos implicitamente, o operador de atribuição, o destruidor e a mesa virtual de uma classe na unidade de tradução que define seu primeiro método não embutido.

Portanto, se você não conseguir definir esse método específico, o vinculador pode reclamar da falta de definições para símbolos aparentemente não relacionados. Infelizmente, para melhorar essa mensagem de erro, pode ser necessário alterar o vinculador e isso nem sempre pode ser feito.

A solução é garantir que todos os métodos virtuais que não são puros sejam definidos. Observe que um destruidor deve ser definido mesmo se for declarado virtual puro [class.dtor]/7.

Alok Save
fonte
3

Sim, tudo bem ... você só precisa implementar quaisquer funções virtuais puras para instanciar uma classe derivada de uma classe base abstrata.

Jason
fonte
1

Sim, é correto que uma classe derivada tem que ignorar a função que é virtual pura na classe pai. A classe pai com uma função virtual pura é chamada de classe abstrata apenas porque sua classe filho deve fornecer seu próprio corpo da função virtual pura.

Para as funções virtuais normais: - Não é necessário sobrescrevê-las mais, como algumas classes filho podem ter essa função, outras podem não ter.

O objetivo principal do mecanismo de Função Virtual é o Polimorfismo do Tempo de Execução, sendo que o objetivo principal da Função Virtual Pura (Classe Abstrata) é tornar obrigatório ter o mesmo nome Função com o corpo próprio.

CodeCodeCode
fonte