What is the best way to indicate that a double value has not been initialized?

前端 未结 9 1869
萌比男神i
萌比男神i 2020-12-22 06:26

I have a class CS which is to represent the co-ordinate system in 3D i.e.(x, y, z)

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


        
相关标签:
9条回答
  • 2020-12-22 06:53

    If I understand correctly, you want to be able to tell the difference between an invalid, default constructed CS and a valid one with values (0.0, 0.0, 0.0). This is exactly what boost::optional http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html is for.

    0 讨论(0)
  • 2020-12-22 06:55

    Why can't you simply do this:

    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;
    }
    
    0 讨论(0)
  • 2020-12-22 06:56

    You can't really represent it in the same number of bits without having a sentinel. If 0 is a valid number, then you can't use it. If you try and foist null handling into a value type you will have fundamentally incorrect and unmaintainable code.

    When handling nulls properly you would expect to see an interface like this:

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

    The implementation can have a flag that it checks before 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");
      }
    }
    

    You don't want to use an invalid value and not know it. Having to check the return values is tedious obviously, but it gives you the most control. You could also have helpers or overloads that would throw if they weren't valid.

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

    For me, the difference between foo and bar is that low level code handling the data shouldn't enforce a policy of whether the data is there or not. At higher levels of abstraction you can and should have expectations of whether the data should valid when you go to use it. The both can exist in a system, but foo is necessary.

    0 讨论(0)
  • 2020-12-22 06:58

    You have several options:

    1. Use pointers.
    2. Use a boolean flag alongside each variable indicating whether the variable has been set.
    3. If the range of allowable values is limited, you could use a special value to stand for "not set". For double, a not-a-number is often a natural candidate. For int and char it's often more tricky to pick a good value.

    None of these options is indisputably better than the other two as they involve different tradeoffs. Take your pick.

    0 讨论(0)
  • 2020-12-22 07:00

    One way to get the semantics of what you want would be to have the datatype of the coordinates be a type that carries with it a value indicating whether it has been assigned. Something like this.

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

    I'd prefer something like this over cuter methods unless memory is really scarce for some reason.

    If the coordinates are either all default or all set, then you can have a single flag rather than a coordinate datatype that includes the flag.

    0 讨论(0)
  • 2020-12-22 07:04

    You can't change the positions of x,y,and z to be NULL, since there positions will always be offsets from the CS object. They will always exist. It's not that CS has an x like you have a car, it's like CS has an x like you have a head. You can't not have a head. If they were integers, you would have to make them pointers (like you said you didn't want to do), because that would be the only way to tell uninitialized from initialized. However, doubles have a magic value that is rarely used:

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

    Users probably won't be setting x, y, and z to (NOT A NUMBER) intentially.

    0 讨论(0)
提交回复
热议问题