Y a-t-il une raison spécifique pour laquelle cela briserait le langage conceptuellement ou une raison spécifique pour laquelle cela est techniquement irréalisable dans certains cas?
L'utilisation serait avec un nouvel opérateur.
Edit: je vais abandonner tout espoir d'obtenir mon "nouvel opérateur" et "nouvel opérateur" directement et être direct.
Le point de la question est: pourquoi les constructeurs sont-ils spéciaux ? Gardez à l'esprit bien sûr que les spécifications linguistiques nous disent ce qui est légal, mais pas nécessairement moral. Ce qui est légal est généralement informé par ce qui est logiquement cohérent avec le reste du langage, ce qui est simple et concis, et ce qui est réalisable pour les compilateurs. La justification possible du comité des normes pour évaluer ces facteurs est délibérée et intéressante - d'où la question.
std::make_unique
etstd::make_shared
peut résoudre adéquatement la motivation pratique sous-jacente pour cette question. Ce sont des méthodes de modèle, ce qui signifie qu'il faut capturer les arguments d'entrée au constructeur, puis les transmettre au constructeur réel.Réponses:
Les fonctions pointeurs vers membre n'ont de sens que si vous avez plusieurs fonctions membres avec la même signature - sinon il n'y aurait qu'une seule valeur possible pour votre pointeur. Mais cela n'est pas possible pour les constructeurs, car en C ++, différents constructeurs de la même classe doivent avoir des signatures différentes.
L'alternative pour Stroustrup aurait été de choisir une syntaxe pour C ++ où les constructeurs pourraient avoir un nom différent du nom de la classe - mais cela aurait empêché certains aspects très élégants de la syntaxe ctor existante et aurait rendu le langage plus compliqué. Pour moi, cela ressemble à un prix élevé juste pour permettre une fonctionnalité rarement nécessaire qui peut être facilement simulée en «externalisant» l'initialisation d'un objet du ctor vers une
init
fonction différente (une fonction membre normale pour laquelle le pointeur vers les membres peut être créé).la source
memcpy(buffer, (&std::string)(int, char), size)
? (Probablement extrêmement non casher, mais c'est du C ++ après tout.)memcpy(buffer, strlen, size)
. Vraisemblablement, cela copierait l'assemblage, mais qui sait. Que le code puisse ou non être invoqué sans se bloquer, il faudrait connaître le compilateur que vous utilisez. Il en va de même pour déterminer la taille. Cela dépendrait fortement de la plate-forme, mais de nombreuses constructions C ++ non portables sont utilisées dans le code de production. Je ne vois aucune raison de l'interdire.Un constructeur est une fonction que vous appelez lorsque l'objet n'existe pas encore, il ne peut donc pas s'agir d'une fonction membre. Cela pourrait être statique.
Un constructeur est en fait appelé avec un pointeur this, après que la mémoire a été allouée mais avant qu'elle ne soit complètement initialisée. En conséquence, un constructeur possède un certain nombre de fonctionnalités privilégiées.
Si vous aviez un pointeur sur un constructeur, il faudrait soit un pointeur statique, quelque chose comme une fonction d'usine, soit un pointeur spécial vers quelque chose qui serait appelé immédiatement après l'allocation de mémoire. Il ne peut pas s'agir d'une fonction membre ordinaire et fonctionne toujours comme constructeur.
Le seul but utile qui me vient à l'esprit est un type spécial de pointeur qui pourrait être transmis au nouvel opérateur pour lui permettre d'indiquer indirectement le constructeur à utiliser. Je suppose que cela pourrait être pratique, mais cela nécessiterait une nouvelle syntaxe importante et probablement la réponse est: ils y ont pensé et cela n'en valait pas la peine.
Si vous souhaitez simplement refactoriser le code d'initialisation commun, une fonction de mémoire ordinaire est généralement une réponse suffisante, et vous pouvez obtenir un pointeur sur l'une d'entre elles.
la source
&A::A
ne fonctionne dans aucun des compilateurs que j'ai essayés.)Cela est dû au fait qu'il n'y a pas de type de retour de constructeur et que vous ne réservez aucun espace pour le constructeur en mémoire. Comme vous le faites en cas de variable lors de la déclaration. Par exemple: si u écrit une variable simple X, alors le compilateur générera une erreur car le compilateur ne comprendra pas la signification de cela. Mais lorsque vous écrivez Int x; Ensuite, le compilateur sait qu'il est de type variable de données, il réservera donc un espace pour la variable.
Conclusion: - donc la conclusion est qu'en raison de l'exclusion du type de retour, il n'obtiendra pas l'adresse en mémoire.
la source
(void)(*fptr)()
déclare un pointeur sur une fonction sans valeur de retour.Je vais faire une supposition folle:
Le constructeur et le destructeur C ++ ne sont pas du tout des fonctions: ce sont des macros. Ils sont alignés dans la portée où l'objet est créé et dans la portée où l'objet est détruit. À son tour, il n'y a ni constructeur ni destructeur, l'objet EST juste.
En fait, je pense que les autres fonctions de la classe ne sont pas des fonctions non plus, mais des fonctions en ligne qui ne sont PAS en ligne parce que vous en prenez l'adresse (le compilateur se rend compte que vous y êtes et n'inline pas ou n'inline pas le code dans la fonction et optimise cette fonction) et à son tour, la fonction semble "toujours là", même si ce ne serait pas le cas si vous ne l'avez pas prise en compte.
La table virtuelle de "l'objet" C ++ n'est pas comme un objet JavaScript, où vous pouvez obtenir son 'constructeur et créer des objets à partir de celui-ci lors de l'exécution via
new XMLHttpRequest.constructor
, mais plutôt une collection de pointeurs vers des fonctions anonymes qui agissent comme un moyen d'interfacer avec cet objet. , à l'exclusion de la possibilité de créer l'objet. Et cela n'a même pas de sens de "supprimer" l'objet, car c'est comme essayer de supprimer une structure, vous ne pouvez pas: c'est juste une étiquette de pile, écrivez-la comme bon vous semble sous une autre étiquette: vous êtes libre de utilisez une classe comme 4 entiers:Il n'y a pas de fuite de mémoire, il n'y a pas de problèmes, sauf que vous avez effectivement gaspillé un tas d'espace de pile réservé à l'interface et à la chaîne, mais cela ne détruira pas votre programme (tant que vous n'essayez pas de l'utiliser) comme une chaîne jamais plus).
En fait, si mes hypothèses précédentes sont correctes: le coût complet de la chaîne est juste le coût de stockage de ces 32 octets et de l'espace de chaîne constant: les fonctions ne sont utilisées qu'au moment de la compilation, et peuvent également être intégrées et jetées après l'objet est créé et utilisé (comme si vous travailliez avec une structure et ne vous y référiez que directement sans appel de fonction, assurez-vous qu'il y a des appels en double au lieu des sauts de fonction, mais cela est généralement plus rapide et utilise moins d'espace). En substance, chaque fois que vous appelez une fonction, le compilateur remplace simplement cet appel par les instructions pour le faire littéralement, à des exceptions que les concepteurs de langage ont définies.
Résumé: les objets C ++ n'ont aucune idée de ce qu'ils sont; tous les outils d'interfaçage avec eux sont statiquement intégrés et perdus lors de l'exécution. Cela rend le travail avec les classes aussi efficace que le remplissage des structures avec des données, et le travail direct avec ces données sans appeler aucune fonction (ces fonctions sont intégrées).
Ceci est complètement différent des approches de COM / ObjectiveC ainsi que du javascript, qui conservent les informations de type de manière dynamique, au détriment du temps d'exécution, de la gestion de la mémoire, des appels de constructions, car le compilateur ne peut pas jeter ces informations: c'est nécessaire pour une répartition dynamique. Cela nous donne à son tour la possibilité de «parler» à notre programme au moment de l'exécution et de le développer pendant son exécution en ayant des composants réfléchissants.
la source
struct { int size; const char * data; };
(comme vous semblez le supposer), vous écrivez 4 * 4 octets = 16 octets sur une adresse mémoire où vous n'avez réservé que 8 octets sur une machine x86, donc 8 octets sont écrits sur d'autres données (ce qui peut corrompre votre pile ). Heureusement, ilstd::string
dispose normalement d'une optimisation en place pour les chaînes courtes, il doit donc être suffisamment grand pour votre exemple lors de l'utilisation d'une implémentation std majeure.