Pergunta

digamos Vamos temos um class Apple concreto. (Objetos da Apple pode ser instanciado.) Agora, alguém vem e deriva uma class Peach abstrato da Apple. É abstrato porque introduz uma nova função virtual pura. O usuário do pêssego agora é forçado a tirar dela e definir esta nova função. É este um padrão comum? Isto é correto fazer?

Amostra:


class Apple
{
public:
    virtual void MakePie();
    // more stuff here
};

class Peach : public Apple { public: virtual void MakeDeliciousDesserts() = 0; // more stuff here };

Agora vamos dizer que temos um class Berry concreto. Alguém deriva um class Tomato abstrato de Berry. É abstrato, porque ele substitui uma das funções virtuais de Berry, e torna puro virtual. O utilizador de tomate tem de voltar a aplicar a função anteriormente definido em Berry. É este um padrão comum? Isto é correto fazer?

Amostra:


class Berry
{
public:
    virtual void EatYummyPie();
    // more stuff here
};

class Tomato : public Berry { public: virtual void EatYummyPie() = 0; // more stuff here };

Nota: Os nomes são inventados e não refletem qualquer código real (espero). Não frutas foram prejudicados na redação desta questão.

Foi útil?

Solução

Re Peach da Apple:

  • Não faça isso se a Apple é uma classe de valor (Isto é, tem cópia ctor, não-idênticos casos pode ser igual, etc). Ver Meyers Mais Effective C ++ item 33 para o porquê.
  • Não faça isso se a Apple tem um público destructor não virtual, caso contrário você convidar comportamento indefinido quando o seu usuários excluir um Apple através de uma ponteiro para Peach.
  • Caso contrário, provavelmente você está seguro, porque você não tenha violado Liskov substituibilidade . A Peach IS-A Apple.
  • Se você possui o código da Apple, preferem fator de uma classe base abstrata comum (Fruit talvez) e derivar Apple e pêssego com isso.

Re Tomate de Berry:

  • O mesmo que acima, mais:
  • Evite, porque é incomum
  • Se você deve, documento que classes derivadas de tomate deve fazer a fim de não violar Liskov substituibilidade. A função que você está substituindo em Berry - vamos chamá-lo Juice() - impõe certos requisitos e faz certas promessas. implementações de Juice() das classes derivadas não mais deve exigir e prometem não menos. Em seguida, um DerivedTomato IS-A Berry e um código que só sabe sobre Berry é seguro.

Possivelmente, você vai conhecer o último requisito, documentando que DerivedTomatoes deve chamar Berry::Juice(). Se assim for, considerar o uso de Template Method vez:

class Tomato : public Berry
{
public:
    void Juice() 
    {
        PrepareJuice();
        Berry::Juice();
    }
    virtual void PrepareJuice() = 0;
};

Agora, há uma excelente chance de que um tomate é-A Berry, ao contrário das expectativas botânicos. (A exceção é se implementações de PrepareJuice das classes derivadas impor pré-condições extras além das impostas pelo Berry::Juice).

Outras dicas

Parece-me como uma indicação de um design ruim. Poderia ser forçado se você queria ter uma definição concreta de uma biblioteca fechada e estendê-lo e ramificar um monte de coisas fora dela, mas naquele momento eu estaria considerando seriamente a diretriz sobre encapsulamento sobre Herança .. Se você possivelmente pode encapsular , você provavelmente deveria.

Sim, quanto mais eu penso sobre isso, esta é uma idéia muito ruim.

Não necessariamente errado , mas definitivamente mau cheiro. Especialmente se você deixar a fruta no sol por muito tempo. (E eu não acho que o meu dentista gostaria me comer maçãs concretas.)

No entanto, a principal coisa que eu vejo aqui que o mau cheiro não é tanto a classe abstrata derivada de uma classe concreta, mas a hierarquia de herança muito profunda.

EDIT: re-ler, vejo que estas são duas hierarquias. Todo o material fruto tenho me misturado.

Se você usar a prática recomendada de ter modelo de herança "é-um", então esse padrão seria muito bonito nunca vêm-se.

Uma vez que você tem uma classe concreta, você está dizendo que é algo que você pode realmente criar uma instância. Se você, em seguida, derivar uma classe abstrata a partir dele, em seguida, algo que é um atributo da classe base não é verdade para a classe derivada, que deve definir de klaxons que algo não está certo.

Olhando para o seu exemplo, um pêssego não é uma maçã, por isso não deve ser derivada dela. Mesmo é verdadeiro para tomate provenientes de Berry.

Este é o lugar onde eu normalmente aconselhar contenção, mas que não parecem mesmo ser um modelo bom, já que um Apple não contém um pêssego.

Neste caso, gostaria de fatorar a interface comum -. PieFilling ou DessertItem

um pouco incomum, mas se você tivesse alguma outra subclasse da classe base e as subclasses da classe abstrata tinha coisas comum o suficiente para justificar a existência da classe abstrata como:

class Concrete
{
public:
    virtual void eat() {}
};
class Sub::public Concrete { // some concrete subclass
    virtual void eat() {}
};
class Abstract:public Concrete // abstract subclass
{
public:
    virtual void eat()=0;
    // and some stuff common to Sub1 and Sub2
};
class Sub1:public Abstract {
    void eat() {}
};
class Sub2:public Abstract {
    void eat() {}
};
int main() {
    Concrete *sub1=new Sub1(),*sub2=new Sub2();
    sub1->eat();
    sub2->eat();
    return 0;
}

Hmmm ... pelo pensamento "o que é um ....." por um par de segundos, eu chegar a uma conclusão que não é comum ... Além disso, eu não iria Peach derivam da Apple e tomate de Berry ... você tem algum exemplo melhor:)

É um monte de coisas estranhas que você pode fazer em C ++ ... Eu não posso nem pensar em 1% dele ...

Sobre substituir um virtual com um virtual pura, você pode provavelmente apenas escondê-lo e ele vai ser realmente estranho ...

Se você puder encontrar um compilador estúpido C ++ que ligaria esta função como um virtual, então você vai ter tempo de execução pura chamada de função virtual ...

Eu acho que isso só pode ser feito por um corte e não tenho idéia do tipo de corte realmente ...

Para responder à sua primeira pergunta, você pode fazer isso desde que os usuários da Apple, se for dado um exemplo concreto derivado de Peach não vai saber nada diferente. E o exemplo não vai saber que não é um Apple (a menos que haja algumas funções virtuais da Apple que são substituídas que você não nos dizer sobre).

Eu ainda não pode imaginar como seria útil para substituir uma função virtual com um virtual puro - é que, mesmo legal

?

Em geral, você quer se conformar com Scott Meyers "Faça todas as classes não-folha abstrato" de itens de seus livros.

De qualquer forma, para além de que o que você descreve parece ser legal -. Seu justo que eu não posso ver você precisar dele, que muitas vezes

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top