Pergunta

http://en.wikipedia.org/wiki/Diamond_problem

Eu sei o que isso significa, mas que medidas posso tomar para evitá-lo?

Foi útil?

Solução

Um exemplo prático:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

Observe como herda de classe D de ambos B & C. Mas ambos B & C herdam A. Isso vai resultar em 2 cópias da classe à ser incluído no vtable.

Para resolver isso, precisamos de herança virtual. É classe A que necessidades a ser praticamente hereditária. Então, isso vai corrigir o problema:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

Outras dicas

herança virtual. Isso é o que está lá.

eu ia ficar a usar herança múltipla de apenas interfaces. Enquanto múltipla herança de classes é, por vezes atraentes, ele também pode ser confuso e doloroso se você contar com ele regularmente.

A herança é uma arma forte, forte. Usá-lo somente quando você realmente precisa dele. No passado, a herança diamante era um sinal de que eu estava indo para longe com classificação, dizendo que um usuário é um "empregado", mas eles também são um "ouvinte Widget", mas também um ...

Nestes casos, é fácil de acertar vários problemas de herança.

Eu resolvi-los usando composição e ponteiros volta para o proprietário:

Antes:

class Employee : public WidgetListener, public LectureAttendee
{
public:
     Employee(int x, int y)
         WidgetListener(x), LectureAttendee(y)
     {}
};

Depois:

class Employee
{
public:
     Employee(int x, int y)
         : listener(this, x), attendee(this, y)
     {}

     WidgetListener listener;
     LectureAttendee attendee;
};

Sim, os direitos de acesso são diferentes, mas se você pode ir longe com tal abordagem, sem duplicação de código, é melhor porque é menos poderoso. (Você pode salvar o poder para quando você não tem nenhuma alternativa.)

class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

Neste os atributos da classe A repetido duas vezes na classe D que faz uso de mais memória ... Então, para economizar memória fazemos um atributo virtual para todos os atributos herdados da classe A que são armazenados em um Vtable.

Bem, a grande coisa sobre o temido diamante é que é um erro quando ele ocorre. A melhor maneira de evitar é descobrir a sua estrutura de herança de antemão. Por exemplo, um projeto que eu trabalho em tem espectadores e editores. Os editores são subclasses lógicas de espectadores, mas desde que todos os espectadores são subclasses - TextViewer, ImageViewer, etc., Editor não deriva de Viewer, permitindo assim que os TextEditor, classes finais ImageEditor para evitar o diamante

.

Nos casos em que o diamante não é evitável, usando herança virtual. A maior ressalva, no entanto, com bases virtuais, é que o construtor para a base virtual deve ser chamado pela classe mais derivada, o que significa que uma classe que deriva praticamente não tem controle sobre os parâmetros do construtor. Além disso, a presença de uma base virtual tende a incorrer em uma penalidade de desempenho / espaço na carcaça através da cadeia, embora eu não acredito que há muito de uma penalidade por mais além do primeiro.

Além disso, você sempre pode usar o diamante se você é explícito sobre que base que você deseja usar. Às vezes é a única maneira.

Gostaria de sugerir um design de classe melhor. Estou certo de que há alguns problemas que são resolvidos melhor por meio de herança múltipla, mas verifique se não há outra maneira em primeiro lugar.

Se não, usar funções virtuais / interfaces.

Use herança por delegação. Em seguida, ambas as classes irá apontar para uma base de A, mas tem que implementar métodos que redirecionamento para A. Ele tem o efeito colateral de transformar membros protegidos de A em membros "privados" em B, C e D, mas agora você não faz precisa virtual, e você não tem um diamante.

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