8
votes

En C ++ 11, comment puis-je appeler de nouveau et réserver une mémoire suffisante pour l'objet?

J'ai une classe qui est décrite de cette façon: xxx

Je ne peux pas changer la conception de cette classe.

Comment puis-je créer une instance de cette classe? Avant d'appeler le constructeur, je dois la faire réserver suffisamment de mémoire pour stocker ses données. Il doit donc être sur le tas, pas sur la pile. Je suppose que je veux quelque chose comme xxx

mais je ne trouve pas un moyen de le faire fonctionner.

EDIT: Comme les commentateurs ont remarqué, ceci est Pas une très bonne conception globale (avec des astuces non-normes telles que données [0] ), il s'agit d'une bibliothèque que je suis obligée d'utiliser ...


11 commentaires

Placement Nouveau avec une allocation manuelle pourrait aider.


int Data [0] n'est pas valide C ++.


Placement Nouvelle syntaxe: Nouveau (adresse) Type (Arguments ...); adresse est vide * . Tout ce qui est créé avec placement nouveau doit être détruit avec un appel de destructeur explicite; Je doute d'une manière ou d'une autre que vous voulez faire cela manuellement, vous allez bien écrire un wrapper autour de FOO qui traite d'une allocation / distribution appropriée.


Toute raison pour laquelle vous ne pouvez pas utiliser std :: vecteur ?


@Kerreksb: Désolé de Nitpick, mais non, c'est valide C ++; Lorsque la taille est 0, il retournera toujours un pointeur valide, mais la désirficite est indéfinie. Détails: Stackoverflow.com/questions / 1087042 / ...


@ Legends2k: Cela n'a rien à voir avec mon commentaire.


@Philipp, en fait, les méthodes que j'ai cachées font beaucoup de choses utiles, donc je ne peux pas utiliser un vecteur à la place. La bibliothèque je suis censée utiliser fait de nombreuses tours de micro-optimisations, c'est l'un d'entre eux.


Le projet de norme J'ai une section [DCL.Array] où il indique clairement des tableaux "si l'expression constante (5.19) est présente, elle doit être une expression constante intégrale et sa valeur doit être supérieure à zéro."


Ce n'est en effet pas valide C ++, mais il est soutenu par certains compilateurs tels que GCC .


Pertinent pour le "est valide?" Discussion: Stackoverflow.com/Questions / 4412749 / ...


@KerReksb: Mon alloc dynamique mal confus. avec stockage automatique :( vient de vérifier la spécification. Si l'expression constante est présente, elle doit être une expression constante intégrale et sa valeur doit être supérieure à zéro. avec -pedantic g ++ drapeaux AVERTISSEMENT: ISO C ++ interdit la matrice de taille zéro .


4 Réponses :


11
votes

Vous pouvez masloc code> la mémoire de l'objet, puis utiliser un Placement Nouveau code> pour créer l'objet dans la mémoire précédemment attribuée:

void* memory = ::operator new(sizeof(Foo) + sizeof(int) * size);
Foo* foo = new (memory) Foo(size, data);
...
foo->~Foo();
::operator delete(memory); //or ::operator delete(foo);


12 commentaires

Serait légèrement plus sûr de faire malloc (tailleof (foo) + ... plutôt que Tailleof (int) . Juste au cas où foo obtient un autre membre à l'avenir.


Vous pouvez effectivement éviter MALLOC () et faire VOID * Memory = Nouveau caractère [TIZEOF (FOO) + Taille de (int) * Taille];


(VOID *) FOO == Memory est garanti et c'est une bonne chose, sinon vous n'auriez pas suffisamment de mémoire.


@Benvoigt Je n'étais pas sûr de ça.


N'utilisez pas MALLOC ou nouveau caractère [] Si vous voulez une mémoire brute, utilisez simplement :: opérateur nouveau . Son seul but dans la vie est d'allouer la mémoire brute.


@Gmannickg C'est vraiment la bonne façon de le faire, mais existe-t-il une différence pratique entre :: opérateur nouveau et malloc ?


@ZAKINSTER: Vous pouvez remplacer la définition de :: opérateur Nouveau avec un gestionnaire de mémoire personnalisé. En utilisant malloc , vous contournez cela.


Après avoir utilisé :: opérateur Nouveau et placement Nouveau , ne serait-il pas valide pour simplement Supprimer FOO; ?


@ASCHEPLER: Non, vous ne pouvez que Supprimer Ce que vous Nouveau 'd. Bien que cela puisse partager le mot lexical "nouveau", ce sont des choses différentes. Pour annuler :: Opérateur Nouveau , vous :: Opérateur Supprimer .


Mais vous avez fait nouveau l'objet foo dans l'expression nouvelle (mémoire) FOO (taille + données) . (Bien que ce soit soit :: supprime foo; , pas seulement Supprimer foo; .)


@ASCHEPLER: Ce n'est pas Nouveau ing A foo , c'est nouveau (mémoire) ing A foo . :) Il n'y a pas de Suppr (mémoire) dans la langue pour correspondre qui est l'endroit où l'asymétrie frappe (suppression manquante).


N'y a-t-il pas de problèmes d'alignement avec ce genre de choses?



1
votes

Si vous voulez vraiment être paresseux, utilisez simplement un std :: vecteur code>. Il utilisera plus d'espace (je pense que 3 pointeurs au lieu de 1) mais vous obtenez tous les avantages d'un conteneur et vraiment pas de descente si vous savez que cela ne changera jamais de taille.

Vos objets seront mobiles de votre définition. Vous pouvez donc faire ce qui suit en toute sécurité pour éliminer la réaffectation du vecteur lors de ... P>

auto initialFooValue = Foo(0, 0)
auto fooContainer = std::vector<Foo>(size, initialFooValue);

int i = 0;
for (auto& moveFoo : whereverYouAreFillingFrom)
{
    fooContainer[i] = std::move(moveFoo);
    ++i;
}


2 commentaires

C'est une solution que j'ai pensée, mais hélas je suis obligé d'utiliser pour fournir une bibliothèque pour des raisons de performance ...


STD :: Vecteur Le contenu propre est contigu en lui-même, mais il n'est pas contigu avec la structure plus grande.



8
votes

Vous avez une bibliothèque qui fait cette chose mais ne fournit pas une fonction d'usine? Pour la honte!

Quoi qu'il en soit, tandis que la méthode de Zakinster a raison (j'appellerais directement l'opérateur nouveau au lieu d'un nouveau tableau de caractères, cependant), c'est aussi sujette d'erreur, vous devez donc l'envelopper. < Pré> xxx

Oui, il y a un peu de frais généraux ici, mais la plupart de ces choses sont réutilisables.


0 commentaires

1
votes

Je pense qu'une solution bonne em> C ++ est d'obtenir une mémoire brute avec de nouveau, puis utilisez le placement nouveau pour intégrer votre classe à celle-ci.

Obtenir la mémoire brute fonctionne comme ceci: P> xxx pré>

construire l'objet dans la mémoire reçue fonctionne comme ceci: p> xxx pré>

N'oubliez pas que cela signifie également que vous devez user> manuellement Strong> Nettoyer l'endroit. P>

f->~Foo(); // call the destructor
operator delete(f); // free the memory again


0 commentaires