Quel est le meilleur moyen d’indiquer qu’une valeur double n’a pas été initialisée?

J’ai une classe CS qui représente le système de coordonnées en 3D, à savoir (x, y, z)

class CS { private: double x; double y; double z; } CS::CS() { x = NULL;//this causes x = 0//i want the address of x to be 0x000000 & not x = 0 y = NULL; z = NULL: } 

Je veux que l’utilisateur puisse créer un CS (0, 0, 0). Dans le constructeur, je veux initialiser l’adresse de x, y & z sur NULL. ceci permet de différencier les valeurs définies par l’utilisateur (0, 0, 0) et la valeur par défaut. Je crée les objects de CS de manière dynamic, il est donc inutile d’utiliser le code suivant:

 class CS { private: double *x; double *y; double *z; } CS:CS() { x = new double; x = NULL; //same for y & z } 

Principalement, je veux assigner manuellement l’adresse 0x000000 à n’importe quelle variable (int, double ou car) sans utiliser de pointeur. Aucune suggestion?

Vous ne pouvez pas modifier les positions de x, y et z pour qu’elles soient NULL, car ces positions seront toujours des décalages par rapport à l’object CS. Ils existeront toujours. Ce n’est pas que CS ait un x comme vous avez une voiture, c’est comme si CS a un x comme vous avez une tête. Vous ne pouvez pas ne pas avoir de tête. S’ils étaient des entiers, vous devriez en faire des pointeurs (comme vous avez dit que vous ne vouliez pas le faire), car ce serait le seul moyen de distinguer les non initialisés des initialisés. Cependant, les double s ont une valeur magique rarement utilisée:

 CS:CS() : x(std::numeric_limits::quiet_NaN()) : y(std::numeric_limits::quiet_NaN()) : z(std::numeric_limits::quiet_NaN()) { } 

Les utilisateurs ne définiront probablement pas x, y et z sur (NON NUMÉRO) de manière essentielle.

Essentiellement, je veux atsortingbuer manuellement l’adresse 0x000000 à n’importe quelle variable (int, double ou car) sans utiliser de pointeur. Aucune suggestion?

Ce n’est pas ce que tu veux. Ce que vous voulez, c’est pouvoir détecter si une variable a été définie ou non.

D’autres ont suggéré d’utiliser des valeurs en virgule flottante spécifiques pour détecter l’état non initialisé, mais je suggère d’utiliser Boost.Optional . Considérer:

 class CS { private: boost::optional x; boost::optional y; boost::optional z; } 

boost::optional stocke le type que vous donnez au paramètre template ou ne stocke rien. Vous pouvez tester la différence avec un simple test booléen:

 if(x) { //Has data } else { //Has not been initialized } 

L’inconvénient est que l’access aux données est un peu plus complexe:

 x = 5.0; //Initialize the value. x now has data. y = 4.0 * x; //Fails. x is not a double; it is an optional. y = 4.0 * (*x); //Comstacks, but only works at runtime if x has a value. 

Vous avez plusieurs options:

  1. Utilisez des pointeurs.
  2. Utilisez un indicateur booléen à côté de chaque variable pour indiquer si la variable a été définie.
  3. Si la plage de valeurs autorisées est limitée, vous pouvez utiliser une valeur spéciale pour signifier “non défini”. Pour double , un non-nombre est souvent un candidat naturel. Pour int et char il est souvent plus difficile de choisir un bon rapport qualité-prix.

Aucune de ces options n’est indiscutablement meilleure que les deux autres car elles impliquent des compromis différents. Faites votre choix.

Pourquoi ne pouvez-vous pas simplement faire ceci:

 class CS { public: // Constructs a CS initialized to 0, 0, 0 CS() : x(0), y(0), z(0), is_initialized(false) {} // User defined values CS(double newX, double newY, double newZ) : x(newX), y(newY), z(newZ), is_initialized(true) {} private: double x; double y; double z; // If you need to know that this was initialized a certain way, you could use this suggestion from the comments: bool is_initialized; } 

Si je comprends bien, vous voulez pouvoir faire la différence entre un CS construit invalide par défaut et un CS valide avec des valeurs (0.0, 0.0, 0.0) . C’est exactement ce à quoi boost::optional http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html .

Vous ne pouvez pas vraiment le représenter dans le même nombre de bits sans avoir une sentinelle. Si 0 est un nombre valide, vous ne pouvez pas l’utiliser. Si vous essayez de manipuler des valeurs null dans un type de valeur, vous aurez un code fondamentalement incorrect et non maintenable.

Lorsque vous gérez correctement les valeurs NULL, vous vous attendez à voir une interface comme celle-ci:

 struct foo { virtual ~foo() {} virtual bool getX(double &val) = 0; virtual bool getY(double &val) = 0; virtual bool getZ(double &val) = 0; }; 

L’implémentation peut avoir un drapeau à vérifier avant l’access.

 void some_func(foo *f) { double x, y, z; if (f->getX(x) && f->getY(y) && f->getZ(z)) { cout << x << ", " << y << ", " << z << endl; } else { throw std::logic_error("expected some values here"); } } 

Vous ne voulez pas utiliser une valeur invalide et ne pas le savoir. Avoir à vérifier les valeurs de retour est évidemment fastidieux, mais cela vous donne le plus de contrôle. Vous pourriez également avoir des aides ou des surcharges qui jetteraient s'ils n'étaient pas valides.

 struct bar { double getX() { if (!valid) throw std::logic_error("bar is not valid"); return x; } bool valid; double x, y, z; } 

Pour moi, la différence entre foo et bar est que le code de bas niveau qui traite les données ne devrait pas imposer une foo indiquant si les données sont là ou non. À des niveaux d'abstraction plus élevés, vous pouvez et devriez vous attendre à ce que les données soient valides au moment de les utiliser. Les deux peuvent exister dans un système, mais foo est nécessaire.

Une façon d’obtenir la sémantique de ce que vous voulez consiste à faire en sorte que le type de données des coordonnées soit un type qui comporte une valeur indiquant s’il a été atsortingbué. Quelque chose comme ça.

 template class CoordinateValue { public: CoordinateValue() : uninitialized(true), val(0) {} CoordinateValue(T x) : uninitialized(false), val(x) {} void setVal(T x) {val = x; uninitialized= false} // Trivial getters private: T val; bool uninitialized; }; 

Je préférerais quelque chose comme ceci aux méthodes plus mignonnes à moins que la mémoire soit vraiment rare pour une raison quelconque.

Si les coordonnées sont toutes définies par défaut ou définies, vous pouvez utiliser un seul indicateur plutôt qu’un type de données de coordonnées incluant l’indicateur.

Je veux que l’utilisateur puisse créer un CS (0, 0, 0). Dans le constructeur, je veux initialiser l’adresse de x, y & z sur NULL. ceci permet de différencier les valeurs définies par l’utilisateur (0, 0, 0) et la valeur par défaut. Je crée les objects de CS de manière dynamic, il est donc inutile d’utiliser le code suivant:

C’est le problème. Tout d’abord, la valeur par défaut? Quelle valeur par défaut? Pourquoi devrait-il y avoir une valeur par défaut? C’est faux. Et deuxièmement, il est fondamentalement impossible pour vous de changer l’adresse d’une variable.

Ce que vous voulez ne peut pas être fait et même s’il le pouvait, ce serait une idée terriblement mauvaise.

Vous ne pouvez pas changer l’adresse d’une variable. Et vous ne pouvez pas affecter de valeurs de pointeur (telles que NULL ou nullptr en C ++) à une variable de type non-pointeur, telle que double .