6
votes

En C ++, pourquoi "nouveau" devait-il créer de manière dynamique un objet plutôt juste une allocation?

J'ai cette hiérarchie de classe triviale: xxx

si j'utilise masloc pour allouer une instance de dérivé , puis Essayez d'accéder à la fonction polymorphe x , des crashs de programme (je reçois une défaillance de segmentation): xxx

bien sûr que mon application réelle est beaucoup plus complexe (C'est une sorte de piscine mémoire).


Je suis sûr que c'est à cause de la façon dont j'alloque d : je n'ai pas utilisé neuf < /code >.

Placement Nouveau opérateur, qui doit être ce dont j'ai besoin, mais je ne l'ai jamais utilisé et j'ai quelques questions:
  • Pourquoi mon application est-elle écrasé, si je n'utilise pas nouveau ?

    Que fait nouveau réellement?

    Pourquoi ne puis-je pas simplement utiliser l'opérateur d'affectation pour affecter la valeur de dérivé (123); dans la zone de mémoire pointée par d < / li>

  • devrais-je utiliser nouveau aussi pour les types non polymorphes?

    Que diriez-vous de la pod?

  • sur le C ++ FAQ I Lié Au-dessus , il dit que la région de mémoire est passée au placement nouveau doit être alignée sur l'objet que je crée.

    Je sais quel alignement est, mais je ne sais pas comment vérifier l'alignement nécessaire à ma classe.

    MALLOC Manuel:

    Les fonctions MALLOC () et CALLOC () renvoient un pointeur sur la mémoire allouée alignée de manière appropriée pour tout type de variable.

    Et j'espère que l'alignement nécessaire à ma classe est la taille de la classe telle que retournée par Tailleof , de sorte que toute adresse dans le formulaire adresse_returned_by_malloc + i * tailleof (my_class) convient pour allouer mes objets.

    mes espoirs sont corrects?


7 commentaires

Pourquoi ne pas simplement remplacer le nouvel opérateur de cette classe?


"Pourquoi n'est-il pas malloc travaillant pour les classes?" et "pourquoi avons-nous besoin nouveau ?" sont des questions très différentes, dont l'une a une réponse.


@ARLZ, je suis sur Chargement Nouvel opérateur, mais je voulais savoir pourquoi je devais utiliser nouveau . @Chris Lutz: Oui et non; Cette question était: "Pourquoi dois-je utiliser nouveau au lieu de masloc pour instantiier des objets de classe?"


@Chris: Ils ont tous deux des réponses: "Parce que la durée de vie de l'objet n'a pas commencé, vous ne pouvez donc pas l'utiliser" et "car c'est le seul moyen d'initialiser un objet alloué de manière dynamique" respectivement.


@Ben Voigt - Je pense, sur une levée plus conceptuelle, ajout de nouveau en tant qu'opérateur (au lieu de par ex. Une fonction de modèle) était une erreur. La même fonctionnalité aurait pu être réalisée sans ces changements radicaux (et difficiles à prolonger) dans la langue.


@Chris: la syntaxe pour appeler un constructeur a besoin d'assistance linguistique. Il n'y a aucun moyen de mettre en œuvre une fonction de bibliothèque sans cela.


@Ben VoigT - Oui, mais il n'a pas besoin d'être désormais mélangé avec une allocation de mémoire.


5 Réponses :


3
votes

Parce que MALLOC n'appelle pas le constructeur de la classe et ne sait rien d'aucune exigence d'alignement en particulier qu'elle pourrait avoir. Si vous devez utiliser MALLOC (non recommandé), consultez placement nouveau (supposant que vous ne voulez pas surcharger le nouveau neuf pour une raison quelconque).


23 commentaires

Cela ne répond à aucune de mes trois questions. MALLOC N'appelle pas le constructeur, mais j'ai utilisé l'opérateur d'affectation, avec un objet correctement construit sur cette région de mémoire, alors pourquoi n'est-ce pas assez?


Je ne sais pas si l'opérateur d'affectation effectue des chèques de sécurité (par exemple, des chèques de type?) Sur le cessionnaire ... Si c'est le cas, cela ne fonctionnera pas. Quel genre de violation d'accès voyez-vous - lisez-y ou écrivez-vous? Et où cela se produit-il exactement dans le code?


@peoro: Les conditions préalables sur l'opérateur d'affectation sont que les deux côtés sont des objets correctement construits.


@Mehrdad: L'alignement est un hareng rouge. Malloc aligne correctement les choses. Le problème est que les classes non-POD ne peuvent pas être utilisées avant la construction.


@Ben: Je voulais dire si l'alignement était plus que masloc 's, pas moins (par exemple si un champ a été défini sur 64 octets d'alignement manuellement).


@Mehrdad: Rien n'a plus d'alignement que Malloc. Peoro cite la droite de la description de Malloc qui couvre cela.


@Ben: Et si vous avez besoin de données alignées sur la page? Vous pensez malloc vous donne ça?


Strictement parler, il est possible pour les types pas en C ++ d'avoir plus d'alignement que Malloc-Malloc ne peut pas être aligné sur les charges / magasins SSE 128-bits, par exemple, cependant, vous remarqueriez que ceci que si vous utilisaient des ASM en ligne ou des intrinsions en dehors de la portée de C ++.


@Mehrdad: Les objets ne nécessitent pas d'alignement que malloc ne donne pas. Si votre algorithme nécessite plus d'alignement qui est une autre histoire, mais l'objet est toujours utilisable.


@bdonlan: Non, vous remarqueriez également facilement si vous avez utilisé __ DeclSpec (Align (32)) (MS VC).


@Ben: Il n'y avait aucune idée de la publication dans la publication où la violation d'accès s'est produite. Cela pourrait donc très raisonnablement avoir été en raison d'un problème d'alignement avec l'un des champs de l'objet, attribuant un objet plus important à un espace plus petit en mémoire. Que ce soit ou non, c'est en fait la raison derrière ce problème particulier, c'était une réponse raisonnable et complètement correcte.


@Mehrdad: hein? "Affectation d'un objet plus important à un espace plus petit en mémoire" n'est pas un problème alignement .


@Ben: er, désolé, typo - je voulais dire un espace qui a un alignement plus petit . Belle prise.


@Mehrdad: Plus au point, c'est un hareng rouge car masloc et nouveau obéir aux mêmes règles d'alignement. À partir de la norme: "Les fonctions d'allocation sont supposées à renvoyer les pointeurs au stockage de manière appropriée pour des objets de tout type avec un alignement fondamental"


@Ben: Vous me dites donc qu'il est impossible d'initialiser correctement un objet aligné de page avec nouveau , tout comme avec un simple malloc ? nouveau est conscient de l'alignement; MALLOC n'est pas ...


@Mehrdad: Je ne pense pas que vous parlez de standard C ++ ici ... C ++ 0x ajoutera la prise en charge de "alignement prolongé".


@Ben: juste parce que __ beClSpec (align ...) ou (son équivalent) n'est pas standard C ++ ne signifie pas que ce n'est pas une situation potentielle valide ... (prétendant-tu que, juste parce que, Dis, le stockage discret n'est pas standard, que les gens ne peuvent pas demander à ce sujet? ou que ce n'est pas un problème / solution potentiel à leur question?)


@Mehrdad: sur la recherche de la proposition C ++ 0X, je conclus que :: opérateur nouveau et MALLOC est conforme au même alignement: l'alignement fondamental maximum. Afin de répondre aux exigences d'alignement d'un type sur aligné , vous devez donc demander suffisamment d'espace supplémentaire de sorte que certains sous-groupes de l'espace soient correctement alignés.


@Ben: Je ne vois vraiment pas le point de votre argument ici ... Vous affirmez que ma réponse est invalide simplement parce que ce n'est pas "standard C ++". Désolé, mais les gens du monde réel utilisent parfois des extensions de compilateur dans des situations valides et je ne vois pas pourquoi vous faites tant d'effort pour me prouver que quelque chose comme ça, lorsque ma réponse est valide (même si ce n'est pas standard ).


@BEN: "En outre, vous pouvez clairement voir du code source dans la question qu'aucune des classes ne nécessite un alignement prolongé." Ah bon? Non, je ne: "Bien sûr, mon application réelle est beaucoup plus complexe." Et honnêtement, je vais arrêter de commenter si vous ne le ferez pas ... N'hésitez pas à commenter plus ici; Votez-le si vous le souhaitez, mais je n'ai pas l'impression que cela devienne n'importe où, pas sûr de toi.


@Mehrdad: Parce que tout votre concentration sur l'alignement affecte le placement nouveau aussi. Ces extensions de compilateur non standard ne font pas que casser masloc , mais aussi std :: vecteur et tous les autres conteneurs de bibliothèque standard.


@Mehrdad, qui peut être le cas, mais en utilisant nouveau ne vous aidera pas ici. Je viens de courir un test, et même dans msvc ++, nouveau ignore __ declSpec (align (n)) et aligne à un multiple de 8 octets.


@Merhdad: OK, bon point sur "le code réel est plus complexe". Je retire mon affirmation selon laquelle il n'y a nécessairement aucune restriction d'alignement prolongée en jeu.



1
votes

Je ne crois pas que le constructeur de l'objet est appelé lorsque vous utilisez MALLOC.


2 commentaires

J'avais l'impression que le nouvel opérateur appelle le constructeur. MSDN et Wikipedia semblent être d'accord. Peux-tu élaborer?


Je ne comprends pas la tendance continue ici. Alanp a fixé le double négatif dans sa réponse, il est maintenant correct.



3
votes

baissons la ligne

  1. Pourquoi ma demande est-elle écrasée, si je n'utilise pas de nouvelles?

    La table virtuelle est corrompue.

    La table virtuelle est bloquée juste après la mémoire allouée. Lorsque vous NOUVEAU une classe, le code généré permettra de configurer correctement la machine à installer. Cependant, MALLOC n'initialisera pas correctement la table / p>

    pour voir la table virtuelle, exécuter g ++ -fdump-class-hiérarchy xxx

    pour une raison similaire, sans opérateur de surcharge =, le code d'assemblage généré ne copiera que les données et non le compilateur [à nouveau, le compilateur Seulement sait de copier les données, pas de la tablette]

    si vous souhaitez voir une version basée sur un pointeur avec une fonction de fourchette valide: xxx

    1. Est-ce que je devrais utiliser de nouvelles personnes non polymorphes?

      Si vous utilisez des fonctions virtuelles, oui, même pour les types non polymorphes

      1. J'espère que l'alignement nécessaire à ma classe est la taille de la classe telle que retournée par Tailleof, de sorte que toute adresse dans le formulaire adresse_returned_by_malloc + i * Tailleof (my_class) convient pour allouer mes objets.

        alignement n'est pas un problème.


7 commentaires

Sizeof ne saute pas la taille de la fourchette. La taille de la fourchette est incluse dans la taille de, pas de problème. Le problème est simplement que MALLOC n'initialise pas la tablette (ou quoi que ce soit d'autre). Si le PO a utilisé le placement nouveau sur les données Malloc'd, aucun problème


"Si vous utilisez des fonctions virtuelles, alors oui, même pour des types polymorphes" est également absurde. La définition même d'un type polymorphique est un type de classe avec des fonctions virtuelles.


Les modèles @ben peuvent être utilisés pour le polymorphisme statique, qui ne nécessite pas de fonctions virtuelles


@peoro Il existe de manière intelligente d'utiliser des modèles pour effectuer un type de polymorphisme sans chèques à usage d'exécution. Très utile dans des situations où la performance compte


@Foo: "L'alignement n'est pas un problème" - vrai, mais seulement Si vous ne le faites pas spécifiquement, ce n'est pas un (avec des exigences d'alignement personnalisées).


@Foo Bah: True, mais c'est un modèle, pas un type polymorphique . La norme dit (section [class.virtudi] ): "Une classe qui déclare ou hérite d'une fonction virtuelle s'appelle une classe polymorphe ".


@Foo Bah, j'ai coupé mon commentaire sur le polymorphisme après avoir remarqué que Ben Voigt le commença aussi. Quoi qu'il en soit, le polymorphisme statique n'est pas un polymorphisme. C'est un motif utilisé pour simuler ce que vous auriez avec le polymorphisme. Le polymorphisme statique n'est fait que lors de la compilation, lorsque vous connaissez le type le plus dérivé des objets, de sorte qu'ils ne sont pas polymorphes (bien que les modèles le rendent comme une apparence).



1
votes

section [basique.life] de la norme dit

La durée de vie d'un objet est une propriété d'exécution de l'objet. On dit qu'un objet d'initialisation non triviale s'il s'agit d'un type de classe ou d'agrégation et que celui-ci ou un de ses membres est initialisé par un constructeur autre qu'un constructeur par défaut trivial. [Remarque: l'initialisation par un constructeur de copie triviale / déplacement est une initialisation non triviale. - Note de fin] La durée de vie d'un objet de type T commence lorsque:

  • Stockage avec l'alignement et la taille appropriés pour le type T est obtenu et
  • Si l'objet a une initialisation non triviale, son initialisation est terminée.

    Étant donné que votre classe a des membres virtuels, il nécessite une initialisation non triviale. Vous ne pouvez pas affecter un objet dont la vie n'a pas commencé, vous devez initialiser avec neuf .


0 commentaires

2
votes

Classes avec Virtual CODE> Les membres contiennent un pointeur sur une circonscription dite - essentiellement une table de pointes de fonction à la mise en œuvre de ces membres virtuels. Lorsque vous utilisez Opérateur Nouveau code>, le constructeur est appelé, ce qui, même s'il s'agit d'un constructeur implicite, configurera ce pointeur sur la machine à livrer correctement.

Cependant, Malloc n'appelle pas le constructeur. Le pointeur de fourble est laissé ininitialisé, pointez sur une mémoire aléatoire. Lorsque vous essayez ensuite d'appeler une fonction virtuelle, votre désarférence un mauvais pointeur et un crash (comportement non défini). P>

La solution consiste à utiliser le placement Nouveau pour initialiser l'objet avant de l'utiliser: P>

int main( ) {
    Derived *d;
    d = (Derived*) malloc( sizeof(Derived) );
    new(d) Derived(123); // invoke constructor
// You could also do:
//    new(d) Derived;
//    *d = Derived( 123 );

    std::cout << d->x() << std::endl; // crash

    // Although in your case it does not matter, it's good to clean up after yourself by
    // calling the destructor
    d->~Derived();
    return 0;
}
  • L'alignement n'est pas un problème. La mémoire de Malloc est correctement alignée pour tout type C ++. LI>
  • attribution avec = code> n'aide pas. La mise en oeuvre par défaut de = code> copie toutes les variables de membre, mais le pointeur de la fourgonnette n'est pas un membre et n'est pas copié. Li>
  • La construction n'est pas requise pour les types de pod. Les types de numéros non pod peuvent ne pas nécessiter (ce comportement indéfini si vous ne le faites pas). En particulier, le constructeur appelle également des constructeurs variables des membres; Donc, si vous ne construisez pas l'objet extérieur, des objets intérieurs peuvent également être brisés. Li> ul> p>


1 commentaires

Tout ce type de table V est vrai dans la plupart des implémentations, mais non requis par la norme. Toute la norme indique que les objets avec les membres virtuels sont non-POD, et doivent donc être initialisés en exécutant le constructeur.