Estou confuso sobre quais poderiam ser os problemas se um construtor fosse herdado de uma classe base. O Cpp Primer Plus diz:
Os construtores são diferentes de outros métodos de classe, pois criam novos objetos, enquanto outros são invocados por objetos existentes . Esse é um dos motivos pelos quais os construtores não são herdados . Herança significa que um objeto derivado pode usar um método de classe base, mas, no caso de construtores, o objeto não existe até que o construtor tenha feito seu trabalho.
Entendo que o construtor é chamado antes da construção do objeto.
Como isso pode causar problemas se uma classe filho herda ( ao herdar, a classe filho é capaz de substituir o método da classe pai, etc. Não apenas acessando o método da classe pai ) o construtor pai?
Entendo que não há necessidade de chamar explicitamente um construtor a partir de um código [não que eu saiba ainda.], Exceto ao criar objetos. Mesmo assim, você pode fazer isso usando algum mecanismo para chamar o construtor pai [No cpp, usando ::
ou usando member initialiser list
, No java usando super
]. Em Java, há uma imposição para chamá-lo na 1ª linha. Entendo que é para garantir que o objeto pai seja criado primeiro e, em seguida, a construção do objeto filho prossiga.
Pode substituí- lo. Mas, não posso apresentar situações em que isso possa representar um problema. Se a criança herdar o construtor pai, o que pode dar errado?
Então, isso é apenas para evitar herdar funções desnecessárias. Ou há algo mais?
fonte
Respostas:
Não pode haver nenhuma herança adequada de construtores em C ++, porque o construtor de uma classe derivada precisa executar ações adicionais que um construtor de classe base não precisa fazer e não conhece. Essas ações adicionais são a inicialização dos membros de dados da classe derivada (e em uma implementação típica, também configurando o vpointer para se referir à tabela de classes derivadas).
Quando uma classe é construída, há várias coisas que sempre precisam acontecer: Os construtores da classe base (se houver) e os membros diretos precisam ser chamados e se houver alguma função virtual, o vpointer deve ser configurado corretamente . Se você não fornecer um construtor para a sua classe, em seguida, o compilador vai criar um que executa as ações e nada mais necessários. Se você fazer fornecer um construtor, mas perder algumas das ações necessárias (por exemplo, a inicialização de alguns membros), em seguida, o compilador irá adicionar automaticamente as ações que faltam para o seu construtor. Dessa forma, o compilador garante que cada classe tenha pelo menos um construtor e que cada construtor inicialize totalmente os objetos que cria.
No C ++ 11, uma forma de 'herança de construtor' foi introduzida, na qual você pode instruir o compilador a gerar um conjunto de construtores para você que usa os mesmos argumentos que os construtores da classe base e que apenas encaminham esses argumentos para o classe base.
Embora seja oficialmente chamado de herança, não é verdade, porque ainda existe uma função específica de classe derivada. Agora ele é gerado pelo compilador, em vez de ser explicitamente escrito por você.
Esse recurso funciona assim:
Derived
agora possui dois construtores (sem contar os construtores copiar / mover). Um que aceita um int e uma string e outro que aceita apenas um int.fonte
m()
vem e como isso mudaria se seu tipo fosse, por exemploint
.using Base::Base
implicitamente? Ele teria grandes consequências se eu esqueci essa linha em uma classe derivada e eu preciso para herdar o construtor em todas as classes derivadasNão está claro o que você quer dizer com "herdando o construtor pai". Você usa a palavra substituição , o que sugere que você pode estar pensando em construtores que se comportam como funções virtuais polimórficas . Eu deliberadamente não uso o termo "construtores virtuais" porque esse é um nome comum para um padrão de código em que você realmente exige uma instância de um objeto já existente para criar outro.
Há pouca utilidade para construtores polimórficos fora do padrão "construtor virtual", e é difícil criar um cenário concreto em que um construtor polimórfico real possa ser usado. Um exemplo muito elaborado que nem é remotamente válido em C ++ :
Nesse caso, o construtor chamado depende do tipo concreto da variável que está sendo construída ou atribuída. É complexo detectar durante a análise / geração de código e não tem utilidade: você conhece o tipo concreto que está construindo e escreveu um construtor específico para a classe derivada. O seguinte código C ++ válido faz exatamente o mesmo, é um pouco mais curto e é mais explícito no que faz:
Uma segunda interpretação ou talvez uma pergunta adicional é - e se os construtores da classe Base estivessem automaticamente presentes em todas as classes derivadas, a menos que explicitamente ocultos.
Você precisaria escrever código adicional para ocultar um construtor pai que é incorreto para usar na construção da classe derivada. Isso pode acontecer quando a classe derivada especializa a classe base de uma maneira que certos parâmetros se tornam irrelevantes.
O exemplo típico é retângulos e quadrados (observe que os quadrados e retângulos geralmente não são substituíveis por Liskov, portanto, não é um design muito bom, mas destaca o problema).
Se Square herdou o construtor de dois valores de Retângulo, você poderia construir quadrados com altura e largura diferentes ... Isso é logicamente errado, então você deseja ocultar esse construtor.
fonte
Por que os construtores não são herdados: a resposta é surpreendentemente simples: o construtor da classe base "constrói" a classe base e o construtor da classe herdada "constrói" a classe herdada. Se a classe herdada herdasse o construtor, o construtor tentaria criar um objeto do tipo classe base e você não conseguiria "construir" um objeto do tipo classe herdada.
Que tipo de derrota o propósito de herdar uma classe.
fonte
O problema mais óbvio em permitir que a classe derivada substitua o construtor da classe base é que o desenvolvedor da classe derivada agora é responsável por saber como construir sua (s) classe (s) base (s). O que acontece quando a classe derivada não constrói a classe base corretamente?
Além disso, o princípio Liskov-Substitution não se aplicaria mais, pois você não poderá mais contar com sua coleção de objetos da classe base para serem compatíveis entre si, porque não há garantia de que a classe base foi construída de forma adequada ou compatível com os outros tipos derivados.
É ainda mais complicado quando mais de 1 nível de herança é adicionado. Agora sua classe derivada precisa saber como construir todas as classes base na cadeia.
Então, o que acontece se você adicionar uma nova classe base ao topo da hierarquia de herança? Você precisaria atualizar todos os construtores de classe derivada.
fonte
Os construtores são fundamentalmente diferentes de outros métodos:
Então, por que eles não são herdados? Resposta simples: porque sempre há uma substituição, gerada ou gravada manualmente.
Por que toda classe precisa de um construtor? Essa é uma pergunta complicada e acredito que a resposta depende do compilador. Existe um construtor "trivial", para o qual o compilador não exige que seja chamado como parece. Eu acho que é a coisa mais próxima do que você quer dizer com herança, mas pelas três razões expostas acima, acho que comparar construtores com métodos normais não é realmente útil. :)
fonte
Toda classe precisa de um construtor, mesmo o padrão.
O C ++ criará construtores padrão para você, exceto se você criar um construtor especializado.
Caso sua classe base use um construtor especializado, você precisará escrever o construtor especializado na classe derivada, mesmo que os dois sejam iguais e encadeá-los de volta.
O C ++ 11 permite evitar a duplicação de código em construtores usando :
Um conjunto de construtores herdados é composto por
Todos os construtores herdados que não são o construtor padrão ou o construtor de copiar / mover e cujas assinaturas não correspondem aos construtores definidos pelo usuário na classe derivada, são declarados implicitamente na classe derivada. Os parâmetros padrão não são herdados
fonte
Você pode usar:
Você está perguntando por que você tem que fazer isso?
A subclasse pode ter propriedades adicionais que talvez precisem ser inicializadas no construtor ou pode inicializar as variáveis da classe base de uma maneira diferente.
De que outra forma você criaria o objeto subtipo?
fonte