Как лучше всего указать, что двойное значение не было инициализировано? - PullRequest
0 голосов
/ 25 октября 2011

У меня есть класс CS, который должен представлять систему координат в 3D, то есть (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:
}

Я хочу, чтобы пользователь мог создать CS (0, 0,0).В конструкторе я хочу инициализировать адрес x, y & z в NULL.это делается для того, чтобы различать пользовательское значение (0, 0, 0) и значение по умолчанию.Я создаю объекты CS динамически, поэтому есть нет точки в использовании следующего кода:

class CS
{
  private:
        double *x;
        double *y;
        double *z;
}

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

Прежде всего, я хочу вручную назначить адрес 0x000000 для любой переменной (int, double или char) без использования указателей.какие-либо предложения?

Ответы [ 9 ]

13 голосов
/ 25 октября 2011

Вы не можете изменить позиции x, y и z на NULL, так как там позиции всегда будут смещениями от объекта CS. Они всегда будут существовать. Это не значит, что у CS есть x, как у вас есть машина, это как у CS есть x, как у вас есть голова. Вы не можете не иметь голову. Если бы они были целыми числами, вы должны были бы сделать их указателями (как вы сказали, что не хотели делать), потому что это был бы единственный способ отличить неинициализированный от инициализированного. Однако double s имеют магическое значение, которое редко используется:

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

Пользователи, вероятно, не будут устанавливать x, y и z в (НЕ НОМЕР) намеренно.

4 голосов
/ 25 октября 2011

Прежде всего, я хочу вручную назначить адрес 0x000000 для любой переменной (int или double или char) без использования указателей.какие-либо предложения?

Это не то, что вы хотите.Что вы хотите - это способность определять, была ли переменная установлена ​​или нет.

Другие предлагали такие вещи, как использование определенного значения с плавающей точкой для обнаружения неинициализированного состояния, но я предлагаюиспользуя Boost.Optional .Обратите внимание:

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

boost::optional либо сохраняет тип, заданный вами для параметра шаблона, либо ничего не сохраняет.Вы можете проверить разницу с помощью простого логического теста:

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

Недостатком является то, что доступ к данным немного сложнее:

x = 5.0; //Initialize the value. x now has data.
y = 4.0 * x; //Fails. x is not a double; it is an optional<double>.
y = 4.0 * (*x); //Compiles, but only works at runtime if x has a value.
2 голосов
/ 25 октября 2011

Если я правильно понимаю, вы хотите, чтобы вы могли различить недопустимую, построенную по умолчанию CS и действительную со значениями (0.0, 0.0, 0.0). Это именно то, для чего boost::optional http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html.

2 голосов
/ 25 октября 2011

Один из способов получить семантику того, что вы хотите, состоит в том, чтобы тип данных координат был типом, который несет в себе значение, указывающее, было ли оно присвоено.Примерно так.

template<typename T>
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;
};

Я бы предпочел что-то подобное методам cuter, если только по какой-то причине памяти действительно мало.

Если все координаты заданы по умолчанию или полностью установлены, тоу вас может быть один флаг, а не координатный тип данных, который включает флаг.

2 голосов
/ 25 октября 2011

Почему вы не можете просто сделать это:

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;
}
2 голосов
/ 25 октября 2011

Вы не можете реально представить его в одном и том же количестве бит без часового.Если 0 является действительным числом, то вы не можете его использовать.Если вы попытаетесь навязать нулевую обработку типу значения, у вас будет принципиально неверный и не поддерживаемый код.

При правильной обработке нулей вы ожидаете увидеть такой интерфейс:

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

У реализации может быть флаг, который она проверяет перед доступом.

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");
  }
}

Вы не хотите использовать недопустимое значение и не знаете его.Необходимость проверки возвращаемых значений, очевидно, утомительна, но дает вам максимальный контроль.У вас также могут быть помощники или перегрузки, которые будут выдавать, если они недействительны.

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

Для меня разница между foo и bar заключается в том, что низкоуровневый код, обрабатывающий данные, не долженпроводить политику того, есть ли данные там или нет.На более высоких уровнях абстракции вы можете и должны иметь ожидания относительно того, должны ли данные быть действительными, когда вы собираетесь их использовать.Оба могут существовать в системе, но foo необходимо.

2 голосов
/ 25 октября 2011

У вас есть несколько вариантов:

  1. Используйте указатели.
  2. Используйте логический флаг рядом с каждой переменной, указывающий, установлена ​​ли переменная.
  3. Если диапазон допустимых значений ограничен, вы можете использовать специальное значение для обозначения «не установлено». Для double не число является естественным кандидатом. Для int и char зачастую сложнее выбрать хорошее значение.

Ни один из этих вариантов не является однозначно лучше, чем два других, как они связаны с различными компромиссы. Выбирай.

1 голос
/ 25 октября 2011

Я хочу, чтобы пользователь мог создать CS (0, 0, 0).В конструкторе я хочу инициализировать адрес x, y & z в NULL.это делается для того, чтобы различать пользовательское значение (0, 0, 0) и значение по умолчанию.Я создаю объекты CS динамически, поэтому нет смысла использовать следующий код:

В этом проблема.Во-первых, значение по умолчанию?Какое значение по умолчанию?Почему должно быть значение по умолчанию?Это неверно.А во-вторых, для вас принципиально невозможно изменить адрес какой-либо переменной.

Невозможно выполнить то, что вы хотите, и даже если бы это было возможно, это была бы ужасно плохая идея.

0 голосов
/ 25 октября 2011

Вы не можете изменить адрес переменной. И вы не можете присвоить значения указателя (например, NULL или nullptr в C ++) переменной типа, отличного от указателя, например double.

...