c++ virtuais Qual é o objetivo de uma função virtual pura privada?




qual das opções a seguir é uma característica das funções amigas (4)

EDITAR: Declarações esclarecidas sobre a capacidade de anular e capacidade de acessar / invocar.

Ele será capaz de substituir essas funções privadas. Por exemplo, o seguinte exemplo inventado funciona ( EDIT: feito método de classe derivado private e elimine a invocação do método de classe derivada em main() para melhor demonstrar a intenção do padrão de design em uso. ):

#include <iostream>

class Engine
{
public:
  void SetState( int var, bool val )
  {
    SetStateBool( var, val );
  }

  void SetState( int var, int val )
  {
    SetStateInt( var, val );
  }

private:

    virtual void SetStateBool(int var, bool val ) = 0;
    virtual void SetStateInt(int var, int val ) = 0;

};

class DerivedEngine : public Engine
{
private:
  virtual void SetStateBool(int var, bool val )
  {
    std::cout << "DerivedEngine::SetStateBool() called" << std::endl;
  }

  virtual void SetStateInt(int var, int val )
  {
    std::cout << "DerivedEngine::SetStateInt() called" << std::endl;
  }
};


int main()
{
  DerivedEngine e;
  Engine * be = &e;

  be->SetState(4, true);
  be->SetState(2, 1000);
}

Private métodos virtual Private em uma classe base, como os do seu código, são normalmente usados ​​para implementar o padrão de design do Método de Modelo . Esse padrão de design permite alterar o comportamento de um algoritmo na classe base sem alterar o código na classe base. O código acima, onde os métodos da classe base são invocados através de um ponteiro de classe base, é um exemplo simples do padrão Template Method.

Eu me deparei com o seguinte código em um arquivo de cabeçalho:

class Engine
{
public:
    void SetState( int var, bool val );
    {   SetStateBool( int var, bool val ); }

    void SetState( int var, int val );
    {   SetStateInt( int var, int val ); }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;    
};

Para mim, isso implica que a classe Engine ou uma classe derivada dela precisa fornecer a implementação dessas funções virtuais puras. Mas eu não achava que as classes derivadas pudessem ter acesso a essas funções privadas para reimplementá-las - então, por que torná-las virtuais?


A função virtual pura privada é a base do idioma da interface não-virtual (OK, não é absolutamente virtual puro , mas ainda virtual lá). Claro, isso também é usado para outras coisas, mas acho isso muito útil (em duas palavras: em uma função pública, você pode colocar algumas coisas comuns (como registro, estatísticas, etc.) no início e no início. no final da função e, em seguida, "no meio" para chamar essa função virtual privada, que será diferente para a classe derivada específica.

class Base
{
    // ..
public:
    void f();
private:
    virtual void DerivedClassSpecific() = 0;
   // ..
};
void Base::f()
{
    //.. Do some common stuff
    DerivedClassSpecific();
    //.. Some other common stuff
}
// ..

class Derived: public Base
{
    // ..
private:
    virtual void DerivedClassSpecific();
    //..
};
void Derived::DerivedClassSpecific()
{
    // ..
}

Pure virtual - apenas obriga as classes derivadas a implementá-lo.

EDIT : Mais sobre isso: Wikipedia::NVI-idiom


Bem, por um lado, isso permitiria que uma classe derivada implementasse uma função que a classe base (contendo a declaração de função virtual pura) pudesse chamar.


A questão no tópico sugere uma confusão bastante comum. A confusão é comum o suficiente, que C ++ FAQ defendeu contra o uso de virtuais privados, por um longo tempo, porque a confusão parecia ser uma coisa ruim.

Então, para se livrar da confusão primeiro: sim, as funções virtuais privadas podem ser substituídas nas classes derivadas. Os métodos de classes derivadas não podem chamar funções virtuais da classe base, mas podem fornecer sua própria implementação para eles. De acordo com Herb Sutter, ter uma interface não virtual pública na classe base e uma implementação privada que pode ser customizada nas classes derivadas, permite uma melhor "separação da especificação da interface da especificação do comportamento personalizável da implementação". Você pode ler mais sobre isso em seu artigo "Virtuality" .

Há, no entanto, mais uma coisa interessante no código que você apresentou, que merece mais atenção, na minha opinião. A interface pública consiste em um conjunto de funções não virtuais sobrecarregadas e essas funções chamam funções virtuais não-públicas e não sobrecarregadas. Como de costume no mundo C ++, é uma expressão idiomática, tem um nome e, claro, é útil. O nome é (surpresa, surpresa!)

"Vencedores públicos não sobrecarregados de chamadas virtuais não sobrecarregados de chamadas virtuais"

Isso ajuda a gerenciar adequadamente a regra de ocultação . Você pode ler mais sobre isso here , mas vou tentar explicar isso em breve.

Imagine que as funções virtuais da classe Engine também são sua interface e é um conjunto de funções sobrecarregadas que não é virtual puro. Se eles fossem virtuais puros, ainda se poderia encontrar o mesmo problema, conforme descrito abaixo, mas menor na hierarquia de classes.

class Engine
{
public:
    virtual void SetState( int var, bool val ) {/*some implementation*/}
    virtual void SetState( int var, int val )  {/*some implementation*/}
};

Agora vamos supor que você queira criar uma classe derivada e você precisa fornecer uma nova implementação somente para o método, que leva dois ints como argumentos.

class MyTurbochargedV8 : public Engine
{
public:
    // To prevent SetState( int var, bool val ) from the base class,
    // from being hidden by the new implementation of the other overload (below),
    // you have to put using declaration in the derived class
    using Engine::SetState;

    void SetState( int var, int val )  {/*new implementation*/}
};

Se você esqueceu de colocar a declaração using na classe derivada (ou para redefinir a segunda sobrecarga), você pode ter problemas no cenário abaixo.

MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);

Se você não impediu a ocultação dos membros do Engine , a declaração:

myV8->SetState(5, true);

iria chamar void SetState( int var, int val ) da classe derivada, convertendo true para int .

Se a interface não for virtual e a implementação virtual não for pública, como na sua análise, o autor da classe derivada tem um problema a menos para pensar e pode simplesmente escrever

class MyTurbochargedV8 : public Engine
{
private:
    void SetStateInt(int var, int val )  {/*new implementation*/}
};




non-virtual-interface