Meu estilo de codificação inclui o seguinte idioma:
class Derived : public Base
{
public :
typedef Base super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
Isso me permite usar "super" como um alias para o Base, por exemplo, em construtores:
Derived(int i, int j)
: super(i), J(j)
{
}
Ou mesmo ao chamar o método da classe base dentro de sua versão substituída:
void Derived::foo()
{
super::foo() ;
// ... And then, do something else
}
Pode até ser encadeado (ainda preciso encontrar o uso para isso):
class DerivedDerived : public Derived
{
public :
typedef Derived super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
void DerivedDerived::bar()
{
super::bar() ; // will call Derived::bar
super::super::bar ; // will call Base::bar
// ... And then, do something else
}
De qualquer forma, acho muito útil o uso de "typedef super", por exemplo, quando o Base é detalhado e / ou modelado.
O fato é que super é implementado em Java e também em C # (onde é chamado de "base", a menos que eu esteja errado). Mas o C ++ não possui essa palavra-chave.
Então, minhas perguntas:
- esse uso de typedef é super comum / raro / nunca visto no código com o qual você trabalha?
- esse uso do typedef é super Ok (ou seja, você vê razões fortes ou não tão fortes para não usá-lo)?
- "super" deve ser uma coisa boa, deve ser um pouco padronizada em C ++, ou esse uso já é suficiente através de um typedef?
Edit: Roddy mencionou o fato de que o typedef deve ser privado. Isso significaria que qualquer classe derivada não seria capaz de usá-lo sem redeclará-lo. Mas acho que isso também impediria o super :: super encadeamento (mas quem vai chorar por isso?).
Edit 2: Agora, alguns meses depois de usar massivamente "super", concordo plenamente com o ponto de vista de Roddy: "super" deve ser privado. Eu votaria sua resposta duas vezes, mas acho que não posso.
fonte
super
pareceJava
e não é nada ruim, mas ... MasC++
suporta herança múltipla.MyFirstBase<MyString, MyStruct<MyData, MyValue>>
em todos os lugares)template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };
Isso funcionou para mim com o gcc 9.1 --std = c ++ 1y (c ++ 14).Respostas:
Bjarne Stroustrup menciona no Design and Evolution of C ++ que
super
como uma palavra-chave foi considerada pelo comitê de padrões ISO C ++ na primeira vez em que o C ++ foi padronizado.Dag Bruck propôs essa extensão, chamando a classe base de "herdada". A proposta mencionava a questão da herança múltipla e teria sinalizado usos ambíguos. Até Stroustrup estava convencido.
Após a discussão, Dag Bruck (sim, a mesma pessoa que fez a proposta) escreveu que a proposta era implementável, tecnicamente sólida e livre de grandes falhas, além de lidar com a herança múltipla. Por outro lado, não havia dinheiro suficiente para o dinheiro, e o comitê deveria lidar com um problema mais espinhoso.
Michael Tiemann chegou atrasado e depois mostrou que um super digitado funcionaria muito bem, usando a mesma técnica solicitada neste post.
Portanto, não, isso provavelmente nunca será padronizado.
Se você não possui uma cópia, Design and Evolution vale bem o preço de capa. Cópias usadas podem ser adquiridas por cerca de US $ 10.
fonte
typedef
técnica: ela não respeita a SECA. A única maneira de contornar seria usar macros feias para declarar classes. Quando você herda, a base pode ser uma classe de modelo de vários parâmetros longa ou pior. (por exemplo, multi-classes), você teria que reescrever tudo isso uma segunda vez. Finalmente, vejo um grande problema com as bases de modelos que possuem argumentos de classe de modelo. Nesse caso, o super é um modelo (e não uma instanciação de um modelo). O qual não pode ser digitado. Mesmo em C ++ 11, você precisausing
para este caso.Eu sempre usei "herdado" em vez de super. (Provavelmente devido ao background do Delphi), e eu sempre o faço privado , para evitar o problema quando o 'herdado' é erroneamente omitido de uma classe, mas uma subclasse tenta usá-lo.
Meu 'modelo de código' padrão para a criação de novas classes inclui o typedef, por isso tenho poucas oportunidades de omiti-lo acidentalmente.
Eu não acho que a sugestão encadeada "super :: super" seja uma boa idéia. Se você está fazendo isso, provavelmente está muito ligada a uma hierarquia específica, e alterá-la provavelmente quebrará muito as coisas.
fonte
virtual
conforme mostrado aqui: martinbroadhurst.com/typedef-super.htmlUm problema é que, se você esquecer (re) definir super para classes derivadas, qualquer chamada para super :: something será compilada corretamente, mas provavelmente não chamará a função desejada.
Por exemplo:
(Conforme apontado por Martin York nos comentários a esta resposta, esse problema pode ser eliminado tornando o typedef privado, em vez de público ou protegido.)
fonte
super1
para Base esuper2
em Derivado? O DerivedAgain pode usar os dois?FWIW A Microsoft adicionou uma extensão para __super em seu compilador.
fonte
Super (ou herdado) é muito bom, porque se você precisar colar outra camada de herança entre Base e Derivado, precisará alterar apenas duas coisas: 1. a "classe Base: foo" e 2. o typedef
Se bem me lembro, o comitê de padrões do C ++ estava pensando em adicionar uma palavra-chave para isso ... até Michael Tiemann apontar que esse truque de digitação funciona.
Quanto à herança múltipla, como está sob controle do programador, você pode fazer o que quiser: talvez super1 e super2, ou o que for.
fonte
Acabei de encontrar uma solução alternativa. Eu tenho um grande problema com a abordagem typedef que me mordeu hoje:
Então, eu vim com uma solução melhor usando um modelo muito simples.
Então agora, em vez de
Você tem
Nesse caso,
BaseAlias
não é particular e tentei me proteger contra o uso descuidado, selecionando um nome de tipo que deve alertar outros desenvolvedores.fonte
public
alias é uma desvantagem, como você está aberto para o bug mencionado no de Roddy e de Kristopher respostas (por exemplo, você pode (por engano) derivarDerived
em vez deMakeAlias<Derived>
)MakeAlias
ou encaminhamento perfeito, mas requer que você se refira aosMakeAlias<BaseAlias>
construtores em vez de apenas se referirC
diretamente aos construtores de s.)Não me lembro de ter visto isso antes, mas à primeira vista eu gosto. Como Ferruccio observa, ele não funciona bem em face do MI, mas o MI é mais uma exceção do que a regra e não há nada que diga que algo precisa ser utilizável em qualquer lugar para ser útil.
fonte
Eu já vi esse idioma empregado em muitos códigos e tenho certeza de que já o vi em algum lugar nas bibliotecas do Boost. No entanto, tanto quanto me lembro, o nome mais comum é
base
(ouBase
) em vez desuper
.Esse idioma é especialmente útil se estiver trabalhando com classes de modelo. Como exemplo, considere a seguinte classe (de um projeto real ):
Não ligue para os nomes engraçados. O ponto importante aqui é que a cadeia de herança usa argumentos de tipo para obter polimorfismo em tempo de compilação. Infelizmente, o nível de aninhamento desses modelos fica bastante alto. Portanto, as abreviações são cruciais para facilitar a leitura e a manutenção.
fonte
Eu já o vi muitas vezes usado, às vezes como super_t, quando a base é um tipo de modelo complexo (
boost::iterator_adaptor
faz isso, por exemplo)fonte
esse uso de typedef é super comum / raro / nunca visto no código com o qual você trabalha?
Eu nunca vi esse padrão específico no código C ++ com o qual trabalho, mas isso não significa que não esteja lá fora.
esse uso do typedef é super Ok (ou seja, você vê razões fortes ou não tão fortes para não usá-lo)?
Não permite herança múltipla (de qualquer maneira, de maneira limpa).
"super" deve ser uma coisa boa, deve ser um pouco padronizada em C ++, ou esse uso já é suficiente através de um typedef?
Pelo motivo acima citado (herança múltipla), não. A razão pela qual você vê "super" nos outros idiomas que você listou é que eles suportam apenas uma herança, portanto não há confusão sobre o que "super" está se referindo. É verdade que nessas linguagens é útil, mas na verdade não tem um lugar no modelo de dados C ++.
Ah, e FYI: C ++ / CLI suporta esse conceito na forma da palavra-chave "__super". Observe, porém, que o C ++ / CLI também não suporta herança múltipla.
fonte
Um motivo adicional para usar um typedef para a superclasse é quando você está usando modelos complexos na herança do objeto.
Por exemplo:
Na classe B, seria ideal ter um typedef para A, caso contrário você ficaria repetido em todos os lugares em que desejasse referenciar os membros de A.
Nesses casos, também pode funcionar com herança múltipla, mas você não teria um typedef chamado 'super', seria chamado 'base_A_t' ou algo parecido.
--jeffk ++
fonte
Depois de migrar do Turbo Pascal para C ++, eu costumava fazer isso para ter um equivalente para a palavra-chave "herdada" do Turbo Pascal, que funciona da mesma maneira. No entanto, depois de programar em C ++ por alguns anos, parei de fazê-lo. Descobri que não precisava muito disso.
fonte
Não sei se é raro ou não, mas certamente fiz a mesma coisa.
Como já foi apontado, a dificuldade de fazer essa parte da linguagem é quando uma classe faz uso de herança múltipla.
fonte
Eu uso isso de tempos em tempos. Apenas quando eu me encontrar digitando o tipo de classe base algumas vezes, eu o substituirei por um typedef semelhante ao seu.
Eu acho que pode ser um bom uso. Como você diz, se sua classe base for um modelo, poderá salvar a digitação. Além disso, as classes de modelo podem aceitar argumentos que atuam como políticas de como o modelo deve funcionar. Você é livre para alterar o tipo de base sem precisar corrigir todas as suas referências, desde que a interface da base permaneça compatível.
Eu acho que o uso através do typedef já é suficiente. De qualquer maneira, não consigo ver como isso seria incorporado à linguagem, porque a herança múltipla significa que pode haver muitas classes base; portanto, você pode digitá-la como achar melhor para a classe que logicamente considera a classe base mais importante.
fonte
Eu estava tentando resolver exatamente o mesmo problema; Eu dei algumas idéias, como o uso de modelos variados e a expansão de pacotes para permitir um número arbitrário de pais, mas percebi que isso resultaria em uma implementação como 'super0' e 'super1'. Eu joguei no lixo porque isso seria pouco mais útil do que não ter para começar.
Minha solução envolve uma classe auxiliar
PrimaryParent
e é implementada da seguinte forma:Então, qual classe que você deseja usar seria declarada como tal:
Para evitar a necessidade de usar a herança virtual
PrimaryParent
emBaseClass
, um construtor de tomar um número variável de argumentos é usado para permitir a construção deBaseClass
.A razão por trás da
public
herança deBaseClass
intoPrimaryParent
é permitir que vocêMyObject
tenha controle total sobre a herança de,BaseClass
apesar de ter uma classe auxiliar entre eles.Isso significa que toda classe que você deseja ter
super
deve usar aPrimaryParent
classe auxiliar, e cada filho pode herdar apenas uma classe usandoPrimaryParent
(daí o nome).Outra restrição para esse método é
MyObject
pode herdar apenas uma classe que herdaPrimaryParent
, e essa deve ser herdada usandoPrimaryParent
. Aqui está o que eu quero dizer:Antes de descartá-lo como uma opção devido ao número aparente de restrições e ao fato de haver uma classe intermediária entre todas as heranças, essas coisas não são ruins.
A herança múltipla é uma ferramenta forte, mas na maioria das circunstâncias, haverá apenas um pai principal e, se houver outros pais, provavelmente serão classes Mixin ou classes que não herdam de
PrimaryParent
qualquer maneira. Se a herança múltipla ainda for necessária (embora muitas situações sejam benéficas para usar a composição para definir um objeto em vez da herança), basta definir explicitamentesuper
nessa classe e não herdá-laPrimaryParent
.A ideia de ter que definir
super
em todas as classes não é muito atraente para mim, o usoPrimaryParent
permitesuper
, claramente, um alias baseado em herança, permanecer na linha de definição de classe em vez do corpo da classe para onde os dados devem ir.Isso pode ser apenas eu embora.
Claro que toda situação é diferente, mas considere essas coisas que eu disse ao decidir qual opção usar.
fonte
Não vou dizer muito, exceto o código atual com comentários que demonstram que super não significa chamar base!
super != base.
Em suma, o que "super" deveria significar afinal? e então o que "base" deveria significar?
Essas 2 regras se aplicam aos typedefs da classe.
Considere o implementador e o usuário da biblioteca, quem é super e quem é base?
para obter mais informações, aqui está o código de trabalho para copiar e colar no seu IDE:
Outro ponto importante aqui é o seguinte:
por dentro
main()
, usamos chamadas polimórficas inferiores que super chamam para cima, não muito úteis na vida real, mas demonstram a diferença.fonte
Eu uso a palavra-chave __super. Mas é específico da Microsoft:
http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx
fonte
Este é um método que eu uso, que usa macros em vez de um typedef. Sei que essa não é a maneira C ++ de fazer as coisas, mas pode ser conveniente ao encadear iteradores por herança, quando apenas a classe base mais abaixo na hierarquia está agindo sobre um deslocamento herdado.
Por exemplo:
A configuração genérica que eu uso facilita a leitura e a cópia / colar entre a árvore de herança que possui código duplicado, mas deve ser substituída, porque o tipo de retorno precisa corresponder à classe atual.
Pode-se usar letras minúsculas
super
para replicar o comportamento visto em Java, mas meu estilo de codificação é usar todas as letras maiúsculas para macros.fonte