Todos sabemos que, ao usar uma herança simples e simples, o endereço de uma classe derivada é o mesmo que o endereço da classe base. A herança múltipla torna isso falso.
A herança virtual também torna isso falso? Em outras palavras, o código a seguir está correto:
struct A {};
struct B : virtual A
{
int i;
};
int main()
{
A* a = new B; // implicit upcast
B* b = reinterpret_cast<B*>(a); // fishy?
b->i = 0;
return 0;
}
c++
virtual-inheritance
user1610015
fonte
fonte
reinterpret_cast
com classes é sempre suspeito (exceto da classe paravoid*
e volta para a mesma classe).Respostas:
Eu acho que a afirmação não é verdadeira. No código abaixo, temos uma herança simples (não virtual) simples (não múltipla), mas os endereços são diferentes.
e a saída para o VS2015 é:
Se você alterar a herança no código acima para virtual, o resultado será o mesmo. portanto, mesmo no caso de herança virtual, os endereços dos objetos base e derivados podem ser diferentes.
fonte
void main()
é aceitável mesmo em compiladores modernos do MSVS. BTW, obrigado pelo comentário. Eu atualizei o código.void main()
não é aceitável. Tem que estar deint main()
acordo com o padrão. E remova issodynamic_cast
do código, não é necessário lá e causa confusão.O resultado de
reinterpret_cast<B*>(a);
é garantido apenas para apontar para oB
objeto anexoa
se oa
subobjeto e oB
objeto anexo forem interconversíveis por ponteiro , consulte [expr.static.cast] / 3 do padrão C ++ 17.O objeto de classe derivada é interconversível com ponteiro com o objeto de classe base apenas se o objeto derivado for de layout padrão , não tiver membros de dados não estáticos diretos e o objeto de classe base for seu primeiro subobjeto de classe base. [base.com]] /4,3
Ter uma
virtual
classe base desqualifica uma classe de ser layout padrão . [classe] /7.2 .Portanto, como
B
possui uma classe base virtual e um membro de dados não estático,b
não apontará para oB
objeto envolvente , mas, em vez disso,b
, o valor do ponteiro permanecerá inalteradoa
.Acessando o
i
membro como se estivesse apontando para oB
objeto tem um comportamento indefinido.Quaisquer outras garantias viriam da sua ABI específica ou de outra especificação.
fonte
Isso não está totalmente correto. Considere este exemplo:
Ao criar uma instância de
D
,B
eC
são instanciados, cada um com sua respectiva instância deA
. No entanto, não haveria problema se a instância deD
tivesse o mesmo endereço de sua instância deB
e sua respectiva instância deA
. Embora não seja necessário, é exatamente isso que acontece ao compilar comclang 11
egcc 10
:Vamos considerar uma versão modificada do exemplo acima:
O uso do
virtual
especificador de função geralmente é usado para evitar chamadas de função ambíguas. Portanto, ao usarvirtual
herança, as instânciasB
eC
devem criar umaA
instância comum . Ao instanciarD
, obtemos os seguintes endereços:Aqui não há razão para usar
reinterpret_cast
, ainda mais, isso resulta em um comportamento indefinido. Use emstatic_cast
vez disso:Ambos os modelos se comportam de maneira diferente neste exemplo. O
reinterpret_cast
reinterpretarápB
como um ponteiro paraA
, mas o ponteiropA
pode apontar para um endereço diferente, como no exemplo acima (C vs A). O ponteiro será convertido corretamente se você usarstatic_cast
.fonte
A razão
a
eb
são diferentes no seu caso é porque, comoA
não possui nenhum método virtual,A
não está mantendo avtable
. Por outro lado,B
mantém umvtable
.Quando você faz upcast
A
, o compilador é inteligente o suficiente para ignorar ovtable
significadoB
. E, portanto, a diferença de endereços. Você não deveriareinterpret_cast
voltarB
, não funcionaria.Para verificar minha reivindicação, tente adicionar um
virtual
método, digamosvirtual void foo() {}
emclass A
. AgoraA
também manterá umvtable
. Portanto, downcast (reinterpret_cast
) para B retornará o originalb
.fonte
Subclass address equal to virtual base class address?
tem tudo a ver com herança virtual. E herança virtual tem tudo a ver com vtables.A
garanta que os endereços correspondam e o elenco funcione, na teoria ou na prática. Não consigo remover meu voto negativo sem uma edição posterior, porque ele foi bloqueado.