8
votes

Les membres de la classe sont-ils garantis pour être contigus en mémoire?

J'ai une classe qui détient un certain nombre de pointeurs de fonction et je voudrais que tous soient initialisés à NULL lorsque des objets sont construits. Pour ce faire, je prévoyais d'utiliser Memset sur les emplacements de mémoire du premier pointeur à celui du dernier pointeur, mais je ne sais pas si cela fonctionnerait 100% du temps.

est-il garanti que si ces pointeurs de fonction sont Déclaré de manière contiguë dans la classe que leurs emplacements de mémoire seraient également contigus. Je suppose que le rembourrage n'affectera pas ce que j'essaie de faire en tant qu' octets de rembourrage, il suffirait d'être défini sur NULL également. P>

Exemple de classe de classe P>

class C
{
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};

c++

9 commentaires

Non et n'utilisez jamais memset dans c ++ comme ceci.


Une meilleure question est de savoir comment obtenir tous les membres initialisés à NULL. Demandez ce que vous voulez vraiment.


Quelle serait la meilleure façon de les mettre à tous pour NULL? De toute évidence, chacun pourrait être fait individuellement, mais comme plus a été ajouté à la classe qui pourrait devenir très encombrante.


@Pubby meta.stackexchange.com/q/66377/188010


Même si cela fonctionnerait, c'est effrayant, dangereux et non travenant.


@ctor Vous pouvez mettre les pointeurs dans une structure distincte, puis initialiser avec la syntaxe globale, c'est-à-dire {0} . Cela initialise tout à NULL.


Il est vraiment triste que les réponses doivent être plus de trois personnages


@Pubby Vous devriez faire une réponse de la suggestion struct . C'est la solution la plus simple. Et dans la liste d'initialistes, vous pouvez simplement faire des données () (où data est le nom de la structure) et obtenez tous les membres zéro initialisés.


Bien que j'apprécie les commentaires ici, ils ne sont pas vraiment "sur le sujet". Bien sûr, je suis d'accord, je suis d'accord, il est probablement imprudent d'initialiser avec Memset et certainement pas de bonnes pratiques; De plus, il existe certainement des moyens de le faire car les gens l'ont souligné. Cependant, la question est «sont-elles contiguës ou non» et dans le code qu'il a montré, Memset fonctionnera bien ici. Bien sûr, ce n'est pas une garantie que les choses ne vont pas exploser à l'avenir.


4 Réponses :


1
votes

jamais le faire en C ++, à moins que vous sachiez vraiment ce que vous faites. C'est très sujet aux erreurs et les futurs mainteneurs de votre code (qui pourraient même être vous!) Peut-être que vous ne reconnaîtrez pas tous les pièges présents et que les choses vont probablement devenir horriblement mal. La manière C ++ de réaliser ceci est d'écrire un constructeur qui initialise correctement les pointeurs de fonction sur nullptr (si vous utilisez c ++ 11) ou 0


15 commentaires

Pourquoi? Memset est parfaitement bien dans ce cas, tant que son code est le même que celui qu'il a affiché.


Il y a le problème. Tant que la structure ordinaire (POD), il n'y a pas de problème. Cependant, je doute que cela reste de cette façon. Une certaine âme innocente viendra, ajoutez une nouvelle chose osty et tout coups dans votre visage.


@Pubby - Je pense que c'est une recette de désastre: regarder la définition de l'interface, rien qui vous indique que la commande interne des membres est importante (c'est-à-dire que vous ajoutez quelque chose d'autre entre ces fonctions, vous pouvez obtenir des corruptions de mémoire »). C'est le type de code qui vous fait des défauts comme «Cela n'a aucun sens, mais l'application ne collecte que sur la victoire avec cela et cette optimisation et uniquement à chaque quatrième exécution». C'est hacky, obscur et un problème si vous (ou quiconque sinon jamais) doivent maintenir / changer ce code.


@Pubby memset est pas garanti de définir un pointeur sur NULL. Dans ce cas, quelque chose comme func1 () est la meilleure solution.


@Jameskanze, comment peut-on Memset (..., NULL, ...) PEUILLEZ-VOUS POSSIBLE POUR UN EMPLACEMENT POUR ÊTRE SET TO NULL?


@CTOR: La mise en œuvre est libre d'utiliser toute représentation qu'elle aime pour un pointeur NULL. Nulle part dans la norme ne dit-il que cela doit être tous des bits-zéro. Les architectures historiques ont été utilisées dans lesquelles l'adresse 0 est utilisée pour un objectif particulier du matériel et les pointeurs NULL sont représentés par une adresse différente.


@ctor memset ignore le type et toutes les conversions implicites. Formellement, il est inutile pour tout sauf type intégral. Je n'ai jamais entendu parler d'une mise en œuvre où il ne marcherait pas aussi pour un point flottant également (même si je ne pense pas que ce soit garanti). Il y a certainement des implémentations où il ne fonctionnerait pas pour les pointeurs, cependant (et des cas où int i = 0; char * p = (char *) i; a donné des résultats différents que p = (char *) 0; ).


@Michaelwild Vous devez définir de manière déficité une proposition de la norme C ++ suivante pour supprimer les POD, le memset et tout ce personnel de bas niveau inutile de C ++.


@Konstantinoznobihin n'est pas le point. Le point est que l'utilisation de ce type de construction est très, très sujette d'erreur. Et à moins que vous fassiez un développement de bas niveau (comme la mise en œuvre d'un protocole de réseau, interagir avec le noyau OS, etc.), la réponse est simple: ne l'utilisez pas. Le C ++ moderne est aussi loin de c comme C provient de l'assembleur. Probablement encore plus loin ...


@Konstantinoznobihin d'ailleurs, la standard mentionne memset uniquement comme faisant partie de la en-tête, rien de plus.


@Michaelwild qui ne peut pas trouver aucun sens pour une telle généralisation. Si vous voulez dire quelque chose d'autre, comme "... Utiliser ce type de construction est très, très sujet à une erreur ...", pourquoi ne pas la mettre à la réponse? Bien que cela ne soit pas plus utile, ce serait correct au moins.


@Konstantinoznobihin Pod Strts est nécessaire pour interfacer avec C (et tout ABI défini en termes de C, comme Windows ou UNIX). Même memset peut avoir ses utilisations, si vous implémentez des trucs de niveau très bas.


@Michaelwild La norme indique que memset (et la plupart des autres fonctions dans ) sont définis par la norme C. Et la norme C décrit la sémantique du Memset très exactement.


Mais simplement parce que cela ne signifie pas que vous devriez l'utiliser dans un code de haut niveau normal. Comme je l'ai souligné, c'est très sujet d'erreur. Si vous n'êtes pas d'accord, utilisez memset , malloc , memcpy et strcpy autant que vous le souhaitez, mais pas Appelez-le C ++.


@Pubby tandis que j'accepte votre argument que ce n'est pas clair pour que quiconque maintienne le code, simplement une mauvaise pratique et un danger pour une modification future, mais comme une chose conceptuelle, comme le sort du code, tout va bien fonctionner.



3
votes

Faites-le d'abord.

class C
{
public:
    C() : func1(nullptr), func2(nullptr), func3(nullptr), func4(nullptr)
    { };
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};


2 commentaires

: FUNC1 (), FUNC2 (), FUNC3 (), FUNC4 () est plus court


+1. J'ajouterais même que si vous utilisez Memset (ou quelque chose de similaire) est le meilleur moyen d'initialiser ces pointeurs, tout optimiseur d'une valeur de son sel utilisera bien sûr. Et si ce n'est pas - pourquoi essayer de le faire à la main en premier lieu?



10
votes

Il est garanti qu'elles apparaissent avec des adresses croissantes dans l'ordre déclaré. Ceci est vrai en général des membres de données sans intervenir les spécificateurs d'accès, donc s'il existe d'autres membres de données dans la classe, la seule façon de pouvoir intervenir est s'il y a des spécificateurs d'accès là-bas.

Je ne pense pas qu'il soit garanti de la garantie Soyez sûr pour modifier les octets de rembourrage. Je ne pense pas qu'il soit garanti que la mise en œuvre ne mettrait pas "quelque chose d'important" entre les membres de données, bien que je ne puisse penser immédiatement à rien d'une implémentation veut em> mixte là-bas. Tapez des informations pour un gc de marquage précis à une conception étrangement conçu? Des valeurs reconnaissables à tester pour les dépassements de tampon? P>

Il n'est pas garanti que tous les bits-zéro représentent un pointeur de fonction NULL. P>

Vous pouvez faire face à la question des tout-bits-zéro Représentation en utilisant quelque chose comme: p> xxx pré>

mais cela laisserait toujours la question du rembourrage. Vous n'êtes garantissant aucun rembourrage dans un tableau, mais pas (par la norme) dans une classe. L'ABI utilisé par votre implémentation pourrait spécifier la mise en page de la structure au degré nécessaire pour garantir que votre classe ci-dessus est la même qu'une matrice de 4 pointes de fonction. P>

Une alternative est de procéder à ce qui suit: P>

struct function_pointers {
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};

class C : private function_pointers
{
public:
    C() : function_pointers() {}
};


6 commentaires

Si tous les bits zéro ne représentent pas un pointeur de fonction NULL, alors quoi d'autre la fixerait à 0?


Si Ce n'est pas un pointeur de fonction valide, il est ub d'accéder au membre de données. Il est peu probable que cela ne soit pas la représentation, mais vous avez demandé ce qui est garanti. En supposant que ce ne soit pas, je pense que le comportement le plus probable est que cela ne manquerait pas de comparer égale à NULL. Une autre possibilité est que la CPU essaie de charger la représentation invalide dans un registre. Fondamentalement, si ce que vous avez écrit n'est pas une représentation valide, elle se comportera comme un objet non initialisé.


@ctor 0 est une constante de pointeur nulle. L'attribuer à un pointeur donne à ce pointeur une valeur de pointeur nulle. La valeur du pointeur NULL n'a pas nécessairement toutes les 0 bits comme représentation.


@ctor Il y a une différence entre définir un pointeur sur 0 (par exemple avec une affectation ou une initialisation), et memset . Lorsque vous attribuez 0 à un pointeur, il y a une conversion de type, qui peut change de bits. (Dans le passé, il y avait des machines où Null Pointers n'étaient pas tous les bits. Je ne sais pas si c'est toujours le cas.)


Re "sans spécificateurs d'accès intermédiaires", bien c'était en C ++ 03. comme je me souviens que C ++ 11 donne une garantie plus large


reposer un peu important entre les deux, peut arriver en classe dérivée



1
votes

Ne jamais utiliser Memset sur des objets non triviaux, en particulier sur ceux qui sont polymorphes (ayant des fonctions virtuelles ou découlant de la classe de base ayant des méthodes virtuelles, etc.). Si vous le faites, vous exploseriez la VPTR qui pointe vers Tablevisible ! Une catastrophe!

VPTR & TVTAble est utilisé pour implémenter un comportement polymorphe, de respecter ces membres de la classe cachée.

Memset doit être utilisé lorsque nous parlons en termes de bits et d'octets, pas lorsque nous parlons d'objets.

Respectez les objets non triviaux, dites NON à MEMSET :)


2 commentaires

UHM, en C ++ un int est un objet. La définition C ++ de "objet" est à peu près une région de mémoire (qui n'a pas besoin d'être contiguë) avec un type associé


Je voulais dire des objets non triviaux. Édité :)