可能重复:
Nullable values in C++
在C ++中表示可空成员的最佳方法是什么?
在C#中,我们可以使用Nullable<T>
类型。非常需要这样的数据类型,因为并非所有内容都具有有意义的值。这是一个非常重要的数据类型,@Jon Skeet已经花了整整一章,跨越了27页,在他的优秀书C# in Depth中只描述了Nullable<T>
。
一个简单的例子可以是Person
类 1 ,定义为:
struct Person
{
std::string Name;
DateTime Birth;
DateTime Death;
//...
};
由于一个人总是有生日,所以上述类的Birth
成员总是会有一些有意义的值。但Death
怎么样?如果这个人还活着,它应该有什么价值呢?在C#中,此成员可以声明为Nullable<DataTime>
2 ,如果此人活着,可以使用null
进行分配。
在C ++中,解决这个问题的最佳方法是什么?截至目前,我只考虑了一个解决方案:将成员声明为指针:
DataTime *Death;
现在,当这个人还活着时,它的值可以是nullptr
。但它迫使new
用于死人,因为它会有一些有效的价值。它反过来意味着不能依赖编译器生成的默认复制语义代码。程序员必须在rule of three(C ++ 03)之后编写复制构造函数,复制赋值,析构函数,或者在C ++ 11中编写rule of five。
对于这个问题,我们是否有更好,更优雅的解决方案,而不仅仅是指针?
1。其他示例包括关系数据库表,因为在许多DBMS列中可以为空。功能
2。还有一个简写。可以写DataTime?
与Nullable<DateTime>
完全相同。功能
答案 0 :(得分:39)
您可以查看Boost.Optional:
struct Person
{
std::string Name;
DateTime Birth;
boost::optional<DateTime> Death;
//...
};
Death
最初是“未初始化的”。=
为Death = myDateTime
分配值,例如Death.is_initialized()
。Death.get()
时,您可以使用Death.reset()
。DateTime
再次取消初始化。对于像这样的简单案例,通常认为选择你自己公然的哨兵价值更为连贯,例如,{00} 00:00的“{1}}”。
答案 1 :(得分:6)
取决于DateTime
- 就像@Tomalak在回答中所说,boost::optional<>
是一种通用的解决方案。但是,如果您的DateTime
是boost::posix_time::ptime
,则表示已支持特殊值(例如not_a_date_time
或pos_infin
) - 您可以使用这些。
答案 2 :(得分:5)
我参与过的每个项目都有某种Fallible
,Maybe
或
Nullable
模板类。 (实际名称倾向于反映出什么
应用程序首先需要它:Fallible
作为返回值,
Nullable
来建模数据库等)。最近,Boost有了
介绍boost::optional
;遗憾的是,他们使用隐式转换
而不是isValid
(命名)函数,这导致显着
不太可读的代码(我可以避免它,除了可能的
实现我自己的Maybe
)。
答案 3 :(得分:1)
你可以做大量的程序员在你之前做的事情!使用特定值作为“不存在”...例如我听说“2000年1月1日”很常见:-) :-)出于互操作性原因,您可以使用“19 Jan 2038 03:14:07 UTC” :-) :-)(这是一个笑话,如果不清楚。我正在引用Y2K问题和Y2038问题。我正在展示使用“特殊”日期作为状态的问题......像11这样的事情-11-11和类似的)
DataTime
的最大值/最小值可能更正确:-)而且它仍然是错误的,因为您正在混合“状态”和“值”。更好的是你在C ++中重建Nullable
类型(最后它很容易:带有bool for null / not null和T字段的模板化类)
答案 4 :(得分:1)
因为死亡特别不可能在出生之前的任何时间出生,你也可以将其设置为出生 - 1最初并且在实际事件中改变它。在更常见的术语中,您可能称为出生 - 1是哨兵值或占位符值。你也可以选择一个足够低的常数值,不要被误认为是真值,但这假设你对数据有一定的了解。
答案 5 :(得分:-2)
我会创建一个表示空值的静态成员,而不是将死亡日期的地址与静态对象的地址进行比较。如果它们相等,则值为NULL。