C++ 指示双精度值尚未初始化的最佳方式是什么?
我有一个CS类,它代表三维坐标系,即(x,y,z) 我希望用户可以创建一个CS(0,0,0)。 在构造函数中,我想将x、y和z的地址初始化为NULL。 这是为了区分用户定义的(0,0,0)和默认值。 我正在动态创建CS的对象,因此使用以下代码没有任何意义:C++ 指示双精度值尚未初始化的最佳方式是什么?,c++,null,C++,Null,我有一个CS类,它代表三维坐标系,即(x,y,z) 我希望用户可以创建一个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 = N
class CS
{
private:
double *x;
double *y;
double *z;
}
CS:CS()
{
x = new double;
x = NULL;
//same for y & z
}
首先,我想在不使用指针的情况下手动为任何变量(int、double或char)分配0x000000地址。
有什么建议吗
我希望用户可以创建一个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
}
这就是问题所在。首先,默认值?什么是默认值?为什么要有一个默认值?那是错误的。其次,你根本不可能改变任何变量的地址
你想做的事是做不到的,即使可以,那也是一个可怕的坏主意。你有几种选择:
double
,not-a-number通常是一个自然的候选者。对于int
和char
来说,选择一个好的值通常比较棘手毫无疑问,这些选择中没有一个比其他两个更好,因为它们涉及不同的权衡。请选择。您不能将x、y和z的位置更改为空,因为这些位置始终与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())
{ }
CS:CS()
:x(标准::数值限制::安静时间()
:y(标准::数值限制::安静时间()
:z(标准::数值限制::安静时间()
{ }
用户可能不会有意将x、y和z设置为(不是数字)。您不能更改变量的地址。您不能将指针值(如C++中的
NULL
,或nullptr
)分配给非指针类型的变量,例如double
,如果没有哨兵,您就无法用相同的位数表示它。如果0是有效数字,则无法使用它。如果您尝试将null处理强加给一个值类型,您将得到根本不正确且不可维护的代码
正确处理空值时,您可能会看到如下接口:
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");
}
}
对我来说,
foo
和bar
之间的区别在于,处理数据的低级代码不应强制执行数据是否存在的策略。在更高的抽象级别上,您可以并且应该对数据在使用时是否有效抱有期望。两者都可以存在于一个系统中,但foo是必需的。为什么不能简单地做到这一点:
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;
}
获取所需内容的语义的一种方法是将坐标的数据类型设置为一个带有值的类型,该值指示是否已分配。像这样的
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;
};
模板
类协调值{
公众:
CoordinateValue():未初始化(true),val(0){}
CoordinateValue(tx):未初始化(false),val(x){}
void setVal(tx){val=x;未初始化=false}
//琐碎的获取者
私人:
T值;
bool未初始化;
};
我更喜欢像这样的方法,而不是更可爱的方法,除非由于某种原因,内存真的很稀少
如果坐标为“全部默认”或“全部设置”,则可以使用单个标志,而不是包含该标志的坐标数据类型
首先,我想手动将0x000000地址分配给任何变量(int、double或char),而不使用指针。有什么建议吗
那不是你想要的。您需要的是检测变量是否已设置的能力
其他人建议使用特定的浮点值来检测未初始化状态,但我建议使用。考虑:
class CS
{
private:
boost::optional<double> x;
boost::optional<double> y;
boost::optional<double> z;
}
缺点是访问数据有点复杂:
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.
x=5.0//初始化该值。x现在有了数据。
y=4.0*x//失败。x不是双精度;它是可选的。
y=4.0*(*x)//编译,但仅当x有值时才在运行时工作。
如果我理解正确,您希望能够区分无效的、默认构造的CS和值为(0.0、0.0、0.0)
的有效CS。这正是boost::optional
的用途。您是否考虑过只添加一个标志已初始化
?这将是一个简单的解决方案。为什么它们首先是指针,为什么不只是双倍?你是否在寻找与nullable struct等效的结构?@JamesMcNellis你能给我看一些代码吗…你如何知道用户是否输入了0值,还是默认值?@MooingDuck:事实上,NULL
必须定义为整数文本,不强制转换为void*
(或在C++11中定义为nullptr
)。然而,从上下文来看,问题是询问空指针和零值对象;这两者之间的差别很大。默认值是在构造函数中指定的
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.