classe de tamanho variável - C ++
-
06-07-2019 - |
Pergunta
Eu vi uma classe que é uma classe que é definido como este ..
class StringChild : public StringBase
{
public:
//some non-virtual functions
static StringChild* CreateMe(int size);
private:
unsigned char iBuf[1];
};
A função de fábrica estático tem a seguinte implementação ..
return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();
Assim, tanto quanto eu entendo que esta função é usando posicionamento novo para estender essa classe.
É seguro só porque há apenas 1 membro e é alocado no heap?
Solução
É um truque C velho que foi usado para trabalho em torno da não-availablity de matrizes de comprimento variável na planície C. Sim, ele também funciona em C ++, enquanto você usar construções alocador adequados (como alocar um monte de memória bruta do tamanho desejado e, em seguida, a colocação newing o objecto lá). É seguro, desde que você não caminhar sobre o final da memória alocada, mas tende a confundir pelo menos alguns depuradores de memória.
Uma coisa que você tem que fazer absolutamente certo ao usar esta técnica é que a matriz de comprimento variável é o último elemento no layout do objeto, caso contrário você vai andar sobre outras variáveis ??internas.
Mas estou um pouco duvidoso sobre a implementação da função de fábrica - Presumo que o parâmetro 'tamanho' é na verdade o tamanho da matriz desejada? Além disso, não se esqueça que você tem que liberar a memória acima usando 'livre' e não 'delete', embora o último trabalho poder na maioria dos casos.
A menos que haja uma razão convincente de por que a memória tem de ser gerida desta forma, eu simplesmente substituir a matriz com um std :: vector.
Outras dicas
Esta deve ser OK para PODs fornecido iBuf é o último membro da estrutura. Os problemas com os não-pods poderia ser que, por exemplo. compilador é livre para / membros reordenar públicas / privadas protegidas, classes base virtuais acabam no final do objeto mais derivado IIUC, etc.
A sua estrutura é não-POD (tem uma classe base) para que eu não recomendo.
Além disso, se você criar instâncias como esta ??p>
return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();
Você deve certificar-se de que a memória adquirida pelo malloc deve ser liberado com graça, então apagar suas instâncias como esta:
obj->~StringChild();
free(obj);
Talvez você gostaria de usar ::operator new()
para alocação
A rigor, desde StringChild
é derivado de StringBase
não é seguro. O padrão C ++ não especifica o layout para subobjects classe base. Cláusula 10 § 3º:
A ordem em que os sub-objectos de classe base são atribuídos do objeto mais derivado (1,8) não é especificado.
Se StringChild
fosse um struct POD, então essa técnica um seria seguro.