我目前正在设计一个看起来大致如下的类层次结构:
struct Protocol
{
// Pass lower-layer protocol as a reference.
Protocol(Protocol & inLLProtocol) :
mLLProtocol(inLLProtocol)
{
}
// A protocol "always" has a LLProtocol.
Protocol & mLLProtocol;
};
struct Layer1Protocol : Protocol
{
// This is the "bottom" protocol, so I pass a fake reference.
Layer1Protocol() : Protocol(*static_cast<Protocol*>(nullptr)) {}
};
只要永远不会访问引用,IIRC绑定对*nullptr
的引用是安全的。所以现在我有责任以这种方式设计我的Layer1Protocol类来防止这种情况。
我喜欢这种方法,因为我确保所有用户协议实例将引用它们各自的低层协议(Layer1Protocol是例外,但它是核心库的一部分)。我认为这比使用指针更可取,因为一旦引入指针,就可以传递空指针,然后可能需要在运行时检查,结果是很多指针检查代码和偶尔的错误。
您认为我的基于参考的方法是否可以辩护?或者使用空引用总是一种不好的做法?
答案 0 :(得分:11)
IIRC绑定对* nullptr的引用是安全的,只要永远不会访问引用。
不,取消引用nullptr
始终是未定义的行为。在正确的程序中不能有“空引用”。
答案 1 :(得分:10)
编写*static_cast<Protocol*>(nullptr)
取消引用nullptr
,它会调用未定义的行为。你绝不能这样做。结果可能是墨菲所感受到的。这个可能就是你所谓的“空引用”,但也可能是你(男性,AFAIK)怀孕了,而程序的工作方式就好像你已经取消引用了一个有效的指针。
因此,严格来说,没有“空引用”。只有未定义的行为。
您的协议类始终且不可避免地需要较低级别的协议,在这种情况下,您不得将nullptr
传递给它(甚至不会在参考中伪装)。或者它不需要一直使用较低级别的协议,并且必须先检查它是否有一个才能访问它,在这种情况下,指针可以更好地实现您的设计约束。
答案 2 :(得分:2)
除了语法之外,你的方法与使用指针的等效方法有何不同之处,除了指针接近这一事实,与参考方法不同,它不会调用UB?
如果你正在处理引用以避免检查空指针但是你传递(非法)空引用,那么你已经否定了引用的优点。现在,您的引用可能突然变为null,因此您可能需要检查该条件,就像使用指针一样。
如果如你所说,你可以保证这些空引用都不会被取消引用 - 好吧,如果你使用指针,那同样的保证就会成立。
长话短说:指针不是不惜一切代价都可以避免的。如果您遇到可能出现空值的情况,则需要使用指针 - 句点。您似乎担心必须在任何地方检查空指针(可能是通过断言),但在大多数架构中,MMU无论如何都会阻止您取消引用空指针 - 这实际上几乎和断言一样好。
答案 3 :(得分:1)
你可能想看一下boost中的“可选”类模板(我相信)。它允许您传递一个对象,但也允许您指定您以一种比原始指针更安全的方式提供任何数据。
这不是我特别喜欢的东西,但你可能会发现它符合你的要求。