Muitas vezes, é uma boa ideia ter uma classe base abstrata para isolar a interface do objeto.
O problema é que a construção de cópias, IMHO, é praticamente quebrada por padrão em C ++, com os construtores de cópias sendo gerados por padrão.
Então, quais são as dicas quando você tem uma classe base abstrata e ponteiros brutos nas classes derivadas?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
E agora você desativa de maneira limpa a construção de cópias para toda a hierarquia? Declarar construção de cópia como particular em IAbstract
?
Existem regras de três com classes base abstratas?
c++
abstract-class
Codificador
fonte
fonte
Respostas:
A construção de cópias em uma classe abstrata deve ser tornada privada na maioria dos casos, assim como o operador de atribuição.
Classes abstratas são, por definição, feitas para serem do tipo polimórfico. Portanto, você não sabe quanta memória sua instância está usando e, portanto, não pode copiá-la ou atribuí-la com segurança. Na prática, você corre o risco de cortar: /programming/274626/what-is-the-slicing-problem-in-c
O tipo polimórfico, em C ++, não deve ser manipulado por valor. Você os manipula por referência ou por ponteiro (ou qualquer ponteiro inteligente).
Essa é a razão pela qual o Java tornou o objeto manipulável apenas por referência, e por que o C # e o D têm a separação entre classes e estruturas (a primeira sendo polimórfica e o tipo de referência, a segunda sendo não polimórfica e o tipo de valor).
fonte
Você poderia, é claro, apenas torná-lo protegido e vazio, para que classes derivadas possam escolher. No entanto, de maneira geral, seu código é proibido de qualquer maneira, porque é impossível instanciar
IAbstract
- porque ele possui uma função virtual pura. Como tal, isso geralmente não é um problema - suas classes de interface não podem ser instanciadas e, portanto, nunca podem ser copiadas, e suas classes mais derivadas podem banir ou continuar copiando conforme desejarem.fonte
operator=(const Derived2&)
emDerived1
.Tornando privado o ctor e a atribuição (ou declarando-os como = delete no C ++ 11), você desativa a cópia.
O ponto aqui é onde você tem que fazer isso. Para manter seu código, o IAbstract não é um problema. (observe que, ao fazer o que você fez, você atribui o
*a1
IAbstract
subobjeto a a2, perdendo qualquer referência aDerived
. A atribuição de valor não é polimórfica)A questão vem com
Derived::theproblem
. Copiar um Derivado para outro pode de fato compartilhar os*theproblem
dados que não podem ser projetados para serem compartilhados (há duas instâncias que podem chamardelete theproblem
seu destruidor).Se for esse o caso, é
Derived
que deve ser não copiável e não atribuível. Obviamente, se você tornar a cópia privadaIAbstract
, uma vez que a cópia padrãoDerived
precisa,Derived
ela também não poderá ser copiada. Mas se você definir o seu próprioDerived::Derived(const Derived&)
sem chamarIAbtract
copiar, ainda poderá copiá-los.O problema está em Derivado e a solução deve permanecer em Derivado: se deve ser um objeto somente dinâmico acessado apenas por ponteiros ou referências, é o próprio Derivado que deve ter
Essencialmente, cabe ao designer da classe Derivado (que deve saber como o Derivado funciona e como
theproblem
é gerenciado) decidir o que fazer com a atribuição e a cópia.fonte